Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Control Playground

A self-contained, interactive demo of PID, LQR and MPPI path tracking for lecturing. No installs, no server — just open index.html in any browser:

open index.html

The plant is a kinematic bicycle at constant forward speed; the control input is the steering angle, clamped to a limit. Where the Planning Playground asks "how do we find a path?", this one asks "how do we follow it?" — same canvas, same controls, same one-chart-per-theorem philosophy.

Using it in lecture

  • Pick the mode at the top of the sidebar: PID, LQR, MPPI, or Race: all three (three cars overlaid on the identical path).
  • PID acts on the signed cross-track error (P), its integral (I) and a filtered derivative (D), with conditional anti-windup at the steering limit. The Result panel shows the live P/I/D contributions in degrees of steering.
  • LQR linearizes the error dynamics (cross-track + heading error) about the reference, solves the discrete Riccati equation live (gains retune instantly when you move the Q/R sliders mid-run), and adds a curvature feedforward δff = atan(L·κ). The breakdown line shows feedforward vs feedback and the current gain vector.
  • MPPI is honest sampling-based MPC: every control tick it perturbs the previous steering sequence into K candidate sequences, rolls each through the nonlinear bicycle model over the horizon, scores tracking + effort, and blends them with weights exp(-cost/λ). You see the whole thing: the faint magenta fan is the sampled futures (opacity = weight), the bold dashed line is the committed plan. Receding horizon means it re-imagines the world ~17 times a second.
  • Space plays/pauses, S advances one sim step (0.02 s), R resets, P toggles projector mode (bigger text, thicker strokes).
  • At slow sim speeds the cross-track error vector is drawn from the path to each car (yellow, dashed) with its live value.
  • Drag the green start ring to change the initial offset — every run opens with a step-response transient, so the start position is the experiment. Any world edit (start, speed, wind, bias, noise, latency) resets the run and clears the chart; controller-gain changes don't.
  • Preset paths: Step (straight line, car starts 90 px off — a textbook step response), Slalom (sinusoid), Corners (two filleted 90° turns), Circle (constant curvature), Planned path (a jagged RRT-style polyline — the bridge back to the planning demo).
  • Sliders: sim speed, vehicle speed, steering limit, crosswind, steering bias, sensor noise, actuator latency, and per-controller parameters. Changing controller gains mid-run is allowed and instructive.

The "race" demo

The centerpiece: all three cars drive the identical path with the same physics, the same wind, and the same seeded sensor-noise stream — the only difference is the control law. The Tracking error chart draws each controller's signed cross-track error over time; the end banner reports the RMS scoreboard. Same path, same physics, different brains.

Curves persist across runs until the world changes, so running PID, LQR and MPPI one after another on the same preset overlays all three step responses on one chart — the whole controller landscape in one picture.

Talking points, in escalating order

  • Step + PID, kP only (set kD = 0): pure oscillation about the path — proportional control on a double-integrator-like plant is undamped. Raise kD and watch the oscillation die; that's derivative action, live.
  • kD amplifies noise: put sensor noise at 3–4 px and watch PID's steering (and effort stat) chatter while the path tracking barely improves. The derivative of a noisy signal is the classic PID pathology.
  • Steering bias — the integral-action story: set steering bias to ~8° (crooked wheels). PD leaves a visible steady-state offset; a bit of kI removes it exactly. Now try LQR: it has no integral action, so the offset stays — as does MPPI's, since the bias isn't in its model. Only integral action rejects what your model doesn't know about.
  • Curvature: on Circle, PID rides visibly outside the reference all lap (steady-state error against constant curvature; kI fixes it). LQR is clean from the start — that's the curvature feedforward, not better feedback.
  • Too much integral: crosswind 30, kI at 0.6 — the integrator's lag during the big initial transient swings the heading past 90° and the car spirals off. Raise kD to ~0.9 and it's rescued. Integral action trades steady-state accuracy for phase lag; saturation makes the trade violent.
  • Strong wind — heading-blindness: at crosswind 50, cross-track PID can spin outright (it knows nothing about heading; once the car points backwards the feedback sign flips). LQR and MPPI, which both reason about heading, crab into the wind and keep tracking — with a steady offset, because the wind isn't in their models either.
  • Latency — the MPC punchline: set actuator latency to 160 ms. PID's damping degrades into oscillation; MPPI barely notices, because its rollouts simulate the still-pending steering commands before optimizing what comes next. Prediction is the cure for delay.
  • Horizon slider: shrink MPPI's horizon to 0.4 s and it degenerates toward reactive control (watch the Corners preset — it stops anticipating the turn). Stretch it to 2.4 s and the fan reaches around the corner before the car gets there. That anticipation is the visible difference between optimizing the future and reacting to the present.
  • Temperature λ: small λ ≈ pick-the-best-sample (greedy, jittery); large λ ≈ average-everything (smooth, sluggish). The soft-max knob, on a slider.
  • Planned path: planner output is jagged; tracking it is where control meets planning. MPPI's RMS beats PID's by ~40% here — smoothing-by- optimization — and the segue back to the planning lecture writes itself.

Solving the MPC: why MPPI

There is no QP solver in this file, and that's the point. MPPI minimizes the same finite-horizon cost an industrial linear-MPC QP would, but by sampling — which handles the nonlinear model and the steering-limit constraint for free, never diverges mid-lecture, and (unlike a QP) lets students literally watch the optimizer think: the rollout fan is the search. It's also a real algorithm run on real robots, not a classroom simplification.

Extending

The code exposes a controller-plugin interface in index.html:

const CONTROLLERS = { pid: PID, lqr: LQR, mppi: MPPI };

A controller implements init() → state and control(state, meas, view) → {delta, info}, where meas is the (possibly noisy) pose plus its projection onto the path (cross-track error, heading error, curvature, arc length), and info drives the canvas annotations and the Result breakdown line. Controllers declare a period (control interval in sim steps — MPPI runs at 17 Hz, PID/LQR at 50 Hz) and everything else (latency queue, noise streams, metrics, chart, race mode) is shared machinery. Pure Pursuit or Stanley would each be ~15 lines and slot straight into the race.

harness.js is a headless smoke test (DOM stubs + the window.__test hook at the bottom of index.html): it drives every controller over every preset and checks that the teaching stories (integral action, windup, latency, noise) still hold. Run it with node harness.js, or on a stock Mac with osascript -l JavaScript harness.js. Re-run it after touching a controller.

About

Interactive, zero-install playground for control and path tracking. PID, LQR, and MPPI driving a kinematic bicycle, with live rollouts, tracking-error charts and adjustable disturbances. Single HTML file, runs in any browser. Built for MIT's "AI in Robotics: Learning, Algorithms, Design and Safety". Chuchu Fan & Augustinos D. Saravanos

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages