2016年10月19日 星期三

Arduino 超音波模組測試

去年三月跟露天賣家 XLAN 買了兩個超音波模組 HC-SR04, 之後就一直放在紙盒裡沒時間測試.

# 露天 XLAN 電子零件購買清單

9/23 日去 Nissan 做 QRV 75000 公里保養時, 把趙英傑的 Arduino 互動設計入門這本書帶去看, 仔細地把超音波感測器部分看完後, 本來想說要一口氣將超音波部分測完, 但接下來因為兩個颱風鄉下停電問題, 注意力轉到太陽能備用電力上面, 所以又耽擱了. 10/14 日要下午才進辦公室, 所以就著手進行測試, 但沒時間寫測試紀錄. 今晚得空就來把它做個完整記錄唄!

以下的測試程式是根據我閱讀下列書籍, 參考其中的範例程式加以改編而來 :
  1. Arduino 互動設計入門 2, 趙英傑, 旗標 (第 9-3 ~ 9-5 節)
  2. Arduino 輕鬆入門, 葉難, 博碩 (第 10-7 節)
  3. Arduino 完全實戰手冊, 王冠勛譯, 博碩 (第 6-1 節)
  4. Arduino 一試就上手第二版, 孫駿榮, 碁峰 (第 9-1 節)
  5. Make : 感測器, 運用 Arduinio 和 Raspberry Pi 感測的專題與實驗, 碁峰 (第三章) 
雖然不需要聯網, 為了方便我還是使用自己組裝焊接的 Arduino NANO+ESP8266 IOT 模組, 搭配一個 HC-SR04 超音波感測模組進行實驗, 此模組電路圖參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇

其實以下實驗只需要一塊 NANO 加上 HC-SR04 以及幾條杜邦線就可以了.

首先整理一下超音波的相關知識. 所謂超音波是指超出人耳所能聽到的頻率 20KHz 的音波, 人類的聽覺一般只能聽到 20Hz~20KHz 左右的音域, 但某些動物如狗, 海豚或蝙蝠等, 可以聽到 20KHz 以上的超音波, 例如海豚利用超音波傳遞溝通訊息; 而蝙蝠則利用超音波在飛行中定位以避開障礙物, 參考 :

# Wiki : 超音波

超音波於 19 世紀被發現, 並於第一次世界大戰首次應用於潛水艇之偵測, 如今在工業, 醫學, 與農業上都有廣泛之應用, 例如非破壞性檢測中的孔隙偵測, 孕婦姙娠掃描, 人體組織斷層掃描, 物件清洗, 以及蔬果品質檢測等等, 參考 :

# 超音波 - 國立嘉義大學 (pdf)

在空氣中傳遞的超音波其頻率在 20K~200KHz 之間, 頻率越高, 衰減程度越大, 可傳播的距離越短. 一般常用的超音波模組, 其頻率通常是 38K, 40K, 或 42KHz 這三種. 我買的 HC-SR04 超音波模組使用的是 40KHz 的超音波, 可探測距離為 2cm~400cm. 它有如下所示的 Vcc (5V), Gnd, Trig, Echo 四隻腳 :

Source : fritzing

其規格如下 :


只要在 Trig 腳送進至少維持 10 微秒以上的高位準訊號, 便能觸發模組中的超音波發射器送出 8 個連續的 40KHz 超音波脈衝, 接收器收到反射波後便會在 Echo 腳輸出一個與量測距離成正比的高位準脈衝, 此 HIGH 位準脈衝上緣可以看成超音波開始發射時間; 而下緣則是接收到反射波的時間, 所以整個高位準脈衝的寬度就是超音波往返的總時間, 參考 :

# HC-SR04 規格 - Micropik (pdf)
# HC-SR04 超音波感測器介紹
# [X-LAN] Arduino HC-SR04 超音波測距傳感器 超音波測距儀(附範例) $35

使用超音波量測距離必須知道音波的傳遞速度, 這主要取決於大氣的密度, 而溫度又是影響空氣密度的主要因素. 音速與溫度的關係如下 :

音速=331.5 m/s + 0.6*攝氏溫度

在常溫 20 度時, 音速是 331.5+0.6*20=343.5 m/s, 大約是 344 m/s.

超音波測距的原理是利用一個超音波發射器與一個接收器組成的模組來量測音波從發射到收到反射波的時間, 乘以音速即可得到音波往返的距離, 除以 2 即得與反射物體間的距離. 在常溫 20 度下, 音波前進 1 公分約需 58 微秒, 計算如下 :

1 公分=0.01 公尺=(344 公尺/秒*t)/2  

此處除以 2 是因為音波花了 t 秒往返走了兩倍距離, 須除以 2 才是單程距離.

t=(0.01*2)/344=5.8*10e-5=58*10e-6 秒=58 微秒

注意, 這個 58 微秒是超音波往返距離 1 cm 障礙物所花的時間. 而上述 HC-SR04 的 Echo 腳輸出的高位準脈衝, 其時長即是超音波往返所花的時間, 因此只要用 Arduino 的 parseIn() 函數量取 Echo 的脈衝時長, 再除以 58, 即可得到與反射超音波的障礙物之間的距離是幾公分了. 關於 parseIn() 參考 :

# parseIn()

此函數有 2 或 3 個參數 :

pulseIn(pin, value)
pulseIn(pin, value, timeout)

第一參數 pin 是 Arduino 接 HC-SR04 的 Echo 腳之 DIO 數位腳位編號, 第二參數 value 是要偵測的位準 (LOW/HIGH), 對於 HC-SR04 而言要用 HIGH, 第三參數 timeout 是等候脈衝出現的延遲時間 (單位是微秒), 預設是 1 秒 (即 1000000). 如果在 timeout 時間內未偵測到脈衝, 則回傳 0.

下面程式是我參考 Arduino 互動設計入門 2 改編, 用來測試與障礙物的距離 :

測試 1 :

const byte trigPin=5; //Output pin to trigger ultra sound
const byte echoPin=6; //Input pin to receive echo pulse

void setup() {
  pinMode(trigPin, OUTPUT); //Arduino pin default input
  Serial.begin(9600);
  }

void loop() {
  unsigned long d=ping()/58; //calculate distance
  Serial.print(d);
  Serial.println("cm");
  delay(1000);
  }

unsigned long ping() { //send 10us pulse to HC-SR04 trigger pin
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);  //sustain at least 10us HIGH pulse
  digitalWrite(trigPin, LOW);
  return pulseIn(echoPin, HIGH);
  }


此程式將 D5 連接到 HC-SR04 的 Trig 腳, D6 接到 Echo 腳, 由於 Arduino 的數位接腳預設是 INPUT, 因此只需要在 setup() 中定義 D5 為 OUTPUT 即可. 這裡 ping() 函數用來對 HC-SR04 的 Trig 腳送出 10 微秒的 HIGH 準位脈衝, 以觸發發射器送出超音波, 然後用 pulseIn() 函數測量 Echo 腳送出的 HIGH 脈衝寬度傳回. 此值為超音波往返的時間 (微秒), 將其除以 58 即得與障礙物之間的距離 (公分).

序列埠監控視窗擷取訊息如下 :

36cm
19cm
16cm
17cm
47cm
3736cm
6cm
59cm
48cm
3731cm
6cm
3744cm
6cm
3761cm

我用紙板當障礙物來反射超音波, 其中 37XX 公分是突然將紙板拿開, 使得超音波沒有在有效距離 400 公分內遇到障礙物, ping() 函數就回傳了這個異常數字. 我用尺校對測量值發現還蠻準的, 誤差不大.

上例中將量測到的超音波往返時間除以其行進 1 公分所需時間 58 微秒得到距離的方法, 似乎讓人難以直接理解, 我甚至覺得這樣算出來的應該是往返的距離吧! 實際上這是單程的距離沒錯, 因為兩個雙程時間相除後,  2 消掉了, 所以得到的是單程距離.

在 "Make : 感測器, 運用 Arduinio 和 Raspberry Pi 感測的專題與實驗" 這本書第三章的超音波測距範例中, 我找到一個比較容易讓人理解的測距程式寫法, 改編如下 :

測試 2 :

const byte trigPin=5; //Output pin to trigger ultra sound
const byte echoPin=6; //Input pin to receive echo pulse
const float temperature=20; //Celsius degree
float v=331.5 + 0.6*temperature; //sound velocity


void setup() {
  pinMode(trigPin, OUTPUT); //Arduino pin default input
  Serial.begin(9600);
  }

void loop() {
  int d=ping(); //get distance (cm)
  Serial.print(d);
  Serial.println("cm");
  delay(1000);
  }

float ping() { //send 10us pulse to HC-SR04 trigger pin
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);  //sustain at least 10us HIGH pulse
  digitalWrite(trigPin, LOW);
  unsigned long tUs=pulseIn(echoPin, HIGH); //return micro seconds
  float t=tUs/1000.0/1000.0/2; //turn us to s of single way
  float d=t*v; //single way distance (meter)
  return d*100; //turn m to cm
  }

此程式將上述聲波速度隨溫度而變的公式寫進來, 只要改變 temperature 常數, 波速也會隨之變化. 在 ping() 函數中, pulseIn() 傳回的是音波往返的時間 tUs, 單位為微秒, 除以 1000000 即變成以秒為單位, 除以 2 即是單程傳遞時間 t, 將其乘以波速 v 即得單程行進距離, 也就是超音波模組與障礙物之距離, 這樣寫可能比較好理解.

在自走車的避障實驗中, 如果要在距離障礙物 5 公分以內時發出警告音該怎麼做呢? 參考之前做的 Arduino 聲音測試中的測試 1 :

# Arduino 的聲音測試 (一)

我將上面測試 2 改為如下測試 3 :

測試 3 :

const byte trigPin=5; //Output pin to trigger ultra sound
const byte echoPin=6; //Input pin to receive echo pulse
const float temperature=20; //Celsius degree
float v=331.5 + 0.6*temperature; //sound velocity
int buzzerPin=9;  //D9 conectted to a buzzer

void setup() {
  pinMode(trigPin, OUTPUT); //Arduino pin default input
  Serial.begin(9600);
  }

void loop() {
  int d=ping(); //get distance (cm)
  Serial.print(d);
  Serial.println("cm");
  if (d < 5) {alarmBeep(buzzerPin);}  //beep when distance less than 5cm
  delay(1000);
  }

float ping() { //send 10us pulse to HC-SR04 trigger pin
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);  //sustain at least 10us HIGH pulse
  digitalWrite(trigPin, LOW);
  unsigned long tUs=pulseIn(echoPin, HIGH); //return micro seconds
  float t=tUs/1000.0/1000.0/2; //turn us to s of single way
  float d=t*v; //single way distance (meter)
  return d*100; //turn m to cm
  }

 void alarmBeep(int pin) {
  tone(pin, 1000, 1000);
  delay(2000); 
  } 

不過這個程式有個缺點, 當距離小於 5cm 時會受到 alarmBeep() 中的 delay() 影響, 使得讀取距離的動作被耽擱了 2 秒.




沒有留言 :