说到 FOC,十个新手有八个是卡在 Clarke 和 Park 这两个变换上的。别急着否认,我当年也是:矩阵能背下来,但不知道为什么是 2/3,为什么 Park 里要乘极对数,更别提一上电机就跑飞。后来我把动图、仿真文件和手推过程全部串起来,才算是真正“搞懂”了这两个变换。这篇就是那套整理出来的干货,目标很直接:让你看完不仅能默写变换矩阵,还能理解 FOC 为什么非要用旋转坐标系,以及电流环调不稳时,怎么从变换环节里找原因。
1. 先搞清楚 Clarke 和 Park 到底在解决什么问题
1.1 用一句话说清 FOC 坐标变换的终极目标
FOC(磁场定向控制)的最终目的,是想把交流电机控制得像直流电机一样简单。直流电机为什么好控制?因为它的励磁和电枢磁动势天然正交,你只需要控制一个电枢电流,转矩就线性可控。交流电机麻烦在哪?三相电流是 120° 错开的正弦量,而且定子合成磁动势还在跟着转子同步旋转,直接去控制这三个正弦电流,PI 控制器根本压不住稳态误差——因为 PI 对直流量的跟踪是无静差的,对正弦量的跟踪天生会留下幅值误差和相位滞后。
坐标变换就是把“三相互相耦合、随时间变化”的物理量,换成“两个互相解耦、在同步坐标系下恒定”的物理量。整个过程分两步走:第一步是 Clarke 变换,把 abc 三相静止坐标转到 αβ 两相静止坐标;第二步是 Park 变换,把 αβ 静止坐标转到 dq 旋转坐标。到 dq 坐标系下看,正弦量变成了直流量,PI 控制器终于能发挥全部功力,Ud/Uq 两路独立控制,最后再通过逆 Park 和逆 Clarke 把控制量还原成三相占空比,喂给逆变器。这一正一反,就是 FOC 算法的骨架。
1.2 一个旋转木马的类比,帮你建立直觉
Clarke 和 Park 这两个变换单看公式很容易懵,我习惯用一个“旋转木马”的类比自己找感觉。
想象你在站台上看一个小孩骑旋转木马,小孩在转圈,他的位置一直在变,你很难描述他到底在哪。这时候如果你自己也跳上旋转木马,以同样的角速度跟着转,再看他,小孩就是静止的了——他永远在你前方那个固定的座位里。Clarke 就是让你从“站台上的人”先走到“旋转木马的圆心附近”,找一个不随木马转的视角把三根杆子的合力看成一个旋转矢量;Park 则是直接跳上木马,让观察者和木马同步转,把一个旋转的空间矢量变成 dq 两个静止不动的分量。转到 dq 坐标系下,那个直流量就是你能直接拿去做 PI 控制的东西。
这个直觉特别重要,因为后面所有的推导、所有的符号约定,都是围绕“合成空间矢量”和“同步旋转”这两个概念展开的。凡是 FOC 调试中出现的怪问题,百分之七八十最后都能追溯到“坐标系没有真正同步”上。
1.3 学习路径建议:先动手验证,再抠细节
我给新手的学习顺序是这样的:先用仿真跑通 Clarke 和 Park 的数值验证(正弦进去、直流量出来),再回到手推过程把系数搞明白,最后才上真机。千万别一上来就对着 STM32 的电流环代码啃,那里面还混着采样时序、PWM 更新、死区补偿一堆东西,你根本分不清是变换写错了还是时序有问题。
2. Clarke 变换:三相静止到两相静止
2.1 投影法手推 Clarke 公式
先约定参考方向:把 α 轴放在 a 相绕组轴线上,β 轴逆时针超前 α 轴 90°。三相 abc 在空间上互差 120° 分布。现在我把三相电流在 αβ 平面上各自分解投影,再叠加。这一步用的是最朴素的几何投影,不涉及任何高级数学。
先算 α 轴分量。a 相绕组和 α 轴重合,所以 a 相电流对 α 轴的贡献就是本身;b 相绕组超前 a 相 120°,它的投影是 Ib·cos(120°) = -0.5·Ib;c 相绕组落后 a 相 120°,投影是 Ic·cos(240°) = -0.5·Ic。于是:
Iα = Ia - 0.5·Ib - 0.5·Ic
再算 β 轴分量。a 相对 β 轴没有投影;b 相投影为 Ib·sin(120°) = (√3/2)·Ib;c 相投影为 Ic·sin(240°) = -(√3/2)·Ic。于是:
Iβ = (√3/2)·Ib - (√3/2)·Ic
到这里必须停下来看一个关键现象:如果输入是平衡三相正弦电流(幅值同为 I),按上面这个式子算出来的 Iα 并不是 I,而是 1.5·I。原因很简单,三相合成空间矢量的模长本来就是单相幅值的 1.5 倍,αβ 上的分量自然跟着按这个比例放大。这就是为什么 Clarke 变换前面要加系数——不加系数的是“几何 Clarke”,加 2/3 的是“等幅值 Clarke”,加 √(2/3) 的是“等功率 Clarke”,三者矩阵不一样,出来的数值也不一样。
2.2 等幅值 2/3 与等功率 √(2/3):选错后果很严重
先说我个人的结论:FOC 电流环里,绝大多数工程实现用的都是等幅值 Clarke,也就是前面乘 2/3 的那种。等功率版本主要用在功率计算、系统级仿真和某些学术场合。
等幅值 Clarke 完整矩阵(含零序)长这样:
| Iα | | 1 -1/2 -1/2 | | Ia | | Iβ | = | 0 √3/2 -√3/2 | | Ib | | I0 | | 1/2 1/2 1/2 | | Ic |上面这个矩阵没有系数,是“几何 Clarke”;等幅值版本在矩阵外面乘 2/3,等功率版本乘 √(2/3)。注意,如果电机是星形接法且没有中线,Ia + Ib + Ic = 0 恒成立,零序分量 I0 恒等于 0,很多工程代码里直接把零序那行砍掉了,这也是没问题的。把平衡条件代进去,等幅值 Clarke 还能化简成更常见的实用形式:
Iα = Ia(因为 Ib + Ic = -Ia,代入后 Iα = Ia + 0.5·Ia = 1.5·Ia,再乘 2/3 等于 Ia)
Iβ = (1/√3)·(Ib - Ic)
这个简化形式在代码里很常见,但请务必记住它的前提是星形无中线、三相电流平衡。如果电机有中线或者你调试时处在明显的不平衡状态,简化式会引入误差。
等功率版本为什么系数不一样?因为要让坐标变换前后瞬时功率守恒。简单验证一下:等幅值变换下,三相幅值为 I 的电流变换到 αβ 后,αβ 分量的幅值也是 I,但 α² + β² 的数值比原来三相平方和大了 3/2 倍,功率就不守恒。为了恢复功率守恒,需要乘 √(3/2)×(2/3) = √(2/3),这就是等功率 Clarke 的来历。所以在功率版本下,αβ 分量的幅值会是单相幅值的 √(3/2) 倍,别一看波形幅值变了就以为写错了。
FOC 里选等幅值,一个非常实际的理由:电流 PI 的输出是电压指令,SVPWM 能合成的最大相电压幅值是 Udc/√3。用等幅值 Clarke,你的电压矢量幅值直接和 SVPWM 的物理极限对应,PI 限幅设置非常直观。等功率版本会凭空多出 √(3/2) 的折算系数,限幅和标定都容易出幺蛾子。
2.3 从动图里能读出哪些关键信息
我在配套动图里放了三个画面:左上是 abc 三相差 120° 的正弦电流波形,左下是 αβ 两相波形,右边是 αβ 平面上合成矢量的轨迹。你盯着看三秒就能得到三个重要结论:
第一,αβ 两相波形仍然是在正负之间摆动的交流量,但两者相位差 90° 电角度,而且各自幅值与单相电流幅值一致(等幅值 Clarke),合成空间矢量是一个绕原点匀速旋转的圆。这个“圆”就是后续一切分析的基础——它说明三相平衡电流合成的磁动势是圆形旋转磁动势,大小恒定。
第二,把时间轴拉长看,α 轴分量和 a 相电流的波形几乎一样(等幅值、同相位),β 轴分量则超前 90°。很多人第一次看到会以为自己 Clarke 写错了,其实这就是等幅值的特征:α 轴和白 a 相重合,Iα 约等于 Ia。
第三,如果电流含有 5 次、7 次谐波,合成矢量轨迹就不再是标准圆,而是带纹波的圆。这些纹波在 dq 坐标系下会表现为 6 倍频波动,是判断电流谐波和死区影响的直观手段。
3. Park 变换:从 αβ 到跟着转子转的 dq 轴
3.1 Park 的推导:复平面旋转最清爽
前面 Clarke 的功劳是问了“能不能把三相耦合变量合成为一个旋转矢量”,而 Park 要解决的,是“这个旋转矢量怎么变成静止量”。办法很粗暴:让坐标系也跟着转。
定义 d 轴与转子永磁磁链方向对齐,q 轴逆时针超前 d 轴 90°。设当前转子电角度为 θe(d 轴相对 α 轴的夹角),那么从 αβ 到 dq 本质上就是一个坐标系的旋转变换。把 αβ 合成的矢量写成复数形式:Is = Iα + j·Iβ。要把它从静止坐标系变到旋转坐标系,就是在复数平面上乘以 e^(-jθe):
Is_dq = Is · e^(-jθe) = (Iα + j·Iβ)·(cosθe - j·sinθe)
把这段复数展开取实部和虚部,就得到了 Park 变换的公式:
Id = Iα·cosθe + Iβ·sinθe
Iq = -Iα·sinθe + Iβ·cosθe
注意,这里用复数推导最不容易出错,因为它把“坐标轴旋转”和“角度符号”的关系讲得清清楚楚:倒退着转 e^(-jθe),相当于坐标系向前转了 θe。矩阵形式是:
| Id | | cosθe sinθe | | Iα | | Iq | = | -sinθe cosθe | | Iβ |这个矩阵是正交矩阵,逆变换就是它的转置,所以逆 Park 写出来是:
Iα = Id·cosθe - Iq·sinθe
Iβ = Id·sinθe + Iq·cosθe
3.2 电角度和极对数:最容易翻车的点
公式里的 θe 不是编码器读回来的机械角 θm,而是电角度。两者关系必须先想清楚:
θe = θm × P/2
P 是电机极对数。4 极对电机,机械转角转一圈(2π),电角度已经转了 4 圈(8π)。你如果直接用机械角去做 Park,结果就是坐标系没有真正跟住磁链旋转,dq 分量会以电频率的差频来回摆动,电流环根本稳不住。
这里还有一个工程上特别容易踩的坑:编码器零点不在 d 轴上。即使你把电角度算对了,如果编码器零位和转子 d 轴之间差了一个固定偏置 θoffset,那 Park 变换对准的坐标系和实际磁链方向就有夹角,Id 和 Iq 无法真正解耦,表现出来就是:给定 Id=0 时,转矩仍然随角度变化,或者 q 轴电流响应慢、有直流偏置。正确做法是在初始化时做一次转子角度对齐(比如把电机开环拖到 d 轴对齐位置,记下编码器值作为零点偏置),或者用高频注入法做转子初始位置检测。后者在无感 FOC 里是必考题,算法本身也是建立在 Park 变换基础上的——它在 αβ 轴注入高频电压,再从高频电流响应里解调出 d 轴位置,本质上还是在做坐标变换。
无感方案里初始位置检测的坑我多说一句:因为电感在 d 轴和 q 轴不一样,高频电流响应里会带有 2 倍电角度的信息,解调出来后通常需要判断磁极极性(N/S),否则可能会差 180° 电角度。这个判断错误,启动瞬间电流会直接反向,保护不及时就容易烧管子。
3.3 逆 Park / 逆 Clarke:SVPWM 需要什么我们就给什么
电流环把 Id、Iq 控住之后,PI 输出的是 Ud、Uq 电压指令。但逆变器不认识 dq 电压,它只认识 abc 三相占空比,所以要走一遍逆变换:逆 Park 把 Ud、Uq 转回 Uα、Uβ,接着逆 Clarke 把 Uα、Uβ 转回三相反相电压的调制波,最后交给 SVPWM 生成互补 PWM。
逆 Park 公式上面已经给了。逆 Clarke(等幅值)由于 Clarke 矩阵不是方阵的简化形式,需要借助平衡条件来反解,结果如下:
Ua = Uα
Ub = -0.5·Uα + (√3/2)·Uβ
Uc = -0.5·Uα - (√3/2)·Uβ
这个递推关系有明确物理含义:只要保证三相电压之和为 0,Uα 的模长就等于相电压幅值。你用等幅值 Clarke 配合 SVPWM 的 Udc/√3 极限,限幅就是干净的、不用额外折算的。
4. 仿真文件搭建与 STM32 代码实现
4.1 Simulink 手动搭一个变换验证模型
网上有很多直接调 Motor Control Blockset 里现成变换模块的做法,但我强烈建议新手自己用最基础的 Fcn 模块手搭一遍,目的就是逼自己把矩阵落实到代码上。模型结构很简单,就这么几块(模型文件是 .slx 格式,我放在系列文档的附件里):
- Signal 模块:生成平衡三相正弦源,幅值设 5,频率设 50Hz,相位互差 120°。这一步是为了对照验证,建议做两个源:一个直接用公式生成,一个从 scope 里读出数值对比。
- Clarke 计算模块:用四个 Fcn 分别输出 Iα 和 Iβ,按等幅值公式写表达式即可。
- Park 计算模块:角度给定用斜坡信号,斜率就是同步电角速度。先给恒定转速斜坡,Id/Iq 输出应该是恒定直流量。
- Scope 组:同时观察 abc、αβ、dq 三组波形,以及 θe 的变化。
我用这个模型验证过,三相源幅值 5A、频率 50Hz 时,等幅值 Clarke 出来的 Iα、Iβ 幅值都是 5,相位差 90°;Park 如果角度斜坡斜率是 2π×50,则 Id 和 Iq 会稳定在某两个固定值。你把角度再偏移一个固定量,Id、Iq 会重新分配数值——这个现象非常有用,它能帮你直观理解“转子初始位置没对齐时,为什么 Id、Iq 不是想要的那组值”。
4.2 C 语言浮点实现:可直接搬到工程里
仿真验证没问题之后,就该把变换写成能在 MCU 上跑的代码了。下面是 STM32 上常用的浮点版本,结构清楚,方便对照公式查错:
typedef struct { float a; float b; float c; } ABC_t; typedef struct { float alpha; float beta; } AB_t; typedef struct { float d; float q; } DQ_t; const float SQRT3 = 1.7320508075688772f; AB_t Clarke(ABC_t in) { AB_t out; /* 等幅值 Clarke,忽略零序 */ out.alpha = (2.0f / 3.0f) * (in.a - 0.5f * in.b - 0.5f * in.c); out.beta = (2.0f / 3.0f) * (SQRT3 * 0.5f * in.b - SQRT3 * 0.5f * in.c); return out; } DQ_t Park(AB_t in, float theta) { DQ_t out; float s = sinf(theta); float c = cosf(theta); out.d = in.alpha * c + in.beta * s; out.q = -in.alpha * s + in.beta * c; return out; } AB_t InvPark(DQ_t in, float theta) { AB_t out; float s = sinf(theta); float c = cosf(theta); out.alpha = in.d * c - in.q * s; out.beta = in.d * s + in.q * c; return out; } ABC_t InvClarke(AB_t in) { ABC_t out; out.a = in.alpha; out.b = -0.5f * in.alpha + SQRT3 * 0.5f * in.beta; out.c = -0.5f * in.alpha - SQRT3 * 0.5f * in.beta; return out; }几个实际工程中的经验:
第一,三角函数调用在 FOC 中断里是开销大户。如果你用的是 M0/M3 这类不带 FPU 的芯片,强烈建议用查表法或 CORDIC 代替标准 sinf/cosf,否则一个 20kHz 的电流环可能被三角函数吃掉一大半中断时间。M4/M7 带 FPU 会好很多,但要开 -O2 以上优化。
第二,电流采样一定要和 PWM 中心对齐,在同一个中断里先采样再执行 Clarke-Park,保证三路电流是同一时刻的快照。三路如果分时采样,相差几微秒,高速下就是角度误差,Id/Iq 会出现脉动。
第三,θe 建议用“电角度”作为变量名传递,不要把机械角传进来再乘极对数,容易漏乘。极对数换算放编码器采样处理那一步完成。
4.3 三种方法验证变换写没写对
我自己的习惯是“三验法”,任何一个新移植的 FOC 工程都先过这三关再上电机。
第一关,数值验证。喂一组已知的 abc 数值,比如 Ia=5, Ib=-2.5, Ic=-2.5,手算正确结果应该是 Iα=5, Iβ=0。把这个 case 写进单元测试,跑不通直接定位,不用上电。
第二关,仿真对比。把 Simulink 里同样输入的结果和 C 代码跑出来的结果对齐,误差应在浮点精度内。这一步能发现公式简化是否疏忽了前提条件。
第三关,离线信号测试。电机不转,用信号发生器或 DAC 给合成三相正弦信号(或者直接用函数发生器产生正弦电压包络),让电流环给定 Id=0,Iq=阶梯值,观察变换输出能否在 q 轴建立对应的恒定电流。这一步过了,基本可以排除变换问题,剩下的锅就都是角度反馈或 SVPWM 的了。
5. 调试实录:变换相关的坑一次说清楚
5.1 高频问题速查表
我在社区和群里答疑时,变换相关的典型问题基本就下面这些,整理成速查表供大家对照。
| 现象 | 大概率原因 | 排查方法 |
|---|---|---|
| dq 电流随转速升高而周期性波动,频率等于电频率 | Park 角度符号反了,或角度反馈方向反了 | 让电机开环缓慢匀速转,检查 dq 波形是直流量还是有同频波动 |
| Id 始终不为 0,且随 Iq 联动 | 编码器零点与 d 轴未对齐,角度有固定偏置 | 做角度对齐,或在线补偿 θoffset |
| 电流幅值整体比理论值大 1.5 倍 | Clarke 漏乘 2/3,用了几何变换 | 对照等幅值矩阵检查系数 |
| 高速时转矩波动剧烈,带 6 倍电频率纹波 | 三相采样不对称、死区补偿不够,或变换正确但谐波进入 | 看 αβ 合成矢量轨迹是否为正圆 |
| 给定 Iq 为 0,但仍然电机缓慢转动 | 角度偏置 90° 电角度,ud/ud 控制轴错位 | 重新做初始位置检测,检查是否差了 90° |
| 无感启动瞬间反转或过流 | 初始位置检测磁极极性判断错误,角度差 180° | 加脉冲注入判断 N/S 极性,或换无感启动策略 |
5.2 转角没对齐,电流环全是“假故障”
这是我见过最多的一种“假故障”。现象是:按正常 FOC 流程写完了所有代码,上电给定 Iq 目标值,电机能转,但电流大、噪声大、效率低,把 PI 参数怎么调都压不住。查到最后发现,编码器安装时零位没对齐 d 轴,θe 一直存在固定偏差,Park 变换实际对的是一个“斜着的坐标系”,d 轴和 q 轴分量互相泄露,电流环看着是在控 dq,其实在控一组没有物理意义的中间量。
解决办法分两种。有感方案:做一次角度对齐,把电机开环电流拖到某一相轴线处,记录编码器值作为零点偏置,之后每次上电都减去这个偏置。无感方案:用高频注入做转子初始位置检测,这个算法输出直接就是 d 轴电角度,但要注意 180° 模糊问题。不管哪种方案,验证方法都一样:给一个恒定电流矢量,慢慢旋转角度给定,看实际产生的转矩方向是否跟着电流矢量方向走,或者用最小 Iq 转矩法确认角度对齐后 q 轴电流的转矩响应最灵敏。
5.3 我的实操心得:先验证变换,再碰电流环
最后分享一点个人体会。我刚开始做 FOC 时,最大的教训就是“别着急调 PI”。电流环 PI 参数确实重要,但你连 Clarke 和 Park 对不对都没确认就去盯 PI,等于在一个方向错误的坐标系里调参数,调出来的参数换个电机、换个转速就全废了。
现在我每次搭新平台都固定走这个流程:先用仿真文件把变换链路数值验证一遍,再在空载小电流下用离线信号测变换输出,最后才把电流环闭环、把 PI 串起来。整个过程半小时内能完成,但能省掉后面几个星期的瞎调参时间。坐标变换是整个 FOC 的地基,地基不正,上面盖什么都白搭。后面的笔记里我会继续写 SVPWM 的实现细节和电流环 PI 的整定方法,但如果你现在还在被变换卡住,先把这篇的推导和验证流程吃透再说。