Discover how Behavior Trees can help you structure intelligent robot behaviors in ROS 2 using modular logic, clear control flow, and intuitive tools like Groot.
Robots that operate in the real world face much more than just navigation tasks. They need to handle dynamic environments, unpredictable humans, and ambiguous situations.
Think about it: people step in front of delivery bots, block them for fun, or push them just to see what happens.
How can we design robots that are both reliable and flexible in the face of such chaos?
The answer is modular, visual, fault-tolerant logic – in other words, Behavior Trees.
Behavior Trees (BTs) originated in game development, but quickly became a favorite in robotics because of their flexibility and readability. They allow us to compose logic in a structured way using nodes that are easy to understand, reuse, and extend.
A Behavior Tree is composed of:
Action Nodes: they perform tasks
Condition Nodes: they check the state of the world
Control Nodes: they decide how to organize children (e.g., Sequence, Fallback)
Decorator Nodes: they modify the behavior of a single child (e.g., Retry, ForceFailure)
Like LEGO bricks, you can combine these nodes to build behavior that is both powerful and modular.
Once you understand the theory, the next step is hands-on practice.
To create and manage Behavior Trees easily, we’ll use a graphical tool called Groot.
Open a terminal and run the following commands to install and launch Groot:
cd ~/Downloads
chmod +x Groot2*.AppImage
./Groot2*.AppImage
Before you start building your navigation logic, you need to import the Nav2 node models into Groot.
You can do this via the GUI by selecting Import Models
and choosing:
/opt/ros/jazzy/share/nav2_behavior_tree/behavior_tree_nodes.xml
After this step, you’ll see nodes like:
ComputePathToPose
FollowPath
IsBatteryLow
and many more
Now you’re ready to build your first navigation tree.
Let’s build a tree where the robot does two things:
Compute a path to a goal
Follow that path
Here’s the full XML definition:
This Behavior Tree defines a simple sequence to navigate the robot to a goal. The main tree is "MainTree"
, and it runs a Sequence node called "navigate_to_goal"
, which executes its children one by one until one fails or all succeed.
The sequence has two actions:
ComputePathToPose: calculates a path from the robot to the target ({goal}
) using the "GridBased"
planner and stores it in {path}
.
FollowPath: takes the generated {path}
and commands the robot to follow it using the "FollowPath"
controller.
If both actions succeed, the sequence returns success; otherwise, it stops at the first failure.
Let’s now tell ROS 2 to actually use this tree.
Save the previous XML code into:
bumperbot_navigation/behavior_tree/simple_navigation.xml
Add this line to your CMakeLists.txt
file to ensure the tree is installed:
install(DIRECTORY behavior_tree/
DESTINATION share/${PROJECT_NAME}/behavior_tree)
This tells ROS 2 to install the entire behavior_tree
folder into the share directory of your package so it’s available at runtime.
Edit your bt_navigator.yaml
file to include:
default_nav_to_pose_bt_xml: "/home/alien/bumperbot_ws/src/bumperbot_navigation/behavior_tree/simple_navigation.xml"
This sets the Behavior Tree file that Nav2 will load when the bt_navigator
node starts.
We need to include the bt_navigator
node in your navigation.launch.py
file.
nav2_bt_navigator = Node(
package="nav2_bt_navigator",
executable="bt_navigator",
name="bt_navigator",
output="screen",
parameters=[
os.path.join(
bumperbot_navigation_pkg,
"config",
"bt_navigator.yaml"),
{"use_sim_time": use_sim_time}
],
)
lifecycle_nodes = ["controller_server", "planner_server", "smoother_server", "bt_navigator"]
This snippet launches the bt_navigator
node from the nav2_bt_navigator
package. The executable is specified as "bt_navigator"
, and the node name is also set to "bt_navigator"
. Output is directed to the terminal with output="screen"
.
It includes a list of parameters, such as the YAML config file and a flag to use simulation time. Finally, the node is added to lifecycle_nodes
so it’s managed automatically by the lifecycle manager—ensuring it’s properly configured and activated during startup.
Now build the workspace and run everything:
cd bumperbot_ws
colcon build
. install/setup.bash
ros2 launch bumperbot_bringup simulated_robot.launch.py world_name:=small_house
. install/setup.bash
ros2 launch bumperbot_navigation navigation.launch.py
Open RViz and load the default Nav2 configuration:
/opt/ros/jazzy/share/nav2_bringup/rviz/nav2_default_view.rviz
Use the Nav2 Goal tool to send a target pose. The robot will:
Plan a path
Follow the path
And if the path is invalid, the tree will return FAILURE
and stop
Behavior Trees give us a powerful way to write robust and reusable logic for autonomous robots.
By using Groot, ROS 2, and Nav2’s BT system, we can build logic that:
Fails gracefully
Can be visualized and debugged
Is easy to extend with recovery behaviors, safety checks, and more