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/ΔKdNBNMNSZOPSPMPB
NBPB/NB/PSPB/NB/NSPM/NM/NBPM/NM/NBPS/NS/NBZO/ZO/NMZO/ZO/PS
NMPB/NB/PSPB/NB/NSPM/NM/NBPS/NS/NMPS/NS/NMZO/ZO/NSNS/ZO/ZO
NSPM/NB/ZOPM/NM/NSPM/NS/NMPS/NS/NMZO/ZO/NSNS/PS/NSNS/PS/ZO
ZOPM/NM/ZOPM/NM/NSPS/NS/NSZO/ZO/NSNS/PS/NSNM/PM/NSNM/PM/ZO
PSPS/NM/ZOPS/NS/ZOZO/ZO/ZONS/PS/ZONS/PS/ZONM/PM/ZONM/PB/ZO
PMPS/ZO/PBZO/ZO/NSNS/PS/PSNM/PS/PSNM/PM/PSNM/PB/PSNB/PB/PB
PBZO/ZO/PBZO/ZO/PMNM/PS/PMNM/PM/PMNM/PM/PSNB/PB/PSNB/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 延迟,可根据需要修改。
模糊自适应PID控制系统结构 r(t) Σ − e(t) d/dt 模糊控制器 规则表推理 ΔKp,ΔKi,ΔKd PID Kp+ΔKp 等 u(t) 被控对象 惯性+延迟 y(t) ec(t) e(t)