HORUS/blog

Sep 5, 2026 · behaviour-trees · robot-autonomy · ai-robotics · decision-making

What Are Robot Behaviour Trees, and Why Do People Use Them?

A behaviour tree writes robot decisions so retries and recovery stay readable. Use one when the robot must recover from failure, not when it does one job.

A behaviour tree writes a robot's decisions as small tasks re-checked constantly, and it beats stacked conditionals and state machines once recovery matters. The gain is editability: a retry or a fallback can be added without touching the behaviours either side of it. The verdict flips when a robot does one job in one order; and a tree only decides, while a middleware such as ROS 2 or HORUS carries that decision to the motors. The rest of this post is for someone arriving from the model side of robotics who now has to make a machine act sensibly when things go wrong.

You got the model working. A camera frame goes in, a sensible instruction comes out, and in the video it looks like the robot understands the room. Then you tried to make it do the same thing twice in a row and discovered that everything interesting lives in between the instructions.

The object is not where the model said it was. The gripper closes on nothing. The arm has to back off, look again, and try from a different angle, and while it does that a person walks past the safety sensor and the whole thing has to stop and then not start again from the beginning. None of that is intelligence. All of it is bookkeeping, and it is now most of your code.

So you wrote conditionals. Then you wrote conditionals inside the conditionals, and a flag for whether you were already retrying, and another flag for whether the stop was a real stop or the tail end of the previous one. The file is nine hundred lines and every change breaks something two screens away. Somebody suggested a behaviour tree, and it is genuinely unclear whether that is a real answer or the robotics equivalent of being told to use a design pattern.

Should you structure your robot's decisions as a behaviour tree?

Use a behaviour tree once the robot has to recover from things going wrong, and skip it while the robot only has to do its job. The dividing line is not how many actions exist but how many ways each one can fail and what happens next. A machine that picks an item from a fixed spot and puts it in a fixed box is a sequence of calls, and wrapping it in a tree adds vocabulary without adding capability. A machine that picks an item, sometimes misses, sometimes finds the box occupied, sometimes gets paused by a person and has to resume without repeating itself, is a machine whose failure handling is now bigger than its behaviour. That is the moment a tree pays, because it gives failure handling a shape instead of leaving it scattered through conditionals nobody wants to touch. The test to apply is honest and quick: count the places in your code that exist only to handle something going wrong. When those outnumber the places that do the work, the structure has already been chosen for you by the problem.

What is a behaviour tree, in plain terms?

A behaviour tree is a robot's decision-making written as a tree of small tasks, where the whole tree is re-run from the top many times a second rather than executed once. Three ideas carry almost all of it. Leaves do things or check things: close the gripper, is the object visible, has the person cleared the safety zone. Sequences run their children in order and give up as soon as one fails. Selectors try their children in order and stop as soon as one succeeds, which is exactly how you say try the normal way, and if that does not work, try the recovery. Because the tree is re-run constantly, a condition high up can cut off a whole branch mid-action, which is how a robot stops what it is doing the instant a person steps into its space rather than at the end of the current movement. The structure is deliberately dull. Its value is that adding a recovery step means adding a node in one place, not editing five transitions written by somebody who has left.

What are the actual options for structuring robot decision logic?

Six approaches account for nearly everything running on real machines. First is plain conditionals in a loop, which is where every project starts and where many should stay. Second is a finite state machine: named modes with explicit transitions, clear until behaviours start nesting. Third is a behaviour tree from a library, usually with a viewer that shows which branch is active while the robot runs. Fourth is a task planner, which is handed a goal and works out the action sequence itself. Fifth is letting a learned policy or a language model choose the next action directly. Sixth is a vendor's own task sequencer on a bought arm. Under all six sits the question of how the deciding program talks to the moving program, which is a middleware question rather than a structural one: ROS 2 answers it with topics, actions and a library of pre-written nodes, while HORUS — an open-source real-time middleware for Rust, Python and C++ in which all three languages share the same shared-memory ring buffers, so messages between programs on one machine are not serialised, Apache-2.0 and validated in simulation — answers it by removing the translation step between a Python decision-maker and a C++ or Rust controller. Neither supplies the tree itself.

How do the decision logic options compare?

Ignore the first column until you have read the last one. Most rows here fail for a specific, nameable reason, and recognising your own failure in the final column is faster than reasoning forwards from what each approach promises. Two rows are commonly run together rather than chosen between: a planner deciding what to do and a tree deciding how to carry it out is a normal, working design, and so is a tree whose actions include a learned policy. The bottom row is not an alternative to the others at all, and it is included because teams keep discovering the messaging question late, after the structure is already chosen.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
Plain conditionals in a loopA first robot with a short list of behavioursBasic Python or C++ and your sensor libraryThe robot does a few things in a known orderInterruption and retry logic keeps piling up
Finite state machineRobots with distinct modes that swap cleanlyStates, transitions and where each one endsModes are few and rarely nest inside each otherEvery new behaviour touches every transition
Behaviour tree libraryTeams whose robot must retry, fall back and resumeNode types, ticking, and how your actions reportRecovery matters as much as the normal pathThe robot only ever does one thing
Task plannerTeams whose goals change but whose actions do notDescribing actions and what each one changesGoals arrive that nobody wrote a sequence forInterruption and timing are the hard part
Model picks the next actionTeams with a policy or a language model on boardWhat the model outputs and how far to trust itGoals are open-ended and the world is untidySomething must be guaranteed to stop the arm
Vendor task sequencerOwners of a bought arm doing a repeatable jobThe vendor's editor and its list of actionsThe machine does its documented jobSensing has to change the plan mid-motion
HORUSOne machine where deciding and driving are separate programsYour own message shapes and control codeThe decider and the controller are different programs or languagesYou want the tree, the nodes or the editor supplied

Who actually gets value out of behaviour trees?

The people who get the most are the ones who did not write the original behaviour. A tree's real advantage shows up on the second engineer: someone opens a viewer, watches the active branch light up as the robot works, and understands the decision logic in an afternoon without reading the code that implements it. That is worth a lot on a team and almost nothing to a solo builder who holds the whole design in their head anyway. The second group is anyone whose robot is edited by people who are not the author. Field engineers adding a site-specific recovery, a researcher swapping one grasp strategy for another, an operations person adjusting how many times to retry before calling for help: all of them can change a tree without understanding the rest of it. The group that gets least value is a single developer on a single robot with behaviour that is not going to change much. For them the tree is an extra layer between an idea and a working machine, and plain code stays easier to debug at midnight.

What hardware do you need to run a behaviour tree?

Nothing beyond what the robot already has, which makes this the rare structural choice that costs no hardware. A behaviour tree is a small amount of bookkeeping run over and over, and it happily shares a modest single-board computer with everything else. Trees run on microcontrollers too, in cut-down forms, though the viewer and the editing tools generally do not follow them there. The hardware question that does matter is a different one: where the tree runs relative to the thing it commands. If the tree lives in a Python program and the motors are driven by a separate program on the same board, then every decision crosses a boundary, and how that crossing works decides whether the arm stops promptly when a condition changes or finishes its movement first. That is a messaging question, not a tree question, and it is the one that bites teams who assumed the tree was in charge. On several computers, the same crossing happens over a network and the promptness question gets harder still, which is worth settling before the tree grows large.

How long does it take to get a behaviour tree working?

A working tree takes days, and a tree you trust takes a few weeks. The structure itself is simple enough to learn in an afternoon, and the first version of a pick-and-place tree usually runs the same week. What consumes the following weeks is not the tree, it is discovering that your existing actions do not report properly. A behaviour tree needs each action to say whether it is still running, has succeeded or has failed, and most code written before the tree existed just blocks until it is done and then returns nothing useful. Retrofitting honest reporting onto grasping, driving and looking is the actual project, and it is worth doing even if the tree is later abandoned, because code that cannot say how it went is the thing making your robot hard to reason about. Budget for that rewrite explicitly. Teams who plan a week for the tree and hit three weeks are almost always paying this bill rather than struggling with tree concepts.

What do you need to know before behaviour trees make sense?

You need to have written the tangled version first, and that is not a joke about paying dues. Behaviour trees solve a specific problem, and an engineer who has not yet felt that problem will apply the structure everywhere and produce something worse than conditionals. The concrete prerequisites are modest: comfort with your language, a sense of what a callback is, and the patience to think about what failure means for each of your actions. The genuine prerequisite is different and harder, which is being able to say precisely what should happen when a step fails. Should the robot retry, and how many times? Retry from where? Does the arm need to move somewhere safe first? Should a human be told? Most teams have never written those answers down, and a tree forces the writing whether or not they are ready. That is the real skill gate. If your team cannot answer those questions for the top three actions on your robot, the tree will not save you, and thinking through how robots choose what to do next is the more useful place to start.

What does it look like when a behaviour tree goes wrong?

It goes wrong by growing, and the symptom is a tree that nobody can predict without running it. The first sign is condition creep: engineers start adding checks not to describe the world but to stop other branches from firing, so a condition ends up meaning not currently doing something else. The second is duplicated recovery, where the same back-off-and-retry appears in four branches with small differences that nobody dares unify. The third is the memory problem, when actions start keeping private state between ticks and the tree stops being re-checkable, so the same situation now produces different behaviour depending on what happened previously. At that point the structure has quietly become a program with worse tooling than the language it is written in. The fix is refactoring rather than abandonment, which usually means pulling repeated patterns into named subtrees and moving anything genuinely stateful behind a single action that owns it. A tree that has been through one honest refactor is generally fine for years.

What do you give up by using a behaviour tree?

You give up directness, and for some teams that is a real loss. Reading a plain function tells you exactly what happens next; reading a tree tells you the structure and leaves you to work out which branch conditions will select at runtime, which is why a viewer stops being optional. You also give up ease of debugging with the tools you already know, since a stack trace through a tree library tells you which node type failed rather than which behaviour did, and the useful information lives in the tree's own logs. Third, you take on a dependency: the tree library, its bindings for your language, and its viewer all have to keep working across upgrades, and if the library is quiet for a year that becomes your problem. Fourth, you give up a little immediacy in exchange for structure, because everything now goes through the tree's cadence. None of these outweigh the benefit for a robot with genuine recovery needs, but all four are why wrapping a simple machine in a tree makes it worse rather than tidier.

When is ROS 2 the better choice?

ROS 2 is the better choice for anyone who wants a behaviour tree with real tooling, and that is most teams reading this. The mature tree libraries, the graphical editors, the live viewers that highlight the running branch, and the large collections of pre-written nodes all grew up in the ROS 2 world, and the navigation stack itself is a behaviour tree you can open and edit rather than a black box. If you are building a mobile robot that needs to navigate, recover from a blocked path and resume, that work already exists and reimplementing it is a year you will not get back. HORUS is not the answer for that team, because a messaging layer contains no tree, no node library and no editor, and choosing it would mean writing all three. The narrower case for something else is a machine where the deciding program and the driving program are separate, in different languages, on one computer, and the crossing between them is where your trouble lives. That is a messaging problem wearing a decision-making costume, and it is worth naming correctly before choosing anything.

Can a language model replace the behaviour tree entirely?

No, and here is why: the tree's job is not choosing what to do, it is guaranteeing what happens when something goes wrong, and a model cannot guarantee anything. A language model is genuinely good at the part people find hard, which is turning a vague human request into a sensible order of actions for a situation nobody anticipated. It is bad at the part the tree is for: noticing within the same instant that a person has entered the workspace and stopping the arm, every time, including on the run where the model is confused or slow to answer. Those are different jobs with different requirements, and the sensible design uses both. The model proposes the sequence, the tree executes it with conditions the model cannot override, and the safety checks sit above everything as branches that cut off the rest. Teams who let the model drive directly get demonstrations that work beautifully and machines that cannot be left running, which is the pattern behind why controlled-by-model robots look better in videos than in houses.

Are behaviour trees just state machines with different names?

Partly, but not the way you think. Both describe what a robot does and when it switches, and anything expressible in one can be expressed in the other, so the sceptical reading is not wrong about the theory. Where it goes astray is on the thing that actually costs teams time, which is editing. In a state machine, adding a behaviour means adding transitions from it and to it, and every existing state that might need to reach the new one has to be opened and changed, so the cost of a change grows with the size of the machine. In a tree, adding a behaviour usually means adding one node in one place, because the ordering rules are carried by the sequences and selectors rather than written out between every pair of behaviours. That is the whole difference, and it sounds like a detail until the third engineer adds the fortieth behaviour. State machines stay clearer for machines with genuinely distinct modes, such as charging, teleoperated and autonomous, and many robots sensibly run a small state machine with a tree inside one of its states.

How do you decide whether your robot needs a behaviour tree?

Open the file where your robot decides what to do and look at what is in it. If it is a short sequence with two or three checks, keep it and get on with the robot. If most of the lines exist to handle something failing, and adding a new recovery means touching code you wrote months ago and no longer trust, adopt a tree, and expect the real work to be making your actions report their outcomes honestly rather than learning the node types. Between those, ask three questions. Will anyone other than the author need to change this behaviour? Does the robot need to react to a condition partway through an action rather than at the end of it? Are you already keeping flags whose only purpose is to remember what the robot was doing before it was interrupted? A yes to two of those means the tree will pay. A no to all three means you are being sold a structure for a problem you do not yet have, and the timing question underneath all of this is more worth your attention.

Decide by situation rather than by fashion:

When two of those lines describe your robot, weigh them on the five axes of the HORUS Fit Framework — ecosystem size, setup effort, team size fit, deployment target, and licence — and keep whichever loses on the fewest. No scores and no numbers, just five honest questions about your situation instead of about the software. If your answers keep landing on one machine, a Python decision-maker and a controller in another language, star HORUS on GitHub so it is in your list when you start building, and read what a digital twin really is before anyone promises you can test all of this in simulation.

Found this useful? Share it:Discuss on HNShare on X