หุ่นยนต์เดินตามเส้นด้วย ESP32: ต่อวงจร เขียนโค้ด และปรับให้นิ่ง

สร้างหุ่นยนต์เดินตามเส้นด้วยเซนเซอร์ 3 ช่อง เทียบเซนเซอร์ 2 ตัวกับ 3 ตัว ตรรกะเลี้ยวซ้ายขวา ทำไมหุ่นส่าย และใช้ P-control ให้วิ่งนิ่ง ลองในตัวจำลองได้ทันที

อัปเดต 2026-11-02

หุ่นยนต์เดินตามเส้นมองพื้นด้วยเซนเซอร์แสงสะท้อนเรียงกันใต้หัวรถ แล้วเลี้ยวไปทางที่เส้นอยู่ วิธีที่นิ่งและเขียนง่ายที่สุดสำหรับมือใหม่คือใช้เซนเซอร์ 3 ช่อง คำนวณว่าเส้นเบี้ยวไปทางไหน (error) แล้วเลี้ยวแรงตามขนาดของ error ซึ่งเรียกว่า P-control หุ่นจะวิ่งโค้งได้ลื่นกว่าการสั่งเลี้ยวแบบเต็มที่/ไม่เลี้ยว และส่ายน้อยกว่ามาก

เปิดหุ่นเดินตามเส้นในตัวจำลอง →

เซนเซอร์ตามเส้นทำงานอย่างไร

โมดูลยอดนิยมคือ TCRT5000 ซึ่งมี LED อินฟราเรดกับตัวรับแสงอยู่คู่กัน พื้นสีขาวสะท้อนแสงกลับมามาก เส้นสีดำสะท้อนน้อย วงจรเปรียบเทียบบนโมดูลแปลงเป็นสัญญาณดิจิทัลที่ขา DO และมี trimpot ให้ปรับจุดตัด แบบที่สะดวกที่สุดคือบอร์ดที่รวม 3 หรือ 5 ช่องไว้ในแผ่นเดียว ระยะห่างแต่ละช่องพอดีกับเทปพันสายไฟกว้างราว 18 mm

เซนเซอร์ 2 ตัว หรือ 3 ตัว

2 ตัว (คร่อมเส้น)3 ตัว (ซ้าย กลาง ขวา)
ตอนอยู่ตรงเส้นทั้งสองตัวไม่เห็นเส้นตัวกลางเห็นเส้น
ตอนหลุดเส้นทั้งสองตัวไม่เห็นเส้น เหมือนตอนอยู่ตรงเส้นไม่มีตัวไหนเห็นเส้น แยกออกได้ชัด
บอกได้ว่าเบี้ยวแค่ไหน2 ระดับ: ซ้าย / ขวา5 ระดับ: ซ้ายมาก ซ้ายนิด ตรง ขวานิด ขวามาก
เหมาะกับเส้นตรงและโค้งกว้าง ความเร็วต่ำสนามที่มีโค้งแคบ อยากวิ่งเร็วขึ้น

ปัญหาหลักของแบบ 2 ตัวคือแยกไม่ออกระหว่าง “อยู่ตรงเส้นพอดี” กับ “หลุดเส้นไปแล้ว” ถ้าใช้ 2 ตัวต้องจำว่าเห็นเส้นครั้งล่าสุดที่ฝั่งไหน ตัวที่ 3 ตรงกลางแก้ปัญหานี้ได้เกือบหมด และยังบอกระดับความเบี้ยวได้ละเอียดขึ้น ซึ่งเป็นสิ่งที่ P-control ต้องการ

ทำไมหุ่นส่าย และ P-control ช่วยอย่างไร

ตรรกะที่เขียนกันบ่อยคือ “เห็นเส้นทางซ้ายให้เลี้ยวซ้ายเต็มที่ เห็นทางขวาให้เลี้ยวขวาเต็มที่” (bang-bang) หุ่นจะเลี้ยวเกินทุกครั้ง แล้วต้องเลี้ยวกลับ จึงวิ่งส่ายเป็นงูไปตลอดทาง ยิ่งวิ่งเร็วยิ่งส่ายแรงจนหลุดโค้ง

P-control แปลงตำแหน่งของเส้นเป็นตัวเลข แล้วเลี้ยวแรงตามตัวเลขนั้น:

ซ้ายกลางขวาerrorสั่ง
●−1เลี้ยวซ้ายแรง
●●−0.5เลี้ยวซ้ายนิดหน่อย
●0ตรงไป
●●+0.5เลี้ยวขวานิดหน่อย
●+1เลี้ยวขวาแรง
±2หลุดเส้น: เลี้ยวแรงไปทางที่เห็นเส้นครั้งล่าสุด
error  = (ขวา − ซ้าย) ÷ จำนวนตัวที่เห็นเส้น
เลี้ยว = KP × error
ล้อซ้าย = ความเร็ว + เลี้ยว,  ล้อขวา = ความเร็ว − เลี้ยว

ค่า KP คือ “ความแรงของการแก้” ถ้าน้อยไปหุ่นเลี้ยวไม่ทันโค้ง ถ้ามากไปจะกลับไปส่ายเหมือน bang-bang นอกจากนี้ให้ชะลอเมื่อ error มาก หุ่นจะไม่หลุดโค้งแคบ

ชิ้นส่วน

ใช้หุ่น 2 ล้อพื้นฐานชุดเดียวกับ บทความ TB6612FNG แล้วเพิ่มเซนเซอร์ตามเส้น 1 แผ่น:

ชิ้นส่วนสำหรับ ESP32 DevKit V1
ชิ้นส่วนจำนวนราคาประมาณร้าน
ESP32 DevKit V1 (30 ขา)1฿130–฿250Shopee
TB6612FNG motor driver1฿50–฿120Shopee
ชุดแชสซี 2WD
มีมอเตอร์ TT 2 ตัว ล้อ 2 ล้อ และล้อ caster
1฿150–฿300Shopee
ถ่าน Li-ion 186502฿60–฿150Shopee
รางถ่าน 18650 แบบ 2 ก้อน (2S)1฿20–฿60Shopee
Buck converter 7.4 V → 5 V 3 A1฿30–฿90Shopee
สวิตช์เปิด-ปิด1฿5–฿20Shopee
สายจัมเปอร์ ผู้-ผู้ / ผู้-เมีย ชุด 40 เส้น1฿30–฿60Shopee
แนะนำเพิ่ม
ที่ชาร์จถ่าน 186501฿100–฿250Shopee
รวมโดยประมาณ฿535–฿1,200

ราคาประมาณการ ณ 2026-10-31 ยังไม่ได้ตรวจกับร้าน · บางลิงก์เป็นลิงก์ affiliate (อ่านเพิ่ม)

เพิ่มจำนวนหมายเหตุ
โมดูลเซนเซอร์ตามเส้น TCRT5000 แบบ 3 ช่อง (ขา DO)1หรือ TCRT5000 แบบช่องเดียว 3 ตัว แบบ 5 ช่องก็ใช้ได้ (ใช้ 3 ช่องกลาง)
เทปพันสายไฟสีดำ กว้าง 18–19 mm1 ม้วนติดบนพื้นสีขาว กระดาน หรือแผ่นฟิวเจอร์บอร์ด

การต่อขา

ใช้วงจรมอเตอร์เดิมทั้งหมด แล้วต่อเซนเซอร์ตามเส้นเพิ่ม:

เซนเซอร์ต่อไปที่ ESP32
VCC3V3
GNDGND
DO ซ้าย (L)GPIO 32
DO กลาง (C)GPIO 33
DO ขวา (R)GPIO 35
การต่อขาบน ESP32 DevKit V1 (จัดขาอัตโนมัติจากตัวจำลอง)
TB6612FNG
Motors
VM → แบต 7.4 V
VCC → 3V3
GND → GND
PWMA → GPIO25
AIN1 → GPIO27
AIN2 → GPIO14
PWMB → GPIO26
BIN1 → GPIO13
BIN2 → GPIO4
STBY → 3V3 (ต่อค้างไว้)

โค้ด (Arduino IDE)

const int PWMA = 25, AIN1 = 27, AIN2 = 14;   // ล้อซ้าย
const int PWMB = 26, BIN1 = 13, BIN2 = 4;    // ล้อขวา
const int EYE_L = 32, EYE_C = 33, EYE_R = 35;

const int LINE = HIGH;     // ค่าที่ DO ให้เมื่ออยู่บนเส้นดำ (โมดูลบางรุ่นให้ LOW)
const int SPEED = 150;     // ความเร็วตอนเส้นอยู่ตรงกลาง (0–255)
const float KP = 90;       // เริ่มที่ค่านี้ แล้วปรับตามหัวข้อถัดไป
float last = 0;            // error ล่าสุดที่ยังเห็นเส้น

void motor(int pwm, int in1, int in2, int speed) {
  speed = constrain(speed, -255, 255);
  digitalWrite(in1, speed > 0);
  digitalWrite(in2, speed < 0);
  analogWrite(pwm, abs(speed));
}

void setup() {
  Serial.begin(115200);
  int pins[] = { PWMA, AIN1, AIN2, PWMB, BIN1, BIN2 };
  for (int p : pins) pinMode(p, OUTPUT);
  pinMode(EYE_L, INPUT);
  pinMode(EYE_C, INPUT);
  pinMode(EYE_R, INPUT);
}

void loop() {
  bool l = digitalRead(EYE_L) == LINE;
  bool c = digitalRead(EYE_C) == LINE;
  bool r = digitalRead(EYE_R) == LINE;
  int seen = l + c + r;

  float error;
  if (seen) {
    error = float(r - l) / seen;     // −1 = เส้นอยู่ใต้ตาซ้าย, +1 = ใต้ตาขวา
    last = error;
  } else {
    error = last >= 0 ? 2 : -2;      // หลุดเส้น: เลี้ยวแรงไปทางที่เห็นครั้งสุดท้าย
  }

  int base = SPEED * max(0.2f, 1 - 0.4f * abs(error));   // เลี้ยวแรง = ชะลอ
  int turn = KP * error;
  motor(PWMA, AIN1, AIN2, base + turn);   // เส้นอยู่ขวา → ล้อซ้ายเร็วขึ้น → เลี้ยวขวา
  motor(PWMB, BIN1, BIN2, base - turn);
  delay(5);
}

ปรับให้นิ่ง

  1. เช็กทิศก่อน ยกหุ่นขึ้น เอาเส้นดำไปไว้ใต้ตาขวา ล้อซ้ายต้องหมุนเร็วกว่าล้อขวา ถ้ากลับกัน สลับ + turn กับ - turn
  2. เริ่มช้า ตั้ง SPEED ต่ำ (เช่น 110) แล้วหาค่า KP ที่หุ่นตามเส้นได้ครบรอบ
  3. เพิ่ม KP จนเริ่มส่าย แล้วลดลงราวหนึ่งในสาม นั่นคือค่าที่เลี้ยวทันและยังนิ่ง
  4. ค่อยๆ เพิ่มความเร็ว ทุกครั้งที่เร็วขึ้นอาจต้องปรับ KP ใหม่
  5. ยังส่ายที่ความเร็วสูง ขั้นต่อไปคือเพิ่มเทอม D (PD-control): ลบด้วย KD × (error − errorก่อนหน้า) ช่วยหน่วงการแกว่ง

เรื่องทางกลก็มีผลมาก: ติดเซนเซอร์ให้อยู่หน้าล้อออกไปพอสมควร (ราว 5–10 cm) หุ่นจะเห็นโค้งก่อนถึงและเลี้ยวได้ทัน และใช้ล้อหน้าแบบลูกบอล (caster) ที่หมุนลื่น

ลองในตัวจำลอง

สนามในตัวจำลองเป็นเส้นแปดเหลี่ยมรอบสนาม หุ่นมีเซนเซอร์มองพื้น 3 ตัวที่ด้านหน้า (ซ้าย กลาง ขวา) ห่างกันพอดีกับความกว้างเส้น และโปรแกรมตัวอย่างใช้ P-control แบบเดียวกับโค้ดด้านบน ในการทดสอบ หุ่นวิ่งได้ราว 2 รอบต่อนาทีโดยตัวรถไม่ออกห่างจากกลางเส้นเกินราว 2 cm

ความต่างจากของจริง ในตัวจำลองพื้นเป็นสีเข้มและเส้นเป็นสีขาว และใช้ color sensor มองสีพื้นแทน TCRT5000 หลักการเหมือนกันทุกอย่าง แค่ค่าที่บอกว่า “อยู่บนเส้น” เป็นชื่อสี 'white' แทน HIGH / LOW ถ้าเปิดหน้า Wiring จะเห็นว่า color sensor TCS34725 สามตัวใช้ address I²C ซ้ำกัน ต้องใช้ตัวแยกบัส TCA9548A นี่เป็นอีกเหตุผลที่ของจริงควรใช้ TCRT5000 ซึ่งถูกกว่าและอ่านได้ด้วย digitalRead() ธรรมดา

ลองโปรแกรมนี้ในตัวจำลอง →

ฝังตัวจำลองนี้ในเว็บหรือบล็อกของคุณ

คัดลอกโค้ดนี้ไปวางใน HTML ของหน้าเว็บ ผู้อ่านจะเห็นสนามจำลองพร้อมปุ่มรันโปรแกรมตัวอย่าง

<iframe src="https://ai-rover.app/app/?preset=line-follower&embed=1" title="หุ่นยนต์เดินตามเส้น – AI Rover Studio" width="720" height="520" style="max-width:100%;border:0;border-radius:12px" loading="lazy"></iframe>

ต่อยอด