Arduino筆記(19):HC-SR04超音波測距模組

在 2014年 3月曾寫過一篇「Raspberry Pi 筆記(十五):超音波測距離Raspberry」,將 Pi 連接超音波模組,測試一下實際的距離與感應測得的數據差多少,實測的結果誤差不到1公分。

最近繼續研究 Pi 跟 Arduino,也發現另有一個超音波測距的模組,HC-SR04,我改用 Arduino 來連接這個模組,也寫程式測試一下兩者得到的結果是否相同。


[超音波測距 HC-SR04]

模組工作原理:
  • 採用I/O觸發測距,給至少為 10us 的高電位信號
  • 模組自動發送 8 個 40KHZ 的方波,自動檢測是否有信號返回
  • 有信號返回,通過 I/O 輸出一高電位,高電位持續的時間就是超聲波從發射到返回的時間
  • 測試距離 = (高電位時間 x 聲速 (343.2 米/秒)) / 2

HC-SR04 技術規格:
  • 電源:DC5V/2mA
  • 輸出電位( 1/ 0):5V/ 0V
  • 精度:3mm
  • 距離範圍:2 ~ 450cm
  • 有效的角度:<15℃
  • 觸發輸入信號:10uS TTL pulse
  • ECHO輸出信號: Input TTL lever signal and the range in proportion
  • 接線方式:VCC、trig (控制端)、echo (接收端)、GND

[材料]

  • Arduino Uno  x 1
  • HC-SR04測距模組  x1
  • 連接線 x 4條

[接線圖]


• HC-SR04測距模組連接線路
HC-SR04 測距模組Arduino
1 - VCCVCC
2 - Trig – TriggerPin 12
3 - EchoPin 11
4 - GNDGND

[程式一]

以下先不呼叫函式,直接以音波在空氣中傳遞的大約速度,算出來回測得的時間,乘以空氣中傳遞聲音的速度,算出距離。程式中有一行 cm = (duration/2) / 29.1,各位會不會覺得奇怪,為什麼要除以 29.1 ? 那是因為在攝氏零度之海平面音速約為331.5公尺/秒,每升高1攝氏度,音速就增加0.607公尺/秒,可以列出一個公式:

•  音速 c = 331.5 + 0.607 * t  (其中 t 為攝氏溫度)。

    例: 攝氏20度時的音速約為: 331.5 + 0.607*20 = 343.64 公尺/秒

•  音速公尺/秒 換算成 公分/微秒:343.64 * 100 / 1000000 = 0.034364 公分/微秒,亦即

    音速每公分需要29.1 微秒:  1 / 0.034364 = 29.1 微秒/公分

    超音波發射的距離為來回,因此單程距離 = 時間差 / 2  再除以  29.1 微秒 / 公分,同樣的如果要換算成英吋,只要將 29.1 再乘以 2.54 即可。來看看程式的內容:
int trigPin = 12;                  //Trig Pin
int echoPin = 11;                  //Echo Pin
long duration, cm, inches;
 
void setup() {
  Serial.begin (9600);             // Serial Port begin
  pinMode(trigPin, OUTPUT);        // 定義輸入及輸出 
  pinMode(echoPin, INPUT);
}
 
void loop()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);
  digitalWrite(trigPin, HIGH);     // 給 Trig 高電位,持續 10微秒
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  pinMode(echoPin, INPUT);             // 讀取 echo 的電位
  duration = pulseIn(echoPin, HIGH);   // 收到高電位時的時間
 
  cm = (duration/2) / 29.1;         // 將時間換算成距離 cm 或 inch  
  inches = (duration/2) / 74; 

  Serial.print("Distance : ");  
  Serial.print(inches);
  Serial.print("in,   ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(250);
}
2020/11/23補充:執行上述程式時,從串列埠看到的測試值都是整數,如果要計算值要顯示小數點,可以將上述程式中「long duration, cm, inches;」改成「float duration, cm, inches;」。

有關 HC-SR04的實作,用 1602 LCD 顯示測得的數據,可參考我的另一篇文章:Arduino筆記(96):HC-SR04與VL53L0X(GY-530)的測距比較

[程式二]

網友將上述計算等方式,寫成一個函式,比較著名的是 NewPing,可以到 這裡 下載最新的函式庫,放到 Arduino 的預設 Libraries目錄,即可呼叫該函式,程式如下:
#include <NewPing.h>
#define TRIG_PIN    12          // trigger Pin
#define ECHO_PIN    11          // Echo Pin
#define MAX_DISTANCE 200        // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);    // 設定 NewPing 物件,並給與最遠測試距離

void setup() {
  Serial.begin(115200);         // 開啟串列埠顯示 ping 的距離值
}

void loop() {
  delay(50);                        // 等待 50 毫秒,需大於前程式說明的 29 秒
  unsigned int uS = sonar.ping();   // 送出 ping,並取得微秒 microseconds(uS) 時間
  Serial.print("Ping: ");
  Serial.print(sonar.convert_cm(uS));    // 換算時間為公分,如顯示 0,表示超出距離範圍
  Serial.println("cm");
}

[測試結果]

測試的結果還蠻正確的,跟規格提供的誤差值3mm差不多。接下來就要將 HC-SR04接到小車上去,看看遇到障礙物時,是否能自動轉彎或倒退。

[參考資料]

14 留言

  1. 大大辛苦了,不過程式二的第四行
    NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
    其中TRIGGER_PIN應為TRIG_PIN

    回覆刪除
    回覆
    1. 你好, 是的應該為TRIG_PIN才對,謝謝告知。

      刪除
  2. 請問大大,程式一中間的duration是甚麼?

    回覆刪除
    回覆
    1. L298N驅動模組,採用ST公司的L298N晶片,可以直接驅動兩顆3-30V直流馬達,並提供了5V輸出介面,可以給5V微控制器電路系統供電,支援3.3V MCU控制,可以方便的控制直流馬達速度和方向,也可以控制1顆2相步進馬達。


      刪除
  3. 感謝您分享,初學者請教一下程式一的執行順序:

    digitalWrite(trigPin, HIGH); // 給 Trig 高電位,持續 10微秒
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    => trigPin在此時已寫入Low了, 那接著下方的duration是不是應該只收到low的訊號了呢?


    pinMode(echoPin, INPUT); // 讀取 echo 的電位
    duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間

    謝謝您

    回覆刪除
    回覆
    1. 很久沒看Arduino的程式了,再去查一下pulseIn()這個函數的用法:前一個指令將TrigPin設為低電位,如有信號返回,EchoPin引脚會輸出高電位,函數是要偵測高電位,也就是計算從低電壓到接收訊號時高電位持續的時間,就是超聲波從發射到返回的時間。使用pulseIn()函數獲取到測距的結果,並計算出距與被測物的實際距離。

      刪除
  4. 請問如何讓超音波測距結合步進馬達呢
    想做可以在15cm內正轉3圈否則不會動
    若正轉3圈後障礙物的距離超出15cm則會反轉3圈回原點結束動作
    我是新手,還有很多不足的地方,麻煩大大了!

    回覆刪除
  5. 請問大大,我是用bDesigner,測出來的數字單位是cm嗎?

    回覆刪除
  6. 想請問一下 上面的code 把 tripin pull high 10ms, 那後來的 echopin的時間是否要先 -10呢?(收到的是10ms前的high)

    就是改成這樣.(理論上pinMode(echoPin, INPUT); 應該也是要時間.所以應該在while迴圈錢設?)

    pinMode(echoPin, INPUT); // 讀取 echo 的電位
    duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間
    duration -=10;

    回覆刪除
  7. 作者已經移除這則留言。

    回覆刪除
  8. 網誌管理員已經移除這則留言。

    回覆刪除
  9. 網誌管理員已經移除這則留言。

    回覆刪除

張貼留言

較新的 較舊