FOC(Field Oriented Control,磁场定向控制)这几个字母,做电机控制的同行一眼就能认出来。这个标题“FOC-P2-DRAFT_V2.0”很有意思,像是我某次项目迭代过程中随手存下来的工程目录名——P2大概率代表Phase 2,也就是第二阶段;DRAFT说明这还不是最终发布版本;V2.0则表示我至少在同一个工程里来回改过两轮。做无感FOC控制的人应该都有这种感觉:版本号从V1.0一路飙到V5.0,每一版都觉得自己这次稳了,结果上电一测波形又得改。这篇文章就是围绕这样一份典型工程笔记展开的,核心内容锁定在PMSM永磁同步电机的无感FOC控制上,重点讲转子初始位置检测、无感启动策略、滑模观测器(SMO)、电流环/速度环整定,以及从Simulink仿真到STM32板卡调试的完整路径。适合正在入门FOC、想把手上的无感FOC代码跑稳,或者准备用STM32做PMSM电机驱动的朋友参考。
我在这个项目里踩过的坑不算少,从启动反转、电流过冲,到高速段波形畸变、位置估算丢步,每一个都花了好几个晚上才排查干净。所以这篇文章不只是把原理重新抄一遍,我尽量按照实际调试的顺序来写,把那些文档里不会写、但上电一定会遇到的细节问题都带出来。
1. 项目概述:FOC-P2-DRAFT_V2.0 的由来与目标
1.1 命名背后的工程思维
先聊聊这个项目名。FOC-P2-DRAFT_V2.0 拆开看,每个字段都对应了工程管理里的一个重要维度。
- FOC:算法核心一定是磁场定向控制,这是项目的主线。
- P2:Phase 2,第二阶段。我第一阶段的成果是能用编码器把电机转起来,电流环闭环跑通;第二阶段的目标是去掉编码器,纯靠软件估算转子位置,也就是所谓的无感FOC。
- DRAFT:说明这份方案还没有完全定型,里面很多参数是根据仿真和初步实测推测出来的,还需要在不同工况下迭代验证。
- V2.0:版本号。第一次完整跑通无感启动之后,我推翻了很多初始设计,重新整理了控制结构和参数表,所以直接跳到V2.0。
很多刚入门的同学对工程文档命名不太在意,经常出现 final_v1、final_v2、final_真的不改了 这种名字。等你真正调试完一个无感FOC项目回头看,就会明白一个好的命名规则能帮你省下大量时间。因为你在V1.0阶段调通的参数,很可能会在V2.0阶段被完全废弃,如果没有版本记录,你根本不知道自己改了什么、为什么要改。
1.2 核心目标:无感FOC控制到底要解决什么问题
结合这次搜索热词里反复出现的“无感FOC入门”“无感启动”“转子初始位置检测”“SMO”等等,可以判断这个项目最核心的目标是做无感FOC。
所谓“无感”,就是系统里不装霍尔传感器,也不装编码器。电机本体上除了三相动力线,没有额外的位置反馈线。这样做的好处很明显:
- 硬件成本下降,省掉了传感器及其处理电路。
- 电机体积更小,能放进更紧凑的机械结构里。
- 可靠性提高,传感器在高温、振动环境下容易失效,无感方案从根源上去掉了这个故障点。
但代价是,控制器必须自己“猜”转子位置和速度。FOC算法本身是依赖于转子磁场方向的——你要把电流分解成励磁分量Id和力矩分量Iq,就必须知道转子磁极到底转到哪了。没有编码器,那就只能靠数学模型加观测器来估算。
1.3 项目整体架构
这个项目从顶层往下分四层:
| 层级 | 内容 | 关键器件/工具 |
|---|---|---|
| 硬件层 | 主控、三相全桥逆变、驱动、电流采样 | STM32F405、IR2104、N-MOS、采样电阻 |
| 控制层 | 电流环、速度环、SVPWM、SMO、PLL | 算法全部用C实现 |
| 软件层 | 状态机、ADC触发、保护逻辑、通信 | Keil / STM32CubeMX |
| 验证层 | 仿真、参数辨识、波形采集 | MATLAB Simulink、示波器 |
这篇文章的重点放在控制层和软件层,硬件层只是简单带过采样和驱动上的几个关键点。
2. FOC控制算法核心原理拆解
2.1 坐标变换:为什么非要搞出 Clarke 和 Park
很多初学者看到FOC的公式就头大,觉得坐标变换很难。其实它解决的问题没有那么玄乎。
电机三相绕组里流的是三个正弦电流,它们之间相差120度。这三个电流合在一起会产生一个旋转的磁场,转子永磁体就是被这个旋转磁场带着转的。麻烦就麻烦在,三相电流是交流量,你直接拿PI控制器去控制交流量,很难做到无静差,而且三个变量相互耦合,控制起来非常别扭。
坐标变换就是干两件事:
第一步是Clarke变换,把三个轴上的交流量变成两个正交轴上的交流量。公式:
Iα = Ia Iβ = (Ia + 2*Ib) / √3本质上就是把三相变成两相,但这两个量仍然是交流的。
第二步是Park变换,把静止的两相坐标变成跟随转子旋转的dq坐标:
Id = Iα*cosθ + Iβ*sinθ Iq = -Iα*sinθ + Iβ*cosθ其中θ就是转子电角度。经过Park变换之后,交流量变成了直流量——Id是励磁电流,Iq是力矩电流。拿PI控制器控制直流量的能力远比控制交流量强。
你可以这样理解:骑自行车的时候,你的脚在踏板上的发力方向是固定的(往下踩),不管曲柄转到哪个角度,你都能把力量转化成前进的力矩。Park变换就是保证电机转子转到任何位置,控制器都知道该把电流往哪个方向推,从而最大化输出力矩。
我调试项目时习惯把Park变换后的Id和Iq用DAC输出到示波器上看——如果这两个值稳定不波动,说明角度估算正确;如果Id和Iq在低频振荡,说明观测器出了问题。
2.2 电流环、速度环、位置环怎么配合
FOC的控制结构是典型的三环串级结构。最内层是电流环,中间层是速度环,最外层是位置环。我这个项目里没有严格意义上的位置环,因为需求是速度控制,只用了电流环加速度环。
电流环的执行频率是PWM开关频率,一般设在10kHz到20kHz。它负责快速跟踪Iq和Id的指令值。电流环输出的结果是Vd和Vq,再经过反Park变换得到Vα和Vβ,最后用SVPWM生成六路PWM波。
速度环在电流环外面,执行频率一般是1kHz到2kHz。它采到估算出的转速,和给定转速做比较,输出Iq的参考值。速度环不需要太快,因为机械系统的时间常数远大于电气系统。你把速度环周期设得和电流环一样快,不仅没有好处,反而会让控制器太敏感,引起机械共振。
如果你要做位置控制,就在速度环外面再加一层位置环,频率500Hz左右就够了。三层环路的经验法则是:外环带宽是内环带宽的十分之一到五分之一。
2.3 SVPWM实现要点
SVPWM(空间矢量脉宽调制)是FOC最终输出PWM波的方法。它并不是简单地和正弦波做比较,而是把逆变器看作一个可以输出八个基本电压矢量的开关组合,用相邻的两个电压矢量在一个PWM周期内合成任意方向和幅值的电压。
SVPWM相比SPWM最大的优势是母线电压利用率提高了约15%。在无感FOC的高速段,反电动势越来越大,母线电压利用率直接决定了电机的最高转速,所以这一步很关键。
实现SVPWM的常规流程:
- 根据Vα和Vβ计算电压矢量所在的扇区。
- 计算相邻两个电压矢量的作用时间T1和T2,以及零矢量时间T0。
- 把T0、T1、T2分配到三相PWM比较值上,生成中心对齐的PWM波。
这里有一个特别容易出问题的点:死区时间。上下桥臂的MOS管不能同时导通,否则母线直接短路,所以要在切换瞬间插入一段死区时间,一般是0.5μs到2μs。死区时间太小容易炸管,太大会导致电流波形畸变、产生6次谐波。V2.0版本里我专门加了死区补偿,后面第五章会单独说。
2.4 无感FOC最容易翻车的地方:转子初始位置检测
搜索热词里“转子初始位置检测”反复出现,说明这是所有无感FOC项目里绕不过去的一个坎。
有编码器时,上电读一下编码器角度就知道转子在哪。无感方案里,上电瞬间转子位置未知,反电动势的幅值正比于转速,转速为零时反电动势为零,所以低速和零速状态下靠反电动势模型完全没法估算位置。
如果直接给一个随意的初始角度开始FOC,转子可能会正转、反转、抖动、甚至过流报警。V2.0项目里我对比测试了三种初始位置检测方法:
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 预定位法(对齐法) | 施加固定电压矢量,把转子强行拉到已知电角度 | 简单、可靠 | 转子如果带负载,可能拉不动 |
| 高频注入法 | 注入高频电压,检测高频电流的凸极性响应 | 准确、不会动转子 | 算法复杂,对采样精度要求高 |
| 脉冲电压矢量法 | 依次施加多个方向的电压矢量,比较电流响应幅值 | 精度高 | 计算量大,实现麻烦 |
我最终采用的是预定位加IF强拉的组合策略。预定位法虽然粗暴,但在大部分轻载场景下非常可靠。它的逻辑很简单:给电机施加一个固定的电压矢量,比如V1(100),让电流全部流向A相,这个时候定子磁场方向固定,转子会自己转到和磁场对齐的位置。等转子稳定之后,你就知道转子的电角度是0度了。
这里有个容易忽略的细节:预定位时间要给够。我做实验时发现,空载时50ms就够,但带一点负载之后,转子可能要200ms才能稳定下来。如果预定位时间太短,转子还没对齐就切换到FOC,系统就会振荡。我V1.0版本踩过这个坑,后来统一把预定位时间设成300ms,只有在带大惯性负载时才需要更长。
3. 无感启动与观测器设计
3.1 从静止到闭环的三步走
无感FOC无法直接零速闭环,业内通用的做法是分阶段启动。我项目里用的是“预定位 -> IF开环强拉 -> 无缝切闭环”三步走。
第一步是预定位,把电角度初始化为0,让转子对齐到已知位置。
第二步是IF控制,也叫电流频率控制(I-F Control)。它是开环的,控制器强制给定一个旋转的电流矢量,电流矢量的角度以固定斜率增加,转速指令从0慢慢升到某个阈值。转子会跟着这个电流矢量旋转。因为电流矢量的幅值是恒定的,所以启动过程非常平滑,电流峰值可控。
第三步是闭环切换。当转速升到大约5%到10%额定转速时,反电动势的幅值已经足够大,观测器能准确估算出转子位置了。此时把控制权从IF切换到SMO+PLL闭环。
切换是整个启动流程里最容易出问题的环节。IF控制阶段,电流矢量的角度和实际转子角度之间会存在一个负载角,这是因为转子永远要落后于定子磁场一个角度才能产生力矩。切换时如果这个角度差超过30度,电流冲击会非常大。
我处理的思路是:在切换前逐步把IF的电流角度向SMO估算的角度靠拢。也就是在切入闭环之前,做一个角度对齐过程,让IF的给定角和观测器估算角收敛到一致,然后再切换。这样切换时基本感受不到冲击。
3.2 滑模观测器(SMO)与锁相环(PLL)
启动完成后,系统中高速段的位置估算主要靠观测器。V2.0我选用的是滑模观测器加锁相环的方案。
滑模观测器的原理不展开太多数学推导,核心思想是这样的:PMSM的定子电流模型里包含反电动势项,而反电动势的方向正好包含转子位置信息。观测器通过比较估计电流和实际电流的误差,经过一个滑模切换函数,把误差收敛到零,从而提取出反电动势。
反电动势方程:
Eα = -ψf * ωe * sin(θe) Eβ = ψf * ωe * cos(θe)Eα和Eβ的反正切就是转子电角度θe。但实际使用中直接取反正切会引入大量谐波噪声,所以更稳妥的做法是把Eα、Eβ送进一个PLL(锁相环)来滤波和提取角度。PLL的等效框图就是一个PI调节器,它锁定的是估算角度的误差,输出平滑的转速和角度。
// 滑模观测器核心代码(简化版) err_alpha = i_alpha_est - i_alpha; err_beta = i_beta_est - i_beta; z_alpha = K_smo * sign(err_alpha); z_beta = K_smo * sign(err_beta); E_alpha = LPF(z_alpha); // 低通滤波提取反电动势 E_beta = LPF(z_beta); // PLL提取角度 theta_est += Kp_pll * (E_alpha*cos(theta_est) + E_beta*sin(theta_est)) * dt; speed_est = (theta_est - theta_old) / dt;滑模增益K_smo的选取有一定讲究。它必须大于反电动势的幅值,才能保证滑模面存在,也就是说转速越高,K_smo要越大。但K_smo太大又会带来严重的抖振。我的做法是根据估算转速实时调整K_smo,做成一个分段函数,低速用小增益,高速用大增益。
3.3 磁链观测器方案对比
除了滑模观测器,还有一类常用的无感方案是磁链观测器(Flux Observer),或者扩展卡尔曼滤波(EKF)。搜索热词里也提到了“PMSM无感FOC控制”,这几种方案各有各的适用场景。
| 方案 | 计算量 | 中高速表现 | 低速表现 | 调试难度 |
|---|---|---|---|---|
| SMO | 低 | 优秀 | 差 | 中等 |
| 磁链观测器 | 低 | 优秀 | 中 | 中等 |
| EKF | 高 | 中 | 中 | 困难 |
| 高频注入 | 中 | 差 | 优秀 | 困难 |
对于大多数用STM32做无感FOC的项目,SMO已经是性能和工程量之间的最佳平衡。如果你用的是国产全志、瑞萨或者更高算力的MCU,可以考虑上EKF,但它的协方差矩阵整定非常费时,我身边做过的同事没有一个不喊痛苦的。
3.4 电流环PI参数整定方法
V2.0的电流环整定我采用了工程上很常用的零极点对消法。PMSM的定子电气模型是:
Vd = Rs*Id + Ld*dId/dt - ωe*Lq*Iq Vq = Rs*Iq + Lq*dIq/dt + ωe*Ld*Id + ωe*ψf忽略交叉耦合项后,电流环的PI参数可以直接按系统带宽来确定:
Kp = Ls * bandwidth Ki = Rs * bandwidth其中Ls是相电感,Rs是相电阻,bandwidth是电流环期望带宽。带宽一般取PWM频率的1/10到1/20,10kHz的PWM就取500Hz到1kHz。
举个例子,某电机参数Rs=0.2Ω,Ld=Lq=0.5mH,PWM频率10kHz,取带宽800Hz(换算成弧度约5026rad/s):
Kp = 0.0005 * 5026 ≈ 2.51 Ki = 0.2 * 5026 ≈ 1005用这个公式算出来的初值通常都能直接跑,之后再做微调。速度环也类似,但速度环的带宽要低得多,一般取5Hz到20Hz。速度环调太大电机容易啸叫,这个啸叫不是机械声音,而是电流环和速度环耦合产生的振荡,听起来非常刺耳。
4. 实操过程与核心环节实现
4.1 基于STM32的代码框架设计
V2.0代码我重新做了模块化拆分。核心思想是算法和驱动分离,方便将来移植到别的MCU上。
文件结构:
foc/ ├── foc_core.c // FOC核心算法,Clarke/Park/SVPWM ├── foc_core.h ├── smo.c // 滑模观测器 ├── smo.h ├── current_loop.c // 电流环PI ├── speed_loop.c // 速度环PI ├── startup.c // 启动状态机:预定位/IF强拉/切闭环 ├── pwm_driver.c // PWM输出和ADC触发 ├── adc_sampling.c // 电流采样 └── protection.c // 过流、过压保护代码里最关键的一个设计是电流采样时机。STM32的ADC触发要配置成PWM中心对齐模式下的中点触发。这个时刻正好是PWM周期的中间,上下桥臂处于稳定的开关状态,开关噪声最小。如果采样时机不对,采到的电流会叠加大量尖峰,电流环PI完全没法正常工作。
这种情况下很多人第一反应是加大滤波电容或者加RC滤波器,但滤波电容加多了会影响电流环相位裕度,反而让电流环不稳定。正确解法是调整采样触发点,让ADC准确地在PWM中点采样。这个细节是V2.0稳定性提升最大的一个改进点。
4.2 Simulink仿真模型搭建
在上板之前,我在Simulink里先建了一版完整的PMSM无感FOC仿真模型。搜索热词里“pmsm simulink foc 仿真模型”出现频率很高,说明这确实是很多人关心的问题。
模型结构:
- PMSM模块:用Simulink自带的永磁同步电机模块,设置极对数、定子电阻、电感、永磁体磁链。
- 逆变器模块:用Universal Bridge搭三相全桥,母线电压设48V。
- 控制器模块:自己用Simulink模块搭的FOC算法,包含Clarke、Park、SMO、PLL、PI调节器。
- 启动逻辑模块:用Stateflow做状态机,模拟预定位、IF强拉、切闭环。
我电机参数用的一组典型值:极对数4,Rs=0.2Ω,Ld=Lq=1.5mH,永磁磁链0.08Wb,母线电压48V,PWM频率10kHz。仿真步长设1μs。
仿真的价值在于,你可以在几秒钟内试各种工况——突加负载、转速阶跃、参数摄动。我在仿真里发现了切换闭环瞬间电流过冲的问题,通过修改IF到闭环的角度对齐时间解决了。如果直接上板调,光这个过冲问题可能就得花掉两天时间。
4.3 波形调试与关键指标
上板之后,示波器要看的核心信号有这么几个:
- 三相电流波形:应该接近正弦,幅值稳定。如果波形有畸变,先看是不是死区太大,再看是不是观测器角度有偏。
- Id和Iq波形:FOC控制正常的情况下,Id应该稳定在0附近,Iq跟随负载波动。如果Id和Iq存在周期性波动,大概率是角度估算有误差。
- 估算速度和给定速度:启动和负载突变时,速度超调应该控制在可接受范围。
- 母线电流:运行平稳时母线电流应该比较平缓,如果有规律性的尖峰,可能是换相时刻电流突变造成的。
我调试过程中有一个习惯:把估算角度和实测角度(临时装一个编码器做对照)同时接到示波器上比一圈。如果两者偏差稳定在一个固定值,那说明是角度补偿问题;如果偏差随转速变化,那说明观测器模型参数不对。
4.4 电路板设计关键要点
无感FOC对硬件的要求比有传感器要高,主要集中在电流采样和驱动部分。
电流采样我用的是低端采样电阻方案,三相桥下桥臂串联0.01Ω的采样电阻,通过运算放大器放大后送进ADC。采样电阻要用温漂小的合金电阻,功率至少2W,不然大电流工况下阻值会漂,直接影响电流环精度。
栅极驱动用的是半桥驱动芯片IR2104,自举电容放在芯片附近,用1μF的贴片电容。布局时自举电容到VB和VS引脚之间的走线要短粗,否则高频开关时驱动能力会下降。
母线电压采样和过流保护不能省。过流保护我用的是比较器加硬件封锁PWM的方案——一旦采样电阻电压超过阈值,比较器直接拉低驱动芯片的使能脚,切断PWM输出。这个保护必须在纯硬件层面完成,不能依赖软件响应,否则IGBT或MOS在短路时会直接烧掉。
5. 常见问题与排查技巧实录
5.1 启动抖动、反转现象
启动时转子抖动或者反转,这个问题在无感FOC初学者里出现概率最高,我自己V1.0也遇到过。产生原因主要有这么几类:
- 预定位角度不对。确认预定位时施加的电压矢量方向和你程序中设定的零度电角度一致。三相相序接反会导致预定位拉到错误角度。
- 负载过大,预定位无法把转子拉到对齐位置。可以适当提高预定位电压幅值,但要注意别把电流顶到过流保护阈值。
- 从IF切换到闭环时角度误差太大。前面说了,切换前要做角度对齐,确保IF给定角度和SMO估算角度一致。
我排查这类问题时会先在空载状态下验证启动逻辑,确认空载没问题,再逐步加大负载。这样能把问题范围缩小到“负载过重”还是“切换逻辑缺陷”。
5.2 高速段电流波形畸变、转速丢失
跑到高速段,问题往往出在反电动势上。转速升高,反电动势也随之升高,当反电动势接近母线电压时,PWM的占空比会进入饱和区,电流环失去调节能力,电流波形出现明显的平顶畸变,严重时观测器失去收敛,转速直接丢掉。
解决思路:
- 提高母线电压,给系统留出更大的电压裕量。
- 加入弱磁控制。通过把Id往负向偏置,抵消一部分永磁体磁链,从而降低反电动势。弱磁控制会增加铜耗,要控制好Id的下限,避免电机过热。
- 减小PWM占空比上限,给电流环留出足够的调节空间,但这样会牺牲一部分电压利用率。
我V2.0里采用的是限制Iq参考值加负向Id偏置的简单弱磁策略,实测最高转速比不加弱磁提高了约12%。
5.3 PWM死区补偿怎么做
死区时间的存在会导致输出电压和理想电压不一致。在电流过零附近,死区引起的误差电压方向会翻转,表现为电流波形上的6次谐波分量,尤其在轻载时特别明显。
解决办法是死区补偿。基本思路是:根据电流的方向,在PWM比较值上加上或减去一个固定的补偿量。电流为正时补偿一个正值,电流为负时补偿一个负值,电流过零附近用滞环避免补偿抖动。
死区补偿的实现代码不复杂,但效果很直接:
if (phase_current_a > 0.05f) { compare_value_a += dead_time_compensation; } else if (phase_current_a < -0.05f) { compare_value_a -= dead_time_compensation; } // 电流接近零时不做补偿,防止误触发0.05是滞环阈值,实际值要根据噪声水平调整。死区补偿之后,轻载电流波形明显干净很多,电机的噪声也小了不少。
5.4 常见问题速查表
| 现象 | 可能原因 | 处理方案 |
|---|---|---|
| 上电预定位时电机异响 | 预定位电压过高或时间过长 | 降低幅值,限制时间 |
| 空载启动正常,带载启动反转 | 预定位力矩不足 | 提高预定位电压,加长时间 |
| 电流环PI发振,电流噪声大 | 采样触发点不对,Kp过大 | 调整ADC触发时机,降Kp |
| 高速段电流波形畸变,转速丢步 | 反电动势饱和 | 加弱磁,提高母线电压 |
| 估算角度滞后实际角度 | 低通滤波器截止频率太低 | 提高滤波器带宽,或加角度补偿 |
| 电机啸叫 | 速度环带宽太高 | 降低速度环Kp/Ki |
6. 最后聊聊调试FOC的几点体会
做FOC这件事,最磨人的不是你搞不懂原理,而是你明明觉得写得都对,上电之后它就是不动。我花了很久才意识到,FOC调试的每一步都要有对应的验证手段,不能跳步。
仿真能帮你验证的是算法逻辑,不代表你代码移植到MCU上就一定能跑;空载能跑,不代表带载能跑;低速能跑,不代表高速能跑。每推进一个阶段,都要回头审视前面环节的假设是否还成立。比如IF切换到闭环之后,如果速度闭环又开始振荡,你要反过来检查电流环是不是已经调稳了,而不是在速度环上反复加滤波。
另外有一个小建议:重视电流采样,无论怎么强调都不过分。FOC最内层就是电流环,电流值采得不准、采的时机不对,后面所有的观测器、速度环、弱磁策略全是建立在错误的数据上。很多离奇的故障现象,最后排查下来都是采样电阻温漂或者ADC触发时序的问题。
这个项目后续如果要继续扩展,我会优先补两块工作:一是把高频注入法加进低速段,这样零速也能带载启动;二是加入母线电压前馈补偿,让电机的动态响应在电压波动时更稳定。FOC这条路很长,但每解决一个坑,下一块拼图就会更清晰。