1. 项目概述
这个自平衡小车项目是我在嵌入式开发领域的一次实战探索,核心目标是利用STM32微控制器实现两轮小车的自主平衡控制。项目涉及硬件选型、传感器数据处理、控制算法实现等多个技术环节,特别适合想要深入理解嵌入式实时控制系统的开发者参考。
在实际开发过程中,我选择了STM32F103C8T6作为主控芯片,搭配MPU6050六轴传感器获取姿态数据,并通过0.96寸OLED显示屏实时显示系统状态。整个项目基于STM32 HAL库开发,充分利用了MPU6050内置的DMP(Digital Motion Processor)进行姿态解算,大幅降低了主控芯片的计算负担。
2. 硬件设计与选型
2.1 主控芯片选择
STM32F103C8T6(俗称"蓝莓派")是这个项目的核心大脑。选择这款芯片主要基于以下几点考虑:
- 72MHz主频的Cortex-M3内核,性能足够处理实时控制任务
- 丰富的定时器资源(PWM生成必备)
- 充足的GPIO接口(需要同时控制电机、读取传感器、驱动显示屏)
- 成本优势(相比F4系列更具性价比)
提示:初学者常犯的错误是直接选用最高配的芯片,实际上对于平衡小车这种项目,F103系列已经完全够用,还能节省成本。
2.2 姿态传感器选型
MPU6050是项目的关键传感器,它集成了三轴加速度计和三轴陀螺仪,通过I2C接口与主控通信。选择它而不是更简单的加速度计主要因为:
- 内置DMP可以硬件解算姿态角,减轻主控负担
- 成本低廉(约10元人民币)
- 社区支持完善(有大量现成驱动代码)
实测中发现,MPU6050的原始数据噪声较大,必须配合DMP使用才能获得稳定的姿态数据。直接使用原始数据做软件解算需要复杂的卡尔曼滤波算法,对初学者门槛较高。
2.3 电机与驱动电路
我选用的是N20减速电机配TB6612驱动芯片的方案:
- N20电机:6V/200转,体积小扭矩适中
- TB6612:双路H桥驱动,支持PWM调速
- 电机需配合编码器使用(我选用的是AB相增量式编码器)
电机选型时需要注意:
- 扭矩要足够(至少能快速响应姿态变化)
- 转速不宜过高(200-300转/分为宜)
- 驱动芯片的持续电流要大于电机堵转电流
3. 软件架构设计
3.1 系统任务划分
整个软件系统采用前后台架构,主要分为以下几个任务:
- 传感器数据采集(MPU6050 via I2C)
- 姿态解算(DMP输出欧拉角)
- 平衡控制(PID算法)
- 电机控制(PWM输出)
- 状态显示(OLED刷新)
其中,姿态解算和控制算法对实时性要求最高,需要保证稳定的执行周期。我的方案是:
- 使用定时器中断触发控制循环(2ms周期)
- 在中断服务程序中完成数据读取和PID计算
- 主循环处理显示更新和其他非实时任务
3.2 HAL库配置要点
使用STM32CubeMX生成基础代码时需要注意:
- I2C接口要配置为Fast Mode(400kHz)
- 定时器要配置为PWM输出模式(电机控制)
- 编码器接口要配置为Encoder Mode
- 系统时钟要正确配置(外部晶振8MHz)
常见问题排查:
- I2C通信失败:检查上拉电阻(4.7kΩ)
- PWM无输出:确认TIM通道映射正确
- 编码器读数异常:检查AB相接线是否反接
4. MPU6050与DMP库集成
4.1 DMP初始化流程
DMP是MPU6050内置的运动处理器,可以硬件解算四元数和欧拉角。初始化步骤如下:
- 复位MPU6050
- 加载DMP固件(需提供预编译的二进制文件)
- 配置DMP参数(量程、输出速率等)
- 使能DMP功能
- 设置FIFO和中断
关键代码片段:
c复制// 加载DMP固件
mpu_load_firmware(DMP_CODE_SIZE, dmp_memory);
// 设置DMP输出速率
mpu_set_sample_rate(DEFAULT_MPU_HZ);
// 使能DMP
mpu_set_dmp_state(1);
4.2 姿态数据获取
启用DMP后,姿态数据会通过FIFO输出。典型的数据读取流程:
- 检查FIFO计数
- 读取FIFO数据包
- 解析四元数/欧拉角
- 转换为需要的姿态角度
实测中发现,DMP输出的俯仰角(pitch)在±90度范围内非常稳定,完全满足平衡小车需求。相比软件解算,DMP方案具有以下优势:
- 计算延迟低(硬件加速)
- 稳定性好(内置校准算法)
- 不占用主控资源
5. PID控制算法实现
5.1 平衡控制原理
自平衡小车的核心是通过PID算法控制电机转速,使车身保持直立。控制流程如下:
- 获取当前俯仰角(来自DMP)
- 计算角度偏差(目标角度 - 当前角度)
- 通过PID计算输出量
- 转换为电机PWM占空比
我采用了位置式PID算法,公式为:
code复制输出 = Kp×误差 + Ki×积分 + Kd×微分
5.2 参数整定经验
PID参数整定是项目中最具挑战性的部分。经过多次试验,我总结出以下经验:
- 先调Kp:从小到大增加,直到小车能对倾斜做出明显反应
- 再调Kd:抑制振荡,使小车能稳定在平衡点附近
- Ki要谨慎:积分项容易导致超调,初始可设为0
- 最终参数:Kp=25, Ki=0.5, Kd=0.8(仅供参考)
调试技巧:
- 先用蓝牙模块实时监控角度和输出
- 用手轻轻扶住小车观察响应
- 每次只调整一个参数
- 改变参数幅度要小(10%-20%)
6. OLED显示实现
6.1 显示内容设计
0.96寸OLED主要显示以下信息:
- 实时俯仰角(数值+进度条)
- PID输出量
- 电机PWM占空比
- 系统状态(正常/校准/错误)
使用SSD1306驱动芯片,通过I2C接口通信。为提高刷新效率,我采用了部分刷新策略:
- 静态内容(如标题)只初始化时绘制一次
- 动态数据(如角度值)局部更新
- 使用双缓冲机制避免闪烁
6.2 显示优化技巧
- 字体选择:使用8x16像素字体保证可读性
- 图形绘制:利用内置的绘图指令画进度条
- 刷新控制:限制刷新率(20-30Hz足够)
- 错误处理:添加超时检测避免I2C死锁
关键代码:
c复制// 更新角度显示
void update_angle_display(float angle) {
char buf[16];
sprintf(buf, "Pitch:%4.1f", angle);
OLED_ShowString(0, 2, buf, 16);
// 绘制进度条
int bar_len = map(angle, -30, 30, 0, 128);
OLED_DrawRectangle(0, 4, bar_len, 6, 1);
}
7. 系统调试与优化
7.1 常见问题排查
在开发过程中遇到的一些典型问题及解决方案:
-
小车剧烈振荡:
- 检查MPU6050安装是否牢固
- 降低Kp增益
- 增加Kd阻尼
-
向一侧持续偏移:
- 检查电机供电是否对称
- 确认MPU6050已校准
- 调整机械重心位置
-
DMP数据异常:
- 重新加载DMP固件
- 检查I2C线路干扰
- 降低I2C通信速率
7.2 性能优化技巧
-
中断优先级配置:
- 控制中断 > 传感器中断 > 显示刷新
- 避免在中断中进行复杂计算
-
电源管理:
- 电机电源与MCU电源隔离
- 添加大容量滤波电容
- 电池电压监测
-
代码优化:
- 使用查表法替代浮点运算
- 关键函数添加inline修饰
- 启用编译优化-O2
8. 项目扩展方向
这个基础版本完成后,还可以考虑以下扩展:
- 增加无线控制(蓝牙/NRF24L01)
- 实现路径跟踪(添加红外/超声波传感器)
- 开发手机APP监控界面
- 升级为四旋翼平衡平台
- 添加跌倒检测与自动保护
我在实际调试中发现,机械结构的稳定性对系统性能影响极大。建议先用3D打印或激光切割制作一个刚性好的底盘,避免因结构变形导致控制失效。另外,电池的选用也很关键,推荐使用动力锂电池(如18650)确保大电流放电能力。