Hands-on practice is essential. None of this material sticks until you have made an oscillation happen, then suppressed it.
- Simulate a PID in Python with
scipy.signalor thecontrollibrary. Plant: . Try P-only, PI, then PID. Watch the step response evolve. Tune for a target overshoot of 10% and settling time of 5 seconds.
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lti, step
# Plant
plant = lti([1], [1, 1, 1])
t, y = step(plant)
plt.plot(t, y, label='open-loop step')
# Closed-loop with PID
from scipy.signal import TransferFunction, feedback
Kp, Ki, Kd = 5, 2, 1
pid = TransferFunction([Kd, Kp, Ki], [1, 0])
loop = pid * plant # series
# closed-loop = loop / (1 + loop) -- this needs careful symbolic handling-
Plot a Bode plot by hand for . Identify breakpoints, sketch asymptotic magnitude (sloping at dB/dec at corners 0, 1, 10 rad/s). Compute phase margin and gain margin.
-
Sketch a root locus for . Three open-loop poles, no zeros. Three branches; one departs from the origin to the right, the other two break away from the real axis between 0 and -2 toward asymptotes. At what does the locus cross the imaginary axis? (Use Routh-Hurwitz on the characteristic polynomial .)
-
Tune a real PID on a 3D printer or DIY oven. Most printer firmware (Marlin, Klipper) has a
M303command that automates a Ziegler-Nichols-style identification. -
Build a self-balancing project. Inverted pendulum on a cart, two-wheel balancing robot, or "Segway clone." Use an Arduino + IMU + motor driver. Implement state feedback or PID. This is the canonical "your first control system" project; it teaches you everything.
-
Implement a Kalman filter to fuse accelerometer and gyroscope into a clean attitude estimate. Compare to the raw sensors. The Kalman estimate will be much smoother and more stable.
-
Sketch the Nyquist plot for for and . Find the at which encirclement of first occurs. Compare with Routh-Hurwitz prediction.
-
Convert a transfer function to state-space with Python's
scipy.signal.tf2ss. Verify the eigenvalues of equal the poles. Place new poles withscipy.signal.place_polesand verify the closed-loop matrix has the desired eigenvalues.
When all of the above feel mechanical, you have the chapter under your belt.