1. BLDC电机控制方案概述
玩过电机控制的工程师都知道,无刷直流电机(BLDC)的控制方案选择就像在走钢丝——有传感器方案稳如老狗,无传感器方案刺激如过山车。今天我要分享的这套基于STM32F1的双料控制方案,可以说是把两种方案的精华都榨出来了。
这套方案的核心价值在于:
- 有传感器模式采用霍尔元件定位,适合刚入门的工程师快速上手
- 无传感器模式基于反电动势过零点检测,适合追求极致性能和低成本的场景
- 双闭环PID控制算法实现速度和电流的精确调节
- 完整的代码实现和详尽的注释,可直接用于产品开发
我花了整整三个月时间调校这套系统,期间烧掉的MOS管足够开个电子元件博物馆了。现在就把这些血泪经验毫无保留地分享给大家,特别是那些在无传感控制中踩过的坑,都会一一说明。
2. 硬件架构设计解析
2.1 主控芯片选型
选择STM32F103C8T6作为主控芯片是经过深思熟虑的:
- 72MHz主频足够处理双闭环PID计算
- 内置3个定时器支持6路PWM输出
- 12位ADC满足电流采样精度要求
- 价格低廉(约10元/片)适合量产
提示:虽然F1系列已经有些年头,但其外设丰富度和性价比在电机控制领域依然能打。如果追求更高性能,可以考虑F4系列,但代码需要做相应调整。
2.2 功率驱动电路
电机驱动部分采用经典的三相全桥拓扑:
code复制上桥臂: Q1-Q3 (NMOS)
下桥臂: Q4-Q6 (NMOS)
驱动芯片: IR2104 (带自举电路)
关键参数计算:
- MOS管选型:根据电机额定电流(假设5A)和电压(24V),选择VDS≥50V、ID≥10A的型号(如IRLZ44N)
- 栅极电阻:10Ω(太小会导致开关损耗大,太大会延长开关时间)
- 自举电容:0.1uF/50V(保证上桥臂MOS管可靠导通)
2.3 电流检测设计
电流检测采用低边采样方案:
- 采样电阻:0.01Ω/3W(精度1%的锰铜电阻)
- 运放电路:INA282(增益50倍)
- ADC采样:STM32内置12位ADC,参考电压3.3V
电流计算公式:
code复制实际电流 = (ADC值 × 3.3/4096 - 1.65) / (0.01 × 50)
其中1.65V是运放的偏置电压,50是放大倍数。
3. 有传感器模式实现细节
3.1 霍尔信号处理
霍尔传感器的安装位置决定了换相时序。我们采用120°电角度布置的三个霍尔元件,其输出组合与转子位置的关系如下表:
| HallA | HallB | HallC | 电角度 | 导通相 |
|---|---|---|---|---|
| 1 | 0 | 1 | 0° | A+B- |
| 1 | 0 | 0 | 60° | A+C- |
| 1 | 1 | 0 | 120° | B+C- |
| 0 | 1 | 0 | 180° | B+A- |
| 0 | 1 | 1 | 240° | C+A- |
| 0 | 0 | 1 | 300° | C+B- |
代码中通过EXTI中断捕获霍尔信号变化:
c复制void HALL_IRQHandler(void){
static uint8_t last_hall = 0xFF;
uint8_t current_hall = (HALL3_GPIO->IDR & HALL3_Pin) ? 0x01 : 0 |
(HALL2_GPIO->IDR & HALL2_Pin) ? 0x02 : 0 |
(HALL1_GPIO->IDR & HALL1_Pin) ? 0x04 : 0;
if(current_hall != last_hall){
Commutation_Table_Update(current_hall);
last_hall = current_hall;
}
}
注意事项:霍尔信号容易受到干扰,建议在硬件上增加0.1uF电容滤波,软件上可以增加消抖处理(如连续两次检测到相同状态才确认变化)。
3.2 PWM调制策略
采用六步换相法(梯形波控制),每个电周期分为6个状态。PWM生成使用定时器的互补输出模式:
c复制void PWM_Init(void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 定时器时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
// 16kHz PWM频率
TIM_TimeBaseStructure.TIM_Period = 4500-1; // 72MHz/4500 = 16kHz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
3.3 双闭环PID控制
速度环和电流环采用级联控制结构:
code复制速度环输出 -> 电流环给定 -> PWM占空比
PID算法实现:
c复制typedef struct{
float Kp,Ki,Kd;
float Err_prev,Integral;
float OutputMax;
}PID_Handler;
float PID_Calculate(PID_Handler *h, float target, float feedback){
float err = target - feedback;
h->Integral += err * 0.001f; //1ms周期
float output = h->Kp * err
+ h->Ki * h->Integral
+ h->Kd * (err - h->Err_prev)/0.001f;
h->Err_prev = err;
return __MAX(-h->OutputMax, __MIN(output, h->OutputMax));
}
参数整定经验:
- 先调电流环:将Ki设为0,逐渐增大Kp直到响应快速但不过冲
- 然后加Ki:消除稳态误差,但不要引起振荡
- 速度环同理,但响应速度应比电流环慢5-10倍
- Kd一般设为Kp的1/10到1/5,用于抑制超调
4. 无传感器模式实现技巧
4.1 反电动势检测原理
在无传感器模式下,我们通过检测未通电相的反电动势(BEMF)过零点来估算转子位置。关键点在于:
- 只有未通电的相才能检测到真实的BEMF
- 过零点后延迟30°电角度进行换相
- 低速时BEMF幅值太小,需要特殊启动策略
4.2 软件实现细节
BEMF检测代码:
c复制void BEMF_Detection(){
static uint8_t zero_cross_count = 0;
float bemf = Get_PhaseVoltage() - (Vbus/2);
if((bemf > 50) || (bemf < -50)){ //噪声过滤
if(++zero_cross_count >= 3){
Motor.Phase += 1; //换相
zero_cross_count = 0;
}
}else{
zero_cross_count = 0;
}
}
避坑指南:那个50mV的阈值需要根据具体电机调整。太小会导致误触发,太大会延迟换相时机。建议用示波器观察BEMF波形,选择在过零点附近有明显变化的阈值。
4.3 启动策略
无传感器模式最难的是启动阶段,我的解决方案是:
- 预定位:给固定相通电1秒,将转子拉到已知位置
- 开环加速:按照固定换相时序逐渐提高PWM频率
- 切换闭环:当检测到可靠的BEMF信号后切换到闭环控制
启动代码框架:
c复制void Sensorless_Startup(){
// 1. 预定位
Set_Phase(0); // A+B-
delay_ms(1000);
// 2. 开环加速
for(int i=0; i<100; i++){
Set_Next_Phase();
delay_ms(10 - i*0.1); // 逐渐加快换相速度
if(Detect_BEMF()) break;
}
// 3. 切换闭环
while(1){
BEMF_Detection();
delay_ms(1);
}
}
5. 调试经验与问题排查
5.1 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机不转 | 霍尔接线错误 | 检查霍尔信号顺序,调整换相表 |
| 电机抖动 | PID参数不合适 | 重新整定PID,特别是减小Ki |
| 反转 | 相序错误 | 交换任意两相线 |
| 无传感模式失步 | BEMF阈值不当 | 用示波器调整检测阈值 |
| MOS管发热 | 死区时间不足 | 增加死区时间(建议500ns-1us) |
5.2 关键调试工具
- 示波器:观察PWM波形、BEMF信号
- 逻辑分析仪:捕获霍尔信号时序
- 电流探头:检查相电流波形
- 串口调试:实时调整PID参数
5.3 血泪教训
- 一定要加死区时间!我曾经因为死区时间不足,半小时烧了6个MOS管。
- 无传感模式启动时,负载不能太大。最好先空载启动,再加负载。
- 电流采样电阻的功率要足够,建议选择3W以上的型号。
- PCB布局时,大电流走线要足够宽,避免压降过大。
这套方案经过多个项目的验证,从电动工具到无人机电调都有成功应用。最后分享一个调参小技巧:当电机发出"悦耳"的运转声音时,通常说明PID参数调得不错;如果声音刺耳或有节奏性的抖动,就需要继续优化参数了。