做FOC调电机,SVPWM是绕不开的环节,但多数人第一次做高速驱动时,会在一个奇怪的地方卡住:转速到了某个值之后就再也上不去了,调PI、调电流环都没用,最后问题定位到母线电压利用率。SVPWM过调制,就是把逆变器在过调制前最后那约10%的电压余量榨出来的手段,也是PMSM弱磁控制之外提升最高转速的第二根杠杆。这篇文章我从FOC电压矢量关系讲起,把过调制I区和II区在六边形里到底对应哪块面积、怎么判断当前调制比、怎么在STM32 HAL库工程里用代码实现,以及我踩过的一些坑一起盘一遍,适合正在搞PMSM FOC、无感FOC,或者想把现有驱动跑到基速以上的朋友。
1. 先搞清楚:FOC里的SVPWM和过调制是什么关系
1.1 FOC闭环里,SVPWM到底在干什么
FOC闭环的流程其实很固定:ADC采样相电流,经过Clark变换得到αβ电流,再经过Park变换得到dq轴电流;然后电流环PI调节器输出Ud和Uq,再经过逆Park变换得到Uα和Uβ;最后把Uα和Uβ交给SVPWM模块,生成六路PWM去驱动逆变器的六个开关管。很多人把SVPWM当成一个“查表/计算占空比”的模块,其实它是一个矢量调制器,核心任务是“在一个PWM周期内,用逆变器可输出的有限个电压矢量,合成出一个任意方向和任意大小的期望电压矢量”。
逆变器六个开关管只能产生8种开关状态:6个有效电压矢量V1~V6,还有两个零矢量V0和V7。这6个有效矢量的端点连起来就是一个正六边形。SVPWM做的事情,就是在每个载波周期里,根据参考电压矢量的幅值和角度,选择相邻的两个有效矢量,加上零矢量,通过控制它们各自的作用时间,让平均电压在伏秒平衡的意义上等于参考电压。也就是说,只要参考电压矢量落在正六边形内部,就能基本无失真地合成出来;一旦参考电压矢量跑到正六边形外面,合成就会超出线性调制范围,必须做额外处理,这就是过调制问题的来源。
1.2 正六边形、内切圆和外接圆,调制比怎么算
画一张αβ平面上的矢量图,正六边形是最直观的东西。六个有效矢量的端点都在六边形顶点上,六边形的内切圆半径是Udc/√3,外接圆半径是2Udc/3。这两个数很重要。
内切圆以内是线性调制区。参考电压矢量只要在以原点为圆心、内切圆半径为半径的圆形范围里,就可以通过SVPWM线性合成,谐波很小。定义调制比M = Vref / (Udc/√3)。当M<=1时,系统运行在线性调制区。当M>1时,参考电压圆轨迹已经有一部分冲出内切圆,进入了过调制区。
这里顺便算一笔电压账。传统SPWM线性区最大相电压幅值只有Udc/2;SVPWM线性区最大相电压幅值是Udc/√3,约等于0.577Udc,比SPWM多了15.47%。如果继续过调制到六边形顶点,再极限一点,直到方波运行,相电压基波幅值可以到2Udc/π,约等于0.637Udc。也就是说,相对SVPWM线性区,过调制最多还能再提升大约10%的电压利用率。这10%在电机高速区非常宝贵,它是从“最大转速卡住不动”到“还能往上拉一截”的关键差量。
1.3 过调制的本质:牺牲波形换电压
过调制不是免费的午餐。当M>1时,参考电压圆已经大于内切圆,SVPWM不管怎么做都无法在每一个载波周期都完美合成参考电压。这时必须对参考电压矢量进行修正,修正方式从“把幅值压到六边形边界”逐渐过渡到“把矢量钳位在顶点不动”,输出电压会产生低次谐波,相电流畸变增大,转矩脉动变大,电机噪音也变大。
理解过调制,最关键的一句话是:它是在电压输出能力到顶之后,用波形质量换电压幅值。所以过调制一般不是让系统长时间运行在最大极限,而是在基速以上、需要更高电压时,作为扩展区间使用。工程上常常是线性区、过调制I区、过调制II区按嵌套的方式来切换,避免一上来就压到方波。
2. 过调制I区和II区:范围和实现原理
2.1 怎么判断参考电压矢量落在哪块面积里
很多刚接触过调制的人会问:“SVPWM的过调制1和2区分别在那个面积内?”这是个很实际的问题。看正六边形图的时候,可以这样分区域:
- 内切圆以内:线性调制区,M<=1。
- 内切圆到正六边形边界之间:过调制I区。这个区域里,参考电压矢量冲出了内切圆,但还没完全超出六边形的边界,实际输出电压矢量的轨迹可以被压到六边形的“边”上。
- 正六边形边界之外,直到外接圆附近:过调制II区。在这个区域里,光靠压幅值已经不够了,因为六边形边界本身也有最大限制,必须让输出矢量在顶点附近“停”一段时间,让基波电压继续往上顶,直到极限方波。
所以用“在哪个面积内”来记忆的话:I区的修正策略围绕“六边形的边”展开,II区的修正策略围绕“六边形的顶点”展开。实际运行时不需要肉眼去看面积,而是用调制比M来判断。M在1附近是I区,M接近最大极限时是II区,工程上一般以M在1.05左右作为I区和II区的经验分界,具体数值建议通过离线仿真或台架标定得到。
2.2 过调制I区:把圆压到六边形边上
过调制I区的实现思路可以概括成四个字:限幅保角。参考电压矢量本来沿着一个半径大于内切圆的圆轨迹走,但实际可线性合成的范围是六边形内部。怎么办?保持参考电压矢量的方向不变,把它的幅值压到与六边形边界相交的位置,再交给常规SVPWM去合成。
具体计算时,需要知道当前参考角度在每个扇区内的偏移量。以第一扇区为例,扇区中心线在30°位置,角度偏离中心线越远,六边形边界到原点的距离越大。边界处允许的最大幅值可以写成:
V_hex = (Udc/√3) / cos(delta_theta)
其中delta_theta是参考电压矢量与当前扇区中心线的夹角,范围在±30°内。当参考矢量幅值Vref超过V_hex时,就把Vref压缩到V_hex;如果没有超过,则保持原值。这样处理之后,实际输出电压矢量的轨迹就落在六边形的边上了,基波电压会随着调制比提高而逐步增大,谐波也在可接受范围内。
这里要注意一个细节:这种“限幅保角”方案在做电流环调试时非常直观,PI输出大一点,电压幅值就压一点,不会出现输出角度突变。但严格说,I区仍然会有一定的相位误差和低次谐波,只是工程上影响不大,尤其是配合过调制深度不深的使用场景。
2.3 过调制II区:从六边形边界跑向方波
当过调制深度继续增加,参考电压矢量已经明显超出六边形边界时,单纯压幅值就不够了。原因很简单:六边形边界本身也有上限,边界上离原点最远的点就是顶点,距离是2Udc/3,超过这个幅值之后,已经没有“边界上的点”可用了。此时要让基波电压继续增大,必须调整策略:让输出电压矢量在扇区边界处保持在某一个有效矢量上,而不是一直按角度沿边界走。
工程实现上,II区最常用的概念是保持角,也叫钳位角。在每个60°扇区内,参考电压矢量角度如果离扇区起始边界小于保持角alpha_h,就强制输出起始边界对应的有效矢量;如果离扇区结束边界小于alpha_h,就强制输出结束边界对应的有效矢量;中间区间则仍然按限幅后的线性调制来处理。alpha_h越大,输出电压波形越接近方波,基波电压越高。
alpha_h的范围从0增加到π/6。当alpha_h=0时,其实就是I区边界;当alpha_h=π/6时,整个扇区都被某个有效矢量占满,输出就是六步方波,基波幅值达到极限。alpha_h和调制比M的关系不是简单线性,建议用仿真软件跑一遍,把M从1到最大值的alpha_h存成表格,运行时查表加线性插值,比现场演算要稳得多。
2.4 两个区域的切换和实际调制比定义
区分I区和II区,说到底是为了决定采用哪种修正策略。I区只需要做幅值修正,计算量很小;II区需要做角度钳位和查表,逻辑复杂度上了一个台阶。工程上切换时,不要只用一个固定的调制比阈值,因为母线电压波动、死区效应、电流环响应都会影响实际输出。我的习惯是:在M<=1时走线性SVPWM;M在1到1.05左右走I区;M再大走II区。并且在切换点加一点滞回,防止PI输出在小范围内抖动时,过调制算法在两个策略之间来回跳,导致电流发生振荡。
关于调制比的定义,这里统一用的是M = Vref / (Udc/√3)。有些资料会把调制比定义成 Vref/(2Udc/π)或者Vref/Udc,算出来的数值不一样,但你只要盯住“参考电压幅值除以线性区极限幅值”这个比值,理解逻辑就不会乱。
3. 在FOC工程里落地过调制:从理论到C代码
3.1 电流环输出到SVPWM的完整链路
实际FOC代码里,过调制不是单独一个功能,而是嵌在电流环输出和SVPWM扇区计算之间。STM32 HAL库工程也好,老一点的F407、F103裸机工程也好,链路都一样:电流PI输出Ud、Uq,经过逆Park得到Uα、Uβ,然后把Uα、Uβ传给SVPWM。常规SVPWM会先算扇区,再算两个有效矢量和零矢量的作用时间T1、T2、T0。如果T1+T2已经超过一个PWM周期,就说明参考电压矢量超出了线性调制能力,此时必须做过调制处理。
最简单也最容易出问题的做法,是把Ud、Uq在dq轴坐标系里直接限幅。这样做表面上看电压不会超过上限,但实际上会改变电压矢量的相位关系,电机负载角被扭曲,轻则转矩下降,重则电流环失控。正确做法是在αβ坐标系里,基于电压矢量幅值调制比来修正。也就是说,先算出Vref的幅值和角度,再根据过调制策略调整幅值或角度,最后再走常规SVPWM计算占空比。
3.2 过调制I区的C代码实现框架
下面给出一个可以直接放进FOC中断里的过调制I区代码框架,逻辑非常简单,适合算法移植和调参。假设你的FOC电流环中断频率已经固定,Udc是母线电压,Ualpha和Ubeta是逆Park之后的输出。
void svpwm_overmodulation(float *Ualpha, float *Ubeta, float Udc) { float Vref, theta, delta_theta, Vhex; float Udc_sqrt3 = Udc * 0.577350269f; // Udc / sqrt(3) Vref = fast_sqrt((*Ualpha) * (*Ualpha) + (*Ubeta) * (*Ubeta)); // 如果在线性区,不做处理 if (Vref <= Udc_sqrt3) { return; } theta = atan2f(*Ubeta, *Ualpha); if (theta < 0.0f) theta += 2.0f * 3.1415926535f; // 计算当前扇区,每个扇区中心线对应角度 // 扇区编号0~5,中心角为 30 + 60*n 度 int sector = (int)(theta / 1.047197551f); // 60度 float sector_center = sector * 1.047197551f + 0.523598776f; // +30度 // 与扇区中心线的夹角,范围[-30, 30]度 delta_theta = theta - sector_center; // 正六边形边界幅值 Vhex = Udc_sqrt3 / cosf(delta_theta); // 幅值压缩到边界 if (Vref > Vhex) { Vref = Vhex; } *Ualpha = Vref * cosf(theta); *Ubeta = Vref * sinf(theta); }这段代码的效率不高,因为用了atan2、cos和sin,但STM32F407这一代带FPU,电流环跑到10kHz以内基本没问题,代码可读性优先。如果你用F103或者想把计算量压到极低,可以用扇区判定和查表替代三角函数,本质是一样的。注意一件事:乘法因子0.577350269f必须用浮点常量,不要在每次中断里都做一次除以根号三的运算。
3.3 过调制II区的保持角实现思路
II区代码比I区多一个保持角查表。这里不打算给完整代码,因为不同电机的电压极限和母线电压不一样,保持角表需要每个人自己的台架标定,但实现结构是通用的。先定义一个数组hold_angle[256],下标对应调制比M从1.0到最大值的256个刻度。每次中断里先由Vref和Udc算出M,再用线性插值查表得到alpha_h。
判断逻辑可以这样写:把当前角度theta折算到所在扇区内的相对角度theta_rel,范围0到60°。如果theta_rel小于alpha_h,输出电压矢量直接选择扇区起始边界对应的有效矢量;如果60°减theta_rel小于alpha_h,选择扇区结束边界对应的有效矢量;否则回到I区限幅逻辑,继续用常规SVPWM。
这个方法的好处是代码结构清晰,且与I区共用一部分逻辑。II区带来的谐波比I区大得多,所以保持角表要慢慢加,不要一次把alpha_h调到很大,否则机械部分会先抗议。
3.4 与弱磁、MTPA配合时的注意事项
过调制不是单独用来“提转速”的,它和弱磁控制是配合关系。基速以下用MTPA,让电流在相同转矩下最小;到基速附近,电压开始触顶,这时如果还想要更高转速,就必须弱磁,也就是往负方向注入id电流,抵消一部分反电动势。弱磁控制的电压极限方程中,可用的最大电压Vmax是一个硬约束。过调制把这个Vmax从线性区极限提高到更高的基波幅值,相当于把电压极限椭圆向外扩了一圈,在同样的转速下可以允许更大的电流和转矩,或者在同样的电流下把转速推得更高。
这里容易踩坑:弱磁之后,电流环PI输出常常在饱和边缘,如果再叠加过调制,输出电压修正量会突然变化,PI积分项很容易越积越大。所以做过调制之前,一定要先给电流环加上抗饱和处理,要么限制PI输出,要么在饱和时冻结积分项,否则系统在过调制区来回切换时,大概率会不稳定。
4. 调试过调制时最常见的坑与排查方法
4.1 电流环饱和、转速卡住,先查调制比
很多人过调制调不出来,现象是转速到了某个值就卡住,还以为是PI参数问题。实际上先看调制比M就行。在代码里加一个临时变量,把M的值通过DAC或者串口监控工具发出来,转速爬升时观察M是否一路涨到1.0并试图往上突破。如果M到1.0就再也上不去,说明电流环PI输出已经饱和,或者是你的SVPWM模块T1+T2等于载波周期之后直接做了等比例缩放,并没有进入过调制修正逻辑。后者是新手最容易踩的坑:很多SVPWM库在T1+T2超过载波周期时,只会简单归一化,这种处理本质上只等效于“极限线性调制”,并没有真正做过调制。
正确做法是:T1+T2超过载波周期时,先判断Vref是否超过线性极限;如果超过,进入过调制函数修改Uα、Uβ,再重新计算占空比,而不是直接把T1、T2等比例压回去。简单说,“等比例缩放”和“过调制”不是一回事。
4.2 过调制后电流畸变太大怎么办
过调制一上,相电流波形出现马鞍形畸变是正常的,但如果畸变到过零处出现明显阶梯、甚至负向电流毛刺,那就说明压得太狠了。处理思路有三条:第一,降低过调制深度,把调制比限制在I区范围,尽量避免长时间运行在II区;第二,提高PWM开关频率,开关频率高了之后,电流谐波有效值会降低,但要注意开关损耗;第三,在无感FOC方案里,畸变电流会直接影响反电动势观测,可以考虑在观测器前端加低通滤波器,或者在高速区切换为电压模型观测器。
另外不要忽略死区效应。过调制区的脉宽往往比较窄,死区时间对窄脉冲的影响比线性区更显著。如果在过调制区发现单相电流波形明显失真,优先检查死区补偿方向是否正确,而不是盲目去调过调制算法。
4.3 无感FOC滑模观测器在过调制区抖动
无感FOC用滑膜观测器做反电动势估计时,过调制会让电流谐波增大,观测器输入不干净,转速估计就容易抖。我的经验是:低速区不要用过调制,那个阶段反电动势本来就小;中高速进入过调制区之后,滑模观测器的切换增益可以适当降低一点,或者根据调制比动态调整观测器带宽,不要让高频谐波全部进入速度环。如果条件允许,在过调制II区切换成基于反电动势的锁相环法,比纯滑模在小电感贴片电机上要稳。
4.4 五段式SVPWM和过调制配合的问题
常规SVPWM叫七段式,每个扇区用7段状态,谐波小但开关次数多;五段式把零矢量分布方式做了变化,每个载波周期有一相保持不动,开关次数只有三分之二。过调制之后电流谐波本来就大,再用七段式,开关损耗上升很快,尤其母线电压高的时候,逆变器发热会非常明显。所以很多驱动在高速过调制区切到五段式,开关损耗降下来,代价是谐波略微增加,但配合过调制本身的大谐波,这个代价完全可以接受。
实现过调制时,不管用七段还是五段,扇区判定和作用时间计算是一样的,唯一区别在于PWM比较值的写入顺序和零矢量分配方式。所以如果你现在的工程已经在用五段式,上面I区和II区的代码可以直接迁移。
4.5 调试技巧:把调制比和保持角用DAC送出来看
最后分享一个我自己用得很顺手的调试技巧。STM32F407这类芯片一般都能在定时器通道上配置成DAC输出,或者用PWM+DAC滤波,把调制比M映射到一路模拟输出,把保持角alpha_h映射成另一路模拟输出。在示波器上同时看母线电压、相电流、M和alpha_h,就能非常直观地看到:转速爬升时M什么时候到1.0,过调制算法什么时候介入,alpha_h有没有跳变。实测下来,这个方法能省掉大量来回改参数的时间,比只看串口打印要高效太多。
我个人在实际项目里,过调制I区常常是长开的,因为它在母线电压波动时能给电压利用率一点余量,且谐波可控;II区则只在弱磁高速或短时过载情况下临时打开,并且不会推到极限方波。毕竟电机系统最终要的是稳定可靠的转矩输出,而不是为了把那一两个百分点的电压榨到极限,弄得整个系统都在发抖。