Introduction

Behavior trees are hierarchical models for decision-making in autonomous systems. They organize logic as a tree of modular tasks, enabling reactive, reusable behaviors that adapt to changing environments. Originally developed for game AI, behavior trees now power robotics, drone autonomy, trading systems, and simulation agents.

This post is for developers, engineers, and hobbyists building intelligent autonomous systems. We compare behavior trees to finite state machines (FSMs), a common but often rigid approach. We demonstrate their scalability and reactivity using a drone mission example.

Finite State Machines

Finite state machines (FSMs) model behavior through discrete states. An agent occupies one state at a time and transitions between states based on inputs or events.

Consider a simple drone mission:

  ---
title: Drone Mission FSM
---
stateDiagram-v2
    direction LR
    
    fly : Fly To Target
    capture : Capture Image
    return : Return Home

    fly --> capture : target reached
    capture --> return : image captured

This looks straightforward. However, FSMs introduce several limitations as complexity grows:

  • Rigid state coupling. Each state explicitly defines its transitions to other states. This tight coupling makes it difficult to reuse behaviors. For example, reusing Fly to Target in a new mission requires redefining all its transitions because they’re hardcoded to the original context.

  • Repeated logic for shared rules. Safety checks like collision avoidance must be embedded in multiple states, leading to duplicated code. Adding traffic detection requires implementing checks in every relevant state, increasing error risk.

  • Rapidly growing complexity. Adding a new state, like Avoid Traffic now requires transitions to and from existing states (e.g., Fly to Target, Capture Image). Adding traffic avoidance across just three states creates six transitions, an early sign of unmanageable complexity.

These challenges make FSMs rigid and difficult to scale, especially in domains like aerial robotics where safety and adaptability are critical. The diagram below shows how adding traffic detection complicates the design:

  ---
title: Drone Mission FSM with Traffic Detection Complexity
---
stateDiagram-v2
    direction LR

    fly : Fly To Target
    capture : Capture Image
    return : Return Home
    avoid : Avoid Traffic

    fly --> capture : target reached
    capture --> return : image captured

    fly --> avoid : traffic detected
    avoid --> fly : traffic cleared
    capture --> avoid : traffic detected
    avoid --> capture : traffic cleared
    return --> avoid : traffic detected
    avoid --> return : traffic cleared

Each state now requires transitions to handle traffic detection and avoidance, duplicating logic and making the FSM harder to modify or debug. This highlights how cross-cutting concerns like traffic detection increase complexity and transition count.

Behavior Trees

Behavior trees organize tasks hierarchically, providing a modular and reactive alternative to finite state machines. Each node represents either a condition or an action, with control flow determining which branches execute based on success or failure.

Here’s our drone mission implemented as a behavior tree, with traffic detection and avoidance as a top-level concern:

  ---
title: Drone Mission Behavior Tree
---
graph TD
    root[Sequence]

    avoid_sel[Selector]
    check_traffic(Clear of Traffic)
    avoid_traffic[Avoid Traffic]

    main_seq[Sequence]
    
    fly(Fly to Target)
    capture(Capture Image)
    return(Return Home)

    root --> avoid_sel
    avoid_sel --> check_traffic
    avoid_sel --> avoid_traffic

    root --> main_seq

    main_seq --> fly
    main_seq --> capture
    main_seq --> return

This structure cleanly separates reactive safety logic from core mission flow. At each tick (evaluation cycle), the tree executes from the root:

  1. The root Sequence node processes children in order, requiring all to succeed
  2. The traffic avoidance Selector first checks if airspace is clear; if not, it activates the avoidance routine
  3. Once traffic concerns are resolved, the main mission sequence executes: fly to target, capture an image, return home

The ticking mechanism, where the tree is evaluated periodically to respond to new inputs, enables immediate response to sensor input changes, like detecting mid-flight traffic, without embedding checks in every state as FSMs would require.

Behavior Tree Node Types

Each node returns one of three statuses:

  • Success: the task completed successfully
  • Running: the task is still in progress
  • Failure: the task could not complete

These statuses enable adaptive control, allowing behavior trees to retry, skip, or halt tasks based on current conditions. There are three core node types:

  • Composite Nodes control the flow:
    • Selector: Tries each child in order. Returns Success or Running as soon as one succeeds or is still running. Fails only if all children fail. Great for fallback logic.
    • Sequence: Runs each child in order. Fails or returns Running if any child does. Succeeds only if all children succeed. Ideal for ordered tasks like Fly → Capture → Return.
  • Decorator Nodes modify the behavior of a single child:
    • For example, a Timeout decorator might fail if Fly To Target takes too long, ensuring the mission doesn’t get stuck.
  • Leaf Nodes perform the actual work:
    • Action nodes (e.g., Fly To Target, Capture Image) carry out tasks.
    • Condition nodes (e.g., Check Traffic, Battery OK) evaluate system state. These return Success or Failure instantly, never Running.

This structure makes behavior trees easy to understand, extend, and test. You can add new behavior branches or reuse existing nodes without reworking unrelated logic.

Making Reactivity Explicit

The initial tree structure is simple, but it hides complexity within node logic. Because of the tree’s reactive nature, nodes in the sequence may be reevaluated even after succeeding. For example, the Capture Image node needs to check whether an image has already been captured before attempting to capture one.

These constraints can be made explicit in the tree structure, resulting in more cohesive and composable nodes. Now that we have a better understanding of node types, let’s restructure our original tree:

  ---
title: Refactored Drone Mission Behavior Tree
---
graph TD
    root[Sequence]

    avoid_sel[Selector]
    check_traffic(Clear of Traffic)
    avoid_traffic[Avoid Traffic]

    main_seq[Sequence]

    fly_sel[Selector]
    fly_check(At Position? : target)
    fly_action[Follow Waypoints : target]

    capture_sel[Selector]
    capture_check(Image Captured?)
    capture_action[Capture Image]

    return_sel[Selector]
    return_check(At Position? : home)
    return_action[Follow Waypoints : home]

    root --> avoid_sel
    avoid_sel --> check_traffic
    avoid_sel --> avoid_traffic

    root --> main_seq

    main_seq --> fly_sel
    fly_sel --> fly_check
    fly_sel --> fly_action

    main_seq --> capture_sel
    capture_sel --> capture_check
    capture_sel --> capture_action

    main_seq --> return_sel
    return_sel --> return_check
    return_sel --> return_action

This refined tree is more verbose, but its reactive nature is now explicit. Each mission step follows a pattern:

  1. First check if the step is already complete (condition node).
  2. If not, perform the action (action node).

We’ve also simplified the implementation of individual nodes and introduced reusable behaviors for checking positions and following waypoints. This approach makes the tree more modular and testable. Each condition-action pair can be unit tested independently, and common behaviors can be reused across different missions without duplicating logic.

Conclusion

Behavior trees offer a modular, reactive, and scalable alternative to finite state machines. Their structure and ticking model simplify complex tasks like navigation and safety enforcement, avoiding the rigidity and duplication often found in FSMs. While behavior trees may incur higher computational costs due to frequent ticking, they shine in safety-critical domains like aerial robotics, where responsiveness is paramount.

In some systems, combining behavior trees with FSMs can balance reactivity and efficiency. For example, an FSM might trigger a behavior tree to manage a local task like obstacle avoidance or landing.

Try behavior trees in your next autonomous system project to streamline decision-making. In future posts, we’ll implement them in code and integrate them into flight control loops.