3. 电流环软件框架:从采样到SVPWM的完整链路
好,咱们进入正题。电流环的软件框架,说白了就是回答一个问题:“电流怎么采进来,又怎么把电压送出去?” 我刚开始做FOC时,总觉得这框架就是个流水账——ADC采样、坐标变换、PI算完、SVPWM发波,完事。但后来踩过几次坑才发现,每个环节的细节都能让电机转得抖三抖。
这一章,我把整个电流环的软件骨架拆开给你看。你跟着我走一遍,心里就有底了。
3.1 FOC控制流程:五步走完一个周期
FOC的核心思想,就是把三相交流电机的控制,等效成直流电机的控制。怎么等效?靠坐标变换。整个流程我习惯分成五步:
- 采样:采集两相电流(Ia、Ib)和母线电压。
- Clark变换:把三相静止坐标系(abc)变到两相静止坐标系(αβ)。
- Park变换:把αβ坐标系变到旋转坐标系(dq),得到有功电流Id和无功电流Iq。
- PI调节:在dq坐标系下,分别对Id和Iq做闭环控制。
- 逆Park变换 + SVPWM:把PI输出的Vd、Vq变回αβ,再通过SVPWM生成六路PWM波。
嗯,这里要注意:这五步必须在一次中断里完成,不能拆到两个中断里去。我在项目里见过有人把采样放一个中断,PI放另一个,结果电流环延迟直接翻倍,电机高频啸叫。
核心原则:电流环的执行周期必须固定,且与PWM载波周期同步。通常我们让电流环在PWM的“下溢中断”或“峰值中断”里触发。
3.2 Clark/Park变换:数学上绕不开的两道坎
坐标变换是FOC的数学基础。你想想看,三相电流Ia、Ib、Ic加起来等于0,所以实际上只需要采样两相就够了。我习惯采样Ia和Ib,Ic直接算出来:Ic = -Ia - Ib。
3.2.1 Clark变换(abc → αβ)
Clark变换把三相电流映射到两相正交坐标系上。公式很简单:
Iα = Ia
Iβ = (Ia + 2*Ib) / √3
注意,这里用的是等幅值变换。还有一种叫等功率变换,系数不同。我个人习惯用等幅值,因为PI调节器的输出直接对应电压幅值,物理意义更直观。
小技巧:如果你用的MCU没有硬件除法器,可以把1/√3预先算成定点数,用乘法代替除法。我在STM32F103上就这么干,一个周期能省下好几微秒。
3.2.2 Park变换(αβ → dq)
Park变换需要转子角度θ。公式是:
Id = Iα * cosθ + Iβ * sinθ
Iq = -Iα * sinθ + Iβ * cosθ
这里有个坑:θ的零点怎么定义? 我见过有人把霍尔传感器的上升沿当作0度,结果电机正反转时电流环表现不对称。后来我统一把θ定义为转子磁链方向与A相轴线的夹角,这样正反转的PI参数就能共用。
避坑指南:我曾经在一个项目中,因为角度θ的更新频率低于电流环频率,导致Park变换后的Id、Iq出现低频振荡。解决办法是:在每次电流环中断里,用插值法更新θ,而不是等霍尔中断来了再更新。
3.3 PI控制器结构:位置式还是增量式?
电流环的PI控制器,我推荐用位置式PI。为什么?因为电流环要求响应快,位置式PI没有累积误差,而且限幅容易做。
标准的位置式PI公式:
u(k) = Kp * e(k) + Ki * Ts * Σe(i) (i=0 to k)
其中Ts是电流环周期。实际代码里,我习惯把积分项单独累加:
integral += Ki * Ts * error;
output = Kp * error + integral;
// 限幅
if(output > limit_max) output = limit_max;
if(output < limit_min) output = limit_min;
这里有个关键点:积分限幅。如果不做积分限幅,当输出饱和时,积分项会一直累积,导致“积分饱和”现象——指令降下来后,输出要很久才能恢复。我一般把积分项单独限幅在输出限幅值的80%左右。
| 参数 | 作用 | 调参经验 |
|---|---|---|
| Kp(比例系数) | 决定响应速度 | 先调Kp,让电流能跟上指令,但不要有超调 |
| Ki(积分系数) | 消除稳态误差 | Kp调好后,慢慢加Ki,直到稳态误差消失 |
| 积分限幅 | 防止积分饱和 | 设为输出限幅的60%~80% |
我的习惯:先让电机空载运行,给一个阶跃电流指令,用示波器看实际电流的响应波形。Kp太小,电流爬得慢;Kp太大,电流会 overshoot。Ki太大,电流会低频振荡。嗯,调参就是个找平衡的过程。
3.4 SVPWM调制:从电压矢量到占空比
SVPWM(空间矢量脉宽调制)比传统的SPWM电压利用率高15%左右。说白了,就是让三相逆变器的六个开关管,合成一个旋转的电压矢量。
SVPWM的核心步骤:
- 根据Vα、Vβ判断当前电压矢量在哪个扇区。
- 计算两个相邻基本矢量的作用时间T1、T2。
- 计算零矢量的作用时间T0 = Ts - T1 - T2。
- 根据扇区,分配三相占空比(Ta、Tb、Tc)。
代码实现时,我习惯用查表法判断扇区,避免用atan2这种耗时的函数:
// 判断扇区
if(Vβ > 0) {
if(Vα > 0) sector = (Vα > Vβ/√3) ? 1 : 2;
else sector = (-Vα > Vβ/√3) ? 3 : 4;
} else {
if(Vα < 0) sector = (-Vα > -Vβ/√3) ? 5 : 6;
else sector = (Vα > -Vβ/√3) ? 1 : 6;
}
小技巧:SVPWM的占空比计算中,可以用“七段式”或“五段式”发波。七段式谐波小,但开关损耗大;五段式开关损耗小,但谐波大。我一般用七段式,因为电流环对谐波敏感。
3.5 中断服务程序架构:别让中断打架
电流环的中断服务程序(ISR)是整个软件的“心脏”。架构设计不好,轻则电流抖动,重则电机失控。
我常用的架构是两级中断:
- 高优先级中断:PWM下溢中断,触发电流环计算(采样→变换→PI→SVPWM)。这个中断必须最快响应,不能被打断。
- 低优先级中断:霍尔传感器捕获中断,更新转子角度θ。这个中断可以稍微慢一点,但不能丢脉冲。
ISR的伪代码框架:
void PWM_Underflow_ISR(void) {
// 1. 读取ADC结果(Ia, Ib, Vbus)
// 2. Clark变换:Iα, Iβ
// 3. Park变换:Id, Iq(需要最新的θ)
// 4. PI调节:Vd, Vq
// 5. 逆Park变换:Vα, Vβ
// 6. SVPWM:计算占空比,写入定时器
// 7. 清除中断标志
}
void Hall_Capture_ISR(void) {
// 1. 读取霍尔状态
// 2. 更新转子角度θ(查表或插值)
// 3. 清除中断标志
}
避坑指南:我曾经在一个项目中,霍尔中断里做了太多计算(比如滤波、插值),导致PWM中断被延迟。结果电流环周期抖动,电机发出“嗡嗡”声。后来我把霍尔中断里的计算量降到最低,只更新角度,其他计算放到主循环里做。