电流环控制(上):FOC的“心脏”与“脉搏”
各位同学,欢迎来到第二章。上一章我们聊了FOC的整体框架,知道了三环控制是个什么结构。今天,咱们直接切入核心——电流环。
为什么说电流环是核心?你想想看,位置环和速度环最终都要靠电流环去执行。电流环响应慢了,上面两环再精准也没用。我个人的经验是,一个FOC系统调得好不好,八成看电流环。它就像人的心脏,负责把“能量”精准地泵到电机里。
电流环在FOC中的角色:承上启下
电流环的任务说起来很简单:让电机绕组里的实际电流,快速、准确地跟随我们给定的目标电流。
在FOC的坐标系里,我们控制的是Id和Iq。Id负责励磁,Iq负责转矩。电流环就是要让这两个值听话。位置环说“我要转到这里”,速度环算出“需要这么多扭矩”,然后转换成Iq_ref。电流环就得立刻让实际的Iq追上这个目标。
核心要点:电流环的带宽决定了整个系统的响应速度。带宽越高,系统越“跟手”。但带宽受限于PWM频率和采样频率,不是想多高就多高。
PI控制器原理:简单但够用
电流环最常用的控制器就是PI。为什么不用PID?因为微分项对噪声太敏感,在电流环这种高频场景下,搞不好会适得其反。我早期一个项目就吃过这个亏,加了D项,电流波形反而毛刺更多。
PI控制器的公式,大家应该都见过:
u(t) = Kp * e(t) + Ki * ∫e(t)dt
翻译成人话:
- 比例项(Kp):看现在。误差大了,就使劲推一把。但光有Kp,系统会有静差,永远追不上目标。
- 积分项(Ki):看过去。把过去的误差累积起来,慢慢消除静差。但积分太强,容易超调,甚至震荡。
调PI就像调一杯鸡尾酒。Kp是基酒,决定了力度;Ki是甜味剂,决定了细腻度。我习惯先调Kp,让系统响应够快但不震荡,再加Ki消除稳态误差。
我的调参小技巧:先给一个很小的Ki(比如0),然后增大Kp直到电流波形出现轻微震荡,再把Kp往回退10%-20%。最后加Ki,直到稳态误差消失。这样调出来的参数,通常比较稳健。
电流采样方法:单电阻/双电阻/三电阻
电流环要工作,首先得知道电流是多少。这就涉及到采样。FOC里常用的采样方法有三种,各有优劣。
| 采样方式 | 硬件成本 | 采样精度 | 重构难度 | 适用场景 |
|---|---|---|---|---|
| 三电阻 | 高(3个采样电阻+3路运放) | 最高 | 低(直接读出两相电流) | 高性能伺服、高端机器人 |
| 双电阻 | 中(2个采样电阻+2路运放) | 高 | 中(需重构第三相) | 通用伺服、工业驱动 |
| 单电阻 | 低(1个采样电阻+1路运放) | 较低 | 高(需复杂重构算法) | 低成本家电、电动工具 |
三电阻最直接,每个下桥臂串联一个采样电阻,同时采样两相就能算出第三相。双电阻次之,只采两相,第三相通过Iu + Iv + Iw = 0算出来。单电阻最省,只采母线电流,但需要根据开关状态重构三相电流。
注意:单电阻采样在PWM占空比接近0%或100%时,采样窗口会非常窄,甚至无法采样。这个区域叫“不可观测区”,需要特殊处理,比如移相或强制插入无效矢量。
电流重构技术:从“残缺”到“完整”
双电阻和单电阻采样,都需要重构。双电阻重构很简单,就是算一下。单电阻重构就有点意思了。
单电阻采样的原理是:在PWM周期的不同时刻,母线电流反映的是不同相的电流。比如,当U相上桥臂导通时,母线电流等于U相电流。通过在一个PWM周期内采样两次,就能得到两相电流,再算出第三相。
但这里有个坑。我当年做吸尘器项目时,就遇到了单电阻重构在高速下失败的问题。原因是PWM占空比变化太快,采样点没对准。后来我加了一个采样点自适应调整的逻辑,才搞定。
重构的关键步骤:
- 确定采样时刻:根据PWM的开关状态,找到能反映特定相电流的时间窗口。
- 执行采样:在合适的时刻触发ADC。
- 计算第三相:利用基尔霍夫电流定律。
- 补偿误差:考虑死区时间和开关噪声,做滤波或修正。
避坑指南:我曾经在单电阻重构时,忽略了ADC采样保持时间。结果采到的值总是偏小,导致电流环震荡。后来把采样点往后挪了1us,问题就解决了。记住,硬件有延迟,软件要补偿。
电流环的离散化实现:从连续到数字
PI控制器是连续的,但我们的MCU是数字的。所以需要把连续域的东西,搬到离散域来。最常用的方法是后向差分法。
连续PI的传递函数:
G(s) = Kp + Ki / s
用后向差分法离散化(s = (1 - z^-1) / Ts):
u(k) = u(k-1) + Kp * [e(k) - e(k-1)] + Ki * Ts * e(k)
其中Ts是采样周期,通常等于PWM周期。写成代码就是:
// 电流环PI控制器(增量式)
float current_pi_ctrl(PI_Controller *pi, float target, float actual) {
float error = target - actual;
float output = pi->output_last +
pi->Kp * (error - pi->error_last) +
pi->Ki * pi->Ts * error;
// 限幅(防积分饱和)
if (output > pi->max_output) {
output = pi->max_output;
} else if (output < pi->min_output) {
output = pi->min_output;
}
pi->error_last = error;
pi->output_last = output;
return output;
}
这段代码里,我用了增量式PI。为什么不用位置式?因为增量式只输出变化量,不容易积分饱和。而且,在启动或切换时,增量式更平滑。
关于积分饱和:当输出被限幅时,积分项还在累积,导致输出恢复慢。我习惯在限幅时,同时冻结积分项,或者把积分值也限幅。这叫“抗积分饱和”,是工程上必须处理的问题。
本章知识体系
下面这张图,帮你理清本章的核心脉络: