2. Clark变换与Park变换:坐标变换的数学推导与DSP实现技巧
坐标变换,说白了就是SVPWM的数学基础。没有它,你根本没法把三相电压转换成空间矢量。我刚开始接触这个的时候,也觉得公式挺唬人的,但真正用起来,其实就那么几步。
2.1 为什么需要坐标变换?
三相电机有三相绕组,每相电流都是正弦波,相位差120度。你想想看,三个变量同时变化,控制起来多麻烦。能不能把它们简化一下?
坐标变换就是干这个的。它把三相静止坐标系(a,b,c)先变到两相静止坐标系(α,β),再变到两相旋转坐标系(d,q)。这样一来,交流量就变成了直流量,控制就简单多了。
核心思想: 把时变的三相系统,映射到非时变的两相旋转坐标系中。说白了,就是让控制器只处理直流信号。
2.2 Clark变换:从三相到两相静止
Clark变换的公式长这样:
Iα = Ia
Iβ = (Ia + 2*Ib) / √3
等等,你是不是觉得少了Ic?没错,因为Ia + Ib + Ic = 0,所以Ic = -Ia - Ib。实际用的时候,我们只需要采样两相电流就够了。
我在项目中遇到过一个问题:采样电阻的噪声会直接耦合到Iα和Iβ上。后来我加了个一阶低通滤波,效果好了很多。
等幅值变换 vs 等功率变换
| 变换类型 | 系数 | 特点 |
|---|---|---|
| 等幅值变换 | 2/3 | 变换后幅值不变,SVPWM常用 |
| 等功率变换 | √(2/3) | 变换前后功率守恒,电机模型常用 |
我个人习惯用等幅值变换,因为SVPWM的扇区判断和矢量作用时间计算都是基于幅值的,用等幅值变换更直观。
2.3 Park变换:从静止到旋转
Park变换把αβ坐标系旋转θ角度,得到dq坐标系:
Id = Iα * cosθ + Iβ * sinθ
Iq = -Iα * sinθ + Iβ * cosθ
这里θ是转子位置角,通常由编码器或霍尔传感器提供。如果没有位置传感器,那就得用观测器估算了——那是另一个话题。
小技巧: 在DSP里实现Park变换时,sinθ和cosθ可以查表得到。我一般用256点的正弦表,精度够用,计算也快。
2.4 避免三角函数计算瓶颈
三角函数计算是DSP的噩梦。一次sin或cos运算可能要几十个时钟周期,而SVPWM的开关频率通常是10kHz~20kHz,每个周期都要算好几次。你想想看,这得多浪费CPU资源。
怎么优化?我总结了三个方法:
- 查表法: 预先算好sin和cos值,存到数组里。运行时直接查表,速度极快。
- CORDIC算法: 只用移位和加法就能算三角函数,适合没有硬件乘法器的低端DSP。
- 角度归一化: 把角度映射到0~2π范围内,避免大角度计算带来的精度损失。
我曾经在一个项目里用过CORDIC,但发现精度不够,后来还是换回了查表法。嗯,这里要注意:查表法的精度取决于表的大小,256点一般够用,如果要求高,可以做到512点或1024点。
2.5 DSP实现技巧:定点数 vs 浮点数
很多DSP没有硬件浮点单元,比如TI的C2000系列早期型号。这时候就得用定点数来模拟浮点运算。
我常用的做法是Q格式:
// Q15格式:1位符号位 + 15位小数位
// 表示范围:-1 ~ 0.999969
int16_t sin_table[256]; // Q15格式的正弦表
// 查表时,角度索引 = (θ & 0xFF) // 取低8位
int16_t sin_val = sin_table[theta & 0xFF];
int16_t cos_val = cos_table[theta & 0xFF];
// Park变换,全部用定点乘法
int32_t id = (int32_t)ialpha * cos_val + (int32_t)ibeta * sin_val;
int32_t iq = -(int32_t)ialpha * sin_val + (int32_t)ibeta * cos_val;
// 结果右移15位,还原为Q15格式
id = id >> 15;
iq = iq >> 15;
注意: 定点乘法容易溢出。比如两个Q15数相乘,结果是Q30格式,需要右移15位才能回到Q15。如果中间结果超过32位,就得用64位累加器,或者分段计算。
2.6 代码示例:完整的Clark+Park变换
下面是一个完整的C语言实现,适合在DSP上跑:
// 输入:三相电流ia, ib(Q15格式),转子角度theta(0~65535对应0~2π)
// 输出:id, iq(Q15格式)
void clark_park(int16_t ia, int16_t ib, uint16_t theta,
int16_t *id, int16_t *iq) {
// Clark变换
int16_t ialpha = ia; // Iα = Ia
int16_t ibeta = (ia + 2 * ib) / 1732; // Iβ = (Ia+2Ib)/√3, √3≈1.732
// 查表得到sin和cos
uint8_t idx = (theta >> 8) & 0xFF; // 取高8位作为索引
int16_t sin_theta = sin_table[idx];
int16_t cos_theta = cos_table[idx];
// Park变换
int32_t tmp_id = (int32_t)ialpha * cos_theta + (int32_t)ibeta * sin_theta;
int32_t tmp_iq = -(int32_t)ialpha * sin_theta + (int32_t)ibeta * cos_theta;
*id = tmp_id >> 15;
*iq = tmp_iq >> 15;
}
这段代码我用了好几年,在多个项目里验证过。唯一要注意的是,角度theta的表示方式——我用的是0~65535对应0~2π,这样取高8位就能得到0~255的索引,非常方便。
2.7 知识体系总览
下面这张图总结了坐标变换的整个流程:
2.8 避坑指南
最后,分享几个我踩过的坑:
- 角度对齐问题: 编码器的零位和电机的电气零位要对齐,否则Park变换出来的dq分量会有直流偏置。我曾经因为这个原因,电机低速时抖得厉害,查了两天才发现是角度没对齐。
- 采样时序: 电流采样要在PWM的中间时刻进行,避开开关噪声。我一般用PWM的ADC触发功能,在载波波谷处采样。
- 数值范围: 定点数运算时,中间结果很容易溢出。建议先用浮点仿真一遍,看看各个变量的动态范围,再确定Q格式的位数。
好了,坐标变换这部分就讲到这里。记住,Clark和Park变换是SVPWM的基石,一定要吃透。下一节我们会用这些变换结果来合成空间电压矢量,那才是SVPWM的核心。