3. Clark变换:三相坐标系到两相静止坐标系

好,咱们进入FOC的第一个核心数学变换——Clark变换。

说实话,我刚接触FOC时,看到这些坐标变换公式,第一反应是「这玩意儿有必要吗?」。后来在项目里调一个三相电机,电流波形乱七八糟,我才真正体会到:不把三相系统简化成两相,你根本没法下手去控制

Clark变换,说白了就是把三相静止坐标系(a, b, c)下的物理量,映射到两相静止坐标系(α, β)下。为什么这么做?因为三相耦合太强了,控制起来麻烦。两相就清爽很多。

3.1 为什么要做Clark变换?

你想想看,电机三相绕组在空间上相差120°。通电后,每一相都会产生磁动势。三个磁动势叠加在一起,合成一个旋转磁场。

问题来了:我们想控制的是这个合成磁场的大小和方向。但直接控制三相电流,你得同时调三个变量,而且它们之间还有耦合。这就像同时牵三匹马,方向还不一样,累不累?

Clark变换就是帮你把这三匹马「合并」成两匹。α轴和β轴互相垂直,没有耦合。控制起来就简单多了。

核心思想:Clark变换保持磁动势不变,只是换了个坐标系来看它。

3.2 数学推导

咱们从物理直觉出发。假设三相电流为 ia、ib、ic,它们满足:

ia + ib + ic = 0

这是星形连接的特点,三相电流之和为零。嗯,这个条件很重要,后面会用到。

现在,我们要找到两个正交的轴——α轴和β轴。通常让α轴与a轴重合,β轴超前α轴90°。

根据磁动势等效原则,可以推导出:

变换 公式
正向Clark变换
(abc → αβ)
iα = ia
iβ = (ia + 2·ib) / √3
反向Clark变换
(αβ → abc)
ia = iα
ib = (-iα + √3·iβ) / 2
ic = (-iα - √3·iβ) / 2

我的小技巧:实际项目中,我习惯用「等幅值变换」版本。也就是上面这个。还有一种「等功率变换」,系数不同,但原理一样。我个人觉得等幅值更直观,调试时好理解。

为什么会得到这个形式?我简单说下推导思路。

设三相电流产生的磁动势为:

F = N·(ia + ib·ej120° + ic·ej240°)

其中N是有效匝数。把这个复数展开成实部和虚部,实部对应α轴,虚部对应β轴。再利用 ia + ib + ic = 0 消去一个变量,就能得到上面的公式。

嗯,数学上就是这么回事。你不需要每次都推导一遍,但理解背后的物理意义很重要。

3.3 代码实现

纸上谈兵差不多了,咱们直接上代码。我用C语言写,因为嵌入式里最常用。

// Clark变换:三相电流 → 两相静止坐标系
// 输入:ia, ib, ic(三相电流)
// 输出:*alpha, *beta(两相电流)
void clark_transform(float ia, float ib, float ic,
                     float *alpha, float *beta)
{
    // 正向Clark变换(等幅值版本)
    *alpha = ia;
    *beta  = (ia + 2.0f * ib) / 1.7320508f;  // 1.732 = sqrt(3)
    
    // 注意:这里没有用到ic,因为 ia+ib+ic=0
    // 但实际采样时,我建议三个都采,用来做校验
}

我曾经踩过的坑:一开始我只采两相电流,第三相用公式算。结果有一次电流传感器坏了,我浑然不知,电机跑起来抖得像筛子。后来我改成三相都采,算完Clark变换后,再用反向变换算回去,对比原始值。一旦偏差超过阈值,立刻报错。这个冗余设计救了我好几次。

反向变换的代码:

// 反向Clark变换:αβ → abc
void inv_clark_transform(float alpha, float beta,
                         float *ia, float *ib, float *ic)
{
    float sqrt3 = 1.7320508f;
    
    *ia = alpha;
    *ib = (-alpha + sqrt3 * beta) * 0.5f;
    *ic = (-alpha - sqrt3 * beta) * 0.5f;
}

你看,代码就这么几行。但实际工程里,有几个细节要注意:

  • 浮点运算精度:嵌入式MCU的FPU如果支持单精度,就用float。如果没FPU,考虑用Q格式定点数。我之前在STM32F103上做过,用Q15格式,速度能快3倍。
  • 查表优化:√3/2 这种常数,可以预计算好。我习惯在初始化时算好,避免运行时反复除。
  • 溢出保护:如果电流很大,α和β可能超出ADC范围。记得做饱和处理。

3.4 实际项目中的经验

我记得有一次做伺服驱动器,电流环频率设到20kHz。Clark变换本身计算量不大,但加上后面的Park变换、PI调节器、SVPWM,整个控制周期就很紧张了。

我当时做了个优化:把Clark和Park变换合并成一个函数。因为Park变换需要角度信息,而Clark变换不需要。合并后,省掉了一次中间变量的存储和读取,大概省了200ns。在20kHz下,这就是4%的CPU时间,挺可观的。

代码大概长这样:

// 合并的 Clark + Park 变换
void clark_park_transform(float ia, float ib, float ic,
                          float theta,
                          float *id, float *iq)
{
    float alpha, beta;
    float cos_t = cosf(theta);
    float sin_t = sinf(theta);
    
    // Clark
    alpha = ia;
    beta  = (ia + 2.0f * ib) * 0.57735f;  // 1/sqrt(3)
    
    // Park
    *id =  alpha * cos_t + beta * sin_t;
    *iq = -alpha * sin_t + beta * cos_t;
}

我的建议:初学者先分开写,逻辑清晰。等调通了,再考虑合并优化。过早优化是万恶之源——这话是Knuth说的,我觉得很有道理。

3.5 小结

Clark变换是整个FOC的基石。它把三相耦合系统,变成了两相正交系统。虽然数学上只是简单的线性变换,但物理意义很深刻——它让我们能用更直观的方式看待电机的磁场

下一章,我们会在这个基础上做Park变换,把静止的αβ坐标系,转到旋转的dq坐标系。到那时,你就能理解为什么FOC能像控制直流电机一样控制交流电机了。

嗯,先消化这些。代码写一遍,跑一跑,比看十遍都管用。