模块熟悉

第 15 课:超声波模块——量个距离

套件里唯一能告诉你"多少厘米"的模块,代价是它得除以 2

我是吴老师。这节课的模块,是套件里唯一一个能测距离的。

你先回头看看手上这些传感器,它们的"性格"是这样的:

模块 它能回答的问题
光敏 有多亮?
按钮 按了没按?
红外 前面有没有东西?
超声波 前面那个东西,离我多少厘米?

看到最后一行的分量没有? 红外只会喊一声"有东西!",然后就没词了。超声波会给你一个具体的数字。

这就是为什么倒车雷达用它、扫地机器人用它、你后面第 28 课的"倒车雷达"和第 30 课的"坐姿提醒器"也用它——因为"有多远"这件事,只有它能说。

看视频时注意的 3 件事

这一课有配套的录屏,先看一遍再往下读。

视频教程超声波:四线接法、Trig/Echo 手感、读距离下载 ↗

第一,数一数它有几根针。四根。 别的模块都是三根,它多一根。

针脚 干什么
VCC 电源
Trig 触发——"喂,喊一声"
Echo 回响——"我听到了"
GND

为什么多一根? 因为它要又喊又听。别的模块一根信号线就够了(要么是读、要么是写),它得一个脚负责"喊"、一个脚负责"听",一个脚干不了两件事

第二,盯住 TrigEcho 分别接在哪。 这俩在代码里是分开填的。接反了不会烧坏,但读数一定是错的——通常就是一个 0,或者一个吓死人的巨大数字。

第三,注意手掌移远的时候数字怎么变。 慢慢把手往远处移,看数字一点点变大。你会看到它不是完全平滑的——偶尔会跳一下。这不是坏了,别慌,后面第四、五节专门讲为什么。

一、认识它:那两只"眼睛"

模块最显眼的,是板子正面两个圆圆的金属筒,像两只眼睛:一个里头是发射头,负责"喊";一个是接收头,负责"听"。

它俩长得一模一样,你分不出来也正常。 好在不用分——板子已经焊好了,你只管接那四根针。

顺带说一句:超声波是声音,不是光。 它的频率高到人耳听不见(所以叫"超声"),但它跟声音是一回事——需要空气才能传,真空里它就是个哑巴。

这一条很重要,后面讲"为什么读数会跳"的时候要用到。

二、原理:对着山谷喊一声

超声波的原理,是这节课最不难的部分——因为它就是你小时候干过的事。

你站在山谷这头,对着对面喊一声"喂——",过一会儿听到"喂……喂……喂……"的回声。你怎么知道山谷有多远?看你喊出去到听见回声,中间隔了多久。

  • 隔得 → 山谷
  • 隔得 → 山谷

超声波模块干的就是这件事,一秒能喊几十次:

超声波测距原理:Trig 发出触发脉冲,Echo 输出高电平,高电平时长就是声音往返的时间

看图,从上往下三步走:

第一步,Trig 上给它一个 10 微秒的高电平。 这叫"喊一声"。10 微秒短到你根本反应不过来,但对板子来说够了。

第二步,它开始喊,同时把 Echo 拉高。 Echo 高着不动的这段时间,就是"喊出去到听见回声"的时间。 声音回来了,Echo 就落回低电平。

第三步,程序量出 Echo 高电平持续了多久,换算成距离。

所以这一整套东西,本质上就是一个"掐秒表"的活。 模块负责喊和听,板子负责计时。

注意 Echo 给的是一个"持续时间",不是距离。 距离是你算出来的

这个区别很重要——它意味着:算错了,读数就是错的,而且是那种"看着挺像那么回事"的错。比如下面这个。

三、那个最经典的错误:别忘了除以 2

这是本节课最重要的一节。

声音是跑过去,再跑回来。它走的路,是你到物体的两倍。

所以:

距离 = 声速 × 时间 ÷ 2

那个 ÷ 2 是整节课最容易漏的东西。 而且它特别阴险——漏了它,程序照样跑,读数照样出,只是所有的数字都正好是真实距离的两倍。 你可能要过很久才发现:"咦,我明明量的是 30 厘米,它怎么说 60?"

来,一起算一遍,你就再也不会忘了。

声速在空气里大概是 340 米/秒。换成厘米每微秒就是 0.034 厘米/微秒——声音每走 1 微秒,前进 0.034 厘米。

那么,如果 Echo 高电平持续了 2000 微秒

步骤 算出来
声音走的总路程 2000 × 0.034 = 68 厘米
这是来回的路程 所以真实距离要除以 2
真实距离 68 ÷ 2 = 34 厘米

漏掉 ÷ 2 的话,你会得到 68 厘米。差了整整一倍。

代码长这样

C++
#define TRIG 4
#define ECHO 5

void setup() {
  Serial.begin(115200);
  pinMode(TRIG, OUTPUT);    // Trig 是板子往外发信号,所以是 OUTPUT
  pinMode(ECHO, INPUT);     // Echo 是板子读它,所以是 INPUT
}

void loop() {
  // 1. 先把 Trig 拉低一下,确保电平是干净的
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);

  // 2. 给它一个 10 微秒的高电平——"喊一声"
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);

  // 3. 量 Echo 高电平持续了多久(单位:微秒)
  long duration = pulseIn(ECHO, HIGH);

  // 4. 换算成厘米——注意那个 / 2!
  float cm = duration * 0.034 / 2;

  Serial.print(cm);
  Serial.println(" cm");
  delay(200);
}

注意 pinMode 那两行,它俩是反的:

  • TrigOUTPUT——板子往外发触发信号
  • EchoINPUT——板子往里读回响

这是你第一次见到一个模块上有两个信号脚、而且方向相反。 想不清的时候就用那句话问自己:这个脚上的信号,是板子发出去的,还是板子读进来的?

还有个细节值得你知道pulseIn 如果一直等不到回声,它会等一秒钟然后返回 0。

所以对着空气(没有东西反射)的时候,程序会明显卡一下——那不是死机,是它在傻等。这个现象你一定会遇到,先告诉你,免得到时候慌。

四、为什么读数会跳

第一次跑起来,你大概率会有点失望:这个数字怎么不安分? 手不动,它也在 33、34、33、35、34 之间晃,有时候还蹦一个 200 出来。

这是超声波的"天性",不是你的代码有问题。 三个原因:

第一,物体表面是什么材质。 声音遇到软的、多孔的东西——衣服、毛巾、海绵、窗帘——会被吸进去,几乎不反射。

声音被吸到哪去了? 变成了材料内部的一点点热。这就是为什么录音棚墙上贴满了软包——不是为了好看,是为了不让声音弹来弹去。

第二,角度。 超声波不是一根直线,它是喇叭形散出去的。物体正对着它,回波最强;歪一点,回波就弱一截

第三,斜面和尖角。 声音打到一个斜面上,会像台球一样弹到别处去,根本不往回来。尖角更惨——它把声音散射到四面八方。

所以别拿单个读数做判断。 想让读数稳一点,最简单的办法是连续读几次,去掉最大最小,取中间那几个的平均。这个技巧你在做倒车雷达的时候一定会用到——总不能让蜂鸣器跟着读数一起抽风吧。

这跟第 13 课麦克风那节的思路是同一个:传感器的原始数据都是毛的,变干净是你的活,不是传感器的活。

五、它有量程:太近测不出,太远也测不出

太近——测不出。 物体贴到模块脸上了(大概两三厘米以内),它反而读不出来,或者给你个乱数。

为什么? 因为它刚"喊"完,还没切换到"听"的模式,回声就已经回来了——自己喊的声音还没停,怎么听得见回声? 这个区域有个专门的名字,叫盲区

太远——超出量程。 一般这类模块的上限在四米左右(具体多少看你的模块,以实测为准)。再远,声音散得太厉害,回来的那点动静它听不见。这也解释了 pulseIn 那个"等一秒返回 0"——对着墙外或者天花板,读数就会是 0,或者一个明显不靠谱的大数。

所以你的程序里迟早要加一句"这个是有效读数吗"的判断:比如小于 2 厘米、大于 400 厘米的,直接丢掉不算。这一步不做,你的倒车雷达会被一个野数据吓得乱叫。

六、动手:拿尺子验一验

这节课的动手环节,是我最喜欢的——因为你能自己验证它对不对。

第一步:接线。

超声波 接到
VCC 3.3V
GND GND
Trig GPIO4
Echo GPIO5

第二步:跑上面的代码,打开串口监视器,用手在它前面晃。 看数字跟着你的手变大变小。

第三步——这一步是重点:把尺子拿出来。把一本书立在桌上,量出它到模块的距离,先量 10 厘米,然后看串口里读出来是多少。

  • 对上了? 恭喜,你刚才亲手验证了一个物理公式。这不是"教程说它对",是你自己量出来的。
  • 差了正好一倍? 那去看看公式里那个 ÷ 2 是不是漏了。
  • 差一点点(你量 10 厘米,它说 12)? 正常。"你眼睛看到的位置"和"模块认定的位置"本来就不完全是一回事,加上声速是个近似值、温度也有影响,百分之几的误差很正常。

换几个距离再量:20 厘米、30 厘米、50 厘米,画个小表格记下来。你会发现越远误差越大——声音散得越开,回波越弱,越容易判早或判晚。

第四步:换材料挡。 手、书本、衣服、一块毛巾——同样放在 20 厘米的地方,读数一样吗?衣服和毛巾多半会让你读不到,或者数字跳得厉害。

做完这一步你就明白了一件事:超声波的"准",是"在特定条件下的准"。 换个材质、换个角度,它就不一样了。这不叫"不准",这叫"你要知道它的边界在哪"。

这跟第 14 课结尾那句是一回事——先搞清楚一个传感器的物理极限,再动手。

七、读数一直是 0,怎么查

这个故障你八成会碰到,我提前把排查顺序给你。

不用串口只能靠猜,用串口能直接看到数值

看这张图:不用串口,你只能对着一个不动的数字猜;用了串口,你能直接看到数值。第 7 课那句"先打印,再判断",在这儿就是救命的话。按顺序查,别跳步:

顺序 查什么 怎么查
1 Trig / Echo 接反了吗 这是最常见的。对调一下试试,十秒钟的事
2 VCC / GND 对吗,板上灯亮吗 灯不亮,后面全白搭
3 前面有东西吗?是不是太远了 对着天花板测,当然读不到
4 是不是太近了 挪到 5 厘米以外再试
5 物体是不是软的、斜的 换本书、正对着再试
6 最后才怀疑代码 代码里的 Trig/Echo 编号跟实际接线对得上吗

注意最后一行——代码排在最后,不是因为它不重要,是因为前面的更常见。

"先测,再猜"(第 5 课)、"先打印,再判断"(第 7 课)——这两句话你从第 5 课听到现在,今天终于用在一个真正的"疑难杂症"上了。

你以后面对的任何一块不工作的板子,排查顺序都是这个套路:先看电,再看线,再看信号,最后才改代码。

自测

单选题超声波模块比别的模块多一根线,多出来的那根是干什么的?
单选题程序量出 Echo 高电平持续了 2000 微秒,物体大概在多远?(声速按 340 m/s)
单选题用毛巾或者衣服挡在模块前面,读数经常不对。为什么?
问答题超声波读出来一直是 0,你的排查顺序是什么?为什么代码排在最后?
拓展声速是"340 米/秒",可这个数其实不是固定的。什么东西会影响它?影响有多大?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"声速 温度 公式 331+0.6t"。想一想:冬天在室外测、夏天在室外测,同一个距离读数会差多少?这算不算一个"你必须知道的误差来源"?

拓展扫地机器人一边走一边测距,它是怎么做到不撞墙的?为什么很多扫地机器人后来改用激光了?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"扫地机器人 激光雷达 LDS 超声波 避障"。想一想:超声波是喇叭形散出去的,这对"看清一个桌腿"来说是好事还是坏事?

本节你要动手做的事

  • [ ] 认清四根针VCCTrigEchoGND接之前先断电
  • [ ] 接好线TrigGPIO4EchoGPIO5用一个本子记下你接的是哪两个脚
  • [ ] 跑通读数:手在它前面慢慢移动,看数字跟着变
  • [ ] 拿尺子验证:量 10 / 20 / 30 / 50 厘米,记下读数和实际值的差
  • [ ] 故意漏掉 ÷ 2:把代码改成 duration * 0.034,看读数变成什么样——亲眼见一次,以后就忘不了
  • [ ] 换材料:手、书本、衣服、毛巾,同样距离各测一次,看哪个读不出来
  • [ ] 试一次"没有东西":对着空气测,看程序卡一下然后打出 0(这就是 pulseIn 在傻等);做完自测题

这节课你第一次拿到了一个"带单位"的读数。

前面所有的传感器给你的都是"相对值"——光敏读 3000,什么意思?不知道,得看你那个房间。而超声波说的是"34 厘米",这是个谁都听得懂的数。

代价是:这个数得你自己算,而且算错了它不会报错。

所以这节课真正教你的,不是 pulseIn 这个函数,是"换算"这件事——一个物理量,从传感器到你手上,中间隔了几步?每一步有没有可能出错?那步 ÷ 2 有没有漏?

这个习惯你后面会一直用。 比如下一课的温度,DHT11 给你的是一串二进制位,你得知道怎么把它读成"26.5°C",不然读出来就是个莫名其妙的整数。

而且你今天验证公式的方式,是拿尺子量。 这件事听起来特别土——但它就是硬件工程的样子。 没有人靠"我觉得应该对"来验收,都是拿个东西量一下。

从今天起,你手上那块板子有了"感知空间"的能力。 这是它第一次知道"世界有多大"。

下一节我们学温湿度传感器 DHT11——它跟今天这个正好相反。

超声波是你自己算的,算错了也没人管你;DHT11 是模块自己算好了,直接告诉你"26.5 度"。听起来省事多了,但它有另一套脾气——它用一种叫"单总线"的方式跟你说话,讲错一个字,它转身就走。

下节课见。