4. 电流环设计(下):数字实现中的延迟补偿,抗积分饱和(Anti-Windup),电流采样与滤波
上一讲我们把电流环的连续域模型推了一遍,PI参数怎么算也给了公式。但说实话,从理论到代码,中间还隔着好几道坎。你想想看,代码跑在MCU里,采样要时间,计算要时间,PWM更新还得等下一个周期——这些延迟不处理,你的电流环跑起来就会抖,甚至振荡。
这一讲,我们就来填这些坑。我个人习惯把数字实现中的三个关键问题拎出来:延迟补偿、抗积分饱和、电流采样与滤波。这三个点做好了,你的电流环才算真正“落地”。
核心观点:数字电流环的性能瓶颈,往往不在算法本身,而在延迟和饱和处理。把这两件事做好,PI参数甚至可以再往激进的方向调一调。
4.1 延迟补偿:别让PI算了个“过期”的值
数字控制里有个很尴尬的事:你辛辛苦苦算出来的电压指令,其实是基于上一个周期的电流值。等PWM真正更新的时候,电流已经变了。这就是一拍延迟问题。
我刚开始做电机驱动时,就吃过这个亏。PI参数按连续域算得好好的,一上机电流就振荡。查了半天,发现是延迟没处理。后来加了补偿,世界清净了。
4.1.1 延迟的来源
- 采样延迟:ADC转换需要时间,通常几微秒到十几微秒。
- 计算延迟:坐标变换、PI计算、SVPWM计算,加起来几十微秒。
- PWM更新延迟:大多数MCU在PWM周期顶点或底点更新比较值,这意味着计算结果要等到下一个周期才生效。
综合下来,从采样到PWM生效,通常有1.5~2个PWM周期的延迟。对于20kHz的PWM,这就是50~100μs。在高速电机上,这个延迟足以让电流环相位裕度下降十几度。
4.1.2 补偿方法:预测+前移
我的做法分两步:
- 预测当前电流:用上一拍的电流变化率,估算出当前时刻的电流值。
- 将PI输出前移:在计算完电压指令后,立即写入PWM比较寄存器,而不是等到下一个周期开始。
小技巧:如果MCU支持“双缓冲”模式(比如TI的ePWM模块),可以在中断服务程序里直接写影子寄存器,下一个周期自动加载。这样能省掉一个周期的等待。
下面是一个简单的预测补偿代码片段,我用的是最基础的线性预测:
// 电流预测补偿示例
// 输入: i_k (当前采样值), i_k1 (上一拍采样值)
// 输出: i_pred (预测后的电流值)
float current_predict(float i_k, float i_k1) {
float di = i_k - i_k1; // 电流变化率
float i_pred = i_k + di * 0.5f; // 预测半个周期后的电流
return i_pred;
}
// 在PI计算前调用
i_d_pred = current_predict(i_d_k, i_d_k1);
i_q_pred = current_predict(i_q_k, i_q_k1);
// 然后用预测值进行PI计算
pi_output = pi_controller(i_d_ref, i_d_pred);
注意:预测不是万能的。如果电流变化太快(比如电机启动瞬间),预测误差会变大。这种情况下,我建议配合前馈控制一起使用,或者适当降低PI比例增益。
4.2 抗积分饱和:积分器“卡住”了怎么办?
积分饱和,说白了就是积分项越积越大,但输出已经限幅了,积分还在往里加。等你需要它降下来的时候,它得花好几个周期才能“消化”掉。结果就是——超调、振荡,甚至失控。
我记得有一次调试一个风机项目,启动时电流直接冲到了限幅值的1.5倍。查了半天,就是积分饱和搞的鬼。从那以后,我写PI代码必加Anti-Windup。
4.2.1 几种常见的Anti-Windup方法
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 积分冻结 | 输出限幅时,停止积分累加 | 实现简单,计算量小 | 退出饱和时可能不够平滑 |
| 积分回退 | 输出限幅时,将积分项回退到限幅值对应的水平 | 退出快,超调小 | 需要计算回退量,稍复杂 |
| 条件积分 | 只在误差较小时积分,误差大时停止 | 兼顾动态和稳态 | 阈值需要调试 |
| 跟踪Anti-Windup | 用实际输出与PI输出的差值反馈到积分输入端 | 理论最完善,响应平滑 | 需要额外反馈回路 |
我个人最常用的是积分冻结+积分回退的组合。简单说就是:输出限幅时,积分项不再累加,同时把积分项的值限制在一个合理的范围内。
4.2.2 代码实现
// 带Anti-Windup的PI控制器
typedef struct {
float Kp;
float Ki;
float integral;
float integral_max; // 积分上限
float integral_min; // 积分下限
float out_max;
float out_min;
} PI_Controller;
float pi_controller_run(PI_Controller *pi, float ref, float fb) {
float err = ref - fb;
float out_unlimited;
// 比例项
float p_out = pi->Kp * err;
// 积分项(带冻结)
if ((pi->out_max - p_out) > 0.001f || (pi->out_min - p_out) < -0.001f) {
// 输出还有余量,正常积分
pi->integral += pi->Ki * err;
}
// 否则冻结积分,不累加
// 积分限幅
if (pi->integral > pi->integral_max) pi->integral = pi->integral_max;
if (pi->integral < pi->integral_min) pi->integral = pi->integral_min;
// 总输出
out_unlimited = p_out + pi->integral;
// 输出限幅
if (out_unlimited > pi->out_max) {
return pi->out_max;
} else if (out_unlimited < pi->out_min) {
return pi->out_min;
} else {
return out_unlimited;
}
}
经验值:积分限幅通常设为输出限幅的50%~80%。比如输出电压限幅为300V,积分限幅可以设在150V~240V之间。具体值取决于你的系统动态要求。
4.3 电流采样与滤波:别让噪声毁了你的环
电流采样是电流环的“眼睛”。眼睛花了,后面算得再好也没用。实际项目中,采样噪声主要来自:
- PWM开关噪声:MOSFET开关瞬间会产生很大的di/dt,耦合到采样电路。
- ADC量化噪声:12位ADC的理论分辨率有限,低电流时尤其明显。
- 共模干扰:特别是使用单电阻采样时,共模电压变化剧烈。
4.3.1 采样时刻的选择
这个很关键。我建议在PWM周期的中间点采样。为什么?
- PWM开关瞬间噪声最大,中间点离开关时刻最远。
- 对于对称PWM,中间点正好是电流纹波的“平均值”位置。
具体做法:配置ADC触发源为PWM定时器的周期匹配事件,而不是过零事件。这样采样点就落在了PWM周期的正中间。
4.3.2 数字滤波器的选择
硬件滤波(RC低通)是必须的,但截止频率不能太低,否则会引入相位延迟。我一般把RC截止频率设在PWM频率的1/5~1/10。比如20kHz PWM,RC截止频率设在2~4kHz。
硬件滤波之后,再加一级数字滤波。常用的有:
| 滤波器类型 | 特点 | 适用场景 |
|---|---|---|
| 一阶低通 | 实现简单,延迟可控 | 通用场景,我最常用 |
| 滑动平均 | 对周期性噪声抑制好 | PWM开关噪声明显时 |
| 中值滤波 | 对尖峰脉冲抑制好 | 采样偶尔出现异常值时 |
| 卡尔曼滤波 | 理论最优,但计算量大 | 高精度场合,低端MCU慎用 |
我个人的习惯是:一阶低通+滑动平均组合。一阶低通负责滤除高频噪声,滑动平均负责平滑掉PWM周期内的纹波。两个滤波器加起来,延迟大约1~2个采样周期,完全可以接受。
4.3.3 一阶低通滤波器的实现
// 一阶低通滤波器
typedef struct {
float alpha; // 滤波系数,0~1
float y_prev; // 上一拍输出
} LPF_1st;
float lpf_run(LPF_1st *lpf, float x) {
float y = lpf->alpha * x + (1.0f - lpf->alpha) * lpf->y_prev;
lpf->y_prev = y;
return y;
}
// 滤波系数计算
// alpha = Ts / (Ts + Tau)
// 其中 Ts 为采样周期,Tau 为滤波器时间常数
// 例如:Ts=50us (20kHz), 希望截止频率2kHz
// Tau = 1/(2*PI*2000) ≈ 79.6us
// alpha = 50 / (50 + 79.6) ≈ 0.386
调试建议:滤波系数alpha不要设得太小。alpha越小,滤波越强,但延迟也越大。我一般从0.3开始试,如果电流波形还有毛刺,再慢慢减小。如果发现电流环响应变慢,就增大alpha。
4.4 三个问题的协同整定
延迟补偿、抗积分饱和、电流滤波,这三个问题不是孤立的。它们之间会相互影响:
- 滤波太强 → 延迟变大 → 需要更强的延迟补偿
- 延迟补偿不准 → 电流误差变大 → 积分器容易饱和
- 抗积分饱和太激进 → 输出不平滑 → 电流纹波变大
所以我的整定顺序是:
- 先调好采样时刻和滤波参数,保证电流反馈干净、延迟可控。
- 再调延迟补偿,让PI算出来的值能“准时”生效。
- 最后调抗积分饱和参数,确保大动态下不失控。
嗯,这套流程我在好几个项目里验证过,效果都还不错。当然,每个系统的硬件特性不一样,具体参数还得你自己在示波器上慢慢调。但方向对了,剩下的就是耐心了。