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.

By 12 min read

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

Predict the effort, correct the error

What does the model supply before an error appears?

Compare three controllers on the same speed reference. Change the actual plant while the feedforward model stays fixed.

0.50
1.00
2.00
2.00
0.00
2.00

J ω′ + B ω = τ + d. Positive d assists positive acceleration. All runs start at zero speed. The nominal model stays at Ĵ = 0.5 kg m² and B̂ = 1 N m s/rad; its command uses the reference and its exact derivative.

Feedforward: τff = 0.5 r′ + r. Feedback: τfb = Kp(r - ω). Combined: τ = τff + τfb. Unused terms are zero. The external torque changes at 4 s and is unknown to every controller.

Feedforward onlyFeedback onlyCombinedReference
Feedforward, feedback, and combined speed trackingAll controllers share the same actual plant, smooth speed reference, and external torque. The reference reaches one radian per second after the ramp. External torque changes at four seconds. The circle marks the selected controller at 2.000000 seconds.-0.50.51.5048Speed (rad/s)Time (s)
With a matched model and no disturbance, feedforward-only and combined tracking coincide with the reference. Lines join exact values at 0.02 s intervals. The circle marks the selected controller. Plot scales adjust to the data.
FeedforwardFeedbackTotal torque
Selected controller torque contributionsThe selected Feedforward + feedback controller applies 1.000000 newton meters at 2.000000 seconds. Orange is feedforward, purple is feedback, and blue is their sum. Unused contributions are zero.-0.50.51.5048Torque (N m)Time (s)
The controller applies the sum of the two terms. Curves can coincide when a contribution is zero. External torque is a separate plant input; it is absent from this controller's command.
Reference speed (rad/s)
1.000000
Reference acceleration (rad/s²)
0.000000
Actual speed (rad/s)
1.000000
Tracking error (rad/s)
0.000000
Feedforward torque (N m)
1.000000
Feedback torque (N m)
0.000000
Total motor torque (N m)
1.000000
External torque d (N m)
0.000000
Required reference torque (N m)
1.000000
Long-time error (rad/s)
0.000000
Sampled peak |motor torque| (N m)
1.135766

Proportional feedback corrects measured speed error. A constant mismatch can still require a nonzero error to supply the correction. The long-time error assumes the final reference and selected external torque remain constant after this plot ends.

The required reference torque J r′ + B r - d assumes exact tracking. It is a comparison calculation using the true plant and disturbance, which the controller does not know. The actuator applies every commanded torque immediately, without limits or noise. Check physical torque and rate limits before using such a trajectory on hardware. Sampled peaks cover only the eight-second trace and can miss extrema between samples.

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

Continue with cascade control to place the motion reference, torque prediction, and feedback correction within nested loops.