3、扇区判断:如何快速判断参考电压矢量所在的扇区

做 SVPWM 的人,第一个绕不开的坎就是扇区判断。

你想想看,六个基本电压矢量把空间分成了六个扇区,参考电压矢量 Vref 到底落在哪个扇区里?这个判断要是慢了,整个控制周期都得往后拖。我早年刚接触这个算法时,第一反应是用角度查表——先算角度,再查扇区。结果一跑起来,发现角度计算占了一大半的算力,心里那个悔啊。

后来我才明白,真正工程上用的方法,根本不需要算角度。

3.1 为什么不用角度查表?

很多人第一反应是:

  1. 先算 θ = arctan(Vβ / Vα)
  2. 再根据 θ 的范围判断扇区

这个思路没错,但效率太低。

arctan 计算在单片机里非常耗时,尤其是低端 MCU 没有 FPU 的时候,一个 arctan 能吃掉几十微秒。你想想看,一个 PWM 周期才 100μs,光算个角度就占了一半,剩下的时间还做不做电流环、速度环了?

我在一个风机项目里就吃过这个亏。当时用的 STM32F103,算一次 arctan 花了 40μs,整个控制周期直接超时。后来改成下面要讲的扇区直接判断法,判断时间降到了 2μs 以内。嗯,差距就是这么大。

3.2 核心思路:用反电动势符号判断

说白了,扇区判断的本质就是看 Vα 和 Vβ 的组合关系。

我们把三相电压 Va、Vb、Vc 通过 Clarke 变换得到 Vα 和 Vβ。然后定义三个中间变量:

Vref1 = Vβ
Vref2 = (√3 / 2) * Vα - (1 / 2) * Vβ
Vref3 = -(√3 / 2) * Vα - (1 / 2) * Vβ

这三个变量的正负号,直接决定了扇区编号。

具体规则是这样的:

Vref1 符号 Vref2 符号 Vref3 符号 扇区编号
+ + + 扇区 I
+ + - 扇区 II
+ - - 扇区 III
- - - 扇区 IV
- - + 扇区 V
- + + 扇区 VI

你看,只需要判断三个数的正负号,连乘法都不用做几次。这就是工程上的最优解。

核心结论:扇区判断 = 三个中间变量的符号组合。不需要角度,不需要查表,只需要比较和位运算。

3.3 工程实现:C 代码怎么写?

我习惯用位运算来编码扇区号。具体做法是:

  1. 定义三个标志位:A、B、C
  2. 如果 Vref1 > 0,则 A = 1,否则 A = 0
  3. 如果 Vref2 > 0,则 B = 1,否则 B = 0
  4. 如果 Vref3 > 0,则 C = 1,否则 C = 0
  5. 扇区号 = A + 2*B + 4*C

代码实现如下:

// 输入:V_alpha, V_beta(经过 Clarke 变换后的值)
// 输出:扇区编号(1~6)

int svpwm_sector_calc(float V_alpha, float V_beta)
{
    float V_ref1 = V_beta;
    float V_ref2 = (SQRT3_2 * V_alpha) - (0.5f * V_beta);
    float V_ref3 = (-SQRT3_2 * V_alpha) - (0.5f * V_beta);

    int A = (V_ref1 > 0) ? 1 : 0;
    int B = (V_ref2 > 0) ? 1 : 0;
    int C = (V_ref3 > 0) ? 1 : 0;

    int sector = A + (B << 1) + (C << 2);  // 等价于 A + 2*B + 4*C

    // 注意:这里得到的 sector 是 0~7,需要映射到 1~6
    // 常见的映射表如下:
    static const int sector_map[8] = {0, 1, 5, 0, 3, 2, 4, 0};
    return sector_map[sector];
}

个人经验:SQRT3_2 建议定义为 0.86602540378f,不要用 sqrt(3)/2 在运行时计算。我见过有人直接在代码里写 sqrt(3)/2,每次调用都算一遍,白白浪费 CPU 时间。

3.4 避坑指南:我踩过的三个坑

坑一:浮点数比较的零值问题

我曾经在一个项目中,Vref1 刚好等于 0.0。按照理论,应该归到正号那一侧。但浮点数比较时,0.0 > 0 是 false,结果扇区判断错了,电机在过零附近抖得厉害。

解决办法:加一个很小的阈值,比如 1e-6。

#define EPSILON 1e-6f
int A = (V_ref1 > EPSILON) ? 1 : 0;

坑二:扇区映射表写错

上面代码里的 sector_map 数组,我一开始写反过。结果电机转起来,电流波形全是毛刺。后来对着扇区图一个一个对,才发现映射关系搞反了。

建议:写完后用几个已知角度验证一下。比如 Vα=1, Vβ=0,应该落在扇区 I。

坑三:中间变量计算溢出

在定点 MCU 上做 SVPWM 时,Vα 和 Vβ 是 Q15 格式的整数。计算 Vref2 时,(√3/2) * Vα 可能超出 16 位范围。我当时没注意,结果扇区判断在高调制比下频繁出错。

解决办法:用 32 位中间变量,或者先缩放到 Q12 格式再算。

3.5 扇区判断的流程图

下面这张图把整个判断流程串起来了,你可以对照着看:

SVPWM 扇区判断流程图 扇区判断流程图 输入 Vα, Vβ 计算三个中间变量 Vref1=Vβ Vref2=√3/2·Vα−½·Vβ Vref3=−√3/2·Vα−½·Vβ 判断 Vref1, Vref2, Vref3 符号 A = (Vref1 > 0) B = (Vref2 > 0) C = (Vref3 > 0) 位运算编码:sector = A + 2B + 4C 查表映射:sector_map[8] → 扇区 1~6 输出扇区编号(1~6) 整个流程只需要比较和位运算,没有三角函数,没有除法,适合在 MCU 上实时运行

3.6 性能对比:角度法 vs 直接判断法

我拿一个实际项目的数据给你看:

方法 计算步骤 耗时(STM32F103, 72MHz) 适用场景
角度查表法 arctan → 角度 → 查表 约 35~45 μs 对算力不敏感的平台
直接判断法 3 次乘加 → 3 次比较 → 位运算 约 1.5~2.5 μs 所有嵌入式平台

你看,差了将近 20 倍。在电机控制这种实时性要求极高的场景里,这个差距就是能不能跑起来的区别。

注意:直接判断法虽然快,但有一个前提——你的 Vα 和 Vβ 必须是正确的。如果 Clarke 变换算错了,扇区判断再快也没用。我见过有人把 Clarke 变换的系数搞反了,结果扇区判断永远对不上,电机根本转不起来。

3.7 小结

扇区判断这件事,说白了就是三个中间变量的符号组合。不需要角度,不需要查表,只需要三次比较和一次位运算。这是 SVPWM 里最基础也最关键的优化点。

我个人建议:

  • 新手先把原理搞懂,对着扇区图把映射关系理清楚
  • 写代码时注意浮点数零值问题,加个阈值
  • 定点平台上注意中间变量的位宽,别溢出
  • 写完一定要用几个已知角度验证一下

嗯,扇区判断就讲到这里。这个环节虽然简单,但它是整个 SVPWM 算法的入口。入口走对了,后面的路就好走了。