1. FOC 控制基础:从电机模型到矢量控制的数学原理

各位同学好,我是老周。做嵌入式电机控制这行十几年了,从最早的方波驱动到后来的正弦波,再到现在的 FOC,一路踩坑过来。今天咱们聊的这部分,是整个 FOC 控制的根基——数学原理和坐标变换。

说实话,很多初学者一上来就被 Clarke 和 Park 变换吓住了。其实没那么玄乎。你想想看,我们为什么要做这些变换?说白了,就是为了把交流电机控制得像直流电机一样简单。

核心思想:FOC 的本质是通过坐标变换,将三相交流电流解耦为相互垂直的转矩分量和磁链分量,实现独立控制。

1.1 永磁同步电机的数学模型

先说说电机模型。我习惯把 PMSM 看作一个旋转的电磁系统。它的电压方程和转矩方程,是后面所有控制算法的基础。

在自然坐标系(ABC 三相)下,电压方程长这样:

u_a = R_s * i_a + d(ψ_a)/dt
u_b = R_s * i_b + d(ψ_b)/dt
u_c = R_s * i_c + d(ψ_c)/dt

其中 ψ_a、ψ_b、ψ_c 是各相磁链,包含自感和互感,还有永磁体产生的反电动势。这个方程组耦合得很厉害,直接用它做控制,头会很大。

我的经验:在实际项目中,我从来不在 ABC 坐标系下直接设计控制器。耦合项太多,参数整定会让人崩溃。坐标变换是必经之路。

1.2 Clarke 变换:从三相到两相

Clarke 变换,也叫 3s/2s 变换。它的任务是把 ABC 三相静止坐标系,变换到 αβ 两相静止坐标系。

为什么会这样?因为三相系统本质上只有两个自由度(三相电流之和为零),用两相就能完整描述。

变换公式如下:

[i_α]   [1      -1/2    -1/2 ] [i_a]
[i_β] = [0      √3/2   -√3/2 ] [i_b]
                                [i_c]

等幅值变换下,系数是 2/3。等功率变换下,系数是 √(2/3)。我个人习惯用等幅值变换,因为调试时电流值更直观。

变换类型 系数 特点
等幅值变换 2/3 变换后电流幅值与原相电流幅值相同
等功率变换 √(2/3) 变换前后功率不变,适合功率计算

注意:我曾经在一个项目里,Clarke 变换的系数写错了,导致电流环一直震荡。查了两天才发现是系数问题。建议你在代码里把两种变换都实现,调试时切换对比。

1.3 Park 变换:从静止到旋转

Clarke 变换之后,我们有了 αβ 两相静止坐标系。但 αβ 轴上的电流仍然是交流量,控制起来不方便。

Park 变换,也叫 2s/2r 变换。它把 αβ 静止坐标系,变换到 dq 旋转坐标系。这个 dq 坐标系跟着转子一起转,角度就是转子位置角 θ。

变换公式:

[i_d]   [cosθ   sinθ] [i_α]
[i_q] = [-sinθ  cosθ] [i_β]

经过 Park 变换后,id 和 iq 变成了直流量。id 控制磁链,iq 控制转矩。这就是 FOC 的精髓——解耦控制。

关键点:Park 变换需要准确的转子位置角 θ。角度不准,id/iq 解耦就不彻底,转矩会有脉动。这也是为什么 FOC 对位置传感器或观测器精度要求高的原因。

1.4 逆变换:从 dq 回到 ABC

控制算法在 dq 坐标系下算出了想要的电压 ud 和 uq,但最终要给电机的是三相电压。所以需要逆变换。

逆 Park 变换(2r/2s):

[u_α]   [cosθ   -sinθ] [u_d]
[u_β] = [sinθ    cosθ] [u_q]

逆 Clarke 变换(2s/3s):

[u_a]   [1        0      ] [u_α]
[u_b] = [-1/2     √3/2   ] [u_β]
[u_c]   [-1/2    -√3/2   ]

嗯,这里要注意:逆 Clarke 变换的系数是 1,不是 2/3。因为 Clarke 正变换已经缩放过一次了,逆变换要还原回去。

1.5 知识体系总览

下面这张图,是我做 FOC 培训时必画的。它把整个矢量控制的流程串起来了。

FOC 矢量控制知识体系 PMSM 数学模型 ABC 三相电压/磁链方程 Clarke 变换 ABC → αβ 静止坐标系 Park 变换 αβ → dq 旋转坐标系 dq 坐标系下独立控制 id 控制磁链 | iq 控制转矩 逆 Park + 逆 Clarke dq → αβ → ABC 三相电压 SVPWM 调制 生成三相 PWM 驱动逆变器 关键输入:转子位置 θ 来自编码器或观测器

1.6 代码实现要点

在 GD32 上实现这些变换,我建议用定点数或 Q 格式,避免浮点运算拖慢速度。下面是一个简单的 Clarke 变换实现:

// Clarke 变换 - 等幅值版本
// 输入: ia, ib, ic (三相电流)
// 输出: i_alpha, i_beta
void clarke_transform(float ia, float ib, float ic, 
                      float *i_alpha, float *i_beta) {
    *i_alpha = ia;
    *i_beta  = (ia + 2.0f * ib) / 1.7320508f;  // 1/√3 系数
    // 注意:这里利用了 ia + ib + ic = 0 的特性
}

优化技巧:在 GD32F4 系列上,如果启用了 FPU,直接用 float 运算没问题。但如果是 M3 内核的 GD32F1 系列,建议转成 Q15 或 Q31 格式,速度能快 3-5 倍。

1.7 常见问题与避坑

  • 角度对齐问题:Park 变换用的 θ 是转子电角度。如果编码器安装有偏差,需要做角度校准。我曾经遇到过电机反转的情况,查了半天发现是编码器 Z 信号相位没对齐。
  • 电流采样时序:Clarke 变换用的三相电流必须同时采样。如果分时采样,会引入相位误差。我习惯用 GD32 的定时器触发 ADC,在 PWM 中心点同步采样。
  • 数值溢出:逆 Clarke 变换中,计算 u_b 和 u_c 时,u_α 和 u_β 的系数不要搞反。我见过有人把 √3/2 写成了 2/√3,结果输出电压直接饱和。

重要提醒:坐标变换的精度直接影响 FOC 性能。建议在调试阶段,把变换前后的波形通过 DAC 输出或串口打印出来,验证变换是否正确。我每个项目都会做这个验证,省去了很多后期排查的麻烦。