Comments (26)
@LoyVanBeek I am glad to say that I was wrong...
I just "finished" adding types to ports in the branch ver_3
.
Ports type is optional, BUT if you specify the type in providedPorts
as shown here, then during the creation of the tree from XML, it is automatically checked that you don't connect ports of different type.
This is, of course, still experimental, but very promising.
from behaviortree.cpp.
I am closing this issue, because I believe that I have done with the refactoring
Thak you all for the feedback and brilliant ideas you shared.
And by the way... @LoyVanBeek you can't imagine how much challenges I had to add typed ports !!!
Something that looked easy actuallt opened a pandora box of potential issues and pitfalls.
In short, I had to upgrade my C++ skills to bend the very fabric of reality...
But I am super happy with the result.
I am currently working on more tutorials and documentation.
You can get a feeling of the new API in version 3 here: https://github.com/BehaviorTree/BehaviorTree.CPP/tree/ver_3/examples
from behaviortree.cpp.
@LoyVanBeek
Usually, I am a very strong advocate of type safety in C++, including strong types.
But the primary goal of State Machines and Behavior Trees is coordination, not dataflow.
On the other hand, we do know that, sometimes, it is useful to share data between states.
I am afraid that adding types to ports might increase the complexity of the library and create an entry barrier; more features attract advanced users but intimidate newbie and, very often, may limit adoption.
I might add types, though, as an optional signature/metadata of the port that is used only if defined by the user, in other words, I can add this as an optional feature.
from behaviortree.cpp.
After a night of sleep and a coffee, I came to the conclusion that whatever we do, this is going to be an annoying and non-backward-compatible change. Sorry users.
Therefore, there is no point to stick with the half baked Solution A.
I unified both Solutions into the single one that seems to me the most future-proof: 9429e7a
from behaviortree.cpp.
According to my plan, the case you described is already covered in my proposal. You can have read write access, since "ports" are actually shared memory under the hood.
from behaviortree.cpp.
Ok, I think I am slowly coming to a viable version 3 of the library.
The new discussion document is here and is much more focused the the previous one:
https://github.com/BehaviorTree/BehaviorTree.CPP/blob/ver_3/RoadmapDiscussion.md
The new version is more or less implemented and I need to write unit tests and documentation.
Overall, I believe that version 3 is much better, but there is something that people might dislike.
All the entries of the the Blackboards that are read/written must be specified explicitly in the XML.
This might look cumbersome to people that use BB a "lot", but it is the only way to avoid name clashing.
Any thougth? @miccol @v-lopez @mjeronimo ?
from behaviortree.cpp.
have you at how Unreal Engine handles this ? Since they use the same mechanisms and concept for their behavior tree
The problem I am trying to solve is related to the fact that you can interact with the BB in your C++ code, in a way that is invisible from the"outside". As far as I understood, Unreal doesn't have this problem because you do not write custom Tasks in C++.
The only practical difference in the new version is that, currently, you can access any entry in the BB, whilst in the future you will need to specify explicitly which entries of the BB are written/read.
Let me rephrase: you will need to create ports and use input ports to read from the BB and output ports to write on the BB.
Actually relying on this library, I'm not sure how much it's going to break.
I guess quite a lot, if you use BB extensively. But as I said, it is necessary for the future of this library.
Sorry :(
The only thing I can do is to offer you some of my software development hours to help you refactoring your code, if it is open source.
from behaviortree.cpp.
Hi Davide,
Thank you for highlighting this issue and for proposing detailed solutions.
Both solutions are reasonable. I personally prefer Solution B at first sight.
Would it be possible to keep NodeParameters required_parameters
in the manifest and add PortsList ports
?
Parameters could be used to define those values that do not change at runtime and defined by the user at design time.
What do you think?
from behaviortree.cpp.
The old behavior related to NodeParameter would still work, so no need to make any distintion.
An "input port" might either be some text that is parsed using convertFromString or be the "pointer" to an entry of the Blackboard (or, if you prefer to think in SMACH terms, a "remap").
On the contrary, output port can only have the latter (pointer to BB).
In terms of syntax in the XML, nothing really changes for NodeParameters (AKA Input Ports).
UPDATE: I pushed an extension of the document to make this clear 561533d
from behaviortree.cpp.
Very interesting summary, I agree with everything.
Just a question, I have a navigation_path entry in the BB, and I want to feed it to a OptimizePath node that has an inout port path, how would I access the optimized path afterwards?
Or in this case, it would be a better design decision, to provide two separate ports, in_path and optimized_path, for instance.
<Action ID="CreatePath" /> <!-- It's output goes to a navigation_path port -->
<Action ID="OptimizePath" path="{navigation_path}" />
<Action ID="FollowPath" path="{navigation_path or path}" />
from behaviortree.cpp.
Kind of late to the party, but I wanted to give my two cents on this.
I completely agree with the proposal, and as an end user of the library I don't think the proposed changes would be too cumbersome to follow. I have some questions, though.
It's stated in the proposal that:
No distinction is made in the XML between inputs, outputs; additionally, passing static text parameters is still possible
I think knowing if a given port is either input or output can be useful for behaviour designers using graphical front-ends. This could make it possible for external tools to include easy-to-understand symbols next to each port entry in a block, thus making the whole process less error prone. In general, I think all the info in the manifest should be included in the XML as well, otherwise other tools would be missing important info.
Also related to this, does the sentence above imply that the ${...}
syntax is completely deprecated and the new port syntax {port}
should be used instead, even in external tools like Groot?
I also wonder if ports could allow us to get better sub-trees support, as we are making extensive use of them. A problem we run frequently into is that sub-trees, being composed by multiples nodes, present several internal connections, but there's some info that usually comes from outside (for example, a "Track" subtree may receive a frame to track as input from the parent tree). Would it be possible to somehow define ports as "external" or "internal"? I guess this is something that should be included in the XML schema of a sub-tree? This way other people using that subtree can forget about the internal details and just feed the tree with the info it needs.
What do you think?
from behaviortree.cpp.
thanks @Thordreck .
I was thinking about that but I didn-t add my thought to the document.
External tools, such as Groot ,do have access to the TreeNodeManifest, which knows if a port in a input or output.
In the XML I expect something like this:
<BehaviorTree>
...
<MyAction my_input="{the_question}" arg="42" my_output="{the_answer}" />
....
<BehaviorTree>
<TreeNodesModel>
...
<Action ID="MyAction" input_ports="my_input;arg" output_ports="my_output" />
....
<TreeNodesModel>
Does it make sense in your opinion?
About Subtrees, I believe you are right, there must be a way to add in/out ports to Subtree.
It is defenetively worth thinking about it now; it is probably going to be the next thing I will concentrate on once I feel that ports has been implemented "right" at the TreeNode level.
If you have any proposal or suggestion I will be very happy to review it :)
from behaviortree.cpp.
I'm just back from vacation and catching up on this thread...
I agree that a port abstraction that allows for both static and dynamic parameters, introspection, and remapping is a superior model. Also, not concerned about the breaking changes to the API; we can accommodate that without much trouble.
However, I didn't understand this statement:
The problem is that SimpleActionNodes and SimpleDecoratorNodes will loose the ability to access ports.
I would expect all node types to be able to use the ports.
from behaviortree.cpp.
I would expect all node types to be able to use the ports.
This is a reasonable expectation ;)
The main problem is that the TreeNodeModel is created using the static method:
static const PortsList& MyNode::providedPorts();
// formerly known as
//static NodeParameters MyNode::requiredNodeParameters()
Since, by definition, the purpose of SimpleNodes is to reduce the boilerplate associated with inheritance, the "static method" trick can't be used.
In other words, the manifest in BehaviorTreeFactory
can't see ports.
The only solution I see is to change the signature of the registering function:
void registerSimpleAction(const std::string& ID, const TickFunctor& func, PortsList ports);
In other words, if the user really wants a SimpleNode with ports, he needs to specify them.
Any other suggestion?
from behaviortree.cpp.
To make things more complicated: should Ports be typed?
Not sure if this is possible yet, but I'm quite interesting in using this library, having type checks would be another reason to use it!
I'm a heavy SMACH user for RoboCup@Home with @tue-robotics, but once our library of reusable behaviors got bigger and the team as well, messing up types for SMACH user data happened a lot.
Another problem often encountered was that some user_data might not be set
I eventually decoupled dataflow from behavior by using so-called Designators
that encapsulate & reuse the process of how a concrete value is resolved. E.g. find any Arm of the robot that is empty to grab an object with or to find an entity in the world model matching some requirements.
from behaviortree.cpp.
@facontidavide To address #18, have you looked at how Unreal Engine handles this ? Since they use the same mechanisms and concept for their behavior tree.
Actually relying on this library, I'm not sure how much it's going to break. This is mainly due to the fact that you said BB are going to be deprecated but you still seems to rely on them to initialize the tree or adjust the values from outside dynamically.
from behaviortree.cpp.
Overall, I believe that version 3 is much better, but there is something that people might dislike.
All the entries of the the Blackboards that are read/written must be specified explicitly in the XML.
I actually like that also from a design point of view. If I want to replace a node, I need to know which entries in the blackboard will be affected without going into the C++ code.
As usual, awesome job Davide!.
from behaviortree.cpp.
@mjeronimo I added the possibility of adding ports tosimple nodes (tutorials updated too).
@LoyVanBeek after thinking carefully... I understand the value of typed ports, but it is really difficult to integrate this in the library in an elegant way (if you disagree, feel free to propose a pull request).
Implementation wise, the only thing left is to add ports to Subtrees, that I will discuss in #44 and #45
Thanks for your feedback!
from behaviortree.cpp.
It's nice to be wrong sometimes :-).
from behaviortree.cpp.
I have a question related to this, is it possible to have optional input ports?
I understand there are ways around it, such as passing a special value that signifies no parameter, or wrapping them in a boost::optional
or similar construct.
But since getInput() already gives the option of returning false or empty optionals, it may be interesting to define in the GUI if a port must be provided always or is optional.
from behaviortree.cpp.
I have mixed feelings about this... since right now all the inputs ARE optional.
The challenge is to make them mandatory, actually. Because you need to check the result of getInput anyway.
And what is a "mandatory" input port? How is it implemented? I don't see any clear answer to this question.
from behaviortree.cpp.
I guess part of the idea here, is that people can build trees without having to look at the code.
Maybe I'm just mixing stuff, right now I still have to document somewhere what my Node does, and what my input and output is. Is there a standard or recommended way of doing it? Should an optional text description be part of the PortsList
?
from behaviortree.cpp.
I can surely add some text description to the PortList, no problem.
Let me extend my previous answer, to be less ambiguous (spoiler alert: long explanation coming):
let's consider this Node:
<MyNode input_A="{A}" input_B="{B}" />
Where the model of the ports C++ is:
PortsList ports = { {"input_A, PortType::INPUT}, {"input_B, PortType::INPUT} };
Let's suppose that we change it to:
PortsList ports = {
{"input_A, PortType::INPUT, OPTIONAL},
{"input_B, PortType::INPUT, REQUIRED} };
We have to distinguish between checks/validation done at deployment time (potentially statically verified) and at run-time (may fail during the execution of MyNode::tick() )
In terms of model, OPTIONAL
means that input_A
doesn't need to be specified in the XML, whilst REQUIRED
means that input_B
must to be specified in the XML.
The C++ code is responsible for run-time validation; in other words, when getInput()
is called you must decide what to do if it returns false
or if it throws an exception.
Are you just ignoring the returned value or not catching the exception? Well.. I don't know what to do about it :(
What will happen when an OPTIONAL port is not passed to the Node? Up to you.
What will happen when an REQUIRED port is not passed to the Node? You got my point...
I don't know how to be sure that the C++ is consistent with the OPTIONAL/REQUIRED policy.
May be I should just ignore the problem and let the user code throw exceptions if the "mandatory/optional" contract is not consistent between model and implementation, but, even so, I have to check if it actually possible to implement these ideas.
from behaviortree.cpp.
Maybe with the description is enough, since we can write there whether it's optional or not in a purely informative and non-enforceable way.
But if not, getInput
(or a similar function to getInput
) can throw if a required arg is missing, and return false silently if it's an optional missing.
In my case it's annoyting that the application prints that it's failing to get what I consider to be optional inputs:
getInput() failed because it was unable to find the key [pose_offset] remapped to [pose_offset]
getInput() failed because it was unable to find the key [initial_search_state] remapped to [initial_search_state]
getInput() failed because it was unable to find the key [pose_offset] remapped to [pose_offset]
getInput() failed because it was unable to find the key [initial_search_state] remapped to [initial_search_state]
from behaviortree.cpp.
Well, thta printf must die ;)
It just came to my mind that I may replace std::optional with std::expected to include the reason of the failure.
https://github.com/TartanLlama/expected
https://github.com/martinmoene/expected-lite
from behaviortree.cpp.
@v-lopez you are definitively right, getInput
must not print anything on console. As I said, std:.expected is the right interface to use in this case, because is like std::optional but we can pack an error message too.
I just pushed the changes. f8128a0
As you may notice, I now use expected, renamed Optional. It works like this:
if( auto res = getInput<double>("port_name") )
{
double value = res.value();
}
else{
std::cerr << res.error() << std::endl;
throw std::runtime_error( res.error() );
}
from behaviortree.cpp.
Related Issues (20)
- Ports with generic types: std::vector<Eigen::VectorXd> HOT 1
- XML content HOT 2
- Allow custom implicit conversion of blackboard types HOT 3
- [v4.x] Why does ControlNode::halt() DecoratorNode::halt() not set status to IDLE? HOT 1
- Can we use script set var HOT 1
- _autoremap keyword not behaving as expected in version 4.4.0 HOT 1
- Exception thrown when multiple nodes have unremapped outputs HOT 5
- 动态创建多课行为树,怎么设置日志记录呢 HOT 1
- Enable local variables in sub-tree HOT 6
- Enums evaluate to double not integral? HOT 1
- Real-time Visualization in Groot: is it included in the Basic package? HOT 6
- Catkin make problem HOT 1
- Tree::getNodesByPath returns a list of constant pointers but the method is not marked constant HOT 1
- Clarification needed: what is supposed to happen when forgetting curly braces for an output port in XML? HOT 2
- Blackboard::set() failed HOT 3
- Implementing a version getNodesByPath() returning non const results HOT 1
- 为啥pTree.haltTree(); 执行之后,pTree 行为树还在执行? HOT 1
- registerNodeType not accepting instance names HOT 2
- Typos HOT 1
- Groot: In case of custom type variables groot prints null in line with the variable name HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from behaviortree.cpp.