1. 从SPWM到SVPWM一个更聪明的电压分配思路如果你接触过电机控制或者三相逆变器SPWM正弦脉宽调制大概率是你入门的第一课。它直观、好理解通过比较正弦波和三角载波来生成驱动开关管的PWM信号目标是让逆变器输出的电压波形尽可能接近正弦。但干过一阵子你就会发现SPWM有个“先天不足”直流母线电压的利用率不高理论上最大输出线电压幅值只有母线电压的0.866倍。这意味着同样的电池或电源你的电机跑不出它该有的劲儿。SVPWM空间矢量脉宽调制就是为了解决这个问题而生的。它不再盯着每一相单独的正弦波而是把三相电压Ua, Ub, Uc看作一个整体——一个在复平面上旋转的空间电压矢量。这个思路非常巧妙它直接控制这个矢量的轨迹让它尽可能逼近一个理想的圆形。这样做的好处显而易见直流母线电压的利用率提升了理论上能达到1比SPWM高了大约15.5%。这意味着在同样的硬件条件下电机能输出更大的转矩和功率或者达到同样的性能对电源电压的要求可以更低成本和安全裕度都更优。我第一次在实际项目中用SVPWM替换掉SPWM时最直观的感受就是电机的中高速段“有劲了”温升也明显改善。这背后的核心就是SVPWM那套基于电压矢量合成与作用时间计算的算法。而“五段式”和“七段式”则是实现这个算法在单个PWM周期内的两种不同开关序列编排方式它们直接影响了开关损耗、电流谐波和实现复杂度。今天我就结合自己用Simulink做算法仿真和代码生成的经验把这套东西的原理、区别和建模过程掰开揉碎了讲清楚。2. 空间电压矢量SVPWM的数学与几何基石要理解SVPWM必须先搞清楚“空间电压矢量”这个概念。它可不是一个玄乎的理论而是一个极其实用的数学工具把三相系统从时域分析带到了更直观的矢量空间。2.1 从三相静止坐标系到两相静止坐标系我们有三相桥式逆变器上下桥臂互补导通那么每相相对于直流母线中点O点的输出电压在任意时刻只能是Udc/2或-Udc/2两种状态。我们用1表示上桥臂导通输出Udc/20表示下桥臂导通输出-Udc/2。那么三相桥臂的开关状态就可以用一个三位的二进制数来表示比如(1,0,0)代表A相上管开、B相和C相下管开。对于三相系统[Ua, Ub, Uc]我们通过克拉克变换Clark Transformation将其映射到两相静止坐标系α-β坐标系上。变换公式如下Uα Ua Uβ (1/√3) * (Ub - Uc) // 注意这是等幅值变换的一种形式还有等功率变换。但更常用的等幅值变换矩阵是[Uα] 2/3 * [1, -1/2, -1/2] * [Ua] [Uβ] [0, √3/2, -√3/2] [Ub] [Uc]这个变换把互差120度的三相量投影到了两个垂直的坐标轴上。关键在于对于开关状态(Sa, Sb, Sc)其输出的相电压值(Ua, Ub, Uc)是确定的例如(1,0,0)对应(2/3Udc, -1/3Udc, -1/3Udc)代入上式就能计算出一个唯一的矢量Uout Uα jUβ。2.2 八个基本电压矢量与扇区划分三相桥臂有2^38种开关组合。把这8种组合对应的Uout全部计算出来并画在α-β复平面上你会得到6个非零矢量和2个零矢量。6个非零矢量U1(100),U2(110),U3(010),U4(011),U5(001),U6(101)。它们的幅值相等都是(2/3)Udc方向依次间隔60度像钟表的6个点一样均匀分布。2个零矢量U0(000)和U7(111)。这两个状态下三相输出电位相同要么全接正要么全接负因此线电压为零空间矢量幅值为零位于原点。这6个非零矢量把平面分成了6个扇区Sector I-VI。SVPWM的核心思想来了任何一个我们期望输出的目标电压矢量Uref只要它位于这6个非零矢量围成的正六边形内都可以通过其所在扇区相邻的两个非零矢量Ux,Uy以及两个零矢量U0,U7在一个很短的控制周期Ts内按不同时间长度合成出来。这就像用东、东北、北、西北、西、西南六个固定方向的力基本矢量通过控制每个力作用时间的长短来合成出任意一个360度方向上的力目标矢量。零矢量的作用就是“占位”当两个基本矢量作用时间加起来不足一个周期Ts时就用零矢量来填充剩余时间保证总周期不变。注意这里有个非常重要的边界条件。目标矢量Uref的幅值不能无限大。当Uref的矢端轨迹是正六边形的内切圆时母线电压利用率达到最大线性调制区。这个内切圆的半径是(√3/3)Udc对应输出线电压峰值为Udc。如果Uref超出这个圆进入过调制区算法就需要特殊处理比如削波或修改合成策略这会导致波形畸变。我们通常说的SVPWM优点指的就是在线性调制区内。3. SVPWM算法核心扇区判断与矢量作用时间计算理解了空间矢量的概念SVPWM的算法流程就清晰了。它本质上是一个在每一个PWM周期Ts内都要重复执行的实时计算任务。下面我结合自己写代码的习惯把每一步的逻辑和背后的“为什么”讲清楚。3.1 第一步扇区号判定我们已知目标电压矢量在α-β坐标系下的分量Uα和Uβ。如何快速判断它落在哪个扇区工程上常用一种基于Uβ,√3Uα - Uβ,-√3Uα - Uβ这三个量符号的判断法。定义三个中间变量A Uβ B (√3 * Uα - Uβ) / 2 // 实际计算时为了减少运算可以先算 √3*Uα再结合Uβ C (-√3 * Uα - Uβ) / 2然后根据A, B, C的正负大于0则为1否则为0组合成一个3位二进制数N [Sign(C), Sign(B), Sign(A)]。这个N值与扇区号有一个固定的映射关系。例如N3 (011)通常对应扇区I。这个映射表需要根据你定义的扇区起始位置和矢量顺序来微调在Simulink里可以用一个简单的查表模块或者Switch模块实现。为什么这么判断这其实是计算了目标矢量与六个扇区分界线的位置关系。每条分界线都是一条过原点的直线例如扇区I和II的分界线是β轴逆时针转30度。A, B, C的符号本质上就是判断Uref在这几条直线的那一侧。这是一种计算效率很高的方法避免了复杂的反三角函数计算。3.2 第二步计算相邻矢量作用时间假设Uref落在扇区I它由相邻的基本矢量U4(011)和U6(101)合成注意这里矢量编号和对应开关状态有多种定义习惯务必与你采用的扇区图对应。根据矢量合成的平行四边形法则有Uref * Ts U4 * T4 U6 * T6 Uzero * T0 Ts T4 T6 T0其中T4,T6分别是U4和U6的作用时间T0是零矢量作用时间。通过解这个矢量方程在α和β轴上的投影可以得到T4和T6的表达式。这个表达式与扇区有关。经过推导和整理可以定义一个与扇区无关的通用计算步骤计算中间变量X, Y, ZX √3 * Uβ * Ts / Udc Y ( (3/2)*Uα (√3/2)*Uβ ) * Ts / Udc Z ( (-3/2)*Uα (√3/2)*Uβ ) * Ts / Udc注意这里的系数Ts/Udc是归一化因子让X, Y, Z成为无量纲的时间量。实际建模时Udc通常作为已知参数输入。根据扇区号确定T1和T2即该扇区两个相邻基本矢量的作用时间与X, Y, Z的对应关系。例如对于一种常见的扇区定义扇区I:T1 Z,T2 Y扇区II:T1 Y,T2 -X扇区III:T1 -Z,T2 X... 以此类推T1,T2对应哪两个矢量取决于你的定义这里有个极易出错的坑计算出的T1T2有可能大于Ts。这发生在Uref幅值过大超出六边形内切圆即调制比m1的时候。因此必须加入饱和处理if (T1 T2) Ts: T1 T1 * Ts / (T1 T2) T2 T2 * Ts / (T1 T2)饱和处理后零矢量时间T0 Ts - T1 - T2。3.3 第三步生成开关切换序列五段式 vs 七段式这是“五段式”和“七段式”区别的核心所在。我们算出了T1,T2,T0现在需要把它们安排进一个Ts周期并生成具体的三相PWM占空比信号。七段式SVPWM在一个周期Ts内开关状态变化7次即7段。它以零矢量开始和结束中间对称地插入两个基本矢量。例如在扇区I一种典型的七段式序列是U0(000) - U4(011) - U6(101) - U7(111) - U6(101) - U4(011) - U0(000)。注意U7(111)出现在正中间。优点开关序列对称谐波特性较好电流纹波相对较小。缺点每个PWM周期内每个桥臂的开关管都要动作2次开和关各一次因此总开关次数是6次/周期。开关损耗较大。五段式SVPWM在一个周期Ts内开关状态变化5次即5段。它只使用一个零矢量并且让这个零矢量持续整个T0时间。序列不对称。例如在扇区I五段式序列可能是U4(011) - U6(101) - U7(111) - U6(101) - U4(011)。可以看到它没有U0(000)并且U7(111)不在正中间。优点每个PWM周期内总有一个桥臂的开关管在整个周期内状态不变不开关。因此总开关次数是4次/周期。相比七段式开关损耗降低了约1/3这对于高频应用或降低散热压力非常有价值。缺点谐波含量比七段式略高因为波形不对称。如何选择这是一个经典的性能与损耗的权衡。在电机控制中如果开关频率较高比如20kHz以上开关损耗成为主要矛盾五段式是更优选择。如果对电流波形质量要求极高如高性能伺服且开关频率有余量七段式更合适。在我做过的多数变频器和伺服项目中五段式是主流选择。3.4 第四步计算比较值占空比确定了开关序列和各矢量的作用时间最后一步就是计算生成实际PWM波所需的比较值CMP。以常用的中心对齐PWM模式为例我们需要计算三相占空比Ta, Tb, Tc它们分别对应一个PWM周期内各相上桥臂导通的时间。以七段式扇区I序列[000]-[011]-[101]-[111]-[101]-[011]-[000]为例我们需要计算每个状态切换的时间点。定义Tcm1 T0/4,Tcm2 T0/4 T1/2,Tcm3 T0/4 T1/2 T2/2。那么A相上管导通时间是从Tcm1到Ts - Tcm1。B相和C相的计算类似但起始和结束点不同因为它们对应的基本矢量作用时机不同。实际操作中我们不会真的去计算这些时间点而是利用扇区信息通过一套通用的公式直接算出Ta, Tb, Tc。例如// 假设已经根据扇区得到了T1, T2, T0并定义了时间分配 Tpwm (Ts - T0)/2; // 一个中间量 // 然后根据扇区给Ta, Tb, Tc赋值不同的Tpwm组合。更常见的做法是先计算三个时间变量t1, t2, t3t1 (Ts - T1 - T2) / 2 // 即T0/2 t2 t1 T1/2 t3 t2 T2/2然后根据不同的扇区和五段式/七段式将(t1, t2, t3)分配给(Ta, Tb, Tc)作为其导通时间的中间点偏移量对于中心对齐PWM比较值 周期值/2 ± 导通时间/2。这部分公式看起来繁琐但本质是固定的映射。在Simulink建模时我们可以用MATLAB Function模块或者一组Switch模块来实现这个映射表。4. Simulink建模实战从零搭建一个SVPWM模块理论讲透了我们动手在Simulink里把它实现出来。我会按照算法流程分步搭建并指出每个环节建模的要点和容易踩的坑。4.1 模型整体架构与输入输出设计首先新建一个Simulink模型。我们规划一个子系统模块命名为SVPWM_Generator。输入Ualpha,Ubeta目标电压矢量在α-β轴的分量。通常来自外环速度环、电流环的输出。Udc直流母线电压测量值或设定值。这是一个非常重要的参数必须准确否则会影响矢量合成精度和调制比。TsPWM载波周期秒。例如开关频率20kHz则Ts 1/20000 5e-5秒。PWM_Counter三角载波计数器值。对于中心对齐PWM这个计数器通常是一个从0向上计数到周期值Peak再向下计数到0的锯齿波。我们的模块需要这个实时计数值来生成边沿。输出PWM_A_H,PWM_B_H,PWM_C_H三相上桥臂的驱动信号1为开通0为关断。通常下桥臂信号与之互补但需要加入死区时间死区生成建议在SVPWM模块外部单独实现以保持模块功能单一。Sector可选当前扇区号用于调试观测。T1, T2可选基本矢量作用时间用于调试。4.2 扇区判断模块实现使用MATLAB Function模块是最清晰的方式。在模块内编写函数输入Ualpha,Ubeta输出扇区号Sector (1~6)。function Sector fcn(Ualpha, Ubeta) % 基于Ualpha, Ubeta计算扇区 % 定义中间变量使用符号函数简化判断 A Ubeta; B (sqrt(3)*Ualpha - Ubeta) / 2; C (-sqrt(3)*Ualpha - Ubeta) / 2; % 判断正负大于0为1 sign_A (A 0); sign_B (B 0); sign_C (C 0); % 组合成3位二进制数 N [Sign(C) Sign(B) Sign(A)] N sign_C * 4 sign_B * 2 sign_A; % 加权求和得到0~7的整数 % 根据N值映射扇区号 (这是一种常见的映射需与后续时间计算匹配) switch N case 1 % 001 Sector 2; case 2 % 010 Sector 6; case 3 % 011 Sector 1; case 4 % 100 Sector 4; case 5 % 101 Sector 3; case 6 % 110 Sector 5; otherwise % 0,7 或其他理论上Uref为零矢量时发生可指定一个扇区 Sector 1; end end注意这个映射表不是唯一的它必须与你后续计算T1, T2时采用的扇区矢量顺序严格对应。如果你参考的论文或代码用了不同的映射一定要整体修改否则整个算法就错乱了。这是联调时最容易出问题的地方之一。4.3 矢量作用时间计算模块这个模块接收Ualpha, Ubeta, Udc, Ts和Sector输出T1, T2, T0。同样可以用MATLAB Function实现。function [T1, T2, T0] fcn(Ualpha, Ubeta, Udc, Ts, Sector) % 计算基本矢量作用时间 % 1. 计算中间变量X, Y, Z (已归一化) % 注意这里使用等幅值变换下的公式 X sqrt(3) * Ubeta * Ts / Udc; Y ( (3/2)*Ualpha (sqrt(3)/2)*Ubeta ) * Ts / Udc; Z ( (-3/2)*Ualpha (sqrt(3)/2)*Ubeta ) * Ts / Udc; % 2. 根据扇区为T1, T2赋值 switch Sector case 1 T1 Z; T2 Y; case 2 T1 Y; T2 -X; case 3 T1 -Z; T2 X; case 4 T1 -X; T2 Z; case 5 T1 X; T2 -Y; case 6 T1 -Y; T2 -Z; otherwise T1 0; T2 0; end % 3. 饱和处理 if (T1 T2) Ts T1 T1 * Ts / (T1 T2); T2 T2 * Ts / (T1 T2); elseif (T1 T2) 0 % 理论上不会但加个保护 T1 0; T2 0; end % 4. 计算零矢量时间 T0 Ts - T1 - T2; % 确保T0非负 if T0 0 T0 0; end end关键点X, Y, Z的计算公式是基于特定的克拉克变换系数推导的。如果你在模型的其他部分如电流环使用了不同的变换系数如等功率变换这里的系数需要调整否则Uref的幅值意义会不一致导致调制比计算错误。务必保证整个系统使用的变换基准统一。4.4 开关序列与比较值生成模块以五段式为例这是最体现“五段式”和“七段式”区别的部分。我们需要根据Sector,T1,T2,T0以及当前PWM计数器的值PWM_Counter来实时决定三相输出是高还是低。首先我们需要根据扇区和五段式规则计算出三相的“导通时间中点”或“比较值”。对于五段式我们通常计算三个时间点tA, tB, tC它们表示各相上桥臂从“关断切换到开通”的时刻对于中心对齐PWM开通时间对称分布在周期中点两侧。我们可以用另一组Switch-case来实现。假设我们采用一种常见的五段式时间分配方案方案有多种需前后一致定义Tpwm Ts/2。定义t1 (T1 T2)/2,t2 T2/2,t3 T1/2。这只是其中一种定义具体关系取决于扇区然后根据扇区将(t1, t2, t3)赋值给(Ta, Tb, Tc)但可能需要加上一个偏移量Tpwm。为了避免混乱我推荐一种更直观、不易出错的方法直接计算三相的占空比。计算各相在半个周期内的导通时间即上管开通时间。对于五段式在一个开关周期Ts内各相上管的开通时间Ton_x可以通过T1, T2, T0和扇区信息直接算出。例如在扇区I使用矢量U4(011)和U6(101)A相在U6(101)作用期间开通。U6对应T2。所以Ton_A T2。B相在U4(011)作用期间开通。U4对应T1。所以Ton_B T1。C相在U4和U6作用期间都关断因为都是上管关断状态仅在零矢量U7(111)时开通不对U7时三相上管全开。所以C相开通时间需要仔细分析序列。实际上对于五段式序列U4-U6-U7-U6-U4C相上管在U4和U7期间开通。因此Ton_C T1 T0。你看这样推导很容易错。更通用的方法是使用占空比查表法。我们可以预先根据扇区和T1, T2, T0计算出三相的占空比Duty_A, Duty_B, Duty_C范围0~1。公式如下适用于一种常见的五段式定义// 先计算中间量 tA (T1 T2) / 2; tB T2 / 2; tC T1 / 2; // 然后根据扇区确定各相比率 // 扇区I: Duty_A (Tpwm tA)/Ts, Duty_B (Tpwm tB)/Ts, Duty_C (Tpwm tC)/Ts // 扇区II: Duty_A (Tpwm tB)/Ts, Duty_B (Tpwm - tA)/Ts, Duty_C (Tpwm tC)/Ts // ... 以此类推共6个扇区的公式其中Tpwm Ts/2。生成PWM比较值对于中心对齐PWM硬件定时器通常需要一个比较值CMPx。假设计数器从0计数到Period对应Ts然后递减到0。那么比较值可以设置为CMPx (Period/2) * (1 - Duty_x) // 或者 (Period/2) * Duty_x取决于你希望计数器在大于还是比较值时输出高电平。这需要与你硬件PWM模块的极性设置匹配。通常我们定义当计数器值小于CMPx时输出高电平上管开通。那么CMPx就对应了高电平的持续时间。因此CMPx Duty_x * Period。实时比较输出在Simulink中我们可以用Relational Operator关系运算符模块来实现。将PWM_Counter与计算出的CMP_A, CMP_B, CMP_C进行比较。例如设置PWM_A_H (PWM_Counter CMP_A)。建模技巧为了模型清晰可以将“扇区判断”、“时间计算”、“占空比/比较值计算”分别封装成子模块。在“占空比/比较值计算”模块中使用一个Multiport Switch模块由Sector作为选择端6个输入端口分别连接6个对应扇区的占空比计算公式。4.5 模型仿真与验证搭建完模型后必须进行仿真验证。输入信号设计使用Sine Wave模块生成相位差120度的三相正弦信号Ua, Ub, Uc然后通过克拉克变换模块得到Ualpha, Ubeta。给定一个恒定的Udc如600V和Ts如5e-5s。PWM_Counter可以用Repeating Sequence模块生成一个三角波或者更简单地在离散仿真中我们有时会省略这个计数器直接使用计算出的占空比驱动一个PWM Generator模块Simulink库里有。观测波形Scope 1观察Ualpha, Ubeta的轨迹用XY Graph应该是一个圆或给定幅值的正弦波。Scope 2观察三相PWM输出信号PWM_A_H等。放大时间轴查看单个PWM周期内的开关序列是否符合五段式或七段式的特征开关次数。Scope 3将PWM信号通过一个理想的三相桥臂和负载如电阻电感负载或电机模型测量输出的三相线电压Uab, Ubc, Uca。对其做傅里叶分析FFT观察基波幅值是否与Uref设定值匹配并查看谐波分布。SVPWM的谐波能量主要集中在开关频率及其倍频附近比SPWM更集中。Scope 4观测Sector信号它应该随着Uref旋转而周期性变化1-2-3-4-5-6-1...。关键验证点调制比线性度缓慢增加Uref的幅值测量输出线电压基波幅值。在线性调制区内Uref幅值 Udc/√3输出幅值应与输入幅值成严格正比。当超过此限值进入过调制区输出幅值增长变缓波形开始畸变。五段式/七段式切换可以做一个使能开关在模型中方便地切换两种调制方式。对比仿真可以看到七段式的电流纹波略小但开关动作更频繁。5. 从仿真到实现工程化细节与避坑指南仿真跑通只是第一步要把算法烧进芯片里稳定运行还有一大堆细节要处理。这些往往是资料里不会写但实际项目中一定会遇到的坑。5.1 标幺化与定点数处理在单片机或DSP中直接进行浮点运算尤其是涉及sqrt(3)会消耗大量CPU资源。工程上普遍采用标幺化和定点数运算。标幺化以直流母线电压Udc和PWM周期Ts为基准将所有电压和时间量标幺。例如定义Ubase Udc,Tbase Ts。那么输入的Ualpha, Ubeta已经是标幺值相对于Udc。计算X, Y, Z时公式中的Udc和Ts就消失了变成了X sqrt(3) * Ubeta_pu。这大大简化了计算。定点数将标幺后的数值放大一定的倍数如2^1532768用整数类型int16_t,int32_t来存储和运算。例如Ualpha_pu的范围是[-1, 1]乘以32768后用int16_t表示范围就是[-32768, 32767]。所有中间运算都在这个定点数体系下进行最后再缩小倍数得到实际值。特别注意乘法运算后位数扩展需要做舍入或移位处理防止溢出和精度损失。在Simulink中你可以使用Fixed-Point Designer工具箱来模拟定点数行为设置数据的字长、小数位观察量化误差和溢出这对确保算法在硬件上稳定运行至关重要。5.2 死区时间补偿实际硬件中为了防止上下桥臂直通必须在互补的PWM信号中加入死区时间Dead Time即在一管关闭后延迟一段时间再打开另一管。这段延迟会导致输出电压损失尤其是在低调制比、低功率因数时会引起波形畸变和电流误差。死区补偿是高性能驱动必须做的。补偿思路是在计算出的占空比上根据电流方向增加或减少一个等效的时间。例如当A相电流流出桥臂即流向电机时死区效应会使得实际输出电压比理想值低那么就在计算出的A相上管开通时间上增加一个死区时间Tdead。在Simulink建模时可以在SVPWM模块后级联一个死区补偿模块。该模块的输入是三相PWM信号和三相电流方向信号通过比较电流与0得到。根据电流方向对PWM信号进行微调。注意补偿后的占空比不能超过0%或100%需要做限幅处理。5.3 过调制与弱磁控制前面讨论的都是线性调制区。当需要输出更高电压时例如电机基速以上运行就需要进入过调制区。过调制算法有很多种比如基于谐波注入的算法或者直接对Uref进行限幅和角度修正。在Simulink中你可以在克拉克变换后对Uref的幅值进行判断如果超过最大线性幅值则调用过调制处理函数修正Ualpha和Ubeta。弱磁控制是另一个高级话题。当电机转速超过额定转速时反电动势升高为了维持母线电压可利用需要减小定子磁链即减小Uref的幅值同时调整其角度。这通常是在更外层的控制环中实现的SVPWM模块本身不处理这个它只负责执行给定的Uref指令。5.4 模型优化与代码生成如果你最终的目标是生成C代码并部署到嵌入式处理器如TI C2000, STM32, Infineon AURIX等Simulink的Embedded Coder工具链就派上用场了。子系统封装与接口将完整的SVPWM算法封装成一个Atomic Subsystem并明确定义输入输出端口的数据类型single,uint16,boolean等。去除仿真专用模块确保模型中使用的是支持代码生成的模块Simulink Coder支持库中的模块。避免使用Scope,To Workspace等仅用于仿真的模块。配置求解器与步长设置为离散固定步长模式步长与你的控制周期Ts一致。配置代码生成参数在Model Settings中选择正确的硬件设备配置代码生成目标为ert.tlcEmbedded Coder并优化代码效率如开启内联函数、简化代码等。生成代码与验证点击BuildSimulink会生成完整的C代码和头文件。仔细阅读生成的代码特别是那些涉及三角函数、开方运算的部分看是否被优化成了查表法或近似计算。将生成的代码与手写代码进行功能对比测试软件在环SIL或处理器在环PIL确保一致性。我个人的经验是用Simulink生成SVPWM这类算法代码在正确配置后其可靠性和效率都非常高而且大大降低了手写代码可能出现的低级错误如扇区映射表写错。但前提是你的仿真模型必须经过充分验证并且理解了模型中的每一个环节对应到代码是什么。不能把它当做一个黑盒。