// 01 · 动机
为什么不用普通的 SPWM?
🎯
一句话理解SVPWM的目标
给电机施加一个任意方向、任意幅值的旋转电压矢量,同时把直流母线电压用到极致。
在普通SPWM中,三路独立的正弦波各自调制,互相不知道对方的状态。而电机实际需要的是一个旋转的合成电压矢量——SVPWM正是从这个整体视角出发设计的。
😐 SPWM
- 三路独立调制
- 直流利用率:50%
- 谐波稍高
- 实现简单
✅ SVPWM
- 以矢量整体考量
- 直流利用率:57.7%
- 谐波更低
- 数字实现友好
🔦
比喻:手电筒打光
想象你要用手电筒在墙上打出一个指向任意方向的光斑。
SPWM:三个手电筒分别独立摆动,光斑是三者叠加的意外结果。
SVPWM:你明确知道要把光斑打到哪里,然后计算三个手电筒分别开多长时间来精确合成。
// 02 · 基础
三相逆变器有多少种开关状态?
三相逆变器有三个桥臂,每个桥臂上管(S_up)和下管(S_down)互补导通(不能同时开,防止直通)。所以每个桥臂只有0或1两种状态,三个桥臂共有 2³ = 8种组合。
当前矢量: V4 (100) — 非零矢量
| 矢量 | Sa Sb Sc | 类型 | 含义 |
| V₀ | 0 0 0 | 零矢量 | 三相全下管,输出为零 |
| V₁ | 1 0 0 | 有效矢量 | 指向0° |
| V₂ | 1 1 0 | 有效矢量 | 指向60° |
| V₃ | 0 1 0 | 有效矢量 | 指向120° |
| V₄ | 0 1 1 | 有效矢量 | 指向180° |
| V₅ | 0 0 1 | 有效矢量 | 指向240° |
| V₆ | 1 0 1 | 有效矢量 | 指向300° |
| V₇ | 1 1 1 | 零矢量 | 三相全上管,输出为零 |
// 03 · 核心概念
六边形与空间矢量图
把8个开关状态对应的电压矢量画到二维平面(αβ坐标系)上,会得到一个正六边形。6个非零矢量均匀分布,把平面分成6个扇区。
🕰️
比喻:时钟的时针
6个有效矢量就像时钟的6个方向(12点、2点、4点、6点、8点、10点)。
你想要时针指向1点30分,但只能用12点和2点的组合——靠近哪个方向就让它多作用一段时间,这就是时间平均合成的思路。
扇区: I
T₁: 0.00·Ts
T₂: 0.00·Ts
T₀: 0.00·Ts
旋转调节角度,观察:参考矢量(白色箭头)如何被相邻两个基矢量(彩色箭头)分解,以及零矢量时间如何随着幅值变化。
// 04 · 数学
Clark变换与时间计算
1
Clarke变换:三相 → αβ坐标
将三相电压(120°互差)投影到两轴正交坐标系,去掉冗余维度。
2
判断扇区:Vref在哪个扇区?
根据αβ分量,用位运算快速确定扇区号(1~6)。
3
计算作用时间:T₁, T₂, T₀
利用正弦定理,算出相邻两个基矢量各需作用多少时间。
4
生成比较值:写入定时器寄存器
按七段式对称排列,计算三个通道的计数器比较值。
Step 1:Clarke 变换
Vα = √(2/3) × [Va − 0.5×Vb − 0.5×Vc]
Vβ = √(2/3) × [ (√3/2)×Vb − (√3/2)×Vc]
Vα = Va
Vβ = (Va + 2×Vb) / √3
Step 2:扇区判断(位运算法)
U1 = Vβ
U2 = −Vβ/2 + (√3/2)×Vα
U3 = −Vβ/2 − (√3/2)×Vα
A = (U1 > 0) ? 1 : 0
B = (U2 > 0) ? 1 : 0
C = (U3 > 0) ? 1 : 0
N = 4×C + 2×B + A
Step 3:作用时间计算(以扇区I为例)
T1 = k × ( (√3/2)×Vα − Vβ/2 )
T2 = k × Vβ
T0 = Ts − T1 − T2
if (T1+T2 > Ts): T1 = T1×Ts/(T1+T2); T2 = T2×Ts/(T1+T2)
其他扇区的公式通过坐标旋转得到,结构完全一样,只是 Vα、Vβ 的系数正负不同。实现时通常统一用查表或 if-else 处理各扇区。
// 05 · 七段式
PWM 波形的对称排列
知道了 T1, T2, T0 之后,还需要决定这一个 PWM 周期内开关管的具体开关顺序。标准做法是七段式(Symmetric 7-segment):
🎵
比喻:三明治结构
七段 = 前半段(V₀ → V_x → V_{x+1} → V₇)+ 后半段(镜像对称)
结果:每个周期内每相只发生2次开关动作,而且波形关于中点对称,谐波最低。
以扇区I(Sa=1先拉高)为例,七段排列如下:
Tcma: 0.0%
Tcmb: 0.0%
Tcmc: 0.0%
三个比较值(Tcma, Tcmb, Tcmc)直接写入定时器的捕获比较寄存器即可,DSP/MCU内部的上下计数器会自动产生对称PWM。
Tcma = T0/4
Tcmb = T0/4 + T1/2
Tcmc = T0/4 + T1/2 + T2/2
// 06 · 实现
完整 C代码 实现
以下是可直接用于 DSP(TMS320F28xxx)或 STM32 的 SVPWM C 函数,注释详尽:
#include <math.h>
#define SQRT3 1.732050808f
#define SQRT3_2 0.866025404f
#define ONE_OVER_SQRT3 0.577350269f
typedef struct {
float Tcma, Tcmb, Tcmc;
int sector;
} SVPWM_Out;
SVPWM_Out SVPWM_Calc(float Va, float Vb) {
SVPWM_Out out;
float T1, T2, T0;
float X, Y, Z;
float U1 = Vb;
float U2 = -Vb * 0.5f + SQRT3_2 * Va;
float U3 = -Vb * 0.5f - SQRT3_2 * Va;
int A = (U1 > 0) ? 1 : 0;
int B = (U2 > 0) ? 1 : 0;
int C = (U3 > 0) ? 1 : 0;
int N = 4*C + 2*B + A;
int sector_map[] = {0,2,6,1,4,3,5,0};
out.sector = sector_map[N];
X = SQRT3 * Vb;
Y = SQRT3_2 * Va + Vb * 0.5f;
Z = -SQRT3_2 * Va + Vb * 0.5f;
switch (out.sector) {
case 1: T1= Y; T2= X; break;
case 2: T1= -Z; T2= -Y; break;
case 3: T1= Z; T2= X; break;
case 4: T1= -X; T2= -Z; break;
case 5: T1= X; T2= Y; break;
case 6: T1= -Y; T2= -X; break;
default: T1=T2=0;
}
if (T1 + T2 > 1.0f) {
float k = 1.0f / (T1 + T2);
T1 *= k; T2 *= k;
}
T0 = (1.0f - T1 - T2) * 0.5f;
float Ta=0, Tb=0, Tc=0;
switch (out.sector) {
case 1: Ta=T0; Tb=T0+T1; Tc=T0+T1+T2; break;
case 2: Ta=T0+T2; Tb=T0; Tc=T0+T1+T2; break;
case 3: Ta=T0+T1+T2; Tb=T0; Tc=T0+T1; break;
case 4: Ta=T0+T1+T2; Tb=T0+T1; Tc=T0; break;
case 5: Ta=T0+T1; Tb=T0+T1+T2; Tc=T0; break;
case 6: Ta=T0; Tb=T0+T1+T2; Tc=T0+T1; break;
}
out.Tcma = 0.5f - Ta;
out.Tcmb = 0.5f - Tb;
out.Tcmc = 0.5f - Tc;
return out;
}
⚠️
新工程师常见坑
- Vα Vβ 的归一化基准要统一(是 Vdc 还是 Vdc/2?)
- 过调制(T1+T2>Ts)不处理会导致输出削波失真
- 死区时间会造成电压误差,高精度场合需死区补偿
- 扇区判断边界点(θ = 0°, 60°...)要处理好,防止抖动
- MCU 定时器计数方向(上下计数)影响比较值的具体写法
// 07 · 小结
一张图理解整个流程
电流环/速度环
↓
Vd, Vq (旋转坐标系)
↓ Park逆变换
Vα, Vβ (静止坐标系)
↓ 扇区判断
Sector (1~6)
↓ 时间计算
T1, T2, T0
↓ 七段式排列
Tcma, Tcmb, Tcmc
↓ 写入定时器
PWM 三路互补信号 → 驱动IC → IGBT/MOSFET → 电机
| 概念 | 关键点 | 常见问题 |
| 开关状态 | 8个(含2个零矢量) | 混淆 V0 和 V7 的区别 |
| 扇区判断 | 用位运算,速度快 | 忘记处理 N=0 异常 |
| 时间计算 | T1+T2 ≤ Ts | 过调制不做归一化 |
| 七段式 | 每相 2 次开关/周期 | 比较值方向搞反 |
| 直流利用率 | 1/√3 ≈ 57.7% | —— |