The Loop That Keeps a Unicycle Upright
A unicycle is an inverted pendulum with a saddle. There is no freewheel, no second wheel, no lean-back-and-coast — the only thing between you and the pavement is a correction loop running fast enough to matter. On the whiteboard I can draw that loop in one breath: measure the lean, decide how hard to drive the wheel, put the contact patch back under your centre of mass, repeat. On the actual unicycle my legs run some ancient reflex instead and the wheel shoots out from under me.
The loop I can run flawlessly is the one made of code. It’s a PID controller — proportional, integral, derivative — the workhorse that peaked in the 1980s when cheap microcontrollers started closing feedback loops in everything from disk drives to cruise control. Three terms: react to how far off you are now (P), to how long you’ve been off (I), and to how fast the error is changing (D). That last term is the anticipation — it’s what stops you from over-correcting into the opposite fall.
double pid(double target, double lean, double *sum, double *prev, double dt) {
double Kp = 5.0, Ki = 0.4, Kd = 1.5;
double error = target - lean;
*sum += error * dt;
double slope = (error - *prev) / dt;
*prev = error;
return Kp*error + Ki*(*sum) + Kd*slope;
}
The same thing in Ruby, where the state rides along in a little hash instead of pointers:
def pid(target, lean, s, dt: 0.1)
kp, ki, kd = 5.0, 0.4, 1.5
error = target - lean
s[:sum] += error * dt
slope = (error - s[:prev]) / dt
s[:prev] = error
kp*error + ki*s[:sum] + kd*slope
end
Start it leaning 0.15 radians off vertical and watch it hunt back to standing:
t=0 lean=-0.000 push=-3.006
t=1 lean=+0.112 push=+2.250
t=2 lean=-0.001 push=-2.259
t=3 lean=+0.084 push=+1.688
t=6 lean=-0.002 push=-1.276
It doesn’t snap upright. It overshoots, corrects, overshoots less, corrects less — a wobble that decays. That decaying wobble is exactly what a beginner does, except the controller’s gains are tuned and mine aren’t. Crank Kp too high and it oscillates forever; that’s me on the wheel, slamming corrections a beat too late and feeding my own fall. The controller and I are solving the identical equation. Only one of us has the gains dialled in.