超声波测距实战:用Arduino IDE玩转HC-SR04,从原理到代码一次讲透
你有没有想过,机器人是怎么“看见”障碍物的?其实它并不靠眼睛,而是靠各种传感器来感知世界。其中最简单、最直观的一种方式就是——超声波测距。
今天我们就来动手实现一个经典项目:使用Arduino IDE驱动HC-SR04超声波模块进行距离测量。不堆术语、不甩公式,咱们一步步拆解这个看似神秘的过程,让你真正搞懂“它是怎么知道前面有东西的”。
为什么选HC-SR04?因为它够“傻瓜”
在众多测距方案中,红外、激光、毫米波雷达各有千秋,但如果你是初学者,那HC-SR04几乎是入门首选。原因很简单:
- 便宜:十几块钱就能买到;
- 易用:只有四个引脚,接线清晰;
- 兼容性强:5V供电,和Arduino完美匹配;
- 精度够用:室内短距离检测完全没问题。
别看它长得像两个小喇叭,其实一个是“喊话筒”(发射器),一个是“听回音的耳朵”(接收器)。它的工作原理,跟蝙蝠飞行时避障一模一样。
它到底是怎么测出距离的?
我们先抛开代码和电路,想象这样一个场景:
你站在山谷里大喊一声:“喂——!”
过一会儿,你听到了回声。
根据声音来回的时间,你能估算对面山壁有多远。
超声波测距就是干这件事,只不过把人换成了芯片,把“喂”换成40kHz的高频声波脉冲。
四步走完一次测距
- 发命令:Arduino给HC-SR04的
Trig引脚发一个持续10微秒的高电平信号,相当于说:“准备好了,我要开始喊了!” - 自动喊话:模块收到指令后,自己发出8个40kHz的超声波脉冲。
- 等回音:这些声波撞到前方物体后反弹回来,被模块的接收端捕捉。
- 回报时间:模块通过
Echo引脚输出一个高电平信号,这个高电平持续的时间,正好等于声波往返所需的时间。
接下来的事就交给Arduino了:算时间 → 换算成距离 → 输出结果。
声音跑得有多快?怎么算距离?
空气中声音的速度大约是340米/秒,也就是0.034厘米/微秒。
假设我们测得Echo高电平持续了5800 微秒,这意味着声波花了5800μs完成了一趟“去+回”的旅程。
所以单程时间是:
$$
\frac{5800}{2} = 2900 \, \mu s
$$
再乘以速度:
$$
2900 \times 0.034 \approx 98.6 \, cm
$$
于是我们就知道,前方约98.6厘米处有个东西。
✅ 小结公式:
$$
\text{距离(cm)} = \frac{\text{duration} \times 0.034}{2}
$$
其中duration是pulseIn()读出来的回波时间(单位:微秒)
接线很简单,但细节决定成败
| HC-SR04 引脚 | Arduino 引脚 | 说明 |
|---|---|---|
| VCC | 5V | 提供电源 |
| GND | GND | 必须共地!否则通信失败 |
| Trig | 数字引脚9 | 触发信号输入 |
| Echo | 数字引脚10 | 回波信号输出 |
📌重点提醒:
- 所有GND要接在一起,包括Arduino和外部电源的地;
- 如果你用的是ESP32、STM32这类3.3V主控,不能直接连Echo引脚!需要加电平转换或分压电阻,否则可能烧毁IO口;
- 电源尽量稳定,劣质USB线容易导致误读。
代码详解:每一行都在做什么?
下面这段代码,就是整个项目的灵魂。我们逐行解析,让你知其然更知其所以然。
const int trigPin = 9; // Trig 接数字9脚 const int echoPin = 10; // Echo 接数字10脚 long duration; // 存储回波时间(微秒) float distance; // 计算后的距离(厘米) void setup() { pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); Serial.begin(9600); // 启动串口,用于打印数据 } void loop() { // 步骤1:发送触发信号 digitalWrite(trigPin, LOW); delayMicroseconds(2); // 稳定状态 digitalWrite(trigPin, HIGH); delayMicroseconds(10); // 至少保持10μs高电平 digitalWrite(trigPin, LOW); // 步骤2:读取Echo高电平持续时间 duration = pulseIn(echoPin, HIGH, 30000); // 步骤3:计算距离 distance = (duration * 0.034) / 2; // 步骤4:串口输出 Serial.print("Distance: "); Serial.print(distance); Serial.println(" cm"); delay(100); // 控制采样频率,避免太快刷屏 }🔍 关键函数解析
pulseIn(pin, value, timeout)
这是本项目的核心函数,作用是:
“请帮我测量一下,从现在开始,
pin引脚上出现value电平(HIGH或LOW)会持续多久。”
- 第三个参数是超时保护。比如设为30000μs(即30ms),意味着最多等30毫秒,如果还没收到信号就返回0,防止程序卡死。
- 实际最大测量距离对应约为5米(因为声波来回要150ms左右才能到5米),所以设置30~50ms比较安全。
为什么要先拉低Trig?
虽然手册没强制要求,但在实际编程中,先将Trig置为LOW是一个好习惯。这能确保每次触发前信号处于已知状态,避免因上次操作残留电平造成误触发。
delay(100)有必要吗?
有!官方建议两次测距间隔不少于60ms,主要是为了让模块内部完成一次完整的收发周期。我们延时100ms,既满足要求,又能控制串口输出节奏,不至于刷屏太快看不清。
常见问题与调试技巧(踩过的坑都给你填上)
❌ 问题1:串口一直输出0或者-1
- 可能原因:接线错误,尤其是GND没接好。
- 解决方法:重新检查所有连线,可用万用表通断档确认;尝试用LED串联电阻接到Echo脚,看看是否有短暂亮起。
❌ 问题2:数值跳变严重,忽大忽小
- 可能原因:环境干扰、多次反射、目标表面吸音(如布料)。
- 解决方法:
- 加软件滤波:比如连续测5次取平均值;
- 或者设定合理阈值,过滤异常数据。
示例改进代码片段:
float readAverageDistance() { float sum = 0; for (int i = 0; i < 5; i++) { // 正常触发+读取流程 digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH, 30000); sum += (duration * 0.034) / 2; delay(10); } return sum / 5; }❌ 问题3:最大只能测到几十厘米?
- 检查点:
pulseIn()的timeout是否太小?默认可能是10000μs,对应不到1.7米。 - 修复:明确写出第三个参数,例如
pulseIn(echoPin, HIGH, 50000),支持测到约8.5米。
可以用来做什么?这些创意你也能做!
别以为这只是个小实验,它的应用场景比你想的丰富得多:
🚗 智能小车避障
配合电机驱动模块,当检测到前方<20cm有障碍物时,自动转向或刹车。
🗑️ 自动感应垃圾桶
人在靠近时,舵机打开桶盖,离开后自动关闭,卫生又方便。
📦 物料高度监测
放在仓库货架上方,实时监控货物堆放高度,防止溢出。
🧭 扫描式地图构建(SLAM雏形)
让超声波模块装在舵机上左右摆动,结合角度信息,就能画出简易环境轮廓图。
💡 进阶思路:加入温度传感器(如DS18B20),根据当前气温动态调整声速,提升测量精度。
写在最后:学会的不只是一个功能,而是一种思维方式
通过这次实践,你掌握的不仅仅是“如何让Arduino读出一个距离值”,更重要的是理解了一个完整的传感系统是如何工作的:
触发 → 感知 → 测量 → 计算 → 输出
这种“感知—处理—响应”的闭环逻辑,正是嵌入式系统和物联网设备的核心思维模式。
下次当你看到扫地机器人灵活绕开家具时,不妨想想:它背后是不是也有一个小小的超声波模块,在默默地“喊”和“听”?
如果你已经成功跑通了代码,欢迎在评论区晒出你的成果照片!如果有任何问题,也欢迎留言交流,我们一起debug,一起进步。