第13章
实战案例
这一章做什么
把前面的知识串起来,完整走几遍「需求 → 建模 → 设计 → 仿真 → 实现要点」。每个案例都标出了用到的章节,遇到不熟的地方可以跳回去看。案例里的数字都来自仿真(代码给出),换成你自己设备的参数照样能用。
- 案例一:直流电机速度控制和位置控制(PI、串级、抗饱和、采样频率);
- 案例二:加热器温度控制(阶跃测试、SIMC、只能加热、抗饱和、安全保护);
- 案例三:小车倒立摆 / 平衡小车(状态空间、LQR、非线性仿真、执行器限幅);
- 案例四:多旋翼姿态控制的思路(串级、滤波、实际坑);
- 最后是一份从需求到上线的流程清单。
案例一:直流电机的速度与位置控制
用到:第2章 直流电机模型、第3章 一阶系统、第6章 PI、第8章 采样周期、第9章 抗饱和与串级。
1. 需求与模型
一台小型有刷直流电机,用 PWM 驱动(等效电压 ±12 V),编码器测转角和转速。要求:速度阶跃响应快且无明显超调,加负载时转速很快恢复;位置控制不能有超调。
| 参数 | 值 |
|---|---|
| 电枢电阻 \(R\)、电感 \(L\) | 2 Ω、1 mH |
| 转矩系数 \(K_t\)、反电动势系数 \(K_e\) | 0.05 N·m/A、0.05 V·s/rad |
| 转动惯量 \(J\)、粘滞摩擦 \(b\) | 2×10⁻⁵ kg·m²、1×10⁻⁵ N·m·s/rad |
按第2章的公式:\(K=\dfrac{K_t}{Rb+K_tK_e}=19.84\) (rad/s)/V,\(\tau=\dfrac{RJ}{Rb+K_tK_e}=15.9\) ms。电气时间常数 \(L/R=0.5\) ms,比 \(\tau\) 小 30 多倍,设计速度环时可以忽略。12 V 时空载转速约 238 rad/s(约 2270 rpm)。
2. 速度环:PI,零极点对消
取 PI 的积分时间等于对象时间常数(\(T_i=\tau\)),PI 的零点正好抵消对象的极点,开环变成 \(\dfrac{K_pK}{\tau s}\),闭环是一阶的:
希望闭环时间常数 \(\tau_{cl}=5\) ms(比开环快约 3 倍):\(K_p=\dfrac{\tau}{K\tau_{cl}}=0.16\) V/(rad/s),\(K_i=K_p/T_i=10.08\) V/rad。忽略延迟时相位裕度为 90°。
采样频率:截止频率约 \(1/\tau_{cl}=200\) rad/s。取 1 kHz,零阶保持损失 \(\omega_cT_s/2\approx5.7^\circ\),再加上计算延迟和被忽略的电气时间常数,相位裕度仍有 70° 以上(第8章 8.6 节)。
3. 仿真:电压限幅和抗饱和
速度设定从 0 阶跃到 100 rad/s。开头误差很大,\(K_p\times100=16\) V 超过了 12 V,电压饱和约 3 ms:
| 做法 | 超调 | 调节时间(2%) |
|---|---|---|
| 不做抗饱和 | 1.2% | 13 ms |
| 条件积分(饱和时停止积分) | 0 | 37 ms |
| 反算法抗饱和,\(T_t=T_i\) | 0 | 16 ms |
| 设定值 12 ms 斜坡 + 条件积分 | 0 | 23 ms(含斜坡时间),最大电压 9.6 V,不饱和 |
这个例子说明:饱和时间很短时,条件积分反而让收尾变慢(积分器在饱和期间完全停住,之后要慢慢补回来)。反算法或设定值斜坡效果更好。如果是长时间饱和(大幅加速、温度控制),条件积分就很合适。
负载扰动:转速稳定在 100 rad/s 时,突然加 0.01 N·m 的负载转矩,转速最低掉到 97.8 rad/s,然后靠积分作用恢复到 99.95 rad/s。如果只用 P 控制,会留下稳态误差(第3章 扰动误差)。
4. 位置环:串级
外环用 P 控制,输出作为速度环的设定值:\(\omega_{ref}=K_{pos}(\theta_{ref}-\theta)\),并把 \(\omega_{ref}\) 限制在 ±200 rad/s。内环(速度环)近似为 \(\frac{1}{\tau_{cl}s+1}\),外环对象就是 \(\dfrac{1}{s(\tau_{cl}s+1)}\)。
按「外环比内环慢 3~10 倍」,取 \(K_{pos}=40\) (rad/s)/rad,外环截止频率约 40 rad/s,是内环的 1/5。闭环特征方程 \(\tau_{cl}s^2+s+K_{pos}=0\),阻尼比 \(\xi=\dfrac{1}{2\sqrt{K_{pos}\tau_{cl}}}\approx1.1\),过阻尼,不会超调,正好满足定位要求。
- 转 0.5 rad:无超调,调节时间 79 ms;
- 转一整圈(2π rad):速度指令达到 200 rad/s 的限幅,以最高速度走大部分行程,无超调,调节时间 95 ms。
import numpy as np
R, Lm, Kt, Ke, J, b = 2.0, 1e-3, 0.05, 0.05, 2e-5, 1e-5
K = Kt / (R * b + Kt * Ke) # 19.84 (rad/s)/V
tau = R * J / (R * b + Kt * Ke) # 15.9 ms
Kp = tau / (K * 0.005); Ki = Kp / tau # 速度环 PI,闭环时间常数 5 ms
class PI:
def __init__(s, kp, ki, ts, umin, umax):
s.kp, s.ki, s.ts, s.umin, s.umax, s.i = kp, ki, ts, umin, umax, 0.0
def update(s, r, y):
e = r - y
u_raw = s.kp * e + s.ki * s.i
u = min(max(u_raw, s.umin), s.umax)
if u == u_raw or (u_raw > s.umax and e < 0) or (u_raw < s.umin and e > 0):
s.i += e * s.ts # 条件积分抗饱和
return u
def run(T, theta_ref, Kpos=40.0, wmax=200.0):
dt, Ts = 1e-5, 1e-3 # 电机模型 10 µs 积分,控制器 1 kHz
i = w = th = u = 0.0
speed_pi = PI(Kp, Ki, Ts, -12, 12)
log = []
for k in range(int(T / dt)):
t = k * dt
if k % int(Ts / dt) == 0: # 每 1 ms:位置环 → 速度环 → 电压
w_ref = float(np.clip(Kpos * (theta_ref(t) - th), -wmax, wmax))
u = speed_pi.update(w_ref, w)
log.append((t, th, w, u))
di = (u - R * i - Ke * w) / Lm # 电路
dw = (Kt * i - b * w) / J # 机械
i += dt * di; w += dt * dw; th += dt * w
return np.array(log)
log = run(0.4, lambda t: 2 * np.pi if t >= 0.01 else 0.0)
print(log[:, 1].max()) # 6.283:没有超调
5. 实现要点
- 测速:编码器计数直接差分得到的速度在低速时很「毛糙」(量化,第12章)。低速用测周法(测两个脉冲之间的时间),或者用观测器(第11章)估计速度;
- PWM 频率取 20 kHz 以上可以避开人耳听到的啸叫;驱动芯片一般自带电流限制,相当于一个很快的内环;
- 先确认方向:正电压是不是让编码器计数增加?方向反了就是正反馈,一上电就失控;
- 上电先用很小的增益、限制最大电压,确认方向和单位都对了再逐步加大;
- 有减速箱时注意间隙(第12章),位置环的增益不宜太大。
案例二:加热器温度控制
用到:第10章 阶跃测试与两点法、第9章 SIMC 整定与抗饱和、第8章 采样周期、第12章 开关输出与回差。适用于 3D 打印机喷头和热床、小烤箱、恒温箱、热水器等。
1. 阶跃测试
在室温(25 °C)下把加热功率从 0 一下子调到 20%,记录温度直到稳定。假设得到:温度最终升高 60 °C,升高 17 °C(28.3%)用了 28 s,升高 37.9 °C(63.2%)用了 68 s。按两点法:
2. 整定
SIMC(\(\tau_c=L=8\) s):
采样周期 1 s 足够:截止频率约 \(1/(\tau_c+L)\approx0.06\) rad/s,\(\omega_cT_s\approx0.06\)。温度对象一般不需要 D。
3. 只能加热:抗饱和必不可少
加热器只能输出 0~100%,不能制冷。从 25 °C 升到 200 °C 时,开头误差 175 °C,\(K_p\times175\approx219\%\),远超 100%,功率会饱和很长时间:
- 有抗积分饱和:最高 200.0 °C,没有超调,约 3 分钟稳定;
- 没有抗积分饱和:冲到 234.6 °C,约 3.7 分钟才稳定。对喷头、塑料件、化学品来说,这 35 °C 可能就是事故。
在下面的 里对比(温度用相对室温的增量表示,设定值 175 对应 200 °C;扰动按钮模拟风扇打开、带走 10% 的加热功率):
4. 输出与安全
- 输出方式:MOSFET 驱动的加热棒可以直接 PWM;固态继电器用时间比例输出(比如 2 s 一个周期,按占空比开关,第12章);机械继电器寿命有限,周期要更长,或者干脆用开关控制加回差;
- 热失控保护:加热功率 100% 持续一段时间温度却不上升,说明传感器脱落或损坏,必须立即关断加热(3D 打印机固件都有这个保护);
- 传感器断线、短路检测:热敏电阻断线时读数会变成极低或极高的值,要识别出来并关断;
- 最高温度硬限制:软件之外,最好还有一个独立的温度保险;
- 自整定:也可以用继电反馈法自动测出 \(K_u\)、\(T_u\)(第9章),很多固件的 PID 自整定命令就是这样做的。
案例三:小车倒立摆(平衡小车)
用到:第2章 线性化、第11章 状态空间与 LQR、第8章 采样、第11章 互补滤波。两轮平衡车、平衡机器人的控制思路相同,只是模型里的参数和输入(车轮转矩)不同。
1. 模型
小车质量 \(M=0.5\) kg,摆杆末端质量 \(m=0.2\) kg,摆长 \(l=0.3\) m,水平推力 \(F\) 为输入。\(\theta\) 从竖直向上量起,向 \(+x\) 方向倾斜为正。由拉格朗日方程得到非线性模型,在 \(\theta=0\)(竖直向上)附近线性化(第2章):
取状态 \(z=[x,\ \dot x,\ \theta,\ \dot\theta]^T\):
开环极点 0、0、±6.77:有一个正实极点,不加控制一定会倒。能控性矩阵满秩,所以可以用状态反馈镇定。
2. LQR 设计
最在乎角度(权重 100),其次是位置(10),速度和角速度各 1,控制力权重 1:
import numpy as np
import control as ct
M, m, l, g = 0.5, 0.2, 0.3, 9.81
A = np.array([[0, 1, 0, 0],
[0, 0, -m * g / M, 0],
[0, 0, 0, 1],
[0, 0, (M + m) * g / (M * l), 0]])
B = np.array([[0], [1 / M], [0], [-1 / (M * l)]])
print(np.linalg.eigvals(A)) # 0, 0, ±6.766:不稳定
print(np.linalg.matrix_rank(ct.ctrb(A, B))) # 4:能控
Q = np.diag([10.0, 1.0, 100.0, 1.0]) # 位置、速度、角度、角速度的权重
R = np.array([[1.0]]) # 控制力的权重
K, P, E = ct.lqr(A, B, Q, R)
print(K) # [[ -3.162 -3.726 -26.848 -4.094]]
print(E) # 闭环极点 -8.70±2.49j, -1.22±1.02j
控制律 \(F=-Kz=3.16x+3.73\dot x+26.8\theta+4.09\dot\theta\)。符号的含义:摆向 \(+x\) 倾斜(\(\theta>0\))时,小车向 \(+x\) 推,「追到摆的下面」把它扶正,这和人用手托住竖直木棍的动作一样。
3. 在非线性模型上验证
线性化模型只在小角度附近准确,所以要在完整的非线性模型上仿真:
from scipy.integrate import solve_ivp
def pendulum(t, z, Fmax):
x, v, th, w = z
F = float(np.clip(-K[0] @ z, -Fmax, Fmax)) # 状态反馈 + 推力限幅
s, c = np.sin(th), np.cos(th)
acc = (F + m * s * (l * w**2 - g * c)) / (M + m * s**2)
return [v, acc, w, (g * s - acc * c) / l]
sol = solve_ivp(pendulum, [0, 6], [0, 0, 0.3, 0], args=(np.inf,), max_step=0.002)
print(sol.y[2, -1]) # 6 s 后角度 ≈ 0:稳住了
| 初始角度 | 结果 | 最大推力 | 小车最远移动 |
|---|---|---|---|
| 0.1 rad(5.7°) | 稳住 | 2.7 N | 0.09 m |
| 0.3 rad(17°) | 稳住 | 8.1 N | 0.29 m |
| 0.6 rad(34°) | 稳住 | 16.1 N | 0.68 m |
执行器限幅:推力不够大时,能救回来的初始角度会变小。仿真结果:推力限 ±2 N 时约 0.26 rad(15°),±5 N 时约 0.58 rad(33°),±10 N 时约 0.88 rad(50°);不限推力时约 1.14 rad(66°),再大就超出了线性化模型的适用范围,线性控制器也救不回来。设计时要按「可能遇到的最大扰动」检查推力够不够。
采样频率:把连续设计的 \(K\) 直接用在零阶保持的数字控制器上,采样周期 100 ms 时闭环仍然稳定,120 ms 时就不稳定了。但这还没算计算延迟、传感器滤波和电机的响应时间。按第8章的经验 \(\omega_cT_s\approx0.1\sim0.3\)(最快的闭环极点约 9 rad/s),采样周期至少要 10~30 ms;实际的平衡车通常用 200 Hz~1 kHz,给延迟和噪声留足余量。
4. 实现要点
- 测角度:用 IMU,陀螺仪和加速度计做互补滤波或卡尔曼滤波(第11章 11.8 节)。角度估计的延迟和噪声直接决定能不能立住;
- 测位置和速度:电机编码器;
- 执行器:电机通常用电流(转矩)控制作为内环,比直接控制电压更接近模型里的「力」输入;
- 先在仿真里把 Q、R 调好,确认推力、速度都在硬件能力范围内,再上实物;实物上先用较小的增益,扶着试;
- 平衡车:车轮代替小车,输入是车轮转矩,状态取车身倾角、角速度、轮子转角、轮速,同样用 LQR(或者串级 PD + PI 的经验结构)。
案例四:多旋翼姿态控制的思路
用到:第9章 串级与微分滤波、第8章 采样频率、第11章 传感器融合。
- 结构:外环角度控制(通常只用 P)→ 内环角速度控制(PID)→ 混控器把三个轴的力矩指令和总油门分配到各个电机。角速度环是最关键的回路;
- 频率:角速度环常见 1~8 kHz,角度环几百 Hz(第8章:内环更快);
- D 项:作用在陀螺仪测量的角速度上(不是误差上,第9章),并且要低通滤波;电机和螺旋桨的振动会在陀螺仪信号里形成尖峰,常用陷波滤波器去掉;
- 积分:停在地面上时积分会慢慢累积(地面阻挡了姿态变化),起飞瞬间就会翻;所以起飞前要清零或冻结积分,快速打杆时也要限制积分;
- 前馈:把摇杆的变化率直接加到输出上(第9章 前馈),可以让响应更跟手;
- 整定顺序:先角速度环后角度环;先 P 后 D 再 I;用日志看陀螺仪噪声和电机输出,而不是只凭手感。
从需求到上线:流程清单
- 明确需求:被控量、精度、响应速度、允许的超调、扰动类型、执行器能力、安全要求;
- 建模:能写物理模型就写(第2章);写不出就做实验辨识(第10章);至少要知道增益、主要时间常数、延迟;
- 选结构:PI 还是 PID;要不要串级、前馈;多变量、不稳定对象考虑状态反馈(第9章、第11章);
- 设计参数:SIMC、零极点对消、频域校正、极点配置、LQR;检查相位裕度 45°~60°、幅值裕度 6 dB 以上(第5章);
- 考虑数字实现:采样频率、计算延迟、抗混叠、量化(第8章);
- 仿真:加上限幅、噪声、延迟、扰动、参数误差(±20%)再跑一遍(第7章);
- 实现:按第9章的清单写代码;加日志和保护;
- 上机调试:先开环确认方向和单位;小增益起步;逐步加大;记录曲线;
- 验收与维护:在不同工况下测试;记录最终参数和测试曲线;设备磨损或环境变化后定期检查。
下一步学什么
学完这十三章,已经能独立完成大多数单回路、串级、简单状态反馈的控制任务。继续深入可以看这些方向(以后也会补充到这份笔记里):
- 模型预测控制(MPC):每一步在线求解带约束的优化问题,能直接处理执行器限幅和状态约束,在自动驾驶、过程工业里应用很多;
- 鲁棒控制(H∞、μ 分析):在模型不确定时保证性能;
- 自适应控制:参数变化时在线调整控制器;
- 多变量解耦控制:多个回路互相影响时;
- 非线性控制(反馈线性化、滑模控制)、最优估计(扩展卡尔曼滤波 EKF、UKF);
- 强化学习与数据驱动控制:和本笔记的模型方法互补。