Behaviour and Decision-Making
Robotics Fundamentals
Course 1 · Chapter 8 · Behaviour and Decision-Making
Robotics Fundamentals 7 ended with controllers that make a robot do exactly what it is told: hold a speed, hold a heading, follow a line. This chapter is about what it is told. Something has to decide that now is the time to follow the line, that a wall in front overrides the line, that the battery is low and the dock matters more than the floor, and that the robot has been stuck for ten seconds and should try something else. That something is the robot's behaviour layer, the “think” of the sense-think-act loop of Robotics Fundamentals 1. We start with the cheapest kind of decision, a reactive behaviour that maps sensors straight to motors, and with arbitration, the rule for choosing between behaviours that disagree. Then we build the main tool for organising behaviours over time, the finite state machine, including how to stop a noisy sensor from making it flicker. We put numbers on the classic behaviours (line following, obstacle avoidance, wall following, homing to a dock), move on to simple planning on a grid map (occupancy grids, breadth-first search, and A* worked out by hand on a real grid), take a first look at localisation (belief, particle filters, and a worked trilateration from three beacons), and finish with behaviour trees and the unglamorous but vital subject of testing and failure handling.
The thresholds, gains, speeds, probabilities and sensor figures in this chapter are illustrative or typical values chosen so that the arithmetic is easy to follow, not data for a particular robot or sensor. The formulas (differential-drive wheel speeds, the repulsive potential, the log-odds update, breadth-first search, A* with the Manhattan heuristic, the particle weight and the trilateration equations) are standard. Every number in the worked examples was recomputed, and the search examples were actually run on the grids shown. The code is deliberately short, hardware-neutral Python-style pseudocode, used only for an arbitration rule, a state machine and A*; real board code is the job of Programming for Robotics.
1. Reactive behaviours
A reactive behaviour computes its output from the sensor readings of this instant, with no map of the world and no memory of the past: sense, then act. The line follower of Robotics Fundamentals 7 section 12 is one (line position in, turn rate out); so is a bumper that makes a robot back up. There is no model to get out of date, so reactive behaviours are fast (one sensor read and a little arithmetic), cheap, and robust against a changing world. Their limits are equally clear: with no memory they cannot count, remember where they have been, or pursue a goal that is out of sensor range, and two simple rules can chase each other in circles.
Braitenberg vehicles
The neuroscientist Valentino Braitenberg described a series of imaginary vehicles in which a few wires from sensors to wheels produce behaviour that looks purposeful. They are the cleanest picture of a reactive robot: the “program” is only the wiring. Take two light sensors (reading 0 to 1, larger meaning brighter) and two wheel motors, each wheel speed set by v = b + g × sensor, where b is a base speed and g a gain.
Real robots use the same idea with ordinary distance sensors: two forward-angled ultrasonic or infrared sensors steer a robot away from whichever side has the nearer obstacle: the nearer the obstacle on the left, the faster the left wheel is driven, so the robot swings right (the “uncrossed” wiring, with a reading of nearness in place of brightness). The weakness is the same too: a reactive vehicle in a dead-end corridor sees the same left and right readings, turns neither way, and drives into the end. Something with a little memory or a little goal has to take over, which is what the rest of the chapter provides.
Arbitration: when behaviours disagree
A real robot has several reactive behaviours at once (follow the line, avoid obstacles, stop if the bumper is hit), and at any moment they may ask for different things. Rodney Brooks's subsumption architecture (1980s) organises them in layers of priority, where a higher layer can suppress (override) the output of those below it. The simplest practical form is priority arbitration: list the behaviours from the most to the least important, ask each in turn, and obey the first one that has something to say. Safety behaviours sit at the top; the pleasant, goal-seeking behaviours sit below; a default behaviour at the bottom always has an answer.
The alternative to winner-takes-all is to blend the commands (a weighted average, or the vector sum used by potential fields in section 3). Blending gives smooth, graded behaviour, but averages can be worse than either input: if AVOID says turn left at +1.0 rad/s and a goal behaviour says turn right at −1.0 rad/s, the average is 0.0 and the robot drives straight into the obstacle between them. Use priorities for anything safety-related, and blending only among behaviours that are all fine with each other.
2. Finite state machines
Reactive rules have no memory. Many tasks do: I am cleaning, I have been told to go home, I am charging. A finite state machine (FSM) adds exactly the memory needed: the robot is always in exactly one of a small, named set of states, and each state says what the robot is doing (which behaviours are active, which targets are set). Events (a bump, a low battery, an arrival, a timer running out) cause transitions from one state to another. Everything the machine has to remember is the name of its current state, which makes it easy to test: for every state and every event, you can write down what happens, and a missing entry is a visible gap rather than a surprise.
A worked example: a vacuum robot
Debouncing a decision: timers and hysteresis
Events come from noisy sensors, and a machine that reacts to every flicker flips between states. Robotics Fundamentals 7 section 2 met this with bang-bang control; the cure is the same, with one more tool. Three standard defences, used together:
- Require the condition to hold for a time (a debounce timer or a count of consecutive samples) before generating the event.
- Use two thresholds (hysteresis): enter a condition at one level, leave it at a different, safer level, so a reading sitting right on the line cannot toggle the machine.
- Add a minimum time in a state (a dwell time) so that a transition cannot immediately be reversed.
Avoiding state explosion
A state machine grows badly when it must track several independent things in one set of states. Suppose the robot has 4 drive modes, 3 battery conditions (ok, low, critical) and 2 payload conditions (loaded, empty). One machine that has to name every combination needs 4 × 3 × 2 = 24 states, and each new feature multiplies the count. Three small machines running side by side, one for each independent aspect, need only 4 + 3 + 2 = 9 states, and each is simple to read. A second tool is the hierarchical state machine: a state such as CLEAN contains its own sub-states (zigzag, edge cleaning, spiral), and a rule written once on the outer state (“battery_low leaves CLEAN”) covers all of them, instead of being repeated on each sub-state. The advice: one machine per independent concern, group states under a parent when they share transitions, and if the table starts to look like a plate of spaghetti, the design (not the drawing) needs another look.
3. Classic behaviours, with numbers
Line following
The controller is the PID of Robotics Fundamentals 7 section 12, fed with the weighted line position of Robotics Fundamentals 5 section 4; this section adds the decisions around it. With e = −x (x in mm, positive to the right) and u = Kp e a turn rate, wheel speeds follow from vR = v + uL/2 and vL = v − uL/2. Slowing on bends keeps the sensor array on the line: v = vmax / (1 + c |e|).
Obstacle avoidance
The simplest avoider is a three-state machine: DRIVE, TURN, and a recovery state. Use a threshold with hysteresis: start turning when the front distance falls below 0.30 m, and only go back to driving when it has risen above 0.45 m (a 0.15 m band), so the robot does not twitch at the edge of the threshold. Choose the turn direction toward the side with more room (the left and right readings, as in section 1), and keep turning in that direction until the front is clear, rather than re-deciding every loop.
The threshold itself comes from a stopping-distance budget (section 7): at 0.5 m/s the robot needs about 0.10 m to stop, so 0.30 m leaves a margin of about three. The sensor must also see that far without a blind spot (Robotics Fundamentals 5: an ultrasonic sensor has a minimum range below which it reads nonsense).
Stuck and escape recovery. A reactive avoider can trap itself: a corner or the legs of a chair make it avoid left, then right, then left. Detect the loop and break it: if there are 3 avoid events in 10 seconds (a count with a timer, like the debounce above), enter ESCAPE: reverse 0.30 m (at 0.10 m/s, 3 s), turn through a larger, randomised angle (90 to 180 degrees), and then resume. If ESCAPE has happened 3 times without progress (position has not changed by more than 0.5 m, which needs odometry from Robotics Fundamentals 6), the robot has no more tricks: it stops and reports being stuck.
Potential fields: a blended way to avoid
A potential field replaces if-then rules with forces. The goal pulls the robot with an attractive force; every obstacle within a range d0 pushes it away with a repulsive force that grows rapidly as the obstacle gets close. The robot steers along the sum. The usual repulsive force comes from a potential U = ½ k (1/d − 1/d0)2 for d < d0 (zero beyond), whose slope gives a force, pointing away from the obstacle, of magnitude:
Wall following
Following a wall (to explore a room, or to find a door) is a position-style loop like the line follower: the error is the distance to the wall minus the wanted distance, and the output is a turn rate. With the wall on the right and a sensor looking at it, if the robot is too far (e = d − dref > 0) it must turn right (clockwise, negative), so u = −Kp e. A single sensor has a flaw: it cannot tell a robot that is close and parallel from one that is far and angled toward the wall (both give the same reading, but the second is about to hit it). With two sensors a distance s apart along the robot's side, the difference gives the angle: θ = atan((dF − dR)/s), and the perpendicular distance is dperp = ((dF + dR)/2) cos θ. A positive θ means the front is farther from the wall than the rear, so the robot is heading away from it.
Homing and docking
To return to a charger, a robot needs something to find: an infrared beacon on the dock that gives the bearing β (angle to the dock, counter-clockwise positive) and a rough range r. The rule is the line follower once more: turn in proportion to the bearing, w = Kb β, and slow down as the range shrinks, v = min(vmax, kv r), so the last centimetres are covered gently enough for the contacts to meet.
4. Simple planning: grid maps and finding a path
Reactive behaviours and state machines choose what to do now. To choose how to get there when the goal is out of sight (the kitchen is down the corridor, and past the table) the robot needs a map and a planner that searches the map for a route.
Grid maps and occupancy grids
The simplest map is a grid: the floor is cut into squares of a chosen size, and each cell is free, blocked, or (before the robot has looked) unknown. World coordinates become cell indices by division: column = floor(x / cell), row = floor(y / cell). With cells of 0.25 m, the point (x = 1.30 m, y = 0.60 m) lies in column floor(5.2) = 5, row floor(2.4) = 2. The cell size is a trade-off between detail and memory: a 20 m by 20 m floor is 80 × 80 = 6,400 cells at 0.25 m but 400 × 400 = 160,000 cells at 0.05 m (at one byte per cell, 160 kB; it also takes 25 times as many cells for a planner to search). To stop the planner from steering the robot's centre next to a wall, inflate each obstacle by the robot's radius (0.15 m is 0.6 of a 0.25 m cell, so one extra cell of margin round every blocked cell), which lets the planner treat the robot as a point.
Sensors are noisy and a cell is rarely certain, so a map built from measurements stores a probability of occupancy in each cell (an occupancy grid). Every cell starts at 0.5, “don't know”. Each time a range sensor beam ends in a cell (a hit), that cell becomes more likely to be occupied; each cell the beam passes through on the way becomes more likely to be free. Updating the probabilities directly means a small multiplication-and-division for every reading. The neat trick is to store log-odds instead, where each new reading just adds a number.
Searching the grid: breadth-first search
A graph search treats each free cell as a node, joined to its 4 neighbours (up, right, down, left) by edges of cost 1. Breadth-first search (BFS) explores outward from the start in rings: first the cells 1 move away, then those 2 moves away, and so on, using a first-in, first-out queue. The first time it reaches the goal it has used the fewest moves, so for equal-cost moves it always finds a shortest path. Its weakness is that it explores in all directions, including the wrong ones. The example grid below is used for BFS and then for A*; S is the start, G the goal, # a blocked cell, and rows and columns are numbered from 0 at the top left. A cell is written (row, column).
A*: searching with a guess
A* (“A star”) keeps the shortest-path guarantee but explores mainly toward the goal. For each cell it tracks g, the cost of the best path found so far from the start, and a heuristic h, an estimate of the remaining cost to the goal. It always expands the open cell with the smallest f = g + h. On a 4-connected grid the standard heuristic is the Manhattan distance h = |row − rowgoal| + |col − colgoal| (the number of moves if there were no walls). It never overestimates the true remaining cost (admissible, and consistent), which is what guarantees that the first path found is a shortest one. With h = 0 A* becomes Dijkstra's algorithm (BFS with unequal costs); the larger the heuristic stays below the truth, the more the search is guided. If diagonal moves are allowed (cost 1.414), use the octile distance instead, or Manhattan will overestimate.
The rules used below, so that the hand-work is repeatable: pick the open cell with the lowest f; if tied, the lowest h (closer to the goal); if still tied, the one added to the open list first. For each neighbour (in the order up, right, down, left) that is not a wall: new g = g(current) + 1, and if that is better than the neighbour's recorded g (or it has none), record it and add it to the open list with its f. Goal G = (1,5), so h(r,c) = |r − 1| + |c − 5|, and h(S) = 1 + 5 = 6.
Practical notes. Path cost need not be distance only: a planner can charge more for cells near walls (inflation zones), for turns (many planners add a small penalty per change of direction, which would favour the straight run along row 3 over the staircase above), or for rough floor; A* simply minimises the total. Replanning: when the robot finds a new obstacle on the path (a chair that was not on the map), mark the cell blocked and search again from the robot's current cell, which on a small grid takes a fraction of a second; incremental methods (D* and its relatives) reuse earlier work. Path following: the planner gives a list of cells; turning it into motion is the job of the controllers of Robotics Fundamentals 7 (steer toward the next waypoint with a heading controller, speed limited near obstacles).
5. Localisation
A map is only useful if the robot knows where it is on it. Robotics Fundamentals 6 showed that dead reckoning drifts and that an absolute fix pulls the error back. Localisation is the art of combining the two: the robot keeps a belief, a probability distribution over where it might be (“probably near (2.1, 1.0), within about 0.2 m”), and repeats two steps. Predict: as the wheels turn, move the belief along with the odometry, and make it wider, because every movement adds uncertainty. Update: when a sensor reading arrives (a distance to a wall, a beacon range, a landmark), keep the possible positions that agree with it and discard those that do not, making the belief narrower. The Kalman filter of Robotics Fundamentals 6 section 5 is this loop for a single bell-curve belief. When the belief is not a single bump (the robot could be in either of two identical corridors), something more flexible is needed.
The particle filter, conceptually
A particle filter represents the belief as a cloud of guesses (particles), each a complete possible pose, often several hundred to thousands (a typical range, depending on the map). In each loop: 1. Move every particle by the odometry reading plus some random noise, so the cloud spreads like the uncertainty. 2. Weight each particle by how well the real sensor reading matches what that particle would read from its own pose on the map; a particle that agrees gets a high weight, one that disagrees a weight near zero. 3. Resample: draw a new cloud, picking particles in proportion to their weights, so that good guesses are copied and bad ones die out. The cloud collapses onto the poses that fit the evidence, and the robot's best estimate is the cloud's average (if it is one group). A cloud that starts spread evenly over the whole map can find the robot's place without being told, which dead reckoning cannot do (“the kidnapped robot” problem).
Trilateration from beacons
With range beacons at known places (Robotics Fundamentals 6 section 8) the robot can compute a position directly. Each measured range di puts the robot on a circle around beacon i. Two circles cross at two points; the third circle picks one. Subtracting the circle equations pairwise cancels the squared terms and leaves two straight-line equations. For beacon A at the origin, B on the x axis at (b, 0) and C on the y axis at (0, c), the solution has a closed form:
SLAM, in concept only
SLAM (simultaneous localisation and mapping) is the problem of building a map and locating the robot on it at the same time, with neither given in advance. It is a chicken-and-egg problem: to add what the sensor sees to the map, you need to know where the robot is; to know where the robot is, you need the map. The solution, in outline: the robot moves and measures, estimates its pose from odometry, matches each new scan against the map so far (or against landmarks it has seen before), and corrects both the map and its pose estimate together. Its hardest moment is loop closure: coming back to a place already seen, recognising it, and pulling the whole drifted trail back into line. Common forms include filters (Kalman, particle) and graph SLAM, which keeps all the poses and measurements as a network of constraints and finds the arrangement that fits them best. SLAM with a lidar is what many floor robots use; it is implemented in ready-made software packages, and building one from scratch is far beyond this course. What matters here is why it exists and what it needs: good range data, a sensible odometry, and a world that holds still.
6. Behaviour trees, briefly
A state machine says where the robot is and what causes it to move on. A behaviour tree says what it should try. The tree is run (“ticked”) from the top at every loop; each node returns success, failure or running. A Selector (fallback) node tries its children left to right and stops at the first that does not fail; a Sequence node runs its children in order and stops at the first that does not succeed; the leaves are conditions (a sensor test) and actions.
Neither is better in general, and they are often combined: a state machine for the robot's operating modes, a behaviour tree (or the priority list of section 1) inside the mode that needs many alternatives. Behaviour trees are named here so that you recognise them; building one is a topic for Programming for Robotics or a software course.
7. Testing and failure handling
A robot's behaviour layer is the part most likely to meet situations nobody planned for, and the part where a mistake moves a heavy object near people. Four habits turn “it worked once” into “it fails safely”.
- Timeouts. Every state that waits for something (an arrival, a dock, a door) gets a maximum time, after which the machine takes a defined exit (the table of section 2: AVOID 5 s, RETURN 120 s, CHARGE 8 hours). Without them a robot can wait forever in a state that no event will ever end.
- A watchdog. A watchdog timer is a countdown that the main program must keep resetting ("kicking") while it runs healthily; if the program freezes or crashes, the countdown reaches zero and hardware (not the frozen software) cuts the motor power. A typical choice is 5 times the loop period (100 ms for a 20 ms loop).
- Safe stop. Define what “safe” means for each failure: usually motors off and brakes on, but an arm that holds a load or a robot on a slope may need to be held, not released (Robotics Fundamentals 10). The safe state must also be the state the hardware falls into when power or signal is lost, not only a state that software can choose.
- Logging. Write down every state transition with its time and its cause, and the key sensor values at a fixed rate. When the robot does something strange, the log is the only witness. At 20 records per second of 40 bytes, that is 800 B/s or 2.9 MB per hour, easily stored on a card. A good log lets you replay a failure in simulation.
8. Challenges
S . # . . / . . # . G / . . . # . / # . . . . (S at (0,0), G at (1,4), # blocked, cells 0.2 m), use moves in the order up, right, down, left. Find the Manhattan distance from S to G, then by BFS the number of moves to G and the distance written in every reached cell, and then run A* with the tie rules of section 4 (lowest f, then lowest h, then first added): list the expansions with g, h, f, the path found and its length in metres, and compare the number of expansions with BFS. (c) Beacons A = (0, 0), B = (5, 0), C = (0, 4) measure dA = 3.20, dB = 3.90 and dC = 2.50 m. Find the position and check it against the true position (2.0, 2.5). (d) A wall is at x = 6.0 m and the range reading is z = 3.6 m with sigma = 0.5 m. Particles sit at x = 1, 2, 3, 4 and 5 m. Find each predicted range, weight and normalised weight, the expected copies in a cloud of 5, and the weighted mean position. (e) A robot at 0.8 m/s with deceleration 2.5 m/s^2 and a total reaction time of 60 ms: find the stopping distance, the stop threshold with a margin of 2, and the largest speed at which a 0.30 m threshold still leaves a margin of 2.Quick reference
- Reactive behaviour: sensors straight to motors, no map and no memory; fast and robust, but it cannot plan or remember. Braitenberg: wheel speed = b + g × sensor; crossed wiring turns toward the stimulus, uncrossed turns away.
- Arbitration: priority list (first behaviour with a command wins; safety first, default last) for anything that matters; blending only among behaviours that agree, because an average of two avoids can steer straight at the obstacle.
- State machine: states, events, a transition table (the table is the program; unlisted pairs are ignored); log every transition; give waiting states a timeout.
- Debounce: require the condition for a time or a count (5 samples at 0.1 s), use hysteresis (enter at 20 %, leave at 25 %) and a dwell time; size the delay to the cost of a late reaction (battery 0.5 s, cliff 15 ms).
- State explosion: independent aspects multiply (4 × 3 × 2 = 24) when put in one machine, but add (4 + 3 + 2 = 9) as separate machines; use hierarchy for shared transitions.
- Line following: e = −x, u = Kp e, vR,L = v ± uL/2, v = vmax/(1 + c|e|); add lost-line search toward the last-seen side with a time limit.
- Obstacle avoidance: stop at 0.30 m, resume above 0.45 m (hysteresis), turn toward the clearer side, 3 avoids in 10 s means ESCAPE. Potential field: Frep = k(1/d − 1/d0)/d2 for d < d0; sum with the goal pull; beware local minima.
- Wall following: u = −(Kp(dperp − dref) + Kθθ) for a wall on the right; θ = atan((dF − dR)/s), dperp = ((dF + dR)/2) cos θ. Homing: w = Kbβ, v = min(vmax, kv r); retry a limited number of times.
- Occupancy grid: store log-odds; l = l + l(reading); p = 1/(1 + e−l); l(hit) = ln(0.7/0.3) = +0.847 for a 70 % model; clamp at ±4. Column = floor(x/cell), row = floor(y/cell); inflate obstacles by the robot radius.
- BFS finds a shortest path (equal costs) but explores everywhere (25 cells in the example). A* expands the lowest f = g + h with the Manhattan h = |Δrow| + |Δcol| (admissible), and found the same 8-move, 2.0 m path with 14 expansions.
- Localisation: belief = predict (wider) + update (narrower). Particle filter: move, weight by w = exp(−(z − zpred)2/2σ2), resample. Trilateration: x = (dA2 − dB2 + b2)/2b, y = (dA2 − dC2 + c2)/2c. SLAM builds the map and the pose together; loop closure fixes the drift.
- Behaviour tree: selector (first child that does not fail), sequence (all must succeed), re-evaluated every tick; state machines suit modes, trees suit prioritised alternatives.
- Failure handling: timeouts, a hardware watchdog (about 5 times the loop period), a defined safe stop, and a log of every transition. Stopping distance = v × latency + v2/2a; keep a margin of 2 or more.
Next, Robotics Fundamentals 9 moves from a robot that drives to a robot that reaches: arms and manipulators. Joints and links, forward kinematics (where is the hand, given the joint angles?) and inverse kinematics (what joint angles put the hand there?) worked out by hand for a two-link arm, grippers, and the torque a joint must supply to hold a payload at arm's length, using the motor sizing of Robotics Fundamentals 3 and the position control and feedforward of Robotics Fundamentals 7.