• A signal called tick is sent to the root of the tree and propagates through the tree until it reaches a leaf node.
  • Any TreeNode that receives a tick signal executes its callback. This callback must return either
  • RUNNING means that the action needs more time to return a valid result.
  • If a TreeNode has one or more children, it is its responsibility to propagate the tick; each Node type may have different rules about if, when and how many times children are ticked.
  • The LeafNodes, those TreeNodes which don’t have any children, are the actual commands, i.e. the Nodes where the behavior tree interacts with the rest of the system. Actions nodes are the most common type of LeafNodes.
  • A Blackboard is a key/value storage shared by all the Nodes of a Tree.
  • Ports are a mechanism that Nodes can use to exchange information between each other. Ports are “connected” using the same key of the blackboard.
\[\mathbf{TICK = To\,invoke\,the\,callback\,tick()\,of\,a\,TreeNode}\]

Type of TreeNode Children Count Notes
ControlNode 1…N Usually, ticks a child based on the result of its siblings or/and its own state.
DecoratorNode 1 Among other things, it may alter the result of the children or tick it multiple times.
ConditionNode 0 Should not alter the system. Shall not return RUNNING.
ActionNode 0 This is the Node that “does something”
\[\mathbf{ActionNodes = \quad SynchronousNodes + \quad AsynchronousNodes}\]

XML schema

 <root BTCPP_format="4" >
     <BehaviorTree ID="MainTree">
        <Sequence name="root_sequence">
           <SaySomething   name="action_hello" message="Hello"/>
           <OpenGripper    name="open_gripper"/>
           <ApproachObject name="approach_object"/>
           <CloseGripper   name="close_gripper"/>
    <!-- the BT executor don't require this, but Groot does -->     
        <Action ID="SaySomething">
            <input_port name="message" type="std::string" />
        <Action ID="OpenGripper"/>
        <Action ID="ApproachObject"/>
        <Action ID="CloseGripper"/>      
  • Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. It doesn’t necessarily mean they’ll ever both be running at the same instant.
  • Parallelism is when tasks run at the same time in different threads, e.g., on a multicore processor.
  • A good template to get started is the StatefulActionNode.

using namespace std::chrono;

// Example of Asynchronous node that uses StatefulActionNode as base class
class SleepNode : public BT::StatefulActionNode
    SleepNode(const std::string& name, const BT::NodeConfig& config)
      : BT::StatefulActionNode(name, config)

    static BT::PortsList providedPorts()
      // amount of milliseconds that we want to sleep
      return{ BT::InputPort<int>("msec") };

    NodeStatus onStart() override
      int msec = 0;
      getInput("msec", msec);

      if( msec <= 0 ) {
        // No need to go into the RUNNING state
        return NodeStatus::SUCCESS;
      else {
        // once the deadline is reached, we will return SUCCESS.
        deadline_ = system_clock::now() + milliseconds(msec);
        return NodeStatus::RUNNING;

    /// method invoked by an action in the RUNNING state.
    NodeStatus onRunning() override
      if ( system_clock::now() >= deadline_ ) {
        return NodeStatus::SUCCESS;
      else {
        return NodeStatus::RUNNING;

    void onHalted() override
      // nothing to do here...
      std::cout << "SleepNode interrupted" << std::endl;

    system_clock::time_point deadline_;
  • Frequently, people using BT.CPP execute the actual task in a different process. A typical (and recommended) way to do this in ROS is using ActionLib. More generally, we may assume that the developer has their own inter-processing communication, with a client/server relationship between the BT executor and the actual service provider.