Path Following Simulator: Pure Pursuit and Stanley
Drive a course with pure pursuit or Stanley, with real steering lag. Drag the lookahead and the whole run replots — including what a 1 Hz GPS fix costs you.
- Category
- Autonomous Robots
- Time
- 20–40 min
- Platform
- Browser · Arduino · Raspberry Pi
01 / Start here
Introduction
A planner hands you a path; nothing in it tells the wheels what to do. This lab is that missing rung. Pure pursuit and Stanley steer the same waypoint course with a first-order steering response and sensing delay, because a model without lag makes the wrong tuning look optimal. Drag a waypoint or the lookahead and the entire run recomputes.
Live lab / Path following
Path following simulator
Pure pursuit and Stanley steering the same course, with the lag a real robot has. Drag the lookahead and the whole run replots — too short oscillates, too long cuts corners, and the best value moves with speed.
The dashed line with dots is the course you asked for; the solid line is where the robot went. The faint line is the whole run at the current settings, computed the moment you change one. Below, cross-track error along the path — dotted verticals are corners. Drag a waypoint to rebuild the course.
- Commanded path
- Driven
- Full run preview
- GPS fix
- RMS error
- —
- Worst error
- —
- Corner cut
- —
- Steering effort
- —
- Progress
- 0%
- Lap time
- —
Keyboard: focus the course, then Space to run/pause, N to step, R to reset, C for a new course, F for full screen. Press 1–8 to pick a waypoint and the arrow keys to move it.
Controls
Controller switches between pure pursuit and Stanley. Lookahead is pure pursuit’s only knob and is disabled for Stanley, which does not use one — that is itself the point.
Speed and lookahead interact, which is the thing worth playing with: set 1 m/s and find the best lookahead, then set 3 m/s and find it again.
Position from swaps perfect odometry for a 1 Hz fix with metre-scale noise, held between updates. Error is always scored against the truth, so the penalty is real rather than hidden.
Drag any waypoint, or press 1–8 to select one and move it with the arrow keys. The whole run recomputes as you drag.
Theory
Both controllers answer the same question — given where I am and where the path is, what steering angle? — and answer it completely differently.
Pure pursuit is geometry. Find the point on the path a lookahead distance L ahead, and drive the circular arc through it:
κ = 2y / L_d² δ = atan(2 b sin α / L_d)
with y the lateral offset of the goal point in the robot’s frame and b the wheelbase. One parameter, and it is a distance rather than a gain — so it transfers between robots.
Stanley is control. Reference the front axle and add two terms:
δ = θ_e + atan(k · e / v)
heading error plus an arctangent of cross-track error. Dividing by speed is what lets one gain work across a speed range. k is a true control gain and does not transfer.
The lab adds what textbook derivations leave out: 0.4 s of total lag, as a 0.25 s first-order steering response plus 0.15 s of sensing and compute delay. Without it, pure pursuit gets monotonically better as the lookahead shrinks and the whole tuning question disappears.
Algorithm
Each step, for both controllers:
- Project onto the path, searching a window around the arc length last reached rather than the whole path. A global search matches whichever segment is nearest, so a course that doubles back teleports the robot’s progress to a corner it passed a minute ago.
- Resolve a tie at a vertex to the outgoing segment. Past a corner, both adjacent segments project to the same point at the same distance; keeping the incoming one hands the controller a heading error and a lateral offset that are both near zero while it drives straight off the path, silently.
- Compute the steering angle from the controller’s own rule, and clamp it to the mechanical limit.
- Apply lag, then integrate the bicycle model.
- Score against the truth, never the estimate.
Step 2 is not an optimisation. It was the difference between Stanley working and Stanley confidently leaving the course at exactly 78% every single run.
Source code
Pure pursuit, complete, in the form it takes on a real robot:
float lastS = 0; // arc length last reached — state, and it only moves forward
float pursue(float x, float y, float heading, float speed) {
// The lookahead is scheduled on speed, with a hard floor: at a standstill a
// proportional schedule asks for zero, which is a division by zero in disguise.
float lookahead = constrain(0.7f * speed + 0.4f, 0.4f, 3.0f);
float s = projectOntoPath(x, y, lastS); // windowed around lastS
lastS = max(lastS, s);
Point goal = pointAtArcLength(lastS + lookahead);
float dx = goal.x - x, dy = goal.y - y;
float lateral = -sinf(heading) * dx + cosf(heading) * dy;
float ld = max(0.3f, hypotf(dx, dy)); // clamp, or curvature blows up
float curvature = 2.0f * lateral / (ld * ld);
return curvature; // omega = v * curvature
}
The three lines that matter are the schedule floor, the lastS monotonicity, and the ld clamp. Everything else is the formula.
Assumptions and hardware differences
The lab gives the robot a perfect map of the path, a perfectly known wheelbase, no wheel slip, and lag that is a clean first-order model. A real robot has none of those.
Wheel slip makes the position estimate wrong in a way no controller can detect. Gearbox backlash adds dead travel at every steering reversal, which behaves like extra lag and pushes the optimal lookahead up. And a differential drive has no steering angle at all — it has a commanded rotation rate, so ω = v κ replaces the atan and the wheelbase only matters for how tightly it can turn.
What survives all of it is the shape: an interior optimum, moving right with speed, and error at that optimum growing with speed regardless.
Circuit diagram
The controller is the cheap part. What it needs from the rest of the robot is the expensive part.
Position. Quadrature encoders on both wheels and differential-drive odometry integrating them, at the loop rate. Indoors that is enough for a few tens of metres.
Heading. A gyro for the short term, a magnetometer for absolute reference, or both. Heading error is the term both controllers weight most heavily, and it is the one odometry loses first.
An absolute correction, outdoors. A GPS module fused into the estimate rather than followed directly — the difference is 0.09 m of RMS error against 1.25.
For the theory in full, see pure pursuit and cross-track error; to build it, the GPS waypoint rover starts indoors on encoders and goes outside only once the follower works.
Hardware checklist
Components
- A base whose steering or wheel speeds you can command directly
- A position estimate — encoders at minimum, fused with GPS outdoors
- A heading reference that does not drift over a mission
- A loop fast enough that its own lag is smaller than your lookahead
Explore the graph
Where this simulator is used
The projects, learning paths, and tutorials that build on this lab.
Continue building
Download resources
Use these on-page references while working through the project. Downloadable project bundles will be added only after their source and version are published.
Common questions
Frequently asked questions
Why does a shorter lookahead not always track better?
In a model with no lag it does, which is exactly why so many textbook derivations are misleading. Real robots take time to reach a commanded steering angle and act on a pose that is already tens of milliseconds old. This lab models a 0.25 second steering time constant and 0.15 seconds of sensing delay. With that lag present the error curve becomes a U: a short lookahead commands a correction that arrives after the robot has already moved past the error, so the next command over-corrects. A controller faster than its own actuator oscillates.
How do I choose the lookahead for my robot?
Measure your total control lag, multiply by your speed, and start between one and two times that distance. In this lab the lag is 0.4 seconds, and the measured optima are 0.9 metres at 1 m/s rising to 1.4 metres at 3 m/s — between 1.2 and 2.3 times the distance covered during the lag. Notice that the optimum grows with speed but less than proportionally, so a fixed number of seconds of travel is close but not right. Ship it as a small linear schedule on speed with a hard minimum, because at a standstill the schedule wants zero.
Which is better, pure pursuit or Stanley?
They fail differently, which is the useful answer. Over this course at 2 m/s pure pursuit cuts corners and finishes 0.8 percent short of the commanded path length; Stanley refuses to leave the line, swings wide, and travels 2.2 percent long. Stanley holds a tighter line where it matters but spends 18.5 radians of steering against pure pursuit's 4.3 — four times the actuator movement, current and tyre scrub. Use pure pursuit when the path is a suggestion and Stanley when it is a constraint.
Why is the error measured against the true position rather than the estimate?
Because a robot that thinks it is on the path reports zero error by construction. Switch the position source to GPS and the controller starts steering on a noisy, one-second-old fix while the lab keeps scoring against the pose the model actually integrated. That is the only reason the GPS penalty shows up as a number: 0.09 metres of RMS error becomes 1.25, and steering effort more than quadruples.
What does the faint line show?
The complete run at the current settings, computed the moment you change one. Every slider move re-runs the whole course headless in about twenty milliseconds, so dragging the lookahead shows you the trajectory it produces rather than a promise about it. The bold line is the robot animating over that same run when you press Start.
Why does the robot never stop turning at the very sharp corner?
Both controllers have a minimum turning radius set by the wheelbase and maximum steering angle, and a corner sharper than that cannot be followed however you tune. The honest fixes are upstream: soften the path with a fillet or an arc, or slow down for the corner, which is what a motion profile is for. No steering controller can drive a geometry the vehicle does not have.
Further reading
References
Authoritative sources for going deeper than this simulator's bounded educational model.