把三相电压型SVPWM整流器从零开始搭到仿真里跑通,是我做双向AC/DC项目时印象最深的一段。第一次接触这东西,觉得坐标变换绕、PI参数不知道从哪下手、SVPWM的扇区判断更像天书;但真把“电压外环+电流内环”的双闭环PID控制逻辑拆清楚之后,后续移植到STM32也就只是照葫芦画瓢。这篇就把我当时从原理到仿真再到移植的全过程整理一下,适合正在做三相PWM整流器仿真、或者打算用STM32做数字电源控制的朋友参考。
1. 三相PWM整流器为什么值得自己搭一遍:从拓扑到工程价值
1.1 它和传统整流方案的根本区别
先聊两句拓扑背景。传统三相不可控整流(二极管)或者相控整流(晶闸管)用得很多,优点是电路简单、成本低,但缺点也明显:网侧电流畸变大、谐波含量高、功率因数低,而且能量只能单向流动。如果你要的东西只是“把电网交流电变成直流电”,那用二极管整流加一个Boost电路也能凑合,但Boost只能单向升压,没法把负载侧的能量回馈到电网。
三相电压型SVPWM整流器(VSR)解决的就是这些问题。主电路是三相全桥,六个开关管(IGBT或MOSFET)每个都反并联一个二极管,交流侧通过三相电感接到电网上,直流侧是滤波电容和负载。通过对六个开关管的开关状态做实时控制,可以让交流侧电流变成非常接近正弦的波形,功率因数可以做到接近1,甚至能控制成超前或者滞后的任意功率因数;直流侧电压也能稳定在设定值附近,动态调节速度远快于相控整流。最关键的是,这种拓扑本身具备能量双向流动能力,非常适合储能变流器、电机回馈制动、有源电力滤波器这些场景。
我当初做这个仿真,目标很直接:交流侧三相380V/50Hz输入,直流侧输出稳定650V,额定功率做到10kW左右,稳态时网侧功率因数0.98以上,负载突变时直流电压跌落在5%以内。控制策略就是题目里说的电压外环加电流内环,双闭环PID(实际上工程里只会用到PI,微分项后面细说)。
1.2 为什么先把仿真跑通再考虑上板
这是个很现实的问题。PWM整流器跟普通DC/DC不一样的地方在于,它是一个强耦合、非线性、三相交互的系统。直接上硬件调试的话,你需要同时面对IGBT驱动、电流采样、电压采样、锁相环、坐标变换、PWM死区、保护逻辑一大堆问题,任何一个环节出错都可能导致炸管或过压。尤其第一版硬件,上电瞬间交流侧冲击电流加直流侧电容充电,很容易超出预期。
在仿真里先把控制策略的逻辑跑通,本质上是把“算法问题”和“硬件问题”分开处理。仿真里没有采样噪声、没有死区、没有电感饱和,你可以专心验证“双闭环的带宽分配对不对”“SVPWM的发波序列对不对”“负载突变时电压环能否拉回来”。这些问题搞定了,再上板,剩下的就是处理工程误差而非控制逻辑错误,难度会降一个量级。我见过不止一个朋友直接拿开环代码上板去调PWM整流器,最后发现电流根本控不住,绕了一大圈才发现是Park变换角度初始化错了——这种问题在仿真里五分钟就能查出来。
2. 八个开关状态如何变成马鞍波:SVPWM调制的数学拆解
2.1 两电平三桥臂的八种开关组合与电压矢量
SVPWM全称是空间矢量脉宽调制,它和传统的SPWM最大的区别是:SPWM是分别对三相正弦波与三角载波比较产生PWM波,而SVPWM直接在复平面上把“期望的交流电压矢量”用八个离散矢量去合成。
两电平三相全桥,每一相上管导通定义为1、下管导通定义为0,那三组开关组合起来一共2^3=8种状态:000、001、010、011、100、101、110、111。其中000和111对应的交流侧三相电压都等于0,叫零矢量;剩下六个非零矢量把复平面均匀分成六个扇区,每个扇区60度。六个非零矢量的幅值都是(2/3)Udc,方向分别指向±α轴方向和60度、120度、180度、240度、300度方向。
这样一来,任意时刻你想要整流器交流侧输出一个幅值和方向都在变化的电压矢量(这个矢量由dq控制器的输出反变换得到),就可以在一个开关周期里用这个矢量所在扇区边上的两个非零矢量各作用一段时间,再用零矢量填补剩余时间,用“伏秒平衡”去等效。这就是SVPWM的本质。
2.2 参考电压矢量的伏秒平衡:扇区判断与作用时间计算
具体算起来,假设参考电压在静止两相坐标系下的分量为Uα、Uβ,目标矢量落在第一扇区(0度到60度之间)。第一扇区相邻的两个基本矢量分别是100(沿α轴方向)和110(沿60度方向)。
设100作用时间为T1,110作用时间为T2,一个开关周期为Ts,直流母线电压为Udc。伏秒平衡方程是:
T1×(2/3)Udc + T2×[(1/3)Udc + j(√3/3)Udc] = Ts(Uα + jUβ)
把实部和虚部分开解:
T2 = √3·Uβ·Ts / Udc
T1 = (3Uα - √3Uβ)·Ts / (2Udc)
零矢量时间就是T0 = Ts - T1 - T2。
这个推导看着有点数学味,但一定要自己推一遍,推完你对SVPWM的理解会完全不一样。至于其他扇区,原理完全一样,只是相邻的两个基本矢量不同。工程实现上,大家普遍的做法是先用三个判断条件得到扇区号N(常见约定是N=A+2B+4C,A、B、C分别由Uβ、√3Uα-Uβ、-√3Uα-Uβ的正负号决定),然后根据扇区号查表选出对应的作用时间和矢量组合。不同教材和TI、ST的例程里N和扇区的映射关系不一定完全一样,关键是把“用相邻矢量合成参考矢量”这套逻辑吃透,具体查表时以你手上的参考代码为准。
2.3 七段式发波:对称调制为什么是默认选择
算出了T1、T2、T0之后,怎么把一个周期的开关动作排布出来,也很有讲究。最常用的是七段式对称调制,以第一扇区为例,开关顺序是:
000 → 100 → 110 → 111 → 110 → 100 → 000
这样一整个周期里,开关动作只有六次,每次变化只有一个桥臂的开关状态翻转,开关损耗小,谐波也相对集中且容易滤波。两端的零矢量000各作用T0/4,中间的零矢量111作用T0/2,中间两个非零矢量各作用T1/2和T2/2,正好凑满一个Ts。
判断你的SVPWM实现是否正确,有个很直观的检查方法:把三相调制波(也就是三相占空比波形)拿出来看,正常应该是马鞍形,而不是正弦形。如果看到的是马鞍波,说明零矢量分配和扇区切换基本对了。这是我在仿真里跑的经典验证手段,比看电流波形更直接。
3. 双闭环PID的分工与整定:电压外环稳母线,电流内环管波形
3.1 dq旋转坐标系下的数学模型与解耦前馈
三相系统直接控制起来很麻烦,因为三相电流彼此耦合。标准的做法是先把三相静止坐标系的电量做Clark变换到两相静止αβ坐标系,再做Park变换到跟随电网角度旋转的dq坐标系。在dq坐标系下,稳态的交流量都变成了直流量,PI控制器才能无静差地跟踪。
三相VSR在dq坐标系下的电压方程可以写成:
L·di_d/dt = e_d - R·i_d - u_d + ωL·i_q
L·di_q/dt = e_q - R·i_q - u_q - ωL·i_d
这里面e_d、e_q是电网电压的dq分量,u_d、u_q是整流器交流侧电压的dq分量,ω是电网角频率,R是线路等效电阻,L是交流侧电感。注意到d轴方程里带着ωL·i_q项,q轴方程里带着ωL·i_d项,这就是交叉耦合。如果不处理,d轴和q轴的电流控制会互相干扰。
工程上处理交叉耦合的标准做法是前馈解耦:控制器输出的u_d、u_q里面主动加上耦合项抵消掉它。常见的表达式:
u_d_ref = -(PI_d的输出) + e_d + ωL·i_q
u_q_ref = -(PI_q的输出) + e_q - ωL·i_d
这样处理后,d、q轴电流环看起来就是两个独立的一阶RL对象,互不干扰。这是整个双闭环控制里面比较容易被忽略、但又特别重要的一个环节。很多朋友仿真波形振荡,第一反应是调PI参数,其实先检查一下有没有做解耦前馈更靠谱。
3.2 电流内环PI设计的带宽思路
电流内环是双闭环里的内环,它负责快速跟踪电流指令,直接决定交流侧电流波形质量。电流环的被控对象简化后是L·di/dt + R·i = u,也就是一阶惯性环节。
设计思路推荐用带宽法。假定开关频率是10kHz,电流环的穿越频率一般取开关频率的1/10到1/5,也就是1kHz到2kHz。被控对象传递函数是1/(Ls+R),PI调节器写成Kp+Ki/s,如果让PI零点对消对象极点,也就是Ki/Kp = R/L,那么校正后的开环传递函数近似变成Kp/(sL),闭环带宽就近似等于Kp/L。因此:
Kp ≈ L × 2π×f_cc
Ki ≈ Kp × R/L
举个例子,我仿真里的电感是5mH,R取0.1Ω左右,电流环带宽按1.5kHz来算的话:Kp ≈ 0.005 × 2π×1500 ≈ 47,Ki ≈ 47 × 0.1/0.005 ≈ 940。注意这个数值是没做标幺化、直接用国际单位算出来的,实际Simulink模型和ST芯片里如果用了标幺值系统,参数数量级会完全不同,直接抄数值没有意义。但带宽法的思路是通用的:内环带宽定了,Kp的数量级就定了,然后进仿真微调。
我在实际调试时发现,仿真里Kp往下调个30%到50%,波形通常会比理论值更稳,因为数字控制有采样延迟和零阶保持器,相位裕度天然比连续系统小。
3.3 电压外环PI的整定与防过冲
电压外环的目标很简单:稳住直流母线电压Udc。它的输出是d轴电流的指令id_ref(有功电流指令),输入是母线电压误差。这里要注意,外环的输出经过内环再作用到系统上,因此外环带宽必须明显低于内环,一般取内环带宽的1/10以内。内环如果1.5kHz,外环做150Hz都算快的,实际我的模型里只用到几十Hz,因为直流电容本身惯性很大,母线电压的调节本来就该是“慢工出细活”,调太快反而容易震荡。
电压环的整定逻辑也有物理依据。不考虑损耗时,直流侧功率等于交流侧有功功率:
C·Udc·dUdc/dt = (3/2)·e_d·i_d - u_dc·i_load
从这个小信号关系可以看出,Udc对id的传递函数近似是一个积分器加一个与负载电流相关的扰动项。所以电压环PI的Ki要承担主要调节作用,Kp负责提供阻尼。参数取值同样建议从带宽法出发,再在仿真里逐步加大Ki直到负载突变时电压恢复时间符合要求。
电压环最容易出现的问题是启动过冲。母线电容在低压时充电很快,如果给定直接给650V阶跃,PI输出的电流指令会瞬间顶到限幅值,电容一路猛充到700多伏才被电压环压回来。我的处理方案是三管齐下:第一,电压给定用软启动斜坡,从0在0.5秒内缓升到650V;第二,电压环输出加限幅,把id_ref限制在额定电流的1.2倍以内;第三,PI加积分限幅和抗积分饱和,误差大时不无脑积分。做完这三步,启动过冲基本能压到10V以内。
3.4 为何实际工程里大家只用PI,而不是完整PID
标题里写的是“双闭环PID”,但实际做电机控制、PWM整流器这些数字电源控制,电流环和电压环几乎没人用微分项。原因很简单:微分项对误差变化率敏感,而采样电流和电压本身带开关噪声和量化噪声,微分会把噪声放大得很厉害。你辛辛苦苦调出来的Kd,最终往往是在放大干扰,而不是改善动态。
数字控制里常规做法就是PI加合适的抗饱和处理。如果觉得动态响应不够快,先检查带宽分配和前馈解耦,而不是加D。我对这个问题的理解是:微分在连续模拟控制器里或许还有意义,但在10kHz采样、带AD量化的数字系统里,用D的收益远小于它带来的麻烦。
4. 仿真模型搭建顺序与波形调试实录
4.1 主电路与控制器的参数设定
仿真平台我用的MATLAB/Simulink,整流器主电路用Simscape模块搭,控制部分用Matlab Function和基本运算模块写。核心参数如下表:
| 参数 | 设定值 |
|---|---|
| 交流线电压有效值 | 380V / 50Hz |
| 交流侧电感 L | 5mH |
| 线路等效电阻 R | 0.1Ω |
| 直流母线电压给定 | 650V |
| 直流母线电容 C | 3300μF |
| 额定功率 / 负载电阻 | 10kW / 约42Ω |
| 开关频率 | 10kHz |
| 电流环带宽设计值 | 1.5kHz |
| 电压环带宽设计值 | 50Hz左右 |
这套参数是典型的“10kW工业级”配置。电感在额定电流下的电感压降约2π×50×5mH×15A≈23V,占相电压的比例很小,SVPWM的线性调制范围足够。直流电容按经验取每千瓦几百微法,3300μF对应约3.3μF/kW,算比较标准的容量。负载电阻42Ω对应10kW损耗,母线电压650V下电流约15.5A。
4.2 调试顺序:先开环发波,再关电流环,最后合上电压环
这个顺序是我强烈建议遵守的。顺序错了,波形乱了都不知道是哪一层的问题。
第一步,先把SVPWM开环跑起来。给定一个固定的调制比(比如0.6),不加任何闭环,直接看交流侧三相电流和直流母线电压能不能建立起来。这一阶段如果调制波是马鞍形、母线电容能充到某个稳定电压、三相电流没有明显畸变,说明SVPWM算法本身没问题。
第二步,把电流内环单独闭环。给一个固定的id_ref(比如5A),iq_ref给0,看实际电流能不能快速跟踪。这个阶段重点检查d轴电流是否有静差、q轴电流是否被压住、电流波形是否正弦。我在这里踩过最典型的坑是Park变换角度没对齐,导致id和iq根本解不开,后来用锁相环输出角度做Park变换的θ才正常。
第三步,才把电压外环合上。此时电流内环已经稳定,外环只管输出id_ref。注意把电压给定设成斜坡,观察母线电压爬升过程,以及稳态时功率因数是不是接近1。
4.3 三个典型异常波形与对应处理
仿真调试过程中,几乎不可避免会碰到下面三类波形异常,我把当时的排查思路和解决办法整理成表:
| 异常现象 | 可能原因 | 处理方式 |
|---|---|---|
| 启动时母线电压冲到730V以上 | 电压环积分饱和、给定阶跃过大 | 电压给定斜坡、外环输出限幅、积分限幅 |
| 交流电流低频振荡、幅值不稳 | 电流环带宽过高、解耦前馈缺失、Park角抖动 | 降内环Kp、补全ωL前馈、检查锁相环带宽 |
| 突加负载后母线电压跌落超过5% | 电压环带宽太低、id_ref限幅太小 | 提高电压环Ki、放宽电流指令限幅、适当加大直流电容 |
第一种情况最常见,也最好解决,记住“软启动+输出限幅+抗饱和”三件套。第二种情况就要耐心排查了,我一般先用示波器把Park变换出来的角θ和电网电压波形叠在一起看,确认锁相环没失锁;再检查前馈项符号是否写反。第三种情况是外环响应速度的问题,适当加大Ki和限幅值,让内环在负载突变时能迅速抽出更多有功电流,电压跌落就能拉回来。
稳态验证功率因数也很简单:把A相电网电压和A相电流放到同一个Scope里看,稳态时电流过零点应该和电压过零点几乎重合;更进一步,可以在模型里加一个功率计算模块,直接读稳态有功、无功功率,PF算出来通常都在0.99附近。
5. 从Simulink搬到STM32时绕不开的那些坑
5.1 离散化与标幺化:仿真里的增益不等于板上的实际值
仿真跑通了,不代表STM32代码写出来就是按仿真参数的。首先是离散化问题。PI调节器在仿真里是连续模块,上板必须写成离散形式。我用的是增量式PI加积分限幅,代码大概长这样:
// 增量式PI:输出限幅与积分限幅都在内部处理 float pi_update(float err, float *integ, float kp, float ki, float dt, float lo, float hi) { *integ += ki * err * dt; if (*integ > hi) *integ = hi; if (*integ < lo) *integ = lo; float out = kp * err + *integ; if (out > hi) out = hi; if (out < lo) out = lo; return out; }第二个问题是标幺化。仿真里电压环PI的输出是“安培数”,那个安培数直接作为id_ref传给电流环;但在STM32里,ADC采回来的电流是某个量程范围内的数字量,或者你把它转成了标幺值,PI参数的物理意义就变了。我的做法是给整个系统定一套基准,电压基准取650V,电流基准取30A(约2倍额定电流),把所有反馈量和指令量都归一化到0到1之间,PI参数在标幺值系统里重新整定。这样做的最大好处是代码里的参数不再受具体硬件量程影响,而且和仿真里的标幺模型参数可以直接对照着调。
5.2 中心对齐PWM与采样点同步
STM32做三相PWM整流器,最顺手的配置是高级定时器输出三对互补PWM,工作在中心对齐模式(up-down counting)。中心对齐模式天然对应SVPWM的对称七段式发波,比较寄存器更新一次,三个桥臂的占空比同时在周期中心对称变化,谐波特性最好。
电流采样点的选择是这次移植里面最关键的小细节。正确的做法是在定时器计数到中心点时触发ADC采样(也就是三角载波顶点处),因为这正好是所有开关状态切换的中点,离开关动作时刻最远,开关噪声最小;同时,这个时刻的相电流近似等于一个开关周期内的平均电流,正好是电流环建模时用到的电流。
我在STM32里是用定时器的更新事件和比较捕获通道配合触发ADC,在ADC完成中断里执行完整的控制算法:先读三相电流、母线电压、电网电压,然后做Clark/Park变换,再跑两个PI调节器,最后算出三个比较值写入定时器比较寄存器。这一套计算必须在下一个载波周期开始前完成,10kHz开关频率下时间窗口只有100μs,用STM32的浮点运算完全够用。
5.3 死区效应与保护逻辑的工程补偿
仿真里的开关管是理想的,没有死区;真实IGBT/MOSFET必须加死区,否则直通炸管。死区会带来两个问题:一是实际输出电压比指令电压偏低,相当于在控制环路里引入了一个扰动;二是交流电流过零点附近,死区造成的电压误差会显现成电流波形的“平台”,严重时会导致电流畸变。
处理死区影响,我优先提醒的还是安全:第一步先把死区配置正确,用定时器的死区寄存器,典型IGBT按1μs到2μs起步,量测温升后再考虑减小。第二步才是性能补偿,用电流方向判断死区造成的电压误差方向,在占空比指令上做前馈修正。补偿量不大,但在单位功率因数控制下能让电流过零波形好看很多。
保护逻辑在Simulink里可以不写,上板不能省。至少要有:母线过压保护、交流过流保护、驱动故障封锁。过流保护我建议做成硬件比较器直接封锁PWM输出的方案,软件里再保留一级过压和过流阈值判断,双保险。仿真可以忽略这些,硬件上少了哪一个都是钱和时间的代价。
最后再分享一个小经验:仿真里调好的PI参数,上板之后基本会震荡,不是因为算法错了,而是采样延时、计算延时、标幺值基准、死区这些因素综合作用的结果。我的习惯是上板时把电流环Kp降到仿真值的一半先跑起来,确认系统稳定后再慢慢往上加。这个过程很枯燥,但比一上来就看到电流啸叫、母线电压乱跳要好受得多。三相电压型SVPWM整流器这套东西,说到底就是“先把SVPWM发波弄对,再谈双环稳定”,这个顺序千万别反过来。