搞电机控制的兄弟应该都有过这个阶段:看着别人调好的FOC波形,觉得也就是"三个电阻加个编码器"的事,真到自己上手,从PWM配置到电流波形乱七八糟,再到电机原地抖成帕金森,一天能怀疑好几次人生。
这篇文章我想把FOC这条线从头到尾捋一遍。不搞那种"FOC算法包括Clark、Park、SVPWM"的百科式复述,而是把真正决定成败的细节讲清楚:坐标变换到底在干什么、ADC采样为什么必须卡在那个时刻、D轴电流要不要给零、无感方案启动靠什么拖起来、以及STM32上落地时最容易卡壳的几个坑。适合正在做电机控制项目、准备从方波控制切换到FOC、或者调了半天还搞不定电流环的工程师和学生参考。
1. 坐标变换不是数学游戏:Clarke、Park与极坐标转换到底在干什么
很多人学FOC卡在第一关就是坐标变换,一堆矩阵看着像线性代数考试。我当时学的时候也绕了很久,后来想明白一件事:坐标变换不是为了算着好玩,而是把交流电机的控制问题硬生生变成直流电机的问题。
1.1 从三相正弦到两相静止:Clarke变换的物理意义
三相永磁同步电机的定子绕组在空间上相差120度,通入三相正弦电流后,合成的磁动势是一个旋转的矢量。问题是,控制三相电流的三套正弦互相耦合,你单独调A相电流是没意义的,因为转矩取决于合成磁动势和转子磁场的夹角。
Clarke变换做了一件很简单的事:把abc三相静止坐标系投影到αβ两相静止坐标系。数学上就是每相电流乘一个固定系数再相加,物理上相当于把三个120度分布的绕组等效成两个90度分布的绕组。
实际写代码的时候,Clarke变换常常不会用矩阵形式,而是直接写成:
i_alpha = i_a; i_beta = (i_a + 2.0f * i_b) / SQRT3;注意这里用了恒幅值变换的系数,而且直接省略了i_c的采样,因为三相星接电机满足i_a + i_b + i_c = 0,只要采两相电流就够了。这个写法比教科书上的矩阵少了几个乘法,但物理上完全等价。前提是你的电流采样偏置必须调准,否则这里每一点误差都会直接变成转矩波动。
1.2 Park变换:把交流变直流的关键一步
Clarke变换之后,电流还是正弦波,只是从三相互差120度变成了两相互差90度。要把它变成"直流",还需要知道转子磁极的位置——这就是Park变换。
Park变换把αβ静止坐标系旋转到dq旋转坐标系。旋转角度就是电角度theta,通常来自编码器、霍尔或者观测器。经过Park变换后,id(直轴电流)和iq(交轴电流)在稳态下都是直流量。这时候你控制它们,就像控制直流电机的电枢电流一样,用个PI就能做到无静差跟踪。
这里必须说清楚一个概念:dq坐标系和电机转子是同步旋转的,theta必须是准确的转子电角度。如果theta偏了,id和iq会互相串扰,表现出来的症状就是电流环怎么调都调不干净,波形上有明显的转速频率纹波。
实践中我见过不少人在这个地方翻车:用增量编码器但没做零位标定,或者霍尔换相角度没校准,结果theta偏差二三十度电角度,电流环带宽稍微拉高就开始啸叫。
1.3 cartesian to polar的那一步:为什么算完PI还要再做一次变换
很多FOC教程讲到Park变换和PI调节器就结束了,但实际工程的电流环输出是d轴电压Vd和q轴电压Vq,这两个量是在旋转坐标系下的直流量。要送给SVPWM去产生三相占空比,还需要先把Vd、Vq变回αβ静止坐标系,这时候用的就是反Park变换。
反Park变换的公式是:
v_alpha = vd * cos(theta) - vq * sin(theta); v_beta = vd * sin(theta) + vq * cos(theta);然后拿v_alpha、v_beta去计算SVPWM。而热词里那个"cartesian to polar",通常指的是在反Park之后、SVPWM之前的另一种路径:直接把v_alpha和v_beta转成极坐标形式的幅值Vmag和角度Vangle,然后用Vmag去和载波比较、用Vangle去选扇区。
其实这两种做法殊途同归。用极坐标形式更方便理解SVPWM的扇区判断——六个扇区本质就是对应六个非零电压矢量把复平面分成了六块。实际工程里我建议直接用v_alpha和v_beta做标准SVPWM的矢量合成计算,少一次开方,数值上更稳。极坐标形式适合用来做调制比分析,比如看输出幅值有没有进饱和区。
1.4 电机控制电压方程:整条控制链的物理地基
坐标变换不是拍脑袋想的,它的依据是永磁同步电机在dq坐标系下的电压方程:
vd = Rs * id + Ld * did/dt - omega_e * Lq * iq vq = Rs * iq + Lq * diq/dt + omega_e * (Ld * id + flux_f)第三项就是反电动势,转速越高它越大,这就是为什么FOC在高速段需要更高的母线电压。第四项里的flux_f是永磁体磁链,它和转速的乘积就是空载反电动势。
这个方程告诉你三件事:
第一,id和iq不是完全独立的,转速项把它们耦合在一起。所以高速时只靠普通PI不够,经常要加解耦项补偿,把omega_e * Lq * iq这类耦合量前馈掉。
第二,vq的很大一部分被反电动势吃掉了,真正留给电流动态响应的电压余量不多。母线电压不够时,电流环的输出会被限幅到六边形边界,电流波形自然变差。
第三,D轴电流不给零而给负值时,Ld * id那一项会让等效磁链下降,这在高速弱磁区是故意用的手段。后面第3节我会专门讲D轴控制在工程上到底怎么取舍。
2. SVPWM与ADC采样时刻:决定FOC成败的"时序级"细节
FOC的坐标变换都是软件层面的数学运算,真正和硬件打交道的是两件事:PWM怎么发、电流怎么采。这两件事如果不配合好,再完美的算法都是空中楼阁。
2.1 FOC和SVPWM的关系:不是"二选一",而是"同一件事"
"SVPWM是空间矢量脉宽调制"这种定义人人都知道,但实际容易搞混的是:FOC和SVPWM不是并列的两种方案。SVPWM是FOC的逆变级执行器,它的任务是让三相桥臂的中点电压在PWM周期内的平均值逼近目标电压矢量。
普通SPWM只保证相电压正弦,母线利用率比较低(线性区最大相电压幅值是Vdc/2)。SVPWM通过重新分配零矢量时间,把线性调制区扩大了约1.15倍,相电压幅值可以到Vdc/根号3,这就是所谓的"直流母线利用率高了15%"。
真正动手写SVPWM的时候,核心就三件事:判断当前电压矢量在哪个扇区、计算相邻两个非零矢量的作用时间、计算零矢量的插入时间并转成三组比较值。常见做法是五段式和七段式两种发波方式:
| 发波方式 | 开关次数 | 谐波特性 | 适合场景 |
|---|---|---|---|
| 七段式 | 每周期6次切换 | 谐波小,波形对称 | 要求低噪声、低纹波的应用 |
| 五段式 | 每周期4次切换 | 开关损耗小,谐波略大 | 追求效率、散热受限的应用 |
如果你的电机发热严重或者驱动器散热条件差,优先用五段式。如果对噪音敏感,比如做家电、风机这类产品,用七段式。
2.2 ADC采样该什么时候采?为什么大家对这个问题这么纠结
"FOC ADC采样应该在什么时刻"是搜索热词,说明太多人在这个地方踩过坑。
先说结论:必须在PWM周期的中点采样,这个时刻对应着下桥臂导通且三相桥臂状态最稳定的一小段窗口。
为什么?因为采样电流通常通过采样电阻实现。你要采的是下桥臂导通时的相电流,而PWM的前半段和后半段开关动作频繁,电流波形上有大量开关噪声尖峰。PWM周期中点附近,占空比稳定,电流续流路径清晰,采出来的值最能代表这一个PWM周期的平均电流。
STM32上做这件事的标准做法是:用定时器的PWM输出通道产生事件,事件触发ADC注入组采样,并配合两个转换之间的间隔设置。F407可以直接把定时器的TRGO事件接到ADC的触发源上,把ADC触发时刻配置在对齐计数的中央对齐模式中点。
我调试过程中遇到过最典型的问题,就是采样窗口没避开死区时间。尤其在占空比接近0或者接近100%的时候,下桥臂导通时间太短,采样窗口落在死区里,采回来的电流不是真实相电流。解决办法一是限制最大占空比(比如限制到95%),二是把ADC采样窗口宽度尽量压缩,只采中点附近几十个纳秒到一两微秒的窗口。
2.3 单电阻采样与双电阻采样的工程取舍
电流采样方案直接影响ADC采样代码的复杂度。
双电阻方案:采任意两相电流,第三相用基尔霍夫定律算,复杂度低,但要多一个运放和采样电阻,成本高一点。单电阻方案:只采母线电流,通过在PWM周期内配置矢量序列,在不同时刻分别采样重建三相电流,成本低,但软件复杂度大幅上升。
如果是学习FOC或者做第一版驱动板,我的建议是老老实实上双电阻,把精力放在算法本身而不是电流重构的刁钻case上。单电阻方案在占空比接近边界时会有无法重构的"盲区",需要做移相处理,这是另一个很深的坑,不适合第一版就上。
还有一个ADC采样相关的细节:采样电阻的匹配和布局。两个采样电阻的阻值和温漂如果不一致,采样出来的电流偏置就不同,会导致dq电流里有转速频率纹波。解决方法是软件上做零电流偏置校准:电机静止时连续采几百次电流取平均,把这个值作为偏置减掉。每次上电做一次,能明显改善低速转矩平稳性。
3. 双闭环PID的整定顺序与D轴电流的"存在意义"
多数做FOC的项目落地的控制架构都是"速度外环 + 电流内环"双闭环,更复杂的还会加位置环做第三环。但架构一样,调出来的效果可能天差地别,问题往往出在整定顺序和D轴电流的处理上。
3.1 为什么必须先调电流环再调速度环
电流环是整个FOC的地基。如果电流环带宽不够或者有振荡,速度环不管怎么调都会表现得很奇怪——不是响应慢就是干脆啸叫。因为速度环的输出是电流环的给定,地基不稳,上面盖什么都歪。
正确的整定顺序是:
- 先把速度环断开,直接给定iq。
- 观察电流波形在阶跃下的响应,调电流环PI,让电流上升时间在1~2ms以内且无超调。
- 电流环稳定后,再接上速度环,从低速开始慢慢往上加。
电流环PI整定可以用理论计算起步:把电机电感L和电阻R代入,带宽选到PI调节器的零点约等于电机的电气时间常数L/R,再结合逆变器开关频率做数字化修正。更省事的做法是先用一个小增益试探,看电流阶跃响应有没有振荡趋势,再逐步加比例和积分。这一类方法在实际调试里比纯理论更快能出效果。
有一点要特别提醒:电流环的输出经过限幅才能作为Vd/Vq送给SVPWM,限幅值必须结合母线电压设置。母线电压越高的系统,电压余量越大,电流环能推上去的di/dt就越猛。你换了母线电压更高的电源,原来的PI参数很可能就不再适用。
3.2 FOC的D轴电流到底该给零吗
传统教科书上的FOC控制策略是从id=0的控制开始的,原因很简单:转矩只和iq成正比,给id电流只会增加铜损,对转矩没有贡献。但对于实际电机,尤其是有凸极效应的内置式永磁电机(IPM),id=0不是最优解。
这就涉及热词里提到的MTPA(最大转矩电流比)控制。MTPA的基本思想是:用一定的定子电流产生最大转矩。在IPM电机上,这需要往D轴注入负向电流,利用磁阻转矩来提升效率。工程上的近似做法是查表或者用公式id = -k * iq^2去拟合MTPA曲线,不需要做复杂的在线寻优。
而在高速区,D轴电流的作用变成了弱磁。因为反电动势和转速成正比,转速到达额定值以后vq会逼近电压极限,这时候给一个负的id,利用Ld * id的负向磁链抵消一部分永磁磁链,从而拉低反电动势,让电机还能继续往上提速。
所以你在网上搜"FOC d轴电机"的时候,会看到很多互相矛盾的结论:有人说D轴给零就行了,有人说D轴要负。实际都对,但分别适用于表贴式和内置式、低速和高速。原则是这样的:表贴电机低速跑,id=0;内置电机做效率优化或高速弱磁,id就要规划成转速和负载的函数。
3.3 电流速度FOC控制中的滤波与相位滞后
速度环采样到的速度反馈如果直接进PI,会引入比较明显的噪声,因为M法的测速在低速时分辨率很差。常用做法是低通滤波,但滤波会带来相位滞后,太重的滤波会让速度环带宽上不去。
我在调试中常用的做法是:电流环变量不做滤波(电流波形本来就要看真实值),速度反馈用一个截止频率在50~200Hz的一阶低通,再配合速度环PI里的积分项去消除稳态误差。如果项目要求很高的动态响应,可以考虑用观测器估算负载转矩做前馈补偿,效果比单纯调PI参数好得多。
另外聊一下PID控制电机时的一个常见误区:只调P和I,不碰D。电流环基本不需要D项,因为采样噪声会被D项放大得很难看;速度环在多数应用里也只需要PI就够了,D项只在做位置控制时才有明显的阻尼效果。很多人一上来三个参数一顿调,越调越乱,最后把D项清零重置反而稳定了。
4. 无感FOC最难的三道坎:转子初始位置、启动策略与滑模观测器
无感FOC(PMSM无感控制)这两年几乎成了做电机控制项目的标配,省掉了编码器和霍尔,成本低了很多。但代价是把"知道转子在哪"这个最基本的问题从传感器变成了算法问题,每一步都容易踩坑。
4.1 FOC转子初始位置检测:为什么电机会"抖"那一下
静止状态下,无感FOC拿不到反电动势,因为反电动势和转速成正比,转速为零时电动势也为零。所以无感方案上电第一件事就是确定转子初始位置。最基本的方法是注入高频信号或短脉冲:在定子绕组上施加几个不同方向的短时电压矢量,通过检测电流响应的差异来判断转子磁极方向。
工程上最简单的实现方式:
- 依次在六个方向施加固定宽度和幅度的脉冲。
- 测量每个方向对应的峰值电流。
- 电流最大的方向就是转子永磁体磁极方向(因为电感在小角度时随着转子位置变化)。
这套方法的缺点是:如果脉冲施加时间过长,转子会被拉过去,电机会"抖"一下。在很多不允许启动反转的产品里,这台设备在每次上电时莫名其妙动一下会非常影响体验。更高级的改进方案是脉冲幅值从小到大逐步试探,在转子真正被拉动之前就完成位置判断,但这个对电流采样精度要求比较高。
如果你的系统允许做零速定位,还有一种更简单粗暴、但可靠性极高的做法:直接给一个固定的D轴电压(把转子强拉到已知位置),再开始正常FOC。代价是上电瞬间会有一个短暂噪音和位移,适合泵、风机、电动工具这类不在乎启动瞬间动作的设备。
4.2 FOC启动:I/F强拖与开环切闭环的时机
初始定位完成后,转速还是零,反电动势还是没有,观测器依然没法用。这就需要一套把电机从零拖到某个转速的策略,让反电动势大到可以被可靠观测。
常用的是I/F强拖启动,本质上是电流闭环、角度开环:给定一个恒定的电流幅值,让电流矢量以一个固定的加速度旋转,转子被牵着走。因为角度是开环给的,转子和电流矢量之间会有一个功角,负载越大功角越大。
I/F强拖启动的核心是把转速斜坡(频率爬升率)控制好。爬太快,功角拉大,容易失步;爬太慢,启动时间太长。通常的做法是:初始角度从定位到的位置开始,电流幅值给到额定值的0.5到1倍,转速斜坡率在几个Hz/s到几十Hz/s之间试出来。等转速超过一定阈值(比如额定转速的5%~10%,反电动势已经清晰可测),再切换为闭环FOC。
切换是另一个重灾区。切换瞬间如果观测器角度和实际转子角度差得太多,电流会猛地跳动,轻则转速过冲,重则触发母线过流保护。我踩过坑的解决方法是切换前先做短暂的对齐:在强拖末尾把给定角度维持一段时间,让电流环把实际电流拉到位,同时把观测器角度慢慢拉向当前角度,对齐到误差小于几度电角度后再切闭环。
4.3 滑模观测器的原理边界与调试心得
无感FOC的转速位置估算主流方案就是滑模观测器(SMO),它利用的是反电动势信息。它的基本思路是:构建一个电流观测器,用滑模项把观测电流和实际电流的误差压到零,此时等价的控制量中就包含了反电动势信息,再从反电动势里提取转子位置和转速。
SMO的优点是鲁棒性强,对参数变化不敏感,实现简单,适合做电动工具、风机这类负载变化剧烈的场景。缺点也明显:滑模的抖振问题会带来较多估算噪声,需要低通滤波器处理,但又会引入相位延迟,最终表现为启动和低速时噪声大、高速时位置滞后。
实际调整中,最好用的调试手段是把观测器估算的theta和真实编码器的theta放在上位机里同时看。没有编码器的时候,可以对比反电动势观测值和理论估算值。你很快会看到两个典型问题:一个是滤波太重导致theta滞后,表现为正常FOC下电流波形变差;另一个是滑模增益太大导致theta高频抖动,电机会发出"滋滋"的高频噪音。这两个问题靠互相挤兑滤波器时间和滑模增益慢慢找到一个平衡点。
顺带提一句,热词里有"无感foc滑膜观测器",这是"滑模"的错别字。搜滑模观测器的时候记得用正确的词,否则会丢掉大量资料。
5. STM32平台上FOC工程落地的实操笔记
FOC的工程落地无外乎四个要素:芯片资源、PWM配置、ADC触发、调试手段。这里我结合STM32F103和STM32F407这两颗最常用的芯片,说一些HAL库下实际会遇到的细节。
5.1 从STM32F103C8T6到STM32F407ZGT6:怎么选芯片
很多入门项目用STM32F103C8T6,这颗芯片没有硬件浮点单元,做FOC全套运算时如果全部用软件浮点,PWM频率只能保守地放到10kHz左右,再高算力会非常紧张。F407ZGT6自带FPU,单精度浮点运算用硬件实现,同样的算法跑20kHz PWM还留有相当的裕量,代码结构也可以写得更规整。
所以我的建议是:学习和验证FOC原理,F103够用;但如果你想做带无感观测器、带MTPA、带WiFi通信的项目,直接上F407或者G4系列,省下的调试时间远比省下的芯片差价值钱。STM32G4系列内部集成了运放和比较器,做单电阻电流采样更方便,是专门为电机控制优化的型号。
5.2 HAL库下用定时器产生带ADC触发点的PWM
FOC用的PWM必须是中央对齐模式,也就是定时器的向上向下计数模式。这样PWM输出在一个周期内对称,且计数器的更新事件正好落在周期中点,方便触发ADC同步采样。
用HAL库配置的核心思路:
- 定时器主干(如TIM1)设为中央对齐模式,PWM输出三路互补信号(带死区),控制三相反相器。
- 把同一定时器的TRGO事件配置为"更新事件"。
- ADC的注入通道触发源选择对应的定时器TRGO。
- 在ADC转换完成中断里开始跑FOC算法,保证算法周期和PWM周期严格同步。
F407上要注意,ADC的注入组转换需要用DMA或者中断快速读取,否则转换结果没及时拿走,下一轮触发就会覆盖数据。我第一版代码就是在这个地方吃了亏:FOC计算的电流值偶尔会跳变,时好时坏,排查了半天才发现是ADC结果寄存器被覆盖了。
HAL库本身对PWM的配置代码偏冗长,尤其是死区时间和刹车输入这些参数,字段嵌套很深。我的经验是:先把PWM主输出单独调试通,用示波器确认三相互补波形和死区正常,再接入逆变桥。不要直接拿电机去试PWM配置,电机转起来不一定代表配置全对,但烧了MOS管一定是配置有问题。
关于用HAL库控制电机转速这个需求,很多人的做法是直接用HAL_TIM_PWM_Start开启PWM,然后改占空比。在FOC框架下不是这么玩的——你不需要直接改占空比,而是通过改速度环的给定值,让电流环自己去调节占空比。如果你还在用"占空比调转速"的思路做电机项目,那本质上还是开环PWM控制,和FOC是两条不同的路。
5.3 把内部变量捞出来看:FOC调试最重要的一个手段
没有示波器和上位机,调试FOC就是在摸黑走路。选型的时候一定要留一个串口或者USB口给调试用。
我常用的调试方案:把关键变量(电流给定、实际电流、估算速度、估算角度、SVPWM输出幅值、母线电压)定时通过串口DMA发送到上位机,上位机用串口绘图工具显示波形。不需要多复杂的软件,能显示时间波形就够用。
这套方案最大的价值是能同时看多个变量的时序关系。比如电机启动失败时,把给定角度、估算角度、iq给定、iq实际四个变量一起丢出来,一眼就能看出是角度没追上导致功角太大,还是iq被限幅压住了。这种问题纯靠看电流波形和听声音,一晚上都未必能定位。
另外强烈建议留一组测试点:三相桥输出的中点、母线电压采样点、电流采样运放输出。硬件上的问题在硬件端先确认,不要去软件里猜。很多"程序跑飞了"的问题,最后用示波器一测发现是电源纹波把运放输出砸出了毛刺,根本就不是代码的问题。
6. FOC还能往哪走:步进电机、WiFi控制与后续扩展思路
FOC用得多了以后你会发现它不仅仅属于PMSM,这套"坐标变换+电流闭环"的思想已经被推广到越来越多的电机类型和控制场景里。
6.1 步进电机FOC:静音、高速力矩保持与细分替代
步进电机传统控制是开环的,高频脉冲一给,力矩随转速急剧下滑。而热词里的"步进电机FOC"就是给步进电机也装上电流环和角度闭环,相当于把它变成一台低极对数的PMSM来控制。
这么做的收益很直观:开环步进在低速时噪音和振动大,因为每一步的换相都是硬性的;FOC之后电流正弦化,低速段几乎安静得和直流电机一样。同时闭环之后高速段力矩大幅改善,不会丢步,也不需要为了防丢步把电流调到很高导致电机发热。
工程上做步进电机FOC的成本也不高:很多步进电机自带编码器版本,成本增加几十块,换来的性能提升却非常明显。如果你的项目里步进电机的噪音被人投诉过,或者因为丢步出过废品,这是很值得试的一条路。
6.2 基于WiFi的电机控制系统设计思路
现在很多设备要求电机能远程控制,于是"基于WiFi的电机控制系统"这类项目越来越多。架构上通常是三层:
| 层级 | 组成 | 主要工作 |
|---|---|---|
| 设备端 | MCU + FOC驱动器 + 传感器 | 实时闭环控制,本地保护 |
| 通信层 | WiFi模块(或走路由) | 接收指令、上传运行数据 |
| 云端/APP | 服务器或手机APP | 状态显示、参数下发、故障告警 |
做这类项目时我的建议是:FOC的实时控制绝不能建在WiFi链路上。WiFi的延迟和抖动都是毫秒级的,而电流环的要求是微秒级。正确的分工是设备端全权负责实时控制,WiFi只做非实时的状态上报和参数修改。想远程调速度就给个速度给定,不要在链路上做任何实时交互。
一个实用的细节是:WiFi模块的串口和FOC调试口往往是同一个UART,调试时和组网时会冲突。设计中最好预留两个串口,或者用支持端口映射的模块,避免每次调试都要拔线。我在实际项目里就是吃了这个亏,最后把调试口挪到了第二个UART才消停。
6.3 给正在入门FOC的人几句实在话
最后说点个人感受。FOC这个东西,最难的从来不是算法本身,而是"系统思维":PWM配置和ADC采样时序是一对,电流环和速度环是一对,观测器和实际电机模型是一对,每一对之间都有时序、相位、幅值的匹配问题。任何一个环节出问题,表现都可能只是简单的"电机不转"或者"电流波形很难看",你得学会顺着链路一层层拆。
我在实际项目中的调试习惯是:先做电流环阶跃实验确认电流环OK,再做低速开环角度控制确认坐标变换没错,再切速度闭环,最后做无感或加保护逻辑。每一层验证通过之后再加下一层,出了问题永远能回到最近一个可信任的版本。这个习惯真的帮我省了无数个debug的夜晚,也推荐给你。