Introduction to Behavior Trees
Which problem are we trying to solve?
- We always start implementing individual robot "skills" and designing the workflow/behavior is an "afterthought".
- Not many guidelines (architectural patterns) related to "Task Planning".
- How do we create reusable, modular and composable software architectures?
"Separation of Concerns" and "Composability" are two core principles
you need to understand to build good robotic systems
Which problem are we trying to solve?
We want to build a high level "Task Planner", AKA "Coordinator / Orchestrator" that executes complex behaviors in a Service Oriented system.
What are "Behavior Trees"?
-
An alternative to Hierarchical State Machines.
-
A Domain Specific Language to describe Behaviors.
- A "grammar" to implement Explicit Task Planners.
- .... not a silver bullet, but an incremental improvement.
Complexity increases exponentially with size.
The complexity of BehaviorTree grow more slowly
HSM
BT
- More intuitive and faster to learn
- Order specified by the transitions
- Might be harder to scale
- Hard to model concurrency
- They invite you to think about failure and fallback strategies
- Order specified by the semantic of the parent node
- More human-readable (with practice)
- Richer and more expressive grammar, easy to extend
Actions, Control Nodes and Decorators
- We have a "tick" signal that propagates from the root of the tree to its leaves.
- All the nodes: may return SUCCESS, FAILURE or (in some cases) RUNNING.
- Action Nodes are leaves of the tree and they actually "do something". From a practical point of view, they just invoke a user defined callback.
- Control Nodes have 1-to-N children, the order is important. If and when a child is executed is based on the result of the other children.
- Decorator Nodes have 1 child and may modify its returned value and decide if and when the child is called.
Sequence
Tick the children, from left to right, while they return SUCCESS.
if a child returns FAILURE, stop and return FAILURE.
Fallback
Tick the children, from left to right, while they return FAILURE.
If a child returns SUCCESS, stop and return SUCCESS.
Decorators
Decorators are a very simple and powerful way to implement more complex logic.
It would be very complicated to do the same with state machines
You need to think about all the branches :(
There are no beers in the fridge and I don't want to keep the door of the fridge open
Key concepts to keep in mind (meta-model)
- A Parent node can only see its direct child / children.
- There is no difference if a child is a leaf or a sub-branch.
- A node doesn't have any knowledge about its parent.
This makes them composable and intrinsically hierarchical
Don't repeat yourself: SubTrees
- The analogy with "functions" is very powerful to understand composability and expressiveness.
bool FetchBeer()
{
if( GoTo("kitchen") &&
OpenFridge() &&
Grasp("beer") &&
CloseFridge() )
{
return true;
}
else{
return false;
}
}
In the context of Behavior Trees this mean:
- Encapsulate reusable blocks into subtrees
- Don't be afraid to create your own custom Control and Decorator nodes
Dataflow: the blackboard
If we think about SubTrees and Nodes as functions, it is easy to understand how we need input and output to be modeled explicitly.
This is done in Behavior Tree using the Blackboard, a simple key-value storage
The NAND gate analogy
The only building block of State Machines are the transitions.
Behavior Trees have more abstractions, but you probably want to have even more (or different one) to make your tree more readable and less verbose.
Example
We are applying here the same rules about "clean code" and "readability".
About BehaviorTree.CPP
Much more than a C++ implementation
Separation of Concerns
- Nodes are programmed in C++, trees in XML. This provides flexibility but also strong decoupling between coordination and business logic.
- In a Python implementation like pytrees, people will inevitably mix everything.
No global variables
- BehaviorTree.CPP has probably one of the best approaches to Dataflow in the field of BTs (inspired by SMACH).
- Dataflow is implemented as Ports instead of Blackboards
- You may define Subtrees and reuse them. Their ports are scoped; you need to "export" them to make them visible. Similar to the scopes of variables in programming languages.
- Ports are like the arguments of your functions.
Summarizing
About Behavior Trees in general
- Both HSM and BT allow the user to create explicit Task Planning nodes.
- Their real value is in the decoupling and encapsulation.
- Thinking in terms of Domain Specific Language will help you writing better BTs.
- BT are more powerful when extended to suit your need.
About BehaviorTree.CPP
- Reactive and Concurrent behaviors are first class citizens.
- Tooling and good documentation makes the life of the user easier.
- Efficient as C++, flexible as a scripting language.
- The concept of "scoped ports" allows scalable composability.
Copy of Introduction to Behavior Trees 2022
By Davide Faconti
Copy of Introduction to Behavior Trees 2022
- 748