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

资讯详情

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

STM32F4无感FOC中PLL替代反正切:解决滑膜观测器角度抖动

STM32F4无感FOC中PLL替代反正切:解决滑膜观测器角度抖动 1. 从滑膜观测器的角度抖动说起为什么换PLL如果你调过STM32F4平台上的无感FOC大概率经历过这个场景电机在中低速运行时能转但一到某个转速区间转速波形就开始出现周期性抖动示波器上看到的是角度估算值在真实值附近来回摆动电流波形也跟着出现毛刺。你反复调滑膜观测器的增益调来调去要么抖得轻一点但动态响应变慢要么响应快了抖动又加剧。这个矛盾本质上不是滑膜观测器本身的问题而是它输出的是反电动势的开关信号里面天然含有高频抖振分量直接拿这个信号去反正切求角度抖动就会被放大。滑膜观测器的核心思路是构造一个滑模面让估算电流跟踪实际电流当系统进入滑模面后反电动势的估算值就等价于滑模控制量经过低通滤波后的结果。问题就出在这个低通滤波上截止频率设低了角度估算滞后高速时误差大设高了高频抖振滤不干净角度抖动明显。而且低通滤波引入的相位延迟还需要额外做角度补偿补偿系数跟转速相关标定起来非常麻烦。PLL锁相环的思路则完全不同。它不直接对反电动势做反正切而是把估算的反电动势α、β分量送入一个锁相环结构通过PI调节器让估算角度跟踪真实角度。PLL本质上是一个闭环角度跟踪系统它对高频噪声有天然的抑制能力因为PI调节器本身就是一个低通特性而且它跟踪的是角度误差的积分稳态时角度误差趋近于零。换句话说PLL把“求角度”这件事从一个开环计算变成了闭环跟踪抖动被闭环带宽压制住了。我在STM32F407上做过对比测试同一台无刷电机同一组滑膜观测器参数只把角度提取环节从反正切换成PLL转速波动从±30rpm降到±8rpm左右电流波形明显干净了。这个改善在中低速段尤其明显因为低速时反电动势幅值小反正切对噪声更敏感而PLL的闭环特性可以把噪声压下去。注意PLL不是万能的它解决的是角度提取环节的抖动问题如果滑膜观测器本身的估算反电动势就不准PLL也救不回来。所以前置的滑膜观测器参数还是要调好PLL是在此基础上的优化。2. STM32F4上PLL锁相环的数学结构与离散化2.1 PLL的基本框图与传递函数PLL锁相环在电机控制里的典型结构是这样的估算反电动势的α、β分量经过Park变换到估算角度对应的旋转坐标系得到ε -Eα·cosθ_est - Eβ·sinθ_est这个ε就是角度误差信号。当估算角度接近真实角度时ε近似正比于角度误差Δθ。然后ε经过PI调节器输出角速度估算值ω_est再经过积分得到角度θ_est。用传递函数描述从真实角度θ到估算角度θ_est的闭环传递函数为θ_est(s)/θ(s) (Kp·s Ki) / (s² Kp·s Ki)这是一个典型的二阶系统自然频率ωn sqrt(Ki)阻尼比ζ Kp/(2·sqrt(Ki))。通常取ζ 0.707最佳阻尼然后根据需要的带宽确定ωn。比如想要100Hz的跟踪带宽ωn 2π·100 ≈ 628 rad/s那么Ki ωn² ≈ 394384Kp 2·ζ·ωn ≈ 888。这个参数设计方法比滑膜观测器那种“试凑法”要清晰得多因为PLL的带宽有明确的物理意义你可以直接根据电机的最高运行频率来选。一般取PLL带宽为电机基频的5到10倍既能保证跟踪速度又能有效抑制噪声。2.2 离散化实现与STM32F4的定点/浮点选择STM32F407带FPU所以直接用浮点运算最省事。离散化用后向欧拉法// PLL参数 #define PLL_KP 888.0f #define PLL_KI 394384.0f #define PLL_TS 0.0001f // 10kHz控制频率 typedef struct { float theta; // 估算角度 float omega; // 估算角速度 float err; // 角度误差 float integral; // 积分项 } PLL_State; void PLL_Update(PLL_State *pll, float Ealpha, float Ebeta) { // 计算角度误差 float sin_th arm_sin_f32(pll-theta); float cos_th arm_cos_f32(pll-theta); pll-err -Ealpha * cos_th - Ebeta * sin_th; // PI调节器 pll-integral PLL_KI * pll-err * PLL_TS; pll-omega PLL_KP * pll-err pll-integral; // 积分得到角度 pll-theta pll-omega * PLL_TS; // 角度归一化到0~2π if (pll-theta 6.2831853f) pll-theta - 6.2831853f; if (pll-theta 0.0f) pll-theta 6.2831853f; }这里有几个实操细节值得说。第一arm_sin_f32和arm_cos_f32是CMSIS-DSP库的查表加插值实现速度比标准库快很多在168MHz的F407上单次调用大约20个周期10kHz控制频率下完全够用。第二积分项要做限幅防止启动时误差过大导致积分饱和一般限幅到额定角速度的1.5倍。第三角度归一化用减法比用fmodf快因为fmodf是函数调用减法就是一条指令。如果你用的是STM32F401这种没有FPU或者FPU性能较弱的型号可以考虑定点实现但说实话F4系列基本都带FPU除非成本压到极致否则没必要折腾定点。我试过在F401上用Q15定点跑PLL代码复杂度翻倍效果还不如浮点直接跑。2.3 与滑膜观测器的衔接反电动势从哪来PLL的输入是估算反电动势Eα、Eβ这两个量来自滑膜观测器。典型的滑膜观测器输出是// 滑膜观测器输出 float Ealpha_est LPF(sliding_mode_output_alpha); float Ebeta_est LPF(sliding_mode_output_beta);这里的LPF截止频率通常设得比较低比如200Hz到500Hz目的是滤掉滑模的开关抖振。但正如前面说的这个LPF会引入相位延迟。用了PLL之后LPF的截止频率可以适当提高因为PLL本身对高频噪声有抑制不需要LPF承担全部滤波任务。我一般把LPF截止频率从200Hz提高到500Hz这样相位延迟减小PLL的输入信号更“新鲜”整体角度跟踪更快。提示LPF截止频率提高后PLL的PI参数可能需要微调因为输入噪声增大了。如果发现角度还是有抖动先把PLL的Kp减小、Ki适当增大让闭环带宽降一点。3. 在STM32F4工程里落地PLL的完整步骤3.1 定时器与ADC配置控制频率的选择FOC的控制频率决定了PLL的离散化步长进而影响参数设计。STM32F4的定时器位数足够灵活TIM1是16位高级定时器适合做PWM生成TIM2/TIM5是32位通用定时器适合做控制循环触发。我一般用TIM1产生20kHz的PWM用TIM1的更新事件触发ADC采样然后在ADC中断里跑FOC和PLL控制频率就是20kHz。但20kHz下PLL的TS 50μs如果PLL带宽设100Hzωn·TS ≈ 0.031离散化误差可以忽略。如果你把控制频率降到10kHzTS 100μsωn·TS ≈ 0.063也还能接受。但再低就不行了比如5kHz下ωn·TS ≈ 0.126离散化带来的相位误差就明显了需要改用双线性变换或者Tustin离散化。// TIM1配置为20kHz PWM中心对齐模式 void TIM1_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_CenterAligned1; TIM_TimeBaseStructure.TIM_Period 4200 - 1; // 168MHz/2/4200 20kHz TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 更新事件触发ADC TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); TIM_Cmd(TIM1, ENABLE); }中心对齐模式下更新事件发生在计数器到零和到峰值时所以实际触发频率是PWM频率的两倍也就是40kHz。如果你在更新中断里跑FOC控制频率就是40kHz。这个细节很容易搞错我第一次调的时候以为控制频率是20kHz结果PLL参数算出来带宽翻倍角度抖得厉害。后来用示波器量了一下中断频率才发现问题。3.2 滑膜观测器与PLL的代码集成整个无感FOC的代码流程是这样的ADC中断触发 → 读取相电流 → Clarke变换 → Park变换 → 电流PI → 反Park变换 → SVPWM输出 → 滑膜观测器更新 → PLL更新 → 得到角度和速度。void ADC_IRQHandler(void) { // 读取电流 Ia ADC_GetConversionValue(ADC1) * CURRENT_SCALE; Ib ADC_GetConversionValue(ADC2) * CURRENT_SCALE; // Clarke变换 Ialpha Ia; Ibeta (Ia 2.0f * Ib) * ONE_BY_SQRT3; // Park变换 float sin_th arm_sin_f32(pll.theta); float cos_th arm_cos_f32(pll.theta); Id Ialpha * cos_th Ibeta * sin_th; Iq -Ialpha * sin_th Ibeta * cos_th; // 电流PI Vd PI_Update(pi_d, Id_ref - Id); Vq PI_Update(pi_q, Iq_ref - Iq); // 反Park变换 Valpha Vd * cos_th - Vq * sin_th; Vbeta Vd * sin_th Vq * cos_th; // SVPWM SVPWM_Update(Valpha, Vbeta); // 滑膜观测器 SMO_Update(Ialpha, Ibeta, Valpha, Vbeta); // PLL更新 PLL_Update(pll, smo.Ealpha_est, smo.Ebeta_est); // 清除中断标志 ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); }这段代码里有个顺序问题PLL更新必须在滑膜观测器之后因为PLL用的是滑膜观测器输出的反电动势。但PLL输出的角度又要在下一次Park变换时使用所以实际上角度是滞后一个控制周期的。这个滞后在20kHz下是50μs对应电角度延迟很小可以忽略。但如果你的控制频率只有5kHz200μs的延迟在高速时就会造成明显误差需要做角度预测补偿。3.3 启动阶段的处理PLL的初始化与切换无感FOC的启动是个麻烦事因为低速时反电动势太小滑膜观测器估不准PLL也就跟不准。常见的做法是先用开环强拖把电机拉到一定转速后再切换到闭环。切换瞬间PLL的积分项需要预置到当前的开环角速度角度直接对齐开环角度否则PLL会有一个大的暂态过程。void FOC_Startup(void) { // 开环强拖阶段 for (int i 0; i 2000; i) { openloop_theta OPENLOOP_OMEGA * PLL_TS; SVPWM_Update(OPENLOOP_VOLTAGE * arm_cos_f32(openloop_theta), OPENLOOP_VOLTAGE * arm_sin_f32(openloop_theta)); delay_us(50); } // 切换到闭环前预置PLL状态 pll.theta openloop_theta; pll.omega OPENLOOP_OMEGA; pll.integral OPENLOOP_OMEGA; // 积分项预置为当前角速度 pll.err 0.0f; // 使能闭环 closed_loop_enable 1; }这里pll.integral OPENLOOP_OMEGA是关键因为PI调节器的输出是Kp·err integral稳态时err≈0输出就是integral。如果不预置integral从零开始PLL输出角速度会从零慢慢爬升电机就会失步。我踩过这个坑切换瞬间电机“咯噔”一下电流冲到限幅值后来加上预置就平顺了。注意开环强拖的角速度不能太低一般至少要到额定转速的10%到15%否则反电动势还是太小切换后PLL可能锁不住。具体值取决于电机参数KV值高的电机可以低一些KV值低的电机需要高一些。4. 调参实战PLL带宽、滑膜增益与滤波器的联合整定4.1 PLL参数对系统性能的影响规律PLL的Kp和Ki不是随便设的它们直接决定了角度跟踪的带宽和阻尼。我整理了一个参数影响表方便你快速判断调整方向参数变化角度跟踪速度角度抖动抗噪声能力适用场景Kp增大变快增大变差高速、动态响应要求高Kp减小变慢减小变好低速、噪声大Ki增大变快增大变差需要快速消除稳态误差Ki减小变慢减小变好稳态精度要求不高带宽ωn增大变快增大变差高速运行阻尼ζ减小变快振荡变差不推荐实际调参时我一般先按ζ0.707、ωn2π·(电机基频×5)算一组初值然后根据实测波形微调。如果发现角度有低频振荡说明阻尼不够增大Kp或者减小Ki如果发现角度有高频毛刺说明带宽太高减小Kp和Ki。4.2 滑膜观测器增益与PLL的耦合关系滑膜观测器的增益决定了反电动势估算的收敛速度而PLL的带宽决定了角度跟踪速度。这两个参数是耦合的如果滑膜增益太低反电动势估算滞后PLL输入信号本身就不准角度跟踪再好也没用如果滑膜增益太高反电动势抖振大PLL输入噪声大角度抖动也会增大。我的经验是先把滑膜观测器调到一个“勉强能用”的状态也就是电机能转、角度大致跟得上然后在此基础上调PLL。调PLL的时候先把Ki设为零只调Kp让角度能跟上但略有滞后然后逐渐增大Ki直到滞后消除但抖动不明显增加。这个过程有点像调电流环PI先调P再调I。// 滑膜观测器增益典型值以STM32F4 20kHz控制为例 #define SMO_K_SLIDE 0.5f // 滑模增益 #define SMO_LPF_FC 500.0f // LPF截止频率Hz #define SMO_TS 0.00005f // 20kHz // PLL参数与滑膜观测器匹配 #define PLL_KP 600.0f #define PLL_KI 200000.0f这组参数在额定转速3000rpm、极对数4的电机上跑过转速波动在±10rpm以内。但换一台电机就需要重新调因为反电动势常数不同滑膜观测器的输出幅值不同PLL的等效增益也就不同。4.3 实测波形分析与问题定位调参过程中示波器是最好的朋友。我一般用DAC输出PLL的err信号和omega信号或者用SWO打印到上位机看波形。几个典型问题问题一角度波形有周期性尖峰。这通常是滑膜观测器的开关噪声导致的检查LPF截止频率是否太低或者PLL的Kp是否太大。把LPF截止频率提高同时减小PLL的Kp尖峰一般会消失。问题二角度波形整体滞后。这是PLL带宽不够增大Kp和Ki。但要注意带宽增大后抖动也会增大需要折中。问题三角度波形在某个转速区间振荡。这是PLL的阻尼不够或者滑膜观测器在该转速下估算不准。先检查滑膜观测器的反电动势波形是否正常如果反电动势本身就有问题先调滑膜。问题四启动时角度锁不住。检查PLL积分项预置是否正确以及开环切换转速是否足够高。提示用DAC输出内部变量是最方便的调试手段STM32F4有两个DAC通道可以同时输出err和omega接示波器就能看。如果没有DAC用SWO的ITM输出也行但采样率有限看不了高频细节。5. 几个容易踩的坑与我的处理方式5.1 角度归一化与三角函数查表的精度问题PLL输出的角度需要归一化到0到2π然后用arm_sin_f32和arm_cos_f32计算三角函数。CMSIS-DSP的这两个函数用的是查表加线性插值表长256点精度大约0.01弧度。对于FOC来说这个精度足够了因为电流环的带宽远低于此。但如果你发现电流波形有周期性畸变可以试试用更高精度的查表比如1024点表或者直接用sinf和cosfF4的FPU算这个大概50个周期20kHz下也够用。我试过用sinf替换arm_sin_f32电流波形的THD从3.2%降到2.8%改善有限但确实有。如果你的应用对噪声敏感可以试试。5.2 低速时PLL的失效与应对PLL在低速时的问题和滑膜观测器一样反电动势太小信噪比低。当转速低于额定转速的5%时PLL的角度误差会明显增大甚至锁不住。这时候有几个选择一是用开环强拖到更高转速再切闭环二是用高频注入法估算低速角度但这需要额外的硬件支持和复杂的算法三是用IF启动也就是电流闭环、频率开环等转速上来再切。我在实际项目中一般用第一种因为简单可靠。如果应用要求低速大扭矩那就得上高频注入但那是另一个话题了。5.3 参数整定的顺序不能乱很多人调无感FOC的时候喜欢把滑膜观测器、PLL、电流环一起调结果哪个参数影响哪个都搞不清楚。我的建议是严格按顺序来先调电流环有传感器或者开环确保电流跟踪正常再调滑膜观测器让反电动势估算大致准确最后调PLL优化角度跟踪。每一步都确认波形正常后再进行下一步这样出问题容易定位。这个顺序看起来慢但实际上比乱调快得多。我见过有人调了一周都没调好按这个顺序半天就搞定了。5.4 不同电机参数的适配PLL的参数和电机参数是相关的。反电动势常数大的电机同样转速下反电动势幅值大PLL的等效增益高Kp和Ki可以适当减小反电动势常数小的电机则相反。极对数多的电机同样机械转速下电频率高PLL带宽需要相应提高。我一般会先测一下电机的反电动势常数和极对数然后按公式算一组初值再微调。这样比盲试快很多。// 根据电机参数计算PLL初值 // Ke: 反电动势常数(V/krpm) // pp: 极对数 // rpm_max: 最高转速 float Ke 12.5f; // 示例 float pp 4.0f; float rpm_max 3000.0f; float fe_max rpm_max * pp / 60.0f; // 最高电频率 float omega_n 2.0f * 3.14159f * fe_max * 5.0f; // 带宽取5倍基频 float zeta 0.707f; float Kp 2.0f * zeta * omega_n; float Ki omega_n * omega_n;这段代码算出来的Kp和Ki可以直接用然后根据实测微调。注意这里的Kp和Ki是连续域的离散化时Kp不变Ki要乘以TS。我在代码里把Ki已经乘过TS了所以直接赋值就行。6. 写在最后的一些个人体会PLL替代反正切做角度提取这个改动本身不大代码量也就几十行但效果提升很明显。我第一次在F407上跑通的时候看到转速波动从±30rpm降到±8rpm确实有点惊喜。后来在多个项目上验证只要滑膜观测器调得不太差PLL都能带来可观的改善。不过也要说清楚PLL不是银弹。它解决的是角度提取环节的噪声和抖动如果滑膜观测器本身估算的反电动势就有系统性误差比如参数不匹配导致的幅值偏差PLL是修不了的。所以电机参数辨识、滑膜观测器的增益整定这些基础工作还是要做扎实。另外PLL的带宽选择需要根据应用场景折中。如果你的应用对动态响应要求高比如伺服定位带宽可以高一些如果是风机水泵这类对稳态精度要求高、动态要求低的带宽可以低一些换取更平滑的角度。最后分享一个调试小技巧用DAC输出PLL的err信号然后用手轻轻给电机轴施加阻力观察err的变化。如果err能快速回到零附近说明PLL跟踪正常如果err持续偏大或者振荡说明参数需要调整。这个方法比看转速波形更直接因为err就是角度误差的直接反映。
返回列表