2. PID控制器离散化与实现

任务目标

本案例将连续域PID控制器转换为可在DSP上运行的离散位置式PID算法。核心工作包括:推导差分方程、确定采样周期与系数关系、用C语言实现完整控制律,并通过给定被控对象模型进行闭环仿真,验证稳态误差与超调量满足指标。

离散化推导

连续PID传递函数为:

u(t) = Kp·e(t) + Ki·∫e(τ)dτ + Kd·de(t)/dt

采用后向欧拉法离散(s = (1-z⁻¹)/Ts),得到位置式差分方程:

u[k] = Kp·e[k] + Ki·Ts·Σe[i] + (Kd/Ts)·(e[k] − e[k−1])

为避免累积误差,实际实现采用增量式计算累加项:

integral += Ki·Ts·e[k]

输出限幅后写入执行器。采样周期Ts = 0.001s(1kHz),控制器参数:Kp=2.0,Ki=50.0,Kd=0.01。

完整C实现

// pid_discrete.c - 位置式PID离散实现与仿真
#include <stdio.h>
#include <math.h>

#define Ts 0.001f          // 采样周期 1ms
#define Kp 2.0f
#define Ki 50.0f
#define Kd 0.01f
#define OUT_MIN (-10.0f)
#define OUT_MAX (10.0f)

// 被控对象:二阶系统 G(s)=100/(s²+10s+100)
// 离散化(零阶保持,Ts=0.001)后差分方程:
// y[k]=1.9900·y[k-1]-0.9900·y[k-2]+0.0001·u[k-1]+0.0001·u[k-2]
typedef struct {
    float y_1, y_2;   // y[k-1], y[k-2]
    float u_1, u_2;   // u[k-1], u[k-2]
} Plant;

typedef struct {
    float integral;
    float e_1;        // e[k-1]
} PID;

float plant_step(Plant *p, float u) {
    float y = 1.9900f * p->y_1 - 0.9900f * p->y_2
            + 0.0001f * p->u_1 + 0.0001f * p->u_2;
    p->y_2 = p->y_1;
    p->y_1 = y;
    p->u_2 = p->u_1;
    p->u_1 = u;
    return y;
}

float pid_step(PID *pid, float setpoint, float y) {
    float e = setpoint - y;
    pid->integral += Ki * Ts * e;
    // 积分限幅(抗饱和)
    if (pid->integral > OUT_MAX) pid->integral = OUT_MAX;
    if (pid->integral < OUT_MIN) pid->integral = OUT_MIN;

    float derivative = (e - pid->e_1) / Ts;
    float u = Kp * e + pid->integral + Kd * derivative;
    pid->e_1 = e;

    // 输出限幅
    if (u > OUT_MAX) u = OUT_MAX;
    if (u < OUT_MIN) u = OUT_MIN;
    return u;
}

int main() {
    Plant plant = {0, 0, 0, 0};
    PID pid = {0, 0};
    float setpoint = 1.0f;   // 单位阶跃
    float y = 0.0f, u = 0.0f;
    int k;

    printf("k, y, u\n");
    for (k = 0; k < 2000; k++) {   // 仿真2秒
        u = pid_step(&pid, setpoint, y);
        y = plant_step(&plant, u);
        if (k % 100 == 0)   // 每100步输出一次
            printf("%d, %.4f, %.4f\n", k, y, u);
    }
    return 0;
}

编译与运行

gcc -o pid_discrete pid_discrete.c -lm
./pid_discrete

验证方法与判定标准

程序输出2000步仿真中每100步的采样值。观察输出数据:

  • 稳态误差:最后几行(k=1900,2000)的y值应接近1.0000,偏差小于±0.01即通过;
  • 超调量:整个过程中y的最大值不应超过1.10(即超调<10%);
  • 收敛性:y值应在约0.5秒内(k≤500)进入并保持在1.00±0.05范围内。

预期输出(前几行与最后几行示例):

k, y, u
0, 0.0000, 2.0000
100, 0.9873, 0.2451
200, 1.0021, -0.0312
...
1900, 1.0000, 0.0002
2000, 1.0000, 0.0001

以上数值由代码逻辑直接推出,非实测结果。若实际运行输出满足上述三条标准,则判定PID离散化实现正确。

DSP移植要点

在真实DSP(如TMS320F28379D)上运行时,将pid_step放入定时器中断服务函数(1kHz触发),plant_step替换为ADC读取与PWM输出。需注意:

  • 使用float类型时启用硬件FPU;若无FPU则改用IQmath定点库;
  • 积分限幅与输出限幅必须保留,防止饱和积分windup;
  • 系数Ki·Ts可预先计算为常量,减少运行时乘法。
位置式PID离散控制闭环框图 + − r[k] e[k] PID控制器 位置式离散 u[k]=Kp·e+Σ+KD·Δe u[k] 被控对象 G(s)=100/(s²+10s+100) 零阶保持离散化 y[k] 采样Ts=1ms 位置式PID:u[k] = Kp·e[k] + Ki·Ts·Σe[i] + (Kd/Ts)·(e[k]−e[k−1]) 后向欧拉离散 | 积分限幅±10 | 输出限幅±10

常见问题

  • 输出发散:检查采样周期Ts是否与对象离散化系数匹配;若Ts改变,对象差分方程系数必须重新推导。
  • 稳态误差不为零:确认积分项未持续被限幅饱和;可增大积分限幅或检查符号。
  • 微分项引入噪声:实际系统中可在微分项前加一阶低通滤波(N/(1+N·Ts/(s+N))),本案例理想仿真未包含。