平衡小车一直是创客圈经久不衰的经典项目,它巧妙地将传感器技术、控制算法和机电系统融为一体。作为核心传感器,MPU6050以其高集成度和性价比成为多数创客的首选。今天我们就从硬件搭建到代码实现,完整走通这个充满成就感的项目。
GY-521作为MPU6050的经典载板,其设计充分考虑了开发便利性:
提示:实际使用中发现,某些廉价GY-521模块的焊盘可能存在虚焊问题,上电前建议先用万用表检查电源与地是否短路。
平衡小车需要快速响应控制信号,常见的驱动方案有:
| 驱动芯片 | 驱动电压 | 峰值电流 | PWM频率 | 优缺点 |
|---|---|---|---|---|
| L298N | 5-35V | 2A | 5-10kHz | 价格低但效率较差 |
| TB6612 | 2.5-13.5V | 1.2A | 100kHz | 体积小效率高 |
| DRV8833 | 2.7-10.8V | 1.5A | 250kHz | 低功耗集成度高 |
cpp复制// 典型TB6612驱动代码片段
#define PWMA 5 // 电机A PWM控制
#define AIN1 6 // 电机A方向1
#define AIN2 7 // 电机A方向2
void setMotor(int speed) {
digitalWrite(AIN1, speed > 0 ? HIGH : LOW);
digitalWrite(AIN2, speed > 0 ? LOW : HIGH);
analogWrite(PWMA, abs(speed));
}
MPU6050需要正确配置才能输出可用数据:
cpp复制#include "Wire.h"
#define MPU_ADDR 0x68
void setupMPU() {
Wire.begin();
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B); // PWR_MGMT_1寄存器
Wire.write(0); // 解除休眠模式
Wire.endTransmission(true);
// 配置加速度计量程±4g
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x1C);
Wire.write(0x08);
Wire.endTransmission();
// 配置陀螺仪量程±500°/s
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x1B);
Wire.write(0x08);
Wire.endTransmission();
}
传感器上电后需要进行简单的零偏校准:
cpp复制struct SensorData {
float accX, accY, accZ;
float gyroX, gyroY, gyroZ;
};
SensorData readCalibratedData() {
SensorData raw = readRawData();
static SensorData offset = { -0.12, 0.05, 0.33, 1.2, -0.8, 0.4 };
raw.accX -= offset.accX;
raw.accY -= offset.accY;
// ...其他轴同理
return raw;
}
常见姿态解算方法对比:
启用DMP功能的代码片段:
cpp复制void enableDMP() {
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6A); // USER_CTRL寄存器
Wire.write(0x04); // 复位DMP
Wire.endTransmission();
delay(50);
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6A);
Wire.write(0x80); // 使能DMP
Wire.endTransmission();
}
平衡小车的核心是角度环PID控制:
cpp复制class PIDController {
private:
float kp, ki, kd;
float integral, prevError;
public:
PIDController(float p, float i, float d)
: kp(p), ki(i), kd(d), integral(0), prevError(0) {}
float compute(float setpoint, float input, float dt) {
float error = setpoint - input;
integral += error * dt;
float derivative = (error - prevError) / dt;
prevError = error;
return kp*error + ki*integral + kd*derivative;
}
};
调试时应遵循以下顺序:
注意:不同车体结构的参数差异很大,建议每次只调整一个参数,变化幅度控制在±10%。
进阶控制可增加:
cpp复制void balanceLoop() {
static PIDController anglePID(20, 0.3, 1.2);
static PIDController speedPID(10, 0.1, 0);
float angle = getFilteredAngle(); // 获取DMP解算的角度
float speed = getMotorSpeed(); // 编码器反馈
float output = anglePID.compute(0, angle, 0.01);
output += speedPID.compute(targetSpeed, speed, 0.01);
setMotor(output);
}
推荐采用双电源方案:
调试中遇到的典型问题:
传感器数据异常:
电机响应迟钝:
系统振荡失控:
cpp复制// 示例:定时中断服务程序
void TC5_Handler() {
static uint32_t lastTime = 0;
float dt = (micros() - lastTime) / 1e6;
lastTime = micros();
balanceLoop(); // 执行控制算法
REG_TC5_INTFLAG = TC_INTFLAG_OVF; // 清除中断标志
}
完成基础平衡后,可以考虑:
一个调试完成的小车,其响应速度应该能做到:当以10°角度倾斜车体时,车轮能在0.3秒内做出正确响应并恢复平衡。这需要机械、电子和控制算法的完美配合——而这正是创客项目的魅力所在。