2、扇区判断:Clark变换与Park变换、扇区编号规则、扇区判断的C代码实现

各位好,我是老周。今天咱们聊聊SVPWM里最基础也最关键的一步——扇区判断。很多刚入行的朋友觉得这步就是套公式,没什么好讲的。但我在项目里吃过亏,有一次电机低速抖动,查了三天,最后发现是扇区判断的边界条件没处理好。嗯,从那以后我对这步就格外上心了。

说白了,扇区判断就是回答一个问题:当前电压矢量到底落在哪个扇区里?你想想看,六个功率管开关状态对应六个基本电压矢量,把空间分成六个60°的扇区。你要合成目标矢量,首先得知道它在哪,才能决定用哪两个基本矢量去拼。

2.1 从Clark变换到Park变换

先理清一个概念。我们做SVPWM,通常是从三相电流或电压开始的。但三相坐标系(abc)不好直接做矢量控制,所以需要变换。

Clark变换(也叫3s/2s变换)把abc三相静止坐标系,映射到αβ两相静止坐标系。公式很简单:

Iα = Ia
Iβ = (Ia + 2*Ib) / √3

注意,这里用的是等幅值变换。我个人习惯用等幅值,因为后续计算PI参数时更直观。当然也有等功率变换,系数不同,但核心思想一样。

做完Clark变换,我们得到的是αβ坐标系下的分量。但这两个分量还是交流量,做闭环控制不方便。于是就有了Park变换(也叫2s/2r变换),把αβ静止坐标系旋转到dq旋转坐标系:

Id = Iα * cosθ + Iβ * sinθ
Iq = -Iα * sinθ + Iβ * cosθ

θ是转子位置角,一般来自编码器或观测器。经过Park变换,交流量变成了直流量,PI控制器就好设计了。

关键点:扇区判断用的是αβ坐标系下的分量,不是dq分量。很多新手搞混了,在dq域里算扇区,结果永远不对。记住:扇区判断在αβ静止坐标系下完成。

2.2 扇区编号规则

六个基本电压矢量把空间分成六个扇区,编号从I到VI。怎么编号的?我习惯按逆时针方向,从0°开始:

扇区 角度范围 包含的基本矢量
I 0° ~ 60° V1(100), V2(110)
II 60° ~ 120° V2(110), V3(010)
III 120° ~ 180° V3(010), V4(011)
IV 180° ~ 240° V4(011), V5(001)
V 240° ~ 300° V5(001), V6(101)
VI 300° ~ 360° V6(101), V1(100)

这里有个小细节:基本矢量的开关状态,我写的是(100)这种形式,分别对应A、B、C三相的上管状态。1表示上管导通,0表示下管导通。V1(100)就是A相上管通,B、C相下管通。

我的习惯:扇区编号从I开始,不用0~5。虽然C语言里数组下标从0开始,但调试时用I~VI更直观,不容易搞混。代码里可以加个枚举或者注释对应关系。

2.3 扇区判断的数学原理

怎么判断扇区?最直接的方法是用角度。算出电压矢量的角度θ = atan2(Uβ, Uα),然后看θ落在哪个区间。但atan2计算量太大,在MCU上跑不划算。

实际工程中,我们用三值判断法。说白了,就是看Uα和Uβ的符号以及它们的组合关系。定义三个中间变量:

B1 = Uβ
B2 = √3 * Uα - Uβ
B3 = -√3 * Uα - Uβ

然后看这三个变量的符号。每个变量取正为1,取负为0,组成一个三位二进制数N:

N = (B1 > 0) * 4 + (B2 > 0) * 2 + (B3 > 0) * 1

N的取值范围是0~7,但实际有效的只有6个值。查表就能得到扇区号:

N 扇区
3 I
1 II
5 III
4 IV
6 V
2 VI

为什么会这样?你想想看,B1、B2、B3其实对应三条直线,把平面分成六个区域。每个区域对应一组符号组合,正好映射到六个扇区。

2.4 扇区判断的C代码实现

好了,理论讲完,上代码。这是我项目里用的一段,经过多次优化,计算量很小:

// 扇区判断函数
// 输入:Ualpha, Ubeta(经过Clark变换后的分量)
// 输出:扇区编号 1~6
int svpwm_sector_calc(float Ualpha, float Ubeta)
{
    int sector = 0;
    float B1, B2, B3;
    int N = 0;
    
    // 计算三个中间变量
    B1 = Ubeta;
    B2 = 1.7320508f * Ualpha - Ubeta;  // √3 ≈ 1.732
    B3 = -1.7320508f * Ualpha - Ubeta;
    
    // 符号判断,组成N值
    if (B1 > 0.0f) N |= 4;
    if (B2 > 0.0f) N |= 2;
    if (B3 > 0.0f) N |= 1;
    
    // 查表得到扇区
    switch (N)
    {
        case 3: sector = 1; break;  // I扇区
        case 1: sector = 2; break;  // II扇区
        case 5: sector = 3; break;  // III扇区
        case 4: sector = 4; break;  // IV扇区
        case 6: sector = 5; break;  // V扇区
        case 2: sector = 6; break;  // VI扇区
        default: sector = 0; break; // 异常情况
    }
    
    return sector;
}

注意边界处理:当Uα或Uβ接近0时,B1、B2、B3可能刚好在0附近抖动。我遇到过这种情况,导致扇区频繁跳变,电机噪音很大。解决办法是加一个死区阈值,比如0.0001f,小于这个值就当0处理。

2.5 实战中的坑与经验

讲几个我踩过的坑:

  • 浮点精度问题:√3用1.7320508f,不要用1.732。差一点,在边界附近就可能判错扇区。我吃过这个亏,电机在60°附近抖动,查了半天发现是系数精度不够。
  • 坐标系对齐:Clark变换的输出要和扇区判断的输入对齐。如果你用了等功率Clark变换,系数不同,扇区判断的公式也要相应调整。别混用。
  • 调试技巧:我习惯在代码里加一个调试输出,把Uα、Uβ和判出的扇区一起打印出来。用手转动电机轴,看扇区是否按0°→60°→120°...的顺序变化。如果跳变或者顺序不对,说明变换或者判断有问题。

2.6 知识体系总览

下面这张图把扇区判断的整个流程串起来了,从三相电流到扇区输出,每一步都很清楚:

扇区判断知识体系总览 三相电流/电压 (abc) Clark变换 (3s/2s) αβ静止坐标系分量 扇区判断 (三值法) Park变换 (2s/2r) 扇区编号 I ~ VI dq旋转坐标系分量 说明:扇区判断在αβ静止坐标系下完成,与Park变换是并行关系,互不依赖

从图上可以清楚看到,Clark变换之后分了两条路:一条去扇区判断,一条去Park变换。两者是并行的,互不依赖。这也是为什么扇区判断可以独立调试——你甚至不需要接电机,给一组Uα、Uβ就能验证。

总结一下:扇区判断的核心就三步——Clark变换得到αβ分量、三值法计算N值、查表得到扇区号。代码量不大,但边界条件和精度处理要小心。我建议你在调试初期,先把扇区判断单独拎出来测试,确认无误后再往下走。这一步稳了,后面的矢量合成和PWM生成才有基础。