2. 扇区判断:如何根据Uα、Uβ快速定位目标电压矢量所在的扇区
好,我们直接进入正题。上一章聊了SVPWM的基本原理,说白了就是怎么用六个开关管拼出任意方向的电压矢量。但有个关键问题摆在你面前:你手头只有Uα和Uβ,怎么知道目标矢量落在哪个扇区?
这步要是搞错了,后面的扇区作用时间、切换点全都会乱套。我在刚接触伺服驱动时,就因为在扇区判断上写了个bug,电机转起来嗡嗡响,电流波形跟心电图似的。后来花了整整一个下午才定位到问题——扇区编号算反了。
今天我们就彻底把这块啃干净。
2.1 扇区划分的几何直觉
先看一张图。三相逆变器的六个非零电压矢量,把平面分成六个60°的扇区,编号从I到VI。
核心规律:每个扇区对应一组特定的开关状态组合。扇区判断的本质,就是根据Uα、Uβ的符号和比例关系,确定目标矢量落在哪个60°区间里。
你看,每个扇区边界就是相邻两个基本矢量的角平分线。比如扇区I的边界是0°和60°,扇区II是60°和120°,以此类推。判断扇区,其实就是判断目标矢量的角度落在哪个区间里。
2.2 传统方法:反正切查表法
最直接的想法——算角度。用Uβ除以Uα求反正切,得到角度θ,然后看θ落在哪个区间。
// 反正切查表法(不推荐用于实时控制)
theta = atan2(Ubeta, Ualpha); // 返回弧度值
if (theta < 0) theta += 2*PI;
if (theta < PI/3) sector = 1;
else if (theta < 2*PI/3) sector = 2;
else if (theta < PI) sector = 3;
else if (theta < 4*PI/3) sector = 4;
else if (theta < 5*PI/3) sector = 5;
else sector = 6;
千万别这么用!反正切运算在DSP或MCU上非常耗时,一个atan2可能要几十甚至上百个时钟周期。伺服控制周期通常只有50~100μs,你算个角度就占掉一小半,剩下的时间还够算电流环、速度环吗?
我在早期一个项目中就踩过这个坑。当时用的是一颗200MHz的DSP,atan2一次大概要1.2μs。算上其他开销,整个PWM刷新周期差点超时。后来改成查表法,但查表也有精度和内存的问题。直到我学会了下面这个方法——
2.3 工程实践:三变量符号判断法
这才是真正实用的方法。不需要算角度,不需要查表,只需要三个中间变量和一组简单的逻辑判断。
先定义三个辅助变量:
// 根据Uα、Uβ计算三个中间变量
U1 = Ubeta;
U2 = (sqrt(3)/2) * Ualpha - 0.5 * Ubeta;
U3 = -(sqrt(3)/2) * Ualpha - 0.5 * Ubeta;
然后看这三个变量的符号:
// 符号判断
A = (U1 > 0) ? 1 : 0;
B = (U2 > 0) ? 1 : 0;
C = (U3 > 0) ? 1 : 0;
// 合成扇区编号
sector = A + 2*B + 4*C;
嗯,这里要注意。上面算出来的sector是4、2、1的加权和,范围是0~7。但实际有效的扇区编号是1~6,需要做个映射:
// 扇区映射表
const int sector_map[8] = {0, 1, 5, 0, 3, 2, 4, 0};
sector = sector_map[sector];
我的习惯:把映射表写成const数组放在Flash里,查表比switch-case快。而且这个表只有8个元素,几乎不占空间。
为什么能这样算?说白了,U1、U2、U3分别对应三个参考轴上的投影。每个轴的符号组合唯一对应一个扇区。你想想看,六个扇区正好对应八种符号组合中的六种(全正和全负不会出现)。
2.4 方法对比
| 方法 | 计算量 | 精度 | 适用场景 |
|---|---|---|---|
| 反正切查表法 | 高(含除法+查表) | 高 | 离线分析、调试阶段 |
| 三变量符号法 | 低(仅乘加+比较) | 足够 | 实时控制(推荐) |
| 角度区间法 | 中(需计算角度) | 中 | 低速或对精度要求不高的场合 |
我个人强烈推荐三变量符号法。它只用到了加减乘除和比较,没有三角函数,没有除法(除了系数sqrt(3)/2可以预先算好)。在C2000、STM32、国产MCU上都能跑得飞快。
2.5 避坑指南
我曾经在一个量产项目上吃过亏,分享几个容易翻车的地方:
- 浮点精度问题:如果MCU没有FPU,用定点数算U2、U3时要注意系数sqrt(3)/2≈0.866的量化误差。我建议用Q15或Q24格式,保留足够的小数位。
- 边界抖动:当Uα或Uβ接近0时,符号判断可能因为噪声来回跳变。加个死区——比如判断条件改成
U1 > EPSILON,EPSILON取0.001~0.01(标幺值)。 - 扇区映射表别写错:我见过有人把映射表写成
{0,1,5,0,3,2,6,0},结果扇区6永远进不去。建议写完后用几个已知角度验证一下。
验证方法:取几个典型角度,手动算一遍。比如Uα=1, Uβ=0(0°),应该落在扇区I。Uα=0.5, Uβ=0.866(60°),应该落在扇区II。Uα=-0.5, Uβ=0.866(120°),应该落在扇区III。跑通这三个点,基本就稳了。
2.6 完整代码示例
最后给出一段可以直接用的C代码。这是我在实际项目中提炼出来的版本,经过多个平台验证:
/**
* @brief 扇区判断函数
* @param Ualpha α轴电压(标幺值)
* @param Ubeta β轴电压(标幺值)
* @return 扇区编号 1~6
*/
int svpwm_sector_calc(float Ualpha, float Ubeta)
{
float U1, U2, U3;
int A, B, C, sector;
// 预计算系数
const float SQRT3_2 = 0.8660254f;
const float EPSILON = 0.001f;
// 计算三个参考变量
U1 = Ubeta;
U2 = SQRT3_2 * Ualpha - 0.5f * Ubeta;
U3 = -SQRT3_2 * Ualpha - 0.5f * Ubeta;
// 带死区的符号判断
A = (U1 > EPSILON) ? 1 : 0;
B = (U2 > EPSILON) ? 1 : 0;
C = (U3 > EPSILON) ? 1 : 0;
// 合成并映射
sector = A + (B << 1) + (C << 2);
// 映射表
const int map[8] = {0, 1, 5, 0, 3, 2, 4, 0};
sector = map[sector];
return sector;
}
这段代码在TI的C2000上实测,加上函数调用开销不到0.5μs(150MHz主频)。你想想看,这么点时间就能搞定扇区判断,剩下的时间足够做电流环PI调节、坐标变换、死区补偿……这才是工程上能用的方案。