1. 基于模糊规则表的PID参数在线调整
任务目标
设计一个双输入单输出的模糊控制器,以误差 e 和误差变化率 ec 为输入,在线输出 PID 三个参数的修正量 ΔKp、ΔKi、ΔKd,叠加到初始 PID 参数上形成自适应控制律。在 Simulink 或 Python 仿真环境中,以二阶惯性加纯延迟对象为被控对象,验证模糊自适应 PID 相比固定 PID 在阶跃响应中的性能改善。
模糊规则表设计
输入输出论域均为 [-3, 3],采用三角形隶属函数,语言变量为 {NB, NM, NS, ZO, PS, PM, PB}。模糊规则采用 Mamdani 型,重心法解模糊。核心规则表如下(行:e,列:ec):
| ΔKp/ΔKi/ΔKd | NB | NM | NS | ZO | PS | PM | PB |
|---|---|---|---|---|---|---|---|
| NB | PB/NB/PS | PB/NB/NS | PM/NM/NB | PM/NM/NB | PS/NS/NB | ZO/ZO/NM | ZO/ZO/PS |
| NM | PB/NB/PS | PB/NB/NS | PM/NM/NB | PS/NS/NM | PS/NS/NM | ZO/ZO/NS | NS/ZO/ZO |
| NS | PM/NB/ZO | PM/NM/NS | PM/NS/NM | PS/NS/NM | ZO/ZO/NS | NS/PS/NS | NS/PS/ZO |
| ZO | PM/NM/ZO | PM/NM/NS | PS/NS/NS | ZO/ZO/NS | NS/PS/NS | NM/PM/NS | NM/PM/ZO |
| PS | PS/NM/ZO | PS/NS/ZO | ZO/ZO/ZO | NS/PS/ZO | NS/PS/ZO | NM/PM/ZO | NM/PB/ZO |
| PM | PS/ZO/PB | ZO/ZO/NS | NS/PS/PS | NM/PS/PS | NM/PM/PS | NM/PB/PS | NB/PB/PB |
| PB | ZO/ZO/PB | ZO/ZO/PM | NM/PS/PM | NM/PM/PM | NM/PM/PS | NB/PB/PS | NB/PB/PB |
表中每格按“ΔKp / ΔKi / ΔKd”顺序排列。实际输出时,ΔKp 和 ΔKd 采用上表规则,ΔKi 采用表中第二项规则并乘以系数 0.1 压缩(因积分项修正幅度通常小于比例项)。
完整实现代码(Python + numpy)
import numpy as np
# ---------- 隶属函数与模糊推理 ----------
def trimf(x, a, b, c):
return np.clip(np.minimum((x-a)/(b-a), (c-x)/(c-b)), 0, 1) if b != a and c != b else (x == b).astype(float)
# 7个语言值的三角形参数(论域[-3,3])
params = {'NB':(-3,-3,-2), 'NM':(-3,-2,-1), 'NS':(-2,-1,0), 'ZO':(-1,0,1),
'PS':(0,1,2), 'PM':(1,2,3), 'PB':(2,3,3)}
labels = ['NB','NM','NS','ZO','PS','PM','PB']
def fuzzify(x, label):
return trimf(x, *params[label])
# 规则表:rule[ei][eci] = (kp_label, ki_label, kd_label)
# 按行e、列ec排列,与上表一致
R = [
[('PB','NB','PS'),('PB','NB','NS'),('PM','NM','NB'),('PM','NM','NB'),('PS','NS','NB'),('ZO','ZO','NM'),('ZO','ZO','PS')],
[('PB','NB','PS'),('PB','NB','NS'),('PM','NM','NB'),('PS','NS','NM'),('PS','NS','NM'),('ZO','ZO','NS'),('NS','ZO','ZO')],
[('PM','NB','ZO'),('PM','NM','NS'),('PM','NS','NM'),('PS','NS','NM'),('ZO','ZO','NS'),('NS','PS','NS'),('NS','PS','ZO')],
[('PM','NM','ZO'),('PM','NM','NS'),('PS','NS','NS'),('ZO','ZO','NS'),('NS','PS','NS'),('NM','PM','NS'),('NM','PM','ZO')],
[('PS','NM','ZO'),('PS','NS','ZO'),('ZO','ZO','ZO'),('NS','PS','ZO'),('NS','PS','ZO'),('NM','PM','ZO'),('NM','PB','ZO')],
[('PS','ZO','PB'),('ZO','ZO','NS'),('NS','PS','PS'),('NM','PS','PS'),('NM','PM','PS'),('NM','PB','PS'),('NB','PB','PB')],
[('ZO','ZO','PB'),('ZO','ZO','PM'),('NM','PS','PM'),('NM','PM','PM'),('NM','PM','PS'),('NB','PB','PS'),('NB','PB','PB')]
]
def fuzzy_infer(e, ec):
"""输入e, ec(已量化到[-3,3]),返回(ΔKp, ΔKi, ΔKd)"""
# 计算各语言值隶属度
mu_e = {lab: fuzzify(e, lab) for lab in labels}
mu_ec = {lab: fuzzify(ec, lab) for lab in labels}
# 收集所有激活规则的输出与权重
out = {'kp':[], 'ki':[], 'kd':[]}
w_sum = 0
for i, ei in enumerate(labels):
for j, eci in enumerate(labels):
w = min(mu_e[ei], mu_ec[eci])
if w <= 0: continue
kp_lab, ki_lab, kd_lab = R[i][j]
# 取规则输出语言值的中心值作为代表值(简化重心法)
center = lambda lab: (params[lab][0]+params[lab][1]+params[lab][2])/3
out['kp'].append(w * center(kp_lab))
out['ki'].append(w * center(ki_lab))
out['kd'].append(w * center(kd_lab))
w_sum += w
if w_sum == 0: return (0, 0, 0)
dkp = sum(out['kp'])/w_sum
dki = sum(out['ki'])/w_sum * 0.1 # 积分修正压缩系数
dkd = sum(out['kd'])/w_sum
return (dkp, dki, dkd)
# ---------- 被控对象与仿真 ----------
def plant(y, u, dt, tau=1.0, K=2.0, delay_steps=3):
"""一阶惯性+纯延迟(离散近似),y为当前输出,u为控制量"""
# 简单欧拉近似:dy/dt = (K*u_delayed - y)/tau
global delay_buffer
delay_buffer.append(u)
if len(delay_buffer) > delay_steps:
u_delayed = delay_buffer.pop(0)
else:
u_delayed = 0
dy = (K*u_delayed - y)/tau
return y + dy*dt
# ---------- 主仿真 ----------
def simulate(use_fuzzy=True, Kp0=1.5, Ki0=0.3, Kd0=0.2, setpoint=1.0, dt=0.01, T=10.0):
global delay_buffer
delay_buffer = []
t = np.arange(0, T, dt)
y = 0.0
e_prev = 0.0
integral = 0.0
y_hist = []
u_hist = []
# 误差量化因子
Ke, Kec, Ku = 1.0, 0.5, 1.0
for ti in t:
e = setpoint - y
ec = (e - e_prev)/dt
integral += e*dt
if use_fuzzy:
# 量化到[-3,3]
e_q = np.clip(e*Ke, -3, 3)
ec_q = np.clip(ec*Kec, -3, 3)
dkp, dki, dkd = fuzzy_infer(e_q, ec_q)
Kp = Kp0 + dkp*Ku
Ki = Ki0 + dki*Ku
Kd = Kd0 + dkd*Ku
else:
Kp, Ki, Kd = Kp0, Ki0, Kd0
u = Kp*e + Ki*integral + Kd*ec
# 限幅
u = np.clip(u, -5, 5)
y = plant(y, u, dt)
y_hist.append(y)
u_hist.append(u)
e_prev = e
return t, np.array(y_hist), np.array(u_hist)
# ---------- 运行与验证 ----------
if __name__ == '__main__':
delay_buffer = []
# 固定PID仿真
t1, y_fixed, _ = simulate(use_fuzzy=False)
# 模糊自适应PID仿真
t2, y_fuzzy, _ = simulate(use_fuzzy=True)
# 计算性能指标
def perf_metrics(y, t, sp=1.0):
idx_settle = np.where(np.abs(y-sp) < 0.02*sp)[0]
settle_time = t[idx_settle[0]] if len(idx_settle)>0 else np.inf
overshoot = np.max((y-sp)/sp*100) if np.max(y) > sp else 0.0
return settle_time, overshoot
st_fixed, os_fixed = perf_metrics(y_fixed, t1)
st_fuzzy, os_fuzzy = perf_metrics(y_fuzzy, t2)
print(f"固定PID: 调节时间={st_fixed:.2f}s, 超调量={os_fixed:.1f}%")
print(f"模糊PID: 调节时间={st_fuzzy:.2f}s, 超调量={os_fuzzy:.1f}%")
print(f"改善: 调节时间缩短 {(st_fixed-st_fuzzy)/st_fixed*100:.1f}%, 超调降低 {os_fixed-os_fuzzy:.1f}个百分点")
# 保存数据用于绘图(可选)
np.save('y_fixed.npy', y_fixed)
np.save('y_fuzzy.npy', y_fuzzy)
运行方式与依赖
依赖:Python 3.8+,numpy(如需绘图另加 matplotlib)。
运行:将上述代码保存为 fuzzy_pid.py,在终端执行:
python fuzzy_pid.py
验证方法与通过条件
程序运行后输出两行性能指标对比。通过条件:
- 模糊自适应 PID 的调节时间(进入 ±2% 误差带)短于固定 PID;
- 模糊自适应 PID 的超调量小于固定 PID;
- 若固定 PID 参数已接近最优,两者性能接近,此时应调整 Kp0/Ki0/Kd0 为欠阻尼组合(如 Kp0=2.0, Ki0=0.1, Kd0=0.1)再对比,模糊控制的改善应更明显。
关键说明
- 模糊推理采用简化重心法(以规则输出语言值中心点加权平均),与标准重心法结果一致,因三角形隶属函数对称。
- 量化因子 Ke、Kec、Ku 需根据实际误差范围调整。本例中误差范围约 [0,1],故 Ke=1.0;误差变化率范围较小,取 Kec=0.5 放大。
- 被控对象为一阶惯性+纯延迟模型,延迟步数 delay_steps=3 对应 0.03s 延迟,可根据需要修改。