SVPWM过调制:提升母线电压利用率的关键技术
FOC电机控制做到一定程度大家都会碰到一个绕不开的问题母线电压利用率。明明电池电压或者母线电压摆在那里电机高速的时候就是感觉“使不上劲”转速上不去力矩又掉得厉害。有人第一反应是换更高电压的平台有人怀疑是不是电流环没调好但实际上很多情况下问题出在SVPWM的调制策略上——你还在用线性区的调制方式白白浪费了一截母线电压。这篇文章就把SVPWM过调制这件事讲透从原理到扇区判定、作用时间推导再到工程上怎么用代码实现最后聊聊我实际调试中踩过的坑希望能给正在搞FOC、搞PMSM、搞BLDC无感控制的朋友一些参考。1. 为什么要过调制SVPWM的局限与突破1.1 FOC控制系统里SVPWM到底在扮演什么角色FOC矢量控制的最终目的是通过控制定子电流的d-q轴分量实现对电机转矩和磁链的解耦控制。但电流环输出的其实是两个电压给定值U_d_ref和U_q_ref这两个是旋转坐标系下的量。要对电机做功必须把它们变换到静止坐标系下变成U_alpha和U_beta然后再通过PWM波的形式施加到三相逆变器的六个开关管上。SVPWM就是干这个活的——它把抽象的电压矢量变成实际的开关动作序列。我经常跟刚入门的朋友打比方FOC的电流环就像是你大脑决定“我要往哪个方向走多少力”而SVPWM就是你的双腿负责真正执行这个指令。如果腿的爆发力不够大脑想得再好跑起来还是慢半拍。SVPWM的“爆发力”上限就是它能在不严重畸变的前提下输出多大的基波电压。1.2 正弦矢量调制的瓶颈母线电压利用率传统正弦PWMSPWM调制方式下相电压的峰值最大只能到母线电压的一半也就是U_dc/2。换算成线电压有效值大约是0.612倍母线电压。而SVPWM的线性调制区相电压峰值可以做到U_dc/√3母线电压利用率比SPWM提升了约15.5%。这就是为什么FOC系统普遍用SVPWM而不是SPWM。但即便是SVPWM它的线性调制区也有天花板。这个天花板对应的是调制比M 1有的资料里也写作调制比m √3·U_ref / U_dc此时的电压矢量轨迹是一个正六边形的内切圆。也就是说电压矢量只能在六边形内切圆内保持“圆整”的输出一旦U_alpha和U_beta合成出来的参考电压矢量半径超过这个圆逆变器就无法严格输出一个旋转的圆形磁链轨迹了。那超过之后怎么办两个选择一是硬限幅把超出部分直接截断虽然系统不会崩但电压输出会失真相当于“削顶”二是用过调制技术在保持基本控制的前提下有策略地“放大”电压输出通过牺牲一部分波形质量来换取更高的基波电压。后者就是本文要讲的SVPWM过调制。这里要特别强调一个概念过调制不是“超频”也不是把PWM频率往死里提。它是调整调制策略本身在电压矢量超出线性区之后仍然让逆变器输出尽可能接近我们希望施加的基波电压。换句话说过调制是“策略性的失真”而非“无脑的加大占空比”。2. SVPWM基础原理与扇区判断2.1 空间矢量合成的本质从三相到两相再到开关序列先简单回顾一下SVPWM的原理因为过调制完全建立在它之上基础不牢的话后面很容易乱。三相逆变器有六个开关管上下桥臂互补导通所以实际上只有8种开关状态6个非零矢量V1~V6和2个零矢量V0、V7。这6个非零矢量在复平面上把360°分成6个60°的扇区相邻两个非零矢量之间可以线性合成任意方向的电压矢量这就是“空间矢量PWM”名字的由来。要把一个给定的参考电压矢量V_ref由U_alpha和U_beta合成变成实际的开关序列分三步走第一步判断V_ref在哪个扇区。这一步通常基于U_beta、U_alpha、√3·U_alpha等量的正负关系来判断不需要查表几条if语句就能搞定。第二步计算相邻两个非零矢量的作用时间T1和T2以及零矢量的作用时间T0。这里就涉及到很多教程里反复出现的“中间变量X、Y、Z”了网上到处能搜到公式但很少有人讲清楚为什么。第三步根据扇区把T1、T2、T0分配到不同的比较寄存器上生成三相互补的PWM波形。2.2 扇区判定后两个非零电压矢量作用时间的中间变量推导这个“公共量中间变量”的推导过程是很多初学者卡壳的地方。网上直接丢一个公式表给你X √3·U_beta·Ts / U_dcY (3/2·U_alpha √3/2·U_beta)·Ts / U_dcZ (−3/2·U_alpha √3/2·U_beta)·Ts / U_dc。然后告诉你不同扇区T1、T2由X、Y、Z组合而成。但为什么是这个式子的组合我推导一遍。先把参考电压矢量V_ref用alpha-beta坐标系表示假设它落在第I扇区0°~60°那么它在V1方向alpha轴上的分量为U_alpha在V2方向即60°方向上的分量可以由U_beta和U_alpha换算出来。此时根据电压-时间平衡原理V_ref·Ts V1·T1 V2·T2。注意V1的方向是alpha轴幅值大小为2/3·U_dc这是逆变器非零矢量的幅值由等幅值Clark变换决定V2方向是60°方向。将V_ref分解到这两个方向沿V1方向分量U_alpha T1/Ts · 2/3·U_dc T2/Ts · 2/3·U_dc · cos60°沿V2垂直方向分量U_beta T2/Ts · 2/3·U_dc · sin60°把cos60°0.5sin60°√3/2代入整理后可以得到T1 (3/2·U_alpha − √3/2·U_beta)·Ts / U_dcT2 √3·U_beta·Ts / U_dc看到了吗这两个式子其实和上面那个X、Y、Z公式是直接对应的。对于不同扇区只是把参考矢量投影到该扇区“自己的两个相邻矢量方向”上所以形式上就是X、Y、Z在不同扇区的不同组合。我建议你们别死记公式表而是亲手把6个扇区各投影一遍算完之后你会发现所谓的X、Y、Z本质上就是三个预先算好的投影分量T1和T2只是从这三个量里“取两个来用”。搞懂这一步后面理解过调制算法中电压矢量的调整逻辑就容易得多。顺便提一句很多STM32的FOC代码里SVPWM实现为了省时间会直接把U_alpha和U_beta缩放到比较寄存器周期值域内上面的Ts/U_dc就直接变成一个缩放系数这就涉及到标幺化的问题后面讲代码时再细说。3. 过调制原理两个区间两套策略3.1 调制比这个概念先对齐一下讲到过调制绕不开调制比M。不同文献里调制比的定义略有区别我先统一本文的口径M π·U_ref / (3·U_dc)有些资料用M √3·U_ref / U_dc两者相差约0.785看文献时注意别搞混。我用的是更常见的定义线性调制区上限M 1对应的电压幅值等于SVPWM线性区电压的上限也就是U_lin_max U_dc/√3。之所以要定义调制比是因为过调制算法的核心输入就是它。当M ≤ 1时在六边形内切圆内部调制是线性的这是常规SVPWM区域。当M 1时参考电压矢量幅值已经超出了内切圆半径逆变器拥堵在六边形边界上必须对参考电压做“修正”才能继续生成PWM。过调制就是研究这种修正策略的。工程上M的值从1往上可以一直增加到约1.1027对应六边形的顶点幅值2/3·U_dc即相电压基波峰值可以做到0.6366·U_dc比线性区的0.577·U_dc提升了约10%。实际应用中我们通常会把这个过程分成两个区间分别用不同的策略来处理。3.2 过调制区间一从内切圆到六边形边界的平滑过渡过调制区间一对应的调制比范围大约是1 ≤ M ≤ 1.05有些文献里给的界线是M ≤ 0.952视不同归一化方式而定。这个区间里参考电压矢量的幅值超过了内切圆但还是落在六边形内部。区间一的修正策略很直观将参考电压矢量轨迹中超出六边形边界的部分沿着半径方向“拉回”到六边形边界上而未超出的部分保持不动。这个修正后的轨迹既有一部分跟踪原参考圆又有一部分贴着六边形边界走像一个“被六边形框住的圆”。为什么区间一用这种“局部修正”而不是直接放大因为M在1附近时参考电压矢量超出边界的角度范围比较小多数角度仍在六边形内部此时局部修正引起的波形失真很小基波电压提升的效率很高。具体实现时要计算参考电压矢量与六边形边界相交的临界角度然后在修正轨迹和原始轨迹之间切换。这个临界角度的计算不需要特别复杂的数学结合当前扇区的边界直线方程直接代入U_alpha、U_beta解出来就行。工程实现时也可以查表或做近似处理优先保证实时性。3.3 过调制区间二大调制比下的幅值-相位折衷当M超过区间一的上限就在过调制区间二范围大约是M ≥ 1.05直到最大约1.1027。这个区间的特点是参考电压矢量轨迹已经大幅超出六边形如果还用区间一的“拉回”策略修正后的轨迹会严重偏离圆形磁链轨迹导致电流畸变和低频谐波明显增大。区间二的思路是“幅值-相位折衷”。修正后的电压矢量轨迹不再试图跟随原始参考圆而是让实际输出的电压矢量沿六边形边界走同时调整保持角holding angle也叫保持角αh——即在扇区起始处把电压矢量“压”在扇区起始边界一个角度范围内然后再切换到沿六边形边界滑动。实质是输出电压矢量先保持在一个方向上输出最大电压沿扇区边界这个保持过程持续一定角度然后释放让矢量沿六边形边界快速“追上”参考矢量的相位。通过控制这个保持角可以调节输出基波电压的幅值使得实际基波幅值等于我们期望的参考值。保持角越大输出电压基波幅值越高直到M最大时保持角为π/6也就是整个扇区都在限制输出最大电压。这么说可能有点抽象我换个角度解释区间二的核心是“用相位滞后换电压幅值”。当参考电压超过了逆变器能力极限时你不可能完全满足它只能折衷——要么幅值准确、相位偏差大要么相位接近、幅值偏差大。区间二选择了更接近“幅值准确”的方案因为对于电机控制来说电压幅值直接决定转矩输出能力相位偏差可以靠电流环的闭环调节来补偿。4. 过调制算法的工程实现4.1 实现方案选型查表法 vs 实时计算聊完原理直接上工程实现。过调制算法在MCU上的实现通常有两种思路查表法和实时计算法。查表法是提前算出每个调制比对应的修正参数比如保持角、临界角度等存到Flash里程序运行时通过当前M值查表得到参数然后查表修正角度偏移。这种方式计算量小适合主频不高的单片机比如STM32F103这类。但缺点是代码不够灵活如果M的分辨率不够细调制比变化时会有台阶感。实时计算法是用数学公式直接算出修正轨迹所需的参数。比如在区间一需要实时计算参考电压矢量与六边形边界的交点在区间二需要计算保持角αh与调制比M的对应关系。这种方式存储开销小、精度高但计算量大一些需要单片机有较强的浮点运算能力或者使用定点数学。我自己的经验是如果是做产品优先实时计算法因为现在的MCU性能都不差Cortex-M4以上的内核跑浮点运算是很轻松的如果只是做验证、做原型查表法省事调起来也直观。下面重点说实时计算法的实现细节。三维模型不太需要但流程图、公式是必要的。4.2 区间一的实时实现步骤区间一的算法逻辑本质上就是把参考电压矢量的轨迹分成两段处理。对每个PWM周期由u_alpha_ref和u_beta_ref算出参考电压矢量的幅值V_ref和角度θ_ref。判断V_ref是否超出六边形边界在当前角度下允许的最大幅值V_bound(θ)。六边形边界幅值函数是分段线性的按扇区写判断函数即可。如果未超出直接输出原始V_ref继续走常规SVPWM。如果超出将电压矢量幅值改为V_bound(θ)角度保持θ_ref不变即“径向拉回”。这一段的关键在于V_bound(θ)的计算。以第I扇区为例0°~60°六边形的第I扇区边界由V1矢量方向和V2矢量方向的连线构成边界上任意一点的幅值可以表示为V_bound(θ) (√3/4·U_dc) / sin(π/3 − θ)其中θ是该点与alpha轴的夹角。其他扇区对称类推。代码里不需要真的去算三角函数的除法可以把角度转换到各个扇区的本地坐标里再用一次直线插值或者反正切来求边界幅值。如果PWM频率是10kHz~20kHz每个周期都这样算也是没问题的。4.3 区间二的保持角计算与实现区间二的实现比区间一复杂。核心是实时计算保持角αh和调制比M的关系。一个常用的近似公式是αh π/6 · (1 − √((1.1027 − M) / (1.1027 − 1.05)))但这个公式在不同文献里有不同变体。更准确的做法是通过迭代或查表得到一个多项式拟合比如用二次多项式拟合αh(M)。实时控制时对每个PWM周期的参考电压矢量角度θ_ref执行以下逻辑将θ_ref映射到当前扇区内的本地角度θ_local0~π/3。若θ_local αh则将电压矢量强制定位到扇区起始边界方向即保持电压矢量方向不变幅值为最大边界值通常是六边形顶点方向。若θ_local ≥ αh则让电压矢量沿六边形边界滑动即按修正后的轨迹输出直到该扇区结束。这种方式会让输出电压的相位发生一定的“滞后”或“提前”具体的相位修正取决于保持角的定义方向。工程上因为电流环是闭环的相位的小幅偏差可以通过电流环的PI调节器自动纠正所以不必过度担心相位误差的影响。4.4 一个最小可跑的过调制代码骨架我简化一个区间一区间二的实现代码重点展示逻辑具体工程代码需要按你的平台适配。以C语言风格写假设输入是alpha轴和beta轴的电压给定值标幺化到U_dc/√3为1.0的标幺系统// 输入u_alpha, u_beta 标幺值线性区上限为1.0 // 输出修正后的u_alpha_mod, u_beta_mod void svpwm_overmodulation(float u_alpha, float u_beta, float mod_index, float *u_alpha_mod, float *u_beta_mod) { float v_ref, theta_ref; float theta_local, sector; float v_bound; // 1. 向量坐标变换到极坐标 v_ref sqrtf(u_alpha * u_alpha u_beta * u_beta); theta_ref atan2f(u_beta, u_alpha); // 2. 判断调制比只对过调制区修正 if (v_ref 1.0f) { // 线性区不做修正 *u_alpha_mod u_alpha; *u_beta_mod u_beta; return; } // 3. 判断所属扇区并转换到本地角度0~60° sector (uint8_t)(theta_ref / (PI / 3.0f)); theta_local theta_ref - sector * (PI / 3.0f); // 4. 区间一M 1.05 左右按实际过调制区间边界调整 if (mod_index 1.05f) { // 计算当前角度下六边形边界的幅值 v_bound (SQRT3 / 4.0f) / sinf(PI / 3.0f - theta_local); if (v_ref v_bound) { v_ref v_bound; // 对超出的部分沿径向修正 } } else { // 5. 区间二幅值-相位折衷 float alpha_h compute_hold_angle(mod_index); // 保持角由M查表或公式计算 if (theta_local alpha_h) { // 保持模式输出扇区边界的最大电压矢量 v_ref 2.0f / SQRT3; // 六边形顶点幅值 theta_ref sector * (PI / 3.0f); // 锁定在扇区起始边界方向 } else { // 跟随六边形边界 v_ref (SQRT3 / 4.0f) / sinf(PI / 3.0f - theta_local); } } // 6. 转换回直角坐标 *u_alpha_mod v_ref * cosf(theta_ref); *u_beta_mod v_ref * sinf(theta_ref); }注意这个骨架为清晰起见做了不少简化。比如保持角计算函数compute_hold_angle没有展开实际用查表或多项式实现还有θ_ref直接用了全局角度实际代码里要注意角度跨扇区时的连续性问题另外把六边形顶点幅值近似成2/√3严格来说应该是√3/2·U_dc归一化后的值这里按标幺系统简化了。真实项目里你还需要把修正后的u_alpha_mod、u_beta_mod送到常规SVPWM函数里去做扇区判断和T1、T2计算。我建议的实现顺序是先把线性区SVPWM调得非常稳确认母线电压利用率达到线性区上限然后加区间一观察电流波形和转速提升确认没问题后再加区间二并重点关注电流谐波。5. 参数调整与系统联调实测经验与性能变化5.1 过调制对电流环的影响一个常常被忽略的交互加了过调制之后很多人以为只要电压输出变大了电机自然就能跑得更快。但实际联调时会发现过调制会让电流环的参数需要重新调整。原因是这样的过调制本质上是非线性环节尤其进入区间二后输出电压和给定电压之间存在明显的非线性关系幅值饱和、相位修正。这会让电流环看到的被控对象发生变化——原来近似线性的电压-电流关系在过调制区变成了分段非线性的特性。如果你还用线性区调好的PI参数过调制区可能会出现电流环响应变慢甚至振荡。实际调测中我常用的办法是在深度过调制区适当降低电流环PI的P值防止因为非线性引起的超调同时略微加大I值保证稳态精度。但这没有一劳永逸的参数只能靠实验调。更好的方案是让电流环的PI参数随调制比做线性过渡比如在M超过0.95时开始逐步调整这个在工程上是可行的但会增加调参工作量。5.2 母线电压波动和过调制的关系直流母线电压不是恒定不变的尤其在电池供电的设备上电池电压会随负载和SOC变化。母线电压波动直接影响调制比的计算——母线电压低了同样的给定电压对应的M值就高了更容易进入深度过调制。我遇到过一个实际案例电池供电的吸尘器电机电池从满电4.2V单节跌到3.7V时同样的电频率下M值从1.02飙到1.08噪声一下变大电流波形出现明显畸变。排查了半天最后发现是M值计算里用的U_dc是常数而不是实时采样值。把母线电压采样引进来参与实时计算后问题就解决了。所以做过调制算法时U_dc一定不要用固定值必须实时采样滤波后参与运算。否则你调好的过调制表在不同电池电压下会“漂移”。5.3 高速弱磁区和过调制如何配合FOC系统里过调制和弱磁控制经常同时出现。电机在高速段反电动势接近甚至超过母线电压这时候需要弱磁来“削磁”同时需要过调制来提高电压输出。两者叠加才是真正的“高速大扭矩”方案。我的经验是优先进入弱磁再进入过调制。也就是说在M接近1时先加大id的负向电流弱磁让M值回落到安全范围只有当弱磁已经加到极限、M仍然超过1时才启动过调制。这样做的好处是弱磁带来的谐波比过调制稍小优先保证波形质量过调制作为“最后手段”用来榨取最后一点电压潜力。当然也有逆向的做法先过调制后弱磁。在一些对高速响应要求极高的场合比如无人机电机快速突加负载有过调制先行的方案。但那种方案对过调制算法的要求更高因为深度过调制时电流畸变较大可能会影响弱磁环的稳定性。5.4 实际代码里的PWM死区补偿不能省做过调制的朋友注意过调制区的占空比可能接近0%或100%尤其在六边形顶点附近逆变器的死区影响会被放大。死区会导致电流波形上出现额外的低次谐波这在过调制区尤其明显。所以工程上做过调制时最好把死区补偿一起做上。死区补偿的本质是根据电流方向对占空比做微调抵消死区时间内体二极管续流造成的电压偏差。如果电流方向检测不准死区补偿反而会引入新的谐波所以要先确认电流采样和重构电路的相移校准没问题。我在调试过一个80kW级别的电机控制器时发现过调制区电流波形总有毛刺查了很久最后定位到是死区补偿表在电流过零点附近存在一个“跳变”跟过调制修正量的非线性叠加后形成了振荡。解决办法是让死区补偿量在电流过零点附近做平滑过渡不要突变。6. 常见问题与调试技巧6.1 电流畸变过大怎么查过调制必然带来电流畸变但畸变过大一定是异常的。按照我的排查顺序来第一步确认是否真的进入了过调制区。打印或通过调试器观察M值如果M 1但电流畸变已经很大说明不是过调制的问题而是电流采样、编码器角度或者PI参数有bug。第二步确认扇区切换是否正常。过调制对扇区边界的敏感度高于线性区边界处理不好会在60°处出现明显的电流突起。可以在示波器上观察相电流在扇区跳变点有没有“台阶”或在代码里加一个调试变量统计每个扇区的作用时间连续性。第三步确认保持角计算是否正确。区间二的保持角算偏了最典型的现象是调制比越高电流波形越不对称比如正向半周和负向半周不一致这个时候特别要注意角度连续性问题。6.2 母线电压利用率到底提了多少有些朋友做完过调制后觉得效果不明显。这里要明白过调制提升的是基波电压有效值不是I/O口电压。从线性区到最大过调制基波电压最多提升约10.3%。听起来不多但对应到电机转速就是多出将近10%的转速上限。在电机设计不变的前提下这10%往往是“够用”和“不够用”的分界线。另外注意过调制提升的是基波幅值但代价是高次谐波显著增加。所以评估过调制效果时不要只盯着转速还要看电机温升和噪声。如果电机因为谐波发热严重转速上去了但可持续工作时间大幅缩短那就需要重新权衡过调制深度。6.3 一个实用的过调制深度调节技巧最后分享一个很实用的小技巧。工程上可以做一个可变的过调制限幅当电机温度较低时允许进入较深的过调制区间当温度超过一定阈值时自动限制M的最大值。这样既不牺牲短时过载能力又能保护电机不过热。实现也简单就是在过调制算法入口处加一个系数float max_mod_index get_temp_limit(tr_temp); if (mod_index max_mod_index) { mod_index max_mod_index; }温度限幅的曲线可以做成线性的比如从80℃开始限幅到120℃时完全禁止过调制。这个方法我在好几个项目里用了效果都很好既兼顾了性能又照顾了热安全你们可以试试。过调制这个东西原理上不复杂但真正做到“既要高速又要稳定”还有很多细节要抠。本文从SVPWM的中间变量推导说起把过调制的两个区间、工程实现和常见问题都过了一遍核心就是一句话过调制是在逆变器物理极限下用可控的波形失真换取更高的基波电压。理解了这个本质再去看那些公式、代码和调试现象思路就会清晰很多。