explainer
Feedforward control: predict torque, then correct error
Calculate model-based torque from a smooth speed reference, then add feedback correction. Compare feedforward, proportional feedback, and their combination under inertia error, drag error, and unknown external torque.
What you will learn
- Calculate feedforward torque from reference speed and acceleration.
- Separate predicted torque from feedback correction and external torque.
- Derive how model mismatch and unknown torque affect tracking error.
- Check the effort a reference requires before claiming physical feasibility.
Before you start
A motor needs torque to speed up and to balance drag. If you know the intended motion and have a useful model, you can calculate that torque before a tracking error develops. Feedback can then correct the difference between the plan and the measured motion.
You will calculate both command terms and see what happens when the model gets the inertia or drag wrong.
Predict effort from the planned motion
Feedforward control uses information about the desired motion or a known input to predict the required command. Reference feedforward uses the planned trajectory. It does not need the current tracking error to produce its torque.
Modern Robotics' torque-control explanation derives this idea from estimated robot dynamics and then adds feedback. An inaccurate model can produce inaccurate motion, so the measurement path still matters.
In this lesson, the feedforward model stays fixed while you change the actual joint. That separates a model's prediction from a controller's ability to correct error.
Define the speed model and torque signs
Use a horizontal rotating joint with inertia J and viscous drag B:
J ω̇ + B ω = τ + d
- ω is angular speed, in rad/s.
- J is inertia, in kg m².
- B is drag per angular speed, in N m s/rad.
- τ is motor torque, in N m.
- d is signed external torque, in N m. Positive d adds positive acceleration; negative d opposes it.
The controller uses nominal values Ĵ = 0.5 kg m² and B̂ = 1 N m s/rad. The actual plant can differ. The model-uncertainty lesson explains why a fitted value does not establish exact knowledge.
Every comparison starts at ω = 0. Measurement is exact, and the ideal actuator applies every commanded torque immediately. The model omits torque limits, gravity, dry friction, and actuator dynamics.
Give feedforward a smooth reference and its derivative
Let r be the desired speed. Raise it from zero to R = 1 rad/s over T seconds:
r(t) = R[1 − cos(πt/T)] / 2, for 0 ≤ t ≤ T
ṙ(t) = Rπ sin(πt/T) / (2T)
r(t) = R and ṙ(t) = 0 after T
Before the ramp, r and ṙ are zero. Speed and acceleration join continuously at both ends, so the torque model receives no impulsive acceleration demand. The next derivative changes at the joins; this reference does not promise continuous jerk.
At the default T = 2 s and t = 1 s, r = 0.5 rad/s and ṙ = π/4 = 0.785398 rad/s². Increasing T lowers the maximum reference acceleration. Trajectory time scaling extends that duration tradeoff to path motion.
Here, the trajectory generator supplies ṙ analytically. Differencing a noisy measured speed would produce a different signal. WPILib's feedforward documentation likewise requires consistent units and a defined desired acceleration when using an acceleration term.
Add predicted torque and measured-error correction
Substitute the desired motion into the nominal model, then form a proportional correction from e = r − ω:
τff = Ĵṙ + B̂r
τfb = Kp(r − ω)
τ = τff + τfb
Kp has units N m s/rad. Positive speed error produces positive correction torque. The feedback-control lesson develops that measurement path.
At t = 1 s in the matched example, τff = 0.5(0.785398) + 1(0.5) = 0.892699 N m. Exact tracking gives e = 0, so τfb = 0. The feedforward term supplies the whole command while the joint follows the reference.
If the actual inertia doubles to J = 1, the combined controller reaches ω = 0.394587 rad/s at that instant. With Kp = 2, feedback adds 2(0.5 − 0.394587) = 0.210825 N m, for total torque 1.103524 N m. That correction responds to measured error; the nominal feedforward term remains 0.892699 N m.
Derive the effect of model mismatch
For the combined controller, substitute ω = r − e into the actual plant equation:
J ė + (B + Kp)e = (J − Ĵ)ṙ + (B − B̂)r − d
This equation identifies three error sources directly. Inertia mismatch matters while the reference accelerates. Drag mismatch matters whenever reference speed is nonzero, and external torque contributes with the opposite sign to error.
With a matched model, d = 0, and e(0) = 0, the right side vanishes and e stays zero. A nonzero initial error would decay with time constant J/(B + Kp) in this particular damped model. Feedforward alone has no error-dependent command, even though the plant's positive drag can naturally dissipate an initial speed error.
All lab settings have J > 0, B > 0, and Kp ≥ 0. Their homogeneous speed error decays. A persistent forcing term can still leave an offset; homogeneous stability alone does not establish exact tracking.
Follow an unknown disturbance through the loop
At 4 s, the lab applies the selected external torque. No controller receives that value. With feedforward enabled and a constant final reference R, the combined controller approaches:
e∞ = [(B − B̂)R − d] / (B + Kp)
For feedforward alone, set Kp = 0. For feedback alone, remove the nominal drag contribution, giving e∞ = (BR − d)/(B + Kp). The long-time readout assumes the final reference and disturbance continue indefinitely.
With a matched plant and d = −0.5 N m, feedforward alone approaches ω = 0.5 rad/s. Combined control with Kp = 2 approaches ω = 0.833333 rad/s. Feedback reduces the error, but its proportional term needs a remaining error of 1/6 rad/s to supply 1/3 N m of correction.
The disturbance-to-speed transfer is 1/(Js + B + Kp) when feedback acts. Feedforward alone leaves it at 1/(Js + B): changing the reference command cannot cancel an unknown disturbance. Integral action can remove a constant offset in a suitable stable loop, subject to actuator limits.
Include gravity and loads only with the needed information
A richer model can predict more than inertia and drag:
- A gravity term needs the geometry, mass estimates, and the angle used to evaluate it.
- A known external torque d̂ enters this sign convention as a subtraction: τff = Ĵṙ + B̂r − d̂.
- A friction term needs a model that matches the relevant motion and contact regime.
Pure reference feedforward can evaluate these terms along the desired trajectory. Compensation that uses measured state also draws information from sensors; state that dependence when describing the controller. Modern Robotics makes this distinction when moving from desired-state feedforward to current-state computed-torque control.
Inverse dynamics expands the torque budget to a coupled arm. Friction models explain why a smooth viscous term cannot describe every low-speed effect. The lab deliberately gives its controllers no disturbance estimate.
Compare tracking and command contributions
The first graph compares all three controllers using the same actual plant, reference, disturbance, and initial state. The second graph separates the selected controller's feedforward and feedback torques. Selecting a mode changes the inspected response, while every parameter change recalculates a fresh experiment from rest.
Start with the matched model. Feedforward-only and combined tracking overlap the reference, while feedback-only needs an error to supply drag torque. At t = 2 s, feedback-only reaches 0.645286 rad/s; its long-time speed is 2/3 rad/s.
Next, inspect the inertia preset halfway through the ramp, then the drag preset during the hold. Finally, select the opposing-torque preset and replay past 4 s. Changing Kp shows what proportional correction can supply and what error remains.
The lab evaluates exact sinusoidal-forcing and constant-forcing solutions, joining them at T and 4 s. It preserves speed and controller torque across the disturbance step; acceleration changes there. The drawn lines and peak readouts sample those exact solutions, so the samples can miss extrema between points.
Check the torque a reference requires
Exact tracking in the actual plant requires:
τrequired(t) = Jṙ(t) + Br(t) − d(t)
|τrequired(t)| ≤ τavailable(t)
This required torque describes a hypothetical joint following the reference exactly. It can differ from the current controller command because the actual joint can have a different speed and acceleration. The lab calculates it using the true parameters and disturbance for comparison, without passing that information to the controller.
In the nominal two-second ramp without disturbance, the exact peak requirement is 0.5 + √[0.5² + (π/8)²] = 1.135777 N m. The plot's sampled peak is 1.135766 N m. A torque budget must cover the continuous peak, and a real actuator also has speed, power, and torque-rate limits.
The lab assumes unlimited instantaneous torque, so its response does not certify physical feasibility. At the external-torque step, exact tracking would require an immediate opposite motor-torque change. An unknown step cannot enter feedforward in advance, and a finite-rate actuator cannot make every instantaneous change.
If a controller reaches a limit, the comparison changes. Integral windup shows why saturation requires attention beyond tuning an unconstrained model.
Reproduce the feedforward comparison in Python
This standard-library program independently integrates the physical speed equation with fourth-order Runge–Kutta steps of 0.001 s. The reference ends at 2 s and the disturbance begins at 4 s, both on step boundaries. Each interval uses its own constant disturbance, including the interval that ends at 4 s.
from math import cos, pi, sin
def reference(t):
if t >= 2:
return 1.0, 0.0
return (1 - cos(pi * t / 2)) / 2, pi * sin(pi * t / 2) / 4
def run(mode, end, inertia=0.5, drag=1.0, disturbance=0.0):
gain = 0.0 if mode == "feedforward" else 2.0
use_ff = mode != "feedback"
speed, h = 0.0, 0.001
for n in range(round(end / h)):
t = n * h
external = disturbance if n >= 4000 else 0.0
def rate(time, value):
r, acceleration = reference(time)
ff = 0.5 * acceleration + r if use_ff else 0.0
torque = ff + gain * (r - value)
return (torque + external - drag * value) / inertia
a = rate(t, speed)
b = rate(t + h / 2, speed + h * a / 2)
c = rate(t + h / 2, speed + h * b / 2)
d = rate(t + h, speed + h * c)
speed += h * (a + 2 * b + 2 * c + d) / 6
r, acceleration = reference(end)
ff = 0.5 * acceleration + r if use_ff else 0.0
fb = gain * (r - speed)
return speed, ff, fb, ff + fb
cases = [
("matched", "combined", 1, {}),
("feedback", "feedback", 2, {}),
("inertia", "combined", 1, {"inertia": 1.0}),
("drag", "combined", 6, {"drag": 1.5}),
("unknown FF", "feedforward", 6, {"disturbance": -0.5}),
("unknown combined", "combined", 6, {"disturbance": -0.5}),
]
for label, mode, time, settings in cases:
# Suppress a minus sign on values that round to zero.
speed, ff, fb, torque = (
0.0 if abs(value) < 0.0000005 else value
for value in run(mode, time, **settings)
)
print(f"{label}: speed={speed:.6f}, ff={ff:.6f}, "
f"fb={fb:.6f}, torque={torque:.6f}")
Output:
matched: speed=0.500000, ff=0.892699, fb=0.000000, torque=0.892699
feedback: speed=0.645286, ff=0.000000, fb=0.709429, torque=0.709429
inertia: speed=0.394587, ff=0.892699, fb=0.210825, torque=1.103524
drag: speed=0.857143, ff=1.000000, fb=0.285714, torque=1.285714
unknown FF: speed=0.509158, ff=1.000000, fb=0.000000, torque=1.000000
unknown combined: speed=0.833334, ff=1.000000, fb=0.333331, torque=1.333331
Try it yourself
Exercise 1: separate nominal torque from a correction
Use Ĵ = 0.5, B̂ = 1, and Kp = 2 in the stated units. At one instant, r = 0.8 rad/s, ṙ = 0.4 rad/s², and measured ω = 0.7 rad/s. Find feedforward torque, feedback torque, and their sum; then find the acceleration for actual J = 1, B = 1.5, and d = −0.2 N m.
Check the torque budget and actual acceleration
Feedforward gives 0.5(0.4) + 1(0.8) = 1 N m. Feedback gives 2(0.8 − 0.7) = 0.2 N m, so the motor applies 1.2 N m. Actual acceleration is [1.2 − 0.2 − 1.5(0.7)]/1 = −0.05 rad/s². The desired positive acceleration does not establish that the actual joint achieves it.
Exercise 2: calculate the remaining constant error
After the ramp, let R = 1 rad/s, B = B̂ = 1, and d = −0.5 N m. Find the long-time speed for feedforward alone and for combined control with Kp = 2. If an exact disturbance estimate were available, what extra feedforward torque would preserve zero-error tracking?
Check the offset and the known-load correction
Feedforward alone commands 1 N m, so speed approaches (1 − 0.5)/1 = 0.5 rad/s. Combined control gives error 0.5/(1 + 2) = 1/6 rad/s and speed 5/6 rad/s. A known disturbance estimate d̂ = −0.5 requires subtracting d̂, adding 0.5 N m to feedforward. That cancellation depends on knowing the signed torque and applying it in time.
Sources and further study
- Lynch and Park, Modern Robotics, torque-input control, Part 3: model-based reference torque, measured-state compensation, and combined control.
- WPILib, Feedforward Control: velocity and acceleration setpoints, consistent gain units, and mechanism-specific feedforward models. Its examples commonly command voltage; this lesson commands torque.
Continue with cascade control to place the motion reference, torque prediction, and feedback correction within nested loops.