4. 抗积分饱和PID设计
任务目标
设计一个带抗积分饱和(Anti-windup)机制的PID控制器,用于数字电源的电压环控制。要求在负载突变时,积分项不会因执行器饱和而过度累积,从而避免输出电压出现过大超调。通过对比普通PID与抗积分饱和PID在相同负载阶跃下的响应,验证抗积分饱和机制的有效性。
核心原理
当控制器输出达到执行器限幅(如PWM占空比上限)时,误差持续存在会导致积分项继续累积,形成积分饱和。一旦误差反向,饱和的积分项需要较长时间才能退饱和,造成大超调。抗积分饱和的常用方法是条件积分法(Conditional Integration):仅在控制器输出未饱和时允许积分累加;输出饱和时冻结积分项,或仅允许积分向退饱和方向变化。
实现代码(C语言,适用于DSP或MCU)
#include <stdio.h>
#include <math.h>
#define DT 0.0001f // 采样周期 100us
#define U_MAX 0.95f // 占空比上限(执行器饱和)
#define U_MIN 0.05f // 占空比下限
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float out;
} PID_t;
// 抗积分饱和PID计算(条件积分法)
float pid_update(PID_t *pid, float ref, float fb) {
float error = ref - fb;
float out_unclamped;
// 先计算未限幅的输出(积分项暂用当前值)
out_unclamped = pid->Kp * error + pid->integral + pid->Kd * (error - pid->prev_error) / DT;
// 限幅
if (out_unclamped > U_MAX) {
pid->out = U_MAX;
// 饱和时冻结积分(条件积分)
// 仅当误差为负(退饱和方向)才允许积分减小
if (error < 0) {
pid->integral += pid->Ki * error * DT;
}
} else if (out_unclamped < U_MIN) {
pid->out = U_MIN;
if (error > 0) {
pid->integral += pid->Ki * error * DT;
}
} else {
pid->out = out_unclamped;
pid->integral += pid->Ki * error * DT; // 正常积分
}
// 积分限幅(防止长时间饱和后积分过大)
if (pid->integral > U_MAX) pid->integral = U_MAX;
if (pid->integral < U_MIN) pid->integral = U_MIN;
pid->prev_error = error;
return pid->out;
}
// 普通PID(用于对比,无抗饱和)
float pid_update_normal(PID_t *pid, float ref, float fb) {
float error = ref - fb;
pid->integral += pid->Ki * error * DT;
if (pid->integral > U_MAX) pid->integral = U_MAX;
if (pid->integral < U_MIN) pid->integral = U_MIN;
pid->out = pid->Kp * error + pid->integral + pid->Kd * (error - pid->prev_error) / DT;
if (pid->out > U_MAX) pid->out = U_MAX;
if (pid->out < U_MIN) pid->out = U_MIN;
pid->prev_error = error;
return pid->out;
}
// 模拟被控对象:Buck变换器简化模型(一阶惯性+延迟)
float plant_model(float duty, float vout_prev) {
// 简化模型:Vout = Vout*0.98 + duty*Vin*0.02,Vin=12V
return vout_prev * 0.98f + duty * 12.0f * 0.02f;
}
int main() {
PID_t pid_aw = {0.5f, 20.0f, 0.001f, 0, 0, 0}; // 抗饱和PID
PID_t pid_nm = {0.5f, 20.0f, 0.001f, 0, 0, 0}; // 普通PID
float vout_aw = 0, vout_nm = 0;
float ref = 5.0f; // 目标电压5V
float duty_aw, duty_nm;
float load_step_time = 0.05f; // 50ms时负载突变
printf("time(s), Vout_AW(V), Vout_NM(V), Duty_AW, Duty_NM\n");
for (int i = 0; i < 2000; i++) { // 模拟200ms
float t = i * DT;
// 50ms后模拟负载突变:等效降低增益(输出电阻减小)
float load_factor = (t > load_step_time) ? 0.5f : 1.0f;
duty_aw = pid_update(&pid_aw, ref, vout_aw);
duty_nm = pid_update_normal(&pid_nm, ref, vout_nm);
// 被控对象响应(负载突变体现在模型系数变化)
vout_aw = plant_model(duty_aw, vout_aw) * load_factor;
vout_nm = plant_model(duty_nm, vout_nm) * load_factor;
if (i % 50 == 0) { // 每5ms打印一次
printf("%.4f, %.4f, %.4f, %.4f, %.4f\n", t, vout_aw, vout_nm, duty_aw, duty_nm);
}
}
return 0;
}
运行与验证
编译运行:
gcc -o pid_antiwindup pid_antiwindup.c -lm
./pid_antiwindup
验证方法:程序输出CSV格式数据,包含时间、两种PID的输出电压和占空比。将输出重定向到文件后用Python或Excel绘图:
./pid_antiwindup > result.csv
通过条件:
- 在0~50ms启动阶段,两种PID均能跟踪5V参考电压;
- 50ms负载突变后,普通PID(Vout_NM)出现明显超调(峰值超过5.3V),而抗饱和PID(Vout_AW)超调显著更小(峰值低于5.15V);
- 抗饱和PID的占空比在饱和后能更快回落,而普通PID的占空比长时间停留在上限附近。
关键说明
- 条件积分法的核心逻辑:输出饱和时,仅当误差方向有利于退饱和时才更新积分项,否则冻结。这防止了积分在饱和期间继续增大。
- 积分限幅(U_MAX/U_MIN)作为第二重保护,避免积分项无限增长。
- 被控对象采用简化一阶模型,实际DSP应用中替换为ADC采样值和PWM输出即可,PID算法部分无需改动。
- Kp=0.5, Ki=20, Kd=0.001为示例参数,实际电源系统需根据环路带宽和相位裕度整定。
上图示意了负载突变后两种PID的响应差异:普通PID因积分饱和导致输出电压明显冲高,而抗积分饱和PID通过冻结饱和期间的积分累加,显著抑制了超调,使电压更快恢复稳定。实际运行程序可得到精确数值曲线。