# Behavior trees

> Learn how NOVA Flow programs are structured and how data flows through a behavior tree.

## Definition

Compared to regular code blocks, a behavior tree is a visual programming model that represents the logic of a robot application as a tree of blocks.
Each block represents a node in the tree, and the connections between them represent the flow of execution.

This structure allows for clear visualization of the program's logic and makes it easier to understand and modify.

## Advantages

Most visual programming tools model programs as state machines that represent states connected by transition arrows.
State machines are suitable for small programs, but don't adapt well to additions and scaling. Any additional situation like a missing workpiece or
an unexpected event adds transitions from many existing states. The previously small diagram quickly becomes complex and hard to read.

Behavior trees, on the other hand, are modular and composable. They have three key properties that make them suitable for complex robot applications:

- Reading the behavior tree top-down, left-to-right tells you
  what happens in what order.
- Sub-trees are self-contained allowing for modular reusage, e.g., Get a workpiece part
  can get tested and reused on its own without requiring context of its new target within the tree.
- Reactions do not need extra wiring. Reactive nodes re-check their
  conditions continuously, e.g., canceling an action triggered by an event is considered a property of the tree structure.

To enable Wandelbots NOVA users to create programs that operate in changing environments,
NOVA Flow uses behavior trees as the underlying programming model.

## Execution model

The execution engine drives the tree by sending a tick into the root node many times per
second. The tick travels down the
tree, and each node answers with one of three responses:

| Response | Meaning |
| - | - |
| `SUCCESS` | The node finished and achieved its goal. |
| `FAILURE` | The node finished and did not achieve its goal. |
| `RUNNING` | The node is still busy, ask again on the next tick. |

Ticks keep flowing while actions run, the tree notices changes and stops cleanly at any moment.
This clean behavior is enabled by action nodes that start a motion once, then answer `RUNNING` on everey following tick until the motion is done.

### Role of parent nodes

Parent nodes decide what to do with a child's answer, e.g.,

- A `sequence` moves onto the next child when it receives `SUCCESS`
- A `selector` moves onto the next child when it receives `FAILURE`

## Node types

Every behavior tree is built from three node types: composites, decorators, and leaves.

**Composites**

### Composites

Composites control the order. They have multiple children and decide which ones run.

| Composite | Behavior | Use case |
| - | - | - |
| Sequence | runs children left to right, stop at first `FAILURE`, read as "do A, then B, then C", can be plain or reactive | sequential logic |
| Selector | tries children left to right first `SUCCESS`, read as "try A; if that fails, try B.", can be plain or reactive | fallback strategies |
| Parallel | runs children at the same time, configurable success/failure thresholds | concurrent execution |

#### Reactive/plain

Reactive and plain composites differ in how they handle running children.

- Plain sequences and selectors remember which child is running and resume there upon the next tick.
  Earlier children are not re-checked.

- Reactive variants of these composites re-checks upon every tick starting with their first child.
  If an earlier child changes its answer, the running child is preempted and the branch re-evaluates from the top.

| Composite | Behavior | Use case |
| - | - | - |
| Plain | guards are checked **once on branch entry**, running children **always finishes or fails** | for steps that must run to completion once started, e.g., placing motions |
| Reactive | guards are checked **on every tick**, running children **are preempted when earlier child changes its answer** | changing signals must be able to interrupt the branch, e.g., safety zone triggers, work piece disappears |

**Decorators**

### Decorators

Decorators have one child and change how it behaves.

| Decorator | Behavior | Use case |
| - | - | - |
| Repeat | repeats the child a specified number of times | looping |
| While | repeats the child while a condition holds | conditional looping |
| Retry | retries a failing child a specified number of times | error handling |
| ForEach | runs the child once for each item in a list | iterating over collections |
| Timeout | cancel the child if it takes too long | time management |
| Delay | waits before starting the child | timing control |
| Inverter | swaps `SUCCESS` and `FAILURE` | negation |

**Leaves**

### Leaves

Leaves have no children. They are the execution points of the tree, triggering the actual action or checking a condition.

| Leave | Behavior |
| - | - |
| Action node | calls operation on action service, e..g, move the robot, read this input, reports `RUNNING` while action is running |
| Condition node | evaluates a condition and reports `SUCCESS` or `FAILURE`, never reports `RUNNING` |

## Data transfer

NOVA Flow provides a run-based and a persistent data store for nodes to share information.
The run-based store is called the blackboard, and the persistent values are called globals.

### Blackboard

Nodes share data in a key-value store during a run. This store is called the blackboard.

A vision action writes the detected pose to the blackboard, and the following motion action reads it from there.
Node inputs are references to this store, therefore outputs can be wired to inputs without writing code.

As an example, when building a flow against Isaac Sim, `GetPose` and `GetRelativePose` both write a pose to the blackboard, but in different frames:

- `GetPose` returns a prim's pose in the Isaac Sim world frame; use it only when a single robot sits at and is aligned with the world origin,
  since otherwise the world coordinates don't match the robot's reference frame.
- `GetRelativePose` returns one prim's pose relative to another, e.g., a box's pose relative to a robot by using the robot's root prim as the reference.
  Either output can be wired directly into a `MotionRoutine` node's target input.

### Globals

For values that must survive a run, e.g., counters, calibration poses, NOVA Flow
provides a persistent store that trees read and write through
dedicated nodes. These persistent values are called globals.

## End a run

A run is considered finished when the root node returns one of three final states.

- `SUCCESS`: The tree reached its goal.
- `FAILURE`: An unknown error broke the tree, and the tree couldn't recover from it.
- `HALTED`: A deliberate action was triggered by someone to request termination, e.g., the operator
  pressed **Stop**. When a halt occurs, running actions can stop cleanly and release their resources before the run ends.

## Example

The scenario: a robot places parts into a box. When the box is full, parts
go into an overflow box instead.

**in NOVA Flow**

![A running behavior tree in the NOVA Flow editor: a root Sequence over a Selector with two child Sequences, Place in Box (guarded by a Box empty condition) and Place in Overflow](/assets/developer/nova-flow/box-overflow-tree.png)

**Text tree**

As a text tree:

```text
Sequence
└── Selector — try children until one succeeds
    ├── Sequence "Place in Box"
    │   ├── Condition     "Box empty"          (box sensor)
    │   ├── MotionRoutine "move to pick"
    │   ├── MotionRoutine "place in box"
    │   └── MotionRoutine "return home"
    └── Sequence "Place in Overflow"
        ├── MotionRoutine "move to pick"
        ├── MotionRoutine "place in overflow"
        └── MotionRoutine "return home"
```

1. Both inner sequences contain the main information. `Place in Box` reads top-to-bottom: check the box → move to the pick position
   → place the part into the box → return to the home position. Each step must return `SUCCESS` before the next one starts.
   If one step returns `FAILURE`, the sequence ends with `FAILURE`.
2. The condition `Box empty` is the guard for the `Place in Box` sequence. It asks the box sensor and answers instantly:
   `SUCCESS` (box is empty) or `FAILURE` (box is full). Because it sits first in a sequence, a full box makes the \`Place in Box\`\`
   branch fail before the robot moves.
3. The selector determines that upon failure the second branch `Place in overflow` is run.
   If the first branch ends its run with `SUCCESS` the fallback is not required.

The guard and the first motion show a green check (`SUCCESS`), "Place in box" is marked
yellow (`RUNNING`) which means that the tick is inside that node and following nodes haven't been reached.

How do the runs differ between a plain and a reactive selector? In the example above, the selector is plain.

In this example, `Box empty` is evaluated **once** when `Place in Box` starts.
If the box becomes full while `Place in box` is running, the plain selector doesn't react since the check has already passed,
and the running motion completes.

Made reactive, the example asks `Box empty` many times per second. Once the sensor flips:

- the guard fails
- the running motion stops with `HALTED`
- the branch fails

and the selector switches `Place in Overflow` mid-run.

## Best practices

1. Guard branches with a condition as the first child and wrap it in a sequence that is wrapped in a selector.\
   The guard decides whether the branch applies, the selector picks the first applicable branch. Prefer it
   over encoding decisions inside actions.
2. Order selector children by priority.\
   The leftmost child is tried first. Put the preferred strategy first and the fallbacks after it, in
   the order you'd want them attempted.
3. Keep conditions instant and without side effects.\
   Time-consuming operations or changes to the world belong in action nodes.
4. Always use plain composites first, and apply reactive only where necessary.\
   Reach for reactive variants only where a mid-run change must preempt the
   running step.
5. Extract repetitions into sub-trees to keep modularity.\
   The example displays a good use case as both branches share the same move-place-return pattern.
