做 FOC(Field-Oriented Control,磁场定向控制)最难受的阶段,往往不是算法看不懂,而是代码和板子都准备好了,一上电电机先给你一声尖叫,然后电流冲到限流值,你只能赶紧断电,从头再来。FOC-P2-DRAFT_V2.0 这个项目代号,就是我为第二版 FOC 控制方案留的开发记录:P2 表示这是重新设计过的第二版原型机,DRAFT_V2.0 说明固件、原理图和调试文档目前都还在草稿迭代状态。相比第一版那种“能转就行”,这一版真正想解决的是无感 FOC 从能用变成好用的细节问题:转子初始位置检测靠不靠谱、电流环在低速段稳不稳定、无感切换那一瞬间会不会冲击、调试的时候到底该盯哪条波形。这篇记录没什么学院派的推导,更多是方案取舍、参数计算、代码结构、调试流程和踩坑记录,适合刚入门 FOC 的嵌入式或硬件方向同学,也适合已经能让电机转起来、但还在为启动失败和波形难看头疼的同行。
1. 项目整体设计思路:从 V1.0 到 P2 的取舍
1.1 第一版留下的教训
第一版我其实已经把电机转起来了,而且速度闭环也基本能跑,但它的问题非常具体。第一是 ADC 采样时刻没有校准到 PWM 比较点之外,采样窗口刚好落在功率管开关切换的尖峰上,导致电流波形上全是毛刺,PI 调节器输出跟着一起抖。第二是低速段力矩波动特别明显,启动经常需要用手拨一下电机才能转过去,这是因为初始位置检测做得太粗糙,开环强拉电流又给得不够。第三是硬件布局问题,功率地和信号地走线没处理好,采样电阻的差分信号被母线大电流噪声污染,示波器上看电流波形跟理论完全对不上。
所以 P2 的定位很明确:不是推翻重来,而是把 V1.0 里这些“能跑但不好用”的坑一个一个填掉。硬件上重新画了采样通路的布线,把功率地和模拟地做了单点连接,驱动电源也加了滤波;固件上补了完整的启动状态机,给电流采样加了软件滤波,并把所有算法模块的接口统一成结构体,方便在仿真和真机之间来回切换参数。
1.2 硬件方案选型与理由
P2 的硬件选型其实没有太激进,全部是成熟方案。主控用的是 STM32F405RGT6,168MHz 主频带 FPU,跑浮点 FOC 完全够用,比 F103 那种需手动做 Q 格式定点的方案省心很多。栅极驱动选了 EG2134 这种三合一驱动,自带死区,外围电路也简单。不过它的死区时间固定,不像分立驱动那样能自己调,实测 20kHz 载波下内置死区没出过问题,但如果后续要跑更高载波或者特殊工况,这个点要重新评估。
功率级用 N 沟道 MOSFET,母线按 48V/10A 设计。电流采样是本次改版的重头戏,最后选了三电阻采样方案,三只低阻值采样电阻放在下桥臂 MOSFET 源极和地之间,信号经过 INA240 这类高带宽电流检测运放放大后进 ADC。三电阻比单电阻成本高,但调试友好度提升非常明显,尤其是在无感初始位置检测和低速控制这类对电流重构要求高的场景,三电阻提供的信息更完整。
1.3 固件整体架构
P2 固件按照下面四个层次来组织:底层负责 PWM 生成、ADC 触发采样、过流保护;算法层负责 Clarke/Park 变换、电流环 PI、SVPWM、反 Park 变换;观测层负责转子位置估算、速度估算、磁极辨识,也就是无感控制的核心部分;应用层负责启动状态机、速度指令解析、通信协议。模块之间尽量不互相引用,全部通过参数结构体传递,这样很多代码可以直接从 Simulink 模型里的框图对应过来,少了一层脑子里的翻译过程。
2. 核心算法拆解与参数计算
FOC 算法本身并不复杂,本质上就是把定子电流拆解成励磁分量和转矩分量分别控制,但在工程上每一个环节都有容易出错的细节。这一章不重复教科书,重点说我在 P2 里面实际用到的计算和配置方法。
2.1 电流环 PI 参数怎么定
电流环是 FOC 最内环,也是整个系统调好的前提。工程上可以用连续域的近似公式先估算一组 PI 参数:
- Kp = L * ω_bw
- Ki = R * ω_bw
其中 L 是相电感,R 是相电阻,ω_bw 是期望电流环带宽对应的角频率。以手头一块 2204 无刷电机为例,相电阻约 0.22Ω,相电感约 1.8mH,期望电流环带宽 1kHz,那么 Kp = 0.0018 × 2π × 1000,大概是 11.3;Ki = 0.22 × 2π × 1000,大概是 1382。
注意:这个公式算出来的是连续 PI 增益。如果代码里用的是位置式或者增量式数字 PI,需要把 Ki 跟采样周期做换算,不同实现方式差一个 Ts 的因子,这是很多新手照着网上代码抄完参数后电流环发疯的常见原因。
电流环带宽不是越高越好。带宽太高会把采样噪声也放大,太低则动态响应慢,电机容易发闷。20kHz 的电流采样频率,配 1kHz 左右电流环带宽是比较稳妥的起点,后面再根据波形微调。我自己调试时会先把 Kp 从计算值的 70% 开始往上加,加到电流阶跃响应不超调、但又没有明显滞后为止,然后再微调 Ki 消除稳态误差,这个流程在 4.1 里还会细说。
2.2 SVPWM 调制与死区补偿
SVPWM 相比 SPWM 能把母线电压利用率提高约 15%,FOC 里基本默认用它。P2 用七段式 SVPWM,扇区判断和比较值计算都是常规做法。这里有一个非常容易搞错的点:Clarke 变换得到的 αβ 轴电压要经过逆 Park 变换和扇区判断,才能映射到三相比较值,任何一个环节的正负号弄反,输出的占空比波形就会乱,电机会尖叫。
另一个被低估的问题就是死区。功率管不能上下桥同时导通,所以需要插入死区时间,P2 用的驱动芯片内置约 1us 死区。死区会导致输出电压实际值和指令值不一致,在低速大电流的时候尤其明显,波形表现为电流过零畸变。要补偿的话,常规做法是根据相电流方向对比较值做修正:电流为正时输出略微增大,电流为负时输出略微减小。V1.0 没做死区补偿,低速电流波形肉眼可见地不好看;P2 加了电流方向预判之后,波形干净了很多。
2.3 转子初始位置检测
无感 FOC 里,启动能不能不反转,完全看初始位置检测。P2 里我同时验证了两种方法,最后根据不同工况切换使用。
第一种是磁极定位法,也叫对齐法。思路非常简单:在任意一个固定角度上施加一个足够大的电压矢量并持续一段时间,让定子磁场把转子强制拉到一个已知位置,此时电角度清零。优点是实现特别简单,对参数不敏感;缺点是启动前转子会跟着动一下,有声音,而且如果电机带了负载,可能拉不到位。
第二种是旋转高频电压注入法。在估计的 d 轴注入高频电压,由于电机凸极率的影响,q 轴电流会携带转子位置信息,通过解调可以估算出初始角度。P2 用的是一种固定方向的高频注入加磁极极性判断的简化方案,在 24V 供电的小电机上实测误差能控制在 10° 电角度以内,启动不反转基本没问题。代价是算法复杂度和计算量都上去了,需要在中断里跑一个带通滤波和一个锁相环。
2.4 无感观测器选型
无感 FOC 的中高速段,我最终选了基于反电动势的滑模观测器配合锁相环提取角度。滑模观测器结构简单,鲁棒性强,对电机参数误差没那么敏感,缺点是抖振明显,而且用反电动势的方案在零速和极低速下基本失效。龙伯格观测器在动态性能上更好,但严重依赖电机参数,参数一变就容易发散。
P2 的做法是把使用范围限定好:低速段用开环强拉和高频注入,中高速段切换到滑模观测器闭环,避免在观测器还没收敛的时候强行闭环。切不切换、什么时候切换,就是第 4 章说的启动状态机要做的事。
3. 仿真验证与固件实现
有人觉得 P2 这个项目花在仿真上的时间不值,但我恰恰觉得这才是把调试周期从几个月压缩到几周的关键。仿真不是拿来算“精确结果”的,而是用来验证逻辑和快速试错的。
3.1 Simulink 仿真模型搭建要点
我搭的模型结构不复杂,核心模块包括:PMSM 电机模块、三相逆变桥、SVPWM 调制模块、Clarke/Park 变换、电流 PI、速度 PI、观测器、启动状态机。顺序上先从开环开始,给一个固定电角度,看电流和转矩能不能建立;再上电流闭环,让 id、iq 能跟踪指令;最后跑速度闭环和切换逻辑。
仿真里最容易栽跟头的其实是电机参数和采样逻辑的设置。PMSM 模块里要填极对数、定子电阻、定子电感,这些参数最好从电桥实测,手册标称值往往不够准。另外仿真里面控制器和电机是连续对接的,但真实系统中存在 PWM 采样延迟和数字计算延迟,所以最好在仿真里也加一个单位延迟模块模拟一拍滞后,不然仿真调出来的 PI 参数上真机会差非常多。
3.2 STM32 工程关键配置
固件层面,P2 把关键配置集中成三块:PWM 定时器配置、ADC 采样触发、电流环中断服务函数。PWM 用定时器的中心对齐模式,计数溢出时触发 ADC 注入组采样,这样可以保证采样点落在 PWM 周期的中心,避开功率管开通关断的噪声窗口。下面是无刷电机驱动里电机相电流采样后执行坐标变换和 PI 的简化流程,核心代码大概长这样:
// 中心对齐 PWM 溢出中断,电流环 ISR void FOC_CURRENT_ISR(void) { // 1. 读取 ADC 采样结果(已硬件滤波) float ia = adc_get_channel(0); float ib = adc_get_channel(1); float ic = -(ia + ib); // 2. 电角度由观测器提供,c_theta 单位 rad float ct = cosf(theta_est); float st = sinf(theta_est); // 3. Clarke 变换 float ialpha = ia; float ibeta = (ia + 2.0f * ib) * 0.577350269f; // 4. Park 变换,得到 dq 轴电流 float id = ialpha * ct + ibeta * st; float iq = -ialpha * st + ibeta * ct; // 5. 电流 PI,输出 dq 轴电压 float vd = pi_run(&pid_d, id_ref, id); float vq = pi_run(&pid_q, iq_ref, iq); // 6. 反 Park,得到 alpha/beta 轴电压 float valpha = vd * ct - vq * st; float vbeta = vd * st + vq * ct; // 7. SVPWM 生成三相占空比 svpwm_set(valpha, vbeta, vbus); }这段代码看起来很短,但每一步都有讲究。比如采样读取之后最好先做一次限幅,防止尖峰直接灌进 PI;pi_run 里必须做好积分限幅,不然启动瞬间的输出能直接顶到限幅值,导致电流过冲。V1.0 曾经在这里偷懒,结果就是每次启动都容易过流。
3.3 采样方案对比:三电阻还是单电阻
单电阻采样在成本上有优势,只需要在直流母线串一只采样电阻,通过在一个 PWM 周期内不同时刻采样重构三相电流。但单电阻重构在低调制比和扇区边界附近的区间会出现采样盲区,电流波形毛刺多,P2 最开始考虑过,最后还是放弃了。原因很简单:无感启动和低速控制对电流质量要求高,我不想把时间浪费在补偿采样盲区上。
三电阻方案虽然多两个运放,但每个 PWM 周期能直接采到三相电流,波形质量好,调试起来省心。如果你只是做成本极度敏感的小功率产品,单电阻可以挑战一下;但如果是学习或者做性能验证,三电阻值得优先。
4. 调试流程与波形分析实操
调试 FOC 最忌讳的就是一上来就测速度闭环。正确的顺序应该是先把电流环调顺,再谈速度环,最后才谈无感。P2 的调试流程基本就是四条主线:电流环、启动逻辑、切换逻辑、速度闭环。
4.1 从电流环开始:先让电流听话
第一步调试电流环时,我会把速度环断开,直接把指令值写成固定量。最省事的办法是让电机堵转,或者干脆把电机锁死,然后分别给 q 轴电流一个小阶跃,观察 iq 的波形。正常响应应该是平滑上升然后稳定在指令值附近,没有明显超调,没有高频振荡。
调试时先给一个小指令比如 0.5A,然后逐步增大 Kp。你会发现 Kp 太小的时候 iq 波形软绵绵的,像个一阶惯性环节;Kp 太大会出现高频啸叫,电流波形呈振荡衰减甚至发散。找到一个临界值之后,再往回退 20% 左右作为 Kp 的最终值,Ki 可以先用计算值,然后观察稳态误差慢慢调掉。
注意:在堵转调试电流环时,电机线圈会持续发热,一次测试时间别超过几十秒,否则有烧电机的风险。我一般把母线电压降到 12V 甚至 8V 来缩短发热问题的影响范围。
4.2 无感启动与切换逻辑
P2 的启动状态机分三步。第一步是磁极定位或高频注入辨识初始角度;第二步是开环强拉,也就是用给定的电角度增量直接控制电机旋转,电流给在 q 轴上,让电机带着负载转起来;第三步是当转速达到设定值后,把运行模式从开环切换到无感闭环。
切换的成功率是 P2 最大的改进点。V1.0 时代我在切换时直接收手,把角度来源从开环累加换成观测器输出,结果十次里有四次转速会掉一下,严重时失步。后来才发现问题在于切换瞬间开环累加的角度和观测器估算的角度可能差了一个固定偏移。解决办法是在开环阶段就同步运行观测器,让它跟踪开环角度,等估算角度和开环角度误差小于一定阈值后,再把控制权交出去。实际切换信号可以用转速反馈加角度误差的双重判断,伪代码大致是这样:
if (state == OPEN_LOOP_RUN) { // 开环角度增量带动电机旋转 theta_open += speed_cmd * dt; theta_ref = theta_open; // 同步运行观测器,并持续比较角度误差 observer_run(); theta_est = observer_get_theta(); angle_err = normalize_pi(theta_est - theta_open); if (speed_est > SWITCH_SPEED && fabsf(angle_err) < SWITCH_ANGLE_ERR) { state = CLOSED_LOOP_RUN; // 切换到闭环 } } if (state == CLOSED_LOOP_RUN) { theta_ref = theta_est; // 角度来源换成观测器 }这个过程中的速度指令也不能一下给得很高,P2 里开环强拉的转速折成电频率大约是 5~10Hz,切换速度设定在 15Hz 左右,留出观测器建立稳定反电动势的时间。
4.3 波形观察检查清单
调试无感 FOC,示波器上要盯的波形不少,但最有价值的是下面几组。不同异常对应的波形特征,我整理成了一个检查表。
| 观察对象 | 正常特征 | 异常特征 |
|---|---|---|
| 相电流波形 | 正弦度好,过零平滑 | 平顶、削波、出现过零毛刺 |
| id 电流 | 稳定在 0 附近小范围波动 | 有周期性振荡,说明角度不对 |
| iq 电流响应 | 阶跃响应平滑无超调 | 高频振荡或发散 |
| 估算电角度 | 随时间单调递增,曲线光滑 | 出现跳变或倒退 |
| 母线电压 | 保持平稳 | 带载时大幅跌落 |
| 转速反馈 | 纹波小,跟随指令 | 波动大或周期性摆动 |
这里尤其提醒一下 id 波形。如果你发现 id 没有稳定在 0 附近,而是有固定频率的波动,大概率是估算角度和真实角度之间存在偏差,角度偏差会在 d 轴上留下投影。反过来,如果能通过示波器看出 id 波动周期和电机转速的关系,也能反推角度误差的大小。
5. 常见故障与排查速查表
把 P2 从 Draft 到能稳定运行的这段过程里踩过的坑整理一下,很多问题是新手一定会遇到的。
| 故障现象 | 可能原因 | 排查与处理 |
|---|---|---|
| 上电瞬间过流 | PWM 上下管直通、死区配置失效、ADC 采样偏移未校正 | 先不装电机测驱动输出波形;确认死区设置;校准 ADC 零点 |
| 电机尖叫不转 | 电流环 PI 参数不当、PWM 载波频率过低、采样噪声大 | 降低 P/I 增益;提高 PWM 到 20kHz 以上;检查采样时刻 |
| 初始定位时反转 | 初始角度辨识错误、磁极极性判断反了 | 用对齐法验证极性;检查高频注入方向及解调符号 |
| 开环能转但切换闭环失步 | 切换时角度误差过大、切换点转速太低、切换瞬间电流指令跳变 | 切换前同步观测器;提高切换转速阈值;切换时平滑电流指令 |
| 高速时转速丢失 | 滑模观测器带宽不足、反电动势采样滤波太强 | 提高观测器增益;优化低通滤波器截止频率 |
| 电流波形平顶 | 调制过限、母线电压不足、占空比饱和 | 检查 SVPWM 是否限幅;提高母线电压或降低转矩指令 |
拿“上电瞬间过流”来说,这是我碰到最多的故障。第一次遇到时我第一反应是 MOSFET 烧了,测了一圈发现驱动芯片输出正常,最后定位到问题竟然是 MCU 的 PWM 引脚在初始化阶段输出电平不确定,导致上下桥同时导通。解决办法是让 PWM 引脚先复用成普通 GPIO 并输出低电平,等定时器完全初始化之后再开启 PWM 输出,这个顺序问题很多芯片都有,已经成为我的固件初始化固定条款了。
还有一个花了不少时间的问题:电机在某个中等转速下会周期性抖动,电流波形看着像有低频率扰动在叠加。排查到最后发现是速度环输出和电流环指令之间的限幅设置不当,速度环输出在正负限幅之间振荡,导致电流指令高频切换。这也是为什么 P2 强调所有环节都要做合理的限幅和滤波,而不是让 PI 输出裸奔。
6. 最后分享两个调试习惯
关于这次 FOC-P2 项目,最后放两个我个人的小习惯。第一个是永远保留一份“离线检查”用的波形记录。电机状态千变万化,很多偶发问题靠肉眼盯屏幕根本发现不了。我会专门用一个串口或 SPI 通道,把 id、iq、估算角度和实际角度同步上传到电脑,转成 CSV 之后用脚本画出来。这样每次上电测试的数据都有存档,后续排查问题时能回溯到具体的时间点。
第二个是版本管理里除了代码,一定连参数一起管理。FOC 的调试大量依赖 PI 参数、采样增益、滤波器系数这些看起来不起眼的配置,经常是同一个固件、不同参数跑出来完全两种效果。P2 里我把所有参数做成了结构体,每次调完一组能用的参数就导出一份配置文件,跟代码版本一起打 tag。现在回看 DRAFT_V2.0 之前的几个版本,很多“固件改了反而变差”的问题,几乎都是参数覆盖导致的。
调试 FOC 的过程本质上就是一遍一遍确认:电流环是否稳定,位置信息是否准确,切换条件是否可靠。把这几个问题按顺序解决掉,无感 FOC 其实没有想象中那么玄。希望这篇记录能让你少走几趟弯路,尤其是那些我踩过的坑,你看了就可以直接绕过去。