尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于Arduino与HC-SR04的超声波测距报警与继电器控制系统

基于Arduino与HC-SR04的超声波测距报警与继电器控制系统 最近在做一个智能小车项目需要实现自动避障功能核心就是利用超声波传感器测量前方障碍物的距离。在调试过程中我发现网上很多教程要么代码不完整要么逻辑有缺陷导致测距不准或响应不及时。特别是想实现一个“可设定报警距离并自动控制继电器开关”的功能时资料更是零散。本文将分享一套基于 Arduino 和 HC-SR04 超声波传感器的完整解决方案从原理、接线、代码编写到功能扩展手把手教你打造一个可自定义触发距离、并能联动继电器控制风扇、灯光等负载的自动化系统。无论你是电子爱好者、学生还是正在做智能家居原型的开发者这套方案都能直接复用。1. 项目背景与核心概念1.1 什么是超声波测距超声波测距是一种非接触式的距离测量技术。它模仿蝙蝠的声呐系统通过计算超声波从发射到遇到物体反射回来的时间差结合声音在空气中的传播速度来计算出传感器与物体之间的距离。其公式非常简单距离 (声速 × 时间差) / 2。除以2是因为声音走了一个来回。在电子制作和自动化领域超声波传感器因其成本低、精度相对较好、不受光线影响等优点被广泛应用于机器人避障、液位检测、停车辅助、安防报警等场景。1.2 HC-SR04 超声波传感器模块本项目使用的 HC-SR04 模块是最常见、性价比最高的超声波传感器之一。它工作电压为5V测量范围通常在2cm到400cm之间测量精度可达3mm。模块上有四个引脚VCC: 接5V电源。Trig (触发): 接收微控制器发送的脉冲信号触发一次测距。Echo (回响): 向微控制器输出一个高电平脉冲其持续时间与测距时间成正比。GND: 接地。其工作流程是微控制器向Trig引脚发送一个至少10微秒的高电平脉冲模块内部会自动发射8个40kHz的超声波脉冲并检测回波。当检测到回波后Echo引脚会输出一个高电平该高电平的持续时间就是超声波从发射到返回的时间。1.3 项目目标可设定距离的报警与负载控制本项目不仅仅是简单的测距显示。我们的目标是构建一个智能化的测距控制系统它具备以下功能实时测距与显示持续测量并显示传感器与前方物体的距离。可调报警阈值用户可以通过按键或电位器设定一个报警距离阈值例如30厘米。声光报警当实测距离小于设定的报警阈值时系统触发蜂鸣器报警和LED指示灯。负载自动控制在报警的同时自动控制一个继电器模块进而可以开关风扇、电机、灯具等大功率负载实现自动化控制。免编程可选对于不希望深入编程的用户我们也探讨如何使用可编程逻辑控制器如PLC逻辑模块或简单的比较器电路来实现基础功能但本文核心仍以Arduino编程实现为主因为其灵活性强。2. 硬件准备与电路连接2.1 所需材料清单在开始之前请准备好以下硬件主控板Arduino Uno R3 (或其他兼容板如 Nano、Mega) ×1超声波传感器HC-SR04 模块 ×1显示设备0.96寸 OLED I2C 显示屏 (或1602 LCD屏) ×1【用于显示距离和阈值】输入设备旋转编码器模块 (或电位器) ×1【用于无极调节报警阈值】按键 ×2【用于阈值增减作为编码器的替代或补充】报警与指示设备有源蜂鸣器模块 (或无源蜂鸣器) ×1LED (红色) ×1 220Ω 电阻 ×1负载控制设备5V 单路继电器模块 ×1负载示例小风扇、LED灯带等 (工作电压需与继电器输出匹配)连接线杜邦线 (公对公、公对母) 若干电源USB数据线为Arduino供电继电器控制的负载需单独供电。2.2 电路接线图与说明正确的接线是项目成功的第一步。请参照下表进行连接Arduino Uno 引脚连接的模块与引脚说明5VHC-SR04 VCC, 继电器模块 VCC, 旋转编码器 VCC, OLED VCC提供5V电源。注意总电流不要超过板载稳压器限额。GNDHC-SR04 GND, 继电器模块 GND, 旋转编码器 GND, OLED GND, 蜂鸣器 GND, LED阴极共地所有模块的GND必须连接在一起。D2HC-SR04 Trig数字输出发送触发脉冲。D3HC-SR04 Echo数字输入接收回响脉冲。建议此引脚通过一个1kΩ电阻连接到Echo再并联一个2kΩ电阻下拉到GND以稳定信号。A4 (SDA)OLED SDAI2C数据线。A5 (SCL)OLED SCLI2C时钟线。D4旋转编码器 CLK (或 SW)编码器时钟引脚用于检测旋转。D5旋转编码器 DT (或按键A)编码器数据引脚。如果用按键则接按键A用于增加阈值。D6旋转编码器 SW (或按键B)编码器开关引脚按下。如果用按键则接按键B用于减少阈值。D7继电器模块 IN数字输出高电平触发继电器吸合。D8有源蜂鸣器模块 I/O数字输出高电平触发蜂鸣器鸣响。D9LED阳极 (通过220Ω电阻)数字输出高电平点亮报警LED。接线注意事项电源如果控制大功率负载如12V风扇务必为负载单独供电切勿尝试从Arduino的5V引脚取电否则可能损坏主板。继电器模块的输入侧VCC, GND, IN接Arduino输出侧COM, NO, NC接负载和外部电源。上拉/下拉电阻Echo引脚和编码器/按键的输入引脚在内部或外部配置上拉电阻可以避免引脚悬空导致读数不稳定。Arduino内置上拉电阻可以通过pinMode(pin, INPUT_PULLUP)启用。引脚分配上表引脚可自定义只需在代码中同步修改即可。3. 软件环境与库安装3.1 Arduino IDE 配置前往 Arduino 官网下载并安装最新版 Arduino IDE。安装完成后打开IDE确保板卡类型选择正确工具-开发板-Arduino AVR Boards-Arduino Uno。选择正确的端口工具-端口选择对应的COM口如COM3, COM4等。3.2 第三方库安装本项目需要用到OLED显示库推荐使用功能强大的Adafruit_SSD1306和Adafruit_GFX库。在Arduino IDE中点击项目-加载库-管理库...。在库管理器中搜索Adafruit SSD1306找到后点击安装。搜索Adafruit GFX Library同样点击安装。安装SSD1306时可能会提示你安装GFX库按提示操作即可。可选如果你使用旋转编码器可以搜索并安装Encoder库它提供了更稳健的编码器读数处理。4. 核心代码实现与分步讲解我们将代码分为几个功能模块进行讲解最后整合成完整程序。4.1 超声波测距函数这是项目的核心测量功能。我们需要编写一个函数负责触发HC-SR04并计算返回的距离。// 定义超声波引脚 const int trigPin 2; const int echoPin 3; // 初始化超声波引脚 void setupUltrasonic() { pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); digitalWrite(trigPin, LOW); // 初始保持低电平 delayMicroseconds(2); } // 测量一次距离的函数返回单位为厘米cm float measureDistance() { // 发送一个10微秒的高脉冲触发测距 digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // 读取 Echo 引脚的高电平持续时间单位微秒 // pulseIn函数会等待引脚变为高电平开始计时再变回低电平时停止计时。 long duration pulseIn(echoPin, HIGH, 30000); // 设置超时30ms对应约5米距离 // 计算距离厘米 // 声速在空气中约340m/s即 0.034 cm/微秒。距离 (时间 * 声速) / 2 float distance duration * 0.034 / 2.0; // 如果超时或距离异常返回一个错误值如-1 if (distance 0 || distance 400) { return -1.0; } return distance; }代码关键点解释pulseIn(pin, value, timeout): 这是一个非常实用的函数用于测量指定引脚上脉冲的持续时间。第三个参数timeout单位微秒很重要它设定了等待脉冲的最大时间避免程序卡死。30ms对应大约5米的测量上限符合HC-SR04的规格。声速校准公式中的0.034是近似值。声速受温度影响更精确的公式是331.4 0.6 * temp单位m/s。如果对精度要求高可以加入温湿度传感器进行补偿。错误处理返回-1用于标识测量失败后续显示或逻辑判断时可以过滤掉这些无效值。4.2 OLED显示功能我们将实时距离和设定的报警阈值显示在OLED屏幕上。#include Adafruit_SSD1306.h #include Adafruit_GFX.h #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); void setupDisplay() { if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // I2C地址通常是0x3C或0x3D Serial.println(F(SSD1306 allocation failed)); for(;;); // 初始化失败死循环 } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println(Ultrasonic System); display.display(); delay(2000); } void updateDisplay(float currentDist, float alarmThreshold) { display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(1); display.print(Distance: ); display.setTextSize(2); display.setCursor(0, 10); if(currentDist 0) { display.print(Error); } else { display.print(currentDist, 1); // 显示一位小数 display.print( cm); } display.setTextSize(1); display.setCursor(0, 35); display.print(Alarm Threshold:); display.setCursor(0, 45); display.setTextSize(2); display.print(alarmThreshold, 0); // 阈值显示为整数 display.print( cm); // 如果当前距离小于阈值在屏幕下方显示警告信息 if(currentDist 0 currentDist alarmThreshold) { display.setTextSize(1); display.setCursor(0, 55); display.print(!! TOO CLOSE !!); } display.display(); }4.3 报警阈值设置使用旋转编码器旋转编码器可以平滑地增加或减少报警阈值。// 使用简单的引脚状态检测编码器库版本更佳此处展示原理 const int encoderCLK 4; const int encoderDT 5; const int encoderSW 6; int lastCLKState HIGH; float alarmThreshold 30.0; // 默认报警阈值30厘米 int minThreshold 5; int maxThreshold 200; void setupEncoder() { pinMode(encoderCLK, INPUT_PULLUP); pinMode(encoderDT, INPUT_PULLUP); pinMode(encoderSW, INPUT_PULLUP); } void readEncoder() { int currentCLKState digitalRead(encoderCLK); // 检测CLK引脚的下落沿从HIGH到LOW if (currentCLKState ! lastCLKState currentCLKState LOW) { // 根据DT引脚的状态判断旋转方向 if (digitalRead(encoderDT) ! currentCLKState) { // 顺时针增加阈值 alarmThreshold 5.0; } else { // 逆时针减少阈值 alarmThreshold - 5.0; } // 限制阈值在合理范围内 alarmThreshold constrain(alarmThreshold, minThreshold, maxThreshold); } lastCLKState currentCLKState; // 检测按键是否被按下重置阈值到默认值 if (digitalRead(encoderSW) LOW) { delay(50); // 简单消抖 if (digitalRead(encoderSW) LOW) { alarmThreshold 30.0; while(digitalRead(encoderSW) LOW); // 等待按键释放 } } }4.4 报警与负载控制逻辑这是系统的决策核心根据测量距离和设定阈值控制蜂鸣器、LED和继电器。const int relayPin 7; const int buzzerPin 8; const int ledPin 9; bool alarmState false; void setupOutputs() { pinMode(relayPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(ledPin, OUTPUT); digitalWrite(relayPin, LOW); // 继电器初始断开 digitalWrite(buzzerPin, LOW); digitalWrite(ledPin, LOW); } void controlAlarmAndLoad(float currentDist) { if (currentDist 0 currentDist alarmThreshold) { // 触发报警 alarmState true; digitalWrite(ledPin, HIGH); // 点亮LED digitalWrite(buzzerPin, HIGH); // 蜂鸣器响有源蜂鸣器 digitalWrite(relayPin, HIGH); // 继电器吸合接通负载 // 如果是无源蜂鸣器这里需要用tone()函数产生特定频率的声音 // tone(buzzerPin, 1000); // 1kHz频率 } else { // 关闭报警 alarmState false; digitalWrite(ledPin, LOW); digitalWrite(buzzerPin, LOW); digitalWrite(relayPin, LOW); // noTone(buzzerPin); // 如果是无源蜂鸣器 } }4.5 完整代码整合与主循环将以上所有模块整合并编写setup()和loop()函数。#include Adafruit_SSD1306.h #include Adafruit_GFX.h // 引脚定义 #define TRIG_PIN 2 #define ECHO_PIN 3 #define ENC_CLK 4 #define ENC_DT 5 #define ENC_SW 6 #define RELAY_PIN 7 #define BUZZER_PIN 8 #define LED_PIN 9 // OLED定义 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, -1); // 全局变量 float alarmThreshold 30.0; int lastCLKState HIGH; unsigned long lastMeasureTime 0; const long measureInterval 200; // 测量间隔200ms void setup() { Serial.begin(9600); // 初始化各模块 pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); digitalWrite(TRIG_PIN, LOW); pinMode(ENC_CLK, INPUT_PULLUP); pinMode(ENC_DT, INPUT_PULLUP); pinMode(ENC_SW, INPUT_PULLUP); pinMode(RELAY_PIN, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW); digitalWrite(BUZZER_PIN, LOW); digitalWrite(LED_PIN, LOW); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F(OLED init fail)); for(;;); } display.clearDisplay(); display.setTextColor(SSD1306_WHITE); display.display(); delay(1000); } void loop() { // 1. 读取编码器更新阈值 readEncoder(); // 2. 定时测量距离非阻塞式避免delay卡死程序 unsigned long currentMillis millis(); if (currentMillis - lastMeasureTime measureInterval) { lastMeasureTime currentMillis; float distance measureDistance(); // 3. 根据距离和阈值控制报警与负载 controlAlarmAndLoad(distance); // 4. 更新OLED显示 updateDisplay(distance, alarmThreshold); // 5. 可选串口打印调试信息 Serial.print(Distance: ); Serial.print(distance); Serial.print( cm, Threshold: ); Serial.print(alarmThreshold); Serial.print( cm, Alarm: ); Serial.println(alarmState ? ON : OFF); } } // --- 以下是前面定义过的函数需放在此处 --- float measureDistance() { /* 同上 */ } void readEncoder() { /* 同上 */ } void controlAlarmAndLoad(float dist) { /* 同上 */ } void updateDisplay(float dist, float threshold) { /* 同上 */ }5. 系统调试与常见问题排查将代码上传到Arduino后系统可能不会立即完美工作。以下是常见问题及解决方法。问题现象可能原因排查与解决思路OLED屏幕不亮或白屏1. I2C地址错误。2. 接线松动SDA, SCL。3. 未正确初始化库。1. 用I2C扫描程序Arduino IDE示例中有确认OLED地址是0x3C还是0x3D并修改代码。2. 检查接线确保SDA接A4SCL接A5。3. 确认已安装Adafruit_SSD1306和Adafruit_GFX库。距离显示为固定值或“Error”1. Echo引脚信号不稳定。2. 测量物体超出范围或表面不反射超声波。3.pulseIn超时设置过短。1. 为Echo引脚添加下拉电阻如2kΩ到GND。2. 确保测量物体在2-400cm内且表面非海绵等吸音材料。3. 检查pulseIn的超时参数确保大于最大测量距离所需时间400cm约需23ms。旋转编码器调节不灵敏或跳变1. 机械编码器抖动。2. 代码消抖逻辑不完善。1. 使用Encoder库替代手动检测它内部有硬件消抖处理。2. 在手动检测代码中增加更严格的消抖判断例如检测到变化后延时几毫秒再确认状态。继电器频繁开关或状态不对1. 距离测量波动大。2. 继电器模块触发方式理解错误。1. 对距离值进行软件滤波如取最近N次测量的平均值。2. 确认你的继电器模块是高电平触发还是低电平触发修改digitalWrite(RELAY_PIN, HIGH/LOW)。蜂鸣器不响或常响1. 有源/无源蜂鸣器类型用错。2. 引脚驱动能力不足。1.有源蜂鸣器给高电平就响用digitalWrite。无源蜂鸣器需要PWM频率驱动用tone()函数。2. 蜂鸣器电流可能较大尝试通过一个三极管或MOS管来驱动而非直接接单片机引脚。系统运行一段时间后复位1. 电源功率不足。2. 继电器通断引起电源波动。1. 确保Arduino使用稳定的电源适配器9V-12V而非仅靠USB供电特别是继电器吸合时电流较大。2. 在继电器线圈两端并联一个续流二极管如1N4007阴极接VCC阳极接控制引脚以吸收反向电动势。6. 功能扩展与最佳实践一个基础系统搭建完成后可以考虑以下优化和扩展方向使其更稳定、更智能。6.1 软件滤波与数据平滑超声波传感器容易受到环境噪声干扰导致单次测量值跳动。采用滑动平均滤波可以显著提升读数稳定性。#define FILTER_SIZE 5 // 滤波窗口大小 float distanceBuffer[FILTER_SIZE]; int bufferIndex 0; float getFilteredDistance() { float rawDist measureDistance(); if(rawDist 0) { // 只将有效数据加入缓冲区 distanceBuffer[bufferIndex] rawDist; bufferIndex (bufferIndex 1) % FILTER_SIZE; } // 计算平均值 float sum 0; int count 0; for(int i0; iFILTER_SIZE; i) { if(distanceBuffer[i] 0) { sum distanceBuffer[i]; count; } } return (count 0) ? (sum / count) : -1; } // 在loop()中使用 getFilteredDistance() 代替 measureDistance()6.2 增加报警延时与 hysteresis迟滞防止物体在阈值边缘晃动导致继电器和蜂鸣器频繁动作这对于控制电机等设备尤为重要。#define HYSTERESIS 2.0 // 迟滞范围2厘米 bool lastAlarmState false; void controlAlarmAndLoadAdvanced(float currentDist) { bool shouldAlarm (currentDist 0 currentDist alarmThreshold); // 应用迟滞只有当距离越过“阈值 - 迟滞”时才触发越过“阈值 迟滞”时才解除 if(shouldAlarm !lastAlarmState currentDist (alarmThreshold - HYSTERESIS)) { lastAlarmState true; } else if(!shouldAlarm lastAlarmState currentDist (alarmThreshold HYSTERESIS)) { lastAlarmState false; } // 根据最终状态控制输出 if(lastAlarmState) { digitalWrite(ledPin, HIGH); digitalWrite(buzzerPin, HIGH); digitalWrite(relayPin, HIGH); } else { digitalWrite(ledPin, LOW); digitalWrite(buzzerPin, LOW); digitalWrite(relayPin, LOW); } }6.3 多种控制模式扩展你可以通过增加一个模式切换开关让系统拥有更多功能。模式1即时报警模式当前模式。模式2区间报警模式物体进入和离开特定区域报警。模式3数据记录模式通过SD卡模块定期记录距离数据。模式4跟随模式通过舵机云台使传感器始终对准最近物体。6.4 生产环境注意事项如果打算将这个系统用于更严肃的场合需要考虑以下几点电源隔离控制端Arduino和被控负载如220V交流电机的电源必须物理隔离通常使用光耦或隔离继电器模块确保安全。外壳与防护为电路制作一个绝缘外壳避免短路。超声波传感器探头保持清洁。看门狗定时器在代码中启用Arduino的内部看门狗#include avr/wdt.h防止程序跑飞导致系统死机。参数存储使用EEPROM存储用户设定的报警阈值这样即使断电重启设置也不会丢失。#include EEPROM.h void saveThreshold(float th) { EEPROM.put(0, th); } float loadThreshold() { float th; EEPROM.get(0, th); return (isnan(th))?30.0:th; }抗干扰设计信号线如Trig, Echo尽量短或使用屏蔽线。在电源入口处增加滤波电容如100uF电解并联0.1uF瓷片电容。7. 项目总结与学习路线通过这个“超声波测距报警与控制负载”项目我们不仅学会了HC-SR04的使用更掌握了一个完整嵌入式系统的构建流程传感器数据采集 - 信号处理与滤波 - 人机交互显示与输入- 逻辑判断 - 执行器控制继电器。这是物联网和自动化设备的通用模型。你可以在此基础上继续深化通信联网加入Wi-Fi模块如ESP8266/ESP32将距离数据和报警状态上传到云平台如阿里云、ThingsBoard实现远程监控。多传感器融合增加一个红外避障传感器与超声波数据互补提高检测可靠性。可视化界面用Processing或Python的Tkinter编写一个电脑上的上位机通过串口接收数据并绘制实时距离曲线。机械结构将整个系统安装到小车或舵机云台上制作一个自动跟踪或避障机器人。硬件编程的魅力在于软硬结合从代码到物理世界的反馈是即时的。希望这份详细的教程能成为你探索自动化世界的一块坚实跳板。动手过程中遇到的任何问题欢迎在社区交流往往解决问题的过程就是收获最大的时刻。
返回列表