ESP32自平衡小车开源项目:从硬件选型到PID调参全记录
简介这是一份基于ESP32的双轮自平衡小车完整开源资料面向单片机爱好者、创客及机器人初学者解决从零搭建闭环自平衡小车的硬件选型、结构组装与代码调试问题。资源共10个文件压缩包约1.5MB包含IGES三维结构源文件、PDF工程图与BOM清单、PlatformIO工程源码.cpp/.ini及接线表xlsx并附有装配图片和说明文档无需打板除3D打印件外其余部件均可采购。程序基于SimpleFOC库实现闭环控制支持自平衡、前进后退、转弯及手机蓝牙调PID代码带注释简洁易读。已有208人学习下载适合想快速上手两轮自平衡项目、希望同时获取机械电子代码全套方案的开发者参考改进。 从来没想过一个“ESP32自平衡小车开源”的项目标题能把我从硬件选型一直折腾到ROS 2通信最后还顺手把手机蓝牙App和LVGL界面给做了。如果你也想做一个能拿得出手、可以长期玩下去的嵌入式作品这个项目非常适合入手。我会把自己从立项到收尾的全过程、踩过的坑、调参经验一次性整理出来尽量让后来的人少走弯路。先说结论自平衡小车不是简单“倒不了”就完事它背后涉及姿态解算、闭环PID、电机驱动、机械重心设计甚至还要处理电源波动和无线遥控干扰。用ESP32来做主控是因为它性能足够、外设丰富、社区资料多而且能轻松扩展出蓝牙、Wi-Fi、LVGL、micro-ROS等功能。这篇就跟着我当时的实现路径把每个关键环节都拆开讲清楚。1. 先聊清楚这个开源项目到底解决什么问题1.1 平衡车原理一个简化版倒立摆很多人第一次看到平衡车时都会觉得它“自己站起来了”很玄乎。本质上它就是一个移动的倒立摆模型车身重心在轮轴上方静止状态下这个状态是不稳定的任何微小扰动都会让车身倒下。想要让它站起来只能让车轮不断向车身倾倒的方向移动把重心重新“接住”。数学上这个系统可以简化成力矩平衡问题。车身倾角记为θ重力会产生一个让车身继续倾倒的力矩mg·l·sinθ如果我们给车轮一个水平加速度a惯性力就会产生一个反向力矩ma·l·cosθ。当两者平衡时车身就能保持动态稳定。控制算法的目标就是实时计算应该给车轮多大的加速度也就是PWM占空比来“抵消”倾倒趋势。所以平衡小车本质上和无人机是一类问题——传感器、姿态解算、运动控制三者必须紧密配合任何一个环节delay都会让系统发散失控。这也是为什么我特别建议初学者先用手上现有的模块搭原型而不是一上来就设计PCB因为机械、传感器、软件之间的配合误差才是最花时间的地方。1.2 为什么选ESP32而不是Arduino Uno或STM32先看图Arduino Uno的主频只有16MHz内存2KB跑一个卡尔曼滤波都费劲加上无线模块基本就顶不住了。STM32F103倒是够用但开发环境配置、调试器、引脚复用这些东西对新手来说门槛偏高而且后续想加蓝牙、Wi-Fi、LVGL触摸屏成本和时间都会明显增加。ESP32的优势主要体现在这么几点:双核240MHz一个核跑控制loop另一个核跑蓝牙/Wi-Fi/屏幕刷新互不干扰。内置Wi-Fi和蓝牙手机App遥控、OTA升级都变成“顺手的事”。有成熟的Arduino框架支持也有ESP-IDF可以做生产级开发无论是学习还是进阶都通畅。外设丰富LEDC硬件PWM、PCNT脉冲计数、I2C、UART这些平衡车需要的资源全部内置。我用的是常见的ESP32 DevKitCWROOM-32芯片30 pin版某宝几十块钱就能拿下开源硬件首选。不过提醒一句ESP32的ADC是非线性的如果你后面想用电池电压做补偿不建议直接用内置ADC读电池实在要读就得做软件校准或者外接高精度ADC。2. 硬件搭建与选型清单、接线、避坑2.1 核心器件清单与选型理由我这条开源车用的是以下方案成本不高但稳定性相当不错模块型号/规格作用选型理由主控板ESP32 DevKitCWROOM-32传感器读取、控制算法、无线通信性能强、外设丰富、社区资料多姿态传感器MPU6050六轴模块测量加速度与角速度便宜、通用性强、资料多电机驱动TB6612FNG模块驱动两个直流减速电机体积小、压降小、比L298N效率高电机编码器带AB相霍尔编码器的直流减速电机1:30减速比输出动力并反馈转速编码器用于速度闭环和轮速测量电源2节18650串联7.4V 5V稳压模块给电机和主控供电容量大、供电稳定、方便车载轮胎与车架直径约65mm橡胶轮 亚克力或3D打印底盘支撑与运动轮径大一点对小颠簸更友好有一点必须强调电机驱动不要用L298N。L298N的饱和压降大在7.4V电池下实际到电机端的电压可能只有6V左右而且发热严重稍微带点负载就烫手。TB6612FNG的MOSFET压降低体积小体感上用起来完全是两个世界。如果你手头只有L298N也不是不行但电池电压最好提高到9V以上否则直立环的动态响应会明显不够。2.2 接线布局与机械结构建议我的接线参考如下ESP32 30 pin版MPU6050VCC接3.3VGND接GNDSCL接GPIO22SDA接GPIO21。TB6612FNGVM接电池正极VCC接5VSTBY接3.3V或5VAIN1/AIN2接GPIO26/25BIN1/BIN2接GPIO33/32PWMA/PWMB接GPIO27/14。左右电机编码器我用的电机编码器A相/B相接在ESP32支持的PCNT引脚上比如左轮编码器接GPIO15/16右轮编码器接GPIO34/35区域具体看你使用的IDF和Arduino配置。这里有个非常重要的机械细节电池尽量放在底盘正中央靠下的位置重心越低越好。我之前第一版把电池竖着绑在支架顶部结果传感器稍微动一下车身就左右摇摆调参怎么调都调不稳。后来把电池横过来贴在底盘底部整车平衡明显改善调参也容易了很多。重心高会让系统等效摆长变长、转动惯量变大响应迟钝不是好事。另外建议给底盘加一个万向滚珠或者小尾轮辅助防摔但这会影响“纯两轮”的观感我开源版本里提供了两种底盘的装配方案你可以自己选择。3. 软件实现从传感器姿态解算到闭环控制3.1 姿态解算MPU6050读取与互补滤波平衡车需要知道车身倾角这个角度来自于MPU6050的加速度计和陀螺仪。加速度计在静止时能给出很准确的重力方向但动态时毛刺很大陀螺仪短时间很准但积分久了会漂移。两者正好互补最常用的做法就是互补滤波比卡尔曼滤波实现简单得多而且在这个场景下精度足够。我实测下来互补滤波的α系数取0.98、控制周期5ms左右效果已经很稳。公式大概是float dt 0.005; // 200Hz控制周期 float rate gyroZ; // 绕前后轴的角速度具体符号看传感器安装 float accAngle atan2(accY, accZ) * 180 / PI; angle 0.98 * (angle rate * dt) 0.02 * accAngle;这里的angle就是车身倾角rate是角速度。很多教程喜欢用MPU6050的DMP固件输出四元数再转欧拉角但DMP对初学者来说就是个黑盒出了问题很难排查所以我开源版本里是直接读原始数据用互补滤波。这样每一行代码你都看得懂后面如果要做扩展也方便。需要注意的是传感器读取和滤波都要放在高优先级循环里不能和显示、蓝牙这些耗时操作混在一起。ESP32双核正好派上用场核心0跑Wi-Fi和蓝牙协议栈核心1跑控制循环互不干扰。3.2 PID控制结构直立、速度、转向三个环真正的平衡车控制不是简单PD就完事而是三环嵌套直立环角度环输入车身倾角和角速度输出是消除倾角所需的加速度量核心是PD控制。速度环输入当前轮速目标是让车平均速度稳定在0或遥控目标值核心是PI控制用于抑制车体朝一个方向漂移。转向环输入绕Z轴的角速度偏航速率输出左右轮差速用于转向和抗外部扭转核心是PD控制。三个环从内到外优先级是直立环 速度环 转向环。输出叠加到左右电机的PWM上。我的简化主循环框架大致如下void loop() { imuUpdate(); // 读取MPU6050互补滤波得到angle、rate balancePWM pid_balance(angle, rate); // 直立环PD speedPWM pid_speed(wheelSpeed, targetSpeed); // 速度环PI turnPWM pid_turn(gyroZ); // 转向环PD leftMotor balancePWM - speedPWM turnPWM; rightMotor balancePWM - speedPWM - turnPWM; motorApply(limit(leftMotor), limit(rightMotor)); // 限幅输出 }直立环是整个系统的主环PD控制的含义是倾角越大输出的补偿加速度越大角速度越大越要“拉住”它防振荡。速度环的输出通常是负反馈到直立环上这一点很多人容易搞混。如果速度环符号反了起步时会发现车不仅不往前走反而猛地往后倒一种简单排查方法就是把车控制在直立状态附近用手往前推一下如果车不仅不跟上反而越退越快说明速度环极性反了。3.3 编码器测速与PWM生成电机编码器是速度环的数据来源我用的是AB相霍尔编码器通过读取两路脉冲的相位差判断旋转方向。ESP32的PCNT外设是硬件计数器比直接在loop里用digitalRead中断靠谱得多。在Arduino框架下初始化PCNT后只需要定时读取count值再换算成转速就行。PWM输出方面ESP32用的是LEDCLED控制外设本质上就是硬件PWM通道。我用的是8位分辨率频率1kHz左右滤波电容配合电机本身感抗基本能避免啸叫。很多新人在这个环节踩坑LEDC初始化后忘了写ledcWrite初始值输出悬空结果电机乱转。建议上电时先给PWM通道清零并启动电机驱动STBY脚然后再进主循环。4. 调参实操从抖动到稳定的完整路径4.1 调参顺序与观察要点自平衡车的调参看起来玄学实际上有一定的流程。我推荐下面这个顺序几乎适用于所有车体先调直立环Kp。Kp从小往大加加到车身能“顶住”倾倒、并在中点附近来回摆动为止。这个过程中不要管振荡就观察有没有“恢复力”。再调直立环Kd。Kd从0开始逐渐加用来抑制刚才的振荡。当车体能比较平稳地维持直立且用手拨一下会快速回正、不会持续抖动时直立环基本完成。接着调速度环。先让车在手里悬空转动轮子观察左右轮反馈方向是否正确然后把车放地上看它是否朝一个方向持续漂移。速度环的Kp负责“回正”Ki负责消除静态误差。最后调转向环。可以用手绕底盘中心扭转观察会不会受到一个反向抵抗。转向环增益不用太大否则原地打转或行驶中“画龙”。我给的参考起始参数8位PWM、控制周期5ms、电池7.4V、电机减速比1:30环节参数参考起始值直立环Kp130直立环Kd0.6速度环Kp5速度环Ki0.08转向环Kp3转向环Kd0.01注意这些参数强烈依赖你的机械结构。重心高一点、轮径大一点参数就会明显变化。如果发现车体高频抖动多半是Kd太大或传感器噪声太大别盲目继续加Kd先回查滤波是否到位。4.2 调参工具与实时可视化裸调参数非常痛苦我开源版本里直接内置了一个“参数无线调试”功能ESP32通过蓝牙广播一个简单协议手机端用一段串口蓝牙助手或者写个简单App就能实时改PID还能看车体倾角、目标PWM、实际速度等数据。这样你不需要每次都重新接线刷固件肉眼看着车体反应来调参效率提升至少三倍。如果条件允许建议用ESP32的Wi-Fi WebSocket把实时波形画到浏览器里。我试过用串口绘图仪和自制的网页波形两种方式网页波形的调试体感是最好的能看到直立环输出和倾角的相位关系判断是否出现延迟过大、符号反相、饱和限幅等问题。我个人用下来一个关键心得是调参前先确认你的传感器安装方向和控制方向一致。如果MPU6050装反了你会觉得Kp怎么加都没有效果甚至越加越坏。可以用一个最简单的方法判断拿起小车保持静止倾角10度左右看程序里打印的angle是否为10或-10只要符号在逻辑上一致。5. 常见问题与排查技巧速查5.1 高频故障的调试思路我把自己和群友在调车过程中遇到的典型故障整理成了一张速查表现象可能原因定位与处理上电没反应电机不转驱动板没使能STBY脚未拉高、PWM初始值为0、电池没接对检查STBY电平程序开头先写PWM清零再用万用表测电池电压车身一上电就朝一个方向加速倒下角度符号反 / 直立环输出极性反手动倾斜车身观察PWM跟随方向取反角度或取反Kp车身持续高频抖动、嗡嗡响Kd过大、传感器噪声大、机械间隙大降低Kd、提升滤波平滑度、检查轮轴和联轴器框量车身直立但缓慢向一边漂移速度环没生效、重心偏、编码器数据异常打印左右轮速度确认速度环积分方向调整重心配平电池用一会就失控电池电压跌落导致PWM增益变化、限幅饱和增加电压补偿或换更高C数电池检查限幅逻辑蓝牙连接断断续续蓝牙协议栈占资源与控制loop冲突把蓝牙操作放到另一个核心或降低蓝牙数据频率排查思路永远是先机械后软件、先开环后闭环。如果怀疑机械有问题直接把车放平用手转动轮子感受阻力是否均匀再逐环节排查传感器、控制量输出、电机驱动。5.2 几个容易被忽略的细节这里补充几个在开源分享里不常见、但非常影响结果的小点MPU6050模块上的VCC不能直接用5V供电。新买的一些模块带稳压电路但老版本没有直接5V进去很可能会烧芯片读出来的数据全是乱码。ESP32的3.3V引脚供电就够了。编码器线材要用屏蔽或者双绞线尤其是PWM电机线离得近时干扰非常明显。我第一次接线太随意编码器数据在电机加速时乱跳速度环直接崩溃。电池电压波动对直立环影响很大。电池从8.4V掉到7V附近时相同占空比下电机实际电压差距很大你会发现车体越跑越软。我后来在程序里用ADC采样电池电压对PWM输出做了线性补偿整车在电量变化下也能保持稳定。电机如果带金属齿轮运行时间长了有轻微磨损会引入齿隙backlash。齿隙会让编码器读数在换向瞬间出现“假反应”这种情况只能靠控制周期和滤波来缓解做不到完全消除。6. 开源项目结构与后续扩展6.1 开源仓库里放了什么我这个项目的开源仓库不只是代码还包括完整的设计资料这样别人拿到就能复现。目录结构大概如下:firmware/ esp32_balance_car.ino // Arduino工程包含PID、互补滤波、蓝牙协议 config.h // 引脚配置、PID参数宏定义 readme.md // 编译烧录说明 hardware/ chassis.stl // 3D打印车架结构 wiring_diagram.pdf // 接线图 docs/ tuning_guide.md // 参数调试指南 troubleshooting.md // 常见问题排查 app/ ble_remote_demo // 手机端蓝牙遥控参考代码代码部分我刻意保持了极简风格没有用复杂的操作系统全部注释用中文方便任何人直接拿去看、去改。整个控制器状态都放在一个结构体里串口和蓝牙都能打印出来这样就算你没有逻辑分析仪也能用串口看到内部变量到底发生了什么。6.2 从入门到进阶的功能扩展路径这个项目做完之后可玩性非常高扩展方向也很多加一块OLED屏或者TFT屏把倾角、PID输出、电池电压、速度显示出来。用LVGL写界面的话ESP32的性能完全带得动。接入micro-ROS让小车变成ROS 2里的一个节点。网上有esp32 micro_ros_espidf_component这类组件配合ROS 2 humble环境可以实现上位机下发目标速度和角度小车回传状态这基本就是轮式机器人教学平台了。加摄像头模块后续做视觉巡线、巡人或者颜色识别配合Wi-Fi传输图像。做OTA升级使用ESP32内置Wi-Fi实现固件远程更新这样以后每次改代码都不用重新插USB线。我在调试后期就频繁用OTA非常方便。当你走完这一步你会发现表面上是“一辆能站起来的车”实际上你已经亲手搭建了一套包含传感器融合、实时控制、无线通信、电源管理的完整嵌入式系统。后面再做无人机、机械臂、移动机器人很多思路和模块都能直接复用。等你的车能达到“随手放手就能稳定直立5秒以上”的状态你会发现之前所有的折腾都值了。本文还有配套的精品资源点击获取