4. 微分先行PID

任务目标

在标准PID控制中,设定值突变会经微分项产生剧烈输出跳变(微分冲击),可能引起执行机构饱和或系统振荡。本案例实现微分先行PID(Differential-First PID),仅对测量值做微分、不对设定值做微分,从而在设定值阶跃时消除微分冲击,同时保留对测量值波动的抑制能力。

核心算法

微分先行PID的位置式离散实现:

  • 比例项:P = Kp * e(k),其中e(k) = SP(k) - PV(k)
  • 微分项:仅对测量值求导,D = -Kd * (PV(k) - PV(k-1)) / Ts
  • 积分项:I = Ki * Σ e(i) * Ts(含抗积分饱和)
  • 输出:u(k) = P + I + D

与标准PID(微分项作用于误差e(k)-e(k-1))相比,设定值阶跃时PV未变化,因此D=0,输出仅由比例和积分平滑过渡。

完整实现

import matplotlib.pyplot as plt

class DiffFirstPID:
    def __init__(self, Kp, Ki, Kd, Ts, u_min=-100.0, u_max=100.0):
        self.Kp = Kp
        self.Ki = Ki
        self.Kd = Kd
        self.Ts = Ts
        self.u_min = u_min
        self.u_max = u_max
        self.integral = 0.0
        self.prev_pv = None

    def reset(self):
        self.integral = 0.0
        self.prev_pv = None

    def compute(self, sp, pv):
        # 比例项
        e = sp - pv
        P = self.Kp * e

        # 微分项:仅对测量值微分
        if self.prev_pv is None:
            D = 0.0
        else:
            D = -self.Kd * (pv - self.prev_pv) / self.Ts
        self.prev_pv = pv

        # 积分项(带抗积分饱和)
        self.integral += e * self.Ts
        I = self.Ki * self.integral

        # 合成输出并限幅
        u = P + I + D
        if u > self.u_max:
            u = self.u_max
            self.integral -= e * self.Ts  # 反算撤销积分增量
        elif u < self.u_min:
            u = self.u_min
            self.integral -= e * self.Ts
        return u

# 仿真:设定值阶跃 + 测量值扰动
def simulate():
    pid = DiffFirstPID(Kp=2.0, Ki=0.5, Kd=0.8, Ts=0.1)
    t = [i * 0.1 for i in range(200)]
    sp = [0.0] * 50 + [10.0] * 150      # 5s时设定值阶跃
    pv = [0.0] * 50 + [0.0] * 150       # 测量值初始为0
    # 在10s处加入测量值阶跃扰动
    for i in range(100, 200):
        pv[i] = 2.0

    u_out = []
    pv_track = []
    for i in range(len(t)):
        u = pid.compute(sp[i], pv[i])
        u_out.append(u)
        # 模拟被控对象:一阶惯性 + 控制量作用
        if i == 0:
            pv_track.append(0.0)
        else:
            dt = 0.1
            pv_new = pv_track[-1] + dt * (0.5 * (u - pv_track[-1]))
            pv_track.append(pv_new)

    # 绘图
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6), sharex=True)
    ax1.plot(t, sp, 'r--', label='设定值 SP')
    ax1.plot(t, pv_track, 'b-', label='测量值 PV')
    ax1.set_ylabel('数值')
    ax1.legend()
    ax1.grid(True)

    ax2.plot(t, u_out, 'g-', label='控制输出 u')
    ax2.set_xlabel('时间 (s)')
    ax2.set_ylabel('输出')
    ax2.legend()
    ax2.grid(True)
    plt.tight_layout()
    plt.show()

if __name__ == '__main__':
    simulate()

运行与验证

运行方式:保存为diff_first_pid.py,执行python diff_first_pid.py,需安装matplotlib。

验证方法:

  • 观察输出曲线:在t=5s设定值阶跃瞬间,控制输出u应平滑上升,无尖峰脉冲——这证明微分冲击已被消除。
  • 在t=10s测量值阶跃扰动时,输出应出现反向调节动作,说明微分项仍对测量值变化敏感。
  • 对比验证(可选):将compute()中微分项改为D = Kd * (e - prev_e)/Ts(标准PID),相同参数下设定值阶跃时输出会出现明显尖峰,可直观对比差异。

通过条件:设定值阶跃时输出无瞬时跳变尖峰,且测量值扰动时控制器能产生有效抑制动作。