
简介本资源是一套完整的STM32平衡小车开发套件面向嵌入式初学者、电子设计竞赛备赛者及智能控制项目开发者解决自平衡系统软硬件协同设计与调试难题。资源包含AD09格式的完整原理图10张模块化SCH文档与PCB工程文件、BOM清单、两套分工明确的STM32源码运动控制与姿态解算分别基于F103RCT6和C8T6、安卓端蓝牙遥控APK安装包及Java源码覆盖传感器融合、PID调参、电机驱动、无线通信等核心环节。压缩包共351个文件以C/H源码146个、编译中间文件.o/.d/.crf、工程配置.uvproj/.uvopt、PCB/SCH设计文件及实物图为主总大小22.89MB。已有339人下载学习资料结构清晰、模块解耦合理支持从电路设计、固件烧录到手机遥控全流程验证附带3D板图、实物照片与多版本工程备份便于理解硬件布局与软件分层架构。 做平衡小车这件事说难不难说简单也绝不简单。最容易让人卡住的不是STM32点不亮而是传感器、电机、蓝牙、电源这几块完全不同的东西怎么才能捏合成一个能自己站起来的系统。我手头正好整理了一套完整的软硬件资料从Altium Designer 09设计的原理图和PCB、STM32单片机软件源码到安卓蓝牙遥控器的APK和工程源码全都在里面。这篇文章就把这套资料背后的设计思路、关键代码和踩坑过程全部摊开讲适合刚学完STM32基础想搞一个综合项目的同学也适合参加电子设计竞赛的团队直接拿去做参考。1. 为什么是“STM32 MPU6050 蓝牙”这个组合1.1 平衡小车的本质一个近似的倒立摆平衡小车本质上是一台倒立摆。重心在轮轴上方轮子只有两个支撑点如果不加控制小车会因为重力而倾倒。要让小车站起来必须实时测量车身倾斜角度然后控制电机让轮子往倾倒的方向加速移动把重心重新“接住”。这个过程是一个典型的闭环反馈传感器检测角度变化单片机计算控制量电机输出力矩再反过来影响角度。很多新手一上来就急着看PID代码其实应该先理解物理模型。倒立摆有一个很重要的特性系统本身不稳定但通过闭环反馈可以让它稳定。姿态角度获取的实时性、准确性和电机响应的快速性决定了小车能不能站稳。用STM32来做主控就是因为它能在毫秒甚至微秒级别完成传感器读取、姿态解算和控制输出而且有丰富的定时器资源用来生成PWM和采集编码器信号。1.2 选型对比为什么不用Arduino也不用51我在资料里选用的主控是STM32F103C8T6也就是大家常说的“蓝丸”核心板。选它不是因为“跟风”而是从性能、资料和成本三个维度综合考虑的。主控方案优势劣势是否适合STM32F103C8T672MHz主频、多路定时器、硬件I2C/SPI/USART实时性强开发门槛稍高需要熟悉寄存器或库函数非常适合Arduino UNO上手快、IDE简单8MHz/16MHz主频定时器和中断资源少实时性一般能玩但容错低51单片机成本极低、教材多资源太少PWM和编码器采集都捉襟见肘不推荐做平衡车传感器方面我用的是MPU6050。它内部集成了三轴陀螺仪和三轴加速度计通过I2C接口输出原始数据也可以使用官方DMP库直接输出四元数省去很多姿态解算的麻烦。电机驱动选TB6612FNG相比L298N它体积小、压降低、PWM频率响应好非常适合电池供电的平衡车。蓝牙模块用HC05主从一体可以方便地和安卓手机配对把遥控指令传给STM32。1.3 整套资料的组成这套资料不是零散的几个文件而是一条完整的开发链AD09设计的原理图源文件和PCB源文件可以随时修改STM32软件源码基于标准外设库编写工程结构清晰安卓蓝牙遥控器APK和Android Studio源码装到手机就能用配套的接线说明、模块清单和调参记录。你拿到手后先按原理图焊接再烧录程序打开手机App配对蓝牙就可以开始调车了。整个过程里最花精力的不是焊接而是调试PID这部分我会在后面单独讲。2. AD09原理图与PCB容易被忽略的硬件细节2.1 原理图模块划分与关键引脚连接AD09虽然是老版本但用来画平衡车这种复杂度不高的双面板完全够用。我看现在很多教学视频都推荐AD20甚至AD21其实思路是一样的只是菜单位置和默认规则有点差异。资料里用AD09一是兼容性好二是不需要太高的电脑配置三是导出Gerber文件给嘉立创打样也没有任何问题。原理图我按功能模块划分这样后面排查问题特别方便。主要模块包括STM32F103C8T6最小系统8MHz晶振两个20pF负载电容复位电路用10K上拉电阻加0.1uF电容到地BOOT0下拉到地BOOT1随意SWD调试口引出SWDIO、SWCLK、GND、3.3V四个引脚。电源电路2S锂电池7.4V经降压到5V再经AMS1117-3.3得到3.3V。电机驱动VM直接接电池正极逻辑部分用3.3V。注意降压模块的输出电容要大一点至少470uF否则电机急加速时电压会跌落。MPU6050SCL接PB6SDA接PB7两条线上各接一个4.7K上拉电阻AD0引脚接GNDI2C地址就是0x68INT中断输出接PB0。TB6612电机驱动VM接电池正极VCC接3.3VSTBY接3.3V使能PWMA和PWMB分别接PA0和PA1对应TIM2的CH1和CH2AIN1/AIN2、BIN1/BIN2用普通GPIO控制正反转。编码器接口左右电机的编码器A/B相分别接PA6/PA7和PB0/PB1使用定时器编码器模式直接计数。蓝牙模块HC05的TXD接STM32的PA10USART1_RXRXD接PA9USART1_TXGND共地。这里有一个很多人忽略的细节HC05模块的逻辑电平名义上是3.3V但很多模块板载了电平转换电路可以直接和5V单片机通信。不过为了稳定我习惯在STM32的TX端串一个1K电阻再并一个2K电阻到GND做分压降到3.3V左右。如果你用的模块明确标注兼容5V不串分压也能跑但长期运行还是推荐加一下。2.2 PCB布局布线的“三轮车”经验PCB布局最大的原则是“先想好物理结构再画板子”。平衡车的车架通常是一个竖直的板子电池放在最下面主板固定在中间电机在两侧。如果你先画好PCB再去设计车架很容易出现孔位对不上、电池挡传感器这种情况。我画板子之前会先用三维建模软件或者甚至纸板搭一个大概尺寸确认各个模块的安装位置。具体到布线几条经验特别值得记电源线加粗。电池正极到电机驱动VM的走线、电机驱动输出到电机端子的走线至少1mm宽最好直接铺铜。地线也一样不要细走线。去耦电容靠近芯片电源引脚。每个IC旁边放一个0.1uF电容STM32的VDD、MPU6050的VDD、TB6612的VCC都要放。这个习惯能减少很多随机复位问题。MPU6050尽量放在PCB的几何中心远离电机和电源线。电机转动会产生磁场干扰如果传感器离电机太近加速度计原始数据会抖动角度自然不稳。模拟地、数字地单点连接。MPU6050的AGND和数字地之间用0欧电阻或磁珠连起来避免电机大电流在地线上产生压降干扰传感器。蓝牙模块的天线区域下方不要铺铜。HC05板载天线如果PCB底层大面积铺铜会影响蓝牙信号导致手机搜索不到或者连接不稳。AD09的DRC检查建议打开Design - Rules设置最小线宽0.3mm最小间距0.3mm电源网络线宽可以单独设置为1mm。画完板后跑一下Tools - Design Rule Check红色报错尽量清零。2.3 从AD09生成制造文件的注意事项很多同学画完PCB后直接拿.PcbDoc文件发给打样工厂这是不规范的。工厂需要的是Gerber文件和钻孔文件。在AD09里路径是File - Fabrication - Gerber Files格式选择RS-274X精度建议2:5然后勾选你要输出的层。钻孔文件在File - Fabrication - NC Drill Files同样用Excellon格式。这里有一个热词里很多人搜过的问题“The imported file was not wholly contained in the valid PCB region”。这个错误通常出现在导入DXF板框或者移动板框时Shape不在禁止布线层的有效范围内。解决的办法很简单在Keep-Out Layer把板框重新画一遍然后用Design - Board Shape - Define from selected objects把板框定义为有效区域。还有一句老话要强调Gerber文件是制造用的不能反向转回PCB源文件所以源文件一定要备份好。3. STM32软件源码解读从寄存器到HAL的姿态闭环3.1 工程结构与代码组织资料里的STM32源码基于标准外设库SPL并不是最新的HAL库。为什么选它因为平衡车控制的重点在于算法和时序标准外设库的代码直接操作寄存器的封装层看起来更直观网上相关教程也最多。如果你已经熟悉HAL库后面对照思路改成HAL也很容易。工程结构大致是这样的Project/ ├── SYSTEM/ │ ├── sys.c │ ├── delay.c │ └── usart.c ├── HARDWARE/ │ ├── mpu6050.c │ ├── motor.c │ ├── encoder.c │ ├── bt.c │ └── pid.c ├── OBJ/ └── main.cmain.c负责初始化外设、进入主循环mpu6050.c负责I2C读取原始数据和姿态解算motor.c负责PWM输出和方向控制encoder.c负责读取编码器速度bt.c负责蓝牙串口数据解析pid.c是三个控制环的实现。主循环建议跑在1kHz也就是每1ms执行一次姿态读取、PID计算和PWM更新。用定时器中断不要用Delay循环否则一旦执行时间抖动小车会明显晃动。在源码里我用的是TIM4产生1ms中断中断里调用一个control_loop()函数。3.2 MPU6050数据读取与姿态解算的两种玩法姿态解算有两种做法一种是用MPU6050内置的DMP另一种是自己在单片机上跑互补滤波或Mahony算法。DMP的优点是输出稳定、对单片机性能要求低适合不太想深究算法的同学。缺点是DMP库代码量大初次移植比较麻烦。资料里我两种都写了默认工程用的是互补滤波便于理解原理。互补滤波的核心思想很简单陀螺仪短时间积分很准但长时间会漂移加速度计低频稳定但高频噪声大。把两者结合起来低频信加速度计高频信陀螺仪就能得到相对准确的角度。核心代码就几行#define FILTER_K 0.98f float angle; // 最终角度 float gyro_y; // 陀螺仪Y轴角速度 float complementary_filter(float acc_angle, float gyro_rate, float dt) { angle FILTER_K * (angle gyro_rate * dt) (1.0f - FILTER_K) * acc_angle; return angle; }其中acc_angle由加速度计计算得到比如acc_angle atan2f(acc_y, acc_z) * 180.0f / 3.14159265f;这里要注意安装方向。MPU6050的坐标轴和车身坐标轴一致时公式才正确。如果传感器装歪了角度会有一个固定偏差后面调PID时会发现车总是往一边加速倒这个偏差可以在程序里做零点校准上电后静止读取100次角度求平均存为offset。3.3 三环PID直立环、速度环、转向环的整定顺序平衡车最少需要两个环路直立环和速度环。转向环是让小车能够左右转弯用的。资料里的源码把三环都写了但是调参顺序千万不能乱。先调直立环。直立环使用PD控制int32_t balance_output (int32_t)(Kp_balance * angle Kd_balance * gyro_y);P越大回复力越强D提供阻尼抑制震荡。把速度环和转向环都屏蔽掉只保留直立环然后用手拿着小车让轮子悬空轻轻转动车身你会感觉到电机在主动抵抗转动。这个现象说明直立环方向正确。接着把小车放在地上它会短暂站立几十毫秒再倒下这时就可以开始调P和D的数值。速度环是PI控制输出的是“目标直立角度的修正量”。原理是利用编码器测到车轮速度如果小车被外力推了一下速度环会输出一个角度修正让车身稍微前倾从而加速去追回到目标速度。速度环P参数调节响应速度I参数消除静差。转向环很简单一般只用P控制int32_t turn_output (int32_t)(Kp_turn * gyro_z);gyro_z是MPU6050绕Z轴的角速度也就是小车的转向速度。把这个输出叠加到左右电机上一正一反形成差速小车就转弯了。主循环里的最终输出大概是int32_t pwm_left balance_output velocity_output - turn_output; int32_t pwm_right balance_output velocity_output turn_output;注意所有输出都要做限幅防止PWM溢出。4. 安卓蓝牙遥控APK源码协议设计与改造4.1 蓝牙模块选型与AT指令配置遥控功能我用的是HC05模块安卓App作为蓝牙主机HC05作为从机。HC05默认就是从机模式但一定要先通过AT指令确认波特率、名称、密码这些参数。把HC05接USB转TTL注意RX接USB转TTL的TXTX接RX还要共地。按住HC05板载按键再给模块上电模块进入AT模式此时模块指示灯慢闪大约每2秒一次。默认波特率是38400可以用串口助手发送AT指令。我常用的几条指令ATNAMEBalanced_Car ATPSWD1234 ATUART115200,0,0 ATROLE0ATUART最后的三个数字分别代表波特率、停止位和校验位我用115200、0个停止位实际是1位、无校验。STM32端串口初始化成相同波特率即可。这里有一个高频问题“HC05蓝牙模块连接不上”。我排查的思路是看模块指示灯如果快闪说明还没配对慢闪说明已经连上。手机端先到蓝牙设置里搜索设备不要直接进App搜索在系统设置里配对成功后App再去连接已配对设备。检查TX/RX有没有交叉很多同学把模块的TX和单片机TX对接当然收不到数据。检查波特率是否一致尤其是HC05的AT波特率和通信波特率是两个概念。AT模式下改成115200后退出AT模式的通信波特率就是115200。4.2 APP实现思路蓝牙串口、摇杆与指令协议安卓端的源码用的是经典蓝牙SPP协议打开Android Studio工程后首先要确认AndroidManifest.xml里已经申请蓝牙权限。安卓6.0以上还需要定位权限因为系统把蓝牙扫描归到了定位权限下面。uses-permission android:nameandroid.permission.BLUETOOTH / uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION /App的逻辑分三块蓝牙连接获取系统已配对设备列表点击设备后建立BluetoothSocket连接。经典蓝牙的UUID固定是00001101-0000-1000-8000-00805F9B34FB。摇杆控制自定义一个RockerView控件根据手指坐标计算摇杆的偏移量映射成方向和高低速度。摇杆比按钮的手感好很多源码里已经实现了。数据发送通过输出流发送协议帧同时可以接收小车返回的电压、角度等信息。如果你不想用摇杆源码里也保留了方向按钮F前进、B后退、L左转、R右转、S停止。4.3 与STM32端的数据校验蓝牙空中传输有一个隐患数据容易丢帧或者被干扰。如果只发一个字符倒还好一旦发多字节指令强烈建议加上帧头和校验。安卓端发送的每一帧固定4个字节uint8_t frame[4]; frame[0] 0xAA; // 帧头 frame[1] command; // 命令 frame[2] speed_value; // 速度值 frame[3] command ^ speed_value; // 校验STM32端收到第一个字节0xAA后进入接收状态连续收满4个字节再校验校验通过才执行控制命令。这样可以避免小车收到错误指令突然加速。源码的bt.c里我用的是状态机解析if (rx_buffer[0] 0xAA) { if (rx_index 3) { received_sign 1; } }收到完整帧后主循环里根据command字段切换前进、后退、转向状态。这个逻辑很简单但比裸收字符可靠得多。5. 联调排错从“车在地上转圈”到稳定站立的过程5.1 上电前的硬件自检清单把硬件全部接好后先别急着烧平衡车程序。我给你一张自检清单照着检查一遍能省好几个晚上检查项操作正常现象电源短路万用表电阻档测电池端无短路电阻大于几十欧稳压输出上电测3.3V和5V电压偏差小于5%SWD下载用ST-LINK连接能识别芯片下载成功最小系统烧录LED闪烁程序LED按预期闪烁MPU6050串口打印原始值静止时加速度计三轴之和接近1g电机固定PWM输出左右轮同时向前蓝牙手机连接并发送F单片机串口能收到0xAA 0x01等上电瞬间最容易出问题的是电源短路电机驱动VM引脚密度高焊接时容易连锡。我习惯先用万用表二极管档测一遍正负极再上电。5.2 姿态数据异常的排查链路姿态数据是整个平衡车的眼睛如果角度不对后面再好的PID也白搭。常见问题有这么几类第一I2C读不到MPU6050。这时候先检查I2C地址到底是0x68还是0x69。AD0接地是0x68接高电平是0x69。软件I2C和硬件I2C的时序有差异如果硬件I2C读不稳定我建议直接用软件I2C模拟。平衡车对速率要求不高几十KHz的I2C足够。第二角度在静止时明显漂移。先判断是传感器安装面不平还是算法问题。把小车放平串口打印角度如果角度不是0度就在代码里读取初始平均值作为零点偏置。如果静止几秒后角度缓慢漂移则是陀螺仪零偏太大可以在上电时采样陀螺仪的平均值作为零偏。第三角度跳变数值突然从几度蹦到几十度。这通常是加速度计受到了机械振动或电磁干扰检查传感器固定螺丝是否拧紧MPU6050旁边有没有大电流走线。5.3 PID不收敛的两个典型案例调PID时遇到车体疯狂抖动是第一个典型问题。现象是电机高速振动车还没站住就开始嗡嗡响摸一下电机外壳烫得厉害。这通常是直立环D参数过大或者PWM频率太低。D项是角速度的放大过大时会对微小抖动产生剧烈反应形成自激振荡。解决方法是先把D降到0只留P让车勉强能晃动再慢慢加D到抖动即将出现的临界点再退回20%。另外PWM频率建议设置在15kHz到20kHz既能避开人耳敏感区也能让电机响应更平滑。第二个典型问题是小车往一个方向加速倒下毫无挣扎。这个现象大概率不是PID参数问题而是角度零点偏了。比如MPU6050安装时向前倾斜了5度程序认为0度其实是实际前倾5度那么直立环会一直让车轮向前加速结果就是倒车。解决办法是上电时读取静止角度作为零点或者让传感器机械位置尽量水平。如果确认零点没问题就看速度环输出方向是不是反了速度环计算出的output和目标方向相反会正反馈加速把输出取反就好。我还整理了一张参数现象表方便对照参数太小太大直立环P车软绵绵无法回中车快速震荡像弹簧直立环D车来回摆动幅度大电机发烫高频抖动速度环P被推后不能回位车来回“追尾”速度环I长时间有静差缓慢溜车容易超调引起震荡转向环P转弯不灵活左右晃头6. 还能怎么玩资料之外的扩展方向最后聊一聊做完基础版之后这套资料还能往哪个方向扩展。如果你手头有带编码器的电机可以做更精确的速度闭环。资料里的编码器接口已经预留了只要在main.c里把encoder_speed的计算加进去就可以配合OLED实时显示左右轮速度。有了速度反馈你还能做上位机曲线显示用串口把角度和速度数据发到PC画成波形调参会直观很多。如果想增加避障功能可以考虑在车头固定一个超声波模块或者用红外接近传感器App端增加自动巡航模式。这个扩展只需在STM32端加一个距离检测任务再把检测结果做判断不需要改动底层控制。想进一步挑战自己可以把标准外设库的工程移植到HAL库并用串口空闲中断加DMA接收蓝牙数据这样能减少CPU占用。或者上FreeRTOS把MPU6050读取、控制环、蓝牙通信分成三个任务用队列传递控制指令代码架构会清晰很多。不过要提醒一句实时控制任务的优先级务必保持最高否则小车会间歇性抽风。在我自己做这套资料的时候最大的体会是平衡车拼的不是某一个模块的黑科技而是从传感器到执行器的整个链路是否可靠。硬件布局稍微差一点软件调参再多也救不回来软件时序稍微乱一点再好的硬件也站不稳。所以拿到资料后不用急着改代码先把原理图和PCB看明白把它当成一块自己设计的开发板再动手调程序这个顺序能帮你把本来要踩的坑提前绕开。本文还有配套的精品资源点击获取