第2章 Clark变换与Park变换:从三相到两相的数学魔法
说实话,刚接触FOC那会儿,我最头疼的就是这两个变换。明明电机转得好好的,为什么要搞这么多坐标系?后来在项目里被电流波形折磨了三个月,才真正明白——没有坐标变换,你根本没法对电机做精细控制。
这一章,咱们就把Clark变换和Park变换彻底讲透。我会从数学推导讲到代码实现,中间穿插一些我在产线上踩过的坑。
核心思想一句话:把三相交流电机的控制问题,变成直流电机的控制问题。说白了,就是通过坐标变换,让控制量从交流变成直流,这样PID才能稳稳地调。
2.1 为什么要做坐标变换?
三相电机有三根线,电流是正弦波,互相差120度。你想想看,如果直接控制这三个正弦量,PID根本没法调——目标值一直在变,误差也在变,控制器会疯掉的。
但如果我们换个角度看:
- Clark变换:把三相(a, b, c)投影到两相静止坐标系(α, β)上。相当于把三个互相纠缠的量,简化成两个正交的量。
- Park变换:再把静止的α-β坐标系,旋转到跟着转子走的d-q坐标系。这时候,电流就变成了直流分量——d轴控制磁通,q轴控制转矩,互不干扰。
我在第一个FOC项目里,就是没搞懂这个逻辑,直接在三相上写PID,结果电流震荡得一塌糊涂。后来老老实实做了变换,一次就稳了。
2.2 Clark变换:三相 → 两相静止
2.2.1 数学推导
假设三相电流为 ia、ib、ic,且满足 ia + ib + ic = 0(星形接法无中线)。
Clark变换公式:
iα = i_a
iβ = (i_a + 2*i_b) / √3
或者用矩阵形式:
[ iα ] [ 1 -1/2 -1/2 ] [ i_a ]
[ iβ ] = [ 0 √3/2 -√3/2 ] [ i_b ]
[ i0 ] [ 1/2 1/2 1/2 ] [ i_c ]
注意,这里我用了等幅值变换。还有一种叫等功率变换,系数不同。我个人习惯用等幅值,因为调试时幅值直观,看波形就知道对不对。
我的习惯:工程上90%的FOC库都用等幅值变换。如果你用等功率,记得把PI参数重新调一遍,不然增益会差√(2/3)倍。
2.2.2 代码实现
typedef struct {
float alpha;
float beta;
} Clarke_Output;
Clarke_Output clarke_transform(float ia, float ib, float ic) {
Clarke_Output out;
out.alpha = ia;
out.beta = (ia + 2.0f * ib) * 0.577350269f; // 1/√3
return out;
}
嗯,这里要注意:实际项目中,我们通常只采样两相电流(ia和ib),ic通过 ic = -ia - ib 算出来。省一个ADC通道,也省成本。
2.3 Park变换:两相静止 → 两相旋转
2.3.1 数学推导
Park变换需要知道转子角度 θ(由编码器或霍尔传感器提供)。
id = iα * cos(θ) + iβ * sin(θ)
iq = -iα * sin(θ) + iβ * cos(θ)
写成矩阵:
[ id ] [ cos(θ) sin(θ) ] [ iα ]
[ iq ] = [ -sin(θ) cos(θ) ] [ iβ ]
为什么这样转?你可以想象成:把α-β坐标系旋转了θ角,让d轴对准转子磁极方向。这样,id就是励磁分量,iq就是转矩分量。
我曾经踩过的坑:角度θ必须是电角度,不是机械角度。对于一对极的电机,两者相等;但对于多对极电机,θ = 机械角度 × 极对数。我第一次用4对极电机时忘了乘,结果电机抖得像筛子。
2.3.2 代码实现
typedef struct {
float d;
float q;
} Park_Output;
Park_Output park_transform(float alpha, float beta, float theta) {
Park_Output 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;
}
实际工程中,sin/cos计算很费时间。我建议用查表法或者CORDIC算法。STM32的硬件CORDIC外设就很好用,一次计算只要几个时钟周期。
2.4 逆变换:从旋转回到静止,再回到三相
2.4.1 逆Park变换
控制完id和iq(输出vd和vq),要变回α-β坐标系才能做SVPWM。
vα = vd * cos(θ) - vq * sin(θ)
vβ = vd * sin(θ) + vq * cos(θ)
2.4.2 逆Clark变换
va = vα
vb = -0.5 * vα + 0.8660254 * vβ // √3/2 ≈ 0.8660254
vc = -0.5 * vα - 0.8660254 * vβ
代码实现:
typedef struct {
float a;
float b;
float c;
} Inverse_Clarke_Output;
Inverse_Clarke_Output inv_clarke(float alpha, float beta) {
Inverse_Clarke_Output out;
out.a = alpha;
out.b = -0.5f * alpha + 0.8660254f * beta;
out.c = -0.5f * alpha - 0.8660254f * beta;
return out;
}
2.5 知识体系总览
下面这张图,把整个变换链路串起来了。我建议你把它存下来,调试时对照着看。
2.6 工程实现要点
| 环节 | 常见问题 | 我的建议 |
|---|---|---|
| 角度获取 | 电角度与机械角度混淆 | 写代码时明确注释:θ = 机械角 × 极对数 |
| 三角函数 | sin/cos计算太慢 | 用查表法或硬件CORDIC,精度0.1°就够 |
| 浮点运算 | MCU没有FPU时很慢 | 用Q15或Q24定点数,注意溢出 |
| 电流采样 | 采样时刻不对导致谐波 | 在PWM中心对齐处触发ADC,避开开关噪声 |
| 变换顺序 | 搞反正变换和逆变换 | 记住:采样→Clark→Park→PID→逆Park→逆Clark→SVPWM |
我曾经犯过的错:有一次把逆Park的符号搞反了,结果电机正转时电流id是负的,我还以为是磁铁装反了。查了两天,最后发现是公式里少了个负号。所以,写完变换代码,一定要先做开环测试:给一个固定的vd和vq,看电机能不能平稳转起来。
2.7 快速验证方法
写完变换代码,怎么知道对不对?我一般用这招:
- 给三相通直流:比如 i_a=1A, i_b=-0.5A, i_c=-0.5A
- 算Clark:得到 iα=1, iβ=0
- 算Park:如果 θ=0,则 id=1, iq=0
- 旋转θ:如果 θ=90°,则 id=0, iq=-1
如果结果跟预期一致,说明变换写对了。这一步花不了10分钟,但能省下后面几天的调试时间。
小技巧:调试时把id和iq通过DAC输出,用示波器看波形。id应该是一条平稳的直线(直流),iq也是直线。如果看到正弦波,说明角度或者变换有问题。