SVPWM算法详解:从原理到Matlab/Simulink工程实现
2026/9/12 15:00:11 网站建设 项目流程

简介:面向电机控制与电力电子领域学习者的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~V6),2 个是零矢量(V0、V7,分别对应下三管全通和上三管全通)。

这 8 个矢量在 αβ 平面上的分布很规则:6 个非零矢量长度相等,都是 2/3 Udc,相邻间隔 60°,把平面切成 6 个扇区;两个零矢量落在原点。这里 Udc 是直流母线电压,系数 2/3 来自 Clark 变换等幅值变换的约定。匀速旋转的参考电压矢量 Uref 扫过这 6 个扇区时,逆变器实际能输出的只有固定方向的离散矢量,所以必须在一个开关周期内做时间平均。

常见做法是每个开关周期 Ts 内只使用“参考矢量所在扇区的两个相邻非零矢量 + 一个零矢量”,通过调节各自的作用时间,让平均效果等于想要的 Uref。这个平均不是电压幅值平均,而是伏秒平衡,这正是后续所有公式的基础。

2.2 伏秒平衡与直流电压利用率差异

设参考矢量位于扇区 I,由 V1(0°)、V2(60°)和零矢量合成,伏秒平衡方程写成:

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β) > 0,B = (√3 * Uα − Uβ) > 0,C = (−√3 * Uα − Uβ) > 0

N = A + 2B + 4C。N 与扇区的映射关系如下:

N扇区
3I(0° ~ 60°)
1II(60° ~ 120°)
5III(120° ~ 180°)
4IV(180° ~ 240°)
6V(240° ~ 300°)
2VI(300° ~ 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α 轴参考电压V100
uBetaβ 轴参考电压V50
Udc直流母线电压V300
Ts开关周期s100e-6
Ta, Tb, Tc三相上桥臂导通时间s0 ~ Ts
sector当前扇区编号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、T2;T1+T2 超周期时压缩到周期内;最后用七段式的时间关系算出三相比较值。需要特别注意的是调制比 m 的分母写作 2/3*Udc,Matlab 里会先算 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.02s,1 个工频周期 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 上,即使有 FPU,atan2 一条库函数也要上百个周期。更重要的是库函数在不同编译器、不同优化等级下的行为可能不同,对实时控制来说,这种不确定性比那几百纳秒更让人难受。

查表法不是单纯为了快,而是把计算变成确定性的:表长固定、插值逻辑固定、执行时间固定,这是它能在电机控制里长期存在的原因。

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 的余数留到下个周期做一阶滤波,比任何高阶补偿都管用。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询