2. Clark变换与Park变换:三相静止到两相旋转的坐标魔法
做电机控制这些年,我越来越觉得坐标变换就像变魔术。你想想看,三相电机里那些缠缠绕绕的电流,经过两次变换,居然能变成直流一样的量,直接控制转矩和磁通。这背后就是Clark和Park这对黄金搭档。
我个人习惯把Clark变换叫做「3→2」,Park变换叫做「2→2旋转」。别小看这两个变换,它们是FOC(磁场定向控制)的基石。今天我们就把它彻底讲透。
2.1 为什么非变不可?
三相电机有三相绕组,电流是交流的,互相差120度。直接控制这三相电流去调转矩?太复杂了。你想想,转矩和磁通是耦合在一起的,调一个另一个也跟着变。
Clark变换先把三相变成两相,相当于把电机从「三脚架」变成「十字架」。Park变换再把这个静止的十字架旋转起来,让它跟转子同步。这样一来,交流量就变成了直流量,控制起来跟调直流电机一样简单。
我在项目中遇到过一位同事,死活不理解为什么要做变换。他直接用PID调三相电流,结果电机嗡嗡响,转矩抖得厉害。后来我给他画了张图,他一看就明白了——坐标变换的本质就是「换个角度看问题」。
核心思想:把时变的交流量,变成时不变的直流量。控制难度直接降一个维度。
2.2 Clark变换:三相静止→两相静止
Clark变换也叫3s/2s变换。输入是三相电流ia、ib、ic,输出是两相电流iα、iβ。这两个轴互相垂直,α轴和A相轴线重合。
公式其实很简单:
iα = ia
iβ = (ia + 2*ib) / √3
等等,你是不是觉得少了点什么?ic去哪了?
嗯,这里有个小秘密:三相电流满足ia + ib + ic = 0。所以ic = -ia - ib,根本不需要单独测量。我在做第一版代码时就踩过这个坑——多采了一路电流,白白浪费了一个ADC通道。
完整的矩阵形式是这样的:
[iα] [ 1 -1/2 -1/2 ] [ia]
[iβ] = [ 0 √3/2 -√3/2 ] [ib]
[ic]
实际代码里,我们一般用等幅值变换,系数是2/3:
// Clark变换代码实现
void clark_transform(float ia, float ib, float ic, float *i_alpha, float *i_beta) {
// 等幅值变换,系数2/3
*i_alpha = (2.0f/3.0f) * (ia - 0.5f*ib - 0.5f*ic);
*i_beta = (2.0f/3.0f) * (0.866025f*ib - 0.866025f*ic);
// 如果三相平衡,可以简化
// *i_alpha = ia;
// *i_beta = (ia + 2.0f*ib) / 1.73205f;
}
避坑指南:我曾经在定点DSP上做Clark变换,忘了处理浮点数精度问题。结果电机低速运行时,iβ分量有0.5%的误差,导致转矩脉动。后来改用Q15格式,问题就解决了。
2.3 Park变换:两相静止→两相旋转
Clark变换之后,我们有了iα和iβ。但它们还是交流量——转子转一圈,它们也跟着变一次。Park变换就是要把这个静止坐标系「转起来」。
旋转的关键是转子位置角θ。你想想看,如果我们跟着转子一起转,看到的电流就是恒定的了。
公式:
id = iα * cosθ + iβ * sinθ
iq = -iα * sinθ + iβ * cosθ
这里id是直轴电流(产生磁通),iq是交轴电流(产生转矩)。FOC的核心就是:控制id恒定,控制iq调转矩。
// Park变换代码实现
void park_transform(float i_alpha, float i_beta, float theta,
float *i_d, float *i_q) {
float sin_theta = sinf(theta);
float cos_theta = cosf(theta);
*i_d = i_alpha * cos_theta + i_beta * sin_theta;
*i_q = -i_alpha * sin_theta + i_beta * cos_theta;
}
注意:θ必须是电角度,不是机械角度。对于一对极电机,电角度=机械角度。对于多对极电机,电角度=机械角度×极对数。我见过有人直接用编码器读数做Park变换,结果电机完全失控——因为忘了乘极对数。
2.4 逆变换:从旋转回到静止
控制完id和iq,我们还得把它们变回三相电压,才能给逆变器发PWM。这就是逆Park和逆Clark。
逆Park变换:
iα = id * cosθ - iq * sinθ
iβ = id * sinθ + iq * cosθ
逆Clark变换:
va = vα
vb = -0.5*vα + 0.866025*vβ
vc = -0.5*vα - 0.866025*vβ
// 逆Park变换
void inv_park_transform(float i_d, float i_q, float theta,
float *i_alpha, float *i_beta) {
float sin_theta = sinf(theta);
float cos_theta = cosf(theta);
*i_alpha = i_d * cos_theta - i_q * sin_theta;
*i_beta = i_d * sin_theta + i_q * cos_theta;
}
// 逆Clark变换
void inv_clark_transform(float v_alpha, float v_beta,
float *v_a, float *v_b, float *v_c) {
*v_a = v_alpha;
*v_b = -0.5f * v_alpha + 0.866025f * v_beta;
*v_c = -0.5f * v_alpha - 0.866025f * v_beta;
}
2.5 知识体系总览
下面这张图,是我做培训时必画的。它把整个变换链路串起来了:
2.6 工程实现要点
代码写起来不难,但工程实现有几个坑要避开:
- 角度同步:Park变换用的θ必须和采样时刻对齐。我建议在PWM中断里统一采样、变换、计算,一气呵成。
- 三角函数计算:别用标准库的sin/cos,太慢了。用查表法或者CORDIC算法。我在STM32上做过测试,查表法比标准库快5倍。
- 定点化:如果MCU没有FPU,一定要做定点化。Q15格式是业界标准,精度够用。
- 溢出保护:变换过程中可能出现数值溢出,尤其是逆Clark变换输出三相电压时。记得做限幅。
我的习惯:调试时先把Clark和Park变换单独拿出来测试。给一组已知的三相电流,看变换后的id、iq对不对。这一步没问题了,再联调整个FOC环路。否则出了问题,你根本不知道是变换错了还是PI参数没调好。
好了,Clark和Park变换就讲到这里。说白了就是两次坐标旋转,把交流变直流。代码我给你了,图也画了,剩下的就是动手去试。记住,纸上得来终觉浅,绝知此事要躬行。