1. 项目概述:两轮自平衡小车的核心挑战
两轮自平衡小车是嵌入式系统开发中的经典项目,它完美融合了传感器技术、控制算法和机电一体化设计。这类小车的核心难点在于如何通过STM32单片机实时处理陀螺仪数据,并驱动电机实现动态平衡。与传统的四轮小车不同,两轮结构本质上是一个不稳定系统,需要通过持续的姿态调整才能保持直立状态。
我在去年帮学生调试这类项目时,发现最关键的三个技术点是:传感器数据融合的准确性、PID控制参数的整定效果,以及电机驱动的响应速度。其中任何一环出现问题,都会导致小车出现剧烈振荡或者直接倾倒。下面这张示意图展示了我们最终实现的硬件布局方案:

2. 硬件系统设计详解
2.1 核心控制器选型
经过多次对比测试,我们最终选择了STM32F103RCT6作为主控芯片,主要原因有三:
- 充足的定时器资源(8个)可同时处理编码器输入和PWM输出
- 内置的硬件I2C接口能稳定读取MPU6050数据
- 72MHz主频足以满足5ms控制周期的计算需求
注意:初学者常犯的错误是直接使用STM32CubeMX生成的I2C代码,这可能导致MPU6050读取不稳定。建议采用IO模拟I2C的方式,并加入超时重试机制。
2.2 姿态传感器配置
MPU6050作为核心传感器,其安装位置直接影响测量精度:
- 应尽量靠近小车重心位置
- 必须与车体平面保持平行
- 建议使用橡胶减震垫减少电机振动干扰
传感器初始化时需要特别注意以下参数配置:
c复制// MPU6050初始化代码片段
I2C_WriteByte(MPU6050_ADDR, PWR_MGMT_1, 0x00); // 解除休眠
I2C_WriteByte(MPU6050_ADDR, SMPLRT_DIV, 0x07); // 采样率1kHz
I2C_WriteByte(MPU6050_ADDR, CONFIG, 0x06); // 低通滤波5Hz
I2C_WriteByte(MPU6050_ADDR, GYRO_CONFIG, 0x18); // 陀螺仪±2000dps
I2C_WriteByte(MPU6050_ADDR, ACCEL_CONFIG, 0x01);// 加速度计±4g
2.3 电机驱动电路设计
我们采用TB6612FNG驱动模块配合N20减速电机,相比传统的L298N方案具有明显优势:
| 参数 | TB6612FNG | L298N |
|---|---|---|
| 效率 | 90% | 60% |
| 待机电流 | 0μA | 6mA |
| PWM频率上限 | 100kHz | 20kHz |
| 体积 | 15x10mm | 43x43mm |
电机安装时要注意:
- 车轮与电机轴必须同轴,偏心会导致周期性振动
- 编码器线应使用屏蔽线防止干扰
- 电机供电与单片机供电需分开走线
3. 软件算法实现
3.1 传感器数据融合
原始传感器数据需要经过多重处理:
- 硬件滤波:MPU6050内置的低通滤波(配置为5Hz)
- 软件滤波:采用滑动平均滤波消除尖峰干扰
- 姿态解算:互补滤波融合加速度计和陀螺仪数据
互补滤波的核心代码逻辑:
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.2 PID控制算法实现
我们采用串级PID控制结构:
- 外环:角度环(P控制)
- 内环:速度环(PI控制)
PID参数整定经验:
- 先调角度环P,使小车能勉强站立但会缓慢移动
- 再调速度环P,抑制小车的移动趋势
- 最后加入速度环I,消除静差
典型参数范围参考:
c复制typedef struct {
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float integral; // 积分项
float prevError; // 上次误差
} PID;
// 角度环参数
PID anglePID = {12.0, 0, 0.5, 0, 0};
// 速度环参数
PID speedPID = {80.0, 0.2, 0, 0, 0};
3.3 防撞保护机制
通过红外测距模块(VL53L0X)实现三级防护:
- 预警距离(30cm):蜂鸣器短鸣
- 减速距离(20cm):降低目标速度
- 制动距离(10cm):立即停止电机
防撞状态机实现逻辑:
c复制typedef enum {
SAFE,
WARNING,
SLOW_DOWN,
EMERGENCY_STOP
} CollisionState;
void CollisionHandler(float distance) {
static CollisionState state = SAFE;
if(distance < 10.0) {
state = EMERGENCY_STOP;
MotorStop();
}
else if(distance < 20.0) {
state = SLOW_DOWN;
SetTargetSpeed(0.3 * normalSpeed);
}
else if(distance < 30.0) {
state = WARNING;
Beep(100);
}
else {
state = SAFE;
}
}
4. 移动端APP设计
4.1 蓝牙通信协议
采用自定义的轻量级协议格式:
code复制[HEAD][LEN][CMD][DATA][CHECKSUM]
- HEAD:固定为0xAA
- LEN:数据长度
- CMD:指令类型
- DATA:有效载荷
- CHECKSUM:异或校验
典型指令示例:
python复制# 设置目标速度指令
def build_speed_cmd(speed):
cmd = 0x02
data = struct.pack('>h', int(speed*1000))
checksum = cmd ^ data[0] ^ data[1]
return bytes([0xAA, 0x03, cmd]) + data + bytes([checksum])
4.2 Android端关键实现
使用Android Studio开发,主要功能模块:
- 蓝牙连接管理
- 实时数据显示(姿态角、速度)
- 参数调节界面
- 运动轨迹记录
重点注意:
- 蓝牙通信需放在子线程避免阻塞UI
- 数据解析要考虑字节序问题
- 建议添加重连机制
5. 系统调试经验
5.1 常见问题排查
-
小车剧烈振荡
- 检查MPU6050安装是否牢固
- 降低角度环P参数
- 确认电机极性是否正确
-
向一侧缓慢倾斜
- 重新校准陀螺仪零偏
- 检查车体重心是否居中
- 调整机械结构对称性
-
蓝牙连接不稳定
- 确保模块供电充足
- 修改通信间隔为100ms以上
- 添加数据包重传机制
5.2 关键调试技巧
- 先用上位机绘制实时曲线(推荐使用VOFA+)
- 参数调节遵循"先比例后积分再微分"原则
- 机械结构调试比软件参数更重要
- 电池电压低于7V时应立即停止调试
6. 锂电池管理方案
采用TI的BQ25895充电管理IC实现:
- 最大2A充电电流
- 支持4.2V/4.35V电池
- 充放电保护功能
电量监测算法:
c复制float GetBatteryPercentage(float voltage) {
// 3.7V~4.2V对应0%~100%
voltage = constrain(voltage, 3.7, 4.2);
return (voltage - 3.7) * 100 / 0.5;
}
实际开发中发现,锂电池的内阻会影响电机响应速度。当电量低于30%时,建议将PID参数适当调小10%-20%。
7. 项目优化方向
-
加入机器学习算法
- 采集不同地面下的运动数据
- 训练自适应PID参数模型
-
升级视觉导航
- 添加OpenMV模块
- 实现路线识别跟踪
-
强化车身结构
- 采用碳纤维材料
- 优化重心分布
这个项目最让我意外的是,机械结构的微小偏差对控制效果的影响远大于算法参数。有次调试三天都没解决的问题,最后发现只是电机支架有0.5mm的错位。建议大家在开始软件调试前,先用游标卡尺确认所有机械尺寸的对称性。