1. 项目概述
这个平衡车项目基于黄山派主控板开发,采用了N20减速电机和TB6612电机驱动模块,通过6轴陀螺仪(LSM6D)和3轴地磁传感器(MMC56X3)实现姿态检测。项目实现了基本的自平衡功能,并加入了PID控制算法来优化平衡效果。
作为一个嵌入式开发工程师,我最近完成了这个平衡车的雏形开发。在开发过程中,遇到了不少传感器数据处理和电机控制方面的问题,也积累了一些经验。下面我将详细介绍这个项目的硬件选型、传感器数据处理、控制算法实现等关键环节。
2. 硬件设计与选型
2.1 主控板与传感器选择
项目选用黄山派作为主控板,这是一款性能适中的嵌入式开发板,具有丰富的外设接口和足够的计算能力来处理平衡车所需的实时控制任务。
传感器方面选择了:
- LSM6D:6轴惯性测量单元(3轴加速度计+3轴陀螺仪)
- MMC56X3:3轴地磁传感器
这种传感器组合能够提供完整的3D姿态信息,满足平衡车对姿态检测的需求。
2.2 电机与驱动模块
电机选用N20减速电机,主要参数:
- 工作电压:3V-12V
- 额定电压:6V
- 减速比:150
- 转速:100转/分钟
- 编码器分辨率:7PPR(每转7个脉冲)
驱动模块使用TB6612电机驱动IC,这是一款双路H桥驱动芯片,具有以下特点:
- 最大连续输出电流:1.2A(单通道)
- 峰值输出电流:3.2A
- 内置过热保护和低压检测电路
- PWM频率支持高达100kHz
2.3 引脚配置与硬件连接
主控板的引脚配置如下:
c复制#define PWMA_PIN 29 // 电机A PWM控制
#define PWMB_PIN 20 // 电机B PWM控制
#define AIN1_PIN 35 // 电机A方向控制1
#define AIN2_PIN 36 // 电机A方向控制2
#define BIN1_PIN 27 // 电机B方向控制1
#define BIN2_PIN 28 // 电机B方向控制2
#define ENC1A_PIN 25 // 电机A编码器A相
#define ENC1B_PIN 24 // 电机A编码器B相
#define ENC2A_PIN 32 // 电机B编码器A相
#define ENC2B_PIN 18 // 电机B编码器B相
注意:编码器B相的RX引脚需要特别注意配置,避免与其他功能冲突。在实际布线时,建议使用屏蔽线连接编码器信号,以减少干扰。
3. 姿态检测与数据处理
3.1 坐标系定义
为了准确描述平衡车的姿态,我们定义了两种坐标系:
- 地面坐标系:以地面为参考,固定不变
- 物体坐标系:以平衡车本身为中心,随车体运动而变化

3.2 欧拉角定义
我们使用欧拉角来描述平衡车的姿态,包括三个角度:
- 俯仰角(Pitch):绕Y轴旋转,表示前后倾斜
- 翻滚角(Roll):绕X轴旋转,表示左右倾斜
- 偏航角(Yaw):绕Z轴旋转,表示水平转向

3.3 传感器数据融合
3.3.1 加速度计数据处理
加速度计可以测量静态角度,但容易受运动干扰:
c复制acce_angle.pitch = atan2(lsm6d_acce.data.acce.x, lsm6d_acce.data.acce.z) / 3.1415927f * 180;
acce_angle.roll = atan2(lsm6d_acce.data.acce.y, lsm6d_acce.data.acce.z) / 3.1415927f * 180;
acce_angle.yaw = 0; // 加速度计无法测量偏航角
3.3.2 陀螺仪数据处理
陀螺仪通过积分角速度得到角度,但存在零偏误差:
c复制gyro_offset.pitch += (lsm6d_gyro.data.gyro.y*0.005*25/32768);
gyro_offset.yaw += (lsm6d_gyro.data.gyro.z*0.005*25/32758);
gyro_offset.roll += (lsm6d_gyro.data.gyro.x*0.005*25/32768);
3.3.3 磁力计数据处理
磁力计主要用于修正偏航角:
c复制mmc56x3_angle.yaw = -atan2f(mmc56x3.data.mag.y, mmc56x3.data.mag.x) * 180.0f / 3.1415927f;
float diff = mmc56x3_angle.yaw - gyro_angle.yaw;
if (diff > 180.0f) diff -= 360.0f;
if (diff < -180.0f) diff += 360.0f;
3.4 互补滤波算法
为了融合各传感器的优点,我们采用互补滤波算法:
c复制angle.pitch = gyro_angle.pitch + a *(acce_angle.pitch - gyro_angle.pitch);
angle.roll = gyro_angle.roll + a * (acce_angle.roll - gyro_angle.roll);
angle.yaw = gyro_angle.yaw * 0.95 + 0.05 * diff;
滤波系数a的选择很关键:
- 值过大:加速度计噪声影响明显,会导致抖动
- 值过小:陀螺仪漂移无法有效校正
- 经验值:通常在0.02-0.05之间
4. 电机控制与PID算法
4.1 PID控制原理
PID控制器由三部分组成:
- 比例项(P):根据当前误差大小进行调节
- 积分项(I):消除稳态误差
- 微分项(D):预测误差变化趋势,抑制振荡

4.2 PID算法实现
c复制pid->LastError = pid->NowError;
pid->NowError = pid->Target - pid->Measure;
if (pid->Ki != 0) {
pid->Integral += pid->NowError;
} else {
pid->Integral = 0;
}
float max_integral = pid->MaxOutput / 2.0 / pid->Ki;
if (pid->Integral > max_integral) pid->Integral = max_integral;
if (pid->Integral < -max_integral) pid->Integral = -max_integral;
pid->Result = pid->Kp * pid->NowError
+ pid->Ki * pid->Integral
- pid->Kd * (pid->Measure - pid->LastMeasure);
if (pid->Result > pid->MaxOutput) pid->Result = pid->MaxOutput;
if (pid->Result < pid->MinOutput) pid->Result = pid->MinOutput;
if(pid->Result >0) pid->Result += pid->Resultoffset;
if(pid->Result <0) pid->Result -= pid->Resultoffset;
pid->LastMeasure = pid->Measure;
return pid->Result;
4.3 控制环设计
4.3.1 直立环(内环)
直立环负责保持车体平衡,是最关键的控制环:
c复制angle_pid.Measure = measured_angle.pitch;
ave_pulse = -pid_controller_cal_sat123(&angle_pid);
4.3.2 速度环(外环)
速度环通过调节目标角度来控制平衡车速度:
c复制ave_speed = (pulse_to_rpm(enc_l) + pulse_to_rpm(enc_r)) / 2.0;
speed_pid.Measure = ave_speed;
angle_pid.Target = -pid_controller_cal_sat123(&speed_pid);
4.3.3 转向环
转向环通过差速控制实现转向:
c复制turn_pid.Measure = measured_angle.yaw;
dif_pulse = pid_controller_cal_sat123(&turn_pid);
调试技巧:三个控制环的采样周期需要合理设置。直立环需要最快(约5ms),速度环可以稍慢(50-100ms),转向环介于两者之间。
5. 硬件结构设计
5.1 机械结构
平衡车的机械结构需要考虑以下因素:
- 重心位置:应尽可能低,提高稳定性
- 电机安装:需要牢固,避免振动
- 传感器位置:尽量靠近重心,减少测量误差

5.2 当前存在的问题
- 孔位偏移:黄山派固定孔位与实际结构不完全匹配
- 排针长度:现有铜柱螺丝与排针插槽长度不匹配
- 缺少挖槽:电机编码器和显示屏排线需要专门的走线槽
5.3 改进建议
- 重新设计固定结构,确保孔位准确对齐
- 使用更长的排针或调整铜柱高度
- 增加必要的挖槽设计,优化内部布线
6. 调试经验与问题解决
6.1 常见问题及解决方法
-
平衡车抖动严重
- 可能原因:PID参数不合适,特别是微分项过大
- 解决方法:逐步减小Kd值,观察效果
-
无法保持直立
- 可能原因:重心过高或传感器数据不准确
- 解决方法:检查传感器校准,降低重心位置
-
转向不灵敏
- 可能原因:转向环PID参数过小
- 解决方法:适当增大转向环的Kp值
6.2 PID参数调试技巧
- 先调P,再调I,最后调D
- 从小值开始,逐步增大
- 每次只调整一个参数
- 记录每次调整后的效果,便于分析
6.3 传感器校准要点
-
加速度计校准:
- 在水平静止状态下采集多组数据求平均
- 计算各轴的零偏和比例因子
-
陀螺仪校准:
- 静止状态下采集数据,计算零偏
- 通过旋转测试验证比例因子
-
磁力计校准:
- 进行8字形旋转校准
- 计算硬铁和软铁误差补偿
7. 功能扩展与优化
7.1 语音控制实现
通过MCP服务实现了基本的语音控制功能:
c复制void angle_turn(float angle) {
angle_pid.Target += angle;
LOG_I("Target angle: %f", angle_pid.Target);
}
AddTool("self.turn.right",
"Set the left turn function.",
PropertyList({
Property("angle", kPropertyTypeInteger, 0, 180)
}),
[=](const PropertyList& properties) -> ReturnValue {
int angle = properties["angle"].value<int>();
angle_turn(-angle);
return true;
});
7.2 未来优化方向
-
算法优化:
- 实现更复杂的滤波算法(如卡尔曼滤波)
- 尝试四元数表示姿态
-
硬件改进:
- 改用无刷电机减少死区
- 优化机械结构降低重心
-
功能扩展:
- 添加避障功能
- 实现路径规划
- 增加无线遥控功能
在开发这个平衡车项目的过程中,我深刻体会到硬件和软件协同设计的重要性。传感器的精度、电机的响应速度、控制算法的实时性,每一个环节都会直接影响最终的性能表现。特别是PID参数的调试,需要极大的耐心和细致的观察。