1. STM32电机控制工程概述
搞电机控制的朋友都知道,ST官方的MotorControl库是个好东西,但里面的水也深得很。最近我在折腾基于STM32的无感FOC方案,用的就是MotorControl库5.4版本,针对三电阻双AD采样做了深度优化。这个方案最吸引我的地方是它把龙贝格观测器和PLL锁相环结合得相当巧妙,实测性能可以媲美市面上不少商业驱动器。
工程采用KEIL开发环境,整体结构清晰。核心文件主要分布在User目录下,其中mc_config.c负责外设初始化,mc_tasks.c处理控制环任务,main.c则是状态机的主控逻辑。这种模块化设计让代码维护起来特别顺手,尤其是在调试不同电机参数时,可以快速定位到需要修改的模块。
注意:使用前务必检查硬件连接,特别是电流采样电阻的布局。三电阻方案对PCB布线要求较高,建议采样走线长度控制在5cm以内,并使用差分走线方式。
2. 关键外设配置解析
2.1 TIM1定时器与PWM生成
TIM1是这个方案的核心,负责产生SVPWM波形。配置时需要注意几个关键点:
- 时钟源选择内部72MHz时钟,通过TIM_InternalClockConfig()函数设置
- 死区时间需要根据功率器件特性计算,IGBT通常需要1.5-2us的死区
- 调试时可以启用DBGMCU的冻结功能,方便观察PWM波形
死区补偿是个容易踩坑的地方。我们项目最初用的死区时间是理论计算的1.5us,但实际测试发现电机换向时有轻微震荡。后来通过示波器抓取波形发现,IGBT的实际关断时间比规格书标称值长了约200ns。调整死区时间后问题解决,这也提醒我们器件参数一定要实测验证。
2.2 双ADC交替采样实现
三电阻方案需要精确的电流采样时机,我们采用双ADC交替触发的方式:
c复制// ADC1配置采样相电流A
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);
// ADC2配置采样相电流B
ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_7Cycles5);
// 使用TIM1更新事件触发ADC
TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
这里的关键是采样时刻必须位于PWM周期中点,通过TIM_CR2寄存器的CCPC位控制。我们在调试时发现,如果ADC触发时刻偏差超过200ns,电流波形就会出现明显畸变。解决方法是在ADC初始化后增加校准序列,并适当延长采样保持时间。
3. 控制算法实现细节
3.1 龙贝格观测器与PLL设计
龙贝格观测器相比传统滑模观测器最大的优势是运行更安静,特别是在低速区域。观测器的核心结构体定义如下:
c复制typedef struct {
int16_t ElectSpeed; // 电气转速(0.1RPM单位)
int16_t MechSpeed; // 机械转速(0.1RPM单位)
int16_t DeltaAngle; // 角度增量(0.01度单位)
int32_t Angle; // 累计角度(0.01度单位)
} OBSERVER_TypeDef;
观测器算法采用定点数运算实现,避免了浮点运算的开销。核心算法在mc_math.c文件中,主要包含反电动势估算和PLL跟踪两部分:
c复制// 反电动势估算
emf_alpha = Ibeta * Rs - Vbeta;
emf_beta = -Ialpha * Rs + Valpha;
// 龙贝格校正项
speed_correction = (emf_alpha * sin_theta - emf_beta * cos_theta) >> 15;
// PLL锁相环更新
est_speed += Kp * speed_correction;
est_angle += Ki * speed_correction + est_speed;
这里有个实用技巧:将PI系数放大2^15倍后用整数运算实现,既保证了精度又提高了运算效率。在实际调试中发现,观测器对电机参数特别是定子电阻Rs非常敏感,温度变化会导致估算误差增大。后来增加了在线参数辨识功能,通过注入高频信号实时更新Rs值,解决了这个问题。
3.2 SVPWM优化实现
SVPWM占空比计算是FOC算法的关键步骤之一,这里分享几个优化技巧:
- 扇区判断采用查表法替代复杂计算
- 将sqrt(3)近似为886/512,用移位运算代替浮点除法
- 作用时间计算采用预缩放系数,减少中间变量位数
具体实现代码如下:
c复制void SVM_CalcDutyCycles(int32_t Ualpha, int32_t Ubeta) {
// 扇区判断(简化版)
uint8_t sector = (Ualpha > 0) | ((Ubeta > 0) << 1);
// 矢量作用时间计算
int32_t T1 = (Ualpha * 886 - Ubeta * 1772) >> 12; // 886≈√3*512
int32_t T2 = (Ubeta * 1024) >> 12;
// 占空比标准化
PWM_DutyCycleA = (T1 + T2 + 2048) >> 2; // 映射到0-ARR范围
}
这种近似计算方法在STM32F103上实测可以节省约30%的计算时间,而谐波失真仅增加不到0.8%,对于大多数应用来说是完全可接受的折中方案。
4. 系统调试与优化
4.1 启动策略优化
斜坡启动是电机控制中最具挑战性的环节之一。我们最初实现的启动流程在空载时表现良好,但带载启动成功率只有60%左右。通过分析发现主要问题是观测器收敛速度不够,导致开环切换到闭环时容易失步。
改进后的启动流程增加了预定位和双斜坡控制:
c复制void Motor_Startup(void) {
// 阶段1:预定位
OpenLoop_Inject(30); // 注入30度固定角度1秒
Delay_ms(1000);
// 阶段2:开环加速
CurrentLoop_Enable();
while(1) {
Angle += Speed_ramp; // 角度斜坡
if(Speed_ramp < 1000)
Speed_ramp += 50; // 速度斜坡
if(Observer_Converged()) // 检测观测器收敛
break;
}
// 阶段3:闭环运行
CloseLoop_Enable();
}
这种三段式启动方案将带载启动成功率提升到了98%以上。其中预定位阶段确保转子初始位置已知,开环加速阶段采用角度和速度双斜坡控制,平稳过渡到闭环运行。
4.2 弱磁控制实现
弱磁控制是扩展电机转速范围的有效手段,但在实际实现中遇到了几个典型问题:
- 高速段电流调节器饱和
- 电压利用率达到极限后控制失稳
- 参数变化导致动态响应变差
我们的解决方案是引入前馈补偿和电压限制策略:
c复制void Flux_Weakening_Control(void) {
// 计算电压幅值
Vm = sqrt(Vd*Vd + Vq*Vq);
// 电压限制策略
if(Vm > Vmax) {
Vd = Vd * Vmax / Vm;
Vq = Vq * Vmax / Vm;
}
// 前馈补偿
Iq_ref += Kfw * (Vmax - Vm);
}
调试中发现,弱磁控制的参数需要根据电机特性仔细调整。特别是前馈系数Kfw,取值过大会导致电流振荡,过小则响应迟缓。建议先用阶跃响应测试确定大致范围,再通过扫频测试精细调节。
5. 实测性能与波形分析
经过几个月的调试优化,系统最终达到了令人满意的性能指标:
- 转速控制精度:±3RPM @1000RPM
- 电流THD:<5%(满载条件下)
- 启动时间:<2秒(带额定负载)
- 最高转速:额定转速的150%(弱磁区)
使用JScope捕获的典型波形显示,电流环响应时间小于100us,速度环带宽约50Hz。特别是在负载突变时,系统能快速恢复稳定,说明观测器和控制参数匹配良好。
调试过程中总结的几个实用技巧:
- 电流采样校准:在电机静止时注入已知电流,校准ADC偏移和增益
- 观测器调试:先用开环模式验证反电动势估算准确性
- 参数辨识:定期自动更新关键参数如定子电阻
- 保护策略:逐周期电流限制比软件保护更可靠
这个开源项目最值得称道的是它的代码注释,不仅解释了"怎么做",还说明了"为什么这么做"。比如在ADC配置部分的这段注释就避免了我们在硬件设计上走弯路:
c复制/* 重要提示:对于低边采样方案,ADC触发时刻应调整为:
t_trigger = (PWM_PERIOD + DEAD_TIME)/2
这样可以确保采样时刻避开开关噪声 */
这些来自实战的经验总结,远比数据手册上的理论描述更有价值。
