3. 电流环软件框架:从采样到SVPWM的完整链路

好,咱们进入正题。电流环的软件框架,说白了就是回答一个问题:“电流怎么采进来,又怎么把电压送出去?” 我刚开始做FOC时,总觉得这框架就是个流水账——ADC采样、坐标变换、PI算完、SVPWM发波,完事。但后来踩过几次坑才发现,每个环节的细节都能让电机转得抖三抖。

这一章,我把整个电流环的软件骨架拆开给你看。你跟着我走一遍,心里就有底了。

3.1 FOC控制流程:五步走完一个周期

FOC的核心思想,就是把三相交流电机的控制,等效成直流电机的控制。怎么等效?靠坐标变换。整个流程我习惯分成五步:

  1. 采样:采集两相电流(Ia、Ib)和母线电压。
  2. Clark变换:把三相静止坐标系(abc)变到两相静止坐标系(αβ)。
  3. Park变换:把αβ坐标系变到旋转坐标系(dq),得到有功电流Id和无功电流Iq。
  4. PI调节:在dq坐标系下,分别对Id和Iq做闭环控制。
  5. 逆Park变换 + SVPWM:把PI输出的Vd、Vq变回αβ,再通过SVPWM生成六路PWM波。

嗯,这里要注意:这五步必须在一次中断里完成,不能拆到两个中断里去。我在项目里见过有人把采样放一个中断,PI放另一个,结果电流环延迟直接翻倍,电机高频啸叫。

核心原则:电流环的执行周期必须固定,且与PWM载波周期同步。通常我们让电流环在PWM的“下溢中断”或“峰值中断”里触发。

FOC电流环五步流程 ① 电流采样 ② Clark变换 ③ Park变换 ④ PI调节 ⑤ SVPWM 关键说明: • 步骤①~⑤必须在同一个中断服务程序中完成,保证延迟最小。 • 步骤③需要用到转子角度θ,通常由霍尔传感器或观测器提供。 • 步骤⑤的输出是六路PWM占空比,直接写入定时器比较寄存器。 • 整个流程的执行时间一般控制在10~50μs以内(取决于主频和优化程度)。

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的核心步骤:

  1. 根据Vα、Vβ判断当前电压矢量在哪个扇区。
  2. 计算两个相邻基本矢量的作用时间T1、T2。
  3. 计算零矢量的作用时间T0 = Ts - T1 - T2。
  4. 根据扇区,分配三相占空比(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中断被延迟。结果电流环周期抖动,电机发出“嗡嗡”声。后来我把霍尔中断里的计算量降到最低,只更新角度,其他计算放到主循环里做。