สร้างรถยนต์อัตโนมัติความเร็วสูง หักหลบแบบ S-Curve ไม่ต้องเบรก! ด้วยเซนเซอร์ Lidar VL53L5CX

Autonomous High-Speed Swerving Vehicle
หุ่นยนต์รถยนต์อัตโนมัติที่มาพร้อมเซนเซอร์ Lidar สุดล้ำ

สวัสดีชาว Maker และนักพัฒนาหุ่นยนต์ทุกคนครับ! 🤖 เวลาที่เราทำหุ่นยนต์หลบหลีกสิ่งกีดขวาง (Obstacle-avoidance robot) แบบเดิมๆ ที่ใช้เซนเซอร์อัลตราโซนิก หุ่นยนต์มักจะมีรูปแบบการวิ่งที่แข็งทื่อ คือ "หยุด-ตรวจจับ-หมุนตัว" ซึ่งทำให้เสียเวลาและเสียความเร็วไปอย่างน่าเสียดาย

แต่วันนี้เราจะพาไปก้าวข้ามขีดจำกัดนั้นครับ! โปรเจกต์นี้เป็นการสร้างรถยนต์อัตโนมัติความเร็วสูง ที่ใช้เซนเซอร์ Lidar แบบ VL53L5CX Multi-Zone Time-of-Flight (ToF) ร่วมกับบอร์ด Raspberry Pi 5 ทำให้หุ่นยนต์สามารถ "หักหลบสิ่งกีดขวางแบบตัว S (S-Curve Swerve)" ได้อย่างลื่นไหลเหมือนคนขับรถหลบสิ่งกีดขวางบนถนนเลยล่ะครับ!

ความลับของเซนเซอร์ VL53L5CX 👁️

ทำไมถึงต้องใช้ VL53L5CX? เพราะเซนเซอร์ตัวนี้ไม่ได้แค่ส่งคลื่นไปกระทบแล้วสะท้อนกลับมาเป็นจุดเดียว แต่มันสามารถสร้าง แผนที่ความลึกเชิงพื้นที่แบบ 8x8 (Spatial depth mapping) หรือพูดง่ายๆ คือมันแบ่งการมองเห็นออกเป็น 64 โซนย่อยๆ ด้วยความเร็ว 15 ครั้งต่อวินาที (15Hz) ทำให้รถมองเห็นว่า "ตรงหน้ามีของขวางอยู่ และด้านซ้าย/ขวามีที่ว่างพอให้หักหลบได้หรือไม่" นั่นเองครับ

💡 Maker's Tip: การเปลี่ยนจากเซนเซอร์ระยะทางธรรมดามาเป็น Lidar หรือ ToF Sensor แบบ Multi-zone จะช่วยให้โปรเจกต์หุ่นยนต์ของคุณฉลาดและมองเห็นสภาพแวดล้อมได้กว้างขึ้นมากครับ!

หากเพื่อนๆ กำลังมองหาบอร์ดตัวแรงอย่าง Raspberry Pi 5, เซนเซอร์ Lidar, มอเตอร์ หรือบอร์ดขับมอเตอร์ (Motor Driver) แวะมาช้อปปิ้งของแท้คุณภาพดีได้ที่ Globalbyte เลยครับ! ของครบพร้อมทำโปรเจกต์!

หลักการทำงานของอัลกอริทึม S-Curve 🛤️

รถยนต์จะหลบสิ่งกีดขวางอย่างลื่นไหลด้วยกระบวนการ 2 เฟสหลักๆ ดังนี้:

  • Phase 1 (Swerve Out) – หักหลบออก: เมื่อเจอสิ่งกีดขวาง รถจะคำนวณว่าฝั่งไหนมีพื้นที่ว่างมากกว่ากัน จากนั้นจะลดความเร็วล้อด้านใน (และคงความเร็วล้อด้านนอก) ทำให้ตัวรถตีโค้งออกจากสิ่งกีดขวางโดยไม่ต้องหยุด
  • Phase 2 (Swerve Back) – หักกลับเข้าเลน: หลังจากหักหลบพ้นแล้ว ระบบจะสลับความเร็วล้อเพื่อหักพวงมาลัยกลับเข้าสู่เส้นทางเดิม ทำให้เกิดเป็นวิถีโค้งแบบตัว "S" ที่สมูทสุดๆ

วิดีโอสาธิตการทำงาน 🎬

รายการอุปกรณ์ที่ใช้ (Components Used) ⚙️

Components Required
  • Raspberry Pi 5
  • เซนเซอร์ VL53L5CX-SATEL (8x8 Time-of-Flight)
  • บอร์ดขับมอเตอร์ L298N
  • DC-DC Buck Converter 30W 10A
  • แบตเตอรี่ Li-ion 12V (18650 x 3 ก้อน)
  • DC Motors 200 RPM (4 ตัว)
  • สวิตช์ DPDT, สาย USB-C (ดัดแปลงสำหรับจ่ายไฟ) และสายจัมเปอร์

ผังวงจรและการทำงาน (Circuit Diagram) 🔌

Circuit Diagram

เนื่องจาก Raspberry Pi 5 ต้องการกระแสไฟที่เสถียรมาก การต่อวงจรจึงต้องแบ่งภาคจ่ายไฟ (High Voltage สำหรับมอเตอร์ และ Low Voltage สำหรับบอร์ด) ออกจากกันอย่างชัดเจน โดยมี Buck Converter เป็นตัวลดไฟจาก 12V ให้เหลือ 5V และสิ่งสำคัญที่สุดคือการต่อสาย Common Ground ของอุปกรณ์ทุกชิ้นเข้าด้วยกัน เพื่อให้สัญญาณสื่อสารกันได้ครับ

Autonomous Vehicle Working
เมื่อประกอบเสร็จ หุ่นยนต์จะสามารถหักหลบสิ่งกีดขวางได้อย่างต่อเนื่อง

ส่วนของโปรแกรม (System Code) 💻

โค้ดโปรเจกต์นี้เขียนด้วยภาษา Python ครับ มีการใช้งานไลบรารี lgpio สำหรับควบคุมขา GPIO ของ Pi 5 และแบ่งโครงสร้างของโปรแกรมออกเป็นส่วนต่างๆ เช่น การจัดการเซนเซอร์, การขับมอเตอร์ (PWM), และ Decision Engine สำหรับตัดสินใจหักหลบสิ่งกีดขวาง

เพื่อนๆ สามารถนำ Source Code ตัวเต็มจาก GitHub ไปศึกษาและปรับจูนความเร็วหรือระยะการหลบหลีก (Threshold) ตามต้องการได้เลยครับ (หรือดูโค้ดฉบับเต็มด้านล่างนี้ได้เลย)

autonomous_swerve.py
"""
================================================================================
PROJECT: Autonomous High-Speed Swerving Vehicle
CORE LOGIC: Phased S-Curve Obstacle Avoidance
--------------------------------------------------------------------------------
CODE MAP:
[Lines 18 - 30]  I.  Setup & Configuration (GPIO Mapping & Logic Thresholds)
[Lines 32 - 50]  II. Hardware Initialization (Lidar Sensor & Pin Claiming)
[Lines 52 - 76]  III. Motor Actuation Functions (Direct Hardware Control)
[Lines 78 - 98]  IV.  Recovery & Escape Logic (Emergency Pivot Maneuvers)
[Lines 100 - 173] V.  Main Control Loop (S-Curve State Machine & Perception)
[Lines 175 - 182] VI. Teardown & Safety Cleanup
================================================================================
"""
import os
import time
import numpy as np
import lgpio
import signal
from vl53l5cx_ctypes import VL53L5CX

# Force X11 backend for OpenCV compatibility on Pi 5
os.environ["QT_QPA_PLATFORM"] = "xcb"

# ==============================================================================
# I. SETUP & CONFIGURATION
# ==============================================================================
LPN_PIN = 23               # Lidar Low-Power/Reset pin
IN1, IN2 = 17, 27          # Left Side Motor Direction
IN3, IN4 = 22, 24          # Right Side Motor Direction
ENA, ENB = 18, 19          # Motor Speed PWM Pins

# Navigation Logic Thresholds (measured in mm)
THRESHOLD_CRITICAL = 120   # Immediate panic stop/reverse distance
THRESHOLD_SWERVE   = 400   # Distance to trigger the S-Curve maneuver
THRESHOLD_GROUND   = 130   # Ground clutter/floor noise filter
MAX_DIST           = 1000  # Cap sensor readings to ignore distant objects

# Speed Duty Cycles (0 - 100)
BASE_SPEED  = 100          # Maximum cruising velocity
SWERVE_LOW  = 50           # Inner wheel speed during a swerving arc
BACK_SPEED  = 70           # Power for reversing maneuvers

# ==============================================================================
# II. HARDWARE INITIALIZATION
# ==============================================================================
running = True
h = lgpio.gpiochip_open(0) # Open GPIO chip 0 (standard for Pi 5)

def signal_handler(sig, frame):
    """Safely breaks the loop and stops motors on Ctrl+C."""
    global running
    running = False
signal.signal(signal.SIGINT, signal_handler)

# Claim GPIO pins for output
for pin in [IN1, IN2, IN3, IN4, ENA, ENB, LPN_PIN]:
    lgpio.gpio_claim_output(h, pin)

# Wake up and boot the VL53L5CX Lidar
lgpio.gpio_write(h, LPN_PIN, 1)
time.sleep(0.1)

vl53 = VL53L5CX()
vl53.set_resolution(64)          # Configure for 8x8 multizone matrix
vl53.set_ranging_frequency_hz(15) # Set sampling rate to 15 frames/sec
vl53.start_ranging()

# ==============================================================================
# III. MOTOR ACTUATION FUNCTIONS
# ==============================================================================
def set_motors(l_speed, r_speed, d1, d2, d3, d4):
    """Base function to translate logic into physical motor movement."""
    l_speed = max(0, min(100, int(l_speed)))
    r_speed = max(0, min(100, int(r_speed)))
    
    lgpio.tx_pwm(h, ENA, 1000, l_speed) # Frequency 1000Hz
    lgpio.tx_pwm(h, ENB, 1000, r_speed)
    lgpio.gpio_write(h, IN1, d1)
    lgpio.gpio_write(h, IN2, d2)
    lgpio.gpio_write(h, IN3, d3)
    lgpio.gpio_write(h, IN4, d4)

def drive_forward(): 
    set_motors(BASE_SPEED, BASE_SPEED, 1, 0, 1, 0)
def drive_back():    
    set_motors(BACK_SPEED, BACK_SPEED, 0, 1, 0, 1)
def stop_car():      
    set_motors(0, 0, 0, 0, 0, 0)

# ==============================================================================
# IV. RECOVERY & ESCAPE LOGIC
# ==============================================================================
def pivot_recovery(left_clr, right_clr):
    """Used when the car is trapped or an object is too close to swerve."""
    stop_car()
    time.sleep(0.2)
    drive_back() # Back up to gain clearance
    time.sleep(0.5)
    
    # Decide turn direction based on side-zone clearance
    if left_clr > right_clr:
        set_motors(80, 80, 0, 1, 1, 0) # Pivot Left
    else:
        set_motors(80, 80, 1, 0, 0, 1) # Pivot Right
    time.sleep(0.5)
    stop_car()

# ==============================================================================
# V. MAIN CONTROL LOOP
# ==============================================================================
current_action = "INITIALIZING"
last_forward_time = time.time()

# S-Curve State Variables
maneuver_phase = 0    # 0: Cruise, 1: Swerve Away, 2: Return to Heading
maneuver_end_time = 0
bypass_dir = ""       
phase_duration = 1.7  # Duration of each half of the "S" maneuver

try:
    while running:
        now = time.time()
        
        if vl53.data_ready():
            data = vl53.get_data()
            # Reshape 1D sensor data into 8x8 matrix for spatial analysis
            z = np.array(data.distance_mm).reshape((8, 8))
            z[z <= 0] = MAX_DIST
            z = np.rot90(z, k=1)
            
            # Zone Analysis: Extract specific matrix regions
            min_center = np.min(z[0:5, 2:6])    # Obstacles directly in front
            left_side_avg = np.mean(z[:, 0:3])  # Average clearance on left
            right_side_avg = np.mean(z[:, 5:8]) # Average clearance on right
            ground_near = np.min(z[6:8, 2:6])   # Low obstacles/ground noise
            
            # 1. EMERGENCY STOP (Priority #1)
            if ground_near < THRESHOLD_GROUND or min_center < THRESHOLD_CRITICAL:
                current_action = "EMERGENCY RECOVERY"
                maneuver_phase = 0
                pivot_recovery(left_side_avg, right_side_avg)
                last_forward_time = now
                continue
                
            # 2. S-CURVE EXECUTION (Priority #2)
            if maneuver_phase == 1:
                # Phase 1: Arcing away toward the clear side
                if now < maneuver_end_time:
                    if bypass_dir == "LEFT": set_motors(SWERVE_LOW, BASE_SPEED, 1, 0, 1, 0)
                    else: set_motors(BASE_SPEED, SWERVE_LOW, 1, 0, 1, 0)
                    continue
                else:
                    # Timer expired, transition to Phase 2 (the return arc)
                    maneuver_phase = 2
                    maneuver_end_time = now + phase_duration
                    current_action = f"PHASE 2: RETURN {bypass_dir}"
                    
            if maneuver_phase == 2:
                # Phase 2: Arcing back to original forward heading
                if now < maneuver_end_time:
                    if bypass_dir == "LEFT": set_motors(BASE_SPEED, SWERVE_LOW, 1, 0, 1, 0)
                    else: set_motors(SWERVE_LOW, BASE_SPEED, 1, 0, 1, 0)
                    continue
                else:
                    # Maneuver successfully completed
                    maneuver_phase = 0
                    current_action = "BYPASS COMPLETE"
                    
            # 3. DETECTION & INITIATION (Priority #3)
            if min_center < THRESHOLD_SWERVE and maneuver_phase == 0:
                # Determine bypass direction based on side clearance
                bypass_dir = "LEFT" if left_side_avg > right_side_avg else "RIGHT"
                current_action = f"PHASE 1: SWERVE {bypass_dir}"
                maneuver_phase = 1
                maneuver_end_time = now + phase_duration
                last_forward_time = now
                
            # 4. NORMAL NAVIGATION (Priority #4)
            elif maneuver_phase == 0:
                current_action = "CRUISING"
                drive_forward()
                last_forward_time = now
                
            # 5. STUCK DETECTION
            if (now - last_forward_time) > 4.0:
                current_action = "TRAP ESCAPE"
                maneuver_phase = 0
                pivot_recovery(left_side_avg, right_side_avg)
                last_forward_time = now
                
            # Console Telemetry
            print("\033[H", end="") # Refresh terminal without scrolling
            print(f"--- ACTIVE MODE: {current_action} ---")
            print(f"Center Path: {int(min_center):4}mm | Phase: {maneuver_phase}")
            
        time.sleep(0.01)
        
except Exception as e:
    print(f"\nCRITICAL ERROR: {e}")
    
finally:
    # ==========================================================================
    # VI. TEARDOWN & SAFETY CLEANUP
    # ==========================================================================
    stop_car()
    vl53.stop_ranging()
    lgpio.gpiochip_close(h)
    print("\n[!] System Shutdown Cleanly.")

อ้างอิงและเรียบเรียงข้อมูลจาก: Globalbyteshop Blog

แหล่งที่มาบทความต้นฉบับ:
- CircuitDigest - Autonomous High-Speed Swerving Vehicle
- Source Code (GitHub) หรือ ดาวน์โหลดไฟล์ .zip

*คำเตือน: เนื้อหานี้เป็นการสรุปและแปลมาจากบทความ DIY หุ่นยนต์ต้นฉบับภาษาอังกฤษ การต่อวงจรควบคุมมอเตอร์ร่วมกับบอร์ดคอมพิวเตอร์ (Raspberry Pi 5) จะต้องใช้ความระมัดระวังในการจัดการเรื่องกระแสไฟฟ้าแรงสูง (High Voltage) จากแบตเตอรี่ และการต่อ Ground ร่วมกัน (Common Ground) เพื่อป้องกันบอร์ดเสียหาย ผู้สนใจควรศึกษาผังวงจรจากเว็บไซต์ต้นฉบับอย่างละเอียดก่อนลงมือปฏิบัติจริงครับ

 

แท็ก


Blog posts

เข้าสู่ระบบ

ลืมรหัสผ่านใช่ไหม?

ยังไม่มีบัญชีใช่ไหม?
สร้างบัญชี