HC-SR04超声波测距接Arduino,时序公式全拆解

2026-11-13 07:20:06 10阅读

给机器人加一双能测距的"眼睛",HC-SR04 大概是绕不开的第一课:两支金属罐似的超声换能器,一块蓝色 PCB,价格只有激光 ToF 模块的零头,却能测 2 厘米到 4 米。它是靠声波而不是靠光工作的,所以透明玻璃这类激光容易直接穿过去的障碍物,反而难不倒它;代价是有盲区、读数会随温度漂。这篇把它的 Trig/Echo 时序、pulseIn 测距和换算公式一次讲清,代码与参数均取自 Makerguides 与 HowToMechatronics 两篇公开教程,可逐项核对。

HC-SR04超声波传感器接面包板实拍

先搞懂它怎么"听"出距离

HC-SR04 本质上是一套迷你声呐,工作原理和潜艇、和蝙蝠觅食是同一套逻辑。两侧的圆柱体一个是发射器(T)、一个是接收器(R):触发之后,发射器朝正前方发出一束 40kHz 的超声波——这个频率已经超出人耳上限,你听不见它"喊"了什么。声波碰到障碍物反射回来,被接收器接住。只要记下从"喊一声"到"听见回声"之间隔了多久,再乘以声速、除以二,距离就出来了。

为什么要除以二?因为声波跑的是一个来回:先飞到物体,再弹回传感器,Echo 给出的时间是两段路程的总和,除以二才是单程。把这个逻辑记牢,后面的公式全是水到渠成。

选型时用得上的关键规格,官方资料给得很齐,列表对着看:

项目参数
工作电压5V
工作电流15mA
工作频率40kHz
测距范围2cm 至 400cm
精度3mm
测量角度15° 以内
尺寸约 45×20×15mm

15mA 的功耗对 Arduino 板载稳压来说毫无压力,直接从 5V 引脚取电即可,不用单独供电。

引脚定义与接线

模块一共四个引脚,PCB 两边各留了安装孔,方便固定在小车前脸:

  • VCC:接 5V 供电;
  • Trig:触发输入,单片机往这里发一个 10 微秒的高电平脉冲,模块就开始发声;
  • Echo:回波输出,模块用高电平的持续时长告诉你声波飞了多久;
  • GND:接地。

HC-SR04引脚定义图

接 Arduino Uno 只需要四根线:VCC 接 5V,GND 接 GND,Trig 接 D9,Echo 接 D10。Trig 和 Echo 用任意数字引脚都行,代码里对应改一下就好——Makerguides 的例程用的是 D2 和 D3,HowToMechatronics 用的是 D9 和 D10,本质上没有区别。

Makerguides 还提到一种省引脚的接法:把 Trig 和 Echo 并到同一个引脚上,做成三针模式。同一个脚先设为输出发触发脉冲,再切成输入等回波,一个引脚当两个用,引脚紧张的小项目里挺实用。

HC-SR04与Arduino接线图

HC-SR04接Arduino Uno面包板接线图

Trig/Echo 时序:一次测距的完整过程

每次测距按固定剧本走。第一步,把 Trig 拉低两微秒左右,把引脚状态清干净;第二步,把 Trig 拉高至少 10 微秒——这是数据手册要求的触发脉宽,短了模块不搭理你;第三步,模块收到触发后发出一串 8 个周期的 40kHz 超声波脉冲。

第四步才是重点:8 个周期发完的瞬间,Echo 引脚被拉高,开始等回波;声波往返一圈回来时,Echo 拉低。所以 Echo 高电平的持续时长,就是声波跑一个来回的总时间,和时间成正比,测得越远高电平越长。

两个容易踩坑的边界要记住。一是超时:如果前方没有反射物,Echo 会一直撑到 38 毫秒才回低,写代码时要给 pulseIn 设个超时或者判断这种情况,否则程序会干等。二是间隔:两次测距之间至少隔 29 毫秒,让上一轮的声波余响彻底散掉,不然新旧回波叠在一起,读数会抽风。后面例程里的 delay(50) 就是这个用意。

超声波传感器测距工作原理示意图

pulseIn 测距与换算公式

Arduino 自带的 pulseIn() 正好干计时这个活:它盯着指定引脚,等电平跳变并开始计时,直到电平变回去,返回脉冲宽度,单位微秒。有了微秒数,换算公式一行搞定:

距离(厘米)= 脉冲时长(微秒)× 0.034 ÷ 2

0.034 是声速的换算系数:20°C 时声速约 343 米/秒,折算过来是 0.0343 厘米每微秒;除以 2 是因为声波走了一个来回。代入验证一下:pulseIn 返回 2000 微秒,0.0343 × 2000 ÷ 2 = 34.3 厘米,量纲和数值都合理。

把 HowToMechatronics 的完整例程原样贴出来,触发、计时、换算、输出一条龙:

/*
  Ultrasonic Sensor HC-SR04 and Arduino Tutorial

  by Dejan Nedelkovski,
  www.HowToMechatronics.com

*/
// defines pins numbers
const int trigPin = 9;
const int echoPin = 10;
// defines variables
long duration;
int distance;
void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  Serial.begin(9600); // Starts the serial communication
}
void loop() {
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2;
  // Prints the distance on the Serial Monitor
  Serial.print("Distance: ");
  Serial.println(distance);
}

烧进去打开串口监视器(波特率 9600),手在传感器前晃一晃,Distance 后面的数字就会跟着变。想显示成英寸也有现成系数:脉冲时长乘 0.0133 再除以 2 即可。

讲究一点还可以做温度补偿。声速对温度挺敏感:20°C 时约 340 米/秒,零下 20°C 就掉到约 315 米/秒,差了快一成。常用近似式是 331.3 加 0.606 乘以摄氏温度;HowToMechatronics 用 DHT22 的版本还把湿度项也算了进去。测三四米的远距离时,这个修正是有感观的,近距离可以忽略。

加块 LCD,做成独立测距尺

不想依赖电脑串口,接一块 1602 液晶就能做成独立的电子尺。HowToMechatronics 的 LCD 例程同时输出厘米和英寸两行读数,代码同样原样保留:

/*
  Ultrasonic Sensor HC-SR04 and Arduino Tutorial

  by Dejan Nedelkovski,
  www.HowToMechatronics.com

*/
#include "LiquidCrystal.h" // includes the LiquidCrystal Library
LiquidCrystal lcd(1, 2, 4, 5, 6, 7); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)
const int trigPin = 9;
const int echoPin = 10;
long duration;
int distanceCm, distanceInch;
void setup() {
  lcd.begin(16, 2); // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}
void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distanceCm = duration * 0.034 / 2;
  distanceInch = duration * 0.0133 / 2;
  lcd.setCursor(0, 0); // Sets the location at which subsequent text written to the LCD will be displayed
  lcd.print("Distance: "); // Prints string "Distance" on the LCD
  lcd.print(distanceCm); // Prints the distance value from the sensor
  lcd.print(" cm");
  delay(10);
  lcd.setCursor(0, 1);
  lcd.print("Distance: ");
  lcd.print(distanceInch);
  lcd.print(" inch");
  delay(10);
}

这段例程里 LiquidCrystal 对象用的引脚是 1、2、4、5、6、7,与测距的 D9/D10 并不冲突,照着接就行。

盲区、误差与实用建议

第一是盲区。最小测距 2 厘米,比这更近的物体它测不了——发射和接收挨得太近,回波和发射余振在时间上分不开。装在小车最前端时,注意别让障碍物有机会贴脸。

第二是波束角和材质。15° 的测量角度意味着它看的是一张锥形"视野",锥外的物体它看不见;斜面会把声波反射到别处去,海绵、布料这类软材料会吸声,这些情况读数都会飘或者丢。需要更稳的输出,用 NewPing 库的 ping_median() 做中值滤波,默认采 5 次丢掉离谱值再取平均,效果立竿见影。

用 NewPing 的话,主循环还能再瘦一圈,一行 ping_cm() 直接拿厘米数,超出量程返回 0,判断起来省事:

#include "NewPing.h"

#define TRIGGER_PIN  9
#define ECHO_PIN     10
#define MAX_DISTANCE 400 // Maximum distance we want to measure (in centimeters).


NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(50);                    // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.

  int distance = sonar.ping_cm(); // Send ping, get distance in cm and print result (0 = outside set distance range)

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println("cm");
}

第三是环境适配。HC-SR04 不防水,潮湿、粉尘大的场合换带防水壳的 JSN-SR04T 更省心;如果应用里温度变化极端、声速修正都嫌麻烦,Makerguides 也提示可以看 SHARP 的红外测距方案(GP2Y0A21YK0F 一类),红外方案基本不受气温影响。

总的来看,四根线、十几行代码,就能给小车或者机器人加上最基础的避障感知。把 10 微秒触发、8 周期 40kHz、Echo 计时、除以二这四件事记牢,HC-SR04 这块小板子就真正为你所用了。


作者:电机传感器师
链接:https://www.aiddithome.com/p/ece41e4498dce.html
本作版权归作者所有,未经允许请勿转载。若存在违规行为,您可以点击 “举报”。


登录 后发表评论
5条评论
好说好说
1楼 · 30分钟前

潮湿车间里用这模块就是遭罪,后来换了带防水壳的 JSN-SR04T 才消停,文中提到这个替代款很实在。

电烙铁爱好者
2楼 · 30分钟前

之前用这块模块做避障,小车老在墙角抽风,后来才明白是两次触发间隔太短,声波余响没散干净。照这篇说的把循环里加上 delay(50) 之后稳多了。另外 38 毫秒超时这个点讲得很到位,pulseIn 不设超时的话,前方没东西时程序真的会卡一拍,远距离测距建议都处理一下。

第三次终于亮了
3楼 · 30分钟前

2 厘米盲区这个是真的坑,我装车头第一次贴墙就翻车了,读数直接没了,哈哈。

拆开看看再说
4楼 · 30分钟前

NewPing 的 ping_median 中值滤波确实好用,默认采五次去离谱值,读数立马不飘了,推荐新手直接上这个库。

刷视频进来围观
5楼 · 30分钟前

除以二这事我当年想了半天,原来声波走个来回,恍然大悟。