4. SVPWM原理与实现:空间矢量脉宽调制

SVPWM,全称空间矢量脉宽调制。说白了,它就是把你要输出的电压矢量,用逆变器那六个开关管能产生的有限状态,给“拼”出来。我刚开始接触FOC时,觉得Clark变换和Park变换才是核心,SVPWM不过是个底层驱动。直到有一次,我在一个高速电机项目上,用传统的SPWM驱动,结果电流谐波大得离谱,电机嗡嗡响,温升也压不住。换成SVPWM之后,波形干净多了,母线电压利用率也上去了。嗯,从那以后,我再也不敢小看这个“底层驱动”了。

4.1 为什么需要SVPWM?

你想想看,FOC算出来的是Vα和Vβ,这是一个在旋转坐标系下的理想电压矢量。但逆变器是数字器件,它只能输出六个非零电压矢量和两个零矢量。SVPWM要做的,就是在一个开关周期内,用这八个基本矢量去“等效”你想要的任意矢量。

核心目标:让磁链轨迹更接近圆形,而不是六边形。磁链越圆,转矩脉动越小,电机跑得越顺。

4.2 数学原理:从三相到空间矢量

我们先回顾一下逆变器的六个开关管。上桥臂导通记为1,下桥臂导通记为0。三个桥臂的状态组合起来,就是(Sa, Sb, Sc)。比如(1,0,0)表示A相上桥臂导通,B、C相下桥臂导通。

这八个状态对应的电压矢量,在α-β平面上的分布是这样的:

  • V1(100):角度0°,幅值2/3 Vdc
  • V2(110):角度60°,幅值2/3 Vdc
  • V3(010):角度120°,幅值2/3 Vdc
  • V4(011):角度180°,幅值2/3 Vdc
  • V5(001):角度240°,幅值2/3 Vdc
  • V6(101):角度300°,幅值2/3 Vdc
  • V0(000) 和 V7(111):零矢量,幅值为0

这六个非零矢量把平面分成了六个扇区,每个扇区60°。你想要的任意电压矢量,必然落在某个扇区里。然后我们用这个扇区边界的两个矢量,加上零矢量,通过时间加权平均来合成它。

我个人习惯:在代码里,我会把V1到V6的幅值归一化到1,这样后面计算时间时,母线电压Vdc就变成了一个标幺值。省去很多麻烦。

4.3 扇区判断:你的矢量在哪个区?

拿到Vα和Vβ之后,第一步就是判断扇区。我见过有人用查表法,也有人用角度判断。但我最推荐的是用三值逻辑判断法,计算量小,没有三角函数,适合嵌入式跑。

定义三个中间变量:

Vref1 = Vβ
Vref2 = (√3/2) * Vα - (1/2) * Vβ
Vref3 = -(√3/2) * Vα - (1/2) * Vβ

然后根据这三个值的符号,就能唯一确定扇区:

扇区 Vref1 > 0 Vref2 > 0 Vref3 > 0
I (0°~60°) 是 是 否
II (60°~120°) 是 否 否
III (120°~180°) 是 否 是
IV (180°~240°) 否 否 是
V (240°~300°) 否 是 是
VI (300°~360°) 否 是 否

实际代码里,我会把这三个符号位拼成一个3位的数,然后直接查表得到扇区号。比如N = 4*C + 2*B + A,其中A、B、C分别对应Vref1、Vref2、Vref3的符号位(正为1,负为0)。

我曾经踩过的坑:在判断符号时,一定要考虑浮点数的精度问题。Vref接近0时,符号可能抖动。我一般会加一个很小的死区,比如1e-6,小于这个值就认为是0。否则扇区会来回跳,导致电流波形出现毛刺。

4.4 作用时间计算:T1、T2怎么算?

确定扇区之后,就要算两个边界矢量的作用时间T1和T2,以及零矢量的作用时间T0。

以扇区I为例,边界矢量是V1(100)和V2(110)。根据伏秒平衡原理:

Vα * Ts = |V1| * T1 * cos(0°) + |V2| * T2 * cos(60°)
Vβ * Ts = |V1| * T1 * sin(0°) + |V2| * T2 * sin(60°)

代入|V1| = |V2| = 2/3 * Vdc,解方程得到:

T1 = (√3 * Ts / Vdc) * ( (√3/2) * Vα - (1/2) * Vβ )
T2 = (√3 * Ts / Vdc) * Vβ

你看,这两个表达式是不是很眼熟?其实就是前面判断扇区时用到的Vref2和Vref1。所以实际工程中,我们会把六个扇区的T1、T2计算公式统一成三个通用变量X、Y、Z:

X = (√3 * Ts / Vdc) * Vβ
Y = (√3 * Ts / Vdc) * ( (√3/2) * Vα + (1/2) * Vβ )
Z = (√3 * Ts / Vdc) * ( -(√3/2) * Vα + (1/2) * Vβ )

然后根据扇区查表:

扇区 T1 T2
I Z Y
II Y -X
III -Z X
IV -X Z
V X -Y
VI -Y -Z

算完之后,记得做过调制处理。如果T1 + T2 > Ts,说明你想要的矢量超出了六边形的边界,需要等比例缩小:

T1_sat = T1 * Ts / (T1 + T2)
T2_sat = T2 * Ts / (T1 + T2)
T0 = 0

否则,T0 = Ts - T1 - T2。

4.5 七段式SVPWM:对称与低谐波

七段式,也叫对称SVPWM。它的特点是:每个开关周期内,开关管动作6次,波形对称,谐波含量低。我大部分项目都用这个。

以扇区I为例,七段式的开关序列是:

000 → 100 → 110 → 111 → 110 → 100 → 000

对应的作用时间是:

T0/4 → T1/2 → T2/2 → T0/2 → T2/2 → T1/2 → T0/4

你看,零矢量被分成了四段,分别放在开头、中间和结尾。这样做的目的是让波形关于周期中心对称,从而降低低次谐波。

实际代码中,我会先算出三相的比较值:

Ta = (Ts - T1 - T2) / 4
Tb = Ta + T1 / 2
Tc = Tb + T2 / 2

然后根据扇区,把Ta、Tb、Tc分配给A、B、C三相的PWM比较寄存器。分配规则如下:

扇区 比较器A 比较器B 比较器C
I Ta Tb Tc
II Tb Ta Tc
III Tc Ta Tb
IV Tc Tb Ta
V Tb Tc Ta
VI Ta Tc Tb

4.6 五段式SVPWM:降低开关损耗

五段式,也叫非对称SVPWM。它在一个周期内只动作4次,开关损耗比七段式低约33%。但代价是谐波含量更高,电流纹波更大。

五段式的思路是:只把零矢量放在开头或结尾,而不是中间。比如扇区I的五段式序列可以是:

000 → 100 → 110 → 111 → 110 → 100 → 000  (实际上只用了5段,因为中间有一段是连续的)

更常见的做法是:每个扇区固定让某一相在整个周期内不切换。比如扇区I,让C相一直为0(下桥臂导通),这样C相的开关管就不动了。

五段式的比较值计算稍微不同:

// 以扇区I为例,C相一直为0
Ta = (Ts - T1 - T2) / 2
Tb = Ta + T1
Tc = Ts  // 或者0,取决于你让哪一相不动

我建议:如果你的母线电压不高,或者对效率要求极其苛刻(比如电池供电的便携设备),可以考虑五段式。否则,七段式是更稳妥的选择。我在一个无人机项目中试过五段式,续航确实提升了,但电机噪音变大了,最后还是换回了七段式。

4.7 SVPWM整体流程图

下面这张图,是我自己画的一个SVPWM完整流程。从拿到Vα和Vβ开始,到最终输出PWM波形,每一步都标清楚了。

SVPWM 完整流程图 SVPWM 完整流程图 输入 Vα, Vβ 计算 Vref1, Vref2, Vref3 查表确定扇区 N 计算 X, Y, Z 查表得 T1, T2 过调制处理 输出 PWM 比较值 关键点 • 扇区判断用三值法 • T1,T2 查表避免分支 • 过调制必须做

4.8 代码实现要点

最后,我分享一下实际写代码时的几个关键点:

  • 标幺化:把Vα、Vβ归一化到[-1, 1]之间,这样T1、T2的计算就变成了纯小数运算,方便定点化。
  • 避免浮点:如果MCU没有FPU,建议把所有系数都转成Q15或Q31格式。比如√3/2 ≈ 0.866,可以表示为0.866 * 2^15 = 28377。
  • PWM定时器配置:一定要用中心对齐模式(Center-Aligned),这样七段式才能对称。边沿对齐模式只适合五段式。
  • 死区插入:别忘了在互补PWM中插入死区时间。我一般设成200ns~500ns,具体看IGBT或MOSFET的关断延迟。

我曾经犯过的错:有一次我把PWM定时器的计数方向搞反了,结果七段式变成了“倒着走”,电机直接反转。排查了半天才发现是定时器的向上/向下计数配置错了。所以,写完代码后,最好用示波器抓一下PWM波形,确认开关序列是对的。

好了,SVPWM的核心内容就这些。从数学原理到扇区判断,从时间计算到七段式与五段式的实现,每一步都有它的物理意义和工程考量。你把这些吃透了,FOC的底层驱动就算真正掌握了。