1. 项目背景与核心价值
去年在开发一款无刷电机控制器时,我遇到了转子位置检测的难题。传统霍尔传感器方案在高温环境下可靠性下降,而光电编码器又增加了系统复杂度和成本。这时候,滑模观测器(SMO)结合锁相环(PLL)的无传感器方案进入了我的视野。
这个方案的核心价值在于:
- 通过电机数学模型和电流采样实现位置估算
- 滑模控制带来的强鲁棒性可以抵抗参数扰动
- PLL结构有效滤除高频噪声,提取平滑的位置信号
- 完全省去了物理位置传感器,降低BOM成本
在STM32F103C8T6这样的M3内核MCU上实现这套算法,既要保证实时性(PWM周期通常50-100μs),又要确保计算精度,对软件架构和算法优化都是不小的挑战。
2. 硬件平台选型与配置
2.1 STM32F103关键外设配置
我选择的STM32F103C8T6虽然只有72MHz主频,但其丰富的外设完全能满足需求:
c复制// PWM生成配置(TIM1通道1/2/3)
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// ADC采样触发(TIM4触发注入组)
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD/2 - 1; // 中点采样
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);
关键提示:ADC采样必须与PWM中心对齐,这样可以避开开关噪声最严重的区域。我实测发现,在PWM边沿采样时电流波形毛刺会导致估算误差增大30%以上。
2.2 电流采样电路设计
相电流采样采用双电阻方案(下桥臂采样):
- 采样电阻:0.01Ω/1%精度合金电阻
- 运放电路:AD8207差分放大器(增益50倍)
- 滤波电路:二阶RC低通(截止频率10kHz)
这里有个细节要注意:运放输出需要加入1.65V偏置电压,因为STM32的ADC不能测量负电压。我最初没加偏置,导致负半周电流数据全被削顶,观测器完全无法工作。
3. 滑模观测器实现细节
3.1 电机数学模型建立
采用α-β坐标系下的PMSM电压方程:
code复制uα = R*iα + L*d(iα)/dt - ω*λ*sinθ
uβ = R*iβ + L*d(iβ)/dt + ω*λ*cosθ
其中λ为永磁体磁链,θ为转子电角度。
3.2 滑模面设计与切换函数
我采用的滑模面:
code复制sα = iα_hat - iα
sβ = iβ_hat - iβ
切换函数选用符号函数sign(s),但实际实现时用饱和函数sat(s/Φ)代替以避免抖振:
c复制float sat(float s, float phi) {
if(s > phi) return 1.0f;
else if(s < -phi) return -1.0f;
else return s/phi;
}
3.3 观测器离散化实现
由于要在中断中实时计算,采用前向欧拉离散化:
c复制// 在PWM周期中断中执行
void SMO_Update(float u_alpha, float u_beta, float i_alpha, float i_beta) {
// 电流观测
float di_alpha_hat = (u_alpha - R*i_alpha_hat + L*Kslider*sat(s_alpha,phi))/L;
float di_beta_hat = (u_beta - R*i_beta_hat + L*Kslider*sat(s_beta,phi))/L;
i_alpha_hat += di_alpha_hat * Ts;
i_beta_hat += di_beta_hat * Ts;
// 更新滑模面
s_alpha = i_alpha_hat - i_alpha;
s_beta = i_beta_hat - i_beta;
// 反电动势估算
e_alpha = -Kslider * sat(s_alpha, phi);
e_beta = -Kslider * sat(s_beta, phi);
}
其中Ts为采样周期(与PWM周期相同),Kslider取值为电机反电动势常数的2-3倍。
4. 锁相环设计与优化
4.1 传统PLL结构问题
直接使用反正切计算位置角:
c复制theta_est = atan2(-e_alpha, e_beta);
这种方法在低速时噪声极大,因为反电动势幅值太小。实测在500rpm以下时,角度抖动超过±15°,根本无法使用。
4.2 改进型PLL实现
采用基于误差信号的闭环跟踪:
c复制// PLL更新函数(在PWM中断中调用)
void PLL_Update(float e_alpha, float e_beta) {
// 计算误差信号
float sin_theta = sin(theta_est);
float cos_theta = cos(theta_est);
float error = e_alpha*cos_theta - e_beta*sin_theta;
// PI调节器
omega_est += Ki_pll * error * Ts;
float delta_theta = (Kp_pll * error + omega_est) * Ts;
theta_est += delta_theta;
// 角度归一化
if(theta_est > PI) theta_est -= 2*PI;
else if(theta_est < -PI) theta_est += 2*PI;
}
参数整定经验:
- Kp_pll = 2ξωn
- Ki_pll = ωn²
其中ξ取0.7-1.0,ωn根据带宽需求选择。对于3000rpm的电机,我最终选用ωn=200rad/s。
5. 系统集成与调试技巧
5.1 中断优先级管理
由于需要在PWM周期中断中完成所有计算,必须合理设置优先级:
code复制PWM中断(TIM1_UP):最高优先级(抢占优先级0)
ADC中断:次高优先级(抢占优先级1)
其他外设:更低优先级
实测表明,如果ADC中断优先级高于PWM中断,会导致采样时刻漂移,引入额外的计算误差。
5.2 变量标幺化处理
为提高计算效率,所有变量都采用标幺值:
c复制typedef struct {
float i_alpha; // 电流α分量(标幺值,基值5A)
float i_beta; // 电流β分量
float theta_est; // 估算角度(弧度制)
float omega_est; // 估算转速(标幺值,基值3000rpm)
} SMO_PLL_TypeDef;
这样处理有两个好处:
- 避免浮点数运算溢出
- 方便参数整定(PI参数都在合理范围内)
5.3 启动策略设计
低速时反电动势太小,观测器无法正常工作。我的解决方案是:
- 先采用开环V/F控制加速到200rpm
- 此时反电动势幅值足够,切换到SMO+PLL闭环控制
- 切换时进行角度对齐:
c复制if(first_switch) {
theta_est = atan2(-e_alpha, e_beta);
first_switch = 0;
}
6. 实测性能与优化记录
6.1 静态测试数据
在空载条件下,不同转速的角度误差RMS值:
| 转速(rpm) | 误差(°) | 电流THD(%) |
|---|---|---|
| 300 | 2.5 | 8.2 |
| 1000 | 1.8 | 5.7 |
| 3000 | 1.2 | 4.3 |
6.2 动态响应测试
突加50%负载时的响应特性:
- 转速恢复时间:120ms
- 最大转速跌落:45rpm
- 角度跟踪延迟:5°(瞬态)
6.3 关键优化记录
- 滑模增益自适应:最初固定Kslider=0.5,发现在低速时抖振明显。改为随转速变化:
c复制Kslider = K_base + 0.2*fabs(omega_est);
抖动幅度减小60%
- PLL带宽调整:发现高速时跟踪滞后,改为变带宽设计:
c复制float wn_pll = 100 + 0.1*fabs(omega_est);
Ki_pll = wn_pll * wn_pll;
Kp_pll = 1.4 * wn_pll;
- 计算精度提升:将三角函数计算改为查表法,CPU负载从35%降到18%:
c复制// 预先生成512点的sin/cos表
const float sin_table[512] = {0,...};
#define SIN(x) sin_table[(uint16_t)(x*81.487f) & 0x1FF]
#define COS(x) sin_table[((uint16_t)(x*81.487f)+128) & 0x1FF]
7. 常见问题与解决方案
7.1 角度估算出现180°跳变
现象:电机运行中突然反转
原因:反电动势过零时符号判断错误
解决:在PLL中增加象限判断逻辑:
c复制if(fabs(error) > 0.866f) { // 对应30°误差
theta_est += PI; // 跳转180°
}
7.2 低速时观测器失锁
现象:转速低于150rpm时角度漂移
优化:
- 提高ADC采样精度(改用12bit模式)
- 在滑模面中加入积分项:
c复制float di_alpha_hat = (u_alpha - R*i_alpha_hat + L*(Kslider*sat(s_alpha,phi) + Ki_int*s_int_alpha))/L;
s_int_alpha += s_alpha * Ts;
7.3 计算耗时超标
现象:PWM周期中断执行时间超过50μs
优化措施:
- 使用ARM的DSP库进行矩阵运算
- 将float改为__fp16半精度(需开启FPU)
- 关键函数用汇编重写:
assembly复制__asm void SMO_Update_ASM(...) {
vmov.f32 s0, r0 // i_alpha
vmla.f32 s1, s0, s2 // R*i_alpha_hat
...
}
8. 项目进阶方向
这套方案在STM32F1上已经能实现3000rpm以内的稳定控制,但如果需要更高性能,还可以考虑:
- 高频注入法:解决零速/极低速下的观测问题
- 模型参考自适应(MRAS):替代滑模观测器,进一步减小抖振
- 状态观测器:结合卡尔曼滤波提升抗噪性能
我在当前项目中最终实现的性能指标:
- 转速范围:50-3000rpm
- 稳态误差:<2%(全范围)
- 负载突变恢复时间:<150ms
- CPU占用率:<40%(72MHz主频)