1. STM32平衡车项目概述
平衡车作为典型的嵌入式控制应用,一直是电子工程师和创客们热衷的实践项目。基于STM32的平衡车实现,不仅考验开发者对微控制器外设的掌握程度,更是PID控制算法应用的绝佳案例。我最近完成了一个完整的STM32F103C8T6核心的平衡车项目,从硬件选型到软件调试历时两个月,期间踩过不少坑也积累了些实用经验。
这个项目本质上是通过STM32实时采集MPU6050传感器的姿态数据,经过PID运算后控制电机转速,实现车体自主平衡。相比Arduino方案,STM32凭借其Cortex-M3内核的强大性能和丰富外设,能够实现更精确的控制环路和更快的响应速度。市面上常见的平衡车商业产品如九号、小米等,其核心控制原理与我们这个DIY项目是相通的。
2. 硬件系统设计与选型
2.1 主控芯片选择
我选用STM32F103C8T6(俗称"蓝莓板")作为主控,主要基于三点考虑:
- 72MHz主频足够处理平衡控制算法
- 内置的定时器支持PWM生成和编码器接口模式
- 丰富的外设接口(I2C、SPI、USART等)便于扩展
注意:当前ST芯片供货紧张时,可考虑国产替代如GD32或APM32。实测APM32F103在平衡车项目上能直接兼容,只需重新编译代码。
2.2 关键传感器选型
MPU6050是平衡车的核心传感器,它集成了3轴加速度计和3轴陀螺仪,通过I2C接口与STM32通信。实际使用中需要注意:
- 采样率建议设置为500Hz以上
- 必须进行传感器校准(静态校准和动态校准)
- DMP(数字运动处理器)功能可减轻MCU负担
c复制// MPU6050初始化示例代码
void MPU6050_Init(void)
{
I2C_WriteByte(MPU6050_ADDR, PWR_MGMT_1, 0x00); // 解除休眠
I2C_WriteByte(MPU6050_ADDR, SMPLRT_DIV, 0x07); // 采样率125Hz
I2C_WriteByte(MPU6050_ADDR, CONFIG, 0x06); // 低通滤波
I2C_WriteByte(MPU6050_ADDR, GYRO_CONFIG, 0x18); // ±2000°/s量程
I2C_WriteByte(MPU6050_ADDR, ACCEL_CONFIG, 0x18); // ±16g量程
}
2.3 电机驱动方案
TB6612FNG是性价比很高的双路电机驱动芯片,相比L298N具有:
- 更低的工作电压(2.5-13.5V)
- 更高的效率(MOSFET驱动)
- 内置过热保护电路
接线时需注意PWM频率选择,建议使用定时器产生10kHz左右的PWM信号。频率过低会导致电机啸叫,过高则增加开关损耗。
3. 软件架构设计
3.1 控制系统框架
整个软件系统采用前后台架构:
- 后台:FreeRTOS实时操作系统管理任务调度
- 前台:中断服务程序处理关键时序
任务划分如下:
- 任务1(优先级最高):MPU6050数据读取与姿态解算(100Hz)
- 任务2:PID计算与电机控制(100Hz)
- 任务3:遥控指令处理(20Hz)
- 任务4:状态监测与保护(10Hz)
3.2 姿态解算实现
采用互补滤波融合加速度计和陀螺仪数据:
- 加速度计测量重力方向,长期稳定但短期噪声大
- 陀螺仪测量角速度,短期精确但存在漂移
c复制float ComplementaryFilter(float accelAngle, float gyroRate, float dt)
{
static float angle = 0;
float alpha = 0.98; // 滤波系数
angle = alpha * (angle + gyroRate * dt) + (1-alpha) * accelAngle;
return angle;
}
3.3 PID控制算法
平衡车需要三环PID控制:
- 角度环(最内环):控制车体直立
- 速度环:防止长时间偏移
- 转向环:处理遥控指令
角度环PID参数整定步骤:
- 先设Ki=Kd=0,增大Kp至车体能勉强站稳
- 增加Kd抑制振荡
- 最后加少量Ki消除静差
实测经验:Kp=40, Ki=0.5, Kd=0.8 是个不错的起点值
4. 关键问题与解决方案
4.1 电机响应不一致
现象:车体总是偏向一侧
解决方法:
- 对电机进行空载测试,记录PWM-转速曲线
- 在代码中加入补偿系数:
c复制void Motor_Output(int left, int right)
{
static const float L_COMP = 1.05f; // 左电机补偿系数
static const float R_COMP = 0.98f; // 右电机补偿系数
TIM_SetCompare1(TIMx, (int)(left * L_COMP));
TIM_SetCompare2(TIMx, (int)(right * R_COMP));
}
4.2 电源干扰问题
现象:运行时MPU6050数据异常跳动
解决方案:
- 电机电源与MCU电源完全隔离
- 在MPU6050的VCC引脚加10μF钽电容
- I2C线上拉电阻改为2.2kΩ(标准4.7kΩ可能偏大)
4.3 死区效应处理
当PWM占空比太小时电机无法启动,需要设置死区:
c复制#define DEAD_ZONE 15 // 死区值
int Apply_DeadZone(int input)
{
if(input > 0) return input + DEAD_ZONE;
if(input < 0) return input - DEAD_ZONE;
return 0;
}
5. 系统优化技巧
5.1 降低控制延迟
- 使用DMA传输MPU6050数据
- 将PID计算放在定时器中断中
- 开启STM32的硬件FPU加速浮点运算
5.2 提高代码效率
- 将常用变量定义为register类型
- 使用查表法代替复杂三角函数计算
- 开启编译器优化选项-O2
5.3 增加安全保护
- 软件看门狗监控系统运行
- 跌落检测(持续0.5s角度大于45度时切断电机)
- 电压监测(低于10.5V时逐渐降速)
6. 进阶扩展方向
6.1 手机蓝牙控制
通过HC-05模块实现:
- 修改Android端APP发送控制指令
- STM32解析协议帧(建议使用自定义简单协议)
- 增加控制模式切换功能
6.2 姿态数据记录
利用STM32内置Flash:
- 划分专用存储区域
- 设计环形缓冲区结构
- 通过USB虚拟串口导出数据
6.3 机器学习优化
采集优秀控制数据:
- 记录人工操控时的传感器数据和电机输出
- 使用Python进行LSTM网络训练
- 部署轻量级模型到STM32(需使用CMSIS-NN库)
这个项目最让我意外的是,原本以为最难的部分是PID调参,实际发现硬件层面的问题(如电源噪声、机械结构松动)往往更棘手。建议大家在开始编程前,先确保所有硬件连接可靠,供电充足稳定。另外,平衡车的机械重心设计非常关键,电池位置高低几厘米的差异,可能就需要完全重调PID参数。
