|
| 1 | +--- |
| 2 | +sidebar_position: 5 |
| 3 | +--- |
| 4 | + |
| 5 | +# Ports VS Blackboard |
| 6 | + |
| 7 | +**BT.CPP** is the only implementation (to our knowledge) of Behavior Tree that |
| 8 | +introduces the concept of **Input/Output Ports**, as an alternative to **Blackboards**. |
| 9 | + |
| 10 | +To be more specific, Ports are an interface that adds a level of |
| 11 | +indirection and additional semantics to the blackboard. |
| 12 | + |
| 13 | +To understand why using Ports is recommended and using directly the Blackboard |
| 14 | +is **discouraged**, we should first understand some of the core principles |
| 15 | +of BehaviorTree.CPP. |
| 16 | + |
| 17 | +## Goals of BT.CPP |
| 18 | + |
| 19 | +### Model Driven Development |
| 20 | + |
| 21 | +It is not the purpose of this article to explain what Model Driven Development is. |
| 22 | +But, in short, we want to build a "model" of our Nodes, i.e. some kind of meta-information |
| 23 | +that tells us how the Node interacts with the rest of the tree. |
| 24 | + |
| 25 | +Models are important both for developers (being self-documenting) and |
| 26 | +for external tools, such as visual editors, being Groot2 a notable example, |
| 27 | +or static analyzers. |
| 28 | + |
| 29 | +We believe that the **description of the data-flow between Nodes must be part of the model**. |
| 30 | +Additionally, we want to clearly express if a blackboard entry is being written (output), |
| 31 | +read (input) or both. |
| 32 | + |
| 33 | +Consider this example: |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | +In other implementations (or if anybody uses this library inappropriately...) the |
| 38 | +only way to know if these two Nodes communicate and depend on each other is either: |
| 39 | + |
| 40 | +- **inspecting the code**: something that we want to avoid. |
| 41 | +- or **reading the documentation**: but documentation is not guaranteed to be accurate and up to date. |
| 42 | + |
| 43 | +Instead, if Input/Output ports are part of the model, the intent of the Node and it |
| 44 | +relationship with the others become more explicit: |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +### Nodes composibility and Subtree scoping |
| 49 | + |
| 50 | +Ideally, we want to offer a platform that allows Behavior Designers to build trees |
| 51 | +(i.e., "compose Nodes") implemented by different vendors / users. |
| 52 | + |
| 53 | +But when the Blackboard is used directly, name-collision will become immediately an issue. |
| 54 | + |
| 55 | +Think about common names such as `goal`, `results`, `target`, `value`, etc. |
| 56 | + |
| 57 | +For instance, the Nodes **GraspObject** and **MoveBase** may be developed by different people |
| 58 | +and they both read the entry `target` from the blackboard. |
| 59 | +Unfortunately, they have different meanings and the type itself is different: |
| 60 | +the former expects a 3D pose, whilst the latter a 2D one. |
| 61 | + |
| 62 | +**Ports** offer one level of indirection, called also "remapping", as explained in |
| 63 | +[Tutorial 2](../tutorial-basics/tutorial_02_basic_ports.md). |
| 64 | + |
| 65 | +This means that, no matter which name is used when defining the Port |
| 66 | +(that name is "hardcoded" into your C++ implementation), |
| 67 | +you can always "remap" it to a different blackboard entry into your XML, |
| 68 | +without modifying the source code. |
| 69 | + |
| 70 | +The same is true for Subtrees remapping, explained in [Tutorial 6](../tutorial-basics/tutorial_06_subtree_ports.md). |
| 71 | +Being the Blackboard a glorified map of global variables, it scales very poorly. |
| 72 | +There is a reason why global variables are a no-no in programming! |
| 73 | + |
| 74 | +Ports remapping offers a solution to this problem. |
| 75 | + |
| 76 | + |
| 77 | +## Summary: never use the Blackboard directly |
| 78 | + |
| 79 | +You should do this: |
| 80 | + |
| 81 | +```c++ |
| 82 | +// example code in your tick() |
| 83 | +getInput("goal", goal); |
| 84 | +setOutput("result", result); |
| 85 | +``` |
| 86 | +
|
| 87 | +and avoid as much as possible this: |
| 88 | +
|
| 89 | +```c++ |
| 90 | +// example code in your tick() |
| 91 | +config().blackboard->get("goal", goal); |
| 92 | +config().blackboard->set("result", result); |
| 93 | +``` |
| 94 | + |
| 95 | +Both these codes may technically work, but the latter (direct access to the blackboard) |
| 96 | +is considered bad practice and **it is highly discouraged**: |
| 97 | + |
| 98 | +The problems with the 2nd version, i.e. the direct access to the blackboard: |
| 99 | + |
| 100 | +1. Names "goal" and "result" are hard-coded. To change them, you must recompile your application. Port, instead, can be remapped at run-time, modifying only the XML. |
| 101 | + |
| 102 | +2. The only way to know if a Node reads or writes one or more entries of the blackboard, |
| 103 | +is by inspecting the code. Up to date documentation is a solution, but the Port model is self-documenting. |
| 104 | + |
| 105 | +3. The `BehaviorTreeFactory` is unaware that those blackboard entries are being accessed. |
| 106 | +In contrast, when using Ports, we are able to introspect how ports communicate to each other |
| 107 | +and also to check at deployment time if the connection is done correctly. |
| 108 | + |
| 109 | +4. It may not work as expected when using SubTrees. |
| 110 | + |
| 111 | +5. The template specialization `convertFromString()` will not work correctly. |
| 112 | + |
| 113 | + |
| 114 | + |
0 commit comments