4. STM32硬件平台选型

上一章我们聊了Clark变换,把三相静止的ABC坐标系,硬生生掰成了两相静止的αβ坐标系。但问题来了——电机转子是转的,你αβ轴还杵在那儿不动,这怎么玩?

Park变换就是来解决这个问题的。它把两相静止的αβ坐标系,映射到两相旋转的dq坐标系上。说白了,就是让坐标系跟着转子一起转。这样一来,原本在αβ轴上忽大忽小的正弦波电流,到了dq轴上就变成了直流分量。直流分量PID好控啊,这就是FOC能实现精准控制的底层逻辑。

核心思想: Park变换的本质是“坐标旋转”。它把交流量变成直流量,让PI调节器不再需要跟踪正弦波,只需要跟踪直流给定。

4.1 从几何直观理解Park变换

先别急着看公式。我带你从几何上理解一下。

想象你站在地面上(静止的αβ坐标系),看着一个旋转的圆盘(转子)。圆盘上有个标记点(磁链方向)。你看到的标记点位置,每时每刻都在变。但如果你也站到圆盘上,跟着它一起转,那标记点在你眼里就是静止的。

Park变换干的就是这件事。它把αβ轴上的分量,投影到旋转的dq轴上。d轴(直轴)与转子磁链方向对齐,q轴(交轴)超前d轴90度电角度。

这里有个关键点:Park变换需要知道转子当前的电角度θ。这个θ从哪里来?从编码器或者霍尔传感器来,或者从无感观测器估算出来。没有θ,Park变换就是无源之水。

4.2 Park变换公式推导

好,我们来推公式。其实很简单,就是二维坐标系旋转。

假设αβ坐标系中有一个向量,它在α轴上的分量为Iα,在β轴上的分量为Iβ。现在我们要把这个向量投影到旋转了θ角的dq坐标系上。

从几何关系上看:

  • d轴分量 = Iα × cos(θ) + Iβ × sin(θ)
  • q轴分量 = -Iα × sin(θ) + Iβ × cos(θ)

写成矩阵形式就是:

[Id]   [cos(θ)  sin(θ)] [Iα]
[Iq] = [-sin(θ) cos(θ)] [Iβ]

这就是Park变换的完整公式。你仔细看,它其实就是个旋转矩阵。我当年第一次看到这个公式时,心想:就这?对,就这。但就是这简单的旋转,把交流控制变成了直流控制。

我的经验: 在实际代码中,cos(θ)和sin(θ)通常通过查表或CORDIC算法计算。STM32的硬件CORDIC外设可以加速这个计算,但用软件查表也够用,只要表够密(比如1024点)。

4.3 逆Park变换

有正变换就有逆变换。FOC控制中,我们算出了dq轴上的给定电压Ud和Uq,但最终要送到SVPWM模块去驱动电机,而SVPWM需要的是αβ坐标系下的电压。所以需要逆Park变换:

[Uα]   [cos(θ)  -sin(θ)] [Ud]
[Uβ] = [sin(θ)   cos(θ)] [Uq]

注意看,逆变换矩阵是正变换矩阵的转置。因为旋转矩阵是正交矩阵,逆矩阵等于转置矩阵。这个性质在代码实现时很有用——你只需要存一份cos和sin值,正变换和逆变换都能用。

4.4 代码实现:STM32上的Park变换

直接上代码。我习惯用浮点运算,清晰易懂。如果你对性能有极致要求,可以改成定点数,但浮点对于STM32G4或H7系列来说完全够用。

/**
  * @brief Park变换:αβ -> dq
  * @param Ialpha: α轴电流
  * @param Ibeta:  β轴电流
  * @param theta:  转子电角度(弧度)
  * @param Id:     输出的d轴电流指针
  * @param Iq:     输出的q轴电流指针
  */
void Park_Transform(float Ialpha, float Ibeta, float theta,
                    float *Id, float *Iq)
{
    float cos_theta = arm_cos_f32(theta);
    float sin_theta = arm_sin_f32(theta);

    *Id = Ialpha * cos_theta + Ibeta * sin_theta;
    *Iq = -Ialpha * sin_theta + Ibeta * cos_theta;
}

/**
  * @brief 逆Park变换:dq -> αβ
  * @param Ud: d轴电压给定
  * @param Uq: q轴电压给定
  * @param theta: 转子电角度(弧度)
  * @param Ualpha: 输出的α轴电压指针
  * @param Ubeta:  输出的β轴电压指针
  */
void Inv_Park_Transform(float Ud, float Uq, float theta,
                        float *Ualpha, float *Ubeta)
{
    float cos_theta = arm_cos_f32(theta);
    float sin_theta = arm_sin_f32(theta);

    *Ualpha = Ud * cos_theta - Uq * sin_theta;
    *Ubeta = Ud * sin_theta + Uq * cos_theta;
}
注意: 这里的theta是电角度,不是机械角度。对于极对数为P的电机,电角度 = 机械角度 × P。如果你用编码器采集的是机械角度,别忘了乘以极对数。

4.5 代码验证:用数据说话

代码写完了,怎么验证对不对?我建议你造一组已知数据来测试。

假设我们有一个单位向量,在αβ坐标系中指向45度方向:

  • Iα = 0.7071
  • Iβ = 0.7071

现在转子电角度θ = 45°(0.7854 rad)。按照Park变换,这个向量应该完全投影到d轴上,q轴分量为0。我们来算:

Id = 0.7071 × cos(45°) + 0.7071 × sin(45°)
   = 0.7071 × 0.7071 + 0.7071 × 0.7071
   = 0.5 + 0.5 = 1.0

Iq = -0.7071 × sin(45°) + 0.7071 × cos(45°)
   = -0.7071 × 0.7071 + 0.7071 × 0.7071
   = -0.5 + 0.5 = 0.0

结果完美:Id=1.0,Iq=0.0。这说明当电流向量与d轴对齐时,q轴没有分量,所有能量都用来产生磁链。反过来,如果电流向量与d轴垂直,则Id=0,Iq=1.0,所有能量都用来产生转矩。

这就是FOC的底层逻辑:通过控制Id和Iq,独立控制磁链和转矩。

4.6 坐标系变换全景图

为了让你对整个变换链路有个全局认识,我画了张图:

FOC坐标系变换链路 三相静止 ABC坐标系 两相静止 αβ坐标系 两相旋转 dq坐标系 PI控制 Id/Iq给定 Clark变换 Park变换 PI调节 逆变换路径(从控制到输出) PI输出 Ud/Uq 逆Park变换 dq → αβ SVPWM Uα/Uβ输入 电机 三相驱动 θ 角度反馈 Park变换和逆Park变换都需要θ θ来自编码器/霍尔/无感观测器 Park变换公式 Id = Iα·cosθ + Iβ·sinθ Iq = -Iα·sinθ + Iβ·cosθ 逆变换:转置矩阵

4.7 常见坑与避坑指南

我在项目中遇到过几个Park变换相关的坑,分享给你:

  • 角度单位搞混: 有人用度,有人用弧度。代码里统一用弧度,否则cos/sin算出来全是错的。我曾经调试一个电机,转起来抖得像筛糠,查了半天发现是角度单位没统一。
  • 角度更新不及时: Park变换和逆Park变换用的θ必须是同一个时刻的值。如果你在中断里先算Park变换,然后被高优先级任务打断,回来再算逆Park变换时θ已经变了,那控制就会乱。我的做法是在一个临界区内同时完成正变换和逆变换。
  • 浮点精度问题: 对于低端STM32(比如F103),浮点运算慢,可以考虑用Q15或Q31定点数。但对于G4/H7系列,硬件FPU加持下浮点运算几乎不占时间,直接用float就行。
重要提醒: 千万不要在Park变换里直接用math.h的sinf/cosf。虽然功能对,但速度慢。用ARM的DSP库(arm_sin_f32/arm_cos_f32)或者查表法,速度能快5-10倍。

4.8 小结

Park变换说白了就是一个旋转矩阵,把静止的αβ坐标系转到跟着转子转的dq坐标系。它让交流量变成了直流量,为后续的PI控制铺平了道路。

代码实现上,记住三点:角度用弧度、正逆变换用同一组cos/sin、角度更新要原子操作。把这三点做到位,Park变换这块就不会出问题。