1. 从“升魂浩荡”说起:这套FOC电流环到底在折腾什么
“升魂浩荡”这个词放在电机控制里其实挺传神——电流环调好了,电机运转那种丝滑跟手的感觉,确实像给一台普通电机“升了魂”。我最早接触FOC是在一台云台电机上,当时用方波驱动,低速抖动、噪音大、力矩脉动明显,换成FOC之后整个手感完全变了。而FOC三环里头,电流环是最内环,也是决定整套控制品质的地基。速度环、位置环调得再花哨,电流环要是软了、抖了、响应慢了,上层全是空中楼阁。
这篇内容主要面向已经上手过STM32、玩过PWM输出、对电机控制有基本概念,但电流环总是调不出理想效果的兄弟。我会把电流环从采样时刻、坐标变换、PI整定、解耦补偿、程序结构这几个维度拆开讲,尽量把“为什么这么写”讲透,而不是只丢一段代码让你抄。文中涉及的参数和代码结构,都是基于STM32G4/F4系列加DRV8301或类似栅极驱动方案的常见实践,其他平台思路一致,细节自行迁移。
先给一个整体认知:电流环的本质,就是让电机绕组里的电流,在几毫秒甚至几百微秒内,精确跟踪你给定的Id和Iq。Id一般给0(表贴式PMSM),Iq对应转矩。听起来简单,但实际做起来,采样时机差一点、角度延迟一点、PI参数偏一点,波形就全乱了。下面按我实际调试的顺序,一层层往下剥。
2. 电流环的整体设计与方案选型思路
2.1 为什么电流环必须用FOC而不是简单PI
很多人一开始会想:我不就是想让电流等于给定值吗?直接拿给定电流减反馈电流,过一个PI,输出PWM占空比不就行了?这个思路在直流有刷电机上勉强能用,但在三相PMSM上会直接崩掉。原因在于,三相电流是耦合的,你调A相,B相和C相的电流也会跟着变,三个PI互相打架,永远稳不下来。
FOC的核心操作,是通过Clarke变换和Park变换,把三相交流量转到两相旋转坐标系下。在dq坐标系里,Id和Iq在稳态下是直流量,直流量用PI调节器可以做到零稳态误差。这就是为什么FOC的电流环能做得又准又快。Clarke变换把三相变成两相静止坐标系(αβ),Park变换再根据转子电角度θ,把αβ转到dq。整个链路是:
Ia、Ib、Ic → Clarke → Iα、Iβ → Park(θ) → Id、Iq → PI调节 → Vd、Vq → 反Park(θ) → Vα、Vβ → SVPWM → 六路PWM
这个链路里,θ的准确性是命门。θ差一点,Id和Iq就会串扰,你给Iq阶跃,结果Id也跟着跳,转矩响应就脏了。所以有感和无感的区别,很大程度上就是θ从哪来的区别。
2.2 有感与无感在电流环层面的差异
有感方案用编码器或霍尔,θ直接读,电流环可以跑得很高带宽,通常PWM频率16kHz到20kHz,电流环也在同频率执行,带宽能做到1kHz以上。无感方案用滑膜观测器或龙伯格观测器估算θ,估算本身有延迟和噪声,电流环带宽就得压一压,否则观测器跟不上,整个系统会啸叫。
我自己的经验是:第一次调电流环,一定先用有感把电流环调明白,把采样、变换、PI、SVPWM这条链路全部验证正确,再去碰无感。否则无感不收敛的时候,你根本分不清是观测器的问题还是电流环的问题。热词里提到的“无感foc滑膜观测器”和“foc转子初始位置检测”,都是建立在电流环已经稳定的前提下的。
2.3 电流环执行频率与PWM频率的关系
一个常见误区是电流环频率随便设。实际上,电流环的执行频率最好等于PWM频率,并且在PWM计数器的特定时刻触发ADC采样。以中心对齐PWM为例,计数器从0往上数到ARR再往下数回0,通常选择在计数器等于ARR(即波峰)或者等于0(波谷)时触发ADC。这个时刻,下桥臂导通,采样电阻上的电流才是真实的相电流。
如果采样时刻选错,比如在开关切换瞬间采样,你会采到开关噪声,电流波形上全是毛刺,PI再一放大,电机直接叫。所以“foc adc采样应该在什么时刻”这个问题,答案就是:中心对齐模式下,在计数器波峰或波谷触发,且要避开开关死区引起的振铃,通常加一点采样延迟。
3. 核心细节解析与实操要点
3.1 ADC采样时刻与电流重构
先讲采样。三相电流一般只采两相,第三相由Ia+Ib+Ic=0算出来。采样电阻可以放在下桥臂对地,也可以放在母线。下桥臂采样成本低,但要求在下桥臂导通时采。中心对齐PWM的波峰时刻,三个下桥臂都导通(假设占空比不是100%),这时候采最稳。
具体操作:把ADC的触发源设为TIM的TRGO,TRGO事件配置在计数器等于ARR时产生。然后在ADC中断里读结果。但要注意,从触发到ADC实际采样保持,中间有采样窗口,如果PWM频率很高(比如20kHz,周期50us),采样窗口要设短一点,比如几个ADC时钟周期,否则会采到下一个开关状态。
我踩过的坑:一开始没加采样延迟,电流波形在轻载时还行,一加载就全是尖刺。后来在触发后加了约1us的延迟再启动ADC,波形立刻干净。这个延迟具体多少,要看你的栅极驱动死区时间和MOS管开关速度,一般0.5us到2us之间试。
3.2 Clarke与Park变换的实现细节
Clarke变换有两种形式,等幅值和等功率。电流环里常用等幅值,系数是2/3。公式:
- Iα = Ia
- Iβ = (Ia + 2Ib) / √3
或者用更对称的形式:
- Iα = (2Ia - Ib - Ic) / 3
- Iβ = (Ib - Ic) / √3
实际写代码时,为了省计算,常用查表或者定点运算。STM32G4有CORDIC和FMAC,可以加速三角函数,但Park变换需要sin和cos,如果MCU没有硬件三角函数,就用查表加线性插值。表长1024或2048,精度足够。
Park变换:
- Id = Iα * cosθ + Iβ * sinθ
- Iq = -Iα * sinθ + Iβ * cosθ
这里θ是电角度,不是机械角度。电角度 = 机械角度 × 极对数。编码器读回来的是机械角度,要乘极对数再取模。这个细节新手经常忘,结果电机转起来电流环完全不对。
3.3 电流环PI调节器的结构与参数整定
电流环PI一般用并联形式:
- Vd = Kp * (Id_ref - Id) + Ki * ∫(Id_ref - Id) dt
- Vq = Kp * (Iq_ref - Iq) + Ki * ∫(Iq_ref - Iq) dt
但实际工程里,为了抗积分饱和,会加限幅和抗饱和逻辑。积分项在输出限幅时停止积分,或者用反算抗饱和。
参数整定有个经典方法:把电流环等效成一阶惯性环节,然后按带宽配置。电机电气时间常数τ = L/R,L是相电感,R是相电阻。PI的零点对消掉这个极点,即Ki/Kp = R/L。然后Kp决定带宽,Kp = L * ωc,ωc是期望带宽,一般取PWM角频率的1/10到1/5。
举个例子:某云台电机L=0.5mH,R=0.5Ω,PWM频率20kHz,ωc取2000rad/s(约318Hz)。则Kp = 0.0005 * 2000 = 1.0,Ki = Kp * R/L = 1.0 * 1000 = 1000。这个值只是起点,实际还要根据采样延迟和滤波再微调。
注意:这个计算假设电流环执行频率远高于带宽,且采样延迟可忽略。如果执行频率只有10kHz,带宽要再降。
3.4 交叉解耦补偿的必要性
dq轴之间不是完全独立的,存在交叉耦合项:Vd和Vq里分别有 -ωLqIq 和 ωLdId + ωψf 的耦合。低速时ω小,耦合可以忽略;高速时如果不补偿,Id和Iq会互相干扰,电流环跟踪变差。
补偿公式:
- Vd_comp = Vd_pi - ω * Lq * Iq
- Vq_comp = Vq_pi + ω * Ld * Id + ω * ψf
其中ψf是永磁磁链。表贴式电机Ld≈Lq,补偿简单些。我一般在中高速时才开解耦,低速时关掉,避免参数不准反而引入扰动。热词里“永磁同步电机电流环内膜解耦”说的就是这个,内膜解耦是更高级的形式,用内模原理设计控制器,对参数鲁棒性更好,但实现复杂,新手先把前馈解耦做好就够用。
4. 实操过程与核心环节实现
4.1 硬件与工程环境准备
我用的平台是STM32G474 + DRV8301 + 一台24V云台电机,编码器是AS5047P。工程用STM32CubeMX生成,IDE是STM32CubeIDE。时钟配置到170MHz,PWM用TIM1,中心对齐,频率20kHz,死区时间500ns。ADC用注入组,由TIM1的TRGO触发。
关键配置清单:
| 项目 | 配置 |
|---|---|
| PWM模式 | 中心对齐,20kHz |
| 死区 | 500ns |
| ADC触发 | TIM1 TRGO,波峰 |
| ADC采样时间 | 1.5 cycles |
| 电流采样 | 下桥臂电阻,两相 |
| 编码器 | SPI,AS5047P |
| 电流环频率 | 20kHz,与PWM同步 |
4.2 电流环中断服务程序结构
中断里做的事情要尽量精简,顺序如下:
- 读ADC结果,得到Ia、Ib
- 计算Ic = -Ia - Ib
- 读编码器,得到机械角,乘极对数得电角度θ
- Clarke变换得Iα、Iβ
- Park变换得Id、Iq
- PI调节,得Vd、Vq
- 解耦补偿
- 反Park得Vα、Vβ
- SVPWM计算占空比
- 更新TIM比较寄存器
整个过程要在50us内完成,所以尽量用定点或硬件加速。G4的CORDIC可以算sin/cos,FMAC可以加速滤波,用起来很爽。
4.3 SVPWM的实现与占空比计算
SVPWM比SPWM电压利用率高15%,是FOC标配。实现方式有查表法和公式法。公式法用Vα、Vβ判断扇区,然后算作用时间。我一般用简化公式:
- 先算三个中间变量:
- Va = Vβ
- Vb = (√3 * Vα - Vβ) / 2
- Vc = (-√3 * Vα - Vβ) / 2
- 然后根据Va、Vb、Vc的正负判断扇区
- 再算T1、T2,最后算三相占空比
实际写的时候,注意Vα、Vβ要归一化到母线电压。如果母线电压波动,最好做母线电压补偿,否则占空比会随电压漂。
4.4 参数整定现场记录
第一次上电,先不接电机,用示波器看PWM输出,确认六路波形正常,死区正确。然后接电机,但母线电压调低到5V,防止炸管。给Id_ref=0.1A,Iq_ref=0,看Id能不能跟上。如果Id震荡,先降Kp,再降Ki。如果Id响应慢,加Kp。
我当时的记录:
- Kp=1.0,Ki=1000,Id阶跃0.1A,响应时间约2ms,超调20%
- 降Kp到0.5,Ki到500,响应时间约4ms,超调5%
- 再加Kp到0.8,Ki到800,响应时间约2.5ms,超调10%
最后取Kp=0.8,Ki=800。然后给Iq阶跃,看转矩响应。注意Iq阶跃时电机会转,要固定转子或者用测功机加载。
5. 常见问题与排查技巧实录
5.1 电流波形毛刺大、PI输出饱和
最常见的原因是采样时刻不对。排查步骤:
- 用示波器看采样触发信号和PWM波形,确认触发在波峰或波谷
- 看ADC采样窗口是否太短
- 检查下桥臂采样电阻的运放滤波电容是否太大,导致响应慢
- 检查死区时间是否过长,导致采样时下桥臂还没完全导通
我遇到过一次,毛刺是因为运放供电被PWM噪声干扰,加了个RC滤波就好了。
5.2 电机啸叫、电流环震荡
啸叫通常是PI参数太激进,或者解耦补偿参数不对。先把解耦关掉,只留PI,如果还叫,降Kp。如果降Kp后响应太慢,检查电流环执行频率是否稳定,有没有被其他中断打断。
另一个原因是电角度θ有偏差。用示波器看相电流和反电动势过零点,如果对不上,说明θ有偏移,需要校准编码器零位。
5.3 无感模式下电流环不收敛
无感时,观测器估算的θ有延迟和噪声。解决办法:
- 降低电流环带宽,Kp和Ki都降
- 在观测器输出加低通滤波,但滤波会引入相位延迟,要折中
- 检查滑膜观测器的增益是否合适,增益太大抖,太小收敛慢
我一般先用有感把电流环调到满意,然后切无感,把Kp降30%左右,再微调。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 电流毛刺大 | 采样时刻不对 | 示波器看触发与PWM |
| 电流震荡 | PI太激进 | 降Kp、Ki |
| 电机啸叫 | 解耦参数错 | 关解耦再试 |
| 响应慢 | Kp太小 | 加Kp |
| 稳态误差 | Ki太小 | 加Ki |
| 高速跟踪差 | 未解耦 | 开前馈解耦 |
| 无感不收敛 | 观测器延迟 | 降带宽、调观测器 |
提示:每次只改一个参数,改完记录波形,否则你永远不知道是哪个参数起了作用。
6. 程序结构优化与代码组织建议
6.1 中断优先级与执行时间
电流环中断优先级要最高,不能被其他中断打断。把ADC中断设为最高优先级,编码器读取可以放在里面,但SPI读编码器可能耗时,如果太慢,可以用DMA或者放在低速任务里,用上一次的角度加补偿。
我实测SPI读AS5047P约2us,可以接受。如果编码器是SSI或BiSS,可能更慢,要考虑。
6.2 定点与浮点的选择
G4有FPU,浮点运算快,我直接用float。F4也有FPU,但主频低,如果电流环频率高,可以考虑定点。定点Q格式要小心溢出,尤其是积分项。
6.3 代码模块划分
建议分成:
- bsp_adc.c:ADC初始化与读取
- bsp_encoder.c:编码器读取与角度计算
- foc_math.c:Clarke、Park、反Park、SVPWM
- foc_current.c:电流环PI与解耦
- foc_main.c:主循环与状态机
这样换平台时,只改bsp层,算法层不动。
7. 一些个人体会与后续扩展
调电流环这件事,急不得。我见过太多人一上来就想跑无感高速,结果连有感的电流波形都没看明白。我的建议是:先把有感的电流环调到阶跃响应干净利落,再碰速度环,最后才是无感和高速。每一步都拿示波器看波形,不要凭感觉。
另外,母线电压补偿、温度补偿这些,低速时影响不大,但高速或大电流时一定要做。还有,电流环的PI输出限幅要设对,一般限制在母线电压的0.95倍以内,留点余量给SVPWM。
后续如果想深入,可以研究内模解耦、预测电流控制、无差拍控制,这些在高性能场合有用。但基础电流环没调好,这些高级算法也救不了。先把地基打牢,再往上盖楼。