4. 扇区判断与矢量合成:基于参考电压矢量在αβ坐标系的分量,判断所在扇区并计算相邻矢量作用时间

好,我们继续往下走。前面我们把 Clark 变换讲清楚了,也知道了怎么把三相电压映射到 αβ 坐标系。那接下来要解决一个很实际的问题:这个旋转的参考电压矢量,到底落在哪个扇区?

你想想看,SVPWM 的核心就是「用相邻的两个基本矢量去合成目标矢量」。如果你连它在哪个扇区都不知道,那该用哪两个矢量去合成?所以扇区判断是第一步,也是后面所有计算的基础。

核心思路: 通过 Uα 和 Uβ 的符号与比例关系,确定参考矢量所在的 60° 扇区。一共 6 个扇区,每个扇区对应一组相邻的基本矢量。

4.1 扇区判断的数学原理

我个人习惯用「三值逻辑」来做扇区判断。说白了,就是定义三个中间变量 A、B、C,每个变量只有 0 或 1 两种取值。然后通过一个简单的公式算出扇区号。

具体怎么做?我们先定义三个参考量:

  • Uref1 = Uβ
  • Uref2 = (√3/2)·Uα - (1/2)·Uβ
  • Uref3 = -(√3/2)·Uα - (1/2)·Uβ

然后根据这三个量的正负,给 A、B、C 赋值:

条件 A / B / C
Uref1 > 0 A = 1,否则 A = 0
Uref2 > 0 B = 1,否则 B = 0
Uref3 > 0 C = 1,否则 C = 0

最后,扇区号 N 的计算公式是:

N = A + 2·B + 4·C

嗯,这里要注意:算出来的 N 是 1~6 的整数,正好对应六个扇区。我在项目中第一次看到这个公式时觉得挺巧妙的——只用三次比较和一次加法,就搞定了扇区判断,效率很高。

我的经验: 实际写代码时,Uref2 和 Uref3 里的 √3/2 可以预先算好存成常量,避免每次计算都做开方。我一般用 0.8660254 这个近似值,精度完全够用。

4.2 扇区与基本矢量的对应关系

知道了扇区号 N,接下来就要确定该用哪两个基本矢量。每个扇区对应的相邻矢量是固定的,我整理了一张表:

扇区号 N 相邻基本矢量 对应开关状态
1 U0 (000) 和 U60 (100) 000 → 100
2 U60 (100) 和 U120 (110) 100 → 110
3 U120 (110) 和 U180 (010) 110 → 010
4 U180 (010) 和 U240 (011) 010 → 011
5 U240 (011) 和 U300 (001) 011 → 001
6 U300 (001) 和 U0 (000) 001 → 000

这张表你最好记熟,写代码时直接查表就行,不用每次都重新推导。

4.3 矢量作用时间的计算

扇区判断完了,接下来就是算时间。每个扇区里,两个相邻矢量的作用时间 T1 和 T2 可以通过 Uα 和 Uβ 算出来。

我直接给通用公式,你拿去用就行:

// 定义三个通用变量 X、Y、Z
X = (√3 · Uβ · Ts) / Udc
Y = ( (3/2)·Uα + (√3/2)·Uβ ) · Ts / Udc
Z = ( -(3/2)·Uα + (√3/2)·Uβ ) · Ts / Udc

// 根据扇区号 N 查表得到 T1 和 T2
switch(N) {
    case 1: T1 = Z;  T2 = Y;  break;
    case 2: T1 = Y;  T2 = -X; break;
    case 3: T1 = -Z; T2 = X;  break;
    case 4: T1 = -X; T2 = Z;  break;
    case 5: T1 = X;  T2 = -Y; break;
    case 6: T1 = -Y; T2 = -Z; break;
}

这里 Ts 是 PWM 周期,Udc 是直流母线电压。算出来的 T1 和 T2 就是两个基本矢量的导通时间。

我曾经踩过的坑: 算出来的 T1 + T2 可能会超过 Ts。这时候必须做饱和处理——把 T1 和 T2 按比例缩小,保证 T1 + T2 ≤ Ts。剩下的时间分配给零矢量(U0 或 U7)。如果不做这个处理,电机在过调制区会出问题,电流波形会畸变。

4.4 扇区判断与时间计算的流程图

为了让你看得更清楚,我画了一张流程图,把整个判断和计算过程串起来:

扇区判断与矢量作用时间计算流程 输入 Uα, Uβ 计算三个参考量 Uref1=Uβ, Uref2=√3/2·Uα - 1/2·Uβ, Uref3=-√3/2·Uα - 1/2·Uβ 根据正负赋值 A, B, C A=Uref1>0?1:0, B=Uref2>0?1:0, C=Uref3>0?1:0 N = A + 2·B + 4·C 计算通用变量 X, Y, Z X=√3·Uβ·Ts/Udc, Y=(1.5Uα+0.866Uβ)·Ts/Udc, Z=(-1.5Uα+0.866Uβ)·Ts/Udc 根据 N 查表得到 T1, T2 switch(N) { case 1: T1=Z; T2=Y; ... } 输出 T1, T2 ⚠ 注意 T1 + T2 可能超过 Ts 需做饱和处理 剩余时间给零矢量

4.5 实际代码实现示例

纸上谈兵没意思,我给你看一段我实际用过的 C 代码。这段代码在 STM32 上跑过,效果不错:

// 扇区判断与时间计算函数
void svpwm_calc(float Ualpha, float Ubeta, float Ts, float Udc, 
                float *T1, float *T2, int *sector) {
    float Uref1, Uref2, Uref3;
    float X, Y, Z;
    int A, B, C, N;
    
    // 1. 计算三个参考量
    Uref1 = Ubeta;
    Uref2 = 0.8660254f * Ualpha - 0.5f * Ubeta;
    Uref3 = -0.8660254f * Ualpha - 0.5f * Ubeta;
    
    // 2. 赋值 A, B, C
    A = (Uref1 > 0) ? 1 : 0;
    B = (Uref2 > 0) ? 1 : 0;
    C = (Uref3 > 0) ? 1 : 0;
    
    // 3. 计算扇区号
    N = A + 2 * B + 4 * C;
    *sector = N;
    
    // 4. 计算 X, Y, Z
    X = (1.7320508f * Ubeta * Ts) / Udc;
    Y = ((1.5f * Ualpha + 0.8660254f * Ubeta) * Ts) / Udc;
    Z = ((-1.5f * Ualpha + 0.8660254f * Ubeta) * Ts) / Udc;
    
    // 5. 查表得到 T1, T2
    switch(N) {
        case 1: *T1 = Z;  *T2 = Y;  break;
        case 2: *T1 = Y;  *T2 = -X; break;
        case 3: *T1 = -Z; *T2 = X;  break;
        case 4: *T1 = -X; *T2 = Z;  break;
        case 5: *T1 = X;  *T2 = -Y; break;
        case 6: *T1 = -Y; *T2 = -Z; break;
        default: *T1 = 0; *T2 = 0; break;
    }
    
    // 6. 饱和处理
    float sum = *T1 + *T2;
    if (sum > Ts) {
        *T1 = (*T1 / sum) * Ts;
        *T2 = (*T2 / sum) * Ts;
    }
}

我的一点建议: 实际工程中,Udc 可能会有波动。如果你发现电机在母线电压变化时性能不稳定,可以考虑把 Udc 也作为实时输入,而不是固定常量。我在一个变频器项目里就是这么做的,效果立竿见影。

4.6 常见问题与避坑

最后,我总结几个实际中容易出问题的地方:

  • 浮点精度问题: 嵌入式芯片的浮点运算能力有限,如果算出来的 T1 或 T2 是负数,说明扇区判断有误,或者参考电压已经超出了线性调制区。这时候要检查 Uα 和 Uβ 的幅值是否合理。
  • 零矢量分配: T1 和 T2 确定后,剩下的时间 T0 = Ts - T1 - T2 要分给两个零矢量 U0(000) 和 U7(111)。我习惯各分一半,这样 PWM 波形对称,谐波小一些。
  • 死区时间补偿: 算出来的 T1 和 T2 是理想值,实际还要考虑死区时间。如果死区时间占比较大(比如 PWM 频率很高时),建议做死区补偿,否则电流波形会失真。