โมดูล 4/5 · สัปดาห์ 10–12 · 27 ชม.

การวางแผนเส้นทางและหลบหลีก

UAT 308 ระบบอัตโนมัติและหุ่นยนต์

เวลาเรียนประมาณ 90 นาทีร่าง รอตรวจปรับปรุงล่าสุด 28 กันยายน 2569

บทเรียน

เมื่อเรียนจบโมดูลนี้ ผู้เรียนจะสามารถ

  1. อธิบายการวางแผนแบบสุ่มตัวอย่างและเขียน RRT บนแผนที่ที่มีสิ่งกีดขวาง
  2. ปรับเส้นทางจาก RRT ให้สั้นลงด้วยการตัดทางลัด
  3. ใช้ pure pursuit ติดตามเส้นทางและเลือกระยะมองหน้าให้เหมาะกับเวลาหน่วง
  4. แยกหน้าที่ของตัววางแผนเส้นทางกับตัวติดตามเส้นทาง

ความรู้พื้นฐานที่ควรมี: UAT 308 โมดูล 1–3 · UAT 314 โมดูล 4 (A* และ Dijkstra)

ทำไมต้องรู้

UAT 314 หาเส้นทางบนตารางด้วย A* และ UAT 366 หลบสิ่งกีดขวางด้วย potential field และ VFH หน่วยความรู้เรื่องการวางแผนเส้นทางและหลบหลีกสิ่งกีดขวางอัตโนมัติของคลังความรู้โดรนกล่าวถึง A*, RRT, occupancy map และ obstacle avoidance โมดูลนี้เพิ่มสองเรื่อง คือ RRT ที่หาเส้นทางได้ในพื้นที่ต่อเนื่องโดยไม่ต้องแบ่งเป็นตาราง และ pure pursuit ที่เปลี่ยนเส้นทางเป็นคำสั่งเลี้ยวของรถ ตำราของ Siegwart และคณะ และ Lynch และ Park แยกงานสองอย่างนี้ออกจากกัน ตัววางแผนตอบว่า “ไปทางไหน” ตัวติดตามตอบว่า “เลี้ยวเท่าไรตอนนี้”

RRT

RRT (Rapidly-exploring Random Tree, LaValle 1998) สร้างต้นไม้จากจุดเริ่ม ทุกรอบสุ่มจุดในพื้นที่ หาโหนดในต้นไม้ที่ใกล้ที่สุด แล้วยื่นกิ่งไปทางจุดนั้นหนึ่งช่วง ถ้ากิ่งไม่ชนสิ่งกีดขวางก็เพิ่มเข้าต้นไม้ การสุ่มทำให้ต้นไม้ขยายเข้าหาพื้นที่ว่างที่ยังไม่สำรวจได้เร็ว และบางรอบสุ่มเป้าหมายโดยตรง (goal bias) เพื่อให้ต้นไม้เข้าหาเป้า เส้นทางแรกที่ได้มักคดเคี้ยว จึงนิยมตัดทางลัดโดยลากเส้นตรงข้ามจุดที่ไม่ชนอะไร

ตัวอย่างที่ 1 ข้ามแถวแผงไปยังตู้อินเวอร์เตอร์

พื้นที่ 20 × 20 m มีแถวแผงสามแถวสลับด้านกัน รถเริ่มที่ (1, 1) และต้องไปตู้อินเวอร์เตอร์ที่ (19, 19) ช่วงยื่นกิ่ง 1 m สุ่มเป้าหมาย 10% ของรอบ กำหนดเมล็ดสุ่มให้ผลซ้ำได้ (ข้อมูลจำลอง)

import numpy as np

rng = np.random.default_rng(7)
start, goal = np.array([1.0, 1.0]), np.array([19.0, 19.0])
# แถวแผงโซลาร์ (xmin, ymin, xmax, ymax) m
rows = [(0, 5, 15, 6.5), (5, 10, 20, 11.5), (0, 15, 15, 16.5)]

def free(p):
    return all(not (x0 <= p[0] <= x1 and y0 <= p[1] <= y1) for x0, y0, x1, y1 in rows)

def segment_free(a, b, step=0.1):
    n = max(2, int(np.linalg.norm(b - a) / step))
    return all(free(a + (b - a) * s) for s in np.linspace(0, 1, n))

def rrt(max_nodes=3000, step=1.0, goal_bias=0.1):
    nodes, parent = [start], [-1]
    while len(nodes) < max_nodes:
        sample = goal if rng.random() < goal_bias else rng.uniform(0, 20, 2)
        i = int(np.argmin([np.linalg.norm(n - sample) for n in nodes]))
        d = sample - nodes[i]
        new = nodes[i] + d / max(np.linalg.norm(d), 1e-9) * min(step, np.linalg.norm(d))
        if free(new) and segment_free(nodes[i], new):
            nodes.append(new); parent.append(i)
            if np.linalg.norm(new - goal) < step and segment_free(new, goal):
                nodes.append(goal); parent.append(len(nodes) - 2)
                path, k = [], len(nodes) - 1
                while k != -1:
                    path.append(nodes[k]); k = parent[k]
                return path[::-1], len(nodes)
    return None, len(nodes)

def length(path):
    return sum(np.linalg.norm(path[i+1] - path[i]) for i in range(len(path) - 1))

def shortcut(path):
    out, i = [path[0]], 0
    while i < len(path) - 1:
        j = len(path) - 1
        while j > i + 1 and not segment_free(path[i], path[j]):
            j -= 1
        out.append(path[j]); i = j
    return out

path, n = rrt()
short = shortcut(path)
print(f"nodes in tree {n}")
print(f"RRT path {length(path):.1f} m ({len(path)} points)")
print(f"after shortcutting {length(short):.1f} m ({len(short)} points)")
print(f"straight line {np.linalg.norm(goal - start):.1f} m (blocked by panels)")
nodes in tree 389
RRT path 70.7 m (74 points)
after shortcutting 52.6 m (7 points)
straight line 25.5 m (blocked by panels)

RRT ได้เส้นทางที่ผ่านได้ภายในไม่กี่ร้อยโหนด แต่คดเคี้ยวยาว 70.7 m การตัดทางลัดลดเหลือ 52.6 m ด้วยจุดเลี้ยวเพียง 7 จุด เส้นทางยังชิดขอบแผง งานจริงจึงต้องขยายสิ่งกีดขวางออกเท่าครึ่งความกว้างรถบวกระยะเผื่อก่อนวางแผน ผลของ RRT ขึ้นกับการสุ่ม ถ้าเปลี่ยนเมล็ดสุ่มจะได้เส้นทางต่างกัน จึงควรทดสอบหลายครั้ง

พื้นที่สี่เหลี่ยม 20 คูณ 20 เมตร มีแถวแผงสีม่วงอ่อนสามแถวสลับซ้ายขวา กิ่งต้นไม้ RRT สีเทาบางกระจายทั่ว เส้นสีชมพูคดเคี้ยวจากมุมซ้ายล่างไปมุมขวาบนอ้อมปลายแถวแผง และเส้นสีเขียวหนาเป็นเส้นทางหลังตัดทางลัดที่ตรงกว่า
ภาพที่ 1 RRT ในโซลาร์ฟาร์มและเส้นทางหลังตัดทางลัด

pure pursuit

Coulter (1992) อธิบาย pure pursuit ว่าเลือกจุดเป้าบนเส้นทางที่อยู่ห่างจากรถเป็นระยะมองหน้า แล้วคำนวณความโค้งของวงกลมที่ผ่านทั้งรถและจุดเป้า โดย คือระยะด้านข้างของจุดเป้าในกรอบรถ เขียนอีกแบบได้ เมื่อ คือมุมจากหัวรถไปยังจุดเป้า อัตราหมุนที่สั่งคือ ระยะมองหน้าสั้นทำให้เข้าเส้นทางเร็วแต่ไวต่อเวลาหน่วงและสัญญาณรบกวน ระยะยาวทำให้นิ่งแต่ตัดโค้งและเข้าเส้นทางช้า

ตัวอย่างที่ 2 เข้าแนวแถวแผงเมื่อคำสั่งเลี้ยวหน่วง 0.3 s

รถวิ่ง 1 m/s เริ่มห่างแนวแถวแผง 1.0 m หันขนานกับแนว คำสั่งเลี้ยวไปถึงล้อช้า 0.3 s จากการสื่อสารและตัวขับมอเตอร์ เปรียบเทียบระยะมองหน้า 0.5, 1.0, 1.5 และ 3.0 m (แบบจำลองจลนศาสตร์)

import numpy as np

V, DT, T = 1.0, 0.05, 15.0     # ความเร็ว m/s ช่วงเวลา s เวลาจำลอง s
DELAY = 0.3                    # คำสั่งเลี้ยวถึงล้อช้า 0.3 s

def track(Ld):
    x, y, th = 0.0, 1.0, 0.0            # เริ่มห่างเส้นทาง y = 0 อยู่ 1.0 m
    buf = [0.0] * int(round(DELAY / DT))
    ys = []
    for _ in range(int(T / DT)):
        dx, dy = Ld, -y                 # จุดเป้าบนเส้นทาง ห่างไปข้างหน้า Ld ตามแนว x
        alpha = np.arctan2(dy, dx) - th
        buf.append(2 * np.sin(alpha) / np.hypot(dx, dy))
        kappa = buf.pop(0)
        th += V * kappa * DT
        x += V * np.cos(th) * DT
        y += V * np.sin(th) * DT
        ys.append(y)
    return np.array(ys)

for Ld in [0.5, 1.0, 1.5, 3.0]:
    ys = track(Ld)
    out = np.where(np.abs(ys) >= 0.05)[0]
    settle = (out[-1] + 1) * DT
    s = f"settles within ±5 cm by {settle:.1f} s" if settle < T else "does not settle in 15 s"
    print(f"Ld={Ld:.1f} m  {s}  max overshoot {max(0, -ys.min())*100:.1f} cm")
Ld=0.5 m  does not settle in 15 s  max overshoot 24.9 cm
Ld=1.0 m  settles within ±5 cm by 2.3 s  max overshoot 1.8 cm
Ld=1.5 m  settles within ±5 cm by 3.2 s  max overshoot 2.7 cm
Ld=3.0 m  settles within ±5 cm by 6.1 s  max overshoot 3.8 cm

ถ้าไม่มีเวลาหน่วง ระยะมองหน้าสั้นจะเข้าเส้นทางเร็วที่สุด แต่เมื่อคำสั่งหน่วง 0.3 s ระยะ 0.5 m แกว่งไปมาไม่หยุด ส่วน 1.0 m เข้าแถบ ±5 cm เร็วที่สุด ระยะ 3.0 m นิ่งแต่ช้า การเลือกระยะมองหน้าจึงต้องคิดรวมกับความเร็วและเวลาหน่วงของระบบจริง ระบบหลายตัวปรับระยะมองหน้าตามความเร็ว

กราฟระยะห่างจากเส้นทางเทียบกับเวลา 0 ถึง 15 วินาที เส้นสีชมพูระยะมองหน้า 0.5 เมตรแกว่งขึ้นลงราวบวกลบ 0.25 เมตรตลอด เส้นสีเขียว 1.0 เมตรลงสู่ศูนย์ภายในราว 2 ถึง 3 วินาที เส้นสีฟ้า 3.0 เมตรลดลงช้าและเข้าศูนย์ราว 6 วินาที
ภาพที่ 2 pure pursuit กับระยะมองหน้าต่างกัน

ปฏิบัติการประจำโมดูล

ปฏิบัติการ: วางแผนและติดตามเส้นทางในสนามจำลอง

  1. สร้างแผนที่สนามจำลองแถวแผงด้วยกรวยหรือแผ่นไม้ ขยายสิ่งกีดขวางตามความกว้างรถ
  2. หาเส้นทางด้วย RRT ตามตัวอย่างที่ 1 สิบครั้งด้วยเมล็ดสุ่มต่างกัน บันทึกความยาวและจำนวนโหนด เทียบกับ A* จาก UAT 314
  3. วัดเวลาหน่วงจริงของคำสั่งเลี้ยวตั้งแต่ส่งจนล้อเริ่มหมุน
  4. ให้รถติดตามเส้นทางด้วย pure pursuit ที่ระยะมองหน้า 3 ค่า วัดความคลาดด้านข้างจาก LiDAR หรือ RTK GNSS
  5. เลือกระยะมองหน้าที่ใช้งานและเขียนเหตุผลโดยอ้างผลวัด

ข้อผิดพลาดที่พบบ่อย

ระวัง

  • ไม่ขยายสิ่งกีดขวางตามขนาดรถ จนเส้นทางชิดขอบเกินไป
  • เชื่อผล RRT ครั้งเดียว ทั้งที่ผลขึ้นกับการสุ่ม
  • ตั้งระยะมองหน้าสั้นโดยไม่คิดถึงเวลาหน่วง
  • ให้ตัวติดตามแก้ปัญหาที่ตัววางแผนต้องแก้ เช่นหลบสิ่งกีดขวางใหม่
  • ไม่จำกัดความเร็วในโค้งแคบ

สรุป

  • RRT หาเส้นทางในพื้นที่ต่อเนื่องด้วยการสุ่มขยายต้นไม้ และควรตัดทางลัดภายหลัง
  • pure pursuit คำนวณความโค้งจากจุดเป้าที่ห่างเท่าระยะมองหน้า
  • ระยะมองหน้าต้องเลือกร่วมกับความเร็วและเวลาหน่วงของระบบ
  • ตัววางแผนกับตัวติดตามทำงานต่างระดับและต่างความถี่

แบบฝึกตรวจความเข้าใจ

  1. RRT ขยายต้นไม้อย่างไรในแต่ละรอบ
  2. goal bias มีไว้เพื่ออะไร
  3. จุดเป้าอยู่ด้านข้าง 0.5 m ระยะมองหน้า 2 m ความโค้งเท่าใด
  4. ความโค้ง 0.25 1/m ที่ความเร็ว 2 m/s อัตราหมุนเท่าใด
  5. ทำไมระยะมองหน้าสั้นจึงแกว่งเมื่อมีเวลาหน่วง
เฉลย
  1. สุ่มจุด หาโหนดที่ใกล้ที่สุด ยื่นกิ่งไปทางจุดนั้นหนึ่งช่วง แล้วเพิ่มถ้าไม่ชน
  2. ให้ต้นไม้เข้าหาเป้าหมายเร็วขึ้น
  3. 1/m
  4. rad/s
  5. รถแก้ทิศแรงและเร็ว แต่คำสั่งถึงล้อช้า จึงเลยเส้นแล้วแก้กลับไปมา

สรุปสูตรสำคัญ

ความโค้งของ pure pursuit
อัตราหมุน

แหล่งอ้างอิงหลัก

  1. LaValle, S. M. (1998). Rapidly-exploring random trees: A new tool for path planning (TR 98-11). Iowa State University. link
  2. Coulter, R. C. (1992). Implementation of the pure pursuit path tracking algorithm (Tech. Rep. CMU-RI-TR-92-01). Robotics Institute, Carnegie Mellon University. link
  3. Siegwart, R., Nourbakhsh, I. R., & Scaramuzza, D. (2011). Introduction to autonomous mobile robots (2nd ed.). MIT Press. link
  4. Lynch, K. M., & Park, F. C. (2017). Modern robotics: Mechanics, planning, and control. Cambridge University Press. link

อ่านเพิ่มเติม

ศึกษาหน่วยความรู้ที่กำหนดล่วงหน้า ดูสื่อประกอบ และทำ quiz ประจำโมดูล

ในชั้นเรียน / ภาคสนาม

ปฏิบัติการในห้องแล็บหรือภาคสนามตามใบงาน พร้อม checklist ความปลอดภัย

หลักฐานการเรียนรู้: ใบงานที่ผ่านการตรวจและผล quiz

แบบทดสอบประจำโมดูล

แบบทดสอบนี้ใช้ตรวจความเข้าใจ (formative) ไม่ใช่การสอบเก็บคะแนน

โดเมนความรู้: การควบคุม ออโตไพลอต และการนำทาง · ระบบอัตโนมัติ หุ่นยนต์ และฝูงโดรน