扇区判断:如何根据αβ轴电压分量快速定位参考电压矢量

上一章我们聊了Clark变换,把三相静止的ABC坐标系,搬到了两相静止的αβ坐标系。这一步做完,我们手里就有了Uα和Uβ这两个值。

但问题来了——SVPWM要生成的是六个扇区里的空间电压矢量。你得先知道,当前这个参考电压矢量到底落在哪个扇区,才能决定用哪两个基本矢量去合成它。

说白了,扇区判断就是SVPWM的第一道关卡。判断错了,后面全白搭。我早年调试一台伺服驱动器时,就因为扇区判断的符号位取反,电机转起来抖得像筛糠,查了整整两天才找到问题。

扇区划分的几何直觉

先看一张图,帮大家建立空间感。

参考电压矢量扇区划分图 扇区Ⅰ 扇区Ⅱ 扇区Ⅲ 扇区Ⅳ 扇区Ⅴ 扇区Ⅵ α β V₁(100) V₂(110) V₃(010) V₄(011) V₅(001) V₆(101) Vref

六个基本矢量把平面切成六个60°的扇区。每个扇区里,任意一个Vref都可以由相邻的两个基本矢量合成。关键就是——怎么快速知道Vref落在哪个扇区。

从几何到代数:三个中间变量

你可能会想,用角度θ不就行了?atan2(Uβ, Uα)算出角度,然后除以60°取整。理论上没错,但实际工程里没人这么干。

为什么?因为反正切计算太慢了。在Cortex-M0或者DSP里,一个atan2可能要吃掉几十个时钟周期。而SVPWM的调制频率通常是10kHz~20kHz,每个周期留给扇区判断的时间窗口非常短。

所以,工业界通用的做法是——用代数不等式判断,不用三角函数。

具体怎么做?先定义三个中间变量:

// 三个中间变量,只用到Uα、Uβ和常数√3
B1 =  Uβ
B2 =  (√3/2) * Uα - 0.5 * Uβ
B3 = -(√3/2) * Uα - 0.5 * Uβ

这三个变量其实对应了三条扇区边界线的法向分量。你想想看,六边形的三条对角线,把平面分成了六个区域。每个B值的正负,就告诉你Vref在边界线的哪一侧。

核心思想:三个B值的符号组合,唯一确定一个扇区。不需要角度,不需要查表,只需要三次乘法和三次比较。

扇区号的计算公式

有了B1、B2、B3之后,我们定义一个临时变量N:

N = sign(B1) + 2*sign(B2) + 4*sign(B3)

这里的sign(x)是符号函数——x大于0时返回1,否则返回0。N的取值范围是0~7,但实际有效的只有6个值。然后通过一个简单的映射表,就能得到扇区号:

N值 1 2 3 4 5 6
扇区号 Ⅱ Ⅵ Ⅰ Ⅳ Ⅲ Ⅴ

这个映射表不是拍脑袋定的,它来源于N值与扇区边界的几何对应关系。我个人习惯把这个表直接写成数组,查表比switch-case更快:

const uint8_t sectorMap[7] = {0, 2, 6, 1, 4, 3, 5};
// 下标对应N值,数组元素就是扇区号
uint8_t sector = sectorMap[N];

小技巧:如果你用的MCU没有硬件浮点,可以把√3/2和0.5预先算成Q15或Q12格式的定点数。这样乘加运算全部用整数完成,速度还能再快一截。

完整代码实现

下面给出一段完整的C代码,输入Uα和Uβ,输出扇区号。这段代码我在STM32F103上跑过,单次判断耗时不到1μs。

#include <stdint.h>

#define SQRT3_OVER_2  0.8660254f
#define ONE_HALF      0.5f

uint8_t svpwm_sector_calc(float Ualpha, float Ubeta)
{
    float B1, B2, B3;
    uint8_t N;

    // 计算三个中间变量
    B1 = Ubeta;
    B2 =  SQRT3_OVER_2 * Ualpha - ONE_HALF * Ubeta;
    B3 = -SQRT3_OVER_2 * Ualpha - ONE_HALF * Ubeta;

    // 组合成N值
    N  = (B1 > 0) ? 1 : 0;
    N += (B2 > 0) ? 2 : 0;
    N += (B3 > 0) ? 4 : 0;

    // 查表得到扇区号
    static const uint8_t sectorMap[7] = {0, 2, 6, 1, 4, 3, 5};
    return sectorMap[N];
}

这段代码里,我用了静态常量表,避免每次调用都重新初始化。嗯,这里要注意——N值不会出现0和7,因为三个B值不可能同时为正或同时为负。所以数组第0个元素我随便填了个0,实际用不到。

边界情况与避坑指南

理论讲完了,聊聊实际中容易踩的坑。

第一个坑:浮点精度。当Vref非常接近扇区边界时,B1、B2、B3可能接近0。浮点运算的微小误差可能导致符号判断出错。我曾经在量产阶段遇到过,某批次电机在低速轻载时偶尔抖动,最后定位到就是扇区边界处的误判。

解决办法:给符号判断加一个滞回区间。

// 加一个死区,避免边界抖动
#define HYSTERESIS 0.001f

uint8_t sign_with_hyst(float x) {
    if (x > HYSTERESIS) return 1;
    if (x < -HYSTERESIS) return 0;
    return previous_sign;  // 保持上一次的结果
}

第二个坑:Uα和Uβ的标幺化。如果你的Uα和Uβ是标幺值(pu值),要确保它们的基值与SVPWM的直流母线电压一致。否则扇区边界会偏移,导致判断错误。

第三个坑:中断优先级。扇区判断通常在PWM中断里执行。如果被更高优先级的中断频繁打断,Uα和Uβ的采样时刻可能不对齐。我建议把Clark变换和扇区判断放在同一个中断里,关掉嵌套中断,保证数据一致性。

警告:不要在扇区判断中使用动态内存分配或阻塞式函数。SVPWM是实时控制,每个微秒都很宝贵。malloc、printf这些函数,在控制中断里绝对不要出现。

扇区判断的工程意义

扇区判断是整个SVPWM的入口。它决定了后续的矢量作用时间计算、比较值装载。判断错了,轻则电流畸变、转矩脉动,重则过流保护触发甚至炸管子。

我见过一些初学者,把扇区判断的映射表搞反了,结果电机反转。还有人在代码里用了switch-case,每个case里又调了子函数,导致中断执行时间超标。其实一个查表就搞定的事,别搞复杂了。

记住:扇区判断的核心就是三个不等式、一个组合值、一张映射表。简单、快速、可靠,这才是工程之道。