How can we help you today?
【感測】超音波 | HCSR04
簡介
超音波感測器就像是機器人的「蝙蝠耳朵」,透過發射與接收聲波來測量距離,是實現自動避障與目標追蹤的核心元件。在日常應用中,它就像倒車雷達一樣,能幫助機器人感知周遭障礙物防止碰撞;在競技場上,它則是精準的「探測雷達」,能即時偵測對手位置。透過 dual2s 的封裝,開發者可以輕鬆建立多方位的感測矩陣,讓機器人具備空間感知能力,不論是自動巡邏還是策略進攻,都能游刃有餘。
HC-SR04 是機器人專案中最常用的測距模組。dual2s 函式庫將複雜的聲波往返時間換算公式與腳位觸發邏輯進行了封裝,提供 HCSR04 類別讓開發者能快速讀取距離(公分)。本範例展示了如何管理多感測器配置(前、左、右陣列),並介紹了實戰中極具價值的「索敵模式」。
硬體配置與支援
- 模組型號:HC-SR04 (4-pin 版本)
- 預設腳位:
- 前方 (F):
USC_F_ECHO/USC_F_TRIG - 左側 (L):
USC_L_ECHO/USC_L_TRIG - 右側 (R):
USC_R_ECHO/USC_R_TRIG
- 前方 (F):
- 相容性:支援
dual2s全系列。針對dual2s mini使用者,在下述範例程式中可透過#define DUAL2S_MINI快速切換為單感測器模式。
核心方法說明
- 距離量測
ObjDistance():- 功能:回傳物體與感測器的實際距離(單位:公分)。
- 特性:回傳值為
float。當目標超出量測範圍(約 3.4 米以上)或未捕捉到回波時,會自動回傳999.0作為無效標記,避免程式因錯誤數值而誤判。
- 快速索敵
ObjSeeking(threshold):- 功能:判斷物體是否進入設定的門檻值(threshold)內。
- 特性:回傳布林值(
True/False)。在開發相撲機器人或自動跟隨機器人時,使用此方法能讓邏輯判斷更簡潔且效率更高。
開發重點建議
- 取樣頻率限制:超音波量測依賴聲波在空氣中的實際傳播。範例建議更新頻率為 150ms,最低不建議低於 50ms。過高的頻率會導致聲波殘餘影響下一波讀取,造成數值跳動或「虛假回報」。
- 非阻斷式設計:範例採用
millis()計時器而非delay()。這能確保在等待超音波回傳數值的同時,機器人的馬達控制與其他感測器依然能正常運作,這對於高速動態運行的機器人至關重要。
dual2s控制器與環境
AI學伴
互動(1):基於函數庫 – dual2s
互動(2):基於範例程式
👉 你也可以在dual2s函數庫(./example/basic)中找到此範例程式。
/*=====================================================================================
yesio.net / 2026.03.12 / by nick
# Filename:03_hcsr04.ino
# Function:超音波(HCSR04類別)使用範例
Test Code of dual2S's HCSR04 CLASS.
# Buzzer Pin G15 in dual2s HW
# Toolchain & Libs:ESP32 Arduino Core v3.3.5 (ESP-IDF v5.1), dual2s
======================================================================================*/
#include <dual2s.h>
//使用dual2s-mini請開啟此定義, dual2s-mini硬體只預設前方超音波。當然自行外接不再此限喔。
//#define DUAL2S_MINI
// 宣告三個方向的超音波物件,直接使用 dual2s.h 內建的硬體腳位定義
// 參數:(Echo 腳位, Trig 腳位)
HCSR04 us_F(DUAL2S_HW::USC_F_ECHO, DUAL2S_HW::USC_F_TRIG); // 前方感測器
#ifndef DUAL2S_MINI
HCSR04 us_L(DUAL2S_HW::USC_L_ECHO, DUAL2S_HW::USC_L_TRIG); // 左方感測器
HCSR04 us_R(DUAL2S_HW::USC_R_ECHO, DUAL2S_HW::USC_R_TRIG); // 右方感測器
#endif
// 測試流程控制變數
unsigned long lastPrintTime = 0;
int testStep = 0;
// 設定攻擊觸發距離:當目標進入 20 公分內視為發現對手
const uint8_t ATTACK_RANGE = 20;
void setup() {
Serial.begin(115200);
Serial.println("=== HCSR04 超音波陣列測試開始 ===");
Serial.println("初始化完成,準備讀取數值...");
}
void loop() {
// 使用 millis() 實現非阻斷式讀取,每 150 毫秒更新一次畫面
// 提示:超音波量測需要時間等待聲波返回,不建議將更新頻率設得太高(例如低於 50ms),
// 否則容易發生「聲波互相干擾」或「抓不到回波」的現象。
if (millis() - lastPrintTime >= 150) {
lastPrintTime = millis();
testStep++;
if (testStep > 200) {
testStep = 1;
}
// ---------------------------------------------------------
// 測試方法 1:ObjDistance() - 取得實際距離 (公分)
// 回傳值:float。若超出量測範圍 (大約 340cm 以上,p == 0) 會回傳 999.0
// ---------------------------------------------------------
if(testStep <= 100){
if (testStep == 1) {
Serial.println("\n\n===========================================");
Serial.println("--- 測試階段 1:【ObjDistance()】, 約 15 秒 ---");
Serial.println("===========================================");
}
float distF = us_F.ObjDistance();
#ifndef DUAL2S_MINI
float distL = us_L.ObjDistance();
float distR = us_R.ObjDistance();
#endif
Serial.print(testStep);
Serial.print("【距離(cm)】 前: ");
if(distF >= 999.0) Serial.print("--- "); else Serial.print(distF, 1);
#ifndef DUAL2S_MINI
Serial.print(" | 左: ");
if(distL >= 999.0) Serial.print("--- "); else Serial.print(distL, 1);
Serial.print(" | 右: ");
if(distR >= 999.0) Serial.print("--- "); else Serial.print(distR, 1);
#endif
Serial.println();
}
// ---------------------------------------------------------
// 測試方法 2:ObjSeeking(thresh) - 快速索敵 (布林值)
// 說明:回傳 True/False,比對目標是否進入我們設定的範圍內。
// 在實戰主迴圈中,用這個方法判斷會比用 ObjDistance() 更快!
// ---------------------------------------------------------
else if (testStep > 100 && testStep <= 200) {
if (testStep == 101) {
Serial.println("\n\n===========================================");
Serial.println("--- 測試階段 2:【ObjSeeking()】, 約 15 秒 ---");
Serial.println("===========================================");
}
Serial.print("["); Serial.print(testStep); Serial.print("] ");
Serial.printf("==> 【索敵狀態 %dcm】: ", ATTACK_RANGE);
if (us_F.ObjSeeking(ATTACK_RANGE)) { Serial.print("🚨前方發現對手!(準備衝撞)"); }
#ifndef DUAL2S_MINI
else if (us_L.ObjSeeking(ATTACK_RANGE)) { Serial.print("👈左側有目標!(準備左轉)"); }
else if (us_R.ObjSeeking(ATTACK_RANGE)) { Serial.print("👉右側有目標!(準備右轉)"); }
#endif
else { Serial.print("🟢安全 (巡邏中...)"); }
Serial.println(); // 只有在這個階段才換行
}
}
}
