MLTE03 · Week 5 · Lecture + Lab

Attitude Control

PID & the cascade inner loop · 姿態控制

Flight Dynamics & Intelligent Control Technologies
Make the quad not tip over — the first loop that flies.

Recap → today

From a model to a flying machine

  • Wk 2–3: the 12-state nonlinear model \( \dot{\mathbf{x}}=f(\mathbf{x},\mathbf{u}) \).
  • Wk 4: hover trim & the linearized \( (A,B) \) — and its unstable poles.
  • Today: the first feedback loop that tames those poles — attitude control.
The whole course in one line:
open loop ⇒ the quad tips over. The job of control is to make the unstable plant track what we ask.

Learning objectives

By the end of today you can…

  • State the PID control law and what each term does — and its failure modes.
  • Explain the cascade architecture: slow outer position loop, fast inner attitude loop.
  • Derive the inner attitude loop: attitude error → body torques.
  • Predict response from gains via \( \omega_n,\ \zeta \) — and tune them.
  • Lab Implement the inner loop in StudentController and stabilize a tilt.

Why attitude first

A quad is underactuated

  • 4 motors → 4 controls: total thrust \(T\) and 3 body torques \( \boldsymbol{\tau} \).
  • But 6 DOF to place (position + attitude). You cannot push sideways directly…
  • …you tilt, then thrust. So horizontal motion is a consequence of attitude.
  • ⇒ Attitude is the inner, fastest loop. Everything else rides on it.
Control wrench (what your controller returns): \[ \mathbf{u} = \begin{bmatrix} T \\ \tau_x \\ \tau_y \\ \tau_z \end{bmatrix} \] The simulator mixes this into 4 motor thrusts and saturates them for you.

The tool

PID in one slide

\[ u(t) = \underbrace{K_p\, e(t)}_{\text{now}} \;+\; \underbrace{K_i\!\int_0^t\! e\,d\tau}_{\text{past}} \;+\; \underbrace{K_d\, \dot e(t)}_{\text{future}}, \qquad e = x_{\text{ref}} - x \]
  • P — stiffness; pulls toward the target. Too high ⇒ oscillation.
  • D — damping; resists velocity. Tames overshoot; amplifies noise.
  • I — kills steady-state bias (gravity, wind). Too high ⇒ windup.
For attitude we use mostly PD: the rate term \(K_d\) is our damping, and there's little steady bias on angle. Integral comes back on altitude next week.

Architecture

The cascade: two nested loops

   position ref ─►┌───────────────┐ desired tilt ┌───────────────┐  torques ┌──────────┐
                  │  OUTER loop   ├────φ_d,θ_d───►│  INNER loop   ├──τx,τy,τz►│  mixer   ├─► motors
   (slow ~5 Hz)  └▲──────────────┘  + thrust T   └▲──────────────┘ (fast)   └──────────┘
       position ──┘  state x                attitude ──┘
  
  • This week: the inner loop (attitude → torques). Next week: the outer loop (position → desired tilt).
  • The mixer (Wk 3, params.mixer_matrix()) turns your wrench into 4 motor thrusts.

The derivation

Inner attitude loop → body torques

Rotational dynamics (Euler): \( \mathbf{I}\dot{\boldsymbol\omega} = \boldsymbol\tau - \boldsymbol\omega\times \mathbf{I}\boldsymbol\omega \). Near hover the gyroscopic term is small, so to command an angular acceleration we just pick \( \boldsymbol\tau \).

Drive the attitude \( \boldsymbol\eta=(\phi,\theta,\psi) \) to a desired \( \boldsymbol\eta_d \) with a PD law:

\[ \boldsymbol\tau = \mathbf{I}\Big( K_p^{\text{att}}\,(\boldsymbol\eta_d-\boldsymbol\eta) \;+\; K_d^{\text{att}}\,(-\boldsymbol\omega) \Big) \]
Per axis this is exactly the line you'll write in cascade_pid.py:
tau = p.inertia * (kp_att * e_att + kd_att * (-omega))

Why it behaves

One axis = a second-order system

Take roll alone. The inertia cancels and the closed loop is the textbook mass-spring-damper:

\[ \ddot\theta = K_p(\theta_d-\theta) - K_d\,\dot\theta \;\;\Longrightarrow\;\; \omega_n=\sqrt{K_p}, \quad \zeta=\frac{K_d}{2\sqrt{K_p}} \]
\( \zeta<1 \) underdamped — fast but overshoots / rings.
\( \zeta\approx1 \) critical — fastest with no overshoot. The sweet spot.

The reference autopilot uses \( K_p=180,\ K_d=28 \Rightarrow \zeta\approx1.04 \). Let's feel it ▶

Interactive · drag the sliders

Tune the inner loop, live

Try: drop \(K_d\) to 4 → ringing. Push \(K_p\) to 360 → faster but twitchy. Hit ↯ Disturb to kick it.

Second half · hands-on

Now you build it

  • Open the Week 5 lab sheet → edit quadsim/controllers/student.py.
  • Add the inner attitude loop; return \( [T,\tau_x,\tau_y,\tau_z] \).
  • Run python examples/02_hover_pid.py --controller student.
  • Checkpoint: from a tilted start, attitude returns to level. ⭐ graded.

Open the Week 5 lab sheet →

Wrap-up

What to remember

  • A quad is underactuated → control attitude first, position rides on it.
  • Inner loop is a PD law: \( \boldsymbol\tau = \mathbf{I}(K_p^{\text{att}}\mathbf{e}_\eta - K_d^{\text{att}}\boldsymbol\omega) \).
  • One axis is second-order: gains set \( \omega_n,\ \zeta \). Aim near \( \zeta\approx1 \).
  • Next week: wrap the outer position loop around this → the full autopilot.

Reading: Quan Quan Ch. 6 · Beard & McLain Ch. 6. Deliverable & deadline on the lab sheet.