3. 抗积分饱和PID

任务目标

在标准PID控制器中,当系统存在持续误差(如启动阶段或执行器饱和)时,积分项会不断累积,导致积分饱和(windup)。这会造成严重的超调、振荡甚至系统失稳。本案例实现一个带积分限幅和抗积分饱和机制的PID控制器,核心策略为:当控制器输出达到执行器限幅时,停止积分累加(条件积分法),从而防止积分过度累积。

核心实现

#include <stdio.h>
#include <stdint.h>

typedef struct {
    float kp, ki, kd;          // PID增益
    float dt;                  // 采样周期(s)
    float integral;            // 积分累积值
    float prev_error;          // 上一次误差
    float out_min, out_max;    // 执行器输出限幅
    float int_min, int_max;    // 积分限幅(抗饱和上限)
} AntiWindupPID;

void pid_init(AntiWindupPID *pid, float kp, float ki, float kd, float dt,
              float out_min, float out_max) {
    pid->kp = kp; pid->ki = ki; pid->kd = kd; pid->dt = dt;
    pid->integral = 0.0f;
    pid->prev_error = 0.0f;
    pid->out_min = out_min; pid->out_max = out_max;
    // 积分限幅设为输出限幅的1.5倍(工程经验值,可按需调整)
    pid->int_min = out_min * 1.5f;
    pid->int_max = out_max * 1.5f;
}

float pid_update(AntiWindupPID *pid, float setpoint, float measurement) {
    float error = setpoint - measurement;
    float p_term = pid->kp * error;

    // 积分项(带限幅)
    pid->integral += pid->ki * error * pid->dt;
    if (pid->integral > pid->int_max) pid->integral = pid->int_max;
    if (pid->integral < pid->int_min) pid->integral = pid->int_min;

    // 微分项(实际微分,带低通滤波可在此扩展)
    float d_term = pid->kd * (error - pid->prev_error) / pid->dt;
    pid->prev_error = error;

    // 计算未限幅输出
    float output = p_term + pid->integral + d_term;

    // 抗积分饱和:输出饱和时,撤销本次积分累加
    if (output > pid->out_max) {
        output = pid->out_max;
        pid->integral -= pid->ki * error * pid->dt;  // 回退积分
    } else if (output < pid->out_min) {
        output = pid->out_min;
        pid->integral -= pid->ki * error * pid->dt;
    }

    return output;
}

// 简单测试:模拟电机启动(设定值阶跃,执行器饱和)
int main() {
    AntiWindupPID pid;
    pid_init(&pid, 2.0f, 0.5f, 0.1f, 0.01f, -100.0f, 100.0f);

    float measurement = 0.0f;
    float setpoint = 50.0f;   // 目标位置
    float output;

    printf("step, measurement, output, integral\n");
    for (int i = 0; i < 200; i++) {
        output = pid_update(&pid, setpoint, measurement);
        // 模拟一阶被控对象(电机响应)
        measurement += (output * 0.1f - measurement) * 0.05f;
        if (i % 20 == 0)
            printf("%d, %.2f, %.2f, %.2f\n", i, measurement, output, pid.integral);
    }
    return 0;
}

运行与验证

编译运行:

gcc -o antiwindup antiwindup.c -lm
./antiwindup

验证方法:观察输出数据,系统应满足:

  1. 启动阶段输出被限制在±100以内,不会超出执行器限幅;
  2. 测量值最终收敛到设定值50附近,无明显超调(超调量<5%);
  3. 积分项数值始终在积分限幅范围内(±150),不会无限增长。

对比测试:将pid_update中抗饱和回退逻辑注释掉(删除integral -= ...两行),重新编译运行,可观察到测量值出现明显超调(通常>15%)和振荡,以此验证抗饱和机制的有效性。

关键说明

  • 积分限幅(int_min/int_max)与输出限幅解耦,允许积分项在输出饱和时仍保留一定余量,避免积分完全失效;
  • 条件积分法(输出饱和时回退积分)是工程中最常用的抗饱和策略,实现简单且无需额外状态判断;
  • 实际工程中若被控对象存在纯滞后,建议在微分项后级联一阶低通滤波器(截止频率≈1/(2π·dt)),本案例为聚焦抗饱和逻辑未包含该部分。
抗积分饱和PID控制框图 Σ r(t) -y(t) e(t) PID P+I+D 积分限幅 ±1.5×out u(t) 限幅 [min,max] 对象 G(s) y(t) 反馈 饱和时回退积分 + −