explainer
Cascade control: let position request velocity and velocity request torque
Build cascade control from nested position and velocity loops. Follow command units, compare finite inner dynamics with ideal velocity tracking, and calculate the effect of an opposing load.
What you will learn
- Follow the units of commands through nested control loops.
- Derive the inner velocity response and the complete position dynamics.
- Explain why faster inner response alone does not guarantee ideal tracking.
- Calculate steady position error from a constant opposing load.
Before you start
A position controller can ask for a velocity. A velocity controller can turn that request into torque. Each layer measures a different quantity, so each can correct an error near the part of the system it controls.
The connection only works as intended when the outer controller accounts for the inner loop's response. This lesson makes that response visible.
Build cascade control from nested feedback loops
In cascade control, an outer controller sets the reference for an inner controller. The inner loop closes around an intermediate quantity; the outer loop closes around the final quantity of interest.
A motor-driven joint can use three nested layers:
- The position loop compares desired and measured angle, then requests velocity.
- The velocity loop compares requested and measured speed, then requests torque or current.
- The current loop compares requested and measured current, then commands drive voltage.
MathWorks' electric-drive example uses a speed loop around a faster current loop. An outer position loop can then treat the complete speed loop as part of its plant. These are separate feedback paths, not merely a chain of gains with one sensor at the end.
Keep command units visible at every layer
For the two proportional controllers in this lesson:
| Signal or gain | Meaning | Units |
|---|---|---|
| r, q | Desired and measured position | rad |
| Kq | Position gain | 1/s |
| ωref = Kq(r − q) | Requested velocity | rad/s |
| Kv | Velocity gain | N·m·s/rad |
| τ = Kv(ωref − ω) | Requested torque | N·m |
The position controller does not send its numeric output directly to a torque actuator. The velocity controller interprets it as a speed reference and produces a torque command with its own gain.
For a direct-drive motor with torque constant Kt, a torque request can become a current reference through iref = τ/Kt. For example, 1.2 N·m and Kt = 0.2 N·m/A give 6 A. A current controller then uses voltage to make the measured current follow that request; actuator dynamics explains why current cannot change instantly in a real inductive motor.
Define the two loops that the lab simulates
Use a horizontal joint with fixed inertia J = 0.5 kg·m² and viscous damping B = 1 N·m·s/rad. It has no gravity or elastic restoring torque. Positive load d opposes positive rotation:
q̇ = ω
Jω̇ = τ − Bω − d
ωref = Kq(r − q)
τ = Kv(ωref − ω)
The reference steps to r = 1 rad at time zero. Position and velocity start at zero. At 4 s, the load changes from zero to the selected value and stays there.
The simulation includes only position and velocity feedback. An ideal current/torque actuator applies τ immediately, with no torque or voltage limit. It assumes exact measurements and continuous-time controllers; there is no simulated current-control state.
Close the velocity loop first
Temporarily open the position loop and supply an independent velocity reference. Substituting the velocity controller into the joint equation gives:
Jω̇ + (B + Kv)ω = Kvωref − d
Ginner(s) = Ω(s)/Ωref(s) = Kv / (Js + B + Kv), with d = 0
The inner time constant is J/(B + Kv). Its pole is −(B + Kv)/J. Its DC gain is Kv/(B + Kv), which is below one because B is positive.
At the default Kv = 4, the time constant is 0.1 s, the pole is −10/s, and the DC gain is 0.8. A constant 1 rad/s request with no load therefore produces 0.8 rad/s after the transient. Increasing Kv changes both response speed and tracking gain.
For a constant load, the independent inner-loop equilibrium becomes ω = (Kvωref − d)/(B + Kv). The inner controller reacts to velocity error caused by that load, but its proportional rule leaves a residual velocity error when holding a nonzero torque.
Include inner dynamics in the position loop
Close the position loop around the velocity loop and the physical integration from velocity to position:
Jq̈ + (B + Kv)q̇ + KvKq q = KvKq r − d
p(s) = Js² + (B + Kv)s + KvKq
Every coefficient is positive over the lab's positive gain range, so both closed-loop poles have negative real parts. All selectable models are asymptotically stable. Their natural frequency and damping ratio are:
ωn = √(KvKq/J)
ζ = (B + Kv) / [2√(JKvKq)]
The defaults give ωn = 4 rad/s and ζ = 1.25. Keeping Kv = 4 and raising Kq from 2 to 8 gives ωn = 8 rad/s and ζ = 0.625. The response becomes underdamped and can overshoot, while this ideal model remains stable.
For these simple proportional loops, expanding the command gives τ = KvKq(r − q) − Kvω. It has the same algebraic form as position PD control with derivative action on measurement. The cascade drawing adds meaning to the intermediate velocity request; it does not create different dynamics from the expanded equation.
Follow an opposing load through both loops
At the load step, position and velocity stay continuous. The acceleration changes immediately by −d/J. Velocity error then changes the inner torque, and the resulting position error changes the outer velocity request.
At a stationary equilibrium, ω = 0 and τ = d. Combining both controller equations gives:
r − q∞ = d/(KvKq)
ωref,∞ = d/Kv
τ∞ = d
With d = 0.5 N·m, Kv = 4, and Kq = 2, the position error approaches 0.0625 rad. The joint stops at 0.9375 rad while the outer loop still requests 0.125 rad/s. That velocity error supplies the holding torque through Kv.
A different disturbance entry point gives a different response. For example, a position-sensor bias changes the outer error directly; the inner velocity sensor cannot identify that bias. Use the signal paths in the block diagram to state where a disturbance or sensor error enters.
Check the ideal-velocity approximation
If velocity follows its request perfectly, the outer loop becomes q̇ = Kq(1 − q). Starting from rest gives qideal(t) = 1 − exp(−Kq t). Modern Robotics' velocity-input control lesson derives this first-order position-error behavior.
The ideal comparison assumes unlimited, instantaneous velocity authority. It keeps its response even when the modeled mechanical load changes. Realizing its initial velocity jump would require an impulse under the finite-inertia model, so it serves as an approximation to assess, not an actuator command to reproduce exactly.
The lab reports the ratio (B + Kv)/(JKq): the inner decay rate divided by the ideal outer decay rate. This is not a measurement of the complete cascade's bandwidth. A large ratio can suggest separated response times, but the inner DC gain and disturbance response still matter.
For example, Kq = 0.5 and Kv = 0.5 give a rate ratio of 6 while the inner DC gain is only 1/3. With the default load, their steady position error is 2 rad. That large error shows why a response-time ratio alone cannot certify good tracking.
The control bandwidth lesson defines a cutoff from the closed-loop frequency response. It separates that cutoff from DC tracking gain, loop crossover, and the torque needed to follow a reference.
Compare the requests with the resulting motion
The signal diagram shows the two feedback paths and the load entry point. The position plot compares the finite inner loop with an ideal velocity source. The velocity plot separates the request from the measured motion, including their difference after the joint approaches rest.
At the default replay time of 6 s, q = 0.939018 rad, ω = −0.003036 rad/s, and ωref = 0.121964 rad/s. Their velocity error is 0.125000 rad/s, which gives τ = 0.500000 N·m after rounding. The ideal position is 0.999994 rad.
Try the lower and higher inner-gain presets at the same replay time. Then keep the inner loop fixed and select the aggressive outer request. Compare the damping ratio with the full position trace; a single point near the target does not describe the whole transient.
Tune from the inside out and check the full system
First establish the current/torque actuator's behavior. Then open the outer position loop and tune the velocity loop against independent requests and relevant disturbances. Close the position loop and tune it using that measured or modeled inner response. MathWorks' multiloop design workflow follows this inner-first sequence.
A faster inner loop can reduce intermediate errors before they accumulate into large outer errors. The amount of useful speed separation depends on phase lag, tracking gain, disturbances, and actuator authority. Recheck the complete closed loop after changing either controller; no fixed bandwidth ratio guarantees acceptable behavior for every system.
Both simulated controllers use P only. Adding integral action can remove certain steady errors, but it adds controller state and changes the stability analysis. Feedforward control can supply predictable effort when a suitable model is available.
The lab has no saturation. In hardware, an outer velocity request may exceed what the inner actuator can deliver. If a loop contains an integrator, its anti-windup mechanism must account for the relevant unavailable command and the units at that interface; review integral windup before treating command clipping as a complete solution.
Reproduce the cascade response in Python
The normalized second-order step S(t) solves the complete position equation with zero initial state and no load. Linearity then gives q(t) = S(t) − [d/(KvKq)]S(t − 4), where S is zero before its step. This program evaluates the underdamped, critical, and overdamped forms using the standard library.
from math import cos, exp, expm1, sin, sqrt
def step(kq, kv, t):
if t <= 0:
return 0.0, 0.0
alpha = 1.0 + kv # (B + Kv)/(2J), J=0.5, B=1
wn2 = 2.0 * kv * kq
delta = wn2 - alpha**2
if delta > 0:
beta = sqrt(delta)
c = exp(-alpha*t) * cos(beta*t)
s = exp(-alpha*t) * sin(beta*t) / beta
elif delta == 0:
c = exp(-alpha*t)
s = t*c
else:
beta = sqrt(-delta)
slow = -wn2 / (alpha + beta)
c = exp(slow*t) * (1 + exp(-2*beta*t)) / 2
s = exp(slow*t) * (-expm1(-2*beta*t)) / (2*beta)
return 1-c-alpha*s, wn2*s
def response(kq, kv, d=0.5, t=6.0):
q, omega = step(kq, kv, t)
qd, wd = step(kq, kv, t-4)
q -= d/(kv*kq)*qd
omega -= d/(kv*kq)*wd
request = kq*(1-q)
torque = kv*(request-omega)
return q, omega, request, torque
for name, kq, kv in (("default", 2, 4), ("lower inner", 2, 0.5),
("higher inner", 2, 8), ("faster outer", 8, 4),
("critical", 2, 1)):
q, omega, request, torque = response(kq, kv)
print(f"{name}: q={q:.6f}, omega={omega:.6f}, "
f"request={request:.6f}, torque={torque:.6f}")
print(f"ideal position at 6 s: {1-exp(-2*6):.6f}")
print(f"default steady error: {0.5/(4*2):.6f}")
Output:
default: q=0.939018, omega=-0.003036, request=0.121964, torque=0.500000
lower inner: q=0.621226, omega=-0.112074, request=0.757548, torque=0.434811
higher inner: q=0.969397, omega=-0.001294, request=0.061206, torque=0.500000
faster outer: q=0.984376, omega=0.000001, request=0.124995, torque=0.499977
critical: q=0.772815, omega=-0.036484, request=0.454371, torque=0.490854
ideal position at 6 s: 0.999994
default steady error: 0.062500
Try it yourself
Exercise 1. At one instant, q = 0.8 rad and ω = 0.1 rad/s. Use the default gains and a load of 0.5 N·m. Calculate both errors, the velocity request, the torque command, and acceleration.
Check the two command interfaces
Position error is 1 − 0.8 = 0.2 rad. The outer loop requests 2 × 0.2 = 0.4 rad/s. Velocity error is 0.4 − 0.1 = 0.3 rad/s, so the inner torque command is 4 × 0.3 = 1.2 N·m. Acceleration is (1.2 − 1 × 0.1 − 0.5)/0.5 = 1.2 rad/s².
Exercise 2. Set Kq = 0.5/s and Kv = 0.5 N·m·s/rad. Keep d = 0.5 N·m. Find the inner time constant, inner DC gain, rate ratio, and final position. Explain why the rate ratio does not imply ideal velocity tracking.
Check response speed and tracking gain separately
The inner time constant is 0.5/(1 + 0.5) = 1/3 s, and its DC gain is 0.5/1.5 = 1/3. Its decay rate is 3/s, so the ratio to Kq is 3/0.5 = 6. Steady position error is 0.5/(0.5 × 0.5) = 2 rad, giving q∞ = 1 − 2 = −1 rad. The finite proportional inner gain needs velocity error to provide holding torque; the rate ratio alone does not describe that error.
Sources and further study
- MathWorks, Design Multiloop Control System: isolate and tune the inner loop, then tune the outer loop around it.
- MathWorks, Tune an Electric Drive: nested speed/current architecture and the assumptions behind replacing an inner loop with unity tracking.
- Lynch and Park, Modern Robotics 11.3, part 1: position feedback when velocity is the control input.
Continue with actuator dynamics to replace ideal torque delivery with electrical and mechanical states, or revisit PID control before adding integral action inside either loop.