news 2026/6/2 20:05:46

基于Arduino与HY-SRF05的超声波测距系统:从原理到实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Arduino与HY-SRF05的超声波测距系统:从原理到实践

1. 项目概述与核心思路

超声波测距,听起来挺高大上,但说白了就是让硬件“喊一嗓子”然后听回声,跟蝙蝠和海豚的定位原理差不多。我在做智能小车、自动门或者一些简单的安防项目时,经常需要用到这种非接触式的距离测量方案。市面上传感器很多,这次我选的是HY-SRF05,搭配一块I2C接口的LCD屏和经典的Arduino UNO,来搭建一个实时显示距离的测距系统。这个组合的好处是成本低、接线简单,代码逻辑清晰,非常适合新手入门或者快速验证一个距离检测的原型。

整个项目的核心流程可以概括为:Arduino给传感器一个“开始”信号,传感器发射超声波,声波碰到物体反射回来,传感器接收到后通知Arduino,Arduino计算中间花费的时间,最后根据声速算出距离,并显示在LCD屏上。听起来步骤不少,但拆解开来每一步都很直接。我会重点聊聊为什么选这些组件,以及在实际焊接和编程中,那些教程里通常不会提的“坑”和技巧。无论你是刚接触Arduino的学生,还是想给某个小设备增加测距功能的开发者,跟着这套方案走,都能少走不少弯路。

2. 核心组件选型与电路设计解析

2.1 为什么是HY-SRF05和I2C LCD?

市面上超声波传感器常见的有HC-SR04和HY-SRF05。我这次用HY-SRF05,它俩原理和用法几乎一样,但HY-SRF05的引脚多一个(OUT),不过我们通常只用VCC、GND、Trig、Echo这四根线。选择它没什么特别原因,手边正好有,而且实测下来性能稳定,最大测距能到4米左右,对于大多数室内场景足够了。这里有个细节要注意:不同批次或厂家的传感器,其测量精度和抗干扰能力可能有细微差别,如果对精度要求极高(比如毫米级),可能需要校准或选择更专业的型号。

再来说说显示部分。直接用1602或2004这种标准LCD屏,需要接一大堆线(6-7根数据线+电源线),不仅占用了大量Arduino的IO口,面包板上也会显得非常杂乱。所以我强烈推荐加一个I2C转接板。这个小小的模块焊在LCD屏的背面,通过I2C协议与Arduino通信,只需要接4根线(VCC、GND、SDA、SCL)就能搞定所有显示控制。这不仅仅是省了几根线,更重要的是解放了宝贵的数字IO口,让它们可以用于连接其他传感器或执行器。I2C地址通常是0x27或0x3F,买来的模块需要确认一下,后续编程会用到。

2.2 电路连接详解与避坑指南

接线图看起来简单,但接错了轻则没反应,重则烧坏元件。下面我按模块拆解,并附上每个连接背后的理由:

1. Arduino UNO供电与基础准备首先,用USB线(A公头转B公头)给Arduino供电并上传程序。如果项目需要移动或脱离电脑运行,可以后期改用9V电池通过DC接口供电。在面包板布局时,我习惯把Arduino放在一侧,用两根长跳线将它的5VGND引到面包板两侧的电源轨上,这样整个系统的电源和地就统一了,后续所有模块的VCC和GND都从电源轨取电,清晰又安全。

2. HY-SRF05传感器接线

  • VCC -> 5V:传感器工作电压是5V,接在Arduino的5V输出或面包板的5V电源轨上。
  • GND -> GND:共地是关键,所有模块的GND必须连接到同一个参考点。
  • Trig (触发) -> 数字引脚12:这个引脚由Arduino控制,用于发送一个短脉冲启动传感器。
  • Echo (回声) -> 数字引脚8:这个引脚是传感器的输出,它会输出一个高电平脉冲,其宽度与超声波飞行时间成正比。

注意:有些教程或模块可能标注为“Trig”和“Echo”,而有些可能标“T”和“R”。务必以你手上传感器的丝印为准。另外,ECHO引脚输出是5V电平,可以直接与Arduino的5V逻辑电平引脚连接,无需电平转换。

3. I2C LCD模块接线这是最省事的部分:

  • GND -> GND
  • VCC -> 5V
  • SDA -> A4:在Arduino UNO上,A4引脚复用为I2C的数据线(SDA)。
  • SCL -> A5:在Arduino UNO上,A5引脚复用为I2C的时钟线(SCL)。

重要提示:I2C通信对线路长度和干扰比较敏感。如果接线较长(超过20厘米)或旁边有电机等大电流设备,显示可能会乱码或不稳定。解决办法是尽量缩短连线,并在电源轨靠近模块处加一个0.1uF的陶瓷电容进行滤波。

4. 关于杜邦线的选择

  • 传感器到Arduino:使用公对公杜邦线,因为两端都是插针(Male)。
  • I2C模块到Arduino:使用公对母杜邦线,因为I2C模块上是插针,而Arduino引脚是插孔(Female)或需要插到面包板(使用公对公插到面包板,再用公对公从面包板连到Arduino也行,但更乱)。准备4根公对母线直接连接是最清爽的方案。
  • 辅助线:准备一些公对公线用于电源轨的扩展和连接。

把所有元件按这个思路接好,硬件部分就完成了。接完线务必先不要通电,按照图纸仔细核对三遍,特别是VCC和GND有没有接反,这是烧毁元件的头号杀手。

3. 代码实现与核心逻辑深度剖析

代码是项目的大脑,理解了每一行背后的原理,你才能灵活修改和调试。我将代码分成几个核心部分来讲解。

3.1 库引入与宏定义

#include <LiquidCrystal_I2C.h> #define PinToTrig 12 #define PinToEcho 8 #define BitRate 9600 float duration; float distance; LiquidCrystal_I2C lcd(0x27, 19, 4); // 关键参数:I2C地址,列数,行数
  • #include <LiquidCrystal_I2C.h>:这是驱动I2C LCD屏的核心库。在编译代码前,你必须通过Arduino IDE的库管理器(项目 -> 加载库 -> 管理库)搜索并安装“LiquidCrystal I2C”,作者通常是Frank de Brabander。没有这个库,编译器会报错。
  • 宏定义:用#define给引脚和波特率起别名是很好的编程习惯。一方面提高了代码可读性(一看PinToTrig就知道是触发引脚),另一方面,如果后期想换用引脚11和9,只需要修改这两个宏定义,而不必翻遍整个代码去替换数字。
  • LiquidCrystal_I2C lcd(0x27, 19, 4);:这是创建LCD对象。这里有三个关键参数:
    1. I2C地址 (0x27):这是模块的“门牌号”。最常见的是0x270x3F。如果你上传代码后LCD没任何反应(背光可能亮),首先怀疑地址不对。你可以用一个简单的I2C扫描程序来查找确切的地址。
    2. 列数与行数 (19, 4):这指定了LCD的规格。我用的是一块4行19列的屏。如果你用的是更常见的16x2屏,这里就要改成(0x27, 16, 2)。如果这里设置错误,可能会导致显示内容错位或无法显示。

3.2 初始化设置 (setup函数)

void setup() { pinMode(PinToTrig, OUTPUT); pinMode(PinToEcho, INPUT); Serial.begin(BitRate); lcd.init(); lcd.print("Distance(cm)="); }
  • pinMode设置Trig引脚是Arduino用来“命令”传感器的,所以设为OUTPUTEcho引脚是Arduino“读取”传感器状态的,所以设为INPUT。这个方向性绝对不能搞错。
  • Serial.begin(9600):开启串口通信,波特率设为9600。这行代码有两个作用:一是用于调试,可以在串口监视器里查看距离数值;二是有些情况下,如果I2C通信不稳定导致LCD显示异常,串口输出可以作为备份的查看手段。
  • LCD初始化lcd.init()是启动LCD。lcd.print(“Distance(cm)=”)打印了静态标签。这里我选择在setup里只打印一次,而不是在loop里重复打印,是为了提高效率,避免屏幕频繁刷新导致闪烁。

3.3 主循环与距离计算核心 (loop函数与calculatingDistance函数)

这是整个项目的算法核心,我们拆开揉碎了看。

主循环 (loop函数):

void loop() { distance = calculatingDistance(); // 1. 获取距离 lcd.backlight(); // 2. 打开背光(可选项) if(distance > 150) // 3. 简单滤波 return; lcd.setCursor(13,0); // 4. 定位光标 lcd.print(distance); // 5. 显示距离 delay(500); // 6. 延时 Serial.println(distance); // 7. 串口输出(可选) clearLCDLine(13,0,6); // 8. 清空旧数据 }
  1. 获取距离:调用calculatingDistance()函数,这是测距的核心逻辑,后面详细讲。
  2. 打开背光lcd.backlight()可以放在setup里一直开启,也可以像这样放在loop里。如果放在loop里,每次循环都会执行,实际效果就是一直亮着。如果你想做节能控制,可以加个条件判断,比如只在有物体靠近时才亮。
  3. 简单滤波 (if(distance > 150) return): 这是一个非常实用但容易被忽略的技巧。HY-SRF05最大量程理论上是4米(400cm),但在实际空旷环境下,没有物体反射时,pulseIn函数可能会等待超时或返回一个极大的值。这个if语句的作用是:如果计算出的距离大于150cm,就认为本次测量无效(可能是误测或超出关注范围),直接跳过本次循环的显示和刷新。这能有效防止屏幕上偶尔出现一个巨大的、不合理的数值,提升了用户体验的稳定性。这个阈值150可以根据你的应用场景调整。
  4. 光标定位与显示lcd.setCursor(13,0)将光标移动到第0行(第一行)、第13列。因为前面已经打印了“Distance(cm)=”,这个字符串占了0-12列,所以从第13列开始打印数字,看起来就是“Distance(cm)= 25.34”这样的格式。
  5. 延时500ms:这个延时决定了测量的刷新频率,这里是每秒2次。太快了(比如小于50ms)可能上一次声波的回声还没消散,会影响下一次测量。太慢了则实时性不够。500ms是一个折中的、稳定的值。
  6. 清空旧数据:由于每次打印的数字长度可能不同(比如“5.2”和“123.5”),如果直接在新位置打印,可能会残留旧数字的尾部。clearLCDLine这个自定义函数就是为了解决这个问题,它会在指定位置打印空格来覆盖旧内容。

距离计算函数 (calculatingDistance函数):这是最精妙的部分,它模拟了传感器的工作时序。

float calculatingDistance(){ // 1. 确保Trig引脚起始为低电平 digitalWrite(PinToTrig, LOW); delayMicroseconds(2); // 2. 发送一个10微秒的高脉冲作为触发信号 digitalWrite(PinToTrig, HIGH); delayMicroseconds(10); // 这个10us脉冲是关键! digitalWrite(PinToTrig, LOW); // 3. 读取Echo引脚高电平的持续时间 duration = pulseIn(PinToEcho, HIGH); // 4. 将时间转换为距离 distance = duration * 0.01715; // 核心计算公式 return distance; }

步骤详解:

  1. 初始化Trig:先拉低Trig引脚,并等待2微秒,确保一个稳定的起始状态。这是一个良好的“复位”习惯。

  2. 发送触发脉冲:拉高Trig引脚,并精确保持10微秒,然后拉低。这个10微秒的高电平脉冲就是告诉HY-SRF05:“开始工作!发射超声波!”传感器手册明确要求这个脉冲宽度至少10us。我们用delayMicroseconds(10)来实现。如果脉冲太短,传感器可能无法识别;太长也没必要。

  3. 监听回声脉冲pulseIn(PinToEcho, HIGH)是Arduino的内置函数,它在这里做了一件非常重要的事:阻塞等待并计时。它会一直等待Echo引脚从低电平变成高电平(即传感器开始发射超声波的那一刻),然后开始计时,直到Echo引脚变回低电平(即传感器接收到回波的那一刻)停止计时。函数返回的值就是这个高电平脉冲的持续时间,单位是微秒(μs)。这个时间duration就是超声波从发射到接收的总飞行时间。

  4. 时间换算距离:这是整个测距的物理基础。

    • 已知:声波在20℃干燥空气中的速度v ≈ 343 米/秒 = 34300 厘米/秒
    • 距离s = v * t。但注意,t是声波往返的时间,所以单程距离应该是s = v * t / 2
    • 我们的duration单位是微秒 (μs),1秒 = 1,000,000微秒。所以t = duration / 1,000,000秒。
    • 代入公式:距离(厘米) = (34300 cm/s * (duration / 1,000,000 s)) / 2
    • 简化计算:距离 = (34300 * duration) / (2 * 1,000,000) = duration * (34300 / 2000000) = duration * 0.01715

    所以,代码中distance = duration * 0.01715;这行,这个0.01715就是由声速推导出的换算系数。如果你想提高精度,可以针对温度进行补偿,因为声速随温度变化:v = 331.4 + 0.6 * T(T为摄氏温度)。那么系数就变成(331.4 + 0.6*T) / 20000

3.4 自定义清屏函数解析

原代码中的clearLCDLine函数是一个很实用的技巧,因为它可以局部清屏,比用lcd.clear()清整个屏幕更高效、无闪烁。

void clearLCDLine(int start, int line, int numOfBlocksIwantToErase) { lcd.setCursor(start, line); for(int i = 0; i < numOfBlocksIwantToErase; i++) { lcd.print(" "); } }
  • 参数start是开始列,line是行号,numOfBlocksIwantToErase是要清除的字符数。
  • 原理:它只是将光标移动到指定位置,然后连续打印空格符来覆盖原有的字符。在loop中,我们调用clearLCDLine(13,0,6),意思是清除第0行、从第13列开始的6个字符空间。为什么是6?因为考虑到距离数值可能带小数,例如“123.45”是6个字符,预留6个空格可以确保完全覆盖任何可能的数值。

4. 系统调试与性能优化实战

代码上传了,线也接好了,但屏幕不亮或者显示一堆乱码?这是新手最常遇到的阶段。别慌,按照以下步骤系统性地排查。

4.1 硬件排查清单

  1. 电源与接地:用万用表直流电压档测量Arduino的5V引脚和GND之间是否为稳定的5V。再测量面包板电源轨上的电压。确保所有模块的VCC和GND都正确连接到电源轨。
  2. I2C LCD无显示
    • 背光亮吗?如果背光都不亮,检查LCD的VCC和GND,以及背光控制(有些模块需要短路一个焊点来开启背光)。
    • 显示方块或乱码99%的问题出在I2C地址不对。运行一个I2C扫描程序(Arduino IDE示例里有),查看串口监视器输出的地址。将代码中LiquidCrystal_I2C lcd(0x27, 19, 4);0x27替换为扫描到的地址。
    • 对比度调节:大多数I2C模块上有一个蓝色的电位器。用螺丝刀缓慢旋转它,直到屏幕上的字符清晰显示。对比度不对,字符会非常淡或完全看不见。
  3. 传感器无反应
    • Trig和Echo线是否接反?再核对一遍。
    • 测量Trig引脚:在代码运行期间,用示波器或逻辑分析仪(如果没有,可以用Arduino的digitalRead快速测试)观察Trig引脚,应该能看到周期性的10us高电平脉冲。如果没有,检查代码和连接。
    • 测量Echo引脚:同样,在传感器前方放置一个物体,应该能在Echo引脚上测量到一个宽度随距离变化的高电平脉冲。如果一直是低电平,可能是传感器损坏,或前方物体吸声太强(如棉布、泡沫)。

4.2 软件调试与串口监控

如果硬件确认无误,问题可能出在软件逻辑。

  1. 打开串口监视器:将波特率设置为9600。你应该能看到每隔500ms打印出一个距离数值。

    • 如果数值是0:可能是pulseIn函数超时(默认1秒)返回0。检查传感器连接,并确保前方有反射物体。
    • 如果数值巨大且不变:可能是pulseIn没有检测到Echo引脚的下跳沿,一直等待直到超时,返回了一个很大的值(约1秒,即1000000微秒)。这通常意味着传感器没有收到回波,或者Echo引脚连接有问题。可以尝试在pulseIn函数中增加一个超时参数,例如pulseIn(PinToEcho, HIGH, 30000),表示最大等待30000微秒(30ms),对应大约5米的距离。超时后函数返回0。
    • 数值跳跃很大:这是超声波测距的常见问题。声波可能被非目标物体反射,或者环境中有其他超声干扰。这就是为什么我在loop里加了if(distance > 150) return进行滤波。你可以进一步采用“中值滤波”或“均值滤波”算法来平滑数据。例如,连续采样5次,去掉最大最小值,取中间3次的平均值,会稳定很多。
  2. 优化代码——增加滤波算法这里提供一个简单的均值滤波示例,可以显著提升读数稳定性:

    float getFilteredDistance() { const int numReadings = 5; float readings[numReadings]; float total = 0; for (int i = 0; i < numReadings; i++) { readings[i] = calculatingDistance(); delay(50); // 每次测量间稍作延迟,避免声波干扰 } // 这里可以加入排序,去掉最大最小值(中值滤波),更稳定 // 为了简单,这里直接求平均 for (int i = 0; i < numReadings; i++) { total += readings[i]; } return total / numReadings; }

    然后在loop中调用distance = getFilteredDistance();

4.3 精度提升与校准

如果你发现测量值有系统性的偏差(比如总是比尺子量的短2cm),可以进行软件校准。

  1. 固定距离校准法:将传感器正对一个平整的墙面,用卷尺精确测量传感器表面到墙面的距离,例如50.0cm。
  2. 读取此时串口输出的数值,例如是48.5cm。
  3. 计算偏差:偏差 = 实际值 - 测量值 = 50.0 - 48.5 = 1.5 cm
  4. 修改计算公式:distance = duration * 0.01715 + 1.5。或者,更科学地计算出一个修正系数:修正系数 = 实际距离 / (duration * 0.01715),然后用这个系数乘以未来的所有计算结果。

5. 项目扩展与应用场景思考

这个基础的测距系统就像一个乐高积木,可以很容易地嵌入到更大的项目中。

1. 智能小车避障这是最经典的应用。将本系统安装在小车前端,当distance小于一个安全阈值(例如20cm)时,控制小车舵机转向或电机后退。你可以将LCD显示去掉,只用核心的测距代码,更加轻量。

2. 液位高度检测将传感器垂直安装在容器顶部,向下发射超声波,测量到液面的距离,从而换算出液位高度。需要注意的是,液体表面和容器内壁可能会产生复杂回波,且不同液体对超声波的反射和吸收不同,需要针对性地调试和校准。

3. 简易安防报警器将系统放置在窗户或门口,当测量距离突然变短(有物体侵入)时,触发一个蜂鸣器报警,或通过Wi-Fi模块(如ESP8266)发送通知到手机。

4. 升级显示与交互

  • 换成OLED屏:I2C的OLED屏更省电,显示效果也更酷炫。
  • 增加按键:通过按键设置报警阈值、切换测量模式(厘米/英寸)等。
  • 数据上传:搭配ESP32等带有无线功能的开发板,将距离数据上传到物联网平台,实现远程监控。

最后一点个人心得:玩嵌入式,硬件调试能力有时比写代码更重要。遇到问题,学会用“分治法”——先确保电源OK,再确保信号线连接正确,然后用最简单的测试程序(比如让一个LED闪烁)验证核心功能,最后逐步叠加复杂度。这个超声波测距项目麻雀虽小五脏俱全,涵盖了数字IO控制、定时、数学运算、外设驱动(LCD)和简单滤波算法,吃透它,你对Arduino开发的理解会上一个大台阶。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/2 20:05:34

基于Arduino与接近传感器的智能闹钟:从仿真到实物的嵌入式开发实践

1. 项目概述与设计思路每天早上被闹钟吵醒&#xff0c;然后迷迷糊糊地按掉它&#xff0c;翻个身继续睡&#xff0c;结果错过重要的事情——这场景是不是太熟悉了&#xff1f;作为一个常年与起床困难症作斗争的工程师&#xff0c;我尝试过各种方法&#xff0c;从把手机放得远远的…

作者头像 李华
网站建设 2026/6/2 20:05:06

55项功能全面解锁:HsMod让炉石传说体验焕然一新

55项功能全面解锁&#xff1a;HsMod让炉石传说体验焕然一新 【免费下载链接】HsMod Hearthstone Modification Based on BepInEx 项目地址: https://gitcode.com/GitHub_Trending/hs/HsMod 你是否曾经在炉石传说中遇到过这样的烦恼&#xff1a;重复的开包动画让人昏昏欲…

作者头像 李华
网站建设 2026/6/2 20:04:17

基于Arduino的双轴太阳能追踪器:从光敏传感器到伺服电机的完整实现

1. 项目概述与核心价值作为一名长期泡在开源硬件和嵌入式开发领域的爱好者&#xff0c;我一直在寻找那些能将技术创意与环保实践结合起来的项目。太阳能追踪器&#xff0c;就是这样一个让我眼前一亮的课题。简单来说&#xff0c;它就是一个能“追着太阳跑”的智能支架&#xff…

作者头像 李华
网站建设 2026/6/2 20:03:56

从Electron到容器化:LX Music桌面版的技术演进之路

从Electron到容器化&#xff1a;LX Music桌面版的技术演进之路 【免费下载链接】lx-music-desktop 一个基于 Electron 的音乐软件 项目地址: https://gitcode.com/GitHub_Trending/lx/lx-music-desktop LX Music桌面版作为一款基于Electron和Vue 3开发的跨平台音乐播放软…

作者头像 李华
网站建设 2026/6/2 20:01:01

VoiceFixer:终极语音修复神器,一键解决音频质量问题

VoiceFixer&#xff1a;终极语音修复神器&#xff0c;一键解决音频质量问题 【免费下载链接】voicefixer General Speech Restoration 项目地址: https://gitcode.com/gh_mirrors/vo/voicefixer 在数字时代&#xff0c;语音质量直接影响沟通效率和用户体验。无论是珍贵的…

作者头像 李华