หุ่นยนต์เดินตามเส้นด้วย ESP32: ต่อวงจร เขียนโค้ด และปรับให้นิ่ง
สร้างหุ่นยนต์เดินตามเส้นด้วยเซนเซอร์ 3 ช่อง เทียบเซนเซอร์ 2 ตัวกับ 3 ตัว ตรรกะเลี้ยวซ้ายขวา ทำไมหุ่นส่าย และใช้ P-control ให้วิ่งนิ่ง ลองในตัวจำลองได้ทันที
หุ่นยนต์เดินตามเส้นมองพื้นด้วยเซนเซอร์แสงสะท้อนเรียงกันใต้หัวรถ แล้วเลี้ยวไปทางที่เส้นอยู่ วิธีที่นิ่งและเขียนง่ายที่สุดสำหรับมือใหม่คือใช้เซนเซอร์ 3 ช่อง คำนวณว่าเส้นเบี้ยวไปทางไหน (error) แล้วเลี้ยวแรงตามขนาดของ error ซึ่งเรียกว่า P-control หุ่นจะวิ่งโค้งได้ลื่นกว่าการสั่งเลี้ยวแบบเต็มที่/ไม่เลี้ยว และส่ายน้อยกว่ามาก
เปิดหุ่นเดินตามเส้นในตัวจำลอง →
เซนเซอร์ตามเส้นทำงานอย่างไร
โมดูลยอดนิยมคือ TCRT5000 ซึ่งมี LED อินฟราเรดกับตัวรับแสงอยู่คู่กัน พื้นสีขาวสะท้อนแสงกลับมามาก เส้นสีดำสะท้อนน้อย
วงจรเปรียบเทียบบนโมดูลแปลงเป็นสัญญาณดิจิทัลที่ขา DO และมี trimpot ให้ปรับจุดตัด
แบบที่สะดวกที่สุดคือบอร์ดที่รวม 3 หรือ 5 ช่องไว้ในแผ่นเดียว ระยะห่างแต่ละช่องพอดีกับเทปพันสายไฟกว้างราว 18 mm
- ติดเซนเซอร์สูงจากพื้น 3–10 mm สูงกว่านี้แยกขาวดำไม่ออก
- ปรับ trimpot วางเซนเซอร์บนพื้นขาวแล้วบนเส้นดำ หมุนจนไฟ LED บนโมดูลติดและดับตรงตามสองพื้น
- ค่า HIGH / LOW โมดูลส่วนใหญ่ให้ LOW บนพื้นขาวและ HIGH บนเส้นดำ แต่บางรุ่นกลับกัน ให้พิมพ์ค่าออก Serial ดูก่อนเขียนตรรกะ
- ไฟเลี้ยง 3.3 V เลี้ยงโมดูลด้วย 3V3 ของ ESP32 ขา DO จะส่ง 3.3 V ด้วย ถ้าเลี้ยงด้วย 5 V ขา DO อาจส่ง 5 V เข้า ESP32
เซนเซอร์ 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 (30 ขา) | 1 | ฿130–฿250 | Shopee |
| TB6612FNG motor driver | 1 | ฿50–฿120 | Shopee |
| ชุดแชสซี 2WD มีมอเตอร์ TT 2 ตัว ล้อ 2 ล้อ และล้อ caster | 1 | ฿150–฿300 | Shopee |
| ถ่าน Li-ion 18650 | 2 | ฿60–฿150 | Shopee |
| รางถ่าน 18650 แบบ 2 ก้อน (2S) | 1 | ฿20–฿60 | Shopee |
| Buck converter 7.4 V → 5 V 3 A | 1 | ฿30–฿90 | Shopee |
| สวิตช์เปิด-ปิด | 1 | ฿5–฿20 | Shopee |
| สายจัมเปอร์ ผู้-ผู้ / ผู้-เมีย ชุด 40 เส้น | 1 | ฿30–฿60 | Shopee |
| แนะนำเพิ่ม | |||
| ที่ชาร์จถ่าน 18650 | 1 | ฿100–฿250 | Shopee |
| รวมโดยประมาณ | ฿535–฿1,200 | ||
ราคาประมาณการ ณ 2026-10-31 ยังไม่ได้ตรวจกับร้าน · บางลิงก์เป็นลิงก์ affiliate (อ่านเพิ่ม)
| เพิ่ม | จำนวน | หมายเหตุ |
|---|---|---|
| โมดูลเซนเซอร์ตามเส้น TCRT5000 แบบ 3 ช่อง (ขา DO) | 1 | หรือ TCRT5000 แบบช่องเดียว 3 ตัว แบบ 5 ช่องก็ใช้ได้ (ใช้ 3 ช่องกลาง) |
| เทปพันสายไฟสีดำ กว้าง 18–19 mm | 1 ม้วน | ติดบนพื้นสีขาว กระดาน หรือแผ่นฟิวเจอร์บอร์ด |
การต่อขา
ใช้วงจรมอเตอร์เดิมทั้งหมด แล้วต่อเซนเซอร์ตามเส้นเพิ่ม:
| เซนเซอร์ | ต่อไปที่ ESP32 |
|---|---|
VCC | 3V3 |
GND | GND |
DO ซ้าย (L) | GPIO 32 |
DO กลาง (C) | GPIO 33 |
DO ขวา (R) | GPIO 35 |
| TB6612FNG Motors | VM → แบต 7.4 VVCC → 3V3GND → GNDPWMA → GPIO25AIN1 → GPIO27AIN2 → GPIO14PWMB → GPIO26BIN1 → GPIO13BIN2 → GPIO4STBY → 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);
}
ปรับให้นิ่ง
- เช็กทิศก่อน ยกหุ่นขึ้น เอาเส้นดำไปไว้ใต้ตาขวา ล้อซ้ายต้องหมุนเร็วกว่าล้อขวา ถ้ากลับกัน สลับ
+ turnกับ- turn - เริ่มช้า ตั้ง
SPEEDต่ำ (เช่น 110) แล้วหาค่าKPที่หุ่นตามเส้นได้ครบรอบ - เพิ่ม KP จนเริ่มส่าย แล้วลดลงราวหนึ่งในสาม นั่นคือค่าที่เลี้ยวทันและยังนิ่ง
- ค่อยๆ เพิ่มความเร็ว ทุกครั้งที่เร็วขึ้นอาจต้องปรับ KP ใหม่
- ยังส่ายที่ความเร็วสูง ขั้นต่อไปคือเพิ่มเทอม D (PD-control): ลบด้วย
KD × (error − errorก่อนหน้า)ช่วยหน่วงการแกว่ง
เรื่องทางกลก็มีผลมาก: ติดเซนเซอร์ให้อยู่หน้าล้อออกไปพอสมควร (ราว 5–10 cm) หุ่นจะเห็นโค้งก่อนถึงและเลี้ยวได้ทัน และใช้ล้อหน้าแบบลูกบอล (caster) ที่หมุนลื่น
ลองในตัวจำลอง
สนามในตัวจำลองเป็นเส้นแปดเหลี่ยมรอบสนาม หุ่นมีเซนเซอร์มองพื้น 3 ตัวที่ด้านหน้า (ซ้าย กลาง ขวา) ห่างกันพอดีกับความกว้างเส้น และโปรแกรมตัวอย่างใช้ P-control แบบเดียวกับโค้ดด้านบน ในการทดสอบ หุ่นวิ่งได้ราว 2 รอบต่อนาทีโดยตัวรถไม่ออกห่างจากกลางเส้นเกินราว 2 cm
- เปลี่ยน
KPเป็น 20 แล้ว Run ใหม่ หุ่นจะเลี้ยวไม่ทัน หลุดออกนอกเส้นตรงมุมแล้วต้องหาเส้นกลับ - เปลี่ยน
SPEEDเป็น 1 (เร็วสุด) ดูว่ายังตามเส้นได้ไหม - ใช้ World Editor ติดเทปเพิ่มเพื่อทำโค้งแคบขึ้น หรือทำทางแยก
'white' แทน HIGH / LOW
ถ้าเปิดหน้า Wiring จะเห็นว่า color sensor TCS34725 สามตัวใช้ address I²C ซ้ำกัน ต้องใช้ตัวแยกบัส TCA9548A
นี่เป็นอีกเหตุผลที่ของจริงควรใช้ TCRT5000 ซึ่งถูกกว่าและอ่านได้ด้วย digitalRead() ธรรมดาต่อยอด
- หยุดตามสีบนพื้น ใช้ color sensor TCS34725 หนึ่งตัวมองหาแถบสีแดงบนเส้น แล้วหยุด 3 วินาทีเหมือนป้ายหยุดรถ
- หลบสิ่งกีดขวางบนเส้น เพิ่ม HC-SR04 ด้านหน้า หยุดเมื่อมีของขวาง (ดู หุ่นยนต์หลบสิ่งกีดขวาง)
- แข่งจับเวลา ทำสนามเดียวกันแล้วเทียบเวลาต่อรอบของแต่ละค่า KP และความเร็ว