首頁 › 動手做 › ESP32 循跡車

ESP32 循跡車:讓小車沿著黑線自己跑

難度:入門時間:約 60 分鐘沿用避障小車車體ESP32 套件 3.x

循跡車是機器人競賽最經典的入門題目。兩個紅外線感測器、一段黑色膠帶,就能讓車子自己沿著路線跑,還能順便學會「根據感測器修正方向」這個控制觀念。

✓ 本文程式已在 2026/09/29 以 Arduino ESP32 開發板套件 3.3.12 編譯通過(開啟全部編譯警告,0 個警告),開發板選 ESP32 Dev Module。

1. 需要的零件

零件數量說明
避障小車車體(ESP32、L298N、馬達、電池)1見 避障小車
TCRT5000 紅外線循跡模組2有藍色可調電阻、DO 數位輸出的那種
黑色電工膠帶1 捲寬約 1.5–2 cm
白色地板、白色海報紙或珍珠板當場地

2. 運作原理

TCRT5000 發出紅外線,白色地面反射強、黑色膠帶反射弱。模組上的比較器把這個差異變成數位訊號:多數模組照到黑線時 DO 輸出 HIGH。

兩個感測器裝在車頭,左右各一,中間夾著黑線:

左感測器右感測器代表動作
白白線在兩個感測器中間直走
黑白車子偏右了往左修正
白黑車子偏左了往右修正
黑黑十字路口或衝出彎道沿上次修正方向找線,找不到就停

3. 接線

馬達與 L298N 接法和避障小車完全相同,只要加上兩個感測器:

從接到備註
左 TCRT5000 DOESP32 GPIO 34GPIO 34/35 是純輸入腳,正好適合
右 TCRT5000 DOESP32 GPIO 35
兩個模組 VCCESP32 3V3用 3.3V 供電,輸出就是 3.3V,不用分壓
兩個模組 GNDGND

安裝位置:感測器離地約 1 公分,兩個感測器的間距比黑線稍寬,約 2.5–3 公分。

4. 調整靈敏度(最重要的一步)

  1. 先上傳程式,把車子架空。
  2. 把感測器放在白色地面上,轉模組上的藍色可調電阻,直到板子上的指示燈剛好熄滅。
  3. 移到黑線上,指示燈應該亮起。兩個模組都要調。
  4. 如果剛好相反,把程式的 LINE_IS_HIGH 改成 false。

5. 程式

/*
 * TinyBot Forge — ESP32 循跡小車(兩路紅外線)
 * https://tinybotforge.com
 *
 * 硬體:ESP32 DevKit V1、TCRT5000 紅外線循跡模組 ×2、L298N、兩顆 TT 馬達
 * 場地:白色地面貼 1.5–2 cm 寬黑色膠帶
 * 環境:Arduino IDE 2.x + ESP32 開發板套件 3.x
 *
 * 多數 TCRT5000 模組:照到黑線時 DO 輸出 HIGH,照到白色輸出 LOW。
 * 你的模組相反的話,把 LINE_IS_HIGH 改成 false。
 */

const int PIN_SENSOR_L = 34;   // GPIO34/35 只能輸入,拿來接感測器剛好
const int PIN_SENSOR_R = 35;
const bool LINE_IS_HIGH = true;

const int PIN_ENA = 25, PIN_IN1 = 26, PIN_IN2 = 27;
const int PIN_ENB = 33, PIN_IN3 = 32, PIN_IN4 = 13;
const int PWM_FREQ = 1000, PWM_BITS = 8;

const int SPEED_STRAIGHT = 150;
const int SPEED_FAST = 170;      // 修正方向時外側輪
const int SPEED_SLOW = -60;      // 修正方向時內側輪(稍微倒轉,轉得比較急)
const unsigned long LOST_MS = 1500;  // 兩邊都找不到線超過這麼久就停車

int lastTurn = 0;  // -1 最後往左修、1 往右修
unsigned long lostSince = 0;

void setMotor(int pinEn, int pinA, int pinB, int speed) {
  speed = constrain(speed, -255, 255);
  digitalWrite(pinA, speed > 0 ? HIGH : LOW);
  digitalWrite(pinB, speed < 0 ? HIGH : LOW);
  ledcWrite(pinEn, abs(speed));
}

void drive(int left, int right) {
  setMotor(PIN_ENA, PIN_IN1, PIN_IN2, left);
  setMotor(PIN_ENB, PIN_IN3, PIN_IN4, right);
}

bool onLine(int pin) {
  return digitalRead(pin) == (LINE_IS_HIGH ? HIGH : LOW);
}

void setup() {
  Serial.begin(115200);
  pinMode(PIN_SENSOR_L, INPUT);
  pinMode(PIN_SENSOR_R, INPUT);
  pinMode(PIN_IN1, OUTPUT); pinMode(PIN_IN2, OUTPUT);
  pinMode(PIN_IN3, OUTPUT); pinMode(PIN_IN4, OUTPUT);
  ledcAttach(PIN_ENA, PWM_FREQ, PWM_BITS);
  ledcAttach(PIN_ENB, PWM_FREQ, PWM_BITS);
  drive(0, 0);
  delay(2000);
}

void loop() {
  bool l = onLine(PIN_SENSOR_L);
  bool r = onLine(PIN_SENSOR_R);

  if (!l && !r) {
    // 兩個感測器夾著黑線:直走
    drive(SPEED_STRAIGHT, SPEED_STRAIGHT);
    lostSince = 0;
  } else if (l && !r) {
    // 左邊壓到線:車子偏右了,往左修
    drive(SPEED_SLOW, SPEED_FAST);
    lastTurn = -1;
    lostSince = 0;
  } else if (!l && r) {
    // 右邊壓到線:往右修
    drive(SPEED_FAST, SPEED_SLOW);
    lastTurn = 1;
    lostSince = 0;
  } else {
    // 兩邊都是黑色:可能是十字路口或衝出線外,沿上次方向找線
    if (lostSince == 0) lostSince = millis();
    if (millis() - lostSince > LOST_MS) {
      drive(0, 0);
    } else if (lastTurn < 0) {
      drive(SPEED_SLOW, SPEED_FAST);
    } else {
      drive(SPEED_FAST, SPEED_SLOW);
    }
  }
  delay(5);
}
下載 .ino 檔

6. 場地製作

7. 常見問題

一過彎就衝出去

速度太快,來不及修正。把 SPEED_STRAIGHT 降到 120–130,或讓 SPEED_SLOW 更負,轉得更急。

直線上一直左右搖擺

兩個感測器間距太窄。稍微拉開,讓黑線能完整待在中間。

完全離開線之後車子一直往前開

兩路感測器的限制:兩邊都看到白色時,車子無法分辨「線在中間」還是「整台車跑出去了」。想更穩,下一步可以升級成三路或五路感測器,我們在教材裡會做。

免費領取:ESP32 機器人零件與接線速查表

一張 A4 看懂 ESP32 安全腳位、馬達驅動與感測器接法,可以列印貼在工作桌旁。

免費下載速查表 PDF