打算通过这个例程体会一下,如何高效、精确地进行浮点数运算

IQmath的作用 :
它是一个高度优化的定点数学库。它定义了一套特殊的数据类型(如_iq, _iq24)和一套完整的数学函数(如_IQmpy, _IQdiv, _IQsin),让你能够用类似浮点数的语法,来编写定点数代码。它在后台为你处理了所有复杂的定点运算细节。
IQ的含义: I代表整数位 (Integer),Q代表小数位 (Fractional)。例如,Q24格式表示一个32位数,其中最高位是符号位,接着7位是整数部分,剩下24位是小数部分。Q值越高,小数精度越高,但能表示的整数范围就越小。
-
代码 : UVWToAlphBetaAxes(_IQ(0.1), _IQ(0), _IQ(0.2), Alph, Beta);
-
动作:
- _IQ(0.1): 将浮点数0.1转换为IQ24格式。0.1 * 2^24 = 1677721.6,取整后为 1677722。
- _IQ(0): 结果是 0。
- _IQ(0.2): 结果是 3355443。
- 函数调用: 这个函数调用有严重的问题。在C语言中,参数是按值传递的。你将全局变量Alph和Beta(当前值为0)传递给了函数,函数内部对局部变量Alph和Beta的修改,完全不会影响到函数外部的全局变量。
如何修正代码?
你需要修改UVWToAlphBetaAxes和AlphBetaToDQ这两个函数,让它们能够修改函数外部的变量**。最标准的方法是使用指针。
void UVWToAlphBetaAxes(long uvw_U, long uvw_V, long uvw_W, long* pAlph, long* pBeta)
{
*pAlph = _IQmpy(uvw_U, _IQ(0.7071));
*pBeta = _IQmpy((uvw_V - uvw_W), _IQ(0.408242));
}
void AlphBetaToDQ(long Alph, long Beta, Sint angle, long* pM, long* pT)
{
long m_sin, m_cos;
m_sin = _IQ24sin(_IQ24(angle));
m_cos = _IQ24cos(_IQ24(angle));
*pM = (((llong)m_cos * (llong)(Alph)) + ((llong)m_sin * (llong)(Beta))) >> 24;
*pT = (-((llong)m_sin * (llong)(Alph)) + ((llong)m_cos * (llong)(Beta))) >> 24;
}
void ChangeCurrent(void)
{
UVWToAlphBetaAxes(_IQ(0.1), _IQ(0), _IQ(0.2), &Alph, &Beta);
AlphBetaToDQ(Alph_12, Beta_12, _IQ(0.1), &M_24, &T_24);
}
这个实例原来是:
好的,你提供的代码片段中包含了电机控制(特别是磁场定向控制 FOC)领域最核心的两个坐标变换:Clarke变换 和 Park变换。
这些变换的目的是将定子(Stator)上不断变化的、物理的三相交流电(U, V, W),转换到一个与转子(Rotor)磁场同步旋转的坐标系(d-q坐标系)中,从而把复杂的交流控制问题,简化成类似直流电机的、简单的直流控制问题。
下面我们来详细解析涉及到的数学和相关公式。
1. Clarke 变换 (3s/2s Transformation)
- 代码对应: UVWToAlphBetaAxes 函数。
- 目的: 将静止的三相坐标系 (U-V-W) 中的物理量(如电流、电压),变换到静止的两相正交坐标系 (α-β) 中。
- 物理意义: 想象一下,三相绕组在空间上互差120°。Clarke变换就是将这三个互差120°的矢量,等效地合成为两个互相垂直(正交)的矢量 Iα 和 Iβ。
数学公式
假设三相系统是平衡的,即 Iu + Iv + Iw = 0。
标准Clarke变换公式:
等幅值Clarke变换 (Amplitude Invariant Clarke Transform):
为了保持变换前后合成矢量的幅值不变,通常使用这个公式:
-
Iα = (2/3) * (Iu - (1/2) * Iv - (1/2) * Iw)
-
Iβ = (2/3) * (sqrt(3)/2 * Iv - sqrt(3)/2 * Iw)
代码中的简化:
你的代码 UVWToAlphBetaAxes 并没有完全实现标准的Clarke变换,它似乎做了一个高度简化或特定的变换,这在某些特定的控制策略或简化模型中可能会出现。
- Alph = uvw_U * 0.7071
- 这看起来像是 Alph = Iu / sqrt(2),这并不是标准Clarke变换的一部分。
-
Beta = (uvw_V - uvw_W) * 0.408242
- 0.408242 约等于 1 / (2 * sqrt(3)/2) 或 1 / sqrt(3) 的某个变体。Iv - Iw 这一项是Clarke变换的一部分。
正常来说,UVWToAlphBetaAxes 应该是实现标准Clarke变换的。
2. Park 变换 (2s/2r Transformation)
数学公式
Park变换本质上是一个二维旋转。
Iα 和 Iβ 是静止坐标系的两个分量,θ 是转子的实时电角度。
- Id = Iα * cos(θ) + Iβ * sin(θ)
- Iq = -Iα * sin(θ) + Iβ * cos(θ)
代码中的实现
我们来看你的代码 AlphBetaToDQ:
-
m_sin = _IQ24sin((angle)); -> m_sin 就是 sin(θ)
-
m_cos = _IQ24sin((16384 - angle)); -> 16384 在IQ16格式下代表π/2。sin(π/2 - θ) = cos(θ)。所以 m_cos 就是 cos(θ)。
-
M = (((llong)m_cos * (llong)(Alph)) + ((llong)m_sin * (llong)(Beta))) >> 24;
- 这完全对应了 Id = Iα * cos(θ) + Iβ * sin(θ)。
- 代码中的M就是Id**。>> 24 是因为两个IQ24数相乘后会得到一个Q48数,需要右移24位来恢复到Q24格式。
-
T = (-((llong)m_sin * (llong)(Alph)) + ((llong)m_cos * (llong)(Beta))) >> 24;
- 这完全对应了 Iq = -Iα * sin(θ) + Iβ * cos(θ)。
- 代码中的T就是Iq。(在FOC中,T通常代表Torque,转矩)
3. 反Park变换 和 反Clarke变换 (SVPWM)
虽然你的代码中没有体现,但在一个完整的FOC控制环路中,还需要逆向的变换:
-
PID控制器: 在d-q坐标系下,两个PID控制器分别根据Id和Iq的期望值与实际值的误差,计算出期望的控制电压 Vd 和 Vq。
-
反Park变换 (Inverse Park Transform): 将d-q坐标系下的Vd和Vq,变换回α-β静止坐标系下的 Vα 和 Vβ。
- Vα = Vd * cos(θ) - Vq * sin(θ)
- Vβ = Vd * sin(θ) + Vq * cos(θ)
-
反Clarke变换 (SVPWM): 将α-β坐标系下的 Vα 和 Vβ,最终合成为三相PWM波的占空比,去驱动电机的U, V, W三相。这个过程通常由一种名为空间矢量脉宽调制 (Space Vector Pulse Width Modulation, SVPWM) 的算法实现。
4. 角度计算
总结:
这段代码的核心数学就是电机矢量控制中的坐标系旋转理论:
- Clarke变换: 3相 -> 2相静止 (U,V,W -> α,β)
- Park变换: 2相静止 -> 2相旋转 (α,β -> d,q)
通过这一系列数学变换,MCU成功地将一个复杂的、耦合的、时变的三相交流系统,解耦成了两个独立的、易于控制的直流系统,从而可以像控制直流电机一样,精确地控制交流电机的励磁和转矩。
但是在调试这段代码的过程中总是不顺利,
00013cdc: movigh gr5 0x68 <_IQ31CosLookup+100>
00013ce0: movigl gr5 0xffff828c
00013ce4: call 0x133a0 <_IQ24mpy>
00013ce8: nop
00013cec: nop
00013cf0: nop
00013cf4: store32 gr2 gr30 0xa <_IQ31CosLookup+6>
14 }
00013cf8: load32 gr31 gr30 0x7 <_IQ31CosLookup+3>
00013cfc: addi gr30 gr30 0x20 <_IQ31CosLookup+28>
00013d00: ret
00013d04: nop
00013d08: nop
00013d0c: nop
17 {
AlphBetaToDQ:
00013d10: addi gr30 gr30 0xffffffd8
00013d14: store32 gr31 gr30 0x9 <_IQ31CosLookup+5>
00013d18: load32 gr2 gr30 0xc <_IQ31CosLookup+8>
00013d1c: store32 gr4 gr30 0x8 <_IQ31CosLookup+4>
00013d20: store32 gr5 gr30 0x7 <_IQ31CosLookup+3>
00013d24: store16 gr6 gr30 0xd <_IQ31CosLookup+9>
00013d28: store32 gr7 gr30 0x5 <_IQ31CosLookup+1>
25 m_sin = _IQ24sin((angle));
00013d2c: load16 gr2 gr30 0xd <_IQ31CosLookup+9>
00013d30: chw gr4 gr2
00013d34: call 0x13900 <_IQ24sin>
Break at address "0x13d0c" with no debug information available, or outside of program code.”
问了下大模型,给出的提示是说,栈溢出了,明天看看究竟是什么情况再继续。