1. 项目概述
平衡小车是机器人领域一个经典的控制系统实践项目,它通过传感器检测车身姿态,利用电机驱动实现自主平衡。这个基于Arduino的方案特别适合作为控制理论和嵌入式开发的入门实践。
我最初接触这个项目是在大学机器人社团,当时被它看似简单却蕴含深奥控制原理的特性所吸引。经过多次迭代,现在这个版本已经能够稳定实现自平衡功能,同时保留了足够的扩展接口供二次开发。
2. 核心组件选型与原理
2.1 主控板选择
Arduino Uno作为主控有以下几个优势:
- 丰富的GPIO接口(14个数字IO,6个模拟输入)
- 16MHz主频足够处理PID控制算法
- 广泛的社区支持和完善的库生态
- 成本低廉(约30-50元)
注意:如果追求更高性能,可以考虑Arduino Due(84MHz)或ESP32系列,但需要重新适配电机驱动库。
2.2 姿态传感器方案
MPU6050是最常用的6轴运动处理传感器:
- 三轴加速度计(±2g/±4g/±8g/±16g可调)
- 三轴陀螺仪(±250/±500/±1000/±2000°/s可调)
- 内置DMP(数字运动处理器)可减轻主控负担
- I2C接口,仅需4根连线
实测参数:
- 加速度计噪声密度:400μg/√Hz
- 陀螺仪噪声密度:0.005°/s/√Hz
- 工作电流:3.9mA(全功能模式)
2.3 电机驱动模块
L298N双H桥驱动模块的选型考虑:
- 峰值电流2A(持续1A),适合N20等小型减速电机
- 内置续流二极管保护电路
- 支持PWM调速和方向控制
- 具有使能控制引脚
电机参数建议:
- 减速比1:48-1:120
- 额定电压6-12V
- 带编码器反馈为佳
3. 硬件搭建详解
3.1 电路连接示意图
code复制[MPU6050] --I2C--> [Arduino]
|
[L298N] <--PWM/DIR--+
| |
[MotorA] [MotorB]
具体接线:
-
MPU6050:
- VCC → 5V
- GND → GND
- SCL → A5
- SDA → A4
- INT → D2(可选)
-
L298N:
- ENA → D9(PWM)
- IN1 → D8
- IN2 → D7
- ENB → D10(PWM)
- IN3 → D12
- IN4 → D11
3.2 机械结构设计
车体框架建议参数:
- 底盘尺寸:15×10cm(亚克力或3D打印)
- 轮径:6-8cm(带橡胶胎面)
- 重心高度:8-12cm
- 电池位置:尽量靠近底盘下部
装配技巧:
- 使用尼龙柱固定Arduino和传感器
- 电机轴与轮毂间加装联轴器
- MPU6050安装方向与车体坐标系一致
- 所有线缆用扎带固定避免缠绕
4. 控制算法实现
4.1 传感器数据处理
cpp复制#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
int16_t ax, ay, az, gx, gy, gz;
void setup() {
Wire.begin();
mpu.initialize();
mpu.setDLPFMode(MPU6050_DLPF_BW_42); // 设置数字低通滤波器
}
void loop() {
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
float accAngle = atan2(ay, az) * RAD_TO_DEG;
float gyroRate = gy / 131.0; // 转换为°/s
// 后续进行互补滤波...
}
4.2 互补滤波算法
角度融合公式:
code复制angle = 0.98 * (angle + gyroRate * dt) + 0.02 * accAngle
其中:
- dt为采样周期(通常5-20ms)
- 0.98/0.02为权重系数,可根据实际情况调整
提示:更精确的方案可以使用卡尔曼滤波,但计算量会增大。
4.3 PID控制器实现
cpp复制// PID参数
float Kp = 15, Ki = 0.5, Kd = 1;
float error, lastError, integral, derivative;
void balanceControl(float currentAngle) {
error = targetAngle - currentAngle;
integral += error * dt;
derivative = (error - lastError) / dt;
float output = Kp*error + Ki*integral + Kd*derivative;
lastError = error;
// 输出到电机
setMotorSpeed(output);
}
参数整定技巧:
- 先设Ki=0,Kd=0,逐渐增大Kp直到出现小幅振荡
- 加入少量Kd抑制振荡
- 最后加入Ki消除稳态误差
- 每次调整后做阶跃响应测试
5. Proteus仿真搭建
5.1 元件清单
- Arduino Uno (仿真模型)
- L298N模块 (可用两个H桥替代)
- DC Motor ×2
- MPU6050 (可用加速度计+陀螺仪组合模拟)
- 虚拟串口终端
5.2 关键仿真设置
-
电机参数:
- 额定电压:6V
- 负载惯性:0.001 kg·m²
- 反电动势常数:0.01 V/rpm
-
传感器噪声模拟:
c复制// 在MPU6050模型中添加高斯噪声 ax += random(-100,100)/100.0; gy += random(-50,50)/10.0; -
物理引擎设置:
- 重力加速度:9.81 m/s²
- 摩擦系数:0.1-0.3
- 采样间隔:10ms
6. 常见问题排查
6.1 小车无法保持平衡
可能原因及解决方案:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 向一侧倾倒 | 机械重心偏移 | 调整电池位置 |
| 高频抖动 | PID参数过冲 | 降低Kp,增加Kd |
| 缓慢偏移 | 零点校准不准 | 重新校准传感器 |
| 电机发热 | PWM频率过低 | 调整至8kHz以上 |
6.2 传感器数据异常
-
I2C通信失败:
- 检查上拉电阻(4.7kΩ)
- 降低时钟速度(400kHz→100kHz)
- 更换优质杜邦线
-
数据漂移:
cpp复制// 上电时执行校准 mpu.CalibrateGyro(6); // 校准6次 mpu.setZAccelOffset(1788); // 示例校准值
6.3 电机响应不一致
校准步骤:
- 将小车抬起悬空
- 发送相同PWM值给两个电机
- 用激光测速仪测量转速
- 调整PWM映射关系:
cpp复制if(motor == LEFT) pwm = map(pwm, 0,255, 30,255); // 左电机补偿
7. 进阶改进方向
-
增加蓝牙遥控(HC-05模块):
cpp复制#include <SoftwareSerial.h> SoftwareSerial BT(3,4); // RX,TX void setup() { BT.begin(9600); } -
添加超声波避障:
cpp复制float getDistance() { digitalWrite(trig, HIGH); delayMicroseconds(10); digitalWrite(trig, LOW); return pulseIn(echo, HIGH) / 58.0; } -
实现路径跟踪:
- 增加红外巡线传感器阵列
- 采用PD控制算法
- 典型接线:5x TCRT5000 → 模拟输入
这个项目最让我有成就感的是看到小车从摇摇晃晃到稳稳直立的过程。建议初学者先用仿真验证算法,再逐步移植到实物。调试时一定要做好小车突然失控撞墙的心理准备——我的第一个版本就是这样壮烈牺牲的。