做电机控制这些年,被问得最多的问题就是"FOC控制过程到底是怎么一步步跑起来的"。说实话,硬件方案成熟之后,FOC并不算难,它更像一道逻辑题:只要把相电流、转子角度、电压指令这几件事在坐标系里理顺,剩下的工作就是在每个PWM周期里按固定顺序算完十几条公式。真正难的是每一步背后的原因,以及调试时碰到电流发散、启动反转、切换冲击这些实际问题要怎么定位。这篇文章我就把FOC控制过程完整拆开,从电流采样到SVPWM输出,从PI参数估算到无感启动与闭环切换,写成一份可以直接照着落地调试的工程笔记,适合刚接触FOC的嵌入式软件工程师,也适合想从方波控制转向FOC的硬件和算法开发者。
1. FOC的实质:把交流电机当直流电机来"骗"
FOC的英文全称是Field-Oriented Control,直译就是磁场定向控制。不少人第一次听到这个概念,以为它是一种很玄的电机驱动波形生成技术,其实它本质上是在做一个控制目标的翻译工作:把电机本体里"三相交流电 + 旋转磁场"的复杂问题,转换成"两个直流电流分量"的简单问题。用一句话概括我的理解:FOC不是在千方百计生成正弦波形,而是在把交流电机当作一台直流电机来控制。
1.1 三相电流为什么不能直接作为PI的反馈
很多人一开始会疑惑,三相电流本身就是正弦波,PI控制器专门跟踪直流量的,为什么不能直接给三个PI呢?这就涉及到控制对象的本质。三相定子绕组在空间上相隔120度,通入相位依次相差120度的正弦电流后,气隙里才会形成匀速旋转的磁场。这三个相电流的频率等于电频率,它随转速一直在变,如果用PI去跟踪一个正弦信号,跟踪精度直接受PI带宽限制,带宽再高也会出现相位滞后。更麻烦的是,转速升高时正弦频率也在升高,相当于让控制器去追一个永远追不上的目标。
还有一层数学上的简化:三相电流并不是独立的。星形连接下ia + ib + ic = 0,知道任意两相,第三相就等于负的和。真正产生转矩的并不是某一相电流的大小,而是三相电流合成的定子电流空间矢量is。这个矢量有自己的幅值和方向,控制目标从"让三相电流分别等于三个正弦量"变成"让合成电流矢量等于期望的矢量",本身就是一次降维。后面的Clark变换和Park变换,全都是围绕这个空间矢量展开的。
1.2 d-q轴电流:控制目标与电机物理的对应关系
把转子永磁体的磁极方向定义为d轴,也就是直轴,再沿旋转方向超前90度定义为q轴,也就是交轴。于是定子电流矢量is被分解成两个分量:id和iq。id作用在d轴方向,它不产生有效转矩,只影响气隙磁场强弱;iq作用在q轴方向,和转子磁场相互作用,这才是真正产生电磁转矩的分量。
对于表贴式永磁同步电机,Ld约等于Lq,电磁转矩近似正比于iq;对于内嵌式永磁电机,Ld和Lq有明显差异,还会多出一项磁阻转矩,这时可以通过让id为负值来提升单位电流的转矩输出,也就是MTPA最大转矩电流比控制。从控制角度看,无论哪种电机,核心动作都收敛到两件事:让id收敛到目标值,让iq跟随速度环给出的转矩电流指令。目标值从变化的交流量变成稳定的直流量之后,PI控制器终于可以做它最擅长的事了。
1.3 转子磁链角度:整个FOC的"基准坐标"
d-q坐标是旋转坐标,它跟着转子磁链的电角度θe走。这个角度是整个FOC的基准坐标,角度不准,id和iq就没有解耦:iq指令里会混入id成分,表现为同样的电流指令下转矩变小、电流偏大,甚至出现电流震荡。我第一版FOC调试时就吃过这个亏,电流环参数怎么调都共振,后来发现编码器安装时零位偏了几度,角度误差直接把整个控制环路带偏了。
角度误差并不是"差不多就行"。实测中3度左右的角度误差在额定工况下就能让电流明显变大,10度以上的误差会把正向转矩算成负向,电机直接反转。所以我的调试顺序永远是:第一步抓角度波形,确认旋转方向和零位,第二步才谈电流环。有传感器的FOC直接读编码器角度乘以极对数,无感FOC靠反电动势观测器估计,不管哪种方案,角度这条线如果没打通,后面调得再久也是白费。
2. 一条完整的FOC链路:从ADC到SVPWM到底走了多少步
每个PWM周期内,FOC做的事情可以压缩成一条固定顺序的链路:采样相电流、偏置校正、坐标变换、电流环PI、逆变换、SVPWM生成六路PWM。初学者最容易把SVPWM理解成"生成正弦波的一种调制方式",其实它只是一套把αβ坐标系下的电压指令翻译成逆变器开关序列的规则。下面把这条链路一步步拆开。
2.1 Clark与Park:静止坐标和旋转坐标的切换
Clark变换把三相静止坐标系变换到两相静止坐标系。采用等幅值变换且在星形连接、三相平衡的前提下,工程里最常用的简化公式是:
iα = ia
iβ = (1 / √3) * (ib - ic)
如果三相不平衡或者在三角形连接下,需要用完整公式,包括ia、ib、ic三项的系数,别为了一点运算量牺牲鲁棒性。
Park变换把两相静止坐标变换到两相旋转坐标,公式如下:
id = iα * cos(θe) + iβ * sin(θe)
iq = -iα * sin(θe) + iβ * cos(θe)
这个符号约定不是唯一的,但一定要和逆Park变换保持配套。我建议统一用上面的约定,并规定iq为正时对应正向电磁转矩。逆Park变换是这样的:
vα = vd * cos(θe) - vq * sin(θe)
vβ = vd * sin(θe) + vq * cos(θe)
注意PI输出得到的是vd和vq,它们不是直接加在电机端线上,而是要经过逆Park变换变成αβ坐标系下的分量,再送到SVPWM环节。这个"PI输出电压指令、逆变器去执行电压指令"的层次关系一定要清楚,否则很容易把SVPWM和逆变器概念搅在一起。
2.2 SVPWM的扇区判断与矢量作用时间计算
逆变器有六个开关管,同一桥臂上下管不能同时导通,只看上管状态的话,八个基本开关状态里包含六个有效矢量U1到U6和两个零矢量U0、U7。这六个有效矢量把平面分成六个扇区,SVPWM要解决两个问题:判断参考电压矢量落在哪个扇区,以及在这个扇区里让相邻的两个矢量各作用多少时间。
扇区判断有现成做法,根据vα和vβ计算三个中间量:
U1 = vβ
U2 = -vβ + √3 * vα
U3 = -vβ - √3 * vα
U1大于0时A记为1,U2大于0时B记为1,U3大于0时C记为1,N = A + 2B + 4C。根据N值就能映射出实际扇区,这个映射我在DSP和普通MCU的FOC库里都见过,只是命名和符号略有差异,核对一次自己手上的矢量图就行。
确定扇区后,一种直观且不易出错的矢量作用时间计算方式是用几何法。先求参考电压矢量的幅值Umag和角度θ,再求它在当前扇区内的相对角度γ,于是相邻两个矢量的作用时间分别是:
T1 = √3 * Umag * sin(60° - γ) * Ts / Udc
T2 = √3 * Umag * sin(γ) * Ts / Udc
T0 = Ts - T1 - T2就是零矢量作用时间。如果T1加T2已经超过Ts,说明进入了过调制区,必须按比例同时缩小T1和T2。这个饱和度处理一定要放在写入PWM比较寄存器之前,否则生成出来的占空比可能超过定时器周期,波形直接乱套。
有人会问,为什么用这种带sin的计算而不是网上常见的X、Y、Z查表法?其实那些查表法本质上就是把上面的三角函数公式按六个扇区展开后的代数化简,为了省掉FPU上的sin和atan2运算。如果你的主控带FPU,几何法完全够用而且不容易写错;如果是不带FPU的8位或者低端MCU,再去用查表法。实际工程里追求的是可维护性和不出错,不是把代码写得越绕越好。
2.3 一个可以照着写的FOC主循环伪代码
把整条链路串起来,控制中断里做的事情就是下面这个固定流程,可以当作模板来用。每个PWM周期进入一次,频率通常取10kHz到20kHz。
// 每个PWM周期Ts内执行一次 void FOC_ISR(void) { // 1. 读取ADC采样结果:ia、ib ia = ADC_GetChannel(ADC_CH_A); ib = ADC_GetChannel(ADC_CH_B); ic = -(ia + ib); // 2. 偏置校正 ia -= ia_offset; ib -= ib_offset; // 3. 角度更新:有编码器时直接用,无感时用观测器结果 theta += omega * Ts; // 4. Clark变换 i_alpha = ia; i_beta = (1.0f / SQRT3) * (ib - ic); // 5. Park变换 id = i_alpha * cos(theta) + i_beta * sin(theta); iq = -i_alpha * sin(theta) + i_beta * cos(theta); // 6. 电流环PI:需要做输出限幅和反饱和处理 vd = PI_Update(&pid_d, id_ref, id); vq = PI_Update(&pid_q, iq_ref, iq); // 7. 逆Park变换 v_alpha = vd * cos(theta) - vq * sin(theta); v_beta = vd * sin(theta) + vq * cos(theta); // 8. SVPWM:扇区判断、计算比较值、更新定时器 svpwm_update(v_alpha, v_beta); }这套伪代码是FOC的最小全集。实际项目里还要在链路上挂速度环、弱磁控制、MTPA、死区补偿、故障保护,甚至观测器更新,但无论加多少东西,主链路的相对顺序不要乱动。我见过有人把PI输出限幅漏了,结果积分疯狂累加,一恢复指令电机猛冲一下,这种问题在代码评审里一定要盯住反饱和处理。
3. 电流环设计与PI整定:先算后调,再不行就扫
电流环是FOC的内环,速度环再漂亮,内环带宽跟不上,外环全白搭。很多初学者一上来就乱试PID三个参数,我强烈不建议这么做。电流环的对象是一阶R-L电路,PI参数完全可以先按电机参数估出一个能直接跑的初值,再用示波器微调。
3.1 用电机参数算出PI初值
表贴式永磁同步电机的d-q轴电压方程,在忽略耦合项并做一定简化后,可以写成:
vd = R * id + L * did/dt
vq = R * iq + L * diq/dt + ω * ψf
d轴和q轴本质上都是R-L一阶惯性对象,电流环PI开环传递函数和电机传递函数相乘后,如果把PI零点放在电机极点位置,也就是满足:
Ki / Kp = R / L
那么闭环就近似成一阶惯性环节,开环增益Kp / L直接决定电流环带宽。于是估算公式很简单:
Kp = L * ωc
Ki = R * ωc
其中ωc是期望的电流环带宽,单位是rad/s。举个例子,一台小型无人机电机,相电阻R是0.25Ω,相电感L是35μH,想把电流环带宽做到3000rad/s,也就是大约477Hz,那么Kp = 35e-6 * 3000 = 0.105,Ki = 0.25 * 3000 = 750。单位分别是V/A和V/(A·s)。
PWM频率20kHz时,Ts只有50微秒,电流环带宽建议取1000到3000rad/s。超过5000rad/s后会非常难稳,因为数字控制本身有1到2个PWM周期的延迟,带宽拉太高,相位裕度会快速掉光,表现为高频抖动和噪音。这个公式估算出来的初值,只要电感误差不是特别离谱,通常已经能稳定运行,接下来再用波形去修正。
3.2 实测调参时让波形开口
有了初值后,不建议直接带着负载调,最实用的做法是锁轴调试。把电机轴机械锁死,电流环给定一个iq阶跃,用示波器看实际iq波形。电流环作为一阶系统,阶跃理论上不应该超调,所以看到超调通常意味着环路延迟补偿不到位或采样噪声过大。
我把常见的现象和调整方向总结在下面这张表里,实际调试时可以对着排查:
| 现象 | 可能原因 | 调整方向 |
|---|---|---|
| 响应太慢,iq像斜坡一样慢慢爬 | Kp偏小,带宽不够 | 增大Kp,保持Ki/Kp比例不变 |
| 阶跃出现明显超调和振铃 | 环路延迟导致相位裕度不足 | 整体降低增益,或者增大PWM频率缩短Ts |
| 稳态有小幅高频抖动 | 采样噪声或零漂 | 检查采样时刻、偏置校准,必要时加一阶低通 |
| 指令方向切换时冲击大 | PI输出限幅或积分饱和 | 检查限幅逻辑,加入反饱和处理 |
注意一个反直觉的点:电流环出现超调时,很多人下意识去减小Kp,但如果是相位裕度不足引起的,减小Kp确实能稳定,代价是带宽变低、动态变慢。我一般会在保证Ki/Kp = R/L的前提下整体缩放增益,把带宽压到一个稳定且有余量的值,然后观察dq轴电流在阶跃下的表现。d轴电流阶跃比q轴更干净,因为d轴没有反电动势项干扰,所以调参时先看id回路的波形会更直观。
3.3 闭环带宽与延迟预算
数字FOC从采样到PWM更新,天然存在延迟:ADC采样需要时间,算法计算需要时间,占空比更新通常要等下一个PWM周期才能生效。这个总延迟大约1到2个PWM周期,直接影响电流环能实现的最高带宽。工程上用经验公式粗估,带宽上限大约在1/(3Ts)附近,20kHz PWM时约6600rad/s,实际建议留足裕量,只用到一半左右。
也就是说,把PWM频率从8kHz提到16kHz或20kHz,不只是让波形更细腻,而是直接给电流环争取了带宽空间。高带宽的电流环能让外环速度环更稳定,也让无感观测器在更高动态下不失步。所以设计FOC系统时,PWM频率通常是先定好的约束条件,电流环带宽跟着PWM频率走,速度环带宽再比电流环带宽低一个数量级左右,这样整个级联系统才有足够的稳定裕度。
4. 无感FOC从静止到高速:启动流程和过渡逻辑
无感FOC不用编码器也不用霍尔,硬件成本最低,但代价是低速时反电动势太小,观测器几乎没法用。所以从静止到高速必须拆成几个阶段,每个阶段的控制策略都不同。这一步是很多无感方案"一转就飞"的重灾区。
4.1 初始位置:预定位还是高频脉冲注入
转子静止时如果不知道磁极方向,直接给一个旋转磁场,轻载下也许能转起来,重载或带机械设备时大概率反转或堵转。工程上最简单可靠的做法是预定位,也叫alignment:给定一个固定的d轴电流,比如0.5到1倍额定电流,持续200到500毫秒,让转子被定子磁场强行拉到已知角度。这个方法的缺点很直白,启动瞬间电机会先动一下,对静摩擦小的水泵、风机没影响,但精密传动或压缩机这类不允许反转的应用就不行。
如果要求静止启动且不能有明显位移,就得用高频信号注入法。向估计的d轴注入高频电压信号,利用d-q电感差异带来的电流响应差异来判断转子位置。这个方案实现复杂度和计算量都高不少,很多无感算法只在低速段用高频注入,中高速切换回反电动势观测器。我的选型习惯是:允许启动瞬间动一下,用预定位;要求静止启动,优先上高频注入;介于两者之间的,先用预定位跑通整个闭环链路,再升级到注入法。
4.2 开环强拉阶段的电流控制
有了初始位置,接下来进入开环强拉阶段。所谓开环,指的是控制用的角度不来自观测器,而是来自软件积分器生成的给定电频率ωe。这个频率会按照预设斜坡从零往上爬,同时电流指令幅值也从启动值逐步提升。即使角度是"假"的,电流环仍然正常工作,d-q轴坐标系跟着软件角度旋转,把真实电流强行拽成期望的id和iq。
这个阶段最关键的是斜坡速率。电频率增加太快,转子实际速度跟不上,就会出现失步,电流瞬间失控;太慢又会让启动过程显得拖沓。加速度斜率主要看负载惯量,我做过的一个风机项目里,电频率每秒加15Hz左右,约1秒内从0拉到15Hz电频率,带负载实测电流波动很小。如果设备有大惯量负载,斜坡要更缓,必要时分段处理:先慢速建立电流,再逐渐提升频率,最后在切换点附近把加速度进一步调小。
4.3 切换闭环时如何避免"咯噔"一声
无感FOC最惊心动魄的时刻,就是开环强拉切换到闭环观测器的那一下。切换瞬间如果角度跳变,电流环看到的坐标系突然转过一个角度,电流冲击会非常剧烈,轻则电机"咯噔"一声,重则过流保护触发。
我的做法是让观测器从开环阶段就一直运行,只估计不参与控制。在强拉过程中持续比较开环给定角度θ_open和观测器估计角度θ_obs,使用一个低成本的逻辑:两个角度的差值要先经过2π跳变处理,变成连续差值,再判断是否小于阈值,比如5度以内,并且维持一小段时间,才允许切换。切换时不要硬切,而是做一个软过渡窗口,控制角度θ_ctrl = (1-k) * θ_open + k * θ_obs,k从0平滑变化到1,窗口持续几十到几百毫秒。这样即使两个角度有一点残余误差,电流冲击也会被平滑掉,而不是瞬间全部接过来。
切换完成后,速度环才真正接管,把速度指令和观测器速度估计做闭环。整个启动过程中,电流环一直在线,所以最怕的不是电流环不稳,而是角度源切换那一瞬间的坐标系错动。把这个过渡做平滑了,无感启动的成功率会大幅提升。
5. 实际工程中容易忽略的坑:采样、死区、零漂
写完算法链路和启动逻辑,再聊几个我在实际调试中踩过、也帮别人排查过的问题。这些问题单看都不难,但藏在波形异常背后时,很容易让人在PI参数上白费几天时间。
5.1 电流采样时刻到底该选在哪
最常用的低压方案是双电阻下桥臂采样,也就是在两个下管导通时,通过采样电阻上的压降获得相电流。采样时刻要选在下管导通区间的稳定中点,通常就是PWM计数器到达周期值或零点的时刻,对应三角载波的顶点。这时三相下桥臂在七段式调制里都处于导通或续流状态,采样窗口最大,电流纹波也最小,ADC采出来的值最有代表性。
如果采样时刻没选好,正好落在开关沿附近,ADC结果会混入振铃和开关噪声。比较诡异的一种现象是:空载时电流波形很干净,一加载就全是毛刺,很多人以为是干扰,加滤波、换地线折腾半天,最后发现是占空比增大把采样窗口挤到了开关沿附近。解决思路不是加滤波器,而是确认ADC触发源由定时器同步触发,并且采样保持时间足够,而不是用软件延时去碰运气。单电阻采样方案更复杂,一个PWM周期里的电流信息要靠多个采样点重构,在低占空比和高占空比区域都存在天然盲区,小型电机项目里能用双电阻或三电阻采样,就不要为了省一个采样电阻去选单电阻方案。
5.2 偏置校准为什么必须开机做
电流采样链路的运放偏置不可能精准为零。偏置不消除,id和iq里会始终叠加一个固定偏置,电机在零指令时也可能有电流,表现为空载发热、轻微抖动,甚至低速时有一点点动静。这个问题和PI参数没有关系,所以很多人调参数调到头也解决不了。
正确的做法是在PWM禁止输出、电机不供电的状态下,多次采样电流通道取平均,把结果保存为偏置值。注意要在上电一段时间、电源稳定之后再校准,如果环境温度变化大,最好定期重校。我遇到过一次很隐蔽的问题:换了运放芯片之后,零漂比原来大了几十毫安,空载电流波形看起来还正常,但带上负载后力矩波动明显。排查到最后才发现是偏置变了,从那以后我都在初始化流程里加开机自校准,并保留运行中空闲时定时重校的接口。
5.3 死区补偿和调制策略的配合
死区是防止桥臂直通故意插入的等待时间,但它会造成输出电压误差和电流波形畸变。死区时间固定时,误差大小与电流方向相关:电流方向为正,实际输出电压会低于指令;方向为负,实际输出电压高于指令。低速轻载时,电流接近零的区域会出现平台畸变,听上去像电机噪音变大,转矩脉动明显增加。
死区补偿的计算式很简单,补偿电压为:
v_comp = -sign(i) * Tdead * Udc / Ts
实现时方向判断很关键。电流接近零时sign()会来回跳,如果盲目补偿,补偿量反而成了扰动源。我习惯加一个滞回或者零区阈值,电流绝对值小于某个门槛时不补偿,超过门槛才按方向补偿。另外一个容易忽略的点是,死区补偿要和调制策略配合,七段式SVPWM和五段式DPWM的零矢量插入方式不同,死区误差分布也不一样,切换调制策略时必须重新验证补偿参数。
最后说说我的实际体会。FOC调通的关键不在公式背得多熟,而在能不能把每个环节的波形拿过来和理论对照。我调过的每一套FOC方案,几乎都经历过电流震荡、启动反转、切换冲击这些必经之路,每次排查问题按这个顺序来:先怀疑角度、再怀疑采样、最后才怀疑控制器参数,能省掉大量无效劳动。如果你正被某个"电流震荡"折磨,可以先把转子机械锁死,单独调电流环,内环稳了再去碰速度环和无感切换。这个流程我反复用,十次里有九次能快速定位问题。FOC本质上不是什么高深莫测的算法,它只是一套需要耐心对待坐标变换和控制时序的工程实践,把主链路顺序和每个环节的物理意义都弄清楚后,剩下的就是按部就班地调、测、验证。