← work
autonomous path following'25-'26

pure pursuit vs guiding vector fields

how we moved from adaptive pure pursuit to a guiding vector field for consistent autonomous at the first world championships.

context

when we made it to the first world championships, we knew we wanted a consistent autonomous routine.

we didn't have much time, but we eventually got it to a state where it worked consistently every match. along the way we ran two different path followers: adaptive pure pursuit, and then a guiding vector field. this is about both, and why we ended up switching.

pure pursuit

pure pursuit follows a path by always steering toward a point a fixed distance ahead of the robot. that distance is the lookahead, and the target is wherever the lookahead circle crosses the path.

the problem is the lookahead. if it's too big, the circle can grab a point that's physically close but actually much farther along the path. on a tight path the robot locks onto the wrong segment and cuts straight across instead of following the route.

drag the lookahead up and watch pure pursuit (left) cut across, while the vector field (right) stays on the path. nudge start offset if you want both bots starting off the centerline.

pure pursuit
guiding vector field

with a large enough lookahead, pure pursuit locks onto the return leg and cuts across (lower it and it follows again). the vector field stays on the path. drag start offset to begin off-center.

we can somewhat fix this by scaling the size of the circle based off the robot's current velocity (this is the "adaptive" part), but that does not fully fix the issue. here the circle shrinks through the turn, but because that shrink lags, it still cuts across on the way in:

adaptive pure pursuit
guiding vector field
lookahead scales with speed

same lookahead, but it shrinks through the turn. the shrink lags, so it still cuts on the way in and then recovers, while the vector field stays on the path. nudge the start offset to see recovery from lateral error.

this can be frustrating, especially in a game where we need to consistently open the gate to intake or feed our partners.

our goals

we wanted to shoot more balls in auto than we managed at our state championship. to do that, we optimized gate intaking so we could grab from the gate sideways, without cutting into the number of balls we hand off to our partners.

so we switched to a guiding vector field.

the guiding vector field

a guiding vector field flips the idea around. instead of chasing a single point ahead of us, every point in the plane gets a desired heading, so there's no lookahead to mistune and no wrong segment to lock onto.

for a path defined implicitly by φ(x,y)=0\varphi(x, y) = 0, the field assigns a desired velocity at every point in the plane. one term drives the robot along the path, and the other pulls it back onto the path when it drifts off:

v=τ(φ)along the path    keφφconverge onto it\mathbf{v} = \underbrace{\tau(\nabla\varphi)}_{\text{along the path}} \;-\; \underbrace{k_e\,\varphi\,\nabla\varphi}_{\text{converge onto it}}

τ\tau rotates the gradient by 9090^\circ so it points along the path, and kek_e is the convergence gain: how hard off-path points get pulled back in. the heading we actually command is θ=atan2(vy,vx)\theta = \operatorname{atan2}(v_y, v_x).

arrows are the commanded heading at each point. click to drop a robot onto the field and drag kek_e: at zero they only ride the tangent; raise it and they snap harder onto the curve.

click to drop a robot

arrows show the commanded heading everywhere. click anywhere to drop a robot that follows them. at kₑ = 0 they only ride the tangent; raise it and off-path robots bend harder onto the curve.

reducing oscillation

to reduce oscillation further, we attached a velocity profile to a regression. we took inspiration from 18438 wolfpack machina in power play, who used a similar system to smooth out their stopping.

the way it works: using a calculated motor curve for each wheel (as in, if we cut all power to the wheel, what would happen?) along with our current velocity, we predict where we'll stop. if we think we'll stop at the end position, we cut power to the wheels and glide into place, instead of forcing ourselves in and then oscillating.

a secondary positional pid then corrects onto the end point after the robot has stopped.

pure pid
target
predictive braking
target

pure pid drives in hard and rings past the endpoint before settling. predictive braking projects its stopping point (the tick), cuts power in time, coasts in, then pids onto the exact endpoint. raise entry speed and watch the tick sit farther ahead.

you can tune a pid controller a lot, but the simplicity of this solution means you don't need to spend as much time tuning a pid to get there.

notes

credits & references