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 知识体系总览
下面这张图把扇区判断的整个流程串起来了,从三相电流到扇区输出,每一步都很清楚:
从图上可以清楚看到,Clark变换之后分了两条路:一条去扇区判断,一条去Park变换。两者是并行的,互不依赖。这也是为什么扇区判断可以独立调试——你甚至不需要接电机,给一组Uα、Uβ就能验证。
总结一下:扇区判断的核心就三步——Clark变换得到αβ分量、三值法计算N值、查表得到扇区号。代码量不大,但边界条件和精度处理要小心。我建议你在调试初期,先把扇区判断单独拎出来测试,确认无误后再往下走。这一步稳了,后面的矢量合成和PWM生成才有基础。