Skip to content

Commit 1c39f1a

Browse files
committed
add tutorial about blackboard reference
1 parent dcb9087 commit 1c39f1a

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

docs/guides/blackboard_reference.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
sidebar_position: 4
3+
---
4+
5+
# Access Blackboard objects by Reference
6+
7+
If you followed the tutorials, you should already know that the Blackboard uses **value semantic**, i.e.
8+
the methods `getInput` and `setOutput` copy the value from/to the blackboard.
9+
10+
In some cases, it could be desirable to use, instead, **reference semantic**, i.e. access the
11+
object stored in the Blackboard directly. This is particularly important when the object is:
12+
13+
- a complex data structure
14+
- expensive to copy
15+
- non-copyable.
16+
17+
For instance, a Node where it is recommended to use reference semantic is the
18+
`LoopNode` decorator, that modifies "in-place" a vector of objects.
19+
20+
21+
## Blackboard entries as Share pointers
22+
23+
For the sake of simplicity, we will consider further an onject that is expensive to copy,
24+
called **Pointcloud**.
25+
26+
Assuming that we have a simple BT like this one:
27+
28+
```xml
29+
<root BTCPP_format="4" >
30+
<BehaviorTree ID="SegmentCup">
31+
<Sequence>
32+
<AcquirePointCloud cloud="{pointcloud}"/>
33+
<SegmentObject obj_name="cup" cloud="{pointcloud}" obj_pose="{pose}"/>
34+
</Sequence>
35+
</BehaviorTree>
36+
</root>
37+
```
38+
39+
- **AcquirePointCloud** will write into the blackboard entry `pointcloud`.
40+
- **SegmentObject** will read from that entry.
41+
42+
The recommended port types, in this case is:
43+
44+
```cpp
45+
PortsList AcquirePointCloud::providedPorts()
46+
{
47+
return { OutputPort<std::shared_ptr<Pointcloud>>("cloud") };
48+
}
49+
50+
PortsList AcquirePointCloud::providedPorts()
51+
{
52+
return { InputPort<std::string>("obj_name"),
53+
InputPort<std::shared_ptr<Pointcloud>>("cloud"),
54+
OutputPort<Pose3d>("obj_pose") };
55+
}
56+
```
57+
58+
The methods `getInput` and `setOutput` can be used as usual and still have value semantic.
59+
But since the object being copied is a `shared_ptr` we are actually accessing the
60+
pointcloud instance by reference.
61+
62+
## Thread safety
63+
64+
The most notable issue, when using this approach, is that it is **NOT thread safe**.
65+
66+
IF multithreading is used, inside a Node (remember that BT.CPP is single-threaded by default),
67+
then there is no guarantee that a copy of the object being shared isn't accessed during writing.
68+
69+
To prevent this issue, we provide a different API that includes a locking mechanism.
70+
71+
```cpp
72+
// ----- NOT thread-safe -------
73+
74+
std::shared_ptr<Pointcloud> cloud_ptr;
75+
getInput("cloud", cloud_ptr);
76+
// modify the pointcloud referenced by cloud_ptr here
77+
78+
// ----- thread-safe alternative -------
79+
80+
// inside this scope, the object in the blackboard is protected by a mutex
81+
if(auto any_locked = getLockedPortContent("cloud")
82+
{
83+
// cast Any to std::shared_ptr<Pointcloud>
84+
auto cloud_ptr = any_locked.get()->cast<std::shared_ptr<Pointcloud>>();
85+
// modify the pointcloud referenced by cloud_ptr here
86+
}
87+
```
88+

src/pages/groot.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export default function Groot() {
3636
points: [
3737
"Full Behavior Tree Editor",
3838
"Monitor and Log Visualizer limited to 20 Nodes",
39-
"Not for commercial use",
4039
],
4140
btn: "Download",
4241
onclick:() => handleClickBasic()

0 commit comments

Comments
 (0)