DocsTutorials
A mission as a behavior tree
Write a patrol as a behavior tree that waits for a start signal, repeats a square and always lands.
In this tutorial you write a patrol as a behavior tree instead of a script. The tree waits for a start signal, flies a square twice and lands, and it still lands if the patrol fails.
Before you start
- You've done A mission in Python. The tree flies the same kind of mission.
- Some idea of behavior trees helps. The BehaviorTree.CPP tutorials explain sequences, fallbacks and decorators. Aerostack2 runs trees with BehaviorTree.CPP 4.
How a tree runs a mission
The tree is an XML file. Each behavior is an action node, like TakeOff or GoTo, and control
nodes decide what runs next.
Sequenceruns its children in order, and fails as soon as one fails.Fallbacktries its children in order until one succeeds.Repeatruns its child a number of times.
A node from as2_behavior_tree called WaitForEvent holds the tree until a message arrives on a
topic. The table under Behavior trees lists every
Aerostack2 node.
Write the tree
In the Quickstart project folder, create trees/patrol.xml:
<?xml version="1.0"?>
<root BTCPP_format="4">
<BehaviorTree ID="Patrol">
<Sequence>
<WaitForEvent topic_name="start" result="{start}">
<AlwaysSuccess/>
</WaitForEvent>
<Arm service_name="set_arming_state"/>
<Offboard service_name="set_offboard_mode"/>
<TakeOff height="1.5" speed="0.5"/>
<Fallback>
<Repeat num_cycles="2">
<Sequence>
<GoTo pose="2;0;1.5" max_speed="1.0" yaw_mode="1" yaw_angle="0.0"/>
<GoTo pose="2;2;1.5" max_speed="1.0" yaw_mode="1" yaw_angle="0.0"/>
<GoTo pose="0;2;1.5" max_speed="1.0" yaw_mode="1" yaw_angle="0.0"/>
<GoTo pose="0;0;1.5" max_speed="1.0" yaw_mode="1" yaw_angle="0.0"/>
</Sequence>
</Repeat>
<Echo data="Patrol failed, landing"/>
</Fallback>
<Land speed="0.5"/>
</Sequence>
</BehaviorTree>
</root>Read it from the top:
WaitForEventholds everything until a message arrives on the drone'sstarttopic.Arm,OffboardandTakeOffget the drone in the air. If any of them fails, the whole sequence fails and nothing else runs.Repeatflies the square twice. AGoTopose isx;y;zin metres, inearth, andyaw_mode="1"turns the drone to face each point.- If a
GoTofails, theRepeatfails, and theFallbackmoves on toEcho, which prints a message and succeeds. Either way theFallbacksucceeds, so the sequence goes on toLand.
Load it at launch
The project starts a behavior tree with the drone, in the mission_execution window. Its command
is in tmuxinator/aerostack2.yaml. Point it at your tree:
# Mission execution
- mission_execution:
layout:
panes:
# Behavior tree
- ros2 launch as2_behavior_tree behavior_trees.launch.py
drone_id:=<%= drone_namespace %>
tree:=./trees/patrol.xmlThen start the drone:
./launch_as2.bashThe tree loads and stops at WaitForEvent. Nothing flies yet.
Start the patrol
The project's mission_behavior_tree.py publishes one empty message on /drone0/start. Run it in
a second terminal:
python3 mission_behavior_tree.pyThe drone takes off, flies the square twice and lands. Press Ctrl-b and the mission_execution
window's number to watch the tree. Every node logs its changes of state as it runs:
GoTo IDLE -> RUNNING
GoTo RUNNING -> SUCCESS
...
Repeat RUNNING -> SUCCESS
Fallback RUNNING -> SUCCESS
Land IDLE -> RUNNING
Land RUNNING -> SUCCESS
Sequence RUNNING -> SUCCESSWhen the tree finishes, the node exits. To fly again, run the launch command again in that pane.
Change the tree
Try a few changes, relaunching the tree after each:
- Set
num_cycles="-1"to patrol until something fails. - Give the
FallbackaGoToback to the origin before theEcho, so a failed patrol returns home before landing.
To see the tree as it runs, start it with groot_logger:=true and connect
Groot to it. Groot can also draw and edit the XML.
Trees or scripts
| Python API | Behavior tree | |
|---|---|---|
| Written as | A Python script | An XML file |
| Good at | Logic, calculations, several drones | Missions that react to events and failures |
| Runs | From any terminal | In its own node, loaded with the drone |
| Edit visually | No | With Groot |
They call the same behaviors underneath, so you can start with either and change later.
Next steps
Last updated on November 3, 2026