FOC控制算法程序详解:单电压闭环与电压+电流双闭环源码实现
简介这是一份面向单片机与电机控制开发者的FOC控制算法程序包涵盖单电压闭环与电压电流双闭环两种源码方案适用于无感/有感FOC项目开发、学习与调试。包内共687个文件以C/C源码h、c、cpp为核心辅以工程配置ioc、uvprojx、cmake、编译输出o、axf、bin、hex及调试日志等文件整体约27.76MB目录结构完整便于直接对照工程编译、烧录与验证。已有677人学习下载。资源重点呈现电压矢量控制、Clark/Park变换、PID调节与SVPWM生成等关键模块的具体实现并涉及电流采样方式高端/低端、电压与电流闭环的整定思路以及有感和无感FOC的差异对比。通过两份闭环源码的差异学习可快速理解从电压单环到电流内环电压外环的演进逻辑适合想从开环/单环进阶到双环控制的开发者借此搭建自己的FOC驱动框架并深入掌握磁场定向控制。1. 电压直接焊上去电机照样转——但问题从一开机就开始了把母线电压直接接到电机两相上转子会猛地冲到一个固定角度然后堵住电流瞬间拉到几十安培驱动板上的MOS管烫得能煎鸡蛋。FOC控制算法程序要解决的不是“转不转”而是“怎么让电流受控地做功”转子位置一变电压就要跟着换向换早了没转矩换晚了电流全变成热量。单电压闭环是入门时最容易写通的结构——只控电压幅值和角度不碰电流环代码量小、调参直观但转矩随负载波动绕组电阻一变输出就漂。电压电流闭环则把电流采样纳入反馈让力矩真正可预测。这篇文章把两套结构的源码级实现、参数整定顺序和转子初始位置检测一次讲透适合刚把FOC跑通、想搞懂内环逻辑的嵌入式开发者也适合回头审视自己工程里“为什么调了PI还是过冲”的老手。2. FOC控制算法程序的核心变换Clarke、Park与SVPWM的离散化实现先不急着贴电压环代码。FOC控制算法程序里变换层是所有环路的公共底座它决定了两套闭环看到的被控量到底是什么。下面从坐标变换的离散实现和SVPWM的扇区判定讲起这两块写对了后面调PI才有意义。2.1 三相电流到dq轴的离散变换采样时刻决定精度自然坐标系下的三相电流是交流量PI控制器跟踪交流量会有稳态误差所以FOC控制算法程序的第一步是把ia、ib、ic投影到旋转的dq坐标系上让交流量变成直流量。Clarke变换把三相平移到两相静止坐标系公式用等幅值约定iα ia iβ (ia 2*ib) / √3这里假设三相平衡只采样ia和ib即可ic -ia - ib。用等幅值变换而不是等功率变换是因为等幅值下dq电流的幅值直接等于相电流峰值电流环PI参数的物理意义更直观。接下来是Park变换把静止的αβ轴旋转到转子d轴方向上id iα * cosθ iβ * sinθ iq -iα * sinθ iβ * cosθ其中θ是电角度由编码器或观测器提供。离散实现时用float型查表或直接调用libm的sinf/cosf对Cortex-M4F而言单次变换需要约2µs对10kHz电流环来说完全够用。这里注意一个细节Park变换用的角度必须是采样时刻的电角度。如果你的ADC触发和PWM中心对齐但角度值从主循环里读那角度和电流之间就有延迟高速时会产生一个随转速增长的相位误差表现为dq电流交叉耦合。采样时刻的具体做法是PWM在计数器达到周期值的中点更新占空比并触发ADC注入组采样ADC转换完成中断里读取电流和角度寄存器。角度用硬件定时器锁存或者用增量编码器接口的索引信号做硬件同步保证电流和位置是同一瞬间的物理量。2.2 SVPWM的扇区判定与三相占空比计算比查表法省一半Flash坐标变换输出的是Uα、UβSVPWM的任务是把这个合成矢量用相邻两个基本电压矢量加零矢量来等效。扇区判定的常见写法是// 输入Ualpha, Ubeta输出扇区号1~6 int svpwm_sector(float Ualpha, float Ubeta) { float ref1 Ubeta; float ref2 (SQRT3_2 * Ualpha) - (Ubeta * 0.5f); float ref3 (-SQRT3_2 * Ualpha) - (Ubeta * 0.5f); int sector 0; if (ref1 0) sector 1; if (ref2 0) sector 2; if (ref3 0) sector 4; return sector; }SVPWM的扇区判定代码用三个参考电压的正负组合映射到扇区避免了arctan运算。这个判定法对应的是“三相坐标轴上的符号组合”其中SQRT3_2 0.8660254是√3/2。扇区编号从1到6后续计算矢量作用时间时直接查表索引。得到扇区后算两个相邻矢量的作用时间T1、T2// 扇区已知后根据Ualpha/Ubeta计算该扇区两个相邻矢量作用时间 float T1, T2; switch (sector) { case 1: T1 TS * SQRT3 / 2 * ( ... ); break; // 对应U60与U0的组合 // ... 每个扇区一组公式 } float T0 TS - T1 - T2; // 零矢量时间然后需要把T1、T2分配到三相桥臂上这个过程包括“七段式”的零矢量拆分为了让电流纹波最小零矢量时间T0劈成两半分别放在PWM周期的头和尾。具体分配表是固定的扇区1对应ABC三相的占空比分别是A相占空比 (T1 T2) / TSB相占空比 T2 / TSC相占空比 0 —— 不同扇区这个映射关系不同通常用查表一次完成。SVPWM本身不产生转矩闭环效果它只是把电压环或电流环计算出的dq电压指令忠实执行到桥臂上。这里牵涉一个与电流采样强相关的设计参数PWM频率与死区时间的关系。常规IGBT模块死区取2~3µsMOS管驱动的低压板取500ns即可电流环周期一般取PWM频率的1/1即10kHz PWM配10kHz电流环也是比较常用的配置。PWM频率超过20kHz后开关损耗明显上升对调参新手来说10kHz是兼顾可听噪声、采样窗口和损耗的折中值。项目10kHz PWM 10kHz电流环20kHz PWM 10kHz电流环电流环控制带宽上限约1~1.5kHz约1~1.5kHz受计算量限制电流采样窗口100µs内可用约60µs50µs内可用约30µs需注意采样保持时间开关损耗基准约翻倍温升更明显噪声10kHz啸叫明显超出人耳感知适合静音需求要点是电流环频率不一定非要等于PWM频率低频电流环可以用“计数值分频”触发ADC但高频PWM下开关噪声容易耦合进采样信号反而建议降PWM或升电流环到20kHz按硬件条件取舍。3. 单电压闭环FOC控制算法程序的最简转矩控制结构单电压闭环顾名思义控制量是d、q轴的电压参考值反馈量只有转子位置没有电流传感器参与闭环。这种结构多见于对成本敏感的泵类或风机负载它们的共同特点是负载转矩变化缓慢且幅度有限不需要精确力矩控制能把转速稳住就达标。3.1 单电压闭环的控制框图与电压方程前馈电压闭环的核心是电压方程。永磁同步电机在dq坐标系下的定子电压方程为vd R*id Ld*did/dt - ωe*Lq*iq vq R*iq Lq*diq/dt ωe*(Ld*id ψf)其中R是相电阻Ld/Lq是d/q轴电感ψf是永磁磁链ωe是电角速度。稳态时导数项为零单电压闭环的常见做法就是用这个稳态方程反推电压给定目标电流id0、iqIq_ref直接算出vd和vq并输出不做电流反馈。对应的控制流程是// 单电压闭环FOC位置-速度估计-电压前馈-SVPWM输出 void foc_voltage_loop(float speed_ref, float theta_elec, float omega_elec) { // 1. 速度环输出作为q轴电流参考隐含在vq前馈中 float iq_ref speed_pi_update(speed_ref, omega_elec); // 2. 电压前馈忽略微分项只补偿电阻压降与反电动势 float vq R * iq_ref omega_elec * PSI_F; // PSI_F为永磁磁链 float vd -omega_elec * LQ * iq_ref; // 交叉耦合项补偿 // 3. 限制电压矢量模长不超过最大调制电压 clamp_voltage_vector(vd, vq, VDC / SQRT3); // 4. SVPWM输出并写比较寄存器 svpwm_inverse_park(theta_elec, vd, vq); }这段代码里速度环PI输出的是电流参考但要明确速度环输出的iq_ref在这里不经过电流PI而是直接换算成电压。它依赖电机参数R和PSI_F的准确性一旦绕组发热导致R增大实际电流就低于期望值表现为带载能力下降低速时R占主导误差更明显。电压模长限制取VDC/√3这是SVPWM线性调制区的最大电压矢量模长限制超过这个值的vq只会让SVPWM进入过调制区产生大量低次谐波电流波形开始失真。所以单电压闭环的定位是“用电机模型代替电流传感器”模型越准控制越接近期望但模型漂移温度导致R变化天然无法自校正。风扇、水泵这类负载转矩近似与转速平方成正比电压闭环已经足够。3.2 单电压闭环的阻力补偿与起动问题电阻压降项R*iq_ref在高速时占比小但低速时它主导了电压输出而R的估计误差又直接影响低速转矩精度。工程上的补偿方法是测一次实际的相电阻用在25℃冷态下的值再留一个随温度线性修正的系数或者干脆在控制代码里加一个“最小电压起动”逻辑// 低速起动时给一个额外的直轴电压偏置克服静摩擦 if (abs(speed_ref - omega_elec) SPEED_ERR_START) { vd VD_START_BIAS; // VD_START_BIAS取0.5V~2V视母线电压和负载而定 }但也要提醒起动时转子位置未知必须先用后面第五章说的初始位置检测定出d轴再切入电压前馈。代码里如果直接用任意角起转电机可能正转半圈反转半圈触发过流保护——这是单电压闭环FOC控制算法程序初版最常遇到的问题。3.3 单电压闭环参数调整与带宽分配的取舍速度环PI的整定和电压环参数是耦合的因为电压方程前馈虽然有物理依据但参数误差让前馈不是精确的。实践中调整顺序是先把R、L、PSI_F三个电机参数标定准确再按临界比例度法调速度环P最后加I消除稳态误差。三个可调的边界参数参数作用典型范围现场现象速度环Kp决定负载突变时的转速跌落恢复快慢0.5~5.0过大导致q轴电压脉动电流声变粗速度环Ki消除稳态转速误差20~100过大会让转速出现低频振荡周期0.1~1sVD_START_BIAS改善起动摩擦力矩母线电压的1%~3%过大会让电流瞬时偏高可能触发硬件过流单电压闭环的调试难点在于“看不到电流”。转速波形看起来平稳但直观察觉不到力矩波动只能通过电机温度、电流钳测量来间接判断。如果负载转矩突变频繁电压闭环的响应速度会明显落后于电流变化此时才需要引入电流传感器升级成电压电流双闭环。4. 电压电流闭环电流内环配速度外环的级联实现电压电流闭环的全称其实是“速度外环 电流内环”速度环输出电流给定电流环输出dq轴电压电压再进SVPWM。pi控制级联结构的好处是内环先压住电流任何反电动势扰动、母线电压波动都由内环快速抑制外环看到的被控对象干净得多调参不再依赖电机参数。4.1 双闭环结构与带宽差异内环至少比外环快5倍电流环的等效对象是R Ls即一阶惯性加电阻负载速度环的等效对象是电流环闭环后被控对象近似为 J·s转动惯量积分是一阶积分器。级联系统稳定性的经典经验是电流环带宽设为速度环带宽的510倍。带宽比例是“内环至少比外环快5倍”的直观工程经验。常见配置是电流环1~2kHz速度环50~200Hz。这个倍数关系不是拍脑袋定的其物理含义是让外环校正动作发生时内环已经稳定在某一点上避免两个环路出现相位叠加。如果速度环带宽接近电流环低速时就会出现力矩振荡、转速波动周期与电流调节节拍耦合在一起。4.2 电流环PI参数整定从电机电气时间常数出发电流环被控对象近似一阶PI参数用“极点对消”法即可// 电流环PI用“极点对消”整定Kp L*ωc, Ki R*ωc typedef struct { float Kp; float Ki; float integral; float out_max; } pi_controller_t; void pi_current_init(pi_controller_t *pid, float L, float R, float bw) { // bw为目标电流环带宽(rad/s)一般取2000~8000 pid-Kp L * bw; // 比例项消去电感主导的极点 pid-Ki R * bw; // 积分项匹配电气时间常数 pid-integral 0.0f; pid-out_max VDC / SQRT3; } float pi_current_update(pi_controller_t *pid, float ref, float fb) { float err ref - fb; pid-integral pid-Ki * err * DT_S; // DT_S为电流环周期 float out pid-Kp * err pid-integral; if (out pid-out_max) out pid-out_max; if (out -pid-out_max) out -pid-out_max; // 防积分饱和限制积分项范围 if (out pid-out_max || out -pid-out_max) { pid-integral - pid-Ki * err * DT_S; } return out; }极点对消整定法的数学基础是被控对象传递函数为1/(LsR)PI调节器传函为(Kp·sKi)/s令零点s -Ki/Kp -R/L极点被消掉回路降为一阶闭环带宽就等于Kp/L。所以代码里给的不是P和I两个独立参数而是输入目标带宽bwKp、Ki自动算出。bw取2000~8000 rad/s对应318Hz~1.27kHz具体选择受限于PWM频率一个可用下限公式是bw 2π·f_pwm/20。这段代码里有两个工程细节积分限幅用“输出限幅后回退积分”的方式避免PI输出饱和后积分项继续累积导致恢复时的大过冲采样周期DT_S必须精确对应电流环实际执行周期用定时器中断确保固定频率不能裸靠主循环延时。还有一个容易忽略的点d轴和q轴电流环的Kp/Ki不同因为Ld与Lq通常不一样内嵌式永磁电机Ld Lq需要分别初始化。4.3 双闭环控制程序里电流采样重构与极性检查的先后顺序电流环是快环它反馈的电流质量直接决定控制效果尤其是采样噪声。母线电压波动与电流采样是双闭环工程实现中的两个关键点电流采样重构与极性检查必须在SVPWM输出使能前完成否则相序错一根线电机会立刻飞车。具体的实现顺序是确认电流采样电阻安装位置低端三电阻采样时相电流可用直流侧母线采样重构但有不可重构窗口。软件重构相电流双电阻采样下PWM占空比靠近0%或100%时某一个电流采样窗口失效常见两相导通区间小于t_min。反电动势观测器估算极性给一个小的电压矢量脉冲观测电机是否转动确认相序正确后再上功率级主输出。相电流重构的必要条件是采样窗口宽度足够ADC完成保持转换常规低压板用1µs采样保持3µs转换对应10kHz、占空比极值约±4%。在占空比接近边界时程序要自动切换为“零矢量采样”模式——把PWM周期分裂成更小的片段或采用三相移位算法避开窄脉冲。极端的做法是改用单电阻采样加中值重构但动态响应会变差对本话题不展开。4.4 电压电流闭环里dq轴解耦与前馈补偿电流环PI输出的是不包含动态耦合项的电压dq轴之间有旋转坐标交叉项ωe·Lq·iq和ωe·Ld·id。在一个标准的电压电流闭环FOC控制算法程序里PI输出后还要叠加解耦项才能作为最终vd/vq。写进代码里就是这样// 电流环输出加前馈解耦后得到最终dq电压指令 float vd_ref vd_pi_out - omega_elec * LQ * iq_fb; float vq_ref vq_pi_out omega_elec * LD * id_fb omega_elec * PSI_F;解耦补偿项中的速度越大电机转矩输出越强。不加解耦交叉耦合会让高速时id明显偏置轻则在弱磁区电流振荡重则在标定不当时触发过流。补偿用的电感值可以用“电感辨识”测常见做法是给一个固定电压脉冲观测电流变化率反算有效电感精度要求不高的场合直接用厂家给的Ld/Lq即可因为解耦本身就是前馈误差带来的偏差会被电流PI吃掉一部分。双闭环调参的顺序是固定的先标定电流采样偏置和增益再标定电机参数电阻、电感、磁链再调电流环低频小幅给定看跟踪和阶跃响应最后才调速度环。很多人喜欢先把速度环调稳实际上一旦电流环带宽不足速度环无论怎么调都有共振点。电流环阶跃响应用示波器看目标是没有超调或轻微超调、上升时间约1ms等级对应1kHz带宽。5. 转子初始位置检测两套闭环程序都要先过的一关单电压闭环和双闭环程序写了那么多如果上电时不知道转子在哪儿SVPWM的第一拍就可能输出一个反向力矩让电机抖一下重载下甚至引发电机异响。FOC控制算法程序里最稳妥的起动方式是先做一次转子初始位置检测把电角度零点找出来然后才进入速度/电流环。这也是FOC程序里最值得提的进阶技巧。常用方法是脉冲电压矢量法。思想是转子永磁体对定子铁心的磁链有饱和效应当外加磁场方向与转子磁极方向一致时铁心磁路更容易饱和电感变小同样电压脉冲下电流更大反向时电流更小。代码实现分两步// 脉冲法初始位置检测施加6个方向的电压矢量比较峰值电流 float i_peak[6]; uint16_t angle_test[6] {0, 60, 120, 180, 240, 300}; // 1. 依次在六个方向上施加固定脉宽(约500us~1ms)的电压脉冲 // 注意脉宽要足够短让转子不产生明显转动 for (int i 0; i 6; i) { set_svpwm_vector(angle_test[i], V_TEST); // V_TEST取0.05*VDC~0.15*VDC delay_us(T_PULSE); // T_PULSE取500~1000us i_peak[i] read_adc_current(); // 记录脉冲期间的电流峰值 set_svpwm_vector(0, 0); // 恢复零矢量等待电流衰减 delay_ms(5); // 让电流完全归零防止磁路残留 } // 2. 在六个方向中找到电流最大的两个相邻方向插值得到精确电角度 int idx_max argmax(i_peak, 6); float theta_est angle_test[idx_max]; // 如果有两个相邻方向都很接近用重心法插值更平滑然后在第二步判断N/S极因为电感饱和特性对N极和S极不对称需要对检测出的方向再施加正向和反向两种脉冲比较两者电流峰值电流略大的那一极就是实际的N极方向。做这个方向判别时建议直接把差值积分下来可靠性高于单次比较因为电流噪声可能使单次结果翻转。以上方法的好处是不依赖任何位置传感器霍尔或编码器都可以省掉而且和标题里的电压/电流闭环代码能无缝衔接——检测完成后得到的电角度直接作为Park变换的初始θ。实际调参数时最需要关注的干扰是脉冲时间过长会真的让转子转动一个微小角度导致起转时位置偏差增大。T_PULSE取值的原则是让电流峰值达到额定电流的20%~50%就够不必追求最大区分度。转子位置检测完成后再给初始的电流环设定参考为id0、iq0起步进入正常的双闭环运行。把这个脉冲突击法再扩展一步如果硬件上有编码器也可以用编码器Z脉冲寻零一次之后就不再检测但没有位置传感器时每一次上电都必须做这个检测不然SVPWM的第一个电压矢量可能使它朝任意方向用力——这恰恰是许多FOC控制算法程序移植后“上电转一下”问题的根源。下次遇到电机上电抖动、反转、过流的优先检查的是位置初始化时序而不是电流环参数。把这段检测代码放在PWM使能与SVPWM输出之间就能保证两套闭环的启动过程都是可预期、可复现的——这也正是“FOC控制算法程序单电压闭环、电压电流闭环源码”这类工程代码里最值得仔细校验的环节。本文还有配套的精品资源点击获取