FOC无感控制核心:Clarke与Park变换详解及C代码实现
1. 从三相定子电流到旋转磁场先想清楚坐标变换是为了什么在做FOC无感控制之前我一直先问自己一个问题为什么非要去倒腾Clarke变换和Park变换直接按照三相电流、三相电压去做控制不行吗我的答案是可以但你会非常痛苦。因为三相电机里真正的状态变量——转子位置、转速、力矩——都不是以三相静止形式存在的。先看物理本质。三相永磁同步电机PMSM的定子上分布着三个绕组空间上相差120度。当三相对称电流流过绕组时会产生一个合成的定子磁动势它在气隙里是一个旋转的磁场转速由电流频率决定。这个旋转磁场和转子磁场相互作用产生电磁转矩。重点来了电磁转矩的大小取决于定子磁场相对于转子磁场的位置和幅值而不是取决于某相电流的瞬时值。如果只盯着ia、ib、ic三个时间函数看这三路都是交流正弦波幅值和相位随负载、转速不断变化。你要在这种时变交流量上面去做PID调节、做力矩控制PID还得不断追踪正弦参考带宽和相位延迟都让人头疼。更麻烦的是三个量之间有120度相位差系统是强耦合的你单独调某一路另两路会跟着扰动。Clarke和Park这两个变换本质上是把描述电机的坐标系从世界坐标系换到转子坐标系。Clarke先把三相静止的交流量投影到两相静止坐标系αβ上Park再把这两个正交交流量旋转到随转子磁场同步旋转的dq坐标系上。结果是什么呢在dq坐标系里看稳态工况下的id和iq都是直流量。直流量的PI控制是没有任何相位滞后问题的你可以轻松做到零稳态误差。而且电磁转矩T与iq近似成正比d轴继续负责磁场调节。控制问题从三个耦合的交流系统变成了两个解耦的直流系统。这就是FOC能实现高动态响应、高力矩精度的数学基础。这一篇我先把Clarke和Park的数学讲透包括两种系数版本的来历再给可直接移植的C代码最后一节用一组实际数字把整条链路算一遍确保你看完能自己在MCU上跑通。2. Clarke变换的完整推导与两个最容易被带到坑里的系数版本2.1 从向量投影看懂Clarke的几何意义Clarke变换解决的是降维问题把三相静止坐标系abc上的三个电流投影到两相静止坐标系αβ上。α轴与a轴重合β轴超前α轴90度。这样做的物理依据是三个绕组产生的合成磁场完全可以用两个正交绕组产生的磁场等效替代前提是三相绕组在空间上120度对称分布。设三相电流分别为ia、ib、ic规定a轴与α轴重合。把ia、ib、ic分别投影到α轴上得到iα ia - ib·cos60° - ic·cos60°因为b相和c相绕组在空间上与a相差120度投影到α轴时就会差一个cos60°的衰减方向相反取负号。化简后iα ia - 0.5·ib - 0.5·ic再看β轴投影a相绕组与β轴垂直投影为0b相和c相与β轴的夹角分别为30度和150度方向相反iβ 0 ib·cos30° - ic·cos30° (√3/2)·ib - (√3/2)·ic这两条式子就是Clarke变换的核心骨架。不过到这里还缺一个系数。上面的式子里iα和iβ算出来的是什么量是两个正交绕组单独作用时相当于单个相电流的投影量这时合成矢量的模长与三相合成的磁动势模长并不相等。为了补偿这个差异需要乘上一个系数。最常见的两个版本是等幅值变换乘2/3保证变换后的合成电流矢量模长等于原三相合成电流矢量的模长等功率变换乘√(2/3)保证变换前后功率不变用矩阵形式写等幅值Clarke| iα | | 1 -1/2 -1/2 | | ia | | | (2/3)| | · | | | iβ | | 0 √3/2 -√3/2 | | ic |你注意看等幅值版本里即使三相不平衡也不影响通用性。但如果三相电流是对称的即ia ib ic 0那iα还能再简化为iα ia这个在电流采样只有两相、第三相用-KCL运算得到的情况下非常方便。实际项目中很多驱动板只采样两路电流第三路通过ic -ia - ib补回来然后直接套简化版Clarke能省一路ADC和一路标定。2.2 等幅值与等功率为什么电机控制里更常用2/3很多初学者会在这一点上绕很久网上资料一会儿2/3一会儿√(2/3)到底用哪个这里直接给结论——做FOC电流环绝大多数电机控制库用的是等幅值变换也就是2/3系数。原因很实际等幅值变换后电流环的反馈值iα、iβ以及经Park变换后的id、iq与你在相电流采样点读到的那相电流峰值是同一个量级。做电流标定时ADC读数、电流传感器量程、PI限幅值都不需要额外换算。比如额定电流10A的电机你在dq坐标系里看到iq10A那它相电流的峰值就是大约10A单位过渡非常直观。等功率版本常用于功率分析、损耗计算这类需要严格能量守恒的场景。它的好处是变换前后总功率相等但代价是反馈电流数值偏小电压、电感的换算系数都要跟着变。如果你在控制系统里混用了两种系数比如Clarke用等幅值、电动机方程里的反电动势系数却按等功率算那电流环会出现看起来增益很大但扭矩输出不对的怪现象。这方面的坑我后面第五节还会再提。补充一个易错细节无论哪个版本Clarke变换只作用于电流/电压/磁链这类物理量。如果是做坐标变换之后的物理量再反变换回去逆矩阵系数也要配套。等幅值版本的逆Clarke矩阵为| ua | | 1 0 | | uα | | ub | |-1/2 √3/2 | · | | | | | | | uβ | | uc | |-1/2 -√3/2 | | |这个式子在做SVPWM输出占空比之前会用到后面代码部分会直接给出。3. Park变换把静止正弦量变成旋转直流量3.1 为什么必须跟着磁场转d轴与q轴的角色Clarke变换之后电流从三相静止变成了两相静止。但iα和iβ仍然是以转速ω交流变化的量正弦形式还得继续追。我再做一个旋转坐标系让这个坐标系的d轴始终对准转子的磁场方向q轴超前d轴90度。站在这个坐标系上看原本旋转的磁场静止了原本正弦变化的电流变成了直流量。这就是Park变换。d轴和q轴在物理上有明确分工d轴对准转子磁极方向id主要控制磁链大小在永磁电机里id通常被控制在0附近表贴式或负值内置式弱磁q轴垂直于磁场方向iq与电磁转矩成正比关系它才是真正出力气的量电机控制里的核心矛盾怎样让电流尽可能多地变成力矩在dq坐标系里就简化为想办法让iq尽快跟上目标值让id尽量维持在设定值。两个解耦的直流回路用经典PID就能处理得非常好。3.2 Park变换的旋转矩阵怎么来设转子磁链方向与α轴的夹角为θ电角度Park变换的定义是把αβ平面上的矢量逆时针旋转θ角投影到新的dq正交轴上。写成数学表达式id iα·cosθ iβ·sinθ iq -iα·sinθ iβ·cosθ矩阵形式就是| id | | cosθ sinθ | | iα | | | | | · | | | iq | | -sinθ cosθ | | iβ |为什么要用这个形式你把角度θ想象成一个旋转基座d轴在αβ平面上的单位方向向量是(cosθ, sinθ)q轴是逆时针旋转90度后的方向即(-sinθ, cosθ)。把iα、iβ投影到d轴方向就得到id投影到q轴方向就得到iq。线性代数里这就是一个标准的旋转矩阵R(θ)它保持矢量模长不变只改变观察角度。逆Park变换同样重要它是把dq坐标系上的电压指令ud、uq映射回αβ平面uα ud·cosθ - uq·sinθ uβ ud·sinθ uq·cosθ这个逆变换会在电流环的输出端、SVPWM调制之前执行。请注意正变换里的θ和逆变换里的θ必须是同一个值。如果观测器给出的角度有延迟那么正变换的电流就不是真正解耦在磁链方向上的逆变换给出的电压也不会落在正确的空间位置上表现出来的症状是电流环高频抖动、扭矩波动变大。这里还有个新手常见的疑问Park变换里的θ到底是转子机械角度还是电角度答案是电角度θ 电角速度×时间 初始角度。如果电机是4对极转子每转一圈电角度走4圈。这个θ在无感控制里不可能直接拿编码器读数位置和速度全靠观测器输出所以Park变换的无感版本对角度质量极为敏感。4. 无感控制版图里这个θ只能靠估转子角度从哪来4.1 反电动势法在风机水泵场景里的局限性如果你做的是带编码器的FOCθ直接读编码器就行Park变换只是查表计算。但无感二字说明系统没有位置传感器一切靠电机本身的电气和机械特性反推。那个θ是控制系统的立身之本也是无感FOC最难的关卡。高速区间最经典的方法是反电动势法。永磁电机旋转时转子磁链会在定子绕组中感应出反电动势反电动势的频率和相位藏着电角速度与转子位置信息。你可以通过观测器估算出αβ坐标系下的反电动势分量再用反正切函数求出角度。这个方法在中高速时很好用因为反电动势幅值随转速升高而增大信噪比很好。但转速跌到额定值5%~10%以下时反电动势太小淹没在噪声和死区效应里估算角度会抖得没法用。这解释了为什么风机、水泵这类工况相对温和的负载反电动势法无感FOC相当常见而需要从零速带载启动的场合就得另想办法。4.2 低速零速用高频注入往电机里塞一个试验信号零速和极低速下反电动势消失但这些场景又不能放弃控制于是有人想了个思路既然转子的凸极性会影响定子电感随转子位置变化那我就往电机里叠加一个高频电压信号然后从高频电流响应里提取角度信息。这就是高频注入法通常注入几百赫兹到几千赫兹的旋转电压或脉振电压电流环响应里能解调出包含位置信息的误差项再用锁相环把它锁住。高频注入法的代价是要有一定的凸极率。表贴式永磁电机的交直轴电感差异小注入法的位置信号非常弱而对内置式永磁电机或者一部分异步电机则更适用。另外高频噪音和多出来的损耗在静音要求高的场景里也是你要背的锅。4.3 观测器干什么用以及它和Park变换的配合无论反电动势法还是高频注入法估算出来的角度直接输入Park变换之前通常还要过一个状态观测器比如龙伯格观测器或滑模观测器。观测器的作用不光是滤波而是利用电机模型把电压、电流这些可测物理量推导出磁链、反电动势、角度和转速一起估计出来让估算角度更平滑、延迟更小。我在实测里最常遇到的问题是观测器角度在动态负载时会出现超前或者滞后。超前太多iq反馈会混入id成分滞后太多力矩指令会打折扣严重的还会引起电流环发散。解决办法是在角度进入Park和逆Park之前统一经过同一个延迟补偿环节保证正变换和逆变换用的是同一份角度数据而不是一个用补偿前、一个用补偿后的值。这个细节看着小排起故障来却能折腾好几个晚上。这一篇不展开具体的观测器方程那是后面专栏里再用一两篇专题细讲的内容。先把坐标系变换吃透后面无论看哪种观测器的推导都能跟得上它是在哪个坐标系下建模的。5. 可直接落地的C代码、逆变换与实测中的三个坑5.1 Clarke_Park的C语言最小实现先给一套能在单片机上跑的C代码不做太多封装方便你直接移植。#include math.h typedef struct { float alpha; float beta; } ClarkeResult; ClarkeResult clarke_transform(float ia, float ib, float ic) { ClarkeResult out; // 等幅值变换系数2/3 out.alpha (2.0f / 3.0f) * (ia - 0.5f * ib - 0.5f * ic); out.beta (2.0f / 3.0f) * (0.8660254f * ib - 0.8660254f * ic); return out; } typedef struct { float d; float q; } ParkResult; ParkResult park_transform(float alpha, float beta, float theta) { ParkResult out; float sin_t sinf(theta); float cos_t cosf(theta); out.d alpha * cos_t beta * sin_t; out.q -alpha * sin_t beta * cos_t; return out; }如果你采用三相平衡假设第三相电流不用采样那可以这样// 只采样ia和ib第三相由KCL推得 float ic -(ia ib); ClarkeResult cl clarke_transform(ia, ib, ic);代码本身不复杂复杂的是调试时怎么验证它是对的。我通常的做法是给定一组任意三相电流先手工算一遍再在调试器里观察alpha和beta的实际输出两者误差在浮点精度范围内就基本可信。5.2 逆变换与占空比输出链路电流环算出ud和uq之后必须经过逆Park变换得到uα、uβ再经过逆Clarke变换得到三相电压指令ua、ub、uc最后才能送去SVPWM生成占空比。逆变换代码如下void inverse_park(float ud, float uq, float theta, float *ualpha, float *ubeta) { float sin_t sinf(theta); float cos_t cosf(theta); *ualpha ud * cos_t - uq * sin_t; *ubeta ud * sin_t uq * cos_t; } void inverse_clarke_amplitude(float ualpha, float ubeta, float *ua, float *ub, float *uc) { *ua ualpha; *ub -0.5f * ualpha 0.8660254f * ubeta; *uc -0.5f * ualpha - 0.8660254f * ubeta; }注意这里的逆Clarke用的是等幅值版本的系数和正变换是配套的。不要出现正变换用2/3、逆变换按等功率系数回推这种混搭那会导致电压指令被错误缩放电流环会莫名其妙地出现增益误差。如果是做异步电机或无感永磁电机很多地方还会把Park变化里的θ换成磁链角度但dq坐标的用法是一致的。你把上面的代码封装成固定接口之后观测器输出什么角度、电流采样在哪个周期进来都只需要改输入变换层不用动。5.3 采样同步、角度对齐、系数一致性三个实测坑第一个坑三相电流必须同时刻采样。有些MCU的ADC是逐通道转换的三路电流采样通道如果前后相差数个微秒在低转速下可能看不出问题但转速一高相位差换算成电角度偏差就大了。更麻烦的是SVPWM调制波在不同扇区切换瞬间电流会有尖峰噪声。所以采样窗口最好安排在PWM中心对齐的时刻并且三路在同一PWM周期内完成并发采样或者至少由硬件触发同步。第二个坑角度对齐。Park变换里的θ应当是对应当前采样时刻的转子位置。如果你在电流采样的同一个控制周期里用上一拍计算出的θ去变换这一拍的电流就引入了拍延迟。一个更稳妥的做法是电流环、角度估算、SVPWM更新都在一个控制周期内按固定时序执行比如在下溢中断里先采样、再变换、再估算、再调SVPWM保证每个环节使用的θ都是同一计算周期的最新值。第三个坑系数一致性尤其是你从某个参考工程里复制Clarke或Park代码时有的工程连电机常数也一并写死在里面变换之后乘了电机的各类常量。如果你只复制了变换代码而忽略了常量换算电流环和速度环的增益全部要重调。我建议自己模块里只放纯粹的数学变换所有常数放到外层参数表中这样移植和排查都很清晰。6. 一个算例把整套链路走通从Ia/Ib/Ic到Id/Iq6.1 Clarke阶段的数字验证数学变换这种东西光看公式永远觉得抽象。我给你实际算一组数你再对照代码走一遍就彻底明白了。假设某一时刻采样得到三相电流ia 1.0Aib -0.5Aic -0.5A是平衡三相电流满足ia ib ic 0。套等幅值Clarkeiα (2/3) × [1.0 - 0.5×(-0.5) - 0.5×(-0.5)] (2/3) × [1.0 0.25 0.25] (2/3) × 1.5 1.0iβ (2/3) × [0.866×(-0.5) - 0.866×(-0.5)] (2/3) × 0 0这个结果很直观当三相电流恰好形成一个指向a轴方向的对称矢量时α轴分量最大β轴分量为0完全符合空间关系。这组电流和坐标系本身是静止的所以在αβ平面上它就是一个沿α轴方向的常矢量。再来一组ia 0ib 0.5Aic -0.5A。iα (2/3) × [0 - 0.25 0.25] 0iβ (2/3) × [0.433 0.433] 0.577A可以看出这三个电流的合成矢量朝β轴方向所以α成分为0。无论怎么旋转Clarke恒能把三相静止量变成平面上对应的两个正交分量。6.2 Park阶段的数字验证与一句话心得接着Clarke的算例往下走。假设电机转到了θ 30°时电流环采样得到的iα 1.0Aiβ 0.577A。这里θ是按转子磁链方向定义的。先算sin30° 0.5cos30° ≈ 0.866id 1.0×0.866 0.577×0.5 0.866 0.2887 ≈ 1.155iq -1.0×0.5 0.577×0.866 -0.5 0.5 0你会看到当电流实际方向与d轴重合时iq正好为0所有电流都在“磁场方向”上。反过来如果θ往q轴方向偏了就会出现正的iq这时转矩分量就产生了。FOC的控制目标无非就是让iq跟随指令让id维持在该在的数值。这一套计算下来你会发现Park变换本质上只是个坐标旋转没有更多神秘之处。我把这两组数在调试器里实测过C版代码输出的结果和手算在浮点误差范围内完全一致。只要你按这个流程验证过一遍之后就再也不会被公式吓住了。最后说一下我自己做FOC这段时间的体会。Clarke和Park这两个变换单独看代码量不超过二十行矩阵也不复杂。难的是想清楚每一步变换背后的物理意义和配套的系数约定。等你把变换层、电流采样层、角度估算层之间的接口理顺了后面再去看电流环整定、速度环整定、SVPWM死区补偿这些内容都会觉得顺很多。这个专栏接下来会继续展开无感FOC里更硬的骨头但坐标系变换这一关我希望你读完这篇能直接跨过去。