简介:本资源是一套完整的STM32平衡小车软硬件开发套件,面向嵌入式初学者、电子设计竞赛备赛者及智能车项目实践者,覆盖自平衡控制核心原理与工程落地全流程。资源包含AD09设计的10张模块化原理图(如AHRS姿态传感、蓝牙通信、电机驱动、主控与电源系统)、对应PCB文件及BOM清单,两套独立MCU源码——运动控制(STM32F103RCT6)与姿态解算(STM32F103C8T6),以及可直接安装调试的安卓蓝牙遥控器APK与完整Java源码。压缩包共351个文件,含75个.h头文件、71个.c源文件、37个.o编译中间文件及多个.uvproj工程配置,辅以实物图、3D结构图与电路板照片,便于理解硬件布局与软件分层架构。目前已有339人学习下载,资料结构清晰、模块解耦明确,支持从传感器数据融合、PID闭环调参到移动端指令下发的全链路验证,是掌握惯性导航、实时控制与跨平台通信的高实用性学习样本。 一提平衡小车,很多人第一反应是“这不就是陀螺仪加电机驱动的经典项目嘛”,但真上手过的人都知道,网上的资料大多数只给了一半:要么只有原理图,要么只丢一份工程代码,或者是手机APP源码压根不完整。而这个项目资料包是少见的“三件套齐全”——AD09设计原理图和PCB、STM32单片机软件源码、安卓蓝牙遥控器APK及源码,硬件、固件、手机端一条龙,拿来就能照着做,也能拆开逐模块学习。
这套资料的适用人群很明确:正在学STM32的学生、刚入行的嵌入式工程师、以及想在电子制作比赛里快速出作品的爱好者。如果你能把这套资料吃透,你等于完整走了一遍“传感器采集→姿态解算→闭环控制→无线通信→手机端交互”的全链路开发过程,这比单纯看教程有价值得多。
1. 资料全景拆解:一个平衡小车项目到底包含哪几块
1.1 硬件设计资料:AD09原理图+PCB
这套资料的硬件部分用AD09(Altium Designer 2009)设计。很多人纠结要不要升级到AD20、AD21,其实对于平衡小车这种规模的板子,AD09完全够用,而且不少学校、公司的旧版本兼容环境还在用09。关键在于AD09能打开工程文件,也能直接输出Gerber文件发给工厂打样,这点老归老,但干活没问题。
原理图里大致包含这几块:STM32最小系统(F103C8T6或者兼容型号)、MPU6050六轴传感器、TB6612电机驱动、HC-05蓝牙模块、电机编码器接口、电源管理、按键和指示灯。把这些外设电路吃透后,你会发现它其实是一个“最小系统+传感器+驱动+通信”的通用模板,以后做智能小车、机械臂、云台都能复用。
PCB部分可以拿来做两件事:第一是直接打样焊接,按BOM清单买物料就能复现整机;第二是当作布局布线的学习样本,看别人怎么处理电机大电流线路、晶振位置、电源滤波电容的摆放。这一点反而最值钱,因为很多新手画板子能亮灯,但跑电机之后系统老复位,根源就是布局布线经验不足。
1.2 STM32软件源码:从驱动到控制闭环的关键路径
软件源码是这套资料的灵魂。它不止是“点灯”级别的例程,而是覆盖了从底层寄存器配置到上层控制算法的完整链路。工程里最核心的模块有:MPU6050初始化与角度读取、陀螺仪和加速度计数据处理、编码器测速、PWM输出、PID控制器、蓝牙串口协议解析。
我特别看重代码里的“控制节拍”设计。平衡小车是一个典型的实时控制系统,姿态环至少要跑到1kHz才能稳,速度环和转向环一般100Hz就够。源码里通过定时器中断来区分任务粒度,这个架构思路比具体某行代码更重要。你只要看懂了这个架构,将来做四轴、平衡机器人、自稳云台,思路完全通用。
另外,源码最好用标准外设库版本,因为网上教程多、排查问题时资料好找。如果资料里恰好是HAL库的版本,问题也不大,重点是看控制逻辑,跑通了之后你完全可以用自己习惯的库重写驱动层。
1.3 安卓蓝牙遥控APK及源码:手机秒变遥控器
安卓端这部分,资料包里同时给了已签名的APK和完整工程源码。APK可以直接装到手机上试用,省去配置环境的时间;源码则方便你改遥控界面、加功能、换成自己的按键布局或通信协议。
APP实现的功能一般包括:前后左右方向控制、速度加减、急停、转向微调。这些功能背后对应一套简单的串口协议,比如特定帧头加指令字加校验位,单片机能解析、执行、回传状态。手机通过经典蓝牙SPP协议和HC-05模块通信,这套方案成熟稳定,代码量不大,适合入门级蓝牙开发。
如果你是做安卓开发的新手,这个APP的源码价值也很高。它涵盖蓝牙权限申请、设备扫描、配对连接、Socket读写线程、UI事件处理、数据分包粘包处理,这些正好是安卓蓝牙应用开发的全部核心点。
2. 硬件核心设计:AD09里怎么画好一块平衡车驱动板
2.1 主控与外设选型及原理图设计要点
先看主控。这类项目最经典的选择是STM32F103C8T6,48脚、64KB Flash、20KB RAM,性能做平衡车绰绰有余,价格也便宜。选它还有一个好处——资料极多,哪怕你完全没用过STM32,网上随便搜都能找到入门教程,避免“入门即放弃”。
原理图设计时,最小系统要做扎实:8MHz主晶振加两个20pF负载电容,复位电路用10k电阻加100nF电容,BOOT0和BOOT1都要通过10k电阻下拉到地。电源引脚旁边放0.1uF去耦电容,每个VDD一组,芯片底部的VSSA、VDDA这些模拟电源也要接好,不能悬空。这些细节看着琐碎,但真遇到“程序跑飞、偶尔死机”的毛病,大部分问题都出在这些小地方。
MPU6050通过I2C接口连接,SDA和SCL必须加上拉电阻,一般用4.7k,一组到3.3V。这里有个关键坑点:I2C地址由AD0引脚决定,AD0接地时地址是0x68,接高电平是0x69,读不到数据时先查这个引脚。INT引脚最好接到STM32的一个普通GPIO,方便后续用中断方式读取数据,比轮询方式省CPU。
电机驱动部分,TB6612FNG是主流选择。它比L298N体积小、压降小、效率高,用在7.4V两节锂电池供电场景下非常合适。控制脚需要6个GPIO:两个PWM引脚控制左右电机速度,AIN1/AIN2、BIN1/BIN2控制方向逻辑。注意TB6612的逻辑电源VCC接3.3V还是5V,要和单片机IO电平匹配,避免逻辑电平不兼容导致电机乱转。
2.2 电源系统与电机驱动的布局布线经验
电源方案我见过最多的是两节18650锂电池串联,标称7.4V,直接给电机驱动供电。控制板需要5V和3.3V,所以板上至少两级降压:常用的做法是先用一颗低压差线性稳压器降到5V给逻辑电路和传感器,再通过AMS1117-3.3降到3.3V给单片机。如果电机电流大导致电压跌落明显,建议用开关降压模块先把电压稳到5V,再线性降到3.3V,这样系统更稳。
这里教大家一个估算方法:电机在堵转状态下电流可能到1A到2A,如果两路电机同时大电流,瞬时电流到3A到4A很正常。所以PCB上电池到电机驱动的电源线、以及电机驱动到电机的输出线,线宽至少要100mil以上,最好直接铺铜皮或者加开窗覆锡。普通信号线可以走10mil左右,但功率线绝不能按信号线的标准来。我在第一版板上就吃过亏,电源线走细了,电机一加速单片机就复位。
布局上,电机驱动和电源部分要尽量靠近电池输入接口,并远离天线、传感器和晶振。大电流线路的去程和回程要短而粗,形成环路面积尽量小,这样可以减少对外辐射干扰。电机接线端子边上放一颗100uF电解电容和一颗0.1uF瓷片电容,用来吸收电机换向时产生的反电动势尖峰,这一步对系统稳定性帮助极大。
2.3 PCB抗干扰设计:哪些细节直接影响稳定性
平衡小车是典型的“功率电路和敏感电路共存”的板子,电机产生的电磁干扰很容易通过电源和地线窜进传感器和单片机,导致姿态数据跳动甚至程序死机。所以布板时有几条原则必须守。
一是电源和地线分开处理。模拟地、数字地、功率地不要直接大面积铺在一起,而是在某一点单点连接。一般做法是分别在原理图里用0欧电阻或磁珠把模拟地和数字地隔开,PCB上功率地单独走,最后在电池负极处汇合。这样电机电流的突变不会直接干扰到MPU6050和单片机的参考地平面。
二是去耦电容的位置要正确。每个芯片的电源引脚都要有0.1uF瓷片电容,并且要放在芯片电源引脚附近,走线要先过电容再到芯片的VDD引脚。大容量的电解电容放在板子电源入口处,起到储能和滤波作用。电容放得不对,等效于没放,高频噪声照样进芯片。
三是晶振和I2C线的处理。8MHz晶振要尽量靠近STM32的OSC_IN和OSC_OUT引脚,晶振下方不要走其它信号线,最好在晶振区域铺地。I2C总线的SDA和SCL线尽量短,如果板子空间大、走线长,可以在靠近MPU6050那一端再加一组4.7k上拉以增强驱动能力。最后,蓝牙模块的天线区域下不能铺铜,不然天线被地平面屏蔽,蓝牙连接距离会明显缩短。
3. STM32软件源码:三环控制闭环实现细节
3.1 初始化与主循环任务架构
这套源码的整体架构是“主循环+定时器中断”的模式。上电后先把时钟配置到72MHz,然后依次初始化GPIO、I2C、串口(用于蓝牙和调试)、定时器(用于PWM输出和编码器计数)、外部中断(接收遥控数据)。
主循环里跑的通常是一个状态机,比如待机、自检、平衡、遥控、急停。状态之间通过按键或者蓝牙指令切换。这种状态机的好处是后续扩展功能非常方便,想加一个“充电状态”或者“学习模式”,只需要加一个枚举值再补一个分支。
关键的实时控制逻辑放在定时器中断里。常用的分配方式是这样的:1ms中断里做直立环控制,读MPU6050姿态数据、算出倾角和角速度、执行PID计算、输出PWM占空比到电机驱动;10ms中断里做速度环和转向环,读取编码器累计值、计算当前车速、累加速度误差、更新直立环的目标角度。把实时性要求高的任务放进高频中断,把低频任务放在低频中断或主循环,CPU占用率低,控制响应又及时。
这里有一个很多人忽视的点:中断里不要做耗时操作,比如串口打印、OLED刷新、复杂浮点运算,尽量少做或者移到主循环。我见过不少人把姿态解算的浮点运算全堆在1ms中断里,结果中断还没跑完下一次中断又来了,系统直接卡死。
3.2 MPU6050姿态解算:DMP与互补滤波怎么选
MPU6050自带DMP(数字运动处理器),可以直接输出四元数,再转换成欧拉角,精度和稳定性都够用。使用方法是在初始化时向DMP加载固件、配置采样频率和FIFO,然后用条件判断检测新数据,从FIFO读出四元数,调用姿态转换函数得到Pitch、Roll、Yaw。DMP方案的好处是省掉复杂的滤波算法,代码量也小,比较适合新手快速跑通。
如果你想做深一层,或者硬件上INT引脚没接,可以自己写互补滤波。思路很简单:加速度计解算出的倾角长期稳定但短期噪声大,陀螺仪积分得到的角度短期准但会漂移。把两者按比例融合,比如姿态角等于0.98乘(上一次角度加陀螺仪角速度乘时间间隔)加0.02乘加速度计解算角度,这就是最基础的互补滤波。代码不超过十行,效果已经能跑。
我个人的建议是:先跑DMP,确认整个系统没问题,再尝试换成互补滤波,对比一下两种方案在振动环境下的表现差异。这种对比学习的收益非常大,因为你能直观理解“为什么传感器融合能互补缺陷”。另外,上电后一定要做陀螺仪零偏校准,让小车静止几秒钟,采集若干帧陀螺仪数据求平均值作为零偏,否则小车即使放平也会朝一个方向缓慢旋转,这就是零漂在捣乱。
3.3 三环PID控制:直立环、速度环、转向环的执行逻辑
平衡车的控制核心是串级PID,通常由三个环组成。直立环是基础,目标是让小车维持竖直状态;速度环负责让小车能稳定在原地或按给定速度移动;转向环控制小车转向,让左右轮按需求产生转速差。
直立环本质上是一个PD控制器。输入是当前倾角和目标倾角(默认0度附近),输出是让小车向直立方向运动的力矩,这里通过左右电机的PWM占空比实现。Kp决定“纠正倾角的力度”,Kd对应对角速度的阻尼,用来抑制震荡。直立环输出的是PWM基值,速度环的输出会对直立环的目标角进行微调,让小车通过前倾或后仰产生前进或后退的加速度。
速度环通常用PI控制器。输入是当前速度和目标速度,输出是一个角度修正量,叠加到直立环的目标角度上。举个例子:目标速度为0,但小车被外力推开往前移动了,速度环检测到正速度误差,输出一个负向角度修正,让小车往后仰,产生一个反向加速度把车拉回来。这里积分项很关键,用来消除稳态误差,但积分必须限幅,否则积分饱和后小车会猛冲。转向环的输入是左右轮编码器转速差,输出是左右电机PWM的差量,一般用PD控制就够。
PID参数整定方法我在后面单独讲。这里只提醒一点:三个环的优先级不要搞反,直立环越稳,速度环和转向环越容易调。如果直立环还在震荡,别急着去调速度环。
3.4 编码器测速与PWM输出细节
测速方式上,这套资料基本会选择定时器的编码器模式。STM32的TIM2、TIM3、TIM4可以工作在编码器接口模式,两个通道接电机编码器的A、B相输出,硬件自动判断方向和计数。这样CPU不用额外处理外部中断,代码简单,计数精度也高。接法上注意A、B相不要接反,如果方向反了,只需要软件里把计数值取负,或者交换接线。
编码器计数频率要和控制周期匹配。比如1ms控制周期内读取一次编码器值,然后求差,换算成当前速度。换算是关键:编码器线数乘减速比,再除以一圈的脉冲数,才能得到真实转速。常见电机比如1:30减速比配13线霍尔编码器,电机轴转一圈编码器输出13乘4倍频个脉冲,输出轴一圈是13乘4乘30,就是1560个脉冲。这个换算关系不搞清楚,速度环参数会一直调不出来。
PWM输出这边,频率建议设置在20kHz左右。频率太低电机转动噪声大,出现可听见的“吱吱”声;频率太高驱动芯片开关损耗增加,MOS管发热。20kHz正好超出人耳听觉范围,电机安静且驱动效率高。输出占空比时,要注意电机存在死区电压,尤其是小占空比时电机根本不转。解决办法是在软件里设置一个最小占空比偏置,让输出低于死区时直接给到死区附近,这样启动会顺滑很多。
4. 安卓蓝牙遥控器APK及源码开发要点
4.1 蓝牙通信方式选型:经典蓝牙SPP和BLE差异
HC-05是市面上最常见的蓝牙串口透传模块,使用的是经典蓝牙SPP(串口配置文件)。SPP的底层就是模拟串口传输,单片机端发串口数据,手机端就能收到,反之亦然。这套方案成熟、兼容性好,而且安卓系统把SPP的UUID固定封装好,开发时直接用标准串口UUID就能连接。
BLE(低功耗蓝牙)则是另一种思路,优点是功耗极低,适合穿戴设备、传感器节点,但它和SPP不兼容,HC-05也不支持BLE。如果你想把APP改成BLE版本,就得换BLE模块,比如CC2541或者JDY-23这类。对于平衡车这种需要持续双向通信、距离要求不高的场景,经典蓝牙SPP完全够用,而且稳定。
所以决策很简单:先用HC-05搭配SPP跑通整个项目,如果后续想做低功耗和小体积,再考虑BLE方案。不要在第一步就卡在选型上。
4.2 安卓权限设置与设备连接流程
开发安卓蓝牙应用,最闹心的是系统权限。老版本只需要BLUETOOTH和BLUETOOTH_ADMIN两个权限,但Android 6.0之后如果要搜索设备,需要申请定位权限;Android 12之后又把蓝牙权限拆分成了BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE,运行时还需要动态申请。如果你的APK编译版本比较新,不处理这些权限,扫码搜不到任何设备是必然的。
连接流程整体是这样:先检查设备是否支持蓝牙、蓝牙是否开启;然后获取所有已配对设备列表,找到HC-05模块名称,比如“HC-05”或修改过的自定义名称;再通过BluetoothDevice.createRfcommSocketToServiceRecord(UUID)创建一个Socket,UUID固定用“00001101-0000-1000-8000-00805F9B34FB”,这是SPP服务的标准UUID;最后调用connect()建立连接。
连接有几个容易踩的坑。第一,连接必须在子线程里做,不能在主线程跑,否则一旦设备暂时不可达,界面直接ANR。第二,如果设备在已配对列表里,但连接失败,可以尝试取消配对后重新配对,部分HC-05模块的配对缓存会导致连接异常。第三,HC-05默认配对密码是1234,个别出厂版本是0000,配对时输入正确密码才有可能成功。
连接成功之后,通信就是标准的输入输出流操作。发送指令时,把命令字符串按约定格式编码后写入OutputStream,接收数据时,在独立线程里循环读取InputStream。注意安卓端的输入流读取要处理半包和粘包问题,最简单的处理方式是规定每帧数据以固定长度或换行符结尾。
4.3 遥控器UI与通信协议设计
APP的界面不用复杂,但布局要实用。推荐这样安排:屏幕中央一个大方向控制区域,包含前进、后退、左转、右转四个按钮;右侧放一个速度滑条或者加减速按钮;底部放急停、回中两个大按钮。按钮的点击和长按事件要区分开来,比如点击一下发送一次运动指令,长按持续发送当前运动指令,松开时发送停止指令。这种交互逻辑在写代码时要理清楚,否则操作起来很别扭。
通信协议要简单可靠。常见做法是定义一个固定格式的字符串,比如“CMD:FORWARD:100\n”,或者更紧凑一些用单字节命令加参数:“F100”,“S0”,“X”等等。单片机端解析时先判断帧头,再取命令字,最后解析参数。校验位可以先用最原始的“帧尾加换行”来保证帧完整性,复杂业务等以后真需要再加密校验。
协议设计的原则是:尽量让单片机端解析代码简单,因为单片机资源有限;APP端可以把更多的字符串处理工作做掉。比如直接发整型的ASCII码,单片机能用sprintf和sscanf轻松处理。如果你发二进制数据,反而要处理字节序问题,比较容易把新手绕晕。
另外,调试时建议在APP里加一个“连接状态”显示和“收发日志”窗口,把发送和接收的原始数据展示出来。这样单片机端有什么问题,你能立刻看出来是数据没发出去、发送格式错误、还是单片机没响应。这个日志窗口虽然不起眼,但排查问题能省一半时间。
5. 平衡车调试实战:常见问题与排查技巧
5.1 PID参数整定的实操顺序与注意事项
PID参数整定是平衡车的头号难关,很多人卡在这一步就放弃了。其实整定顺序比参数本身更重要,按下面的路径走,成功率很高。
第一步,先只调直立环。把速度环和转向环的PID输出暂时屏蔽掉,只让直立环工作。初始Kp给一个比较小的值,比如10到30,Kd给0。上电把小车放在平衡位置附近,观察它是往一个方向倒还是来回震荡。如果小车软绵绵没有回正力,就加大Kp,直到小车能明显感觉到“用劲”维持直立;如果小车还在震荡,就慢慢加Kd增加阻尼,直到车身基本稳定,只有轻微抖动。这里有个经验值:Kp加大后如果出现高频震荡,Kd至少要加到Kp的10到30倍才能压住。
第二步,加速度环。把小车放到地上,关闭遥控,让速度环激活。速度环Kp先从小值开始,比如0.1到0.5,观察小车会不会慢慢朝一个方向跑。如果小车朝一个方向漂移,说明需要积分项来修正,慢慢加Ki。小车能稳定在原地不跑,速度环就成功了。注意速度环的输出要限幅,修正角度的范围最好不要超过正负3到5度,限制太大会让直立环饱和。
第三步,调转向环。让小车原地转起来,观察它转得是否顺滑、是否回中。转向环一般也是先加Kp再加Kd,参数值比直立环小一两个数量级很正常,不用慌。
调参时建议用串口打印或蓝牙回传实时数据。把目标角度、当前角度、PID输出、电机PWM值打印出来,你就能看到输出的变化曲线,比盲调高效得多。建议每调一次参数保存一次记录,数据化调参,不要“感觉好像更稳了”就乱继续。
5.2 蓝牙通信故障排查:从连接不上到数据乱码
蓝牙连接不上,最容易出的问题有三个。第一个是模块供电电流不够。HC-05工作电流大概在30mA到50mA,如果板子上稳压芯片余量不足,模块上电后无法正常广播,手机自然搜不到。排查方法很简单:单独给模块供电试试,搜不到说明模块有问题或供电不足。
第二个是波特率不匹配。HC-05默认波特率常见9600,但很多资料代码里用的是115200。手机端APP发送数据用的是UART字节流,单片机端直接用串口接收,如果两边波特率不一致,收到的数据就是乱码。可以在USB转TTL上先用AT指令查询模块当前波特率,再统一修改传输层参数。
第三个是配对后无法建连。如果手机端已经配对成功,但APP点击连接后一直转圈,大概率是UUID配置不对,或者同一模块被多个手机连接导致连接占用。解决方法是先用“恢复出厂设置”AT指令把模块清干净,再重新配对,APP代码里确认UUID是SPP标准值。
数据乱码还有一种情况:APP发送的字符串编码格式和单片机解析格式不一致。比如APP发送UTF-8中文,单片机按ASCII逐字节解析,完全没有意义。所以协议设计时建议全部用ASCII可见字符,不要发中文,不要发特殊符号,这是最省心的方案。
5.3 电源跌落与电磁干扰导致的疑难问题
平衡车调试到后期,遇到的问题往往不是算法,而是供电。最典型的故障是:电机一加速,单片机就复位或者MPU6050数据跳变。原因就两个:电池电压被拉到单片机复位阈值以下,或者电机PWM产生的地弹噪声干扰了传感器。
电压跌落问题可以通过实测确认。用万用表或者示波器测量单片机VDD处的电压,电机高速转动时如果电压低于3.0V,基本就是电源压降太大。解决办法是加强电源路径:电池用动力型18650,不要用旧电池;电源线加粗;在电机驱动VM引脚附近加大容量电解电容到470uF甚至1000uF;必要时用开关降压模块替代线性稳压器。
电磁干扰导致的数据跳变,特征是电机转的时候MPU6050的Roll和Pitch偶尔跳一下,停下来就正常。处理思路是硬件和软件双管齐下。硬件方面,在电机驱动板的电源输入端加共模电感或者磁珠,电机本体上并一个小容量的100nF陶瓷电容直接焊在电机端子上;软件方面,读取MPU6050数据后加中值滤波或滑动平均滤波,把偶发的尖峰滤掉。注意滤波器不要加太狠,否则姿态数据滞后,直立环反而会抖。
最后再分享一个小技巧:调试平衡车时,最好准备一个可调直流稳压电源,先不要用电池。因为稳压电源可以设定电流上限,如果程序写错导致电机堵转,电流限制能避免烧器件。等到控制参数都整定好了,再换电池做整机测试。这个习惯帮我避免了很多次“焊完板子一上电就冒烟”的尴尬场面。
这套资料能扩展的方向其实不少。比如给小车加一个OLED显示屏显示姿态和电池电压,或者加超声波模块做避障,再或者把手机端改成手势控制。我个人的体会是,参考项目最好的用法不是“照抄跑通就算完”,而是把它拆开——硬件直接看布线思维,固件看控制任务划分,APP看蓝牙通信协议设计——每一块都值得单独学到透。等你把这三部分吃透了,再做任何带电机、带传感器的移动机器人项目,心里都会有底。
本文还有配套的精品资源,点击获取