Semorphe

測距離

用聲音的來回時間換算距離。 · ⏱ 約 35 分鐘

你會學到三件事

  1. 超音波感測器怎麼運作:送一聲、量它多久回來
  2. pulseIn 量一個脈衝有多長
  3. 為什麼這一課非得寫成一個函式不可

開始之前

接線:HC-SR04 的 Trig 接 9 號腳、Echo 接 10 號腳、 VCC 接 5V、GND 接 GND。

🟢 先把編輯器打開,跟著下面一起做:跟著做

一、原理

①  Trig 送出一個 10 微秒的高電位   →  感測器發出一串超音波
②  聲音撞到東西彈回來
③  Echo 這支腳變高,持續的時間 = 聲音來回花的時間

聲音在空氣中大約 340 公尺/秒,也就是 0.034 公分/微秒。

所以:

來回距離 = 時間 × 0.034
單程距離 = 時間 × 0.034 ÷ 2 = 時間 ÷ 58

⚠️ 那個「除以 2」不能忘:量到的是來回,而你要的是單程。 忘了的話所有距離都會是兩倍,而它看起來只是「不太準」。

二、送出那一聲

照著打上這一段,它會送出一次超音波:

1digitalWrite(9, LOW);2delayMicroseconds(2);3digitalWrite(9, HIGH);4delayMicroseconds(10);5digitalWrite(9, LOW);
9送出超音波觸發脈衝到腳位定義函式 回傳型別void(無回傳)loop1

規格書要求:一個乾淨的 10 微秒高電位。

前面那個 LOW + 2 微秒是「先確保它是低的」—— 不然上一次留下的殘留會讓這一次的脈衝長度不對。

積木盤上這五行收成了一顆,寫著「送出超音波觸發脈衝到腳位 …」 ——⚠️ 而底下算距離那一段用的是「呼叫函式 …(…)」。

delayMicroseconds 和 delay 是同一件事,單位差一千倍 (1 毫秒 = 1000 微秒)。

三、量它多久回來

pulseIn 會等那一聲回來,回傳的是微秒:

1long t = pulseIn(10, HIGH);
10HIGH測量腳位的脈衝長度 逾時微秒宣告long變數t=1

pulseIn(腳, HIGH) 的意思是「等這支腳變高,然後量它高了多久」, 回傳的單位是微秒。

⚠️ pulseIn 會卡住整支程式,直到它量到(或超時)。 所以在它等待的那段時間,你的板子什麼都做不了。這是它的代價。

⚠️ 用 long 不用 int:int 在 Uno 上只裝得下 32767, 而 pulseIn 的值輕易就超過。溢位不會報錯,只會給你一個負數。

四、🔴 為什麼要寫成函式

上面那六行,每量一次距離就要全部再寫一次。

而「測距」在一支程式裡通常要用好幾次——判斷障礙、記錄、顯示。 所以它應該是一個名字:

1long distance() {2    digitalWrite(9, LOW);3    delayMicroseconds(2);4    digitalWrite(9, HIGH);5    delayMicroseconds(10);6    digitalWrite(9, LOW);7    long t = pulseIn(10, HIGH);8    return t / 58;9}
910HIGH測量腳位的脈衝長度 逾時微秒變數t58÷回傳宣告long變數t=送出超音波觸發脈衝到腳位定義函式 回傳型別longdistance1278

之後就只要寫 distance()。

函式不是「進階語法」——它是你第一次覺得 「這六行我不想再寫一遍」的時候,自然會想要的東西。

完成的樣子

這一課的參考做法——程式碼每一行的號碼,就印在對應的那塊積木上。

1long distance() {2    digitalWrite(9, LOW);3    delayMicroseconds(2);4    digitalWrite(9, HIGH);5    delayMicroseconds(10);6    digitalWrite(9, LOW);7    long t = pulseIn(10, HIGH);8    return t / 58;9}10 11void setup() {12    Serial.begin(9600);13    pinMode(9, OUTPUT);14    pinMode(10, INPUT);15}16 17void loop() {18    Serial.println(distance());19    delay(200);20}
910HIGH測量腳位的脈衝長度 逾時微秒變數t58÷回傳宣告long變數t=送出超音波觸發脈衝到腳位96009OUTPUT10INPUT設定腳位為設定腳位為開啟序列埠 傳輸速率呼叫函式distance200等待毫秒序列埠輸出小數位數並換行定義函式 回傳型別void(無回傳)loop定義函式 回傳型別void(無回傳)setup定義函式 回傳型別longdistance121171217813181419

換你了

加一顆蜂鳴器:越近叫得越急。

提示:delay 的長度用 distance() 算出來—— 近的時候 delay 短,遠的時候 delay 長。(倒車雷達就是這樣做的。)

到編輯器做這一題:練習:加一顆蜂鳴器,越近叫得越急

做一個

用超音波量你桌上某個東西的長度,再拿尺量一次,把兩個數字都用序列埠印出來。

🔴 這一題的答案不在程式裡,在兩個數字差多少。 ⚠️ 量五次,你會發現它們不一樣。感測器的讀值本來就會跳,那要怎麼辦? 取平均?取中位數?丟掉最大最小?跟旁邊的人討論一下你們各選了哪一種。

到編輯器做這一題:做一個:拿它去量一個真的東西

這一課你做了什麼

如果卡住了

你看到 多半是因為
距離都是 0 Echo 腳接錯,或感測器沒供電
距離是實際的兩倍 忘了除以 2(也就是除以 58 寫成除以 29)
出現負數 用了 int。要用 long
程式偶爾卡住不動 pulseIn 沒量到就會等到超時。那是正常的
讀數跳來跳去 超音波會被斜面和軟布吸走。多量幾次取中位數
在編輯器打開這一課 →