用 Arduino 驱动 HC-SR04 很简单:给它接 5V 供电,在 TRIG 上发一个 10 µs 的高电平脉冲,然后用 pulseIn() 测量 ECHO 保持高电平的时间。距离(厘米)= 这个微秒数 ÷ 58。声音一来一回要除以 2 这件事,已经算进 58 里面了——所以这个数字第一眼看上去才那么莫名其妙。
HC-SR04 的四个引脚各自负责什么?
HC-SR04 的四个引脚顺序是固定的——VCC、TRIG、ECHO、GND——丝印就印在排针旁边,有些批次印在正面,有些印在背面。VCC 和 GND 是电源;TRIG 是你去驱动的输入脚;ECHO 则是模块反过来驱动给你的输出脚。
HC-SR04 是 5V 器件:数据手册标注的工作电压是 DC 5V,工作电流 15 mA。这点负载很轻,Uno 自带的 5V 电源轨就能轻松供上——这一点和我们180°、270°、360° 舵机对比教程里那些舵机完全不同。而这个 5V 的规格,一旦离开 Uno 就开始变得要紧,下文的 ESP32 部分会讲到。
所需材料
HC-SR04 怎么接到 Arduino Uno?
HC-SR04 接到 Uno 上,正好需要四根公对母杜邦线。TRIG 和 ECHO 可以接任意两个数字引脚——下面用 9 和 10 只是约定俗成而已。
| HC-SR04 引脚 | Arduino Uno 引脚 | 备注 |
|---|---|---|
| VCC | 5V | 必须是 5V——3.3V 引脚带不稳经典款 HC-SR04 |
| TRIG | D9(任意数字引脚) | OUTPUT——由你发脉冲 |
| ECHO | D10(任意数字引脚) | INPUT——模块把它拉成高电平 |
| GND | GND | 共地不是可选项 |
TRIG 和 ECHO 接反,读数会一直是 0,接回来就好了。通常烧不坏,但原理上并不是完全无害:这时 Arduino 的 TRIG 输出脚接到了模块的 ECHO 输出脚上,两个输出一旦电平不一致,就会互相对顶。

HC-SR04 最基础的 Arduino 代码是什么样的?
HC-SR04 不需要任何库——三次 digitalWrite() 加一次 pulseIn() 就是完整的驱动。上传程序,把串口监视器波特率设成 9600,然后对着传感器挥挥手。
// HC-SR04 基础测距程序 - Arduino Uno
// TRIG 接 9 号引脚,ECHO 接 10 号引脚。
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
void loop() {
// 1. 先给一个干净的低电平,再发出 10 us 的触发脉冲。
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// 2. 测量 ECHO 保持高电平的时间,单位微秒。
// 30000 us 的超时上限 = 目标超过约 5 m 就放弃
// (30000 / 58 = 517 cm;声音本身来回跑了约 10 m)。
// 这个值也刚好低于模块什么都没测到时发出的约 38 ms 脉冲。
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
// 3. 换算。58 = 每厘米对应的往返微秒数。
if (duration == 0) {
Serial.println("No echo - out of range, or nothing reflected back");
} else {
float distanceCm = duration / 58.0;
Serial.print(distanceCm);
Serial.println(" cm");
}
delay(100); // 数据手册要求两次测量之间至少隔 60 ms
}
为什么要除以 2,58 又是从哪来的?
HC-SR04 的 ECHO 脉冲量的是一趟往返行程,这一个事实就解释了两个常数的来历。TRIG 收到 10 µs 脉冲后,模块会发出 8 个周期的 40 kHz 超声波,然后把 ECHO 拉高,高电平持续的时间正好等于这串声波跑到目标再跑回来所花的时间。
常温空气中声速约为 343 m/s,也就是每微秒约 0.0343 cm。声波把这段距离走了两遍,所以距离 =(时间 × 0.0343)÷ 2 —— 也就是时间 ÷ 58.3,数据手册把它取整成 ÷ 58。(数据手册在 ÷ 58 旁边又写了 340 m/s,那样算出来应该是 ÷ 58.8——它自己就有点前后不一致。)也有不少示例程序写成 duration / 29.1 / 2,等于 ÷ 58.2。这几种写法彼此相差都在 1% 以内,而气温对声速的影响比这还大。

想看视频把整个流程走一遍?这支影片从头到尾讲得很完整:
HC-SR04 的实际测距范围是多少?什么情况下会测不出来?
HC-SR04 的标称测距范围是 2 cm 到 400 cm,测距精度最高可达 3 mm,有效测量角度小于 15°。那个 15° 的锥形波束,正是新手从来不会考虑的一项规格。
| 规格 | 数值 | 对你的项目意味着什么 |
|---|---|---|
| 测距范围 | 2 cm – 400 cm | 2 cm 以下是盲区:8 个周期的 40 kHz 声波光是发射就要 200 µs,而 2 cm 的往返只需约 116 µs,回声在模块还没发完时就已经回来了 |
| 精度 | 最高 3 mm | 最理想的情况:平整坚硬、正对传感器的目标 |
| 测量角度 | 小于 15° | 是一个锥形波束,不是激光——它测到的是锥内最近的那个物体 |
| 工作电压 / 电流 | 5V DC / 15 mA | 和舵机不同,挂在 Uno 的 5V 引脚上很安全 |
| 发射频率 | 40 kHz,8 个周期 | 人耳听不见;相邻的传感器之间会互相串扰 |
| 测量周期 | 最短 60 ms | 发得太快,上一串声波还没散尽,残留的回声就会混进下一次测量 |
| 模块尺寸 | 45 × 20 × 15 mm | 外壳开孔要按两个换能器圆筒整体来量 |
有四类目标物是代码怎么写都救不回来的。柔软或纤维状的表面——布料、泡棉、毛绒玩具——会把声波吸收掉,而不是反射回来。倾斜的表面则像镜子反射光线一样,把声波弹到旁边去。
比波束锥还小的物体会被整个漏掉,而 2 cm 以内的东西落在盲区里。在怪罪程序之前,先看看你到底把传感器对着什么。

怎么消除莫名其妙的 0 读数和跳数?
HC-SR04 什么都没测到的时候,并不会让 ECHO 保持低电平——数据手册写的是,这时该引脚会输出一个 38 ms 的高电平脉冲。如果你不设超时去读它,就会得到约 38000 µs,换算成 655 cm 这种似是而非的数值,而不是一个一眼可辨的错误。上面那段程序里 30000 µs 的超时刚好卡在 38 ms 之下,所以前方无障碍物时会干净利落地返回 0。至于大家常抱怨的“卡一秒”,那是另一种故障:当 ECHO 根本没有拉高过——接线松了、没有共地、模块坏了——pulseIn() 就会按默认的一秒一直阻塞在那里。两种情况都一样:请显式传入超时参数。
至于跳数,做法是连读几次,取中间那个值。中位数剔除离谱值的能力比平均值强得多,因为单独一次坏读数拉不动结果。
// 5 次读数取中位数 - 剔除偶尔出现的离谱值。
// 沿用上面那段程序里的 TRIG_PIN / ECHO_PIN 常量。
long readOnce() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
return pulseIn(ECHO_PIN, HIGH, 30000);
}
float readMedianCm() {
long s[5];
for (int i = 0; i < 5; i++) {
s[i] = readOnce();
delay(60); // 遵守 60 ms 的测量周期
}
for (int i = 1; i < 5; i++) { // 插入排序,5 个元素
long k = s[i];
int j = i - 1;
while (j >= 0 && s[j] > k) { s[j + 1] = s[j]; j--; }
s[j + 1] = k;
}
if (s[2] == 0) return -1.0; // 中间那次读数超时了
return s[2] / 58.0;
}
NewPing 库把这些事全都替你做了:它会自动设置超时,完全不用 pulseIn(),还自带一个 ping_median() 辅助函数。学习阶段、只接一个传感器的话,纯 pulseIn() 完全够用;等你要同时跑好几个传感器,或者程序承受不起阻塞循环时,再上 NewPing。
HC-SR04 能配 ESP32 之类的 3.3V 板子用吗?
HC-SR04 用 3.3V 触发通常是可以的——数据手册把 TRIG 定义为 TTL 输入,3.3V 越得过 TTL 门限——不过对于板载单片机采用 5V CMOS 输入电平的模块来说,这个余量偏紧。真正的问题在 ECHO:它输出的是完整的 5V,而 Espressif 给 ESP32 的 GPIO 标定上限是 3.6V,并明确要求超过这个值就要加分压电阻。把 ECHO 直接接到 ESP32,就是让这个引脚超规格工作。
解决办法是只在 ECHO 上加一组两电阻分压:ECHO 到 GPIO 之间串 1 kΩ,同一个节点再用 2 kΩ 接到 GND。这样 5V 就降成 5 ×(2 ÷ 3)= 3.33V。TRIG 直接接,不需要分压。另一个办法是买 HC-SR04P 版本,它的工作电压是 3V–5.5V;经典款 HC-SR04 就是 5V 器件。ESP32 干脆连认都认不出来?那是另一回事——请看我们的ESP32 无法识别、上传失败修复教程。
真实顾客常踩的坑
在我们卖的所有传感器里,HC-SR04 收到的提问几乎是最多的,尤其来自学校 RBT 课程和毕业设计的同学,而且翻来覆去就是那几种翻车方式,每周都会再来一遍。它们没有一个是传感器本身的问题。
最常见的一种,是模块上的引脚丝印褪色、缺失或者几乎看不清——排针旁边那行小字经不起反复摩擦。引脚顺序永远是 VCC、TRIG、ECHO、GND,所以不要猜,直接照着上面那张表来读,先把这一条排除掉,再去判断模块是不是坏了。
另一个常客是读数刚好翻倍。那就是漏了 ÷2——要除以 58,不是 29。紧随其后的是程序看起来卡住或一顿一顿的,这几乎总是因为 pulseIn() 调用时没有传超时参数,于是只要 ECHO 完全没有拉高过——接线松动、没有共地或者模块已坏——它就会按默认的一整秒一直阻塞。
然后是那 15° 的锥形波束。很多人只顾着对准目标,忘了桌沿、墙面或者自己做的外壳也落在波束里,于是传感器老老实实地报出了那个更近的东西。安装时要让模块面对空旷的方向。
最后是地线。传感器由另一路电源供电,信号却接进 Arduino,两边没有共地,读出来的数就跟纯噪声一样——把地线连起来。我们的 LM2596 降压模块调 5V 教程讲了怎么做出一条干净的 5V 电源轨。
常见问题 FAQ
HC-SR04 能测多远?
2 cm 到 400 cm,条件好的时候精度可达约 3 mm。2 cm 以内是盲区——读数不再跟着变化,会贴在模块的下限附近——而超过大约 4 m,回声就弱到检测不到了。
为什么我的 HC-SR04 一直读 0 或者一直是同一个数?
一直是 0,说明 pulseIn() 超时了——通常是 TRIG 和 ECHO 接反、没有共地,或者目标物把声音吸收掉了。读数卡在 655 cm 附近,则是模块手册里写明的“无障碍物”输出,也就是那个 38 ms 的 ECHO 脉冲,意思是它压根什么都没看到。数值一直不变也可能是因为你发得比 60 ms 的测量周期还快,于是每次读到的仍然是上一串声波的回声。
用 HC-SR04 一定要装库吗?
不用。三次 digitalWrite() 加一次 pulseIn() 就是全部驱动。当你要同时跑好几个传感器,或者需要非阻塞的时序时,NewPing 才值得加进来,但它始终是可选的。
HC-SR04 可以直接接到 ESP32 上吗?
TRIG 可以,ECHO 不行。ECHO 输出 5V,而 ESP32 的 GPIO 并不耐 5V,所以要在 ECHO 上加一组 1 kΩ / 2 kΩ 分压电阻,或者干脆改用 3V–5.5V 的 HC-SR04P 版本。
为什么我测出来的距离刚好是实际的两倍?
你除的是 29,不是 58。ECHO 脉冲涵盖了声音一去一回的完整行程,所以这段往返必须减半——把微秒数除以 58 才得到厘米。
最后更新:2026年7月。有问题?WhatsApp 联系我们。



Ultrasonic Sensor HC-SR04 Ultrasonic Range Detection Distance Finder Obstacle avoidance For Arduino Application
Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
MB102 Breadboard 170 400 830 Holes Breadboard Donut Board Arduino Prototype Multi Color - BREADBOARD (830 HOLES)
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-F 20CM