恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
STM32两轮平衡车实战:MPU6050姿态解算与PID闭环控制
首页
资讯中心
/
STM32两轮平衡车实战:MPU6050姿态解算与PID闭环控制
STM32两轮平衡车实战:MPU6050姿态解算与PID闭环控制
发布时间:2026/9/15 11:35:37
简介这套基于STM32的两轮平衡小车资料面向电赛备赛学生、嵌入式入门开发者及机器人爱好者提供一套可落地的完整项目方案。资源以MCU源码为核心包含大量C语言与头文件.c/.h覆盖姿态解算、电机控制、蓝牙通信等关键模块硬件部分提供原理图.schdoc、PCB生产图纸及CAD文件并附BOM表和姿态传感器设计说明可支撑从打样到调试的全流程。压缩包共221个文件除源码和图纸外还包含蓝牙遥控器APK及源码、Keil工程配置.uvproj与备份文件整体约29.34MB结构划分清晰便于按需查阅。目前已有120人学习下载对于想快速搭建两轮平衡车原型或理解平衡控制算法的读者是一份值得参考的工程资料。1. 两轮平衡车的关键不在电机在 5ms 实时闭环两轮平衡车最容易让人误判的地方是以为难点在电机驱动或机械结构。实际上电机驱动和结构件只要按图纸做都能转真正决定车能不能站稳的是 STM32 在每一个 5ms 控制周期里能不能把 MPU6050 的姿态数据解算完、把三层闭环跑完。这套资料的主控工程直接以 IMU_AHRS 命名一眼就能看出重心在姿态参考系统上配套的 TWABV 工程、蓝牙遥控器 APK 及源码、CAD 生产图纸和 BOM 表意味着拿到手能从硬件图纸一路做到 App 层改造。准备参加电赛、做毕设或者想把 stm32 项目从「点灯」推进到「实时控制」的人这套资料值得拆开看一遍。2. 硬件电路设计细节供电树、传感器接线与 PCB 避坑2.1 器件选型与工程文件的对应关系从资料正文里能看到bmp180.__i、uarts.__i、IMU_AHRS.uvproj、TWABV.uvproj这些编译中间文件和工程文件说明这套设计里有三个关键外围BMP180 气压计、UART 串口用于调试和蓝牙、以及独立的主控工程和遥控器工程。主控 MCU 是 STM32F103 系列具体型号以 Keil 工程设置里 Device 一栏为准常见做法是 STM32F103C8T648 脚封装72MHz 主频足够跑姿态解算和三层控制环。姿态传感器用的是 MPU6050 六轴加速度计陀螺仪BMP180 挂在同一条 I2C 总线上地址分别是 0x68 和 0x77互不冲突。电机驱动选 TB6612FNG 这类低压 H 桥相比 L298N 它的导通压降小得多7.4V 锂电池供电时电机端电压更足堵转时发热也小。蓝牙模块用 HC-05走 UART 透传和手机 APK 配对后直接收发协议帧。2.2 供电树设计与 PCB 布局要点整车的供电路径是7.4V 两节锂电池 → 5V LDO 给 MCU、蓝牙模块和传感器 → MPU6050/BMP180 模块本身带 3.3V 稳压的话就无需额外 LDO。电机不经过 LDO直接从电池正极取电由 TB6612 的 VM 引脚供电避免电机大电流把 MCU 电压拉垮。PCB 布局上电机驱动部分的地和信号地要单点连接。PWM 占空比突变时电机电流会在功率地上产生毫伏级压降如果功率地和 MPU6050 的地是同一段走线加速度计读数会出现周期性跳变表现就是车身高频抖动。电机焊盘附近放 100nF 陶瓷电容加 10μF 电解电容吸收换向尖峰。另外 MPU6050 尽量靠近车体几何中心且保证芯片 Z 轴垂直安装面否则后续姿态解算要额外做安装角补偿。2.3 接线表与上电检查步骤以常见 STM32F103C8T6 蓝色 pill 板为参考接线表如下外设信号MCU 引脚说明MPU6050SDA / SCLPB7 / PB6I2C1地址 0x68BMP180SDA / SCLPB7 / PB6同一条 I2C 总线地址 0x77TB6612PWMA / AIN1 / AIN2PA8 / PB12 / PB13TIM1_CH1 输出 PWMTB6612PWMB / BIN1 / BIN2PA9 / PB14 / PB15TIM1_CH2 输出 PWM编码器A 相 / B 相PA6 / PA7TIM3 编码器模式HC-05TXD / RXDPA10 / PA9USART1注意交叉连接注意 STM32F103 的硬件 I2C 有时会有总线锁死问题如果解算时读到的加速度值全为零优先怀疑 I2C 总线状态。我一般直接改用软件模拟 I2C把 SCL 和 SDA 配置为开漏输出加上拉电阻稳定性反而更好尤其是在电机电磁干扰大的环境下。上电后别急着下载程序先量三处电压电池端 7.4V、LDO 输出端 5.0V、模块 VCC 端 3.3V。然后用串口线接 USART1打开 115200 波特率终端正常启动时能看到初始化日志包括 MPU6050 自检通过、BMP180 气压值、蓝牙模块状态。哪一步没打印就查哪一路比焊完直接下程序再猜问题省时间。3. 姿态解算落地互补滤波与 AHRS 模块化设计3.1 为什么姿态解算要单独做成 AHRS 模块IMU_AHRS这个工程名拆开看是 IMU惯性测量单元加 AHRS姿态参考系统说明这套代码把「传感器驱动」和「姿态解算」分成了两层。传感器驱动只负责读原始数据解算层只负责把加速度和角速度变成稳定的俯仰角这样换传感器或者换算法都不影响上层控制代码。BMP180 在平衡车里的作用容易被忽略。气压计能感知高度变化常见做法是用它做悬空检测当车被提起或者翻倒时气压高度突变同时编码器脉冲也停止MCU 据此关闭电机输出避免轮子空转加速。另一个用途是起步辅助测量水平面高度差防止车在斜坡上误判零位。3.2 三种姿态解算方案怎么选MPU6050 官方提供了 DMP 固件F103 可以直接读四元数很多工程图省事就挂 DMP。但 DMP 输出频率受 FIFO 配置影响和 5ms 控制周期对不齐的话读取时刻会抖动。卡尔曼滤波在小车这种线性近似系统里效果确实好但协方差矩阵需要调 4 个参数静态收敛速度和动态响应是矛盾的调试成本高。互补滤波代码量最小只有一个加权公式权重系数有明确的物理意义平衡车只关心俯仰角 pitch 和横滚角 roll互补滤波完全够用。这也是资料里 IMU_AHRS 工程最可能采用的方式。3.3 互补滤波核心代码与参数说明以下代码逻辑对应资料中 AHRS 模块的实现思路可在工程里IMU_AHRS工程目录下搜索AHRS相关源文件对照#define ALPHA 0.98f #define RAD_TO_DEG 57.29578f static float g_fPitch 0.0f; // 融合后的俯仰角单位度 static float g_fGyroY 0.0f; // 陀螺仪 Y 轴角速度单位度/秒 // gyroY_raw: MPU6050 原始 Y 轴角速度FS_SEL1 时灵敏度 65.5 LSB/(°/s) // accX_raw / accZ_raw: 原始加速度值直接参与 atan2 计算 // dt: 控制周期单位秒5ms 中断时传 0.005f void AHRS_Update(short gyroY_raw, short accX_raw, short accZ_raw, float dt) { float accel_pitch atan2f((float)accX_raw, (float)accZ_raw) * RAD_TO_DEG; g_fGyroY (float)gyroY_raw / 65.5f; g_fPitch ALPHA * (g_fPitch g_fGyroY * dt) (1.0f - ALPHA) * accel_pitch; }互补滤波的本质是一个一阶低通滤波和一个一阶高通滤波并联加速度计计算出的角度长期稳定但短期噪声大陀螺仪积分出的角度短期精确但会随时间漂移。ALPHA越大越信任陀螺仪动态响应越好但静态漂移也越明显ALPHA越小越信任加速度计静态稳定但动态会显得迟钝。dt必须和实际中断周期严格一致否则陀螺仪积分项会引入固定偏差。读加速度计的accX_raw和accZ_raw用atan2f计算俯仰角比先用反正切再拼接要更抗坐标轴串扰。3.4 姿态解算结果怎么验证代码下进去先别急着装车把g_fPitch通过串口打印出来频率 100Hz。车体水平放置时角度应接近 0误差超过 2° 就检查加速度计零偏和安装方向。用手把车绕 Y 轴前倾 20°串口数值应立即跟到 20° 附近松手回到 0°回程超调超过 3° 说明ALPHA偏大。静止 30 秒角度漂移如果超过 1°检查陀螺仪零偏补偿一般要在初始化时采集 100 组陀螺仪数据取平均作为零偏在积分前减掉。这步做完再进入控制环调试否则 PID 参数再准也救不回来。4. 三层闭环实现直立、速度、转向的参数整定4.1 控制拓扑与时序分配两轮平衡车的控制结构是三个并联反馈环直立环用 PD 控制车身俯仰角速度环用 PI 控制轮子平均速度转向环用 P 控制左右轮差速。三者输出叠加后分别给左右电机而不是串联结构。直立环必须跑在最高优先级资料里的 5ms 周期刷新一次是常见配置速度环积分的本质是让车通过整体前倾或后倾来调节前进速度周期放 20ms 甚至 50ms 都没问题太长车会晃但不会倒转向环和直立环同步刷新即可。一个常见的错误是把三个环算完再一起输出 PWM直立环本身计算量小但 UART 发送调试信息会打断时序我一般会把串口打印放在主循环里用计数器降频到 20Hz。4.2 Balance.c 核心实现代码以下为简化后的控制逻辑对应工程中 Balance 或 Control 模块#define PWM_MAX 720 int g_iBalancePwm 0; int g_iSpeedPwm 0; int g_iTurnPwm 0; // 参数由外部整定初始值参考 4.3 表 float g_fKAngle 800.0f; float g_fKAngleSpeed 2.5f; float g_fKSpeed 30.0f; float g_fKSpeedI 0.4f; float g_fKTurn 1.8f; static int g_iSpeedIntegral 0; // 5ms 周期调用 void Balance_Control(float pitch, float gyroY, int speedLeft, int speedRight, float turnRate) { int speedError (speedLeft speedRight) / 2; // 目标速度为 0 g_iSpeedIntegral speedError; if (g_iSpeedIntegral 1500) g_iSpeedIntegral 1500; if (g_iSpeedIntegral -1500) g_iSpeedIntegral -1500; g_iBalancePwm (int)(g_fKAngle * pitch g_fKAngleSpeed * gyroY); g_iSpeedPwm (int)(g_fKSpeed * speedError g_fKSpeedI * g_iSpeedIntegral); g_iTurnPwm (int)(g_fKTurn * turnRate); int pwmL g_iBalancePwm g_iSpeedPwm - g_iTurnPwm; int pwmR g_iBalancePwm g_iSpeedPwm g_iTurnPwm; if (pwmL PWM_MAX) pwmL PWM_MAX; if (pwmL -PWM_MAX) pwmL -PWM_MAX; if (pwmR PWM_MAX) pwmR PWM_MAX; if (pwmR -PWM_MAX) pwmR -PWM_MAX; Motor_SetPWM(pwmL, pwmR); }注意几个量纲问题pitch单位是度gyroY单位是度/秒PWM_MAX对应定时器 ARR 值的一半比如定时器 7200 分频 10 后满量程 720。这个表里g_fKAngleSpeed直接作用在角速度上相当于 PD 控制的微分项数值远小于比例项是正常的。积分限幅必须比 PWM 限幅小否则速度环积分饱和后松开遥控器车会以最大速度冲向一边。转向环的turnRate我习惯用陀螺仪 Z 轴角速度比左右轮编码器差更平滑也避免了差速计算在大占空比时溢出。4.3 PID 参数三步整定法参数作用建议初值调大后的现象KAngle直立环比例项决定恢复力600 ~ 1000车身高频抖振电机发烫KAngleSpeed直立环微分项提供阻尼1.5 ~ 3.5回中变慢转向迟钝KSpeed速度环比例项20 ~ 50前后周期震荡幅度变大KSpeedI速度环积分项0.3 ~ 0.6回中粘滞容易原地抽动KTurn转向环比例项1.0 ~ 2.5急转向时车身侧倾明显第一步只跑直立环把速度环输出和转向环输出直接注释掉。手持车体在桌面正上方KAngle从 300 开始往上加车会开始自动找平衡点。如果车身来回摆动幅度越来越大多半是KAngle符号反了把角度反馈取反即可。加到车能在松手后原地站 2 秒以上再加入KAngleSpeed直到手推车身能迅速回正且不抖。第二步打开速度环空载时看车是否会在原地缓慢移动速度环积分项能消除静态误差但积分增益过大会导致车周期性前冲后撤。第三步打开转向环手机蓝牙发送左转/右转指令观察转弯半径是否平滑。整个调试过程建议把蓝牙遥控和串口调参分开避免遥控数据帧在控制周期内打断直立环。4.4 蓝牙遥控链路与 APK 源码改造蓝牙模块走 UART 透传MCU 在串口接收中断里解析协议帧。协议格式是典型的 8 字节帧AA 55 04 01 00 00 00 1A前面AA 55是帧头04表示后续数据长度01是命令字后两个字节分别代表前后速度-100 ~ 100和左右转向-100 ~ 100末位是校验和等于前 7 字节累加取低 8 位。手机端 APK 源码直接搜AA 55就能定位到数据打包函数下载下来的 APK 可以先安装看效果再用 jadx 反编译确认协议细节最后对比源码修改。需要改包名的话Android Studio 里 Refactor 一下包名即可但要同步改 manifest 里的声明以及蓝牙权限Android 12 以上还需要在运行时申请BLUETOOTH_CONNECT权限否则扫描不到 HC-05。HC-05 上电后如果一直慢闪说明没有配对。按住模块上的按键上电进入 AT 模式此时快闪串口发ATUART115200,0,0改波特率ATROLE1设为主机或从机ATPSWD1234设置配对码。APK 扫描到 HC-05 后输入 1234 即可连接连上后指示灯变为双闪。常见问题是 HC-05 默认波特率 9600 而源码里初始化 115200串口打印全是乱码这个先查波特率再查接线。5. 源码复现与排错清单资料里IMU_AHRS.uvproj是主控工程TWABV.uvproj从命名看对应遥控器侧 MCU 固件.uvgui_*.bak这类文件是 Keil 界面布局的备份不影响编译可以忽略。先用 Keil 打开主控工程确认 Device 型号和仿真器设置ST-Link 选 SWD 模式烧录前检查Utilities里的 Flash Download 配置。现象原因处理烧录报错 No Flash Algorithm Found芯片型号选错或 Pack 未安装安装对应 Device Family Pack重新选择芯片SWD 无法连接目标程序里把 SWD 引脚复用为 GPIOBOOT0 拉高复位进入 ISP 后重新连接串口打印乱码波特率不匹配检查代码里 UART 初始化和终端设置蓝牙模块一直慢闪不配对未进入 AT 模式或配对码错误按住按键上电进入 AT重新配对电机发出高频尖锐声PWM 频率过低或直立环比例过大检查 PWM 频率是否低于 10kHz降低 KAngle最后分享一个调试技巧先用逻辑分析仪抓电机 PWM 输出确认占空比极限值再在直立环代码里加一个条件断点当pitch大于 30° 时强制关闭电机输出。这样即使参数整定失败车也只是倒下而不是飞出去。车能站稳之后把串口输出的pitch用 pySerial 导成 CSV画个曲线看稳态误差和超调量比肉眼调参快得多。提示排错时优先关闭蓝牙串口中断。遥控器高频数据会在 5ms 控制周期里插入串口处理导致直立环计算被延迟车身会莫名抖动。把蓝牙数据接收改为主循环轮询问题通常立刻消失。本文还有配套的精品资源点击获取