SVPWM过调制原理与工程实现:FOC高速段电压利用率提升指南
2026/9/13 16:21:02 网站建设 项目流程

接手一个新项目时,电机高速段转速上不去、相电流还有明显毛刺,这种问题十有八九出在SVPWM输出饱和上。我上个月就卡在这个坑里:母线电压72V,电机额定转速6000rpm,跑到4800左右电流环就开始“憋气”,电压矢量一进过调制区,占空比被简单削顶,电流波形立刻畸变,转速再也推不上去。后来把过调制策略捋清楚,同样硬件直接拉到5800rpm,电流波形也干净了不少。

这篇文章就把FOC控制里SVPWM过调制这件事从头到尾讲透,包括线性区的电压利用率天花板、过调制能“抠”出多少电压、两个非零电压矢量作用时间里的公共量怎么推导,以及代码里到底该怎么处理t1+t2大于开关周期的情况。内容偏实践,适合已经能跑通基础FOC、正卡在高速段调试的朋友。

1. 先看清SVPWM线性区的天花板:母线电压为什么被浪费了

1.1 从基本矢量合成说起

SVPWM不是真的“调制”三个独立的PWM波,而是在八种开关状态里做矢量合成。三相逆变器每相只有上桥臂导通和下桥臂导通两种状态,三个桥臂组合起来一共是2³=8个状态,其中6个是非零电压矢量,2个是零矢量(000和111,也就是三个上桥全开或全关)。

这6个非零矢量把αβ平面分成6个60°的扇区,每个矢量幅值都是2/3倍的母线电压Udc。任何时刻想要的参考电压矢量V*落在哪个扇区,就由这个扇区相邻的两个非零矢量按时间加权合成,剩下的时间给零矢量。这就是伏秒平衡:

V*·Ts = V1·T1 + V2·T2 + V0·T0

其中Ts是开关周期,T1和T2是两个相邻非零矢量的作用时间,T0是零矢量时间。这个式子是所有扇区时间计算的根,后面推导公共量就是从这里出发的。

1.2 线性区的极限:内切圆与Udc/√3

问题来了:6个非零矢量首尾相连围成一个正六边形,SVPWM线性区能输出的最大电压,不是六边形的顶点,而是六边形的内切圆。为什么?因为在每个扇区里,参考矢量一旦超出内切圆,T1+T2就会大于Ts,零矢量时间变负,这在物理上不可能实现。

内切圆的半径就是线性区的极限,算一下:

Vmax_linear = (2/3)·Udc·cos30° = (2/3)·Udc·(√3/2) = Udc/√3 ≈ 0.577·Udc

翻译成人话就是:母线电压100V,SVPWM在线性区最大只能输出约57.7V的相电压幅值。如果拿SPWM做对比,SPWM线性区最大相电压是0.5·Udc,所以SVPWM比SPWM多出了15.47%的电压利用率,这正是SVPWM成为FOC标配的原因之一。但0.577Udc还不是上限——六拍方波还能更高。

1.3 这10%的差距在高速区有多要命

六拍方波输出时,6个非零矢量每个持续60°,基波幅值等于方波相电压的基波分量,算出来是:

V1_sixstep = (2/π)·Udc ≈ 0.637·Udc

从0.577Udc到0.637Udc,相对提升了大约10.3%。你可能觉得10%没什么,但在FOC高速段,电机反电动势和转速基本成正比,母线电压不变的前提下,电压利用率多10%,就意味着转速能多拉10%左右。对于追求极限转速的无人机电机、高速主轴、电动工具,这10%往往就是“能用”和“不能用”的差别。

而且高压母线系统里,母线电压不能无限往上加,逆变器、电机绝缘、驱动芯片都有耐压限制,与其换硬件,不如把过调制这10%吃干净。这也是为什么过调制不是花架子,而是FOC高速段实打实的刚需。

2. 过调制到底能“过”到哪里:从六边形边界到六拍方波

2.1 第一层:幅值饱和沿六边形边界(T0=0)

当参考矢量V幅值超过内切圆但还没碰到六边形顶点时,最直接的做法是保持V的方向不变,把幅值拉到与六边形边界相交的位置。此时T1+T2=Ts,T0=0,逆变器全程都在切换非零矢量,没有任何零矢量时间。

这是过调制最温和的一档,也最好实现。代码里只要在算完T1、T2之后加一句:

if (t1 + t2 > TS) { float scale = TS / (t1 + t2); t1 *= scale; t2 *= scale; // t0 = 0,不需要显式处理 }

这句话就是很多人说的“过调制”,严格讲它只走到了过调制区的浅层。输出电压沿六边形边界走,基波确实比内切圆大了一点,但波形已经出现畸变,电流谐波开始增多。

2.2 第二层:角度修正,逼近六拍

如果参考矢量继续增大,单纯限幅到六边形边界就不够用了,因为这时候六边形边界上任何一点的基波幅值都跟参考值对不上。为了进一步逼近六拍方波,需要同时修正参考矢量的角度和幅值。

这一层学术上分两个模式,工程上经常统称“单模式过调制”或“查表修正”:一是输出电压矢量交替运行在参考位置和六边形边界之间,让平均基波跟随参考;二是当调制度进一步加深,输出电压矢量大部分时间停留在六边形顶点附近,只在过零点附近短暂离开边界。两个模式的分界点大约在调制比0.95附近,不同文献略有差异,实际调试时不用太纠结这个点。

2.3 基波提升多少、谐波付出多少

把三种状态放在一起对比,心里就有数了:

状态输出相电压基波幅值相对Udc占比谐波畸变程度开关状态特点
线性区(内切圆)Udc/√30.577Udc低,接近正弦T0存在,七段式正常
过调制浅层(沿边界)0.577Udc ~ 0.6Udc动态变化中,5/7次谐波开始增加T0=0,六边形边界限幅
过调制深层(角度修正)0.6Udc ~ 2Udc/π趋近0.637Udc高,谐波显著增大矢量停驻在顶点附近
六拍方波2Udc/π0.637Udc极限,畸变最大每个状态持续60°,开关频率最低

注意,过调制本质上是在“用谐波换基波”,这是物理规律,不可能白拿。相电流里的5、7、11、13次谐波会增加电机铁耗、铜耗和噪声,转矩脉动也会变大。所以过调制永远是在性能与电压利用率之间做权衡,不是什么场景都值得开到最大。

3. 公共量推导与扇区时间计算:先把正常SVPWM吃透

3.1 第一扇区伏秒平衡的完整推导

很多代码里直接给出三个中间变量就查表了,初看像魔法。其实推导特别朴素,拿第一扇区(0°到60°)举例。第一扇区相邻的两个非零矢量是V4(100)和V6(110),幅值都是2/3Udc,方向分别是0°和60°。把它们写成αβ分量:

V4 = 2/3Udc(沿α轴,β分量为0) V6 = 1/3Udc + j(√3/3)Udc

设参考电压为vα + jvβ,伏秒平衡方程组:

vα·Ts = (2/3)Udc·T1 + (1/3)Udc·T2 vβ·Ts = (√3/3)Udc·T2

先看β轴,它是T2在60°方向上的投影,只有T2贡献,所以直接从第二式解出:

T2 = √3·Ts/Udc·vβ

把这个结果代回第一式:

vα·Ts = (2/3)Udc·T1 + (1/3)Udc·(√3·Ts/Udc·vβ) = (2/3)Udc·T1 + (√3/3)·vβ·Ts

移项整理:

(2/3)Udc·T1 = Ts·(vα - √3/3·vβ)

T1 = (3·Ts)/(2·Udc)·(vα - √3/3·vβ) = √3·Ts/Udc·((√3/2)·vα - 0.5·vβ)

为了好看,把两个式子都提出一个公共系数K:

K = √3·Ts/Udc

于是第一扇区:

T1 = K·((√3/2)·vα - (1/2)·vβ) T2 = K·vβ

到这里,中间变量已经呼之欲出了。vβ就是公共量X,((√3/2)vα - (1/2)vβ)就是公共量Y,再定义一个对称的Z = -(√3/2)vα - (1/2)vβ用于扇区判断。所有扇区的T1、T2,本质上就是X、Y、Z这三个量在不同扇区里取正负、交换位置,乘以同一个系数K。

3.2 六个扇区怎么判断,代码长什么样

扇区判断有几种流派,我习惯用三个中间量v1、v2、v3直接判,好处是不需要算atan2,纯比较符号就行:

float v1 = vbeta; float v2 = 0.8660254f * valpha - 0.5f * vbeta; // sqrt(3)/2 float v3 = -0.8660254f * valpha - 0.5f * vbeta; uint8_t sector = 0; if (v1 > 0.0f) sector |= 1; if (v2 > 0.0f) sector |= 2; if (v3 > 0.0f) sector |= 4;

这个sector值经过映射后对应1到6扇区。拿到扇区号后,每个扇区的T1、T2就是从第一扇区公式经过旋转对称得到的,本质上就是X、Y、Z的组合。写出来是下面这样(注意这里的t1、t2还没有乘K,最终要乘√3·Ts/Udc):

float X = vbeta; float Y = 0.8660254f * valpha - 0.5f * vbeta; float Z = -0.8660254f * valpha - 0.5f * vbeta; float t1, t2; switch (sector) { case 1: t1 = Z; t2 = Y; break; // 不同代码的映射可能不同 case 2: t1 = Y; t2 = -X; break; case 3: t1 = -Z; t2 = X; break; case 4: t1 = -X; t2 = Z; break; case 5: t1 = X; t2 = -Y; break; case 6: t1 = -Y; t2 = -Z; break; } t1 *= 1.7320508f * Ts / Udc; t2 *= 1.7320508f * Ts / Udc;

拿到t1和t2之后,再做七段式分配,计算三相占空比。这一步很多FOC库都有现成实现,核心就是根据扇区把t1、t2、t0分配给A/B/C三相。

3.3 不同代码风格差异:为什么我的t1/t2和你的不一样

网上SVPWM代码版本多到眼花缭乱,变量名有的是XYZ,有的是v1/v2/v3,有的直接输出占空比。第一次看很容易懵。其实这些都只是“表达方式不同”,底层同一个伏秒平衡。

区别主要在两点:一是公共量的定义顺序和正负号不一样,比如有人定义Y = (√3/2)vα - 0.5vβ,有人定义Y = -0.5vα + 0.866vβ,旋转方向不同导致符号变化;二是有的代码把Ts/Udc整个归一化进了前面的标幺变换,有的没有,所以同样一个t1数值,在不同代码里对应的物理时间差别很大。

我自己的经验是:拿到任何一份SVPWM代码,先用第一扇区公式手推一遍,确认t1和t2分别对应哪个矢量、作用方向对不对,再往PID后面接。直接抄代码不校验扇区映射,十个里有九个会在某一个扇区出现电流畸变或转矩脉动。

4. 想过调制,t1+t2>Tpwm时怎么办:三种工程做法

4.1 做法一:等比例缩放到边界(最简单,够用)

过调制最朴素的入口就是T1+T2大于Ts。前面已经给了代码:

if (t1 + t2 > TS) { float scale = TS / (t1 + t2); t1 *= scale; t2 *= scale; }

这个做法的物理含义是:参考矢量方向不动,把幅值压到六边形边界上。它不需要扇区信息,不需要查表,计算量可以忽略,所以几乎所有FOC代码里都至少会有这一句防饱和保护。

但它也只是一个“保命”手段,不算严格意义上的过调制控制。当调制度比较深时,这种简单限幅会导致输出电压基波和参考值之间出现偏差,电流畸变明显增大,转矩脉动也会变差。如果你的项目只需要偶尔摸到过调制区边缘,用这个就够了。

4.2 做法二:αβ坐标直接裁剪到六边形

另一种等价实现是跳过扇区时间,直接在αβ层面对参考电压做几何裁剪:如果参考矢量超出六边形,就把它拉回六边形边界上。六边形边界的约束可以用三相目标电压写出来:

float ua = valpha; float ub = -0.5f * valpha + 0.8660254f * vbeta; float uc = -0.5f * valpha - 0.8660254f * vbeta; // SVPWM零序注入,等效空间矢量法 float umin = fminf(ua, fminf(ub, uc)); float umax = fmaxf(ua, fmaxf(ub, uc)); float uoff = -0.5f * (umin + umax); ua += uoff; ub += uoff; uc += uoff; // 限幅到[-Udc/2, Udc/2] ua = clamp(ua, -0.5f * Udc, 0.5f * Udc); ub = clamp(ub, -0.5f * Udc, 0.5f * Udc); uc = clamp(uc, -0.5f * Udc, 0.5f * Udc); // 转占空比 float da = 0.5f + ua / Udc; float db = 0.5f + ub / Udc; float dc = 0.5f + uc / Udc;

这种方式和做法一是等价的,只是把“矢量落在六边形内”这个约束显式地变成了三相电压限幅。优点是代码直观、和电机的dq轴坐标解耦,方便在过调制前后做零序注入逻辑的统一。很多商业FOC库就是用这种方式。

4.3 做法三:查表角度修正(单模式过调制,效果好但调试量大)

如果你需要追求极限电压利用率,简单限幅不够,要做角度修正。核心思路是:参考矢量进入过调制区后,不再保持原方向,而是根据当前调制比查表得到“角度偏移量”和“幅值修正系数”,把修正后的矢量再做边界限幅。

float m = vmag / (0.5773503f * Udc); // 线性区最大值标幺为1 if (m > 1.0f) { // alpha、gamma是从查表或多项式得到的角度修正量/幅值增益 float theta = atan2f(vbeta, valpha) + alpha(m); float newmag = vmag * gamma(m); valpha = newmag * cosf(theta); vbeta = newmag * sinf(theta); }

这个查表没有统一的万能表,和母线电压、死区电流、开关频率都有关系。工程上一般是先仿真扫出全调制度的谐波和基波特性,离线生成表,再上板实测微调。效果最好,但工作量也最大,适合产品阶段去啃。

4.4 我用哪种,以及为什么不推荐随意混用

我自己的项目里,快速原型阶段用做法一,因为改动最小、风险最低,确认过调制确实能带来转速提升后再换做法三。做法二作为代码重构和跨平台移植时的统一实现。

不推荐的做法是三种策略混着用:一会儿在这里限幅,一会儿在那里削角度,一会儿又用另一种查表。过调制本身就是一个强非线性环节,混用容易导致输出电压突变、电流环震荡。选定一种策略后,整个电压环、弱磁环和过调制逻辑要一起联调,不要单独替换。

5. 过调制在FOC里引起的连锁反应:波形、噪音、弱磁

5.1 电流谐波与转矩脉动增大的机理

过调制区输出的相电压不再是理想PWM波形,电压矢量沿六边形边界运动时,实际输出电压相比正弦会多出大量6k±1次谐波。谐波电压作用在电机定子绕组上,产生对应的谐波电流。电机的高速反电动势和低速时不同,谐波电流在低速段还能被电感滤掉一部分,高速段反而更容易在电流环带宽之外兴风作浪。

转矩脉动是这些谐波电流和转子磁场相互作用的结果。你会在示波器上看到相电流不再是漂亮的正弦,波形顶部或过零点附近出现明显的“削顶”或“台阶”。这时候别急着怀疑传感器,先用示波器量一下相电压,看是不是已经进了过调制区。

5.2 PI输出饱和与积分器处理

过调制最大的隐患其实不是谐波,而是电流环PI输出进入了饱和区。电压参考值一旦超出线性区,就相当于电流环的控制器输出被限幅了,此时如果积分项还在正常累积,就会出现积分饱和——表现为电流突降之后要很久才能拉回来,严重时直接震荡。

处理办法是给PI的积分项加上条件积分或反积分饱和:

float vq_raw = kp * err_q + integral_q; if (vq_raw > VMAX || vq_raw < -VMAX) { integral_q -= ki * err_q * Ts; // 积分器回退,防止饱和 }

这里VMAX不是某个固定值,而是实时算出来的当前可用最大电压,至少要减掉最大线性区电压Udc/√3。更精细的做法是考虑过调制深度和弱磁裕量,动态调整VMAX。这一行代码,值得你在调试时认真对待。

5.3 死区时间与最小脉宽的冲突

过调制区有一个很容易被忽略的细节:T0消失之后,某个扇区里可能有一相占空比长时间维持在100%或0%,这一相在这个扇区内几乎不动作,死区时间的影响只集中在一个很窄的换相区。看起来好像死区影响变小了,实际情况恰恰相反——由于电压裕量被压榨得很干净,死区造成的电压误差占比变大,在参考矢量接近六边形顶点的位置,电流波形会出现明显的“毛刺”或“塌陷”。

如果系统有死区补偿,过调制区里死区补偿的方向容易判断错。近些年部分MCU的电机库会在死区补偿里加一个电压矢量角度约束,压制过调制区间内的错误补偿。自己写代码的话,至少要在死区补偿前判断一下当前是否处于过调制区,必要时直接关闭补偿。

5.4 和弱磁控制怎么配合

过调制和弱磁是两个容易搞混的东西。弱磁是通过减小d轴电流(实际是往负方向加电流),降低转子磁链在定子侧的耦合,从而压低反电动势,让电机在超出转折点的转速下还能维持电压平衡。过调制则是提高逆变器输出电压的上限。两者都能让电机跑更快,但作用机理完全不同。

工程上建议:先把过调制这一档电压用完,再进弱磁。就是说电压环先输出到Udc/√3以上,电流环饱和之后,弱磁环才开始介入。如果两个环节同时抢电压裕量,轻则控制混乱,重则永磁体退磁。做一个简单的优先级判断:

float v_mag = sqrtf(vd*vd + vq*vq); if (v_mag > 0.5773503f * Udc) { // 过调制区,电压已相对饱和 // 此时弱磁环可以开始调节 id_ref(但要限幅) } else { // 线性区,弱磁一般不需要工作 }

6. 调试记录:从仿真到板子上的几个关键观测点

6.1 仿真里应该盯什么

搭一个PMSM的Simulink模型跑FOC,对比三种状态下(线性区、过调制浅层、过调制深层)的相电流FFT是最直观的。重点不是看基波幅值,而是看5、7、11、13次谐波随调制比的变化趋势。另一个必看的是dq轴的电压参考值和实际输出电压的差,这个差值就是过调制引发的非线性失真,弱磁环和电流环都要消化它。

仿真里还有一个容易踩的坑:母线电压设成理想常数,实际板子上母线电压是有纹波的。过调制区的最大可用电压直接和瞬时Udc挂钩,如果仿真里忽略这一点,上板后会发现进入过调制区的时机比仿真早很多。

6.2 实测波形与参数调整

上板调试,先把示波器夹在相电压和相电流上,开环给一个很小的电压矢量,确认扇区切换时占空比连续。然后逐步增大电压指令,观察电流波形何时开始“变形”。这个变形点理论上应该出现在输出相电压幅值达到Udc/√3附近,用万用表或示波器量一下就知道了。

如果变形点提前出现,大概率是母线电压采样偏低导致限幅提前;如果变形点滞后,可能是电流环PI太软,电压输出还没到限幅就被拉下来了,得先确认电流环带宽。实测时开关频率、死区时间、电流采样延时都会影响过调制点的实际位置,不要指望仿真参数直接上板就差分毫。

6.3 我踩过的几个坑

第一次做高速FOC时,我直接在电流环后面加了个硬限幅,结果高速空载时电流波形全程可见明显的6k±1次拍频噪声,原因就是我把参考电压矢量简单限幅到内切圆上,还没真正进入过调制区就已经丢了10%的电压利用率,电机效率反而变差了。后来去掉这层限幅,让电压矢量自然进入六边形边界,问题才消失。

第二个坑是母线电压采样没有做滤波,过调制限制条件是拿瞬时Udc计算的,母线电压纹波一大,限幅点跟着抖动,电机在高速区出现了周期性的“喘息”。最后在软件里对Udc做了20μs左右的滑动滤波,同时把最大电压限制计算放到PWM中断的最后一步执行,减少采样延迟的影响。

第三个坑比较隐蔽:过调制区的七段式PWM里,某相占空比可能长时间保持为0或1,导致这一相电流采样窗口异常。我的电流采样是在PWM中点触发的,当某相占空比饱和时,采样点附近的电流纹波变大,电流反馈带了额外噪声。后来换了双采样点做对称采样,电流环的稳定性才拉回来。

第四个坑是转速上去了,MOS管温升也跟着上去了。过调制区开关次数减少,按理说开关损耗应该下降,但电流谐波增大带来的铁耗和铜耗反而上来了。散热设计时别只按线性区算损耗,至少留20%的裕量给过调制和弱磁工况。

最后分享一条个人经验:过调制的调试顺序一定要是先确认“线性区最大电压是否吃满”,再搞过调制。很多人一上来就开大调制度,电压利用率没提多少,电流环先震了,最后连正常转速都跑不到。把基础SVPWM的占空比计算、死区补偿、电流采样对齐做好,过调制只是最后一层薄纱,捅破就通。

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

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

立即咨询