4. SVPWM原理:空间矢量调制、扇区判断、作用时间计算、七段式SVPWM

好,咱们今天聊SVPWM。说实话,这是FOC算法里最“硬核”的一块,也是我当年啃得最久的部分。很多初学者一上来就被那些矢量图、扇区判断、时间计算给绕晕了。别急,我带你一步步拆开看。

SVPWM,全称是Space Vector Pulse Width Modulation,空间矢量脉宽调制。说白了,它干的事就是:用六个固定的开关状态,去合成任意方向、任意大小的电压矢量。你想想看,电机想要转得顺滑,电压就得连续可调。但逆变器只有六个开关管,要么开要么关,怎么搞出连续电压?SVPWM就是答案。

4.1 空间矢量调制的基本思想

先看一个三相逆变器。六个开关管,上桥臂三个,下桥臂三个。同一相上下不能同时导通,所以每一相只有两种状态:上管导通(记为1)或下管导通(记为0)。

三相组合起来,一共2³ = 8种状态。其中6种是有效矢量,2种是零矢量(000和111)。这6个有效矢量,在空间上互差60度,形成一个正六边形。我习惯把它们画在复平面上,这样看起来更直观。

核心思想:任意一个目标电压矢量Vref,都可以用相邻的两个有效矢量加上零矢量来合成。通过控制每个矢量的作用时间,就能让平均值等于Vref。

我在项目中遇到过一个问题:一开始用SPWM(正弦脉宽调制),电机低速时还行,一跑高速就抖得厉害。换成SVPWM后,电压利用率高了15%,噪音也小了很多。嗯,这就是SVPWM的优势——直流母线电压利用率比SPWM高,能达到1.0(SPWM只有0.866)。

4.2 扇区判断

要合成Vref,第一步就是判断它在哪个扇区。六个扇区,每个60度,编号从I到VI。

怎么判断?我教你一个土办法。把Vref分解到αβ坐标系下,得到Vα和Vβ。然后算三个中间变量:

B1 = Vβ
B2 = (√3/2)*Vα - (1/2)*Vβ
B3 = -(√3/2)*Vα - (1/2)*Vβ

然后根据B1、B2、B3的符号,查表就能得到扇区号。具体规则是这样的:

B1符号 B2符号 B3符号 扇区
+ + - I
+ - - II
+ - + III
- - + IV
- + + V
- + - VI

其实还有一种更简洁的方法:直接算扇区号N = sign(B1) + 2*sign(B2) + 4*sign(B3),然后查表。我建议你用这种,代码写起来清爽很多。

我的小技巧:在DSP或者MCU里,用查表法比实时计算快得多。我一般把扇区判断做成一个256字节的查找表,一次查表搞定,省掉一堆条件判断。

4.3 作用时间计算

知道扇区后,就该算两个相邻矢量的作用时间了。假设Vref在扇区I,相邻矢量是V1(100)和V2(110)。

根据伏秒平衡原理:

T1 = Ts * Vref * sin(60° - θ) / (Vdc * sin(60°))
T2 = Ts * Vref * sin(θ) / (Vdc * sin(60°))
T0 = Ts - T1 - T2

其中Ts是PWM周期,Vdc是直流母线电压,θ是Vref在扇区内的角度。

实际工程中,没人这么算。太慢了。我一般用简化公式:

// 先算三个通用变量
X = Vβ * Ts / Vdc
Y = (√3/2 * Vα + 1/2 * Vβ) * Ts / Vdc
Z = (-√3/2 * Vα + 1/2 * Vβ) * Ts / Vdc

// 然后根据扇区查表
扇区I:  T1 = -Z,  T2 = X
扇区II: T1 = Z,   T2 = Y
扇区III: T1 = X,  T2 = -Y
扇区IV: T1 = -X,  T2 = Z
扇区V:  T1 = -Y,  T2 = -Z
扇区VI: T1 = Y,   T2 = -X

你看,这样只需要算三个中间变量,然后查表赋值,效率高多了。

注意:算出来的T1+T2有可能超过Ts。这叫过调制。我刚开始做的时候没处理这个,结果电机在高速时电流波形畸变得厉害。后来加了限幅处理:如果T1+T2 > Ts,就按比例缩小T1和T2,让T0=0。

4.4 七段式SVPWM

时间算好了,怎么安排开关顺序?这就是七段式SVPWM的活了。

七段式,顾名思义,一个PWM周期分成7段。为什么要7段?因为要保证每次只切换一个开关管,减少开关损耗和电磁干扰。

以扇区I为例,矢量顺序是:000 → 100 → 110 → 111 → 110 → 100 → 000

你看,从000到100,只切换了A相上管;从100到110,只切换了B相上管。每次只动一个管子,这就是七段式的精髓。

具体到三相的比较值计算,我一般这样写:

// 先算三个比较值
Ta = (Ts - T1 - T2) / 4
Tb = Ta + T1/2
Tc = Tb + T2/2

// 然后根据扇区,分配给三相
switch(扇区) {
    case I:  CMPA = Tb, CMPB = Ta, CMPC = Tc; break;
    case II: CMPA = Ta, CMPB = Tc, CMPC = Tb; break;
    // ... 其他扇区类似
}

我曾经在调试一个200kW的伺服驱动器时,发现七段式在低频时电流纹波有点大。后来改成了五段式(只插入一个零矢量),纹波小了,但谐波多了点。嗯,这就是工程上的取舍——没有完美的方案,只有最适合的方案。

4.5 代码实现要点

最后,给你一个完整的SVPWM计算流程,我习惯把它封装成一个函数:

void svpwm_calc(float Valpha, float Vbeta, float Vdc, float Ts,
                float *Ta, float *Tb, float *Tc) {
    // 1. 扇区判断
    float B1 = Vbeta;
    float B2 = 0.866f * Valpha - 0.5f * Vbeta;
    float B3 = -0.866f * Valpha - 0.5f * Vbeta;
    int sector = 0;
    if(B1 > 0) sector |= 1;
    if(B2 > 0) sector |= 2;
    if(B3 > 0) sector |= 4;
    // 查表得到实际扇区号
    static const int sector_table[8] = {0,1,5,0,3,2,4,0};
    sector = sector_table[sector];

    // 2. 计算X,Y,Z
    float X = Vbeta * Ts / Vdc;
    float Y = (0.866f * Valpha + 0.5f * Vbeta) * Ts / Vdc;
    float Z = (-0.866f * Valpha + 0.5f * Vbeta) * Ts / Vdc;

    // 3. 查表得到T1,T2
    float T1, T2;
    // ... 根据sector查表赋值 ...

    // 4. 过调制处理
    if(T1 + T2 > Ts) {
        float scale = Ts / (T1 + T2);
        T1 *= scale;
        T2 *= scale;
    }

    // 5. 计算三相比较值
    float Ta = (Ts - T1 - T2) * 0.25f;
    float Tb = Ta + T1 * 0.5f;
    float Tc = Tb + T2 * 0.5f;

    // 6. 根据扇区分配
    // ... 查表赋值给*Ta, *Tb, *Tc ...
}

这个函数我用了好多年,从STM32到TMS320F28335,稍微改改就能移植。你拿去用的时候,注意一下浮点运算的精度,特别是Vdc和Ts的取值,别让中间变量溢出。

好了,SVPWM的核心内容就这些。说白了就是三步:判断扇区、算时间、排顺序。你动手写一遍代码,跑个仿真看看波形,比看十遍书都管用。下一章咱们聊电流采样和坐标变换,那是FOC的另一个关键环节。