1. 项目概述
作为一名嵌入式开发工程师,最近我完成了一个基于STM32的平衡车项目。这个项目不仅让我深入理解了PID控制算法和传感器数据融合,还让我对实时控制系统有了更深刻的认识。平衡车作为经典的嵌入式控制案例,涵盖了电机驱动、姿态感知、算法控制等多个关键技术点,是检验嵌入式开发能力的绝佳项目。
我使用的硬件平台是STM32F103C8T6最小系统板,搭配MPU6050六轴传感器模块和直流减速电机。整个开发过程历时三周,期间遇到了不少坑,也积累了很多实战经验。下面我就把这个项目的完整开发过程分享给大家,希望能帮助到正在学习STM32和自动控制的同学。
2. 硬件设计与搭建
2.1 核心硬件选型
在选择硬件时,我主要考虑了以下几个因素:
-
主控芯片:STM32F103C8T6
- 72MHz主频,足够处理传感器数据和运行控制算法
- 丰富的外设接口(PWM、定时器、USART等)
- 价格便宜,开发资源丰富
-
姿态传感器:MPU6050
- 集成3轴加速度计和3轴陀螺仪
- I2C接口,方便与STM32通信
- 内置DMP(数字运动处理器),可减轻主控负担
-
电机驱动:TB6612FNG
- 双H桥设计,可同时驱动两个电机
- 最大1.2A持续电流输出
- 内置过热保护和低压检测
-
电机:N20减速电机
- 6V电压,200RPM转速
- 内置编码器,可反馈转速信息
- 体积小巧,适合平衡车使用
2.2 电路设计要点
平衡车的电路设计有几个关键点需要特别注意:
-
电源管理:
- 使用18650锂电池供电(7.4V)
- 通过AMS1117-5.0稳压到5V给STM32供电
- 电机直接使用电池电压驱动
-
传感器布局:
- MPU6050应尽量靠近车体中心位置
- 安装方向要与车体坐标系一致
- 避免振动干扰,必要时加装减震垫
-
电机驱动电路:
- PWM频率建议在10-20kHz之间
- 电机两端需要并联续流二极管
- 电源输入端加装大容量滤波电容
提示:在焊接电路时,电机驱动部分要特别注意走线宽度,大电流路径至少要有2mm线宽,避免因电阻过大导致发热。
3. 软件架构设计
3.1 系统框架
平衡车的软件系统采用前后台架构:
-
前台系统:
- 1ms定时器中断:用于姿态解算和控制算法
- 外部中断:处理编码器信号
-
后台主循环:
- 传感器数据读取(MPU6050)
- 用户界面更新(OLED显示)
- 通信处理(蓝牙、NRF24L01)
c复制// 系统主循环示例
while(1) {
MPU6050_GetData(&mpu_data); // 读取传感器数据
OLED_ShowAngle(angle); // 显示当前角度
Bluetooth_Process(); // 处理蓝牙数据
NRF24L01_Process(); // 处理无线数据
}
3.2 关键模块驱动
3.2.1 MPU6050驱动
MPU6050通过I2C接口与STM32通信,需要实现以下功能:
-
初始化配置:
- 设置采样率(通常500Hz)
- 配置加速度计和陀螺仪量程
- 使能传感器
-
数据读取:
- 读取原始加速度和角速度数据
- 转换为实际物理量(g和°/s)
c复制// MPU6050初始化示例
void MPU6050_Init(void) {
I2C_WriteByte(MPU6050_ADDR, MPU6050_RA_PWR_MGMT_1, 0x00); // 解除休眠
I2C_WriteByte(MPU6050_ADDR, MPU6050_RA_SMPLRT_DIV, 0x07); // 采样率1kHz/(7+1)=125Hz
I2C_WriteByte(MPU6050_ADDR, MPU6050_RA_CONFIG, 0x06); // 低通滤波42Hz
I2C_WriteByte(MPU6050_ADDR, MPU6050_RA_GYRO_CONFIG, 0x18); // 陀螺仪±2000°/s
I2C_WriteByte(MPU6050_ADDR, MPU6050_RA_ACCEL_CONFIG, 0x18); // 加速度计±16g
}
3.2.2 电机驱动
电机驱动主要涉及PWM生成和方向控制:
-
PWM配置:
- 使用定时器的PWM模式
- 频率设置为10kHz
- 占空比0-100%可调
-
方向控制:
- 通过GPIO控制H桥的IN1/IN2引脚
- 实现正转、反转和刹车功能
c复制// 电机控制函数示例
void Motor_SetSpeed(int16_t speed) {
if(speed >= 0) {
GPIO_SetBits(MOTOR_IN1_PORT, MOTOR_IN1_PIN);
GPIO_ResetBits(MOTOR_IN2_PORT, MOTOR_IN2_PIN);
} else {
GPIO_ResetBits(MOTOR_IN1_PORT, MOTOR_IN1_PIN);
GPIO_SetBits(MOTOR_IN2_PORT, MOTOR_IN2_PIN);
speed = -speed;
}
TIM_SetCompare1(TIM2, speed); // 设置PWM占空比
}
4. 姿态解算算法
4.1 传感器数据处理
MPU6050输出的原始数据需要经过校准和转换:
-
加速度计数据:
- 去除零点偏移
- 转换为g单位
- 计算倾角:θ = atan2(ay, az)
-
陀螺仪数据:
- 去除零点偏移
- 转换为°/s单位
- 积分得到角度变化量
c复制// 加速度计计算角度示例
float Accel_GetAngle(float ax, float ay, float az) {
return atan2(ay, az) * 180.0 / PI; // 转换为角度
}
// 陀螺仪积分角度示例
float Gyro_GetAngle(float gx, float dt) {
static float angle = 0;
angle += gx * dt; // 积分得到角度
return angle;
}
4.2 互补滤波算法
单独使用加速度计或陀螺仪都有明显缺陷,因此需要采用互补滤波:
-
加速度计:
- 长期稳定,但动态响应差
- 高频噪声大
-
陀螺仪:
- 短期精确,但存在积分漂移
- 低频特性差
互补滤波公式:
code复制angle = α*(angle_gyro + gyro*dt) + (1-α)*angle_accel
其中α通常取0.98左右
c复制// 互补滤波实现示例
float Complementary_Filter(float acc_angle, float gyro_rate, float dt) {
static float angle = 0;
float alpha = 0.98;
angle = alpha * (angle + gyro_rate * dt) + (1 - alpha) * acc_angle;
return angle;
}
5. PID控制实现
5.1 PID控制原理
平衡车需要两个PID控制器:
-
直立环(角度环):
- 控制车体保持直立
- 输入:角度偏差
- 输出:电机速度
-
速度环:
- 维持车体位置
- 输入:速度偏差
- 输出:角度设定值
5.2 PID参数整定
参数整定遵循以下顺序:
-
先调直立环(Kp):
- 从小到大逐步增加
- 直到车体能够勉强站立
-
再调速度环(Kp、Ki):
- 加入速度控制
- 调整使车体不会快速移动
-
最后微调:
- 观察响应速度
- 调整阻尼特性
c复制// PID控制器实现
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
} PID_Controller;
float PID_Calculate(PID_Controller* pid, float error, float dt) {
float p = pid->Kp * error;
pid->integral += error * dt;
float i = pid->Ki * pid->integral;
float d = pid->Kd * (error - pid->prev_error) / dt;
pid->prev_error = error;
return p + i + d;
}
6. 系统调试与优化
6.1 常见问题排查
在调试过程中,我遇到了以下典型问题:
-
车体抖动严重:
- 原因:PID参数过于激进
- 解决:降低Kp,增加Kd
-
车体缓慢倒下:
- 原因:直立环Kp不足
- 解决:适当增加Kp
-
车体快速移动:
- 原因:速度环积分过大
- 解决:限制积分项或降低Ki
6.2 性能优化技巧
-
定时器中断优化:
- 将耗时操作移到主循环
- 中断中只做必要计算
-
传感器数据处理:
- 使用DMA读取传感器数据
- 采用滑动平均滤波
-
控制算法优化:
- 使用增量式PID
- 加入抗积分饱和措施
c复制// 带抗积分饱和的PID实现
float PID_Calculate_AntiWindup(PID_Controller* pid, float error, float dt, float output_limit) {
float p = pid->Kp * error;
// 积分项限幅
float i_term = pid->Ki * (pid->integral + error * dt);
if(fabs(i_term) > output_limit) {
i_term = (i_term > 0) ? output_limit : -output_limit;
pid->integral = (i_term - p) / pid->Ki;
} else {
pid->integral += error * dt;
}
float d = pid->Kd * (error - pid->prev_error) / dt;
pid->prev_error = error;
return p + i_term + d;
}
7. 项目扩展与改进
在完成基础功能后,可以考虑以下扩展方向:
-
无线遥控功能:
- 通过蓝牙或NRF24L01模块
- 实现前进、后退、转向控制
-
手机APP监控:
- 开发Android应用
- 实时显示姿态数据和PID参数
-
自动平衡模式:
- 加入位置闭环
- 车体自动回到原点
-
运动控制算法:
- 实现定圆运动
- 加入路径规划功能
经过这个项目的实践,我对STM32的外设使用、实时控制系统设计和PID算法应用都有了更深入的理解。平衡车虽然看起来简单,但要真正做到稳定可靠,需要反复调试和优化。最重要的经验是:参数调整要循序渐进,每次只调整一个参数,并仔细观察系统响应。