3、扇区判断:如何快速判断参考电压矢量所在的扇区
做 SVPWM 的人,第一个绕不开的坎就是扇区判断。
你想想看,六个基本电压矢量把空间分成了六个扇区,参考电压矢量 Vref 到底落在哪个扇区里?这个判断要是慢了,整个控制周期都得往后拖。我早年刚接触这个算法时,第一反应是用角度查表——先算角度,再查扇区。结果一跑起来,发现角度计算占了一大半的算力,心里那个悔啊。
后来我才明白,真正工程上用的方法,根本不需要算角度。
3.1 为什么不用角度查表?
很多人第一反应是:
- 先算 θ = arctan(Vβ / Vα)
- 再根据 θ 的范围判断扇区
这个思路没错,但效率太低。
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 代码怎么写?
我习惯用位运算来编码扇区号。具体做法是:
- 定义三个标志位:A、B、C
- 如果 Vref1 > 0,则 A = 1,否则 A = 0
- 如果 Vref2 > 0,则 B = 1,否则 B = 0
- 如果 Vref3 > 0,则 C = 1,否则 C = 0
- 扇区号 = 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 扇区判断的流程图
下面这张图把整个判断流程串起来了,你可以对照着看:
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 算法的入口。入口走对了,后面的路就好走了。