Python Behavior Trees for Robotics — Core Concepts

What Is a Behavior Tree?

A behavior tree (BT) is a directed acyclic graph that models decision-making as a hierarchy of tasks. Each node returns one of three statuses: SUCCESS, FAILURE, or RUNNING (still working on it). The tree is “ticked” repeatedly — typically 10–30 times per second — and each tick traverses the tree to determine what the robot should be doing right now.

Behavior trees originated in the video game industry (Halo 2, circa 2004) and were adopted by robotics because they solve the same problem: managing complex, reactive behavior in an organized way.

Node Types

Leaf Nodes

Action nodes do things: move the arm, drive forward, speak a phrase. They return SUCCESS when done, FAILURE if they cannot complete, or RUNNING while in progress.

Condition nodes check things: is the battery above 20%? Is there an obstacle ahead? Is the gripper holding an object? They return SUCCESS (true) or FAILURE (false) immediately — never RUNNING.

Composite Nodes

Sequence (→) executes children left to right. If a child succeeds, it moves to the next one. If any child fails, the sequence fails immediately. Think of it as an AND gate — all children must succeed for the sequence to succeed.

Example sequence: Check door is reachable → Navigate to door → Open door. If navigation fails, the whole sequence fails without trying to open the door.

Selector (?) tries children left to right until one succeeds. If a child fails, it tries the next one. If any child succeeds, the selector succeeds. Think of it as an OR gate — at least one child must succeed.

Example selector: Try to open door with handle → Try to push door → Ask human for help. The robot tries the easiest approach first and falls back.

Parallel runs multiple children simultaneously. It succeeds or fails based on a policy — for example, succeed when all children succeed, or succeed when at least one succeeds.

Decorator Nodes

Decorators wrap a single child and modify its behavior:

  • Inverter: Flips SUCCESS to FAILURE and vice versa
  • Repeat: Runs the child N times or until failure
  • Timeout: Forces FAILURE if the child takes too long
  • Retry: Re-ticks a failed child up to N times

How Ticking Works

Every tick starts at the root and flows downward. A sequence node ticks its first child. If that child returns SUCCESS, it ticks the second child. If the second returns RUNNING, the sequence also returns RUNNING. On the next tick, the sequence remembers where it left off and starts by re-ticking the child that was RUNNING.

This “memory” behavior is crucial. A robot navigating to a location returns RUNNING for hundreds of ticks until it arrives. The parent sequence waits patiently, and the rest of the tree above it can continue checking higher-priority interrupts.

Priority and Reactivity

Behavior trees naturally support priority through the order of children in selectors and the tree’s top-down traversal:

Selector
├── Sequence: Emergency Response
│   ├── Condition: Battery critically low?
│   └── Action: Return to charger
├── Sequence: Main Task
│   ├── Condition: Have task assignment?
│   └── Action: Execute task
└── Action: Idle patrol

If battery drops critically low, the emergency sequence activates on the next tick, preempting whatever the robot was doing. This happens naturally because the emergency branch is checked first.

Behavior Trees vs. State Machines

Finite State Machines (FSMs) are the traditional alternative. Key differences:

FeatureBehavior TreesState Machines
Adding behaviorAdd a branch (no rewiring)Add state + transitions (may rewire many)
ReadabilityTree structure, top-downGraph, can become spaghetti
ReactivityBuilt-in (priority through tree order)Must add explicit interrupt transitions
ReusabilitySubtrees can be copy-pastedStates are tightly coupled
ScalabilityHandles 100+ behaviors wellBecomes unmanageable past 20-30 states

State machines excel for simple systems with few states. Behavior trees win when complexity grows, which is why robotics has largely migrated to them.

Python Libraries

py_trees is the most popular Python behavior tree library. It provides all standard node types, visualization tools, and ROS 2 integration. It was developed for real robot systems at the Australian Centre for Robotic Vision.

behaviortree.cpp (BehaviorTree.CPP) is the C++ standard, used by the ROS 2 Navigation stack (Nav2). It has Python bindings for configuration but runs in C++ for performance.

Common Misconception

“Behavior trees are just fancy if-else chains.” The key difference is modularity and the RUNNING status. If-else logic executes once and makes a decision. A behavior tree ticks continuously, allowing long-running actions (navigate for 30 seconds), reactive interrupts (stop immediately if obstacle detected), and composable sub-behaviors that can be developed and tested independently. The continuous ticking model is fundamentally different from one-shot conditional logic.

One thing to remember: Behavior trees organize robot decisions into composable sequences (do these things in order) and selectors (try these options until one works), with the tree structure naturally encoding priority and enabling reactive switching between behaviors.

pythonbehavior-treesroboticsdecision-makingai

See Also