
简介面向电机控制与电力电子领域学习者的SVPWM空间矢量脉宽调制MATLAB/Simulink仿真项目作者自行搭建了五段式与七段式发波模块可直观对比两种调制策略的波形质量、谐波含量与实现复杂度适合从原理到仿真逐步进阶的初学者及工程师。压缩包共32个文件包含Simulink模型.slx、C源码.c/.h、MATLAB数据.mat以及mexw64等编译辅助文件整体仅250KB轻量易部署slx为主仿真模型c/h对应算法与封装接口mat可用于结果分析。已有365人学习下载配套资源结构清晰便于按模型—源码—数据分层查阅。通过运行模型可观察逆变器开关状态与电机端电压波形比较五段与七段发波的谐波抑制效果同时理解SVPWM扇区判断、矢量作用时间计算等关键步骤为后续算法优化或硬件在环验证打下基础。1. SVPWM 不是“调制”而是用离散开关状态拼出连续旋转电压SVPWM 的全称是 Space Vector Pulse Width Modulation在电机 FOC、伺服驱动和并网逆变器里几乎绕不开。它和 SPWM 最大的区别是SPWM 一相一相地比较正弦波与载波SVPWM 则把三相参考电压看成一个整体空间矢量再用逆变器有限的 8 个开关状态在时间上平均让电机端电压以“伏秒平衡”的方式逼近圆形旋转磁链。我第一次在 Matlab 命令行里跑通七段式 SVPWM 时看到相电流自然变成平滑正弦才明白所谓“调制”不需要造出正弦只需要把离散状态拼得足够精细。这篇会从扇区判断、作用时间推导讲到可跑的 Matlab 函数、Simulink 参数整定最后给一个查表优化的工程做法。适合刚开始调 FOC 但没系统推过 SVPWM 的同学也适合仿真波形总出毛刺、想深查原因的开发者。2. svpwm 算法原理及详解8 个开关状态如何合成任意电压矢量2.1 六个开关、八个状态与矢量空间布局三相两电平逆变器有 ABC 三个桥臂每个桥臂上下两个开关管互补导通。理论上 2^3 8 种组合其中 6 个是非零电压矢量V1~V62 个是零矢量V0、V7分别对应下三管全通和上三管全通。这 8 个矢量在 αβ 平面上的分布很规则6 个非零矢量长度相等都是 2/3 Udc相邻间隔 60°把平面切成 6 个扇区两个零矢量落在原点。这里 Udc 是直流母线电压系数 2/3 来自 Clark 变换等幅值变换的约定。匀速旋转的参考电压矢量 Uref 扫过这 6 个扇区时逆变器实际能输出的只有固定方向的离散矢量所以必须在一个开关周期内做时间平均。常见做法是每个开关周期 Ts 内只使用“参考矢量所在扇区的两个相邻非零矢量 一个零矢量”通过调节各自的作用时间让平均效果等于想要的 Uref。这个平均不是电压幅值平均而是伏秒平衡这正是后续所有公式的基础。2.2 伏秒平衡与直流电压利用率差异设参考矢量位于扇区 I由 V10°、V260°和零矢量合成伏秒平衡方程写成Uref * Ts V1 * T1 V2 * T2 V0 * T0其中 T1 T2 T0 Ts。把 V1、V2 的幅值 2/3 Udc 代入Uref 在该扇区内能合成的最大轨迹是相邻矢量中点连成的正六边形内切圆半径为 Udc/√3。这个值直接决定了 SVPWM 的直流电压利用率线性区最大相电压幅值是 Udc/√3而 SPWM 只有 Udc/2算下来 SVPWM 大约高 15.4%。也就是说同样的母线电压SVPWM 能让电机跑更高转速或者在同样转速下母线电容可以选得更小。代价是算法复杂度明显高于 SPWM每个开关周期都要判断扇区、计算作用时间、生成切换序列。2.3 扇区判断的工程方法扇区判断常见有两种思路。第一种是纯几何法算 atan2(Uβ, Uα) 得到角度再除以 60° 取整。好处是直观坏处是 atan2 在 ±π 附近有跳变浮点误差容易让扇区在边界上来回抖。第二种是符号判断法只用三次比较是 TI 和大多数开源库的标准做法A sign(Uβ) 0B (√3 * Uα − Uβ) 0C (−√3 * Uα − Uβ) 0N A 2B 4C。N 与扇区的映射关系如下N扇区3I0° ~ 60°1II60° ~ 120°5III120° ~ 180°4IV180° ~ 240°6V240° ~ 300°2VI300° ~ 360°这套映射我建议直接背下来写代码时反复出现。如果担心符号判断在边界条件 Uβ 恰好等于 0 时抖动可以给比较结果加一个很小的滞环比如阈值取 1e-6。工程上这个滞环对波形质量影响很小但能避免扇区高频切换。2.4 作用时间公式与七段式切换顺序确定扇区后剩下的是计算相邻矢量的作用时间。设 Uref 的幅值为 |Uref|调制比 m |Uref| / (2/3 Udc)扇区内相对角度为 θloc范围 0 ~ 60°则T1 m * Ts * sin(60° − θloc) T2 m * Ts * sin(θloc) T0 Ts − T1 − T2如果 T1 T2 Ts说明参考矢量已经超出线性区必须做等比例压缩。这里有一个常见的判断标准线性区最大调制比 m_max √3/2 ≈ 0.866超过这个值波形开始削顶。七段式是工程上最常用的切换序列它把零矢量分成两段一段放在周期开头、一段放在周期末尾T1、T2 各拆成两半围绕中心对称排布。相比五段式七段式开关次数多一倍但电流谐波更小死区影响也更对称是电机控制的首选。切换顺序按扇区不同而轮换具体从哪一组非零矢量开始决定了每个周期内每相开关动作几次。这部分直接决定了最终三相上桥臂的比较值不同资料里给出的扇区与比较值映射表可能不同但只要保证相邻周期切换不跳变、每相只在必要时刻动作就是正确的。3. 用 Matlab 手写一个 svpwm 调制函数从扇区判断到切换点3.1 函数接口与输入输出定义先定义函数签名让输入输出都明确。输入是 αβ 轴参考电压、母线电压和开关周期输出是三相上桥臂的导通时间比较值。参数含义单位示例值uAlphaα 轴参考电压V100uBetaβ 轴参考电压V50Udc直流母线电压V300Ts开关周期s100e-6Ta, Tb, Tc三相上桥臂导通时间s0 ~ Tssector当前扇区编号1 ~ 63这里 Ta、Tb、Tc 不是占空比是导通时间。后续和三角载波比较时再除以 Ts 换成 0~1。把时间量而不是占空比作为输出能直接适配 DSP 的比较寄存器也能避免在传递过程中反复做除法损失精度。3.2 核心代码扇区判断、T1/T2 与过调制处理下面这个函数可以直接存成 svpwm_calc.m 使用。function [Ta,Tb,Tc,sector] svpwm_calc(uAlpha,uBeta,Udc,Ts) % 七段式 SVPWM输入 αβ 轴电压输出三相上桥臂导通时间 Uref sqrt(uAlpha^2 uBeta^2); % 参考矢量幅值 theta atan2(uBeta, uAlpha); % 参考矢量角度 [-pi, pi] if theta 0 theta theta 2*pi; % 转到 [0, 2pi) end sector floor(theta / (pi/3)) 1; % 扇区 1~6 thetaLoc theta - (sector-1)*pi/3; % 扇区内相对角度 [0, pi/3) m Uref / (2/3*Udc); % 调制比线性区上限 0.866 T1 m * Ts * sin(pi/3 - thetaLoc); % 相邻矢量 1 的作用时间 T2 m * Ts * sin(thetaLoc); % 相邻矢量 2 的作用时间 if T1 T2 Ts % 过调制等比例压缩 scale Ts / (T1 T2); T1 T1 * scale; T2 T2 * scale; end T0 Ts - T1 - T2; Ta T0 / 4; % 七段式零矢量分两段 Tb Ta T1 / 2; Tc Tb T2 / 2; switch sector % 按扇区分配三相比较值 case 1, tmp [Ta Tb Tc]; case 2, tmp [Tb Ta Tc]; case 3, tmp [Tc Ta Tb]; case 4, tmp [Tc Tb Ta]; case 5, tmp [Tb Tc Ta]; case 6, tmp [Ta Tc Tb]; end Ta tmp(1); Tb tmp(2); Tc tmp(3); end逻辑上分四步先由 Uα、Uβ 求幅值和角度确定扇区和扇区内相对角度再由伏秒平衡算 T1、T2T1T2 超周期时压缩到周期内最后用七段式的时间关系算出三相比较值。需要特别注意的是调制比 m 的分母写作 2/3*UdcMatlab 里会先算 2/3 再乘 Udc和手写公式一致。这里的 atan2 写法在工程实现里不算高效但胜在逻辑清晰。如果后续要把代码搬进 MCU可以换成 2.3 节的符号判断法或者用第五章的查表法替换三角函数。3.3 用 RL 负载做最小验证函数写完先别急着上 Simulink用一个纯 M 脚本做最小验证给固定幅值和频率的旋转参考电压跑几千个开关周期把相电流算出来。Udc 300; Ts 100e-6; f 50; % 10 kHz 开关频率 UrefMag 0.8 * Udc / sqrt(3); % 线性区 80% samples 2000; % 0.02s1 个工频周期 t (0:samples-1) * Ts; cmpA zeros(samples,1); cmpB cmpA; cmpC cmpA; for k 1:samples theta 2*pi*f*t(k); [cmpA(k), cmpB(k), cmpC(k)] svpwm_calc( ... UrefMag*cos(theta), UrefMag*sin(theta), Udc, Ts); end R 0.5; L 5e-3; % 阻感负载 ia zeros(samples,1); ib ia; ic ia; for k 2:samples va (2*cmpA(k-1)/Ts - 1) * Udc/2; % 相对母线中点的相电压 vb (2*cmpB(k-1)/Ts - 1) * Udc/2; vc (2*cmpC(k-1)/Ts - 1) * Udc/2; vn (va vb vc) / 3; % 三相无中线去掉共模 ia(k) ia(k-1) ((va - vn) - R*ia(k-1)) / L * Ts; ib(k) ib(k-1) ((vb - vn) - R*ib(k-1)) / L * Ts; ic(k) ic(k-1) ((vc - vn) - R*ic(k-1)) / L * Ts; end figure; subplot(2,1,1); plot(t, cmpA); title(A 相上桥臂导通时间); subplot(2,1,2); plot(t, ia, t, ib, t, ic); title(三相电流); legend(ia,ib,ic);代码里将导通时间换算成相电压时用了 2cmp/Ts - 1 再做 Udc/2 缩放本质是把 0~Ts 的导通时间映射到 −Udc/2 ~ Udc/2 的相电压区间。源相电压幅值取 0.8 * Udc/√3确保调制比 m ≈ 0.80.866 0.693远离过调制区波形不会削顶。正确的结果是A 相导通时间曲线呈现马鞍形中间隆两边垂而不是正弦三相电流基本对称ia、ib、ic 相位互差 120°幅值在 100A 量级取决于 R 和 UrefMag 的比值且叠有开关频率纹波。3.4 波形正常时该看到什么如果看到随扇区周期变化的马鞍波说明扇区判断和作用时间计算没有大问题。如果马鞍波变成明显正弦波大概率是 T1/T2 的分配表用错把七段式拼成了伪 SPWM。如果相电流纹波异常大优先怀疑载波频率太低或负载电感太小10 kHz 对 5mH 电感来说纹波通常已经很小。如果电流不对称检查三相比较值分配是否正确特别是扇区 4、5、6 有没有搞反方向。4. Simulink 里的 svpwm 参数整定开关频率、死区时间与谐波验证4.1 两种建模路径在 Simulink 里搭 SVPWM 系统常见路径有两种。第一种是完全用 Simscape Electrical 的 Universal Bridge 加 PWM 脉冲整个模型看起来很接近实际主电路但门极信号要给足 6 路死区也要自己加模型稍重。第二种是把第 3 章的 Matlab 函数包进 MATLAB Function 块输出三相上桥臂导通时间再用三角载波比较生成门极信号阻感负载或电机部分可以用 Simscape 元件也可以继续用受控电压源搭简化模型。我一般优先选第二种。好处是算法部分和波形验证解耦SVPWM 函数已经用纯 M 脚本验证过进 Simulink 后只排查接口和时序问题问题面小很多。4.2 必调参数表以下参数是 Simulink 仿真中最容易踩坑的四个先用表格列出常用范围和影响。参数常用范围对波形的影响调试建议开关频率 fsw5 kHz ~ 20 kHz越高电流纹波越小开关损耗越大先定 10 kHz纹波和仿真速度折中死区时间 td1 μs ~ 3 μs过零点附近电流畸变、产生低次谐波先设 0 跑通模型再加死区直流母线 Udc按电机额定反电动势留 15% 裕量决定最大可输出相电压幅值 Udc/√3不要卡在边界给 PI 留调节空间仿真步长≤ Ts/100看死区时 ≤ td/10步长太大 PWM 脉冲丢失、FFT 失真定步长 ode4步长取 1e-7s 量级注意开关频率和仿真步长的关系一个 10 kHz 的 PWM 周期是 100 μs如果步长取 10 μs一个周期只有 10 个点脉冲宽度解析得很粗想看清 2 μs 死区就必须要亚微秒级步长。Simulink 里这个代价很大所以纯算法验证用第 3 章的 M 脚本Simulink 只跑几毫秒看关键波形是更实际的做法。4.3 在 Simulink 中接入 SVPWM 的门极生成逻辑MATLAB Function 块里放一个函数输入是 svpwm_calc 算出的导通时间输出是三相上桥臂的开关信号function [swA, swB, swC] gate_gen(cmpA, cmpB, cmpC, Ts, carrier) % cmp 单位为秒carrier 为 0~1 三角载波 dutyA cmpA / Ts; % 换算为 0~1 占空比 dutyB cmpB / Ts; dutyC cmpC / Ts; swA double(carrier dutyA); % 载波低于占空比时输出高 swB double(carrier dutyB); swC double(carrier dutyC); end这段代码做的事情是把开关周期内的导通时间转成占空比再用三角载波转换成开关信号。carrier 可以用 Simulink 的 Repeating Sequence 生成也可以自己写一个递增计数器再映射到 0~1。上桥臂的互补信号由 Not 逻辑取反后再加 Transport Delay 模拟死区延迟时间就是前面参数表里的 td。实际运行时需要注意一个细节载波频率必须和 svpwm_calc 里的 Ts 严格一致否则每个周期的比较值会对不上号。常见错误是两个地方分别定义了 Ts改了一处忘记改另一处结果电流波形出现周期性抖动。4.4 波形验证与谐波分析Simulink 模型跑完后先看三相电流是否对称、纹波频率是否为开关频率及其边带。然后用 Powergui 的 FFT Analysis 或者直接对电流数据做 spectrum观察谐波分布理想情况下基波占绝对主导高次谐波集中在开关频率附近10 kHz、20 kHz低次谐波5、7、11 次应该很小。如果低次谐波明显偏大优先查三种情况。一是扇区边界毛刺表现为电流波形在扇区交界处有微小台阶常见原因是 atan2 角度跳变导致扇区判断抖动可以换用符号判断法或对扇区结果做滞环。二是死区效应加死区后电流过零点附近明显变平这正是死区补偿要解决的问题。三是过调制当调制比超过 0.866相电压波形开始削顶电流畸变会迅速增大这种谐波是原理性存在不是参数能调掉的。5. 进阶用查表法替掉三角函数让 svpwm 在仿真和 MCU 上都跑得更稳5.1 三角函数在 svpwm 里的实际开销第 3 章的函数每个开关周期调用一次 atan2 和两次 sin。在 Matlab 仿真里这个开销不算大但同样的代码搬到 STM32、TMS320F28335 这类 MCU 上即使有 FPUatan2 一条库函数也要上百个周期。更重要的是库函数在不同编译器、不同优化等级下的行为可能不同对实时控制来说这种不确定性比那几百纳秒更让人难受。查表法不是单纯为了快而是把计算变成确定性的表长固定、插值逻辑固定、执行时间固定这是它能在电机控制里长期存在的原因。5.2 预计算正弦表加线性插值先预计算一张正弦表再写一个快速查询函数N 4096; % 表长越大越准 sinTable sin(2*pi*(0:N-1)/N); % 预计算只在初始化时执行一次 function s fast_sin(x, sinTable) % x in [0, 2pi) N length(sinTable); pos x / (2*pi) * N; % 映射到表索引浮点 idx floor(pos) 1; % 整数索引 frac pos - floor(pos); % 小数部分用于插值 idxNext mod(idx, N) 1; % 绕回表头 s sinTable(idx) ... (sinTable(idxNext) - sinTable(idx)) * frac; end查表后svpwm_calc 里的 sin(pi/3 - thetaLoc) 和 sin(thetaLoc) 都改成查 fast_sin扇区判断如果继续用 atan2也可以换成把角度映射到表索引后直接查。这样整个调制过程只剩乘法和加法。5.3 表长选择与误差对比线性插值的理论误差和表长的平方成反比实际选择看控制环需要多高的电压精度表长最大相位误差量级适用场景256约 1e-3对谐波不敏感的简易开环调速1024约 1e-5电流环调试初期4096约 1e-6高精度 FOC、并网逆变器1024 点对大多数电机控制已经够用4096 点适合强迫症。验证方法很简单把同一组 uAlpha、uBeta 分别送进原函数和查表函数跑满一个工频周期对比输出的 Ta、Tb、Tc 最大差值不超过一个载波周期的 0.1% 就可以放心用。5.4 扇区边界的滞环处理扇区判断如果还是用 atan2 出的角度做 floor角度跨 ±π 时扇区会从 6 跳到 1。此时不要立即切换扇区而是把上一周期的扇区拿出来比较如果跳变超过 1先按原扇区多走一个周期下一拍再切。这个滞环逻辑对波形的影响几乎不可见但能省掉排查边界毛刺的大量时间。扇区切换点上同步更新查表索引、把 T1/T2 的余数留到下个周期做一阶滤波比任何高阶补偿都管用。本文还有配套的精品资源点击获取