1. 项目概述
两轮自平衡小车是嵌入式系统开发中一个经典的控制系统实践项目。它通过单片机实时采集姿态传感器数据,运用PID控制算法驱动电机,实现类似"倒立摆"的自主平衡效果。这个看似简单的玩具级项目,实际上融合了传感器技术、自动控制理论、电机驱动和嵌入式编程等多个领域的核心知识。
我第一次接触这个项目是在大三的课程设计中,当时用STM32F103C8T6最小系统板搭建的简易版本,光是让小车勉强站立3秒钟就花了整整两周时间调试。后来在工业自动化领域工作多年后,再回头看这个项目,发现其中蕴含的控制思想在AGV、服务机器人等产品中都有广泛应用。下面我就从实际开发角度,详细解析这个项目的技术实现要点。
2. 硬件系统设计
2.1 核心器件选型
主控芯片的选择需要考虑计算性能和接口资源:
- STM32F103C8T6(72MHz Cortex-M3):性价比首选,但浮点运算性能有限
- STM32F407VET6(168MHz Cortex-M4):带硬件FPU,适合复杂算法
- ESP32(双核240MHz):集成WiFi/蓝牙,适合需要无线控制的场景
姿态传感器的对比:
- MPU6050(6轴):最常用方案,集成3轴加速度+3轴陀螺仪
- BMI160(6轴):功耗更低,但价格较高
- ICM-20948(9轴):增加磁力计,适合需要绝对方向的场景
电机驱动方案:
- TB6612FNG:双路1.2A H桥,适合N20等小型减速电机
- DRV8833:双路1.5A,集成电流检测
- L298N:经典但效率较低,需外接散热片
2.2 机械结构设计
车体结构需要注意几个关键参数:
- 轮径建议60-80mm,过小会导致响应迟钝
- 电机轴到车轮的传动比通常选择1:3到1:5
- 电池应尽量安装在车体下部以降低重心
- 车体高度建议控制在15-20cm范围内
实际制作中发现:使用3D打印的尼龙车架比亚克力切割的版本振动更小,因为材料具有一定的阻尼特性。车轮建议选择橡胶胎面,比光面塑料胎有更好的抓地力。
3. 软件算法实现
3.1 传感器数据处理
MPU6050原始数据需要经过以下处理流程:
c复制// 1. 读取原始数据
MPU6050_GetAccel(&ax, &ay, &az);
MPU6050_GetGyro(&gx, &gy, &gz);
// 2. 单位转换
float accel_x = ax / 16384.0 * 9.8; // 转换为m/s²
float gyro_y = gy / 131.0 * (3.1415926/180); // 转换为rad/s
// 3. 互补滤波
float dt = 0.01; // 100Hz采样周期
angle = 0.98 * (angle + gyro_y * dt) + 0.02 * atan2(accel_x, accel_z);
3.2 PID控制算法
位置式PID的基本实现:
c复制typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
} PID_Controller;
float PID_Update(PID_Controller* pid, float error, float dt) {
pid->integral += error * dt;
float derivative = (error - pid->prev_error) / dt;
pid->prev_error = error;
return pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
}
参数整定经验:
- 先调Kp直到小车能快速响应但不振荡
- 加入少量Kd抑制超调
- Ki最后微调消除静差
- 典型初始值:Kp=20, Ki=0.5, Kd=1.0
4. 系统调试技巧
4.1 常见问题排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 小车往一边倾斜 | 机械结构不对称 | 重新校准传感器零点 |
| 高频抖动 | PID微分项过强 | 降低Kd或增加低通滤波 |
| 反应迟钝 | 控制周期过长 | 提高采样频率至≥100Hz |
| 电池续航短 | 电机堵转 | 检查PWM死区设置 |
4.2 高级优化方向
-
状态空间控制:建立更精确的数学模型
matlab复制A = [0 1; g/h 0]; B = [0; 1/(M*h)]; K = lqr(A, B, diag([10 1]), 1); -
卡尔曼滤波:替代互补滤波
python复制kf = KalmanFilter(dim_x=2, dim_z=1) kf.F = np.array([[1, -dt], [0, 1]]) # 状态转移矩阵 kf.H = np.array([[1, 0]]) # 观测矩阵 -
能量控制:通过动能调节平衡点
c复制float energy = 0.5*I*theta_dot*theta_dot + m*g*h*(1-cos(theta)); if(energy > threshold) { target_angle = asin(2.0*energy/(m*g*h)) * 0.5; }
5. 项目扩展应用
完成基础平衡功能后,可以考虑以下扩展:
- 通过蓝牙APP远程控制(建议使用HC-05模块)
- 增加超声波或红外传感器实现避障
- 移植到ROS系统实现SLAM建图
- 改用无刷电机和FOC驱动提升性能
我在最新一版设计中尝试了ESP32-C3+MPU6050+DRV8833的组合,通过PlatformIO开发环境实现了以下改进:
- WiFi远程监控实时姿态数据
- OTA无线固件更新
- 自适应PID参数调整
- 低功耗模式续航达4小时
调试过程中发现一个有趣现象:当控制频率从100Hz提升到500Hz时,小车的平衡稳定性反而下降。经过示波器抓取发现是MPU6050的I2C通信在高速时出现数据丢失。最终通过以下方式解决:
- 降低I2C时钟频率到100kHz
- 在读取函数中添加数据校验
- 对异常数据采用上一周期值替代