3. 自动舵积分饱和抑制与抗积分风up策略实现
恒定风干扰下的积分饱和问题
航向保持时,恒定横风会产生持续偏航力矩,PID 中的积分项必须累积出足够的舵角来抵消它。但若积分在误差较大阶段持续累积,一旦航向接近目标,积分项已“存”了过多舵角指令,导致舵角反向超调、航向来回摆动,这就是积分饱和。本例在 Nomoto 一阶模型上实现两种抗饱和策略并对比。
采用离散模型:ψ(k+1)=ψ(k)+T·(K·δ(k)+w)/T₁,其中 ψ 为航向,δ 为舵角,w 为等效恒定风干扰力矩(以角速度形式注入),T 为采样周期。控制器输出舵角指令 δ_cmd = Kp·e + Ki·Σe + Kd·ė,e = ψ_ref − ψ。
仿真实现
下面给出完整可运行的 Python 脚本,包含两种策略的控制器类、同一风干扰场景下的仿真与指标计算。参数为教学示例值,非实测。
import numpy as np
# Nomoto 一阶模型参数(示例)
K, T1, T = 0.18, 12.0, 0.5 # 增益、时间常数、采样周期
W_WIND = 0.012 # 恒定风干扰(等效角速度,rad/s)
PSI_REF = 0.0 # 目标航向
PSI0 = 0.35 # 初始航向偏差(rad)
KP, KI, KD = 1.6, 0.05, 2.2 # 基础 PID 参数
I_MAX = 0.35 # 积分限幅值(策略A)
EPS = 0.05 # 积分分离门限(策略B)
def simulate(strategy, n=600):
psi, integ, prev_e = PSI0, 0.0, PSI_REF - PSI0
hist = []
for k in range(n):
e = PSI_REF - psi
de = (e - prev_e) / T
if strategy == "clamp":
integ += e * T
integ = np.clip(integ, -I_MAX, I_MAX) # 积分限幅
elif strategy == "sep":
if abs(e) < EPS: # 仅小误差时积分
integ += e * T
u = KP * e + KI * integ + KD * de
u = np.clip(u, -0.6, 0.6) # 舵角物理限幅
psi = psi + T * (K * u + W_WIND) / T1
prev_e = e
hist.append((psi, u, integ))
return np.array(hist)
def metrics(h):
psi = h[:, 0]
err = np.abs(psi - PSI_REF)
overshoot = max(0.0, (PSI0 - psi.min()) / PSI0) * 100 if PSI0 > 0 else 0
settle = next((i for i in range(len(err)) if np.all(err[i:] < 0.01)), len(err))
return overshoot, settle * T, err[-1]
for s in ("clamp", "sep"):
h = simulate(s)
ov, ts, ess = metrics(h)
print(f"{s:6s} 超调={ov:5.1f}% 调节时间={ts:5.1f}s 稳态误差={ess:.4f}rad")
运行后预期可观察到:两种策略都能把稳态误差压到接近 0(积分项最终抵消风干扰),但积分限幅策略在初始大偏差阶段积分被钳位,超调更小;积分分离策略在误差进入门限后才开始积分,前期响应更快但门限附近可能出现轻微抖动。具体数值随参数变化,需以实际运行输出为准。
结果判读与调参要点
| 观察量 | 积分限幅 | 积分分离 | 判读依据 |
|---|---|---|---|
| 超调量 | 较小 | 取决于 ε | ψ 越过目标的最大幅度 |
| 稳态误差 | ≈0 | ≈0 | 末段 |e| 是否收敛 |
| 积分项峰值 | 被 I_max 钳位 | 大误差时冻结 | 观察 integ 曲线 |
| 参数敏感点 | I_max 过小→消差慢 | ε 过大→积分滞后 | 调整后重跑对比 |
I_max 建议取“抵消风干扰所需稳态积分值”的 1.2~1.5 倍;ε 建议取目标航向精度的 2~3 倍。两者都需结合舵角限幅一起整定,否则抗饱和效果会被舵机约束掩盖。
若仿真中出现持续等幅振荡,先检查积分项是否仍在累积(限幅值或门限设置不当),再检查 Kd 是否放大了噪声;不要盲目增大 Kp。