1. 项目概述:打造一台会思考的平衡小车
去年冬天,我在工作室里折腾出了这台会自主避障的平衡小车。当它第一次稳稳立在桌面上,随着我的手势灵活转向时,那种成就感比拿到任何现成玩具都要强烈。这个基于STM32的自平衡平台不仅具备基础平衡能力,还整合了超声波避障、蓝牙遥控等实用功能,更预留了丰富的扩展接口。下面我就把整个开发过程中的核心要点和踩过的坑完整分享给大家。
2. 硬件架构设计解析
2.1 核心控制器选型
选择STM32F103C6T6作为主控是经过多维度考量的结果:
- 72MHz主频足够处理PID平衡算法(实测单次PID计算仅需12μs)
- 内置的定时器资源完美匹配PWM电机控制需求
- SWD调试接口相比JTAG节省引脚资源
- 价格控制在20元以内,性价比极高
注意:C6T6的64KB Flash可能略显紧张,若需添加复杂功能建议选择C8T6(128KB)
2.2 姿态传感方案对比
测试过三种方案后最终选定MPU6050:
- 模拟陀螺仪+加速度计:成本低但需自行融合算法
- MPU6050:集成DMP引擎,直接输出欧拉角(代码量减少70%)
- BMI160:精度更高但价格翻倍
c复制// MPU6050初始化关键代码
I2C_WriteByte(MPU6050_ADDR, PWR_MGMT_1, 0x01); // 唤醒设备
I2C_WriteByte(MPU6050_ADDR, SMPLRT_DIV, 0x07); // 采样率1kHz
I2C_WriteByte(MPU6050_ADDR, CONFIG, 0x06); // 低通滤波42Hz
I2C_WriteByte(MPU6050_ADDR, GYRO_CONFIG, 0x18); // 量程±2000°/s
I2C_WriteByte(MPU6050_ADDR, ACCEL_CONFIG, 0x18);// 量程±16g
2.3 动力系统设计
采用DRV8833驱动双路减速电机(减速比1:48),实测数据:
| 参数 | 空载值 | 带载值 |
|---|---|---|
| 转速 | 200RPM | 160RPM |
| 堵转扭矩 | - | 2.5kg·cm |
| 工作电流 | 80mA | 350mA |
| 峰值电流 | - | 1.2A |
电池选用两节14500锂电池(7.4V/1200mAh),实测续航:
- 平衡模式:约2.5小时
- 运动模式:约1.8小时
3. 核心算法实现
3.1 平衡控制PID实现
采用串级PID结构:
- 外环:角度环(输入:期望角度 vs MPU6050实测角度)
- 内环:速度环(输入:电机编码器脉冲计数)
c复制// 角度环PID计算示例
float Angle_PID(float target, float current) {
static float errSum = 0, lastErr = 0;
float err = target - current;
errSum += err;
errSum = constrain(errSum, -500, 500); // 抗积分饱和
float output = KP_ANGLE * err
+ KI_ANGLE * errSum
+ KD_ANGLE * (err - lastErr);
lastErr = err;
return output;
}
参数整定经验:
- 先调KP直到出现小幅振荡(约12.0)
- 加入KD抑制振荡(约0.25)
- 最后微调KI改善稳态误差(约0.05)
3.2 超声波避障算法优化
传统方案直接使用距离阈值,改进为动态响应策略:
c复制void Avoidance_Control() {
float dist = GetUltrasonicDistance();
if(dist < 15.0) { // 紧急制动
Motor_Stop();
}
else if(dist < 30.0) { // 减速+转向
Motor_SetSpeed(50, -50);
}
else if(dist < 50.0) { // 降速通过
Motor_SetSpeed(80, 80);
}
}
实测发现添加10ms的移动平均滤波后,误触发率从15%降至3%以下。
4. 蓝牙控制实现细节
4.1 通信协议设计
自定义轻量级协议(帧格式):
code复制[HEAD][LEN][CMD][DATA][CRC]
0x55 0x04 0x01 0x00 0xXX
支持指令集:
| 指令码 | 功能 | 数据范围 |
|---|---|---|
| 0x01 | 前进/后退 | -100~+100 |
| 0x02 | 转向控制 | -100~+100 |
| 0x03 | 模式切换 | 0-2 |
| 0x04 | PID参数调整 | 见附表 |
4.2 Android APP开发要点
使用Android Studio开发控制端,关键实现:
java复制// 蓝牙连接核心代码
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
// 发送成功回调
}
};
避坑指南:部分手机需要手动开启GPS才能正常扫描BLE设备,这是Android系统限制
5. 扩展功能实现
5.1 循迹模块集成
采用TCRT5000红外传感器阵列,安装位置建议:
- 距地面1.5-2cm
- 传感器间距2cm(适应常见黑线宽度)
循迹算法采用加权算法:
c复制int line_pos = (s1*10 + s2*5 + s3*0 + s4*(-5) + s5*(-10)) / (s1+s2+s3+s4+s5);
Motor_SetSpeed(100 - line_pos, 100 + line_pos);
5.2 OLED状态显示
SSD1306显示屏实时显示:
- 电池电压(带低压报警)
- 当前控制模式
- PID参数
- 超声波距离
优化技巧:使用双缓冲机制避免闪烁
c复制void OLED_Refresh() {
OLED_FillBuffer(); // 在缓存区绘制
OLED_Display(); // 整页刷新
}
6. 常见问题解决方案
6.1 电机异常抖动
可能原因及对策:
- 电源不足:测量电池电压,负载时不应低于6.5V
- PID参数过激:适当降低KP或增加KD
- 机械共振:在电机支架添加橡胶垫片
6.2 蓝牙连接不稳定
排查步骤:
- 检查天线位置(应远离电机)
- 修改通信间隔(建议20ms以上)
- 添加数据重传机制
6.3 平衡角度漂移
校准方法:
- 水平放置小车,记录MPU6050零偏值
- 在代码中添加自动校准例程
c复制void Calibrate_Gyro() {
float sum = 0;
for(int i=0; i<500; i++) {
sum += MPU6050_GetGyroZ();
delay(2);
}
gyro_offset = sum / 500;
}
7. 进阶改造建议
- 增加IMU冗余设计:添加BMI160与MPU6050数据融合
- 改用FOC驱动:提升电机控制精度(需换用STM32F4)
- 移植RTOS:方便多任务管理(推荐FreeRTOS)
- 视觉扩展:搭配OpenMV实现颜色识别
这个项目最让我惊喜的是它的扩展性——通过预留的IO口,可以不断添加新功能。最近我正在尝试加上WiFi摄像头实现第一视角遥控,等调试稳定了再和大家分享具体实现方案。
