2. 滑模观测器转子位置估算与误差补偿
任务目标
本案例实现永磁同步电机(PMSM)无传感器控制中的转子位置估算:构建滑模观测器(SMO)从静止坐标系下的电流与电压中估算反电动势,进而提取转子位置;针对滑模抖振引起的估算误差,设计锁相环(PLL)对位置信号进行滤波与补偿,输出平滑的转子位置与速度。
核心原理与实现路径
滑模观测器基于PMSM在αβ轴系下的电压方程构建。通过滑模面切换函数逼近反电动势分量,再经低通滤波得到连续的反电动势估算值。转子位置由反电动势的反正切求得,但滑模抖振与滤波延迟会引入相位误差,因此采用PLL对位置误差进行闭环修正,PLL的输出即为补偿后的转子位置与电角速度。
关键完整代码
// smo_pll_estimator.c
#include <math.h>
#define LSMO_GAIN 100.0f // 滑模增益
#define LPF_CUTOFF 100.0f // 低通滤波截止频率(Hz)
#define POLE_PAIRS 4 // 电机极对数
#define TS 0.0001f // 控制周期(s)
typedef struct {
float ialpha, ibeta; // 静止坐标系电流
float valpha, vbeta; // 静止坐标系电压
float ealpha_lpf, ebeta_lpf; // 滤波后反电动势
float theta_pll, omega_pll; // PLL输出位置与速度
} SMO_PLL_State;
// 低通滤波系数
static float lpf_coeff(float dt, float fc) {
float rc = 1.0f / (2.0f * M_PI * fc);
return dt / (rc + dt);
}
// 滑模观测器 + PLL 位置估算(单步执行)
void smo_pll_step(SMO_PLL_State *s, float ialpha, float ibeta,
float valpha, float vbeta, float dt) {
// 1. 电流误差滑模面
float err_alpha = ialpha - s->ialpha;
float err_beta = ibeta - s->ibeta;
// 2. 滑模控制律(符号函数)
float zalpha = LSMO_GAIN * (err_alpha > 0 ? 1.0f : -1.0f);
float zbeta = LSMO_GAIN * (err_beta > 0 ? 1.0f : -1.0f);
// 3. 电流观测器方程(忽略电阻与电感压降,仅保留反电动势项)
float dialpha = (valpha - s->ealpha_lpf - zalpha) * dt;
float dibeta = (vbeta - s->ebeta_lpf - zbeta) * dt;
s->ialpha += dialpha;
s->ibeta += dibeta;
// 4. 反电动势由滑模输出经低通滤波获得
float k = lpf_coeff(dt, LPF_CUTOFF);
s->ealpha_lpf += k * (zalpha - s->ealpha_lpf);
s->ebeta_lpf += k * (zbeta - s->ebeta_lpf);
// 5. PLL 提取位置与速度
// 反电动势矢量角 = atan2(-ealpha, ebeta)(与转子位置相关)
float theta_raw = atan2f(-s->ealpha_lpf, s->ebeta_lpf);
// 位置误差(角度差归一化到 [-pi, pi])
float err = theta_raw - s->theta_pll;
while (err > M_PI) err -= 2.0f * M_PI;
while (err < -M_PI) err += 2.0f * M_PI;
// PI 调节器(比例+积分)
float kp = 200.0f, ki = 2000.0f;
s->omega_pll += ki * err * dt;
s->theta_pll += (s->omega_pll + kp * err) * dt;
// 角度归一化
while (s->theta_pll > M_PI) s->theta_pll -= 2.0f * M_PI;
while (s->theta_pll < -M_PI) s->theta_pll += 2.0f * M_PI;
}
运行与验证
环境依赖:需要数学库 libm,编译时链接 -lm。将上述代码保存为 smo_pll_estimator.c,并添加一个测试主函数:
// test_main.c
#include <stdio.h>
#include <math.h>
// 模拟电机模型:给定真实位置,生成电流与电压
void motor_model(float theta_e, float omega_e, float dt,
float *ialpha, float *ibeta,
float *valpha, float *vbeta) {
float R = 1.0f, L = 0.005f, flux = 0.1f;
float ia = 2.0f * sinf(theta_e);
float ib = 2.0f * cosf(theta_e);
// 反电动势
float ea = -flux * omega_e * sinf(theta_e);
float eb = flux * omega_e * cosf(theta_e);
// 电压方程(简化)
*valpha = R * ia + L * (ia - 0) / dt + ea;
*vbeta = R * ib + L * (ib - 0) / dt + eb;
*ialpha = ia;
*ibeta = ib;
}
int main() {
SMO_PLL_State smo = {0};
float theta_real = 0.0f, omega_e = 100.0f; // 电角速度 100 rad/s
float dt = TS;
float ia, ib, va, vb;
for (int i = 0; i < 5000; i++) {
theta_real += omega_e * dt;
while (theta_real > 2*M_PI) theta_real -= 2*M_PI;
motor_model(theta_real, omega_e, dt, &ia, &ib, &va, &vb);
smo_pll_step(&smo, ia, ib, va, vb, dt);
if (i % 500 == 0) {
float err = theta_real - smo.theta_pll;
while (err > M_PI) err -= 2*M_PI;
while (err < -M_PI) err += 2*M_PI;
printf("t=%.3f 真实位置=%.3f 估算位置=%.3f 误差=%.3f rad\n",
i*dt, theta_real, smo.theta_pll, err);
}
}
return 0;
}
编译运行:
gcc -o test_smo smo_pll_estimator.c test_main.c -lm
./test_smo
通过条件:程序输出中,估算位置与真实位置的误差在0.5s后稳定收敛至±0.05 rad以内,且不随运行时间发散。若误差持续偏大或振荡,可调整LSMO_GAIN(增大以增强收敛)或LPF_CUTOFF(降低以平滑反电动势,但会增加相位延迟,需PLL补偿)。
误差补偿说明
滑模观测器输出的反电动势经低通滤波后存在相位滞后,直接反正切求取的位置会滞后于真实位置。本案例中的PLL环路通过积分作用自动消除该稳态相位误差:当theta_raw因滤波滞后而小于真实位置时,PLL的误差项持续为正,积分器提升omega_pll,使theta_pll加速追赶,最终收敛到真实位置。因此PLL同时实现了滤波与相位补偿双重功能。