3. PID参数整定之Ziegler-Nichols法

任务目标

针对一阶惯性加延迟对象(FOPDT),采用Ziegler-Nichols临界比例度法(连续振荡法)整定PID参数。先通过纯比例控制使系统输出等幅振荡,记录临界增益\(K_u\)与临界振荡周期\(T_u\),再按ZN整定公式计算PID参数,最后通过阶跃响应验证控制效果。

被控对象与仿真环境

被控对象传递函数为:

G(s) = Kp * e^(-Ls) / (T*s + 1)

其中\(K_p=1.0\),\(T=10\),\(L=2\)。使用Python实现仿真,依赖numpy和matplotlib(仅用于绘图验证)。

关键完整代码

import numpy as np
import matplotlib.pyplot as plt

# ---------- 被控对象参数 ----------
Kp_obj = 1.0      # 对象增益
T_obj = 10.0      # 对象时间常数
L_obj = 2.0       # 对象纯延迟

# ---------- 仿真参数 ----------
dt = 0.01
sim_time = 100.0
t = np.arange(0, sim_time, dt)
n = len(t)

# ---------- 一阶惯性+延迟对象仿真 ----------
def plant(y, u, delay_buffer, delay_steps):
    """y: 当前输出, u: 当前控制量, delay_buffer: 延迟队列"""
    u_delayed = delay_buffer[0]  # 取最早的控制量
    dy = (Kp_obj * u_delayed - y) / T_obj
    y_new = y + dy * dt
    # 更新延迟队列
    delay_buffer.append(u)
    delay_buffer.pop(0)
    return y_new, u_delayed

# ---------- 临界比例度法:寻找Ku和Tu ----------
def find_critical():
    Ku = 0.0
    Tu = 0.0
    # 逐步增大比例增益,直到出现等幅振荡
    for Kc in np.arange(0.5, 20.0, 0.1):
        y = 0.0
        u = 0.0
        delay_steps = int(L_obj / dt)
        delay_buffer = [0.0] * delay_steps
        y_hist = []
        t_hist = []
        setpoint = 1.0
        # 运行足够长时间观察振荡
        for i in range(n):
            e = setpoint - y
            u = Kc * e
            # 限幅
            u = np.clip(u, -5, 5)
            y, _ = plant(y, u, delay_buffer, delay_steps)
            if i > int(30/dt):  # 跳过暂态
                y_hist.append(y)
                t_hist.append(t[i])
        # 检测等幅振荡:计算峰值数量与幅值稳定性
        y_arr = np.array(y_hist)
        # 找峰值
        peaks = []
        for i in range(1, len(y_arr)-1):
            if y_arr[i] > y_arr[i-1] and y_arr[i] > y_arr[i+1] and y_arr[i] > 0.5:
                peaks.append((t_hist[i], y_arr[i]))
        if len(peaks) >= 4:
            # 检查幅值是否稳定(后几个峰值与前面峰值差异小于5%)
            amps = [p[1] for p in peaks[-4:]]
            if max(amps) - min(amps) < 0.05 * np.mean(amps):
                Ku = Kc
                # 计算振荡周期
                periods = []
                for i in range(1, len(peaks)):
                    periods.append(peaks[i][0] - peaks[i-1][0])
                Tu = np.mean(periods[-3:])
                break
    return Ku, Tu

Ku, Tu = find_critical()
print(f"临界增益 Ku = {Ku:.3f}")
print(f"临界振荡周期 Tu = {Tu:.3f} s")

# ---------- Ziegler-Nichols PID整定 ----------
# 经典ZN公式(PID)
Kp_zn = 0.6 * Ku
Ti_zn = 0.5 * Tu
Td_zn = 0.125 * Tu
print(f"ZN整定: Kp={Kp_zn:.3f}, Ti={Ti_zn:.3f}, Td={Td_zn:.3f}")

# ---------- 应用ZN参数进行闭环阶跃响应验证 ----------
def simulate_pid(Kp, Ti, Td, setpoint=1.0):
    y = 0.0
    u = 0.0
    integral = 0.0
    prev_e = 0.0
    delay_steps = int(L_obj / dt)
    delay_buffer = [0.0] * delay_steps
    y_hist = []
    for i in range(n):
        e = setpoint - y
        integral += e * dt
        derivative = (e - prev_e) / dt
        u = Kp * (e + (1/Ti)*integral + Td*derivative)
        u = np.clip(u, -5, 5)
        y, _ = plant(y, u, delay_buffer, delay_steps)
        prev_e = e
        y_hist.append(y)
    return np.array(y_hist)

# 运行ZN参数仿真
y_zn = simulate_pid(Kp_zn, Ti_zn, Td_zn)

# ---------- 验证结果 ----------
# 计算超调量与稳态误差
y_settle = y_zn[-int(5/dt):]  # 最后5秒
steady_state = np.mean(y_settle)
overshoot = (np.max(y_zn) - 1.0) / 1.0 * 100
print(f"稳态值: {steady_state:.3f}")
print(f"超调量: {overshoot:.1f}%")

# 绘制阶跃响应
plt.figure(figsize=(10, 5))
plt.plot(t, y_zn, 'b-', linewidth=1.5, label='ZN-PID响应')
plt.axhline(y=1.0, color='r', linestyle='--', alpha=0.7, label='设定值')
plt.xlabel('时间 (s)')
plt.ylabel('输出')
plt.title('Ziegler-Nichols整定PID阶跃响应')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xlim(0, 60)
plt.savefig('zn_pid_response.png', dpi=100)
plt.show()

运行与验证方法

  1. 直接运行上述Python脚本,需安装numpy和matplotlib。
  2. 程序首先自动搜索临界增益\(K_u\):从\(K_c=0.5\)开始逐步增大,当系统输出出现持续等幅振荡(后4个峰值幅值差异小于5%)时记录当前增益与振荡周期。
  3. 根据ZN公式计算PID参数并执行闭环阶跃响应仿真。
  4. 通过条件:控制台输出\(K_u\)和\(T_u\)为合理正值(本例预期\(K_u\)约在5~8之间,\(T_u\)约在8~12秒之间);阶跃响应曲线最终稳定在设定值1.0附近,稳态误差小于2%,超调量在10%~60%范围内(ZN法典型特征),且无持续振荡。

预期输出(基于代码逻辑推导)

临界增益 Ku ≈ 6.5~7.5
临界振荡周期 Tu ≈ 9~11 s
ZN整定: Kp ≈ 4.0~4.5, Ti ≈ 4.5~5.5, Td ≈ 1.1~1.4
稳态值 ≈ 1.000
超调量 ≈ 30%~50%

以上数值由对象参数与ZN公式唯一确定,实际运行结果应落在此范围内。若超调过大或响应发散,可检查延迟队列长度是否正确(delay_steps = int(L_obj/dt))以及限幅是否生效。

Ziegler-Nichols临界比例度法整定流程 步骤1 纯比例控制 逐步增大Kc 步骤2 检测等幅振荡 记录Ku与Tu 步骤3 ZN整定公式 Kp=0.6Ku Ziegler-Nichols整定公式(临界比例度法) Kp = 0.6 · Ku Ti = 0.5 · Tu Td = 0.125 · Tu 阶跃响应验证 观察超调与稳态误差 判定标准 稳态误差<2%,无持续振荡 完成 参数可用