← All lessons
Prediction · characterization · feedforward · WPILib

Push first,
correct later.

Feedback control waits for a mistake, then reacts. Feedforward control studies the mechanism, predicts the push it will need, and applies it before any mistake exists. This is the story of how flywheels, arms, and elevators get controlled by prediction.

setpoint (what you want) feedforward (prediction) feedback (reaction) the mechanism
Part 1 / The problem with only reacting

A controller that waits for errors is always late

Two riders hit the same hill at the same speed. The orange rider waits to feel the bike slow down, then pushes harder — reacting to the error. The green rider sees the hill coming, knows what it costs, and pushes at the moment the slope starts — predicting. Watch who wins:

Widget 00 — The hillreact vs predict
Plots — speed & pedal effort vs time

Top: the reactor's speed dips on the hill and slowly claws back; the predictor's line never moves. Bottom: the reason — the predictor's effort jumps at the hill's edge, while the reactor's effort only rises as its speed falls (effort proportional to lost speed).

Both riders end up over the hill — but the reactor has to lose speed before it responds, then spends the rest of the climb winning it back. The predictor never slows down at all, because it never let an error exist.

That's the whole idea. Feedback control measures the error — the gap between what you want and what you have — and reacts. Self-correcting, but it must be wrong before it acts. Feedforward control predicts the required effort from knowledge of the system and applies it up front. Never late — but only as good as its knowledge.

Robots are full of predictable effort: a shooter flywheel droops every shot, an arm sags under gravity, an elevator drifts down while "holding." None of that should wait to be discovered by failing first.

Vocabulary for the road: the thing being controlled (flywheel, arm, elevator) is the plant. The value you want is the setpoint (or reference). What the controller sends — for us, motor voltage — is the control effort. The plant's actual behavior is the output, and setpoint − output is the error.
Part 2 / The hidden flaw

Feedback's lag, caught on camera

The orange rider is a real controller: a feedback loop. Here it is drawn in standard block-diagram notation — if boxes-and-arrows are new to you, take ten minutes with the companion primer, Boxes, Arrows, Circles, first. Read the shape: the output loops back through a sensor, gets subtracted from the setpoint, and only the leftover error is allowed to drive the motor:

setpoint r + − error e Feedback V = kP · e voltage V Plant (motor + flywheel) speed ω Sensor (encoder) measured ω
The feedback loop. Self-correcting by construction — and error-powered by construction. The controller literally cannot act until something has already gone wrong. That's the rider's lag, drawn as a circuit.

Now watch the lag become a number. The widget below is this exact diagram, live — real values riding the wires of a simulated shooter flywheel:

Widget 01 — The live loopfeedback only

A P controller (V = kP · e) drives the flywheel. Watch the numbers on the wires: the flywheel settles where the error is just big enough to generate the voltage that holds it there — which means it never actually reaches the setpoint.

Plots — speed & voltage vs time

The speed curve flattens out below the setpoint line — that vertical gap is the steady-state error. The voltage curve shows why it's stuck: V = kP·e, so holding a steady voltage requires holding a steady error. Change kP mid-run and watch both curves renegotiate.

Setpoint
400
Steady error
—
Speed reached
0

Crank kP to shrink the leftover error — it helps, but never to zero, and on a real mechanism large kP eventually causes violent oscillation (our simple simulation is too polite to show it). This permanent gap is called steady-state error: pure proportional feedback needs a standing error to produce a standing voltage. The flywheel is being held up by its own failure.

Part 3 / The big idea

Feedforward: control by prediction

Here's the escape from that trap. The voltage that holds a flywheel at 400 rad/s doesn't depend on any error — it depends on physics: bearing friction, how hard the motor fights back at speed. Those don't change moment to moment, so why rediscover them through error every loop? Measure them once, write them into a model, and compute the voltage directly from the setpoint.

That's feedforward. Feedback asks "how wrong am I?" — feedforward asks "knowing this mechanism, what input produces the output I want?" It's a prediction, and predictions demand a model. Getting the model is called characterization — the price of admission, paid in Part 5. In the diagram, feedforward is literally a path fed forward from the setpoint, never touching the error loop:

setpoint r Feedforward model V_ff = predict(r) V_ff + − error e Feedback V_fb = kP · e V_fb + + V Plant motor + load ω measured ω (sensor)
Feedforward + feedback. The setpoint splits at the dot. The green path never sees the error — it predicts V_ff straight from the setpoint using the model. The orange loop only has to clean up whatever the prediction missed. The two voltages simply add.
V = Vff + Vfb = predict(r) + kP · (r − ω)
V — total voltage sent to the motorVff — the feedforward term: the model's prediction of what the setpoint requires, computed with no knowledge of the errorVfb — the feedback term: a correction proportional to the errorpredict(r) — the model function we build in Part 4kP — the proportional gain, volts per unit of errorr — the setpointω — the measured output.Division of labor: feedforward carries the predictable ~95% of the effort; feedback mops up model imperfection, disturbances, and battery sag. With feedforward doing the lifting, the error stays tiny — so feedback can stay gentle.
Part 4 / The models

Three mechanisms, one motor equation

So what does predict(r) actually look like? For nearly everything on a robot, it starts from the permanent-magnet DC motor, which charges three costs, paid in voltage:

Friction charges a cover fee — below some voltage nothing moves at all. That threshold is kS, paid in whichever direction you're going. Speed charges a tax — a spinning motor is also a generator, pushing back a voltage proportional to speed (back-EMF); out-paying it costs kV volts per unit of velocity. Acceleration charges a price — fighting inertia costs kA volts per unit of acceleration. Stack them and you have WPILib's SimpleMotorFeedforward — the model for flywheels, shooters, and drivetrains:

Flywheel V = kS · sgn(v) + kV · v + kA · a
V — the predicted voltagev — the desired velocity (the setpoint, not the measurement — this is a prediction!)a — the desired accelerationsgn(v) — the sign function: +1 moving forward, −1 in reverse; friction always opposes motion, so kS flips with directionkS — static friction voltage (volts)kV — velocity gain (volts per unit velocity), the back-EMF tax ratekA — acceleration gain (volts per unit acceleration).Cruising at constant speed? Then a = 0 and the last term vanishes — which is why kA can often be omitted for low-inertia mechanisms.

Elevators and arms are the same motor with one new bill: gravity, handled by a kG term. Watch where the geometry shows up in the math. An elevator hauls straight up — gravity pulls the same at every height, so kG is a constant:

Elevator V = kG + kS · sgn(v) + kV · v + kA · a
kG — the gravity voltage: the constant voltage that exactly holds the carriage against gravity, identical at every heightv, a — desired vertical velocity and accelerationeverything else as in the flywheel equation.Set v = 0 and a = 0: the prediction is V = kG. An elevator "holding still" is not off — it's quietly paying the gravity bill forever. Note kG has no sgn(): gravity always pulls the same way, whether you're moving up or down.

An arm swings mass on a stick, so gravity's leverage changes with angle — brutal horizontal, zero straight up or down. The cosine captures exactly that:

Arm V = kG · cos(θ) + kS · sgn(ω) + kV · ω + kA · α
θ — the arm angle, measured with 0° = horizontal (that convention is what makes cosine the right shape)cos(θ) — gravity's leverage: 1 at horizontal, 0 straight up or downkG — the gravity voltage at the worst case, horizontalω, α — desired angular velocity and accelerationkS, kV, kA — as before, now in angular units.This term is why an unpowered arm doesn't sag equally everywhere — and why a good arm controller applies kG·cos(θ) continuously, even when "holding still."

Play with both gravity terms below — this is the part of the model you can feel.

Widget 02 — The gravity billkG explorer

Both mechanisms are commanded to hold perfectly still (v = 0, a = 0), so the entire prediction is the gravity term. Drag the sliders and watch what each one owes.

Plot — holding voltage vs arm angle

The arm's gravity bill traced across every angle: a cosine, peaking at 0° (horizontal) and hitting zero at ±90°. The flat dashed line is the elevator's bill — same at every position. The moving dot is wherever the slider currently sits.

Arm: kG·cos(θ)
— V
Elevator: kG
— V

Sweep the arm from −90° to +90°: the holding voltage traces a cosine, peaking at 0° (horizontal) and dying to zero at both vertical extremes. Now sweep the elevator top to bottom: its bill never changes. Same gravity, different geometry — the model's job is to encode that geometry.

These three models are implemented in WPILib as the classes SimpleMotorFeedforward, ElevatorFeedforward, and ArmFeedforward — you hand them your constants and a setpoint, they hand back the predicted voltage. The derivations live in Introduction to DC Motor Feedforward, and the code-side usage in Feedforward Control in WPILib.

Part 5 / Earning the prediction

Characterization: where the constants come from

The equations are useless as written — full of unknowns. What is kS for your flywheel, with your bearings and your belt tension? No datasheet knows. The constants must be measured from the real mechanism: that's characterization (formally, system identification). The core trick is one line — at steady state, a = 0 and the kA term vanishes:

V = kS + kV · v   (steady state, moving forward)
V — the voltage you appliedv — the steady velocity the mechanism settled atkS — the y-intercept of this linekV — its slope.This is y = b + m·x wearing a lab coat. Feed the mechanism a series of voltages, record the steady speed each produces, and the data points fall on a line. Fit the line, read off the intercept and slope, and you've characterized kS and kV.

Run that experiment yourself. The widget below has a mystery flywheel — its true constants are hidden. Collect data, fit the line, and steal its secrets.

Widget 03 — The characterization labmeasure → model

Each test applies one voltage, waits for the flywheel to settle, and logs the point (steady velocity, voltage). Real data is noisy — sensor jitter and vibration are included free of charge.

Plot — fit residuals (how SysId judges your data)

Each dot is one data point's residual: measured voltage minus what the fitted line predicts. A healthy fit scatters randomly around zero — just noise. If the residuals showed a pattern (a curve, a trend), that would mean the model itself is wrong for this mechanism. SysId's diagnostic plots are exactly this check.

Fitted kS (intercept)
—
Fitted kV (slope)
—
True values
hidden

The fit uses least-squares regression — the same method WPILib's SysId tool applies to your logged robot data. Hit "New mystery flywheel" a few times: the fitted values land close to the truth despite the noise, and closer the more data you have. This is the moment a real, physical mechanism becomes a model you can predict with.

On a real robot, WPILib automates this with the SysId tool and the SysIdRoutine class: a quasistatic test ramps voltage so slowly that acceleration stays negligible (isolating kS and kV, like our widget), and a dynamic test steps it hard to expose kA. The robot logs voltage, position, and velocity; SysId runs the regression and hands you the constants plus diagnostic plots. Setup lives in the Introduction to System Identification.

The philosophical point — and the one to remember from this whole page: prediction is purchased with understanding. Feedback needs almost no knowledge of the plant; it just pushes against error. Feedforward demands you study the mechanism first. Characterization is that study, distilled into three or four numbers.
Part 6 / Putting it all together

Feedforward + feedback: the full controller

Time to run the complete two-path diagram from Part 3 on a shooter flywheel — the classic case, because a shooter lives or dies by recovery time: every ball fired rips energy out of the wheel, and the next shot is only accurate once the speed is back. Toggle each path on and off and watch what each one is actually good at. The model-error slider corrupts the feedforward's constants, simulating a sloppy characterization — the exact situation where feedback earns its keep.

Widget 04 — Shooter flywheelgrand finale
Plot — voltage split: V_ff vs V_fb over time

The healthy-controller signature, drawn live: V_ff big, flat, and boring; V_fb hugging zero and spiking only at shots and setpoint changes. Turn feedforward off and watch V_fb forced to carry the whole load — permanently large, permanently error-powered.

V feedforward
— V
V feedback
— V
Speed error
—

Experiments to try, in order: (1) Feedback only — the Part 2 droop is back, and recovery after a shot is lazy. (2) Feedforward only, 0% model error — snaps to the setpoint with no error at all… now fire a shot: it recovers, but drag the model error to +15% and the wheel confidently holds the wrong speed forever. A prediction can't see its own mistake. (3) Both on, with model error — feedforward does the heavy lifting, feedback quietly absorbs the model's imperfection and the shots. Notice the voltage split: V_ff is big and steady, V_fb hovers near zero and twitches only when something goes wrong. That's a healthy controller.

Part 7 / Recap

Five ideas to walk away with

1. Feedback is reaction. It's self-correcting but must be wrong before it acts — pure proportional feedback even needs a standing error to hold a standing output.

2. Feedforward is prediction. It computes the required input directly from the desired output, using a model — so it acts before any error exists.

3. The diagram tells the story. Feedforward is literally the path that runs forward from the setpoint instead of back from the error — one glance at the two-path diagram and the whole architecture is visible. (Rusty on the notation? Revisit Boxes, Arrows, Circles.)

4. Prediction is purchased with understanding. The model's constants — kS, kV, kA, kG — must be measured from the real mechanism. That's characterization, and WPILib's SysId turns it into a routine.

5. Use both. Feedforward carries the predictable bulk of the effort; feedback cleans up disturbances and model error. Big steady V_ff, small twitchy V_fb — that's the signature of a well-tuned mechanism.

plant — the mechanism
setpoint r — the goal
error e — r − output
kS — static friction (V)
kV — V per velocity
kA — V per acceleration
kG — gravity voltage (constant for elevators, ×cos θ for arms)
V = Vff + Vfb — prediction plus correction
Pocket glossary — the whole page, one line each. Further reading: the feedforward equations, the WPILib classes, and SysId characterization.