Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8a23156

Browse files
committed
WIP DOC
1 parent 8f000d6 commit 8a23156

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

MigrationGuide.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# Migration Guide from V2 to V3
2+
3+
The main goal of this project id to create a Behavior Tree implementation
4+
that uses the principle of Model Driven Development to separate the role
5+
of the __Component Developer__ from the __Behavior Designed__ and __System Integrator__.
6+
7+
In practice, this means that:
8+
9+
- Custom Actions (or, in general, custom TreeNodes) must be reusable building
10+
blocks. Implement them once, reuse them many times.
11+
12+
- To build a BehaviorTree out of TreeNodes, the Behavior Designer must not need to read
13+
nor modify the source code of the a given TreeNode.
14+
15+
There is a __major design flaw__ that undermines this goal: the way
16+
the BlackBoard was used in version `2.x` to implement dataflow between nodes.
17+
18+
In general, DataFlow should not be the main concern of a library like this,
19+
that focuses on Coordination, but it is apparent to anyone that had implemented
20+
a sufficiently large coordination component that if would be impossible
21+
to ignore it completely.
22+
23+
As described in [issue #18](https://github.com/BehaviorTree/BehaviorTree.CPP/issues/18)
24+
there are several potential problems with the Blackboard approach:
25+
26+
- To know which entries of the BB are read/written, you should read the source code.
27+
- As a consequence, external tools such as __Groot__ can not know which BB entries are accessed.
28+
- If there is a name clashing (multiple nodes use the same key for different purposes),
29+
the only way to fit it is modifying the source code.
30+
31+
SMACH solved this problem using [input and output ports](http://wiki.ros.org/smach/Tutorials/User%20Data)
32+
and remapping to connect them.
33+
34+
In the ROS community, we potentially have the same problem with topics,
35+
but tools such as __rosinfo__ provides introspection at run-time and name
36+
clashing is avoided using remapping.
37+
38+
This was the main reason to develop version `3.x` of __Behaviortree.CPP__, but we
39+
also took the opportunity to do some additional refactoring to make the code
40+
more understandable.
41+
42+
In this document we will use the following terms often:
43+
44+
- __Composition__: it refers to "composing" TreeNodes into Trees. In general
45+
we want a TreeNode implementation to be composition-agnostic.
46+
47+
- __Model/Modelling__: it is a description of a Tree or TreeNode that is
48+
sufficient (and necessary) to describe it, without knowing any additional
49+
detail about the actual implementation.
50+
51+
52+
# 2. Blackboard NodeParameters an DataPorts
53+
54+
In version `2.x` we had the intuition that passing one or more arguments
55+
to a `TreeNode` would make the node more generic and reusable.
56+
57+
This is similar to the arguments of a funtion in any programming language.
58+
59+
```C++
60+
// with arguments
61+
GoTo("kitchen")
62+
63+
//Without arguments
64+
GoToKitchen()
65+
GoToLivingRoom()
66+
GoToBedRoom1()
67+
GoToBedroom2()
68+
// ....
69+
```
70+
71+
On the other hand, we had the Blackboard, that was nothing more than a
72+
shared __key/value__ table; the key is a `string`, whilst the value is a
73+
stored in a type-safe container similar to `std::any` or `std:.variant`.
74+
75+
The problem is that writing/reading in an entry of the BB is done implicitly
76+
in the source code and it is usually hard-coded. This makes the TreeNode
77+
not fully reusable.
78+
79+
To fix this, we still use the Blackboard under the hood, but it can not be
80+
accessed directly. Entires are read/written using respeticely `InputPorts`
81+
and `OutputPorts`.
82+
83+
This ports __must be modelled__ to allow remapping at run-time.
84+
85+
Let's take a look to an example at the old code:
86+
87+
```XML
88+
<root>
89+
<BehaviorTree>
90+
<SequenceStar>
91+
<CalculateGoalPose/>
92+
<MoveBase goal="${GoalPose}" />
93+
</SequenceStar>
94+
</BehaviorTree>
95+
</root>
96+
```
97+
98+
```C++
99+
//Old code (V2)
100+
NodeStatus CalculateGoalPose(TreeNode& self)
101+
{
102+
const Pose2D mygoal = { 1, 2, 3.14};
103+
// "GoalPose" is hardcoded... we don't like that
104+
self.blackboard()->set("GoalPose", mygoal);
105+
return NodeStatus::SUCCESS;
106+
}
107+
108+
class MoveBase : public BT::AsyncActionNode
109+
{
110+
public:
111+
112+
MoveBase(const std::string& name, const BT::NodeParameters& params)
113+
: AsyncActionNode(name, params) {}
114+
115+
static const BT::NodeParameters& requiredNodeParameters()
116+
{
117+
static BT::NodeParameters params = {{"goal", "0;0;0"}};
118+
return params;
119+
}
120+
121+
BT::NodeStatus tick()
122+
{
123+
Pose2D goal;
124+
if (getParam<Pose2D>("goal", goal))
125+
{
126+
printf("[ MoveBase: DONE ]\n");
127+
return BT::NodeStatus::SUCCESS;
128+
}
129+
else{
130+
printf("MoveBase: Failed for some reason\n");
131+
return BT::NodeStatus::FAILURE;
132+
}
133+
}
134+
/// etc.
135+
};
136+
```
137+
138+
We may noticed that the `NodeParameter` can be remapped in the XML, but
139+
to change the key "GoalPose" in `CalculateGoalPose`we must inspect the code
140+
and modify it.
141+
142+
In other words, `NodeParameter` is already a reasonably good implementation
143+
of an `InputPort`, but we need a consisten equivalent version for `Outputport`.
144+
145+
This is the new code:
146+
147+
```XML
148+
<root>
149+
<BehaviorTree>
150+
<SequenceStar>
151+
<CalculateGoalPose target="{GoalPose}" />
152+
<MoveBase goal ="{GoalPose}" />
153+
</SequenceStar>
154+
</BehaviorTree>
155+
</root>
156+
```
157+
158+
```C++
159+
//New code (V3)
160+
class CalculateGoalPose : public BT::SyncActionNode
161+
{
162+
public:
163+
164+
MoveBase(const std::string& name, const BT::NodeConfiguration& cfg)
165+
: SyncActionNode(name, cfg) {}
166+
167+
static BT::PortsList providedPorts()
168+
{
169+
return { BT::OutputPort<Pose2D>("target") };
170+
}
171+
172+
BT::NodeStatus tick()
173+
{
174+
const Pose2D myTarget = { 1, 2, 3.14 };
175+
setOutput("target", myTarget);
176+
return BT::NodeStatus::SUCCESS;
177+
}
178+
};
179+
180+
class MoveBase : public BT::AsyncActionNode
181+
{
182+
public:
183+
184+
MoveBase(const std::string& name, const BT::NodeConfiguration& config)
185+
: AsyncActionNode(name, config) {}
186+
187+
static BT::PortsList providedPorts()
188+
{
189+
return { BT::InputPort<Pose2D>("goal", "Port description", "0;0;0") };
190+
}
191+
192+
BT::NodeStatus tick()
193+
{
194+
Pose2D goal;
195+
if (auto res = getInput<Pose2D>("goal", goal))
196+
{
197+
printf("[ MoveBase: DONE ]\n");
198+
return BT::NodeStatus::SUCCESS;
199+
}
200+
else{
201+
printf("MoveBase: Failed. Error code: %s\n", res.error());
202+
return BT::NodeStatus::FAILURE;
203+
}
204+
}
205+
/// etc.
206+
};
207+
```
208+
209+
The main differences are:
210+
211+
- `requiredNodeParameters()` is now `providedPorts()` and it is used to
212+
declare both Inputs and Output ports alike.
213+
214+
- `setOutput<>()` has been introduced and must be declared in `providedPorts()`.
215+
216+
- `getParam<>()` is now called `getInput<>()` to be more consistent with
217+
`setOutput<>()`. Furthermore, if an error occurs, we can get the error
218+
message.
219+
220+
- Remapping to a shared entry ("GoalPose") is done at run-time in the XML.
221+

0 commit comments

Comments
 (0)