第9章
PID 工程实践
学完这一章你应该能
- 写出一个能在真实项目里用的 PID:限幅、抗积分饱和、微分滤波、设定值权重、无扰切换;
- 用前馈和串级结构改善控制效果;
- 按模型整定参数(SIMC),或者用继电反馈法自动测出临界参数;
- 根据现象(振荡、抖动、稳态误差)判断原因并调整;
- 按清单检查一个控制回路能否上线。
前置知识:第6章 PID 原理、第8章 数字 PID 和采样周期。
教科书上的 PID 公式只有一行,可直接把它写进程序,接到真实设备上,往往会遇到这些问题:一上电输出就顶到满、温度冲过头好几十度、电机嗡嗡响、改一下设定值执行器猛地一抖……这一章逐个解决它们。本章所有数字都可以在页面里的 PID 实验台上复现。
9.1 一个能用的 PID 长什么样
和课本公式相比,多了这几样东西:
| 改动 | 解决什么问题 | 详见 |
|---|---|---|
| 输出限幅 + 抗积分饱和 | 执行器到头后积分继续累积,造成大超调 | 9.2 |
| 微分作用于测量值、加低通滤波 | 设定值突变时的微分冲击;噪声被放大 | 9.3 |
| 设定值权重 \(b\)(或设定值斜坡) | 设定值阶跃时超调大 | 9.4 |
| 前馈 \(u_{ff}\) | 反馈只能「事后补救」,已知的变化应该提前补偿 | 9.5 |
| 无扰切换 | 手动切到自动、改参数时输出跳变 | 9.9 |
9.2 积分饱和与抗饱和
现象:设定值大幅改变时,误差很大,控制量很快顶到上限,执行器已经尽全力了,可积分项还在继续累积。等输出终于接近设定值、误差开始反号时,积分已经攒了一大堆,要很久才能「退」回来,于是输出冲过头很多,还可能来回振荡。这叫积分饱和(windup)。
真实后果:第13章的温度控制例子,目标 200 °C,没有抗饱和时会冲到 234.6 °C。对 3D 打印机喷头、锅炉、化学反应釜,这样的超调可能直接损坏设备。
常用的抗饱和方法:
- 条件积分(clamping):输出饱和时停止积分,但如果误差的方向会让积分往回退,就继续积分。简单可靠,本笔记的代码都用这种。
- 反算法(back-calculation):把「限幅前后的差」反馈给积分器,饱和时积分自动往回拉:
$$\frac{dI}{dt}=K_ie+\frac{1}{T_t}(u-u_{\text{raw}})$$\(T_t\) 是跟踪时间常数,常取 \(\sqrt{T_iT_d}\) 或 \(T_i/2\) 左右。
- 积分限幅:直接限制积分项的大小。最粗糙,限值不好定。
- 用增量式 PID(第8章):对输出限幅就自然不会饱和。
条件积分不是在所有场合都最好:如果饱和只持续很短的时间,饱和期间完全停止积分,反而会让后面收尾变慢。第13章电机速度环的例子里,饱和只有 3 ms,条件积分的调节时间是 37 ms,反算法(\(T_t\approx T_i\))只要 16 ms。饱和时间长(升温、大行程定位)时,条件积分的效果很好。
在实验台上试:执行器限幅只有 ±1.5,PID 参数较大。先不勾「抗积分饱和」(超调约 44%),再勾上(超调约 2%)。
9.3 微分项的处理
- 微分作用于测量值:把 \(\dfrac{de}{dt}\) 换成 \(-\dfrac{dy}{dt}\)。设定值阶跃时 \(e\) 会突变,导数理论上是无穷大,控制量会猛地一抖(微分冲击);\(y\) 是连续变化的,不会这样。对扰动的响应两者完全一样。
- 微分加低通滤波:理想微分在高频的增益无穷大,测量噪声会被放大成很大的控制量抖动,让执行器发热、磨损、发出噪音。实际用 \(\dfrac{K_ds}{T_fs+1}\),\(T_f=T_d/N\),\(N\) 一般取 5~20。离散实现就是对差分结果再做一次指数滑动平均(第8章 8.4 节)。
- 能不用 D 就不用:大多数过程控制回路(温度、液位、流量)用 PI 就够了。D 主要用在需要增加阻尼的机械系统(电机位置、飞行器姿态)。
在实验台上试:① 取消「微分作用于测量值」,看右图设定值阶跃时 u 的尖峰(可达 200 以上,被限幅截断);② 恢复后,把测量噪声加到 0.01,看控制量抖动;③ 勾上「微分低通滤波」,抖动明显减小。
9.4 设定值处理
设定值阶跃时超调大,但又不想降低增益(降低增益会让抗扰动变差),可以只改变「设定值进入控制器的方式」:
- 设定值权重:比例项用 \(b\,r-y\)(\(0\le b\le1\))代替 \(r-y\),积分项仍然用 \(r-y\)(保证无静差)。\(b\) 越小,设定值变化时比例项「推」得越轻,超调越小,但响应也越慢;对扰动的响应完全不变,因为扰动不经过 \(r\)。这种结构叫二自由度 PID:抗扰动性能和跟踪性能可以分开调。
- 设定值斜坡(或滤波):不让设定值一下跳过去,而是按一定速率变化,避免执行器饱和,也更平稳。
三阶对象、Z–N 参数下:\(b=1\) 时超调 52.7%,\(b=0.5\) 时 17.0%,\(b=0\) 时 2.7%(上升时间从 1.5 s 变长到 3.1 s)。在实验台上拖动「设定值权重 b」验证,并勾上「中途加负载扰动」,看扰动响应不随 \(b\) 变化。
9.5 前馈
反馈控制要等误差出现了才动作,永远是「事后补救」。如果某个变化是事先知道或者可以测量的,就应该直接补偿它,这就是前馈。前馈不在回路里,不影响稳定性,剩下的误差再由反馈收拾。
- 设定值前馈:已知要达到设定值 \(r\) 大概需要多大的控制量,就直接加上。最简单的是静态前馈 \(u_{ff}=r/K\)(\(K\) 是对象的稳态增益),反馈只需要补偿模型误差。运动控制里常用速度、加速度前馈:\(u_{ff}=K_v\dot r+K_a\ddot r+\)摩擦补偿,跟踪轨迹时误差能小一个数量级。
- 扰动前馈:扰动 \(d\) 能测到(比如室外温度、负载电流、进料流量),它经过 \(G_d(s)\) 影响输出,控制量经过 \(G_p(s)\) 影响输出。理想的前馈 \(G_{ff}(s)=-\dfrac{G_d(s)}{G_p(s)}\) 可以完全抵消扰动;实际常用它的静态部分 \(-\dfrac{G_d(0)}{G_p(0)}\),或者加一个超前/滞后环节近似动态。
判断要不要加前馈:扰动大、能测量、反馈回路又慢的时候,前馈最有用。前馈只能补偿你知道的东西,模型不准时补偿不完全,所以总是和反馈一起用。
9.6 串级控制
两个(或多个)回路套起来:外环的输出作为内环的设定值。
- 电机:位置环(外)→ 速度环 → 电流环(内)。内环先把电流、速度管好,外环只需要管位置;
- 温度:温度环(外)→ 加热功率或流量环(内)。电网电压波动这类扰动在内环就被消掉了,温度环感觉不到;
- 多旋翼:角度环(外)→ 角速度环(内)。
串级的好处:内环把它包住的那部分扰动和非线性就地消掉(比如电机的反电动势、执行器的非线性),外环面对的是一个更简单、更「听话」的对象。
设计规则:
- 内环要比外环快得多,带宽一般差 3~10 倍;
- 先整定内环,再整定外环;整定外环时把内环近似看成一个增益为 1 的一阶惯性环节;
- 内环的采样频率也要更高;
- 外环的输出(内环的设定值)要限幅,比如限制最大速度、最大电流。
第13章的电机案例给出一个具体的串级设计。
9.7 参数整定方法
方法一:先测模型,再用公式(推荐)
大多数过程对象(温度、液位、压力、电机速度)的阶跃响应都是「先延迟一会儿,再像一阶惯性环节那样上升」,可以近似成一阶加纯延迟(FOPDT)模型:
做一次阶跃测试就能得到 \(K\)、\(T\)、\(L\),方法见第10章。然后用 SIMC 整定规则(Skogestad):
\(\tau_c\) 是你希望的闭环时间常数,默认取 \(\tau_c=L\):响应较快,同时有足够的鲁棒性。想更稳就取大一些。对积分型对象 \(\dfrac{K'}{s}e^{-Ls}\)(比如液位、电机转角):\(K_p=\dfrac{1}{K'(\tau_c+L)}\),\(T_i=4(\tau_c+L)\)。
例 对象 \(\dfrac{e^{-s}}{5s+1}\)(\(K=1\),\(T=5\),\(L=1\)),取 \(\tau_c=1\):\(K_p=\dfrac{5}{1\times2}=2.5\),\(T_i=\min(5,8)=5\),\(K_i=0.5\)。闭环超调 4.2%,调节时间约 6 s。作为对比,Z–N 阶跃响应法(\(K_p=0.9T/(KL)\),\(T_i=3.33L\))给出的 PI 参数超调 55%。
方法二:Z–N 法(了解即可)
Z–N 法有两种:第6章讲的临界比例度法,和这里的阶跃响应法(\(K_p=1.2T/(KL)\)、\(T_i=2L\)、\(T_d=0.5L\) 给 PID)。它们的目标是「衰减比 4:1」,参数很激进,超调常在 50% 以上,现在一般只当作起点。
方法三:继电反馈自整定
想测临界增益,又不想冒险把系统调到等幅振荡?用继电反馈(Åström–Hägglund):把控制器临时换成一个开关,误差为正时输出 \(+d\),为负时输出 \(-d\)。大多数对象会自动进入一个稳定的小幅振荡:
\(a\) 是输出振荡的幅值。原理是第12章的描述函数法。例:三阶对象 \(\frac{1}{(s+1)^3}\)、\(d=1\),仿真得到 \(a=0.163\)、\(T_u=3.68\) s,估计 \(K_u=7.80\)(真值 8,\(T_u\) 真值 3.63 s),误差约 2%。得到 \(K_u\)、\(T_u\) 后再用 Z–N 表或更保守的规则(比如 Tyreus–Luyben:PI 取 \(K_p=K_u/2.2\),\(T_i=2.2T_u\))。
很多工业温控器的「自整定」按钮,以及 3D 打印机固件的 PID 自整定(比如 Marlin 的 M303 命令),都是这个原理:先让加热器在目标温度附近开关几个周期,测出振荡幅度和周期,再算 PID 参数。
方法四:现场手动调
没有模型、也不能做实验时:先 P 后 I 再 D(第6章)。调的时候每次只改一个参数,改动幅度按倍数来(比如 ×2 或 ×0.5),记录每次的响应。
9.8 看现象找原因
| 现象 | 可能的原因 | 怎么办 |
|---|---|---|
| 振荡越来越大 | 增益太高;回路里延迟太大;采样太慢 | 减小 \(K_p\)、\(K_i\);缩短采样和计算延迟 |
| 周期较长的大幅振荡,衰减很慢 | 积分太强 | 减小 \(K_i\)(加大 \(T_i\)) |
| 周期较短的振荡,频率接近某个机械共振 | 比例或微分太强;机械共振 | 减小 \(K_p\)、\(K_d\);加陷波滤波器 |
| 控制量高频抖动、执行器噪音大 | 微分放大了噪声;测量噪声大 | 微分滤波;减小 \(K_d\);检查传感器和接地 |
| 设定值一改,执行器猛地一抖 | 微分冲击;比例项太强 | 微分作用于测量值;设定值权重或斜坡 |
| 大幅改变设定值时冲过头很多 | 积分饱和 | 抗积分饱和;设定值斜坡 |
| 一直有稳态误差 | 没有积分;积分被限幅卡住;执行器饱和;死区 | 加积分;检查限幅;执行器是否够用 |
| 在设定值附近小幅来回振荡(极限环) | 死区、间隙、静摩擦、量化 | 见第12章:减小积分、加死区补偿、提高分辨率 |
| 响应太慢 | 增益太低;执行器限幅太小;设定值斜坡太缓 | 逐步加大 \(K_p\);检查执行器能力 |
9.9 完整实现
下面的 C 代码实现了本章的所有要点:并联形式、设定值权重、微分作用于测量值并滤波、输出限幅、条件积分抗饱和、无扰切换。Python 版本的逻辑完全一样,已在仿真中验证(本页的实验台用的也是同样的算法)。
typedef struct {
float kp, ki, kd; /* 并联形式的增益 */
float b; /* 设定值权重:比例项用 b*r - y,取 0~1 */
float tf; /* 微分滤波时间常数 [s],0 表示不滤波 */
float umin, umax; /* 输出限幅 */
float ts; /* 采样周期 [s] */
float integral; /* 以下是内部状态 */
float d_filt;
float prev_y;
int first;
} PidCtrl;
void pid_init(PidCtrl *p, float kp, float ki, float kd, float ts, float umin, float umax)
{
p->kp = kp; p->ki = ki; p->kd = kd; p->ts = ts;
p->umin = umin; p->umax = umax;
p->b = 1.0f; p->tf = 0.0f;
p->integral = 0.0f; p->d_filt = 0.0f; p->prev_y = 0.0f; p->first = 1;
}
/* 每个采样周期调用一次:r 为设定值,y 为测量值,返回控制量 */
float pid_update(PidCtrl *p, float r, float y)
{
float e = r - y;
float d_raw, u_raw, u;
/* 微分作用于测量值(设定值突变时不产生尖峰),并做一阶低通滤波 */
d_raw = p->first ? 0.0f : -(y - p->prev_y) / p->ts;
p->prev_y = y;
p->first = 0;
if (p->tf > 0.0f)
p->d_filt += p->ts / (p->tf + p->ts) * (d_raw - p->d_filt);
else
p->d_filt = d_raw;
u_raw = p->kp * (p->b * r - y) + p->ki * p->integral + p->kd * p->d_filt;
/* 输出限幅 */
u = u_raw;
if (u > p->umax) u = p->umax;
if (u < p->umin) u = p->umin;
/* 抗积分饱和(条件积分):没有饱和,或者误差会让积分往回退时,才继续积分 */
if (u == u_raw || (u_raw > p->umax && e < 0.0f) || (u_raw < p->umin && e > 0.0f))
p->integral += e * p->ts;
return u;
}
/* 手动切到自动时调用,让自动模式的第一个输出等于当前手动输出(无扰切换) */
void pid_bumpless(PidCtrl *p, float r, float y, float u_manual)
{
p->prev_y = y;
p->first = 0;
p->d_filt = 0.0f;
if (p->ki != 0.0f)
p->integral = (u_manual - p->kp * (p->b * r - y)) / p->ki;
}
在单片机里的用法(read_speed、set_motor_voltage 换成你自己的硬件函数):
static PidCtrl speed_pid;
static volatile float speed_ref = 0.0f; /* 设定值,可以由其他任务修改 */
void control_init(void)
{
/* 1 kHz 采样,输出 ±12 V */
pid_init(&speed_pid, 0.05f, 2.0f, 0.0f, 0.001f, -12.0f, 12.0f);
speed_pid.tf = 0.002f;
}
/* 定时器中断,每 1 ms 进来一次:读测量 → 算控制量 → 输出,顺序和周期都要固定 */
void timer_1ms_isr(void)
{
float w = read_speed();
float u = pid_update(&speed_pid, speed_ref, w);
set_motor_voltage(u);
}
上面的增益只是示意,实际要按你的电机整定(第13章)。改参数时最好先调用 pid_bumpless,避免输出跳变。
9.10 上线前检查清单
- ☐ 采样周期固定,实际测过;读测量、计算、输出的延迟尽量小;
- ☐ 传感器方向、单位、零点正确;控制量的符号正确(正的控制量让输出往正方向变);
- ☐ 输出限幅符合执行器能力和安全要求;有抗积分饱和;
- ☐ 微分作用于测量值并滤波(或者干脆不用 D);
- ☐ 设定值有限幅,大幅变化时有斜坡;
- ☐ 手动/自动切换、参数修改时无扰;
- ☐ 有安全保护:传感器断线、超温、超速时关断输出(看门狗);
- ☐ 能记录设定值、测量值、控制量的曲线,出问题时能回放分析;
- ☐ 先在仿真里验证过(第7章),上机先用小增益,逐步加大。
本章小结
- 能用的 PID = 课本公式 + 限幅与抗饱和 + 微分作用于测量值并滤波 + 设定值处理 + 无扰切换。
- 前馈补偿已知或可测的变化,不影响稳定性;串级用快的内环消掉局部扰动,内环带宽是外环的 3~10 倍。
- 整定优先「测 FOPDT 模型 + SIMC」(\(\tau_c=L\));继电反馈法可以安全地测出 \(K_u\)、\(T_u\);Z–N 参数偏激进。
- 按现象找原因:快振荡看增益和延迟,慢振荡看积分,抖动看微分和噪声,小幅极限环看死区和摩擦。
练习
1. 加热炉从室温升到 800 °C,PI 控制,每次升温都冲过头 50 °C,到了稳态却很稳。最可能的原因是什么?
积分饱和:升温初期误差很大,加热功率一直是 100%,积分在这段时间里不停累积。稳态时没有饱和,所以表现正常。解决:加抗积分饱和,或者设定值按斜坡上升。
2. 阶跃测试得到 \(K=2\)(°C/%),\(T=120\) s,\(L=15\) s。用 SIMC(\(\tau_c=L\))给出 PI 参数。
\(K_p=\dfrac{120}{2\times30}=2\) %/°C;\(T_i=\min(120,\ 4\times30)=120\) s;\(K_i=K_p/T_i\approx0.0167\) %/(°C·s)。
3. 设定值权重 \(b\) 从 1 改成 0.5,对负载扰动的响应会变吗?为什么?
不变。\(b\) 只出现在 \(b\,r\) 这一项里,扰动不经过 \(r\),扰动引起的误差响应只由 \(K_p\)、\(K_i\)、\(K_d\) 决定。这正是二自由度结构的好处。
4. 继电反馈测试:继电器幅值 \(d=20\%\),温度振荡幅值 \(a=2\) °C,周期 \(T_u=90\) s。估算 \(K_u\),并用 Z–N 表给出 PID 参数。
\(K_u=\dfrac{4\times20}{\pi\times2}\approx12.7\) %/°C。Z–N PID:\(K_p=0.6K_u\approx7.6\),\(T_i=0.5T_u=45\) s,\(T_d=0.125T_u\approx11\) s。温度对象通常建议更保守一些,比如把 \(K_p\) 再减半。
5. 电机位置控制用串级结构,速度环带宽 50 Hz,位置环带宽大约选多少合适?
位置环比速度环慢 3~10 倍,取 5~15 Hz 左右。位置环太快,内环来不及跟上,等效对象就不再是简单的一阶环节,容易振荡。