Skip to the content
FixTheRobot Explore Robotics / UAV / Module 5 / M5-T6

UAV Systems Engineering · Module 5

M5-T6: PID controllers

Most flying robots are held up by PID loops: understand what each term does, why the loops are cascaded, and how integrators go wrong.

  • Lesson 6 of 17
  • UAV Guidance, Navigation and Control (GNC)
  • Established engineering
  • About 7 minutes to read

PID is old, unglamorous and everywhere. A well-tuned cascade of PID loops is the baseline that any fancier controller has to beat, and a poorly understood integrator is behind more crashes than any exotic algorithm.

Learning objectives

After this lesson, you should be able to:

  • Explain the effect of the PP, I and DD terms and of each on stability and steady-state error.
  • Describe the cascaded structure of a multirotor controller and why inner loops must be faster.
  • Implement a PID with derivative filtering and anti-windup.
  • Tune an attitude loop on a simulated plant and measure rise time, overshoot and settling.

Conceptual explanation

A PID controller computes a command from the error e=r−ye=r-y: PP pushes proportionally to the error, I accumulates the error to remove steady offsets, DD reacts to the rate of change to add damping.

Cascaded PID for a multirotorPosition error feeds a position P controller giving a velocity setpoint, which feeds a velocity PID giving a tilt setpoint, which feeds an attitude P controller giving a rate setpoint, which feeds a rate PID giving torque. Each inner loop is faster than the one outside it.Position PVelocity PIDAttitude PRate PIDMixer slowest (~1–5 Hz bandwidth)fastest (tens of Hz)setpoint → position → velocity → attitude → body rate → motors: each loop at least several times faster than its outer loop
The cascade of a typical multirotor controller. Each loop treats the next inner loop as an (approximately) instantaneous actuator.

A multirotor cascades loops because its dynamics do: torque changes angular rate, rate changes attitude, attitude changes acceleration, acceleration changes velocity and position. Each stage is one integrator from the next, and each can be tuned separately.

Animation of a PID position loop with a saturating actuator following a large step. Without anti-windup the integrator state keeps accumulating while the output is clamped, so after the vehicle reaches the target the stored integral keeps pushing it past the target and the overshoot is large. With anti-windup clamping, the integrator stops accumulating during saturation and the overshoot is small. Strips show the position, the actuator command and the integrator state for both.
While uu is clamped the error keeps integrating (II ← I +Ki e+K_{i}\,e dt); on approach the stored integral must be unwound by an opposite error, producing overshoot. Anti-windup (clamping or back-calculation) freezes or bleeds the integrator while saturated. Double integrator, ∣u∣≤1|u|\le 1, PID gains tuned for the unsaturated case; computed. (Looping, about 6 seconds; drawn and computed for this course with illustrative values.)

Engineering intuition

Think of PP as a spring, DD as a damper, and I as a slowly tightening bias that lifts the vehicle against a constant load such as wind or an off-centre battery. A spring alone oscillates; add damping to calm it; add an integrator to remove the remaining offset, but be gentle: an integrator is a source of lag and a classic cause of overshoot after saturation.

The key engineering rule: inner loops must be faster than outer loops, by a factor of roughly 3–10. An outer loop that tries to move faster than the inner loop can follow sees phase lag and oscillates. When a UAV “wobbles” slowly, the outer loop is too aggressive; when it buzzes, the inner derivative gain is too high or the signal is noisy.

Mathematical formulation

The PID law

Continuous time
u(t)=Kp e(t)+Ki∫0te(τ) dτ+Kd de(t)dtu(t) = K_p\,e(t) + K_i \int_{0}^{t} e(\tau)\,\mathrm{d}\tau + K_d\,\frac{\mathrm{d}e(t)}{\mathrm{d}t}
The command u is the sum of a proportional, an integral and a derivative term of the error e = reference − measurement.
Discrete time (sample period T)
uk=Kp ek+Ki T∑j=0kej+Kd ek−ek−1Tu_k = K_p\,e_k + K_i\,T \sum_{j=0}^{k} e_j + K_d\,\frac{e_k - e_{k-1}}{T}
The integral becomes a running sum multiplied by T, and the derivative a backward difference.

Derivative on measurement with filtering

Differentiating a noisy measurement amplifies noise, and differentiating the error makes the output kick on every setpoint step. Use the derivative of the measurement, low-pass filtered with time constant τf\tau _{f} (often Td/NT_{d}/N with NN in 5–20):

Filtered derivative of the measurement
dk=α dk−1+(1−α) yk−yk−1T,α=τfτf+Td_k = \alpha\, d_{k-1} + (1-\alpha)\,\frac{y_k - y_{k-1}}{T}, \qquad \alpha = \frac{\tau_f}{\tau_f + T}
Control law with the filtered derivative
uk=Kp ek+Ik−Kd dku_k = K_p\,e_k + I_k - K_d\,d_k

Anti-windup

When the actuator saturates, the integrator keeps accumulating and then must be “unwound” after saturation ends, causing overshoot. Two standard fixes are clamping (stop integrating when saturated and the error would push further into saturation) and back-calculation (feed the saturation difference back into the integrator):

Back-calculation anti-windup
Ik=Ik−1+Ki T ek+Kt T (usat−u),Kt≈1TtI_k = I_{k-1} + K_i\,T\,e_k + K_t\,T\,(u_{\text{sat}} - u), \qquad K_t \approx \frac{1}{T_t}
Tt is the tracking time constant: how quickly the integrator is pulled back when the actuator saturates.

Attitude loop as an example

For the rotational plant J ω˙=τJ\,\dot{\omega }=\tau with θ˙=ω\dot{\theta }=\omega , a PD law τ=J(Kp e−Kd ω)\tau =J(K_{p}\,e-K_{d}\,\omega ) gives θ¨+Kd θ˙+Kp θ=Kp θref\ddot{\theta }+K_{d}\,\dot{\theta }+K_{p}\,\theta =K_{p}\,\theta _{\text{ref}}, a second-order system with natural frequency ωn=Kp\omega _{n}=\sqrt{K_{p}} and damping ratio ζ=Kd/(2Kp)\zeta =K_{d}/(2\sqrt{K_{p}}). The Quadrotor Dynamics Simulator uses exactly this structure. Choose ζ≈0.7–1\zeta \approx 0.7\text{–}1 and ωn\omega _{n} within what the motors and their lag can deliver.

Architecture and data flow

In PX4 the cascade runs as separate modules (position and velocity control in the multicopter position controller; attitude and rate control in their own modules), each with its own parameters. The rate loop runs fastest on the main flight-controller loop; position loops run slower. Knowing where to look for each loop’s gains is the first debugging step: consult the PX4 multicopter PID tuning guide for the exact parameters of your version.

Real-world UAV applications

  • Attitude and rate stabilisation in every multirotor autopilot.
  • Altitude hold with barometer and accelerometer inputs.
  • Gimbal and camera stabilisation.
  • Baseline against which LQR and MPC results are judged (lessons M5-T7, M5-T8).

Implementation example

This runs as shown. A PID with filtered derivative-on-measurement and back-calculation anti-windup controls a double-integrator attitude plant with a torque limit. The test shows the windup problem and its fix.

import numpy as np

class PID:
    def __init__(self, kp, ki, kd, dt, umax, tau_f=0.02, kt=None):
        self.kp, self.ki, self.kd, self.dt, self.umax = kp, ki, kd, dt, umax
        self.alpha = tau_f / (tau_f + dt); self.d = 0.0; self.I = 0.0; self.y_prev = None
        self.kt = kt                                            # None: no anti-windup
    def __call__(self, r, y):
        e = r - y
        dy = 0.0 if self.y_prev is None else (y - self.y_prev) / self.dt
        self.y_prev = y
        self.d = self.alpha * self.d + (1 - self.alpha) * dy       # filtered derivative of the MEASUREMENT
        u = self.kp * e + self.I - self.kd * self.d
        u_sat = float(np.clip(u, -self.umax, self.umax))
        self.I += self.ki * self.dt * e
        if self.kt is not None: self.I += self.kt * self.dt * (u_sat - u)   # back-calculation
        return u_sat

def simulate(kt, step=1.0, T=8.0, dt=0.002, J=0.01, umax=0.06):
    pid = PID(kp=0.12, ki=0.15, kd=0.03, dt=dt, umax=umax, kt=kt)
    th = w = 0.0; hist = []
    for k in range(int(T / dt)):
        tau = pid(step if k * dt > 0.5 else 0.0, th)
        w += tau / J * dt; th += w * dt
        hist.append(th)
    hist = np.array(hist)
    return hist.max() / step - 1, abs(hist[-1] - step)

os_no, err_no = simulate(kt=None)
os_aw, err_aw = simulate(kt=1.0 / 0.05)
print(f"overshoot without anti-windup {100*os_no:.0f}%, with {100*os_aw:.0f}%; final error {err_no:.3f} / {err_aw:.3f} rad")
assert os_aw < os_no                                           # anti-windup reduces overshoot after saturation
assert err_aw < 0.05
PID with filtered derivative on measurement and back-calculation anti-windup, tested on a torque-limited plant.

Common engineering mistakes

Integrating while saturated

Causes large overshoot after saturation; add anti-windup and report saturation to the controller.

Differentiating the error

A setpoint step becomes a huge derivative kick. Differentiate the measurement.

Unfiltered derivative on a noisy signal

Amplifies gyro noise and heats motors; low-pass the derivative.

Tuning the outer loop first

Tune from the inside out: rate, then attitude, then velocity, then position.

Ignoring plant changes

Mass and battery voltage change the effective gain; a tune that works at full battery oscillates at low battery or with payload.

Failure modes

Failure modes, symptoms and causes
Failure modeHow it shows upLikely cause
Slow oscillation (1–3 Hz)Whole vehicle drifts back and forthPosition or velocity loop too aggressive for the attitude loop
Fast buzzHigh-pitched vibration, hot motorsDD gain or rate-loop PP too high, noisy gyro or resonant frame
Overshoot after saturationLarge swing following a hard manoeuvreIntegrator windup
Steady-state offset in windVehicle holds a few metres off targetNo integral action or integrator limited too tightly

Debugging approach

  1. Plot setpoint, measurement and output for each loop; look for ringing frequency to identify which loop is oscillating.
  2. Do step tests on one loop at a time with the outer loops open.
  3. Inspect the gyro spectrum for frame resonances and add notch filters if needed.
  4. Check that the log shows controller saturation flags and compare to integrator state.

Practical exercises

  1. Derive ωn\omega _{n} and ζ\zeta for the PD attitude loop and choose gains for ωn=20 rad/s\omega _{n}=20\,\mathrm{rad}/\mathrm{s}, ζ=0.8\zeta =0.8.
  2. Remove derivative filtering in the code and measure the effect on output noise when the measurement has noise.
  3. Add a constant disturbance torque and show that the integral term removes the offset.
  4. Use the Quadrotor Dynamics Simulator to find Kp at which the attitude loop starts to oscillate with a 0.12 s motor time constant.

Interview questions

Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.

Question 1

Why are multirotor controllers cascaded, and what must hold between loops?

Model answer

The dynamics are a chain of integrators from torque to position, so each stage is controlled by a loop whose setpoint comes from the outer loop. Inner loops must be several times faster than the outer ones or phase lag makes the outer loop oscillate.

Question 2

What is integrator windup and how do you prevent it?

Model answer

The integral term keeps accumulating while the actuator is saturated and then overshoots when saturation ends. Prevent it by clamping the integrator or using back-calculation from the saturation difference.

Question 3

Why take the derivative of the measurement rather than the error?

Model answer

A setpoint step would create an impulse in the derivative of the error (derivative kick); the measurement is smooth, so it avoids the kick while still providing damping.

References and further reading

Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.

What’s next

Mark this lesson

Saved in this browser and synced to your account when you are signed in.

Practice this lesson

M5-L1: Implement PID attitude control

Tune a PID attitude loop on the quadrotor.

Practice this lesson

M5-L4: Compare PID, LQR and MPC

Compare the three on one reference.

Next lesson

M5-T7: LQR control

Derive and apply LQR to a linearised hover model, choosing weights with Bryson's rule and checking stability.