3. 带纯滞后环节的PID整定与稳定性边界测试
对象模型与稳定边界的关系
本节针对的一阶纯滞后对象传递函数为 G(s)=K·e−τs/(Ts+1)。取教学示例参数:K=1.8、T=4.0 s、τ=1.2 s。纯滞后 e−τs 在频域上只改变相位、不改变幅值,因此它不消耗幅值裕度,却持续“吃掉”相位裕度——这正是滞后系统比同参数一阶惯性系统更容易振荡的根本原因。临界比例度法正是利用这一特性:把积分、微分置零,只增大比例增益,直到闭环输出出现等幅振荡,此时的增益即临界增益 Ku,振荡周期即临界周期 Tu。
上图把“结构”和“判读”放在同一张图里:左侧闭环链路说明纯滞后串在对象之后、只贡献相位;下方三条包络曲线对应 Ku 扫描的三种结果。工程上判断临界点的依据是输出包络既不收敛也不发散,而不是看某一次峰值大小。对上述示例参数,用频率法可估算相位穿越条件:对象相位为 −arctan(ωT) − ωτ,令其等于 −180° 时解得 ωu≈1.85 rad/s,对应 Tu=2π/ωu≈3.4 s;再由幅值条件 K·Ku/√(1+(ωuT)²)=1 得 Ku≈3.6。这两个值就是后续整定的基准。
临界点测试的实操要点
临界比例度法在仿真或实机上都能做,但必须控制好“逼近”的方式,否则容易把系统推到不安全的大幅振荡。建议按下面的逻辑执行:
- 先断开 I、D:置 Ti=∞(积分增益为 0)、Td=0,只保留比例通道,避免积分在临界点附近累积造成持续偏置。
- 用阶跃而非脉冲激励:给一个幅值较小的设定值阶跃(例如 0.1 单位),观察输出包络。阶跃能同时暴露超调和振荡衰减情况。
- 按比例递增扫描:从明显稳定的 Kp 开始,每次乘以约 1.3~1.5 倍,直到包络由衰减转为等幅。记录此时的 Kp 与相邻两个峰之间的时间间隔作为 Tu。
- 等幅判定要观察 3~4 个周期:单周期内峰值相近不足以判定临界,需确认后续周期峰谷不再明显增大或减小。
- 留安全余量:实机上不要长时间停留在 Ku 附近,判定后立即回退到 0.6~0.7 倍 Ku 再进入正式整定。
由 Ku、Tu 计算 PID 参数
临界比例度法(Ziegler-Nichols 临界振荡法)给出的经验整定公式如下表。表中 Kp 为比例增益,Ti 为积分时间,Td 为微分时间。
| 控制器类型 | Kp | Ti | Td |
|---|---|---|---|
| P | 0.5·Ku | — | — |
| PI | 0.45·Ku | Tu/1.2 | — |
| PID | 0.6·Ku | Tu/2 | Tu/8 |
代入示例值 Ku=3.6、Tu=3.4 s,得到 PID 初值:Kp=2.16、Ti=1.7 s、Td=0.425 s。这组参数通常偏激进,超调会比较大,因此实际投运时建议先把 Kp 降到 1.6~1.8 附近,再根据阶跃响应微调。
可运行的整定与验证脚本
下面用 Python 对上述对象做离散仿真:先扫描 Kp 找临界点,再按公式计算 PID 参数,最后用设定值阶跃验证稳定性,并叠加对象参数摄动(K、T、τ 各变化 ±20%)检验鲁棒性。代码自洽,可直接运行。
import numpy as np
# 对象参数(教学示例)
K, T, tau = 1.8, 4.0, 1.2
dt = 0.01
t_end = 60.0
t = np.arange(0, t_end, dt)
N = len(t)
delay_steps = int(round(tau / dt))
def simulate(kp, ti, td, k=K, tt=T, ta=tau, sp=1.0):
"""离散 PID + 一阶对象 + 纯滞后,返回输出序列"""
y = np.zeros(N)
u = np.zeros(N)
y_hist = np.zeros(N) # 对象输出历史(用于滞后)
integ = 0.0
prev_e = 0.0
d_steps = int(round(ta / dt))
for i in range(1, N):
# 滞后后的测量值
y_meas = y_hist[i - d_steps] if i - d_steps >= 0 else 0.0
e = sp - y_meas
# 积分(ti 为无穷大时视为纯 P)
if ti is not None and ti > 0:
integ += e * dt
i_term = integ / ti
else:
i_term = 0.0
# 微分(对误差差分,简化实现)
d_term = td * (e - prev_e) / dt if td and td > 0 else 0.0
prev_e = e
u[i] = kp * (e + i_term + d_term)
# 一阶对象离散化(前向欧拉)
y_hist[i] = y_hist[i-1] + dt * (k * u[i-1] - y_hist[i-1]) / tt
y[i] = y_hist[i]
return y
def envelope_ratio(y):
"""用后半段峰谷比判断收敛/等幅/发散"""
seg = y[N//2:]
peaks = [seg[i] for i in range(1, len(seg)-1)
if seg[i] > seg[i-1] and seg[i] > seg[i+1]]
if len(peaks) < 2:
return 1.0
return peaks[-1] / peaks[0] if peaks[0] > 1e-6 else 1.0
# 1) 扫描 Kp 找临界点
print("Kp 包络比 判定")
for kp in [1.0, 1.5, 2.0, 2.5, 3.0, 3.3, 3.6, 3.9, 4.2]:
y = simulate(kp, None, None)
r = envelope_ratio(y)
tag = "衰减" if r < 0.9 else ("等幅≈临界" if r < 1.1 else "发散")
print(f"{kp:5.2f} {r:7.3f} {tag}")
# 2) 按临界法计算 PID 参数(示例 Ku、Tu 由扫描得到)
Ku, Tu = 3.6, 3.4
kp_pid = 0.6 * Ku
ti_pid = Tu / 2
td_pid = Tu / 8
print(f"\nPID 初值: Kp={kp_pid:.2f}, Ti={ti_pid:.2f}s, Td={td_pid:.2f}s")
# 3) 阶跃验证 + 鲁棒性摄动
for label, (k, tt, ta) in {
"标称": (K, T, tau),
"K+20%": (K*1.2, T, tau),
"T+20%": (K, T*1.2, tau),
"tau+20%": (K, T, tau*1.2),
}.items():
y = simulate(kp_pid, ti_pid, td_pid, k, tt, ta)
overshoot = (y.max() - 1.0) * 100
print(f"{label:8s} 超调={overshoot:6.1f}% 终值={y[-1]:.3f}")
运行后应看到:Kp 在 3.3 附近包络比接近 1,3.6 以上开始发散,与频率法估算的 Ku≈3.6 基本吻合;按公式得到的 PID 参数在标称对象上超调偏大(典型 30%~50%),在 τ 增大 20% 时超调进一步上升——这正是纯滞后系统鲁棒性较差的体现,也是下一步需要降低 Kp 或增大 Ti 的依据。
结果判读与参数微调
验证阶段重点看三个量:超调量、调节时间、以及摄动后的稳定性。对示例对象,若标称超调超过 40%,可先把 Kp 从 2.16 降到 1.8,再把 Ti 从 1.7 s 增到 2.2 s,通常能把超调压到 20% 左右而调节时间增加不多;若 τ 摄动后出现持续振荡,则优先增大 Ti(减弱积分作用)并适当减小 Td,因为微分对滞后环节的相位补偿有限,过大反而放大测量噪声。每次只改一个参数并重跑阶跃,记录超调与调节时间的变化,才能形成可复现的整定路径。