自走車避障應該是Maker圈最經典的入門專案之一了,原理其實不難,重點反而是接線跟角度架設這些細節,今天把整套流程講清楚。
HC-SR04的量測原理
簡單講,Trig腳送出一個10微秒的高電位訊號,模組會發出超音波,碰到物體反射回來後Echo腳會輸出一個高電位,持續時間就是聲波來回的時間——距離=時間×音速/2。程式庫(NewPing或直接自己算pulseIn)都幫你包好了,不用自己算數學。
組裝建議
- 感測器建議裝在車頭正前方,搭配壓克力固定支架比較穩,如果直接用熱熔膠黏,車子晃動幾次角度就跑掉了
- 量測角度大概15度左右是盲區容易漏測的範圍,如果預算夠可以裝兩顆分別偏左右各15度,避障判斷會準很多
- 馬達驅動用L298N接雙TT馬達,PWM控速+方向控制一次到位,這顆模組電流承受夠大,新手不容易燒板
邏輯上很簡單:距離小於設定門檻(例如15公分)就停下來倒車+轉向,沒有障礙物就直走。真正難的地方反而是「轉多久算轉夠」這種要實際測試調參數的細節,建議先固定轉向時間寫死測試,跑順了再考慮加陀螺儀做精準轉向。
材料清單(BOM)
| 品項 | 數量 | 備註 |
|---|---|---|
| Arduino UNO R3 開發板 | 1 | 本篇教學使用的開發板 |
| HC-SR04 超聲波測距模組 | 1 | |
| HC-SR04 超聲波傳感器固定支架 | 1 | 選用但建議,熱熔膠固定容易晃動跑位 |
| L298N DC馬達驅動模組 | 1 | |
| 雙軸 TT馬達 直流減速馬達 | 2 | |
| 電池組(供電馬達) | 1 | 依車體設計自備,建議7.4V鋰電池或6顆3號電池 |
腳位接線對照表
| 開發板腳位 | 模組腳位 | 說明 |
|---|---|---|
| 5V | HC-SR04 VCC | 感測器供電 |
| GND | HC-SR04 GND | 共地 |
| D9 | HC-SR04 Trig | 觸發訊號輸出 |
| D10 | HC-SR04 Echo | 回波訊號輸入 |
| D5 (PWM) | L298N ENA | 左馬達速度控制 |
| D6 (PWM) | L298N ENB | 右馬達速度控制 |
| D2 | L298N IN1 | 左馬達方向控制1 |
| D3 | L298N IN2 | 左馬達方向控制2 |
| D4 | L298N IN3 | 右馬達方向控制1 |
| D7 | L298N IN4 | 右馬達方向控制2 |
| GND | L298N GND | 共地(邏輯地與馬達電源地務必共地) |
📷 〔照片預留位:請在這裡插入實際接線/組裝照片〕
完整程式碼
// HC-SR04超音波避障自走車
#define TRIG_PIN 9
#define ECHO_PIN 10
#define ENA 5
#define ENB 6
#define IN1 2
#define IN2 3
#define IN3 4
#define IN4 7
const int STOP_DISTANCE = 15; // 公分,低於這個距離視為有障礙物
const int TURN_TIME = 400; // 毫秒,轉向持續時間(依實際車體調整)
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
Serial.begin(9600);
}
long readDistanceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms逾時,避免沒有回波時整個程式卡住
if (duration == 0) return -1; // 沒有量到距離
return duration * 0.0343 / 2; // 距離(公分) = 時間 * 音速 / 2
}
void goForward() {
analogWrite(ENA, 200);
analogWrite(ENB, 200);
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
}
void goBackward() {
analogWrite(ENA, 200);
analogWrite(ENB, 200);
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
}
void turnRight() {
analogWrite(ENA, 180);
analogWrite(ENB, 180);
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
}
void stopCar() {
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
}
void loop() {
long distance = readDistanceCm();
if (distance > 0 && distance < STOP_DISTANCE) {
stopCar();
delay(200);
goBackward();
delay(300);
turnRight();
delay(TURN_TIME); // 轉多久算轉夠,建議先固定時間測試,跑順了再考慮加陀螺儀
stopCar();
} else {
goForward();
}
}
