news 2026/10/6 6:57:25

ESP32-S3微鼠V2.0实战:选型、PID调参与比赛全程记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32-S3微鼠V2.0实战:选型、PID调参与比赛全程记录

“NeuroMouse V2.0”这个方案在AAMC 2026拿了个第三名。说实话,冲线那一刻我的心情挺复杂:没能守到第一,但隔壁几个调试团队都在抱怨鼠“发疯”时,我的鼠还能稳定跑完接近满速的全程,这本身就是一种胜利。我是从V1.0那个“跑直线都会画龙”的版本一路改过来的,整个项目最核心的变化,就是主控从STM32F103换成了ESP32-S3,并且围绕这颗芯片重写了全套控制框架。这篇文章就把我这次V2.0迭代时的硬件选型、结构设计、算法调参以及比赛现场踩坑的记录完整摊开,适合正在做Micromouse或者准备参加类似自主迷宫竞赛的朋友参考。

不少人和我说过,Micromouse这种比赛用ESP32-S3显得“不够严肃”,毕竟MATLAB仿真、FPGA、高速裸机代码才是主流。但我的出发点很简单:我缺的不是底层计算,而是快速迭代和调试的效率。ESP32-S3有双核240MHz,带浮点单元和向量指令,USB原生烧录,还内置WiFi和蓝牙。这个规格对一台微型鼠来说已经溢出,真正难的是如何在有限的体积里把传感器、电机、电源和算法可靠地捏成一个闭环。V2.0花了三个多月才达到“敢在比赛场上把速度拉到80%以上”的状态,下面这些内容全是从一次次炸车里总结出来的。

1. 项目背景与选型:为什么是ESP32-S3

1.1 Micromouse到底是什么

Micromouse比赛要求一台完全自主的小型机器人,在没有外部干预的情况下探索一个由16x16个方格组成的迷宫,每格尺寸通常为18cm x 18cm,通过感知墙壁的位置,实时构建地图,并找到从起点到迷宫中心的最短路径,最后在最短时间内完成从起点到终点的冲刺。这项比赛已经有几十年历史,不过真正让它“卷”起来的是近十年电机、传感器和控制算法的进步。现在的顶级鼠能跑出10秒以内的成绩,而普通爱好者做出来的作品往往还在“能不能不撞墙”这个阶段挣扎。

AAMC 2026是我参加的一个亚太地区小型自主迷宫竞赛,项目名NeuroMouse V2.0用的是我自己的命名体系。所谓“Neuro”,并不是说用了什么高深的神经网络,而是我把整个系统整理成类似神经反射的结构:感知层每隔一小段时间采集传感器数据,中间层根据地图和当前速度生成运动指令,底层电机驱动负责执行。分层设计让每部分可以独立调参,V2.0最大的改变就是把V1.0里纠缠不清的裸机回调,重构成了清晰可维护的实时状态机。

1.2 主控选型的纠结与最终选择

做V1.0时我用的是STM32F103,外设寄存器太灵活,写代码时经常因为一个GPIO复用配置错误,花了半天时间查“为什么轮子不动”。后来换了STM32F4,性能有所提升,但每次调试都要外接一个ST-Link,现场改参数还要重新编译烧录,每次迭代浪费大量时间。加上板子上的蜂鸣器、LED和OLED调试信息实在有限,很多时候只能靠盲调。

选择ESP32-S3有几个决定性原因。第一,原生双核。我把传感器读取和运动控制放到一个核上,把WiFi日志、地图更新和遥测放到另一个核上,互不干扰。第二,内置WiFi,配合ROS或自写上位机,可以无线修改PID值,不需要重新烧录。第三,USB CDC可以直接当串口用,哪怕手边没有CH340,也只需要一根USB线连电脑就能看到日志。第四,价格便宜,模组几十块钱,买几块做破坏性试验都不心疼。

当然,ESP32-S3也有坑。ADC线性度较差,模拟传感器读数容易出现波动;FreeRTOS的调度在默认配置下有时候会出现优先级反转;还有WiFi射频辐射可能会干扰旁边走线的传感器信号。这些问题在V2.0中都遇到过了,后面会一个一个拆开说解决办法。我的结论是:对于“以开发效率优先”的业余或半专业参赛团队,ESP32-S3是很合适的选择;如果你已经有成熟的高速代步鼠,那换主控反而会打破原有的稳定状态,不建议盲目跟风。

2. 硬件架构:V2.0迭代里那些取舍

2.1 机械底盘设计要点

V1.0的底盘是用3D打印的一块聚乳酸板材,重量达到280g,电机直接装在打印件上,一跑起来就抖动,编码器读数经常跳变。V2.0放弃了纯打印底盘,改用碳纤维板加铝合金立柱的方案。碳纤维板用激光切割出来,厚度1.5mm,重量比V1.0轻了将近100g。不过碳纤维导电,安装时必须注意PCB和碳板之间要有绝缘垫片,否则电源正极压到碳板就是一场微型烟花。

底盘尺寸上,我设计的车体长96mm,宽82mm,前轮距55mm,后轮距55mm,车高包括顶部传感器板大约62mm。这个尺寸比规则允许的最大值小很多,但并不意味着越小越好。如果轮距太窄,车在高速直线行驶时对左右传感器一致性要求极高,而且电池安装位置也受限;轮距太宽,转弯半径变大,在做迷宫搜索的90度转向时会明显缩短可用的“提前量”。

电机我选的是N20微型减速电机,减速比1:30,带霍尔编码器。轮子是36mm直径的硅胶轮,表面有轻微摩擦纹。这里有个关键点:轮子的直径决定了每转对应的距离,而编码器分辨率又是通过轮径换算的,因此轮径的精确值不要完全相信标称值,我用游标卡尺测了多个轮子,发现直径差0.3mm都会导致左右轮速度差,最终表现为跑不直。最稳妥的办法是实际滚动几圈,量出平均周长,再把单位脉冲对应的距离标进系统参数里。

2.2 传感器阵列:红外测量还是ToF

Micromouse的墙壁检测有三种常用方案:红外距离传感器、激光ToF传感器、超声波传感器。超声波响应慢、精度低,几乎不用于高速竞赛;激光ToF如VL53L0X精度好,但每次测量需要几毫秒,在高速冲刺中如果每个周期读5颗传感器,控制周期就会被拖到十几毫秒以上,运动响应会显得迟钝。V2.0最终选择的是定制红外传感器阵列:用850nm红外发射管和光电接收管,通过测量反射光强来估算墙壁距离。

这种方案最大的问题是“模拟量”很脏,墙壁反光率、环境光、传感器温漂都会造成读数偏差。但我愿意付出校准成本,换取的是极快的响应速度:模拟量可以直接被ADC采样,加上滤波和外推,整个控制周期能做到1ms以内。实际布局是前向2颗、左右侧各2颗、后向1颗,总共5颗传感器,呈斜向安装,以兼顾不同距离的测量范围。每颗传感器输出经过一级运放放大后接入ESP32-S3的ADC引脚。

ADC线性差的问题,我处理方式很简单:不依赖线性关系,每颗传感器单独做距离标定。标定时用步进电机带动一个小墙板,从3cm到30cm每隔2mm记录一次ADC值,生成一张查找表。因为ADC的重复性在恒温下尚可,只要标定一次,跑比赛前再只用距离为0的参考点做偏移修正就够了。如果你手头没有步进电机,也可以手工用带刻度的滑台来标,虽然没有那么平滑,但已经比赛足够。

2.3 电源与电机驱动

车上的电源直接用2S锂聚合物电池,标称7.4V,实际充满电压8.4V,放到3.7V每节就不能再跑。电机驱动选用TB6612FNG,原因是它体积小、压降低,且带电流限制功能,对电机这种堵转电流可以达到数安培的负载来说足够稳定。TB6612的电源端直接接电池,逻辑端接3.3V,注意电机驱动器的地和ESP32-S3的地必须单点连接,否则数字噪声会引入编码器计数异常。

ESP32-S3的工作电压是3.3V,我用了一颗低静态电流的LDO把电池电压降到5V,再用一颗LDO降到3.3V。很多人会忽略电池电压的变化对电机驱动力矩的影响。PID速度环虽然能补偿一部分,但补偿前馈电压项非常关键。我的做法是每5ms采样一次电池电压,算出一个与电压成反比的前馈PWM值,叠加在PID输出之前。这样电池从8.4V掉到7.2V时,相同的速度指令对应的PWM占空比会相应变化,避免速度环的积分器不断加深导致响应迟钝。

3. 软件系统:感知、决策、运动闭环

3.1 状态机与多任务划分

ESP32-S3双核让我有条件把代码拆成两个并行上下文。核心0运行运动控制线程,优先级设为最高,并且用固定频率的定时器中断触发,比如500Hz。核心1运行传感器融合、地图更新、WiFi日志、调试命令,甚至还有一小块故障监视器。这样即使WiFi传输占用了大量CPU,也不会影响电机控制循环。

整个系统状态机分为如下状态:

  • BOOT:校准传感器、初始化电机、等待启动按键。
  • EXPLORE_SEARCH:迷宫搜索模式,结合Floodfill算法更新地图。
  • RETURN_HOME:搜索完成后返回到起点,为冲刺做准备。
  • SPEED_RUN:冲刺模式,依据已建立的地图高速运行。
  • STOP:结束并待机。

状态切换不能直接硬跳。比如从EXPLORE_SEARCH切换到RETURN_HOME,需要先将速度降到安全值,等车完全停稳后,再修改目标点,避免因为速度突变导致位置估算错误。这个看起来“多余”的步骤,其实是很多新手鼠撞墙的根源:位置信息在高速移动中已经积累了误差,不减速就切换路径规划,后面全乱套。

3.2 速度环与位置环PID设计的细节

速度环是控制小车的核心,我用的是标准增量式PID,控制频率是500Hz。你别看PID原理简单,真正稳定跑高速,需要处理好三个问题:

  • 编码器测速噪声。霍尔编码器在低速时容易有漏脉冲,如果直接微分会得到毛刺。我用了滑动窗口中值滤波,窗口长度3,配合一阶低通,基本能把速度估计的噪声压在2%以内。
  • 积分限幅。电机堵转时PID积分会飞速累积,不加限幅的话,等外力消失后会给一个猛烈输出,让车弹出去。我的积分限幅设置成最大PWM的20%。
  • 前馈。单靠PID响应低频变化够用,但在梯形速度曲线的加速段,纯PID会有明显的滞后。因此我加了一个速度前馈项,根据目标速度查表给出基础PWM,PID只负责修正偏差。

代码里大致建模如下:

// 500Hz控制循环内 speed_pid.InnerFeedback(encoder_speed_mm_s); float ff = feedforward_curve.sample(target_speed_mm_s); float pid_out = speed_pid.compute(target_speed_mm_s, encoder_speed_mm_s); float voltage_comp = battery_voltage_comp(target_voltage_nominal, battery_voltage); float pwm_set = ff + pid_out + voltage_comp; set_motor_voltage(left_motor, pwm_set);

位置控制则是外环,频率100Hz。外环每次根据路径规划计算当前应该到达的位置,反推出目标速度,再交给内环执行。转向控制我用的是“圆弧过渡”策略:在到达十字路口前,提前计算出从当前直线过渡到目标方向的圆弧半径,并把该圆弧转化为一系列目标横坐标和航向角。这样比“走直角”速度更快,也减少了对墙面传感器的依赖。

3.3 Floodfill迷宫搜索与冲刺规划

迷宫算法是最经典的了。我用了Floodfill,以迷宫中心为目标区域,每次探测到新的墙壁后重新计算每个格子的距离值,当前格子选择旁边距离最小的格走。这个算法实现起来简单,但要让鼠在迷宫搜索时少走回头路、减少转向次数,还得额外加启发式规则。比如优先级是:优先走直线,其次右转,然后左转,最后掉头。这个规则的排序可以调整,不同的迷宫布局会带来不同效率,用仿真器验证我觉得很值。

搜索阶段结束后,鼠已经有了完整地图,然后运行迪杰斯特拉算法求出从起点到迷宫中心的最短路径。为了让冲刺段更快,我把这个路径做了一步平滑处理:把连续三个格子形成的两条直线段替换成一条直线段,只要中间格子的墙壁允许且转向所需距离足够。这一步平均能缩短4到6个弯道,大概节省0.8秒左右。

冲刺时鼠运行在速度优先模式:直线段用最大的梯形速度曲线,极限速度可以到1.8m/s左右;弯道处会根据圆弧半径动态限速,这个速度阈值不是随便给的,我通过实验找出了不同半径下的最大安全过弯速度,并存表。再靠陀螺仪或侧向传感器跟踪修正。V2.0没上陀螺仪,因为红外侧传感器已经足够密集,用侧壁跟随就能完成大部分航向修正。但如果你要跑来年更高等级的赛道,我建议还是装一颗低成本IMU,尤其对快速掉头会有帮助。

4. 赛场上真实遇到的问题与调参记录

4.1 红外传感器“死亡结界”:强反光导致的误判

比赛前一天场地验收,我把鼠放上去试跑,结果一进那段用高光泽亚克力贴纸围起来的墙面,传感器读数立刻爆表:明明前面没有墙,却显示“距离20cm处有物体”,导致车直接减速四处乱探。这就是典型的红外反光干扰。墙面表面反光太强,红外接收器收到的是镜面反射,而不是漫反射回波。

现场解决方法有三个,按优先级推荐:

  • 提高传感器灵敏度阈值,同时启用“二次检测”:如果读数突然小于3cm,而前一刻还在20cm左右,认为该读数为无效,等待两个周期后再确认。
  • 在传感器探头后方贴深色遮光棉,减少外部光杂散信号。
  • 给发射管加调制脉冲。这需要硬件支持,V2.0的PCB上没有设计脉冲驱动,所以只能靠软件一致性检测缓过比赛。赛后我已经把下一版硬件改成了38kHz调制的红外发射,噪声抑制能力提升明显。

4.2 电池电压波动带来的出力不均

比赛场地环境温度较高,电池性能下降很快,满电时最高速度能到1.75m/s,跑几圈后掉到1.5m/s。一开始我没有做电压前馈,只在速度PID里靠积分硬顶,结果就是高速段发飘,中速段又过头。后来我把电池电压采样接入前馈表,效果立竿见影:同样的速度指令在不同电量下输出PWM基本一致。建议准备两张前馈表,一张针对新电池,一张针对老化电池,比赛前根据电量切换。

4.3 迷宫第二圈掉速:地图重置问题

搜索阶段跑完回到起点时,软件里地图数组还保留着搜索时生成的临时标记,导致冲刺阶段不能用最短路径。第一次发现这个问题时,我以为是传感器又闪了,但后来通过无线日志看到,车在冲刺时走了一条奇怪的绕路。问题出在Floodfill算法为了搜索效率会把终点目标格标记为“绝对值0”,而冲刺阶段应该把目标重新设置为迷宫中心,并且清空所有格子的访问标记。解决起来就两行代码,但如果不加这步,就会发生“慢车绕路”的滑稽场景。

5. 独家调试技巧与后续扩展

5.1 ESP32-S3无线日志与在线调参

这是V2.0迭代效率提升最明显的地方。ESP32-S3内置WiFi,我把它配成SoftAP模式,车本身开一个热点,电脑连上这个热点后通过UDP发送和接收调试数据。上位机用Processing写了个极简表格界面,每50ms刷新一次速度、编码器值、传感器距离、PID误差和电池电压。比赛现场如果发现问题,可以坐在电脑前实时改PID参数包,然后通过UDP下发,不用拔插U盘、重刷固件。这套流程在V1.0是完全不可想象的。

你可能会觉得无线传输会增加系统开销。实测下来,WiFi吞吐只有几百字节/秒,中断占用量可以忽略。唯一需要注意的是WiFi天线附近不要走模拟传感器线,我把天线放在了车尾,朝上,这样既不影响射频性能,也不干扰传感器模拟信号。

5.2 基于记录的迷宫回放工具

调试深了以后发现,单纯看日志并不直观。于是我在PC端加了一个回放工具:把每次运行时的地图状态、位置坐标、速度曲线都写入SD卡上的二进制文件。跑完后把文件拖进上位机,程序就会把鼠的运动轨迹和感知到的墙壁叠加到虚拟迷宫上,这样你可以快速回放“到底第几圈在哪撞上墙”。这个回放工具不复杂,核心就是记录每个时间片的位置和传感器读数,但效果远超一切现场猜测。建议所有做自主移动竞赛的团队都搞一个。

比赛结束后,我没有继续开发V3.0,而是把这个回放数据整理成了软硬件改进清单:第一项是换掉红外发射管驱动部分,改用脉冲调制;第二项是给电机编码器输出增加信号整形电路;第三项是在算法里加入陀螺仪数据融合,让过弯更稳。这些方向不一定都适合所有人,但如果你在调试自己的鼠时遇到我上面提到的类似症状,希望能给你一些可以上手的思路。

做Micromouse最有趣的地方,不只是比谁跑得快,而是比谁在有限时间里解决问题更聪明。ESP32-S3让我把更多精力集中在运动控制和迷宫策略上,而不是在烧录器和示波器之间反复横跳。下次比赛,我想试试看把WiFi调参工具直接做成手机App,赛场边冲电边改参数,应该会更有意思。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 6:57:22

嘉立创EDA专业版实战:STM32最小系统原理图设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 6:57:21

横河CS3000 DCS培训资料实战解读:从组态到故障排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 6:57:03

STM32G474RE开发实战:HRTIM、CORDIC与FMAC工业级应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 6:57:02

Allegro导出Gerber全流程:从Film配置到钻孔文件与打包自检

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 6:56:54

华为云数据中心方案落地实践:网络互联、容灾演练与工程化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 6:55:15

FPGA驱动OV9281全局快门传感器实战:寄存器配置与DVP/MIPI数据通路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华