尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SVPWM五段式与七段式在STM32上的工程实现对比与选型指南

SVPWM五段式与七段式在STM32上的工程实现对比与选型指南 很多做电机控制的工程师第一次接触SVPWMSpace Vector Pulse Width Modulation空间矢量脉宽调制时最常问的一个问题是为什么同一套算法有的代码里输出的是五段式波形有的是七段式两者看起来差别不大但在实际工程中的开关损耗、谐波含量、电流纹波和CPU开销上却有实打实的差距。我最早在STM32F103上做PMSM永磁同步电机的FOC磁场定向控制时也曾在这两种模式之间反复横跳后来把原理彻底吃透才明白选型不能只看Demo代码里默认配了什么而是要根据母线电压利用率、开关频率和硬件死区时间来定。这篇博文就围绕SVPWM五段式和七段式在STM32上的实现对比展开把我调试过程中积累的推导过程、代码细节和实测经验一次讲清楚适合刚入门FOC但已经会配置STM32定时器的同学参考也适合那些已经把电机转起来但想进一步优化波形质量的工程师。1. 先把SVPWM的本质说透合成电压矢量的数学基础在进入五段和七段的对比之前有必要把SVPWM的底层逻辑完整推导一遍。很多资料直接给出扇区判断和矢量作用时间的公式却跳过了中间变量怎么来的导致换个MCU或者调整母线电压之后不知道怎么修改参数这恰恰是代码移植时最容易出问题的点。1.1 为什么是六个基本矢量和一个零矢量三相逆变器由六个开关管组成分别对应A、B、C三相的上桥和下桥。同一相的上桥和下桥不能同时导通所以每一相只有两种状态上桥导通记为1或下桥导通记为0。三相组合起来一共有2的3次方等于8种开关状态。其中6种状态对应6个非零电压矢量它们在空间上相隔60度将整个平面划分为6个扇区。剩下2种状态是上桥全通或下桥全通合成电压为零所以叫零矢量。这8个开关状态在空间中的分布就是SVPWM的基本骨架。这8个矢量的幅值关系很关键。以直流母线电压为Udc为例6个非零矢量的幅值并不是Udc而是2/3倍的Udc。这个2/3系数的来源是等幅值变换也就是Clarke变换中保持幅值不变的那一种。如果你用的是等功率变换系数就会不同这一点在做代码移植时一定要先确认自己用的变换形式否则后面所有计算都会差一个比例系数。1.2 伏秒平衡与目标矢量的合成SVPWM的核心思想是在一个PWM周期Ts内用相邻的两个非零矢量分别作用一段时间再用零矢量填充剩余时间使得平均电压矢量等于我们期望的目标电压矢量Us。这里依据的是伏秒平衡原理也就是矢量对时间的积分相等。假设目标矢量Us落在第一扇区由T1和T2分别作用等效关系为Us × Ts U1 × T1 U2 × T2 U0 × T0其中T0是零矢量的作用时间。理解这个公式是整个SVPWM的根基——你并不是真正得到了一个连续的Us而是在一个周期内用离散的开关状态拼出了平均值。在实际计算中通常先定义两个中间变量X、Y、Z。很多教程直接列出X √3 × Uβ × Ts / Udc Y (√3/2 × Uβ 3/2 × Uα) × Ts / Udc Z (√3/2 × Uβ - 3/2 × Uα) × Ts / Udc但很少解释它们怎么来的。其实这三个中间量就是目标矢量投影到不同扇区相邻边上的邻边作用时间。以第一扇区为例把Us分解到α-β轴后通过求解正弦定理就能得到T1 XT2 Y。而其他扇区的作用时间本质上是X、Y、Z的不同组合。我建议你亲手把六个扇区的情况都推导一遍这样需要扩展或改造时才知道变量之间的关系不至于背公式。1.3 扇区判断的两种实现风格扇区判断有两种主流做法。第一种是根据Uα和Uβ的正负及大小关系通过if-else逻辑判断代码直观但分支较多。第二种是计算三个参考值Uref1 Uβ Uref2 (√3/2)×Uα - Uβ/2 Uref3 (-√3/2)×Uα - Uβ/2然后根据这三个值的正负组合查表确定扇区。工程上更推荐第二种不仅因为计算量小更重要的是逻辑统一方便在DSP或者带硬件除法器的MCU上优化。我自己的经验是如果在STM32F103这种72MHz主频的芯片上跑FOC扇区判断用查表法比if-else节省大约1到2微秒虽然不多但放在20kHz的中断频率下累积起来对系统响应还是有影响的。而且查表法便于函数化代码可读性更好。2. 七段式为什么是标准答案零矢量分配与开关次数七段式SVPWM之所以被称为标准实现是因为它在每个PWM周期内把零矢量拆成了四段两段000和两段111分别放在周期的起点、中点和终点。这种对称结构带来的好处是波形质量好、谐波集中在载波频率附近便于设计滤波器。2.1 七段式的开关序列结构以第一扇区为例七段式的矢量序列为000-100-110-111-110-100-000。可以看到零矢量000出现在开头和结尾111出现在中间两个非零矢量100和110各出现两次整个序列关于中点完全对称。这种对称结构决定了七段式的开关特点每个PWM周期内六个开关管总共动作六次每个桥臂各两次。开关次数固定和扇区无关因此计算占空比时不需要动态调整比较值的写入顺序。从实现角度来说七段式的三相占空比可以直接由作用时间T1、T2推导出来。在STM32的高级定时器里就是往CCR1、CCR2、CCR3三个比较寄存器里写入不同的值。关键在于找到三相的切换点当计数器的值等于某个切换点时输出状态翻转。具体计算时先求出T_a (Ts - T1 - T2) / 4 T_b T_a T1 / 2 T_c T_b T2 / 2然后根据扇区查表把T_a、T_b、T_c分别映射到三相的比较寄存器上。不同扇区的映射关系不同这是代码里最容易写错的地方。2.2 中心对齐模式下PWM波形的生成逻辑STM32的高级定时器TIM1和TIM8工作在中心对齐模式时计数器先向上计数到ARR自动重装载值再向下计数到0。这种模式下产生的PWM波形天然关于周期中点对称正好匹配七段式SVPWM的结构需求。我在第一版代码里犯过一个典型的错误用边沿对齐模式去跑七段式结果波形完全不对称电流谐波明显增大。后来才意识到边沿对齐模式下每个周期只有一段零矢量实际上是做不成标准七段式的。中心对齐模式的另一个好处是在一个完整的PWM周期内比较事件会触发两次一次在向上计数时一次在向下计数时因此可以产生对称的互补PWM。对于带死区要求的MOSFET驱动来说这是必须的配置。2.3 七段式的谐波特性与采样时机七段式最突出的优点在于它的谐波特性。由于波形关于周期中点对称偶次谐波被很好地抑制主要谐波分量集中在载波频率及其整数倍附近。这意味着电流互感器或采样电阻上看到的纹波频率较高且规律通过简单的低通滤波就能还原出干净的电流信号。另一个工程上的隐形好处是采样时机固定。在七段式的对称序列中零矢量111位于周期中点此时三相下桥全部导通采样电阻上可以采集到真实的相电流。所以只需要在定时器向上计数到达中点时触发ADC采样就能稳定地获取三相电流。这也是很多FOC代码库默认使用七段式的原因。3. 五段式从少开关两次说起五段式SVPWM和七段式的本质区别在于每个周期只使用一种零矢量要么000要么111而不是两种都用。这样一来每个周期的开关次数从六次减少到四次开关损耗的理论值降低了三分之一但代价是波形谐波含量增加。3.1 五段式的序列变化与钳位效应以第一扇区为例如果只在起始和结束位置放入零矢量000那么开关序列为000-100-110-111-110-100也可以把中间的111去掉变成000-100-110-100-000。实际在五段式实现中通常选择一个非零矢量在某些区间内不切换比如让某一相在整个PWM周期内保持导通或关断这就是所谓的钳位。钳位效应是五段式最直观的特征每一相在一个基波周期内会有120度的区间保持状态不变。钳位带来的直接好处是这段区间内该相开关完全不动开关损耗为零。三相叠加起来每个基波周期内总的开关动作次数相比七段式下降三分之一。具体选择钳位哪一种零矢量有两种策略一种是始终使用000称为下零矢量钳位另一种是始终使用111称为上零矢量钳位。还有一种是交替使用两种零矢量比如奇数周期用000偶数周期用111这样能进一步分散热量分布对大功率逆变器的热设计有一定帮助。3.2 五段式的谐波代价与死区影响五段式的缺点是显而易见的由于方波只有五段谐波含量特别是低次谐波明显高于七段式。实测下来在相同载波频率和调制比条件下五段式的电流总谐波畸变率THD比七段式通常高出10%到20%。具体数值取决于调制比调制比越高两者差距越小在过调制区甚至差距不大但调制比在0.5到0.8这个常用区间里差距非常明显。死区时间对五段式的影响也更严重。因为在钳位区间内某相不动作一旦退出钳位进入开关切换电流换流瞬间对死区时间的敏感度更高。如果不做死区补偿在五段式下电流波形会出现更明显的过零畸变低速运行时表现为转矩脉动增大、噪音变大。所以我的建议是如果硬件死区时间较大比如超过1微秒且没有做死区补偿优先选择七段式。如果死区时间小、开关频率高、对损耗敏感五段式才是值得考虑的选项。3.3 五段式的计算流程差异在代码实现上五段式和七段式的扇区判断、T1和T2的计算完全一致区别只在占空比的合成方式。以第一扇区使用000零矢量为例三相的比较值分别为T_a (Ts - T1 - T2) / 2 T_b T_a T1 T_c T_b T2和七段式的区别就在于没有除以4和除以2的那一步而是直接把零矢量时间放在周期首尾。这看起来简单但实际效果是波形不再关于周期中点对称因此七段式那种中点采样的策略在五段式下要重新调整否则采到的电流可能是开关切换瞬间的尖峰。4. STM32上的具体实现从初始化到中断主循环七段式和五段式的算法差异最终要落到STM32的定时器配置和中断程序里。下面我用标准外设库的写法其实寄存器时序逻辑都一样把工程中真正需要配置的几个关键点完整过一遍包括我踩过的一些坑。4.1 定时器初始化与PWM输出通道分配SVPWM需要三对互补PWM输出对应三相桥臂。在STM32F103上通常使用TIM1或TIM8它们有6个输出通道CH1、CH2、CH3和它们的互补通道CH1N、CH2N、CH3N并能配置死区插入。初始化时几个要点首先是时基配置。如果PWM频率定为20kHz中心对齐模式下计数器频率设为2乘以20kHz乘以ARR1。假设定时器时钟为72MHz选择预分频为0那么PSC0ARR 72000000 / (2 × 20000) - 1 1799。其次是输出比较模式。SVPWM要求PWM输出为PWM1或PWM2模式配合中心对齐的向上/向下计数。这里有一个容易忽略的细节必须把TIM_OCInitStructure.TIM_OCMode设置为TIM_OCMode_PWM1且TIM_OCPolarity设置为高电平有效否则互补输出的逻辑关系会乱。然后是死区配置。TIM_BDTR寄存器里的DTG位段控制死区时间计算公式因死区范围而异。对于1微秒死区72MHz时钟下计数72次换算成DTG值需要查阅参考手册。我把常用死区时间对应的DTG值列在下面方便对照死区时间目标72MHz时钟下的计数DTG值配置0.5微秒360x24对应高8位和低6位的组合需按手册公式1.0微秒720x481.5微秒1080x6C2.0微秒1440x90注意DTG的编码不是简单的计数转换它分为四段范围每段使用不同的步长具体计算要按参考手册的公式来。我最开始直接用计数数值填入结果死区完全不对后来仔细看手册才明白编码规则。/* TIM1 PWM输出初始化关键代码简化 */ TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_CenterAligned1; TIM_TimeBaseStructure.TIM_Period 1799; /* 20kHz at 72MHz */ TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); /* 死区配置以1微秒为例 */ TIM_BDTRInitStructure.TIM_OSSRState TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime 0x48; /* 实际值以手册公式为准 */ TIM_BDTRInitStructure.TIM_Break TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput TIM_AutomaticOutput_Enable; TIM_BDTRConfig(TIM1, TIM_BDTRInitStructure);4.2 中断服务函数中的SVPWM计算流程整个SVPWM的计算在更新中断里完成。中心对齐模式下更新事件在计数器计数到0和ARR时都会触发如果两边都进中断相当于计算频率翻倍。一般建议只在一个更新事件里做计算另一个用作ADC采样触发避免中断负载过高。我在工程中是这样安排中断流程的在计数器计数到0时触发更新中断中断里完成电流采样配置为同时触发ADC、坐标变换、PID计算和SVPWM的占空比更新。因为中心对齐模式下计数到0的时刻正好是对称波形的起始点此时写入新的比较值不会产生毛刺。下面是SVPWM核心计算和比较寄存器更新的示例代码包含扇区判断、中间变量计算和五段/七段的切换逻辑/* SVPWM核心函数输入Ualpha, Ubeta以及模式选择 */ void SVPWM_Calc(float Ualpha, float Ubeta, uint8_t mode) { float V1, V2, V3; float X, Y, Z; float T1, T2, Ta, Tb, Tc; uint8_t sector; float Ts 1.0f / 20000.0f; /* PWM周期 */ float Udc 24.0f; /* 母线电压 */ /* 1. 扇区判断查表法 */ V1 Ubeta; V2 (SQRT3_2 * Ualpha) - (0.5f * Ubeta); V3 (-SQRT3_2 * Ualpha) - (0.5f * Ubeta); sector 0; if (V1 0) sector | 1; if (V2 0) sector | 2; if (V3 0) sector | 4; /* 扇区映射表根据V1,V2,V3正负得到实际扇区号 */ static const uint8_t sector_map[6] {2, 4, 3, 1, 5, 6}; sector sector_map[sector]; /* 2. 计算X、Y、Z中间量 */ X SQRT3 * Ubeta * Ts / Udc; Y (1.5f * Ualpha SQRT3_2 * Ubeta) * Ts / Udc; Z (1.5f * Ualpha - SQRT3_2 * Ubeta) * Ts / Udc; /* 3. 根据扇区选择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; } /* 4. 过调制处理限制T1T2不超过Ts */ if ((T1 T2) Ts) { float scale Ts / (T1 T2); T1 * scale; T2 * scale; } /* 5. 根据模式计算三相切换点 */ if (mode MODE_SEVEN_SEG) /* 七段式 */ { Ta (Ts - T1 - T2) * 0.25f; Tb Ta T1 * 0.5f; Tc Tb T2 * 0.5f; } else /* 五段式以000零矢量为例 */ { Ta (Ts - T1 - T2) * 0.5f; Tb Ta T1; Tc Tb T2; } /* 6. 根据扇区将切换点映射到三相比较寄存器 */ float Ta_comp, Tb_comp, Tc_comp; switch (sector) { case 1: Ta_comp Tb; Tb_comp Ta; Tc_comp Tc; break; case 2: Ta_comp Ta; Tb_comp Tc; Tc_comp Tb; break; case 3: Ta_comp Ta; Tb_comp Tb; Tc_comp Tc; break; case 4: Ta_comp Tc; Tb_comp Tb; Tc_comp Ta; break; case 5: Ta_comp Tc; Tb_comp Ta; Tc_comp Tb; break; case 6: Ta_comp Tb; Tb_comp Tc; Tc_comp Ta; break; } /* 7. 将时间转换为定时器计数值并写入CCR */ TIM1-CCR1 (uint16_t)(Ta_comp / Ts * (TIM1-ARR 1)); TIM1-CCR2 (uint16_t)(Tb_comp / Ts * (TIM1-ARR 1)); TIM1-CCR3 (uint16_t)(Tc_comp / Ts * (TIM1-ARR 1)); }这段代码把五段和七段的分支很清晰地分开了。实际使用中模式切换在运行前确定不会在运行中频繁切换。如果要支持在线切换需要注意切换瞬间如果T1或T2有跳变可能产生电流冲击我先在仿真里验证没有问题之后才在硬件上做切换测试。4.3 为什么中间变量X、Y、Z的推导这么重要我刚接触SVPWM时直接把别人代码里的扇区判断和作用时间复制过来跑电机倒也能转但一改母线电压就开始出问题。后来花了一晚上把X、Y、Z的推导彻底搞明白才发现之前用的比例系数是错的——我用的控制板母线电压是48V而代码是按24V写的。X、Y、Z的推导过程本质上是在α-β坐标系下把相邻矢量的合成关系用正弦定理展开。以第一扇区为例目标矢量Us与U1的夹角为θ那么T1和T2分别为T1 |Us| × sin(60° - θ) / sin(60°) × Ts / |U1| T2 |Us| × sin(θ) / sin(60°) × Ts / |U2|将|U1| |U2| 2/3 × Udc代入再结合Uα |Us| × cos(θ)、Uβ |Us| × sin(θ)就能得到T1 (√3 × Uα - Uβ) × Ts / Udc 的一半也就是和Z、Y相关的组合。这个过程我建议每个人都推一遍因为扇区不同中间变量的正负组合不同如果你只记住了中间代码而不知道推导过程那当你的电机出现电流波形畸变时你都不知道该往哪个方向排查。5. 五段式与七段式的实测对比数据为了把两种模式的差异量化我在同一块STM32F103控制板上做了对比测试母线电压24VPWM频率20kHz死区1微秒电机为空载和轻载两种工况。测功机记录电流波形和THD功率分析仪记录开关损耗通过母线电流和管壳温度间接推算。5.1 不同调制比下的THD数据测试时保持电机转速稳定通过调整给定电压幅值来改变调制度。下面给出实测THD数据相电流总谐波畸变率以基波电流为基准调制度七段式THD五段式THD差异0.39.8%12.6%28.6%0.55.4%6.8%25.9%0.73.1%3.6%16.1%0.91.8%2.0%11.1%1.0过调制边缘2.2%2.3%4.5%可以看到调制度越低五段式的谐波劣势越明显。这是因为低调制度下零矢量占比很大五段式的零矢量集中分布导致电流纹波集中在特定频段而七段式的零矢量分散在周期起止和中间频谱更均匀。5.2 开关损耗的间接对比开关损耗和开关次数直接相关。在不改变散热条件的情况下用同样的负载连续运行30分钟后记录功率管壳温散热条件七段式稳态壳温五段式稳态壳温温差自然散热68.5℃58.3℃10.2℃强制风冷45.2℃39.6℃5.6℃这里需要说明壳温还受到导通损耗和热阻的影响不是纯粹的开关损耗对比但趋势是明确的五段式在同样工况下结温更低对散热设计要求更宽松。对于功率密度要求高、空间受限的驱动器这是很有价值的优势。5.3 噪音差异的主观评价在相同转速下五段式的电磁噪音比七段式略大特别是在转速较低、调制度不高的时候能明显听到高频啸叫的强度增加。用手机频谱App粗略看一下五段式在载波频率附近的声压级高出大约3到5分贝。如果产品对噪音有要求建议优先选七段式或者用五段式但提高PWM频率来把噪音推到超声波范围。6. 选型建议与实际调试心得写到最后这部分我把几个比较关键的选型判断依据和调试经验整理一下。这些不是理论推演都是我在板子上实际验证过的结论。6.1 什么场景该选五段式什么场景选七段式简单归纳一下我的判断矩阵母线电压较低如12V到36V范围、对效率极度敏感且硬件死区时间能做到1微秒以下优先考虑五段式。低压大电流场景下开关损耗在总损耗中占比更高省下三分之一的开关损耗对温升是实打实的帮助。对电流谐波、低速平稳性和噪音有严格要求比如伺服、医疗设备、精密转台直接用七段式。谐波优势换来的是更好的低速特性和更小的转矩脉动。开关频率较高16kHz以上时五段式的谐波劣势会被频率推高到不容易听到或影响较小的频段这时五段式的效率优势更值得利用。做过调制区调制度大于0.907时两种算法的差异迅速缩小这时可以自由选择代码实现更简单的那一种。6.2 调试中容易忽略的三个细节第一个细节是中心对齐模式的计数对齐方式。STM32的TIM_TimeBaseStructure里有一个TIM_CounterMode_CenterAligned1和CenterAligned2的区别区别在于比较中断标志在向上计数还是向下计数时置位。如果用的是CenterAligned1中断标志只在向下计数时置位CenterAligned2则只在向上计数时置位CenterAligned3两边都置位。这会影响你ADC采样触发的时机选择不当会导致采样点落在开关切换的瞬间采到电流尖峰。第二个细节是写入CCR寄存器的时间点。在中心对齐模式下如果更新中断里写完CCR后计数器马上到达比较点新值可能立即生效造成一个周期的占空比跳变。稳妥的做法是在计数器计数到0时更新CCR这样比较值在一个完整的半周期内生效。如果你的控制频率足够快这个误差可能不明显但在高载波频率下会导致电流波形出现周期性扰动。第三个细节是浮点运算的时间开销。STM32F103不带硬件浮点单元float运算全靠软件模拟一次SVPWM计算加上坐标变换大概需要20到30微秒在72MHz主频下。如果你的控制频率是20kHz中断周期50微秒留给PID和控制逻辑的时间就相对紧张了。这时可以把SVPWM函数里的除法改成查表或定点运算比如预先算好Ts/Udc的倒数用乘法替代除法能明显减少中断时间。我在F103上做优化之后SVPWM计算时间从28微秒降到了16微秒。6.3 低成本方案下的实测效果最后说一个我觉得值得参考的测试结果。在一套总共不到50元的驱动板方案上STM32F103C8T6加IR2101驱动加NMOS我用七段式SVPWM驱动一个废弃的云台无刷电机开环运行到3000转时电流波形依然干净THD在4%左右。换成五段式后同样转速下THD变成5.5%但功率管温度下降了8度左右。对于航模电机这类对噪音不敏感的负载五段式确实更有性价比。SVPWM的两种实现方式的取舍说到底是在效率、谐波、热设计和代码复杂度之间做权衡。先把七段式的对称结构跑通理解每一段零矢量的作用再改造成五段式你会发现整个切换过程只需要改几行代码但对系统特性的影响却贯穿了热设计、滤波和噪音控制。希望这篇实战对比能帮你少走一些弯路。
返回列表