ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32 PMSM FOC电流环实战:从数学推导到代码落地

STM32 PMSM FOC电流环实战:从数学推导到代码落地 做电机驱动的朋友应该没人绕得开FOC这几个字母。前阵子我在写一套基于STM32的PMSM驱动程序项目代号就叫“升魂浩荡”名字听着中二但东西是真的往实里做的。整个过程从电流环的数学推导到C代码落地再到现在能在示波器上看到干净的电流波形和稳定转速中间踩了不少坑也把整个链路彻底跑通了。这篇就把FOC驱动里最核心的电流环推导过程连同程序实现的关键细节一起梳理出来给正准备入门或者卡在调试阶段的朋友做个参考。这不仅仅是理论复习更是一份贴着实际项目走的流程记录。FOC控制、电机驱动、电流环推导这些关键词如果只看教材很容易被里面的矩阵变换和公式劝退。但当你手里有一个真实电机有一个可以定点溢出的单片机有示波器或者串口绘图工具把数学公式翻译成代码再去调感受会完全不同。我会从整体方案讲到坐标变换再讲到PI参数怎么定、程序怎么组织最后把调试时遇到的最典型的几个问题列出来希望帮你少走弯路。1. 项目概述这套FOC驱动到底要干什么1.1 项目目标与控制需求拆解先说清楚这套驱动要解决什么问题。我做的是一台PMSM永磁同步电机的低压驱动器目标应用是小功率位置/速度控制场景额定电压24V额定电流几安培峰值电流需要限制到安全范围。控制要求不算极端但必须具备三环结构最外环是位置环中间是速度环最内是电流环。此次项目重点放在电流环因为电流环是整个FOC控制的基础电流响应快慢直接决定了速度和位置环能跑多稳。整个控制回路的核心逻辑是这样位置环根据目标位置和当前位置计算出目标速度速度环根据目标速度和实际转速计算出目标电流也就是iq的给定值电流环让实际电流快速、准确地跟踪这个给定值。最内环天生要求最高带宽而带宽的瓶颈往往不在PI参数而在采样频率、PWM开关频率和程序执行时间。因此电流环的设计要和硬件强绑定软件上的每一个延时都会折损电流环性能。从电机驱动角度来说FOC控制的关键优势在于把交流电机的非线性强耦合模型通过坐标变换转化成两个互相独立的直流分量来控制。在同步旋转坐标系下d轴电流控制励磁q轴电流控制转矩。对PMSM这种转子本身带有永磁体的电机通常采用id0控制这样电机所有电流都用来产生转矩效率最高控制模型也最简单。1.2 控制方案选型为什么选了FOC而不是方波驱动行外人可能觉得直流电机都能直接给电压转起来交流电机无非也是三根线给三相正弦波不行吗实际上PMSM需要的是与转子位置同步旋转的三相电流波形如果只是在定子上给一个固定频率的正弦电压转子会失步、震荡甚至反转。方波驱动也叫六步换相虽然也能转但它本质上是离散的转矩脉动大低速和伺服场景根本没法用。FOC则完全不同它通过实时获取转子位置把三相静止坐标系下的交流量投影到转子旋转坐标系下把正弦控制问题转化成直流控制问题。这就好比在旋转木马上观察旁边的人如果你自己也跟着旋转那么那个人在你眼里是静止的。FOC就是把自己“站到转子上”去看电流这个视角转换是整套控制的地基。方案选型时还考虑过直接转矩控制DTCDTC动态响应快但转矩脉动和噪声比较大电流波形也不如FOC干净。对伺服类应用来说FOC是更平衡的答案。因此最终确定了基于PMSM的FOC三环控制方案电流环作为本次推导的重点整个软件架构也围绕电流环来设计。2. 电流环的数学推导从三相正弦到两个直流量2.1 Clarke变换三相平面到两相静止坐标电流环从三相电流采样开始但三相绕组上的正弦电流是交流量直接拿来做PI控制会有稳态误差因为PI控制器处理不了随时间变化的正弦给定。第一步需要做坐标变换先把三相静止坐标系abc变换到两相静止坐标系αβ这个变换叫Clarke变换。三相电流满足基尔霍夫电流定律也就是iaibic0理论上只要采两相就够了。等幅值形式的Clarke变换公式如下iα iaiβ (ia 2*ib) / √3如果三相电流是标准的120°正弦波变换之后iα和iβ是在静止坐标系下互相垂直的两个正弦量但频率不变依然是随时间变化的交流量。矩阵形式可以写成| iα | | 1 -1/2 -1/2 | | ia | | iβ | | 0 √3/2 -√3/2| | ib | | ic |系数2/3在等幅值变换时化简掉了。Clarke变换本身不谈有多少物理意义它就是纯粹的数学投影把空间上相差120°的三个绕组等效成互相垂直的两个绕组。在程序实现时一定要注意等幅值变换和等功率变换的系数区别这会影响后续PI参数的标定。如果用了等功率变换Clark变换矩阵系数是√(2/3)后面算转矩或反电动势时要同步调整。2.2 Park变换站到转子上看电流Clarke变换之后依然是交流量直接做PI还是会存在稳态误差。Park变换将静止的αβ坐标系旋转到与转子同步的dq旋转坐标系旋转角度就是转子电角度θ。变换公式为id iα * cosθ iβ * sinθiq -iα * sinθ iβ * cosθ变换之后在稳态情况下id和iq都变成了直流量。id是电流在转子磁场方向上的分量代表励磁分量iq是垂直于转子磁场方向的分量代表转矩分量。对表贴式PMSM通常令id0控制目标就是让iq跟踪给定值。这里就能看出转子位置信息的关键作用了如果θ不准Park变换得到的就是错误的dq电流控制效果会大打折扣。所以FOC系统里位置传感器编码器/霍尔或者无感观测器的精度和延迟直接影响整个电流环的质量。顺带一提θ是电角度如果有极对数为p电机转一圈对应p个电角度周期机械角度乘以p才是电角度这个换算在程序里要小心。2.3 电机方程与PI参数推导从传递函数到Kp、Ki有了dq坐标系下的电流表达式就能在这个坐标系里写出PMSM的电压方程Vd Rsid Ldd(id)/dt - ωeLqiqVq Rsiq Lqd(iq)/dt ωe*(Ld*id ψf)其中Rs是定子电阻Ld和Lq是d/q轴电感ωe是电角速度ψf是永磁体磁链。对表贴式PMSMLd≈Lq记为L。这个方程有个特点d轴和q轴之间是耦合的。d轴方程里出现了含iq的项q轴方程里出现了含id的项还有反电动势项ωe*ψf。这些耦合项在低速时可以忽略高速时必须用前馈补偿。电流环调节器的设计通常是把耦合项当成扰动先只考虑一阶惯性模型G(s) 1 / (L*s Rs)PI控制器传递函数为C(s) Kp Ki / s电流环开环传递函数是两者相乘我用零极点对消法来整定PI参数也就是让PI控制器的零点去抵消被控对象的极点设置 Kp/Ki L/Rs这样开环传递函数变成 Kp / (L*s)整个闭环传函退化成典型一阶系统Gc(s) (Kp/L) / (s Kp/L) ωc / (s ωc)其中ωc就是电流环带宽。于是得到PI参数Kp L * ωcKi Rs * ωc这个是理论上的整定公式实际使用时带宽ωc一般取开关频率的1/10到1/20比如20kHz的PWM频率电流环带宽取1~2kHz比较合理。拿我手头这台电机来说Rs约0.85ΩL约120μH取电流环带宽1kHzKp 120e-6 * 2π * 1000 ≈ 0.754 V/AKi 0.85 * 2π * 1000 ≈ 5341 V/(A*s)这两个值可以作为初始参数再结合实际电流波形微调。注意这个推导是基于连续域的离散化之后采样周期和延时会影响稳定性实际Kp可能要打点折扣带宽取太低则动态响应慢取太高则系统可能震荡。3. 程序落地把公式变成能跑的代码3.1 代码架构与中断时序有了公式下一步就是让代码在单片机上跑起来。这套FOC程序的核心循环放在PWM中断里我用的PWM频率是20kHz也就是说电流环的执行频率也是20kHz。时间预算非常紧20kHz对应50μs周期实际代码在STM32F405上跑到大约30μs还有余量做故障保护和数据上报。程序架构按功能模块划分电流采样模块负责读ADC并做零点校准坐标变换模块执行Clarke和Park变换PI控制器模块分别处理id和iq两个轴前馈补偿模块计算耦合电压项SVPWM模块把dq电压变成三相互补PWM占空比位置传感器模块负责读取编码器并计算电角度。模块之间通过结构体传递数据这样每个环节都能单独拿出来测试调试效率高很多。中断流程大概是这样的PWM中心对齐时触发ADC采样采样完成中断里读取三相电流实际采两相第三相通过基尔霍夫定律算出接着执行Clarke、Park得到id和iq然后跑id和iq两个PI控制器把输出加上前馈补偿项再经过逆Park得到Vα、Vβ最后用SVPWM生成三路占空比更新到定时器比较寄存器。整个过程就是一条流水线任何一个环节卡住都会影响控制效果。3.2 坐标变换与PI控制器的具体实现代码里的Clarke变换非常直接用不到浮点库但需要把握好数据格式。我全部使用float运算在Cortex-M4F上自带FPU速度足够快。Clarke变换核心代码void clarke_transform(float ia, float ib, float *i_alpha, float *i_beta) { *i_alpha ia; *i_beta (ia 2.0f * ib) * 0.577350269f; // 1/sqrt(3) }Park变换需要用角度计算正弦余弦值。为了在中断里节省时间我用ARM CMSIS-DSP库的arm_sin_cos_f32函数它一次同时算出正弦和余弦比分别调用两个三角函数快得多。void park_transform(float i_alpha, float i_beta, float theta, float *i_d, float *i_q) { float sin_t, cos_t; arm_sin_cos_f32(theta, sin_t, cos_t); *i_d i_alpha * cos_t i_beta * sin_t; *i_q -i_alpha * sin_t i_beta * cos_t; }PI控制器我写了带积分限幅和输出限幅的版本这是电流环控制中的关键工程细节。纯理论的PI会无限积分一旦输出饱和就会产生积分饱和现象电流过冲严重。我的做法是当输出达到限幅值时停止积分累加输出未饱和时正常积分。typedef struct { float kp; float ki; float integral; float out_max; // 输出限幅 float integral_max; // 积分限幅 } pi_ctrl_t; void pi_ctrl(pi_ctrl_t *pi, float ref, float fdb, float ts, float *out) { float err ref - fdb; pi-integral pi-ki * err * ts; if (pi-integral pi-integral_max) pi-integral pi-integral_max; else if (pi-integral -pi-integral_max) pi-integral -pi-integral_max; float pid_out pi-kp * err pi-integral; if (pid_out pi-out_max) pid_out pi-out_max; else if (pid_out -pi-out_max) pid_out -pi-out_max; *out pid_out; }需要注意Ki在离散化时乘了采样周期ts所以伪代码里积分项直接累加的是kierrts而不是ki*err再乘ts。还有一种常见写法是在中断里做 : integral err * ts; output kp * err ki * integral;两种写法等效但参数标定时要统一口径不要混用。3.3 前馈补偿和SVPWM的工程细节前馈补偿在电流环里不能省。dq轴之间有动态耦合外加q轴还存在反电动势项。如果转速升高这些扰动电压幅度可能超过母线电压的一大部分如果只靠PI慢慢去“纠正”电流环的动态响应会很拉胯。我的做法是在PI输出之后直接加上按转子磁链和转速计算的补偿电压Vd_ff -ωe * Lq * iqVq_ff ωe * (Ld * id ψf)具体到id0控制时Vd_ff ≈ -ωe * Lq * iqVq_ff ≈ ωe * ψf。频率越高这些项越重要。实现上只需要把转速、电流和电机参数代入即可代码不超过十行。如果电机参数不够准确前馈补偿效果也不会毁掉控制但会减弱动态中的耦合抑制能力。SVPWM的工程实现也比较有讲究。逆Park变换得到Vα和Vβ之后需要把它变成ABC三相互补的PWM波形。SVPWM比直接SPWM的好处是母线电压利用率更高线性调制范围也更大。简化实现时不需要把扇区判断写得那么复杂直接算出每相接通的占空比即可。void svpwm(float v_alpha, float v_beta, float vdc, float *ta, float *tb, float *tc) { float t1 v_alpha; float t2 -0.5f * v_alpha 0.8660254f * v_beta; float t3 -0.5f * v_alpha - 0.8660254f * v_beta; float tmax fmaxf(fmaxf(t1, t2), t3); float tmin fminf(fminf(t1, t2), t3); float offset (tmax tmin) * 0.5f; float v1 t1 - offset vdc * 0.5f; float v2 t2 - offset vdc * 0.5f; float v3 t3 - offset vdc * 0.5f; *ta v1 / vdc; *tb v2 / vdc; *tc v3 / vdc; }这个实现相当于中心对称的SVPWM简单、稳定、效果好。关键点是v1、v2、v3不能超出[0, vdc]范围超出部分会被夹断产生非线性失真所以要加钳位处理。4. 调试实录那些电流环调试中避不开的坑4.1 电流采样噪声与零点校准电流采样是整个电流环里最容易出问题的地方没有干净的电流反馈信号后面PI调到飞也白搭。我当时用的双电阻采样采样电阻串联在下桥臂在PWM中心对齐时触发ADC。但一开始采出来的电流波形噪声很大尤其在开关动作瞬间会有尖峰干扰。排查下来发现是ADC采样时刻落在了开关噪声窗口里。解决办法是推迟采样触发点到PWM中心附近并且在下桥臂开通一段时间之后再采样避开上下管切换时的振铃。再一个常见的坑是电流采样零点漂移。运放的偏置电压、ADC的偏移都会导致零电流对应的ADC值不是理论中点如果在软件里不校准闭环后会出现固定的电流偏置电机会发热甚至微动。我的做法是在上电初始化时、没有电流的情况下连续采几十次ADC平均值作为零点偏移记录下来。每次采样后先减去这个偏移再做坐标变换。这个校准一定要放在电机上电之前否则offset里面包含了真实电流反而会引入错误。4.2 转子初始位置检测与启动流程FOC要正常工作第一步必须知道转子位置。如果用编码器上电时位置未知转子初始电角度可能是任意值。此时如果强行给定idiq跑起来电机可能会抖动甚至倒转。实际项目中我用的是预定位法上电后给一个固定的d轴电压矢量把转子强行拉到一个已知角度记录编码器值作为零位然后清零电角度。预定位的过程相当于给电机一个非零的d轴电流产生电磁力把转子吸到指定位置。这个方法简单可靠但注意如果负载惯性很大转子可能会震荡。另一个办法是做开环强拖施加旋转电压矢量让电机转起来再切换到闭环但这种启动方式不适合带大负载的场合。对于无传感器FOC没有编码器和霍尔初始位置检测通常用高频注入法向d轴注入高频电压信号通过q轴的高频电流响应判断磁极位置。这个方法我还没完全调通先不展开但方向上是在静止坐标系注入高频信号再解调q轴电流中的2次谐波分量来估算转子位置确实比预定位复杂得多对采样同步性和滤波要求都很高。4.3 PI参数整定的现场操作与原理解读理论推导给出了PI参数的初值但真拿到电调台上还要根据实际波形微调。我个人的整定顺序是先整定d轴再整定q轴先闭环给定一个固定的小电流观察实际电流波形。第一步给我设置的iq_ref为0只给iq一个阶跃参考比如0.5A然后观察iq实际响应。如果波形震荡说明Kp偏大如果响应太慢、半天追不上给定说明Kp偏小。从理论值出发我先按计算的Kp设置Ki先设得比较小然后逐步增加Ki改善稳态误差直到阶跃响应快速无稳态误差、且无明显过冲这组参数就是合适的。注意采样数据和示波器挂钩我用DAC输出iq给定和反馈值直接接到示波器上看。也可以用串口以一定频率上报数据上位机绘图。DAC直出更直观但需要占用两个DAC通道。没有DAC的情况下串口帧率要够快建议控制周期上报否则看不到高频的动态变化。调PI时我踩过一个典型问题电流波形是正弦波形态的“抖动”一开始误以为是PI震荡但后来发现是编码器安装偏心导致的角度波动位置信息里掺入了周期性扰动。这里提醒一句调试FOC时如果电流波形有异常先确认角度信号干净再动PI参数顺序反了会浪费大量时间。4.4 常见问题速查表下面整理一个我在调试过程中最常遇到的几个问题对照方便你排查。现象可能原因排查方法电流波形毛刺大ADC采样时刻落在开关噪声窗口调整采样触发点避开上下管切换瞬间零电流时反馈值偏大运放零点漂移未校准上电时做ADC零点校准电机抖动且电流震荡PI参数过大或角度位置跳动先减小Kp再检查位置信号高速运行时电流跟不上前馈补偿缺失或参数不准确认前馈项已使能核对Rs/L/ψf参数启动瞬间过冲严重积分限幅或输出限幅未合理设置缩小积分限幅和输出限幅低速带载能力弱id0控制下低速转矩不足考虑弱磁控制或修改给定策略这个表看着简单但每一条背后都有实际调试的代价建议把常用的排查手段固化到自己的调试流程里能省下不少反复试错的功夫。写在最后的个人体会从“升魂浩荡”这个项目立项到电流环推导完成、程序写好再到调通出波形整个过程让我对FOC有了更深的理解。给我的感觉是FOC的数学推导看似复杂但核心就那几个关键点——Clarke变换、Park变换、电机方程、PI整定、前馈补偿。真正难的地方在于把这些数学公式“翻译”成能在定时器中断里稳定运行的代码以及面对真实电机时的各种工程细节。如果你正在做FOC驱动的学习和项目调试我比较建议先不要急着上无感算法先把带编码器的电流环调稳再去拓展速度环、位置环最后切入无感方向。电流环是FOC闭环的地基把id和iq的响应调到又快又稳后面所有控制策略都有了可靠的基础。最后分享一个小技巧在调试电流环时建议把PI控制器的输出和电流反馈都引出来放到示波器上对比观察这样可以很直观地分辨到底是参数问题、角度问题还是采样问题。电流环调试不是一次性能调好的参数标定、波形分析、误差溯源这三个步骤来回走几轮才能把FOC驱动打磨到可用的状态。
返回列表