ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ESP32超声波防跌落小车:ZTMR开发板制作与避障逻辑详解

ESP32超声波防跌落小车:ZTMR开发板制作与避障逻辑详解

1. 项目思路与方案选型

1.1 为什么用ZTMR来做防跌落小车

先说清楚这个项目的来路。我在闲鱼上蹲了三天,收了一块二手ZTMR开发板,成色很新,价格比全新便宜差不多一半。ZTMR这东西本质上是ESP32核心的兼容开发板,双核240MHz,带WiFi和蓝牙,GPIO引出完整,焊盘和引脚间距都是标准的2.54mm,直接用杜邦线就能搭电路。对业余玩家来说,它是“能用很低的成本实现一堆想法”的典型代表。

小车防跌落,简单说就是让小车在桌面上跑的时候,接近桌沿能自动刹车或后退,不掉下去。这个场景特别适合入门级智能车玩家,因为它的逻辑链路很清晰:传感器测距、主控判断、电机响应。整个闭环只涉及三个环节,每个环节都能独立调试,出问题也容易定位。而且做完之后很有成就感,你可以把它改造成桌面巡逻小车、悬崖检测小车,甚至加上摄像头变成边缘避让平台。

为什么选ZTMR而不是Arduino Uno或者树莓派?我的理由有两点。第一,ZTMR的ESP32芯片性能冗余足够,就算后面加OLED显示、加舵机云台、加蓝牙遥控,主控也不会吃力,不像Uno那样跑个多任务就捉襟见肘。第二,二手价便宜,学生党或者只想低成本试水的朋友不会有心理负担,翻车了、烧了也不心疼。大概三十到五十元就能收到一块成色不错的,算上电机驱动模块和底盘,整套成本可以压在八九十元以内。

1.2 测距方案怎么选:超声波、红外还是激光

防跌落的前提是“知道桌子边缘在哪”,所以距离传感器是整个项目的眼睛。我实际对比了三种常见方案,各有各的脾气。

先看超声波,具体是HC-SR04这种模块。它的工作方式就是发射一束40kHz的超声波脉冲,然后等回波,通过时间差算距离。这货最大的优点是便宜、皮实,而且它对物体颜色几乎不敏感,黑色桌沿、半透明亚克力板都能测到。缺点呢,一是锥形波束比较宽,探测的是前方一片区域而不是一个点;二是近距离有盲区,一般2cm以内测不准;三是声速会受温湿度影响,但防跌落这种场景厘米级误差完全能接受。

再看红外测距,就是那种发射红外光、靠三角反射算距离的模块。它的优点是响应快、方向性强,但有个致命弱点是容易受环境光干扰,在阳光直射或者强灯光下读数会漂移。更麻烦的是,黑色物体吸收红外光,如果你的桌面是黑色哑光的,红外模块会把距离测成大得离谱,小车直接冲下桌。这种场景我在朋友的项目里见过不止一次。

至于激光/ToF方案,比如VL53L0X这类,精度高到毫米级,响应也快,但价格比HC-SR04贵好几倍,而且要I2C通信,对新手没那么友好。做防跌落属于“够用就好”的场景,花三块钱解决的事没必要用三十块。

所以结论很直接:入门防跌落小车,超声波HC-SR04是性价比天花板。如果后面你想做更精准的悬崖检测,再换ToF模块也不迟,逻辑框架不用动,只改传感器驱动函数。

2. 硬件清单与接线细节

2.1 整车硬件配置一览

列一下我这台车的完整清单,基本都是容易买到的东西:

部件型号/规格用途参考成本
主控板ZTMR(ESP32核心)逻辑控制30-50元(二手)
超声波模块HC-SR04桌沿测距3-5元
电机驱动DRV8833或L298N驱动直流电机5-15元
小车底盘2轮TT马达底盘+万向轮车体15-30元
电池7.4V 18650两串动力+主控供电20-40元
降压模块AMS1117-5.0或MP1584稳定5V2-8元
其他杜邦线、热熔胶、支架连接与固定5元

电机驱动模块这里特别提一句。新手常买L298N,它便宜但内部是PN结开关管,压降大,发热也大,如果电池电压3.7V单节,到了电机端可能只剩不到3V,小车根本跑不动。我后来换成DRV8833,它用的是MOSFET,导通电阻低,体积也只有指甲盖大小,双路输出刚好控制两个TT马达,用起来舒服很多。后期如果想上编码器电机、做大负载,可以再换TB6612,那个也稳定。

供电方案是很多新手翻车的地方。我踩过坑,所以要重点说:电机和主控必须分开供电。TT马达启动瞬间电流能到1A甚至更高,如果你用同一个5V引脚给ZTMR和马达供电,电压会被拉低到3V以下,主控直接掉电重启。正确做法是电池直接给电机驱动供电,ZTMR用独立的降压模块供电,然后把两个电源的负极共地。这样电机怎么折腾都不会让主控复位。

2.2 接线对照表与引脚选择

接线是整个项目里最容易被忽略但影响最大的环节。我把我的接法整理成表,照着接基本不会错:

功能模块引脚ZTMR引脚
超声波特触发HC-SR04 TRIGGPIO 13
超声波回波HC-SR04 ECHOGPIO 12
电机A正转DRV8833 IN1GPIO 25
电机A反转DRV8833 IN2GPIO 26
电机B正转DRV8833 IN3GPIO 27
电机B反转DRV8833 IN4GPIO 14
电机A速度DRV8833 PWM_AGPIO 32
电机B速度DRV8833 PWM_BGPIO 33
超声波电源VCC/ECHO电平处理5V / 分压后接IO

这里面有两个细节容易被忽略。第一个是ECHO引脚的电压问题。HC-SR04是5V逻辑的模块,ECHO输出高电平也是5V,而ESP32的GPIO耐压标称是3.3V。虽然实际很多芯片带一定程度的过压容忍,但我建议稳一手,用两个电阻分压把ECHO降到3.3V左右再接GPIO。分压用1k和2k电阻就行,把5V按比例压到约3.3V,不会烧引脚,也不会让信号失效。第二个是PWM引脚要选支持PWM输出的脚,ESP32几乎所有GPIO都能PWM,但为了不和超声波引脚冲突,我选了32和33,这两个是纯IO,没有其他外设复用,清净。

共地问题我再说一次:驱动模块的GND和ZTMR的GND必须连在一起,否则TTL信号没有参考电平,你的电机控制指令会像对空气喊话,完全没反应。这个坑我当年排查了两个小时,最后发现是地线没接。

2.3 传感器的安装位置与角度经验

超声波模块装在哪、什么角度,直接决定防跌落好不好使。我第一次做的时候把超声波平着朝前装,结果小车在桌上跑,超声波测到的是前面的墙壁,根本没管脚下的桌子边缘,车直接冲下桌。后来想明白了:防跌落传感器要看的是“前方下方”,不是“正前方”。

推荐安装方式是让模块朝斜下方45度左右,离地面大概3到5厘米。这样超声波波束会扫到桌面边缘和地面交界的位置,距离突变点就是危险信号。装太低,波束会打到正下方地面,反射太强导致读数一直很近,误判成危险;装太高,探测范围就偏远了,遇到矮桌沿灵敏度下降。所以3到5厘米是个经验平衡点。

固定方式我建议用打印一个L型支架,没有打印机就用硬纸板或亚克力片加热熔胶。关键是装完之后要保证模块在小车加减速时不会松动,哪怕松动1毫米,测距数据就会跳变几个厘米,防跌落逻辑就会乱来。装好后先别急着跑,拿手扶着车身,对着桌沿前后移动,看串口打印的距离值变化是否平滑。

3. 防跌落逻辑与代码实现

3.1 HC-SR04测距原理与距离计算

HC-SR04的测距时序其实很简单,只有三个步骤:主控把TRIG引脚拉高至少10微秒,模块收到触发后自动发出8个40kHz的超声波脉冲,同时把ECHO引脚拉高;超声波遇到障碍物返回,模块把ECHO引脚拉低。所以ECHO高电平的时间,就是声波从模块到障碍物再回来的总时间。

距离公式可以手推一遍。声速约340m/s,也就是0.034cm/微秒。假设ECHO高电平持续时间为t微秒,那么来回总路程就是0.034乘以t,再除以2就是单程距离。整理一下就得到:距离(厘米)= t / 58。比如ECHO高电平持续了1160微秒,距离就是1160 / 58 = 20厘米。很多人喜欢写t / 58.2甚至t / 58.8,因为声速随温度会变,但防跌落场景没必要抠这点误差,用58就挺好,代码里写起来也清爽。

代码实现上有个关键点:pulseIn函数会阻塞等待回波,如果前方没有障碍物或者障碍物太远,ECHO一直不高,最坏情况会等很久。HC-SR04最大测距约4米,对应pulseIn超时约23毫秒。这个时间在小车控制里不算长,但如果你用了舵机云台或者多个传感器,就要考虑把测距改成非阻塞方式,或者用定时器中断。这个项目简单,直接用pulseIn加超时参数就行,我后面代码里写了timeout为30000微秒(约5米量程)。

3.2 防跌落核心控制逻辑

防跌落的判断逻辑不复杂,但写成代码要有一点章法。我的控制循环是一个简单的状态机:安全状态、警戒状态、刹车/后退状态。安全状态下小车正常前进,采集到的距离值大于阈值;一旦距离小于阈值,说明车头已经到了桌沿附近,进入警戒状态,此时不停车,而是连续采集几次距离,确认不是瞬时干扰;如果连续几次都低于阈值,就执行刹车,甚至反转电机后退一段距离,直到距离值回到安全范围。

为什么要“多次确认”而不直接刹车?因为超声波模块偶尔会受电机电刷火花或者声波反射干扰产生一个尖峰值,比如明明50厘米的地方突然报出5厘米,如果你立即刹车,小车会莫名其妙一顿一顿的。实测下来连续3次采样都低于阈值再动作,误报率能降低很多,代价仅仅是多花几十毫秒,这点延迟在低速小车上完全不是问题。

阈值设多少也有讲究。桌面高度一般在70到80厘米,小车跑起来速度假设0.3m/s,人的反应加代码判断大概在0.2秒内能完成刹车,制动距离也就6厘米左右。再加上超声波波束本身有一个斜向探测夹角,实际探测到的“桌面边缘”会比车轮先到,所以我设阈值在15到20厘米比较合适。车速越快,阈值要越大,不然刹车距离不够。如果你用的是高速电机,我建议阈值直接干到25厘米,稳一点。

3.3 完整代码与参数注释

下面是这个项目的核心代码,我用的是Arduino框架,ZTMR的ESP32直接就能编译烧录。代码里我把每个关键步骤都做了注释,重点看防跌落判断函数avoidFall和距离计算。

// ZTMR 小车防跌落 Demo // 硬件:ZTMR + HC-SR04 + DRV8833 + 两路TT马达 // 超声波引脚 const int TRIG_PIN = 13; const int ECHO_PIN = 12; // 电机驱动引脚 const int MOTOR_A_IN1 = 25; const int MOTOR_A_IN2 = 26; const int MOTOR_B_IN3 = 27; const int MOTOR_B_IN4 = 14; const int MOTOR_A_PWM = 32; const int MOTOR_B_PWM = 33; // 防跌落参数 const int FALL_THRESHOLD = 18; // 距离小于18cm判定为危险 const int SAFE_DISTANCE = 30; // 距离回到30cm以上才恢复前进 const int CONFIRM_TIMES = 3; // 连续3次低于阈值才触发动作 void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); pinMode(MOTOR_A_IN1, OUTPUT); pinMode(MOTOR_A_IN2, OUTPUT); pinMode(MOTOR_B_IN3, OUTPUT); pinMode(MOTOR_B_IN4, OUTPUT); pinMode(MOTOR_A_PWM, OUTPUT); pinMode(MOTOR_B_PWM, OUTPUT); delay(2000); // 等系统稳定 } // 测量距离,单位cm,超时返回999 float getDistance() { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 超时30ms if (duration == 0) { return 999.0; } return duration / 58.0; } // 小车前进 void moveForward(int speed = 200) { digitalWrite(MOTOR_A_IN1, HIGH); digitalWrite(MOTOR_A_IN2, LOW); digitalWrite(MOTOR_B_IN3, HIGH); digitalWrite(MOTOR_B_IN4, LOW); ledcWrite(MOTOR_A_PWM, speed); ledcWrite(MOTOR_B_PWM, speed); } // 小车停止 void moveStop() { digitalWrite(MOTOR_A_IN1, LOW); digitalWrite(MOTOR_A_IN2, LOW); digitalWrite(MOTOR_B_IN3, LOW); digitalWrite(MOTOR_B_IN4, LOW); } // 小车后退,用于脱离危险区 void moveBackward(int speed = 180) { digitalWrite(MOTOR_A_IN1, LOW); digitalWrite(MOTOR_A_IN2, HIGH); digitalWrite(MOTOR_B_IN3, LOW); digitalWrite(MOTOR_B_IN4, HIGH); ledcWrite(MOTOR_A_PWM, speed); ledcWrite(MOTOR_B_PWM, speed); } // 防跌落核心判断,返回 true 表示执行了避险动作 bool avoidFall() { static int lowCount = 0; float dist = getDistance(); Serial.print("distance: "); Serial.println(dist); if (dist < FALL_THRESHOLD) { lowCount++; if (lowCount >= CONFIRM_TIMES) { // 确认危险,执行后退,直到脱离危险区 while (true) { moveBackward(); delay(200); dist = getDistance(); if (dist > SAFE_DISTANCE) { lowCount = 0; moveStop(); break; } if (dist > 999) { // 距离读取异常,保守起见继续后退 continue; } } return true; } } else { lowCount = 0; } return false; } void loop() { avoidFall(); moveForward(200); delay(50); // 短暂延时,控制整体采样周期 }

3.4 关键参数的调优方法与实测数据

代码烧录后,第一件事不是放桌上跑,而是把串口监视器打开,用手拿着小车在桌面边缘慢慢推,观察距离变化。我实测的一组数据可以参考:正常桌面上,小车距离前方桌沿约40厘米时读数稳定在38到42厘米之间;当车头越过桌沿,超声波的探测点落到桌面边缘立面再落到地面上,读数会从30厘米左右突然跳到8厘米以下。这个跳变就是最明显的“危险信号”,比阈值判断可靠得多。

如果你的桌面是比较薄的板子,比如只有1厘米厚,超声波波束可能会直接从边缘上方掠过,导致读数一直没变化。这种情况下要适当调大传感器朝下角度,让波束更“贴地”一些。还有一种情况是桌腿镂空,比如玻璃桌的金属框架在桌沿下面15厘米处,超声波会先扫到桌腿,报出的距离也是危险值,小车会误判。这个没法完全避免,只能靠调阈值来平衡,或者用更窄波束的ToF传感器解决。

PWM速度参数我建议起步用200(范围0到255),这个速度在桌面上既够快,又能保证刹车距离在阈值范围内。如果你把速度调到255,建议把阈值同步调到22厘米以上,否则真到桌沿你会听到电机还在嗡嗡转。我有一台小车就是阈值没调,速度一快直接冲下桌,还好桌子不高,摔坏了两个尼龙螺丝。

4. 常见问题与排查技巧实录

4.1 典型问题与排查方法速查

做这个项目过程中,我前前后后整理了七个最常见的问题,每个都有对应的排查思路:

现象可能原因排查方法
串口打印距离值乱跳超声波模块松动、电池电压不足、ECHO引脚接触不良先看3.3V/5V电压是否稳定;重新插拔杜邦线;用手固定模块看读数是否稳定
小车在桌面中央就突然刹车超声波波束打到车轮或底盘边缘检查模块安装位置是否有遮挡;把模块抬高或调整角度
到了桌沿却不刹车阈值设太小、超声波探测点没对准桌沿串口打印距离值,观察靠近桌沿时读数是否变化;调大阈值
电机完全不转电池没电、驱动板供电不足、IN引脚和PWM引脚接错单独测试驱动板的IN逻辑;用万用表量电机端电压
一上电就反复重启供电共地不稳、电池压降太大检查电池电压;换独立降压模块给主控供电;加470uF电容
超声波测距一直显示999ECHO引脚接错、模块损坏、接线没共地短接TRIG和ECHO测试模块;检查共地线
后退动作太猛,小车掉头翻倒后退速度过快、后退时间过长降低后退PWM;把持续性判断改成每200ms重新测距

4.2 调试流程:从静态测试到动态路测

我强烈建议按这个顺序调试,别一上来就把小车放在桌上放飞:

第一步,静态测距。小车断电,只给ZTMR供电,手动移动小车,看串口距离值是否平滑变化。这一步能排除大部分传感器和代码问题。

第二步,悬空测试。把小车拿在手里,运行代码,用手在超声波下方晃动,模拟桌面和桌沿,确认刹车逻辑能触发。这时候电机应该是能转的,但车没着地,不需要担心摔。

第三步,低速桌面测试。把PWM调到120左右,让小车慢慢在桌面中央跑,然后用纸箱板在前进方向挡一下,模拟桌沿。确认小车能在碰到纸箱板前刹住。

第四步,真实桌沿测试。这一步建议桌子旁边铺个垫子,就算翻车也不至于摔坏。先用手扶着车身跟着走,观察小车到边缘时的反应,再松手。

每一步之间如果发现问题,优先回退到上一步排查,不要直接改代码重烧,否则问题只会越来越乱。

4.3 容易被忽略的环境因素

超声波测距受环境影响比很多人想象的大。首先是地毯、窗帘这类软材质,它们会吸收超声波能量,导致回波变弱,测距值会偏大。如果桌面上铺了绒布桌垫,你会发现阈值要调大一些才有用。其次是风扇、空调气流,强风会扰动空气,声速路径发生变化,读数会有些微抖动,但通常不会超过2厘米,不影响判断。

还有一点特别值得说:同频干扰。如果旁边有人也在调试超声波模块,两个模块同时发40kHz脉冲,可能会互相串扰,导致距离值突然变成对方障碍物的距离。遇到这种情况,要么错开调试时间,要么给模块加一个简单的编码调制,前者更现实。

最后是桌面材质的问题,大理石、木质、玻璃面的反射特性不同,玻璃桌面因为背面有反射面,超声波可能穿透后在下方某个距离产生二次回波,读数会“拉长”。在这些桌面上测试前,一定先看一遍串口数据,做到心里有数再跑车。

5. 项目扩展与个人心得

5.1 防跌落小车还能往哪个方向扩展

做完防跌落基础版之后,这台车其实还可以继续升级,性价比会越来越高。我有几个建议方向:

加一个舵机云台,让超声波模块可以左右扫动,小车就不仅能防跌落,还能实现简单的左中右三方避障。方向盘式的扫描逻辑会让小车看起来“聪明”不少,代码只需要在舵机角度变化时依次采样距离。

加一个OLED屏幕,实时显示距离值和当前状态,调试的时候不用一直插着电脑看串口。做到这个程度,整个系统的“人机交互”就有了,非常适合拿去展示或者做课程设计。

加MPU6050姿态传感器,做成跌落检测,可以识别小车是否被拿起、是否倾倒。这个在防跌落之外又多了一层保护,适合你之后把小车拿到不平整地面跑的场景。

如果你手头有第二块ESP32或者手机,可以做蓝牙/ESP-NOW遥控,把防跌落逻辑变成遥控车的一个保护机制——遥控器让它往危险方向走,它“不敢”去,这就很有意思了。当然,电池和底盘要相应升级,不然承受不住更复杂的逻辑。

5.2 我再聊几句闲话

这个项目是我闲鱼淘板系列里做得最顺手的一个,说几个真实体会。第一,二手开发板到手后先别急着接外设,用USB供电点个灯、跑个例程,确认板子本身是好的再动手。我遇到过一块声称“只上电一次”的板子,结果IO口有一半是坏的,白费了半天功夫。第二,杜邦线质量比想象的重要,劣质杜邦线接触电阻大,会造成传感器数据飘移,排查起来非常折磨人,我后来全换成了镀金端子线,问题少了一大半。第三,做这种项目一定要保留串口打印,它是你判断系统状态的唯一眼睛,等出了问题再临时加调试代码,效率会低很多。

防跌落的核心其实不只是那个超声波模块和几行判断代码,而是你建立的一套“从传感器数据到执行动作”的思维框架。这个框架以后做寻迹、避障、遥控车都能复用。我的建议是:先把基础版跑顺,再一步步往上加东西,不要一开始就贪多。等到小车能稳稳地在桌沿停住,你会觉得闲鱼上那个三十块的板子,花得太值了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进