โมดูล 3/5 · สัปดาห์ 7–9 · 27 ชม.

หลบหลีกสิ่งกีดขวางและแผนที่

UAT 322 ปฏิบัติการปัญญาประดิษฐ์และการประกอบรวมระบบอากาศยานไร้คนขับอัตโนมัติ

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

บทเรียน

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

  1. สร้าง occupancy grid ขยายสิ่งกีดขวางตามขนาดลำ และหาเส้นทางด้วย A*
  2. คำนวณระยะหยุดเมื่อมีเวลาหน่วง และเทียบกับระยะตรวจจับของเซนเซอร์
  3. อธิบายการทำงานของ Collision Prevention ใน PX4 และ BendyRuler ใน ArduPilot พร้อมข้อจำกัด
  4. อธิบายหลักของ SLAM และประเมิน trajectory ด้วย ATE RMSE

ความรู้พื้นฐานที่ควรมี: UAT 322 โมดูล 1–2

ทำไมต้องรู้

โดรนสำรวจที่บินเองต้องตอบสามคำถามตลอดเวลา: ข้างหน้ามีอะไร จะไปทางไหน และถ้าเจอสิ่งกีดขวางกะทันหันจะหยุดทันหรือไม่ ถ้าตอบผิดข้อใดข้อหนึ่ง โดรนอาจชนต้นไม้หรือผนังอาคาร โมดูลนี้ฝึกเครื่องมือพื้นฐานของทั้งสามคำถาม และวิธีวัดว่าแผนที่และตำแหน่งที่ระบบสร้างขึ้นเชื่อถือได้เพียงใด

Occupancy grid และ A*

Occupancy grid แบ่งพื้นที่เป็นช่องตาราง แต่ละช่องบอกว่าว่างหรือมีสิ่งกีดขวาง แนวคิดนี้เสนอโดย Elfes (1989) และยังใช้แพร่หลาย เพราะรวมข้อมูลจากเซนเซอร์หลายตัวลงตารางเดียวได้ง่าย

โดรนไม่ใช่จุด จึงต้อง ขยายสิ่งกีดขวาง (inflation) ออกไปเท่ารัศมีของลำบวกระยะปลอดภัย แล้วจึงหาเส้นทางบนพื้นที่ที่เหลือ A* (Hart, Nilsson และ Raphael, 1968) หาเส้นทางสั้นที่สุดโดยเลือกขยายช่องที่มีค่า ต่ำสุด เมื่อ คือต้นทุนที่เดินมาแล้ว และ คือค่าประมาณระยะที่เหลือ ถ้า ไม่เกินระยะจริง A* รับประกันว่าได้เส้นทางสั้นที่สุด

ตัวอย่างที่ 1 หาเส้นทางด้วย A*

แต่ละช่องกว้าง 1 m ขยายสิ่งกีดขวางออก 1 ช่อง และเดินได้สี่ทิศ

import heapq

grid = ["............",
        "............",
        "....###.....",
        "....###.....",
        "....###..#..",
        ".........#..",
        "............",
        "............"]
ROWS, COLS = len(grid), len(grid[0])
start, goal = (6, 1), (2, 10)


def inflate(g, r=1):
    occ = {(i, j) for i in range(ROWS) for j in range(COLS) if g[i][j] == "#"}
    return {(i + a, j + b) for i, j in occ for a in range(-r, r + 1) for b in range(-r, r + 1)
            if 0 <= i + a < ROWS and 0 <= j + b < COLS}


def astar(blocked, s, g):
    h = lambda p: abs(p[0] - g[0]) + abs(p[1] - g[1])       # ระยะแมนฮัตตัน ไม่เกินระยะจริง
    heap, came, cost, expanded = [(h(s), 0, s)], {s: None}, {s: 0}, 0
    while heap:
        _, c, p = heapq.heappop(heap)
        expanded += 1
        if p == g:
            break
        for d in ((1, 0), (-1, 0), (0, 1), (0, -1)):
            q = (p[0] + d[0], p[1] + d[1])
            if 0 <= q[0] < ROWS and 0 <= q[1] < COLS and q not in blocked and c + 1 < cost.get(q, 1e9):
                cost[q], came[q] = c + 1, p
                heapq.heappush(heap, (c + 1 + h(q), c + 1, q))
    path = [g]
    while came[path[-1]] is not None:
        path.append(came[path[-1]])
    return path[::-1], expanded


blocked = inflate(grid)
path, expanded = astar(blocked, start, goal)
print(f"path length {len(path) - 1} m, cells expanded {expanded}")
for i in range(ROWS):
    print("".join("S" if (i, j) == start else "G" if (i, j) == goal else "*" if (i, j) in path
                  else "#" if grid[i][j] == "#" else "+" if (i, j) in blocked else "." for j in range(COLS)))
path length 17 m, cells expanded 56
..*********.
..*+++++..*.
.**+###+..G.
.*.+###++++.
.*.+###++#+.
.*.++++++#+.
.S......+++.
............

# คือสิ่งกีดขวาง + คือส่วนที่ขยาย และ * คือเส้นทาง A* อ้อมทั้งสิ่งกีดขวางและส่วนขยาย เส้นทางที่ได้เดินตามขอบตารางเป็นมุมฉาก ระบบจริงจึงทำให้เส้นทางเรียบก่อนส่งเป็น setpoint

ตาราง 8 แถว 12 คอลัมน์ ช่องชมพูคือสิ่งกีดขวาง ช่องส้มคือส่วนขยายตามขนาดลำรอบสิ่งกีดขวาง ช่องฟ้าคือเส้นทาง A* จากจุด S มุมล่างซ้ายขึ้นไปแถวบนแล้วลงมาที่จุด G ยาว 17 ช่อง
ภาพที่ 1 occupancy grid และเส้นทาง A*

หยุดทันหรือไม่

ระยะหยุดมีสองส่วน คือระยะที่เคลื่อนไประหว่างเวลาหน่วงก่อนเริ่มเบรก และระยะเบรก เหมือนการขับรถที่ต้องรวมเวลาที่เท้ายังไม่ถึงแป้นเบรก

v, latency, decel, sensor_range = 5.0, 0.25, 3.0, 8.0   # m/s, s, m/s², m
reaction = v * latency
braking = v ** 2 / (2 * decel)
stop = reaction + braking
print(f"reaction {reaction:.2f} m + braking {braking:.2f} m = {stop:.2f} m (sensor range {sensor_range} m)")
v_max = (-latency + (latency**2 + 2 * sensor_range / decel) ** 0.5) * decel
print(f"highest speed that still stops within the sensor range: {v_max:.2f} m/s")
reaction 1.25 m + braking 4.17 m = 5.42 m (sensor range 8.0 m)
highest speed that still stops within the sensor range: 6.22 m/s
แถบแนวนอนบนแกนระยะ 0 ถึง 9 เมตร ส่วนสีเหลือง 1.25 เมตรคือระยะระหว่างเวลาหน่วง ส่วนสีส้ม 4.17 เมตรคือระยะเบรก หยุดที่ 5.42 เมตร เส้นประสีเขียวที่ 8 เมตรคือระยะของเซนเซอร์
ภาพที่ 2 ระยะหยุดที่ 5 m/s เวลาหน่วง 0.25 s และหน่วง 3 m/s²

ที่ 5 m/s โดรนหยุดได้ภายใน 5.42 m ซึ่งน้อยกว่าระยะเซนเซอร์ 8 m แต่ความเร็วสูงสุดที่ยังหยุดทันคือประมาณ 6.2 m/s เท่านั้น ในทางปฏิบัติต้องเผื่อเพิ่มอีกสำหรับลม ความคลาดเคลื่อนของตำแหน่ง และระยะที่เซนเซอร์ตรวจจับวัตถุบางอย่างไม่ได้ เช่น สายไฟ

ระบบหลบหลีกในตัวควบคุมการบิน

  • PX4 Collision Prevention ทำงานเฉพาะโหมด Position ของโดรนหลายใบพัด จำกัดความเร็วเมื่อเข้าใกล้สิ่งกีดขวาง โดยรับข้อมูลระยะจากตัววัดระยะหรือจากคอมพิวเตอร์บนลำผ่านข้อความ OBSTACLE_DISTANCE ระยะที่ต้องการเว้นตั้งด้วย CP_DIST ซึ่งค่าเริ่มต้นเป็น −1 คือ ปิดอยู่
  • ArduPilot มีหลายระดับ ตั้งแต่ Simple avoidance ที่หยุดหน้าสิ่งกีดขวาง ไปจนถึง BendyRuler ที่หาทางอ้อมรอบสิ่งกีดขวาง (ใช้ในโหมด Auto, Guided และ RTL) และ Dijkstra’s ที่วางเส้นทางรอบ fence และพื้นที่ห้ามบินที่รู้ล่วงหน้า

ระบบเหล่านี้พึ่งเซนเซอร์ทั้งหมด ถ้าเซนเซอร์มองไม่เห็นวัตถุ ระบบก็หลบไม่ได้ จึงเป็นชั้นป้องกันเสริม ไม่ใช่เหตุผลให้บินใกล้สิ่งกีดขวาง

SLAM และการประเมิน trajectory

SLAM (simultaneous localization and mapping) คือการสร้างแผนที่และหาตำแหน่งตัวเองในแผนที่นั้นไปพร้อมกัน ใช้ในที่ที่ไม่มี GNSS เช่น ในอาคารหรือใต้สะพาน บทความของ Cadena และคณะ (2016) สรุปพัฒนาการของ SLAM และ ORB-SLAM3 เป็นไลบรารีเปิดที่รองรับกล้องเดี่ยว กล้องคู่ และกล้องร่วมกับ IMU

วิธีวัดคุณภาพที่นิยมคือ ATE (absolute trajectory error) จากชุดทดสอบของ Sturm และคณะ (2012) คือค่า RMSE ของระยะระหว่างตำแหน่งที่ประมาณได้กับตำแหน่งจริง Zhang และ Scaramuzza (2018) เตือนว่าต้อง จัดแนว (align) สอง trajectory ให้อยู่ในกรอบเดียวกันก่อน และต้องบอกวิธีจัดแนวทุกครั้ง

import numpy as np

truth = np.array([[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]], dtype=float)
estimate = np.array([[0, 0], [1.1, 0.1], [2.1, 0.2], [3.2, 0.2], [4.2, 0.3], [5.3, 0.4]])
errors = np.linalg.norm(estimate - truth, axis=1)
print("error per pose (m):", np.round(errors, 2))
print(f"ATE RMSE {np.sqrt(np.mean(errors ** 2)):.3f} m, final drift {errors[-1]:.2f} m")
error per pose (m): [0.   0.14 0.22 0.28 0.36 0.5 ]
ATE RMSE 0.297 m, final drift 0.50 m

ความคลาดเคลื่อนเพิ่มขึ้นตามระยะทาง ซึ่งเป็นลักษณะของการลอย (drift) ตัวอย่างนี้สองชุดอยู่ในกรอบเดียวกันอยู่แล้วจึงไม่ต้องจัดแนว ข้อมูลจริงต้องจัดแนวก่อน

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

ปฏิบัติการ: หลบสิ่งกีดขวางใน SITL

  1. เปลี่ยนแผนที่ในตัวอย่างที่ 1 ให้มีช่องแคบ แล้วทดลองขยายสิ่งกีดขวาง 0, 1 และ 2 ช่อง บันทึกว่าเส้นทางเปลี่ยนหรือหาไม่พบเมื่อใด
  2. เปิด Collision Prevention ใน PX4 SITL ที่มีตัววัดระยะจำลอง ตั้ง CP_DIST แล้วบินเข้าหาผนังในโหมด Position บันทึกระยะที่โดรนหยุดจริง
  3. เปลี่ยนความเร็วและเวลาหน่วงในสูตรระยะหยุด เทียบกับผลจาก SITL
  4. ใช้ข้อมูลจากชุด trajectory ที่อาจารย์เตรียม คำนวณ ATE RMSE และอธิบายวิธีจัดแนวที่ใช้
  5. สรุปความเร็วสูงสุดที่ปลอดภัยสำหรับเซนเซอร์ของโดรนฝึก พร้อมสมมติฐาน

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

ระวัง

  • ไม่ขยายสิ่งกีดขวางตามขนาดลำ เส้นทางจึงเฉียดจนใบพัดชน
  • ลืมเวลาหน่วงในระยะหยุด และคิดแค่ระยะเบรก
  • คิดว่า Collision Prevention เปิดอยู่แล้ว ทั้งที่ค่าเริ่มต้นปิด
  • เชื่อว่าเซนเซอร์เห็นทุกอย่าง ทั้งที่สายไฟ กระจก และกิ่งไม้เล็กอาจตรวจไม่พบ
  • รายงาน ATE โดยไม่บอกวิธีจัดแนว

สรุป

  • Occupancy grid แทนพื้นที่เป็นตาราง ต้องขยายสิ่งกีดขวางตามขนาดลำก่อนหาเส้นทาง
  • A* เลือกช่องที่ ต่ำสุด และได้เส้นทางสั้นที่สุดเมื่อ ไม่เกินระยะจริง
  • ระยะหยุดคือระยะระหว่างเวลาหน่วงบวกระยะเบรก ต้องน้อยกว่าระยะเซนเซอร์พร้อมส่วนเผื่อ
  • PX4 และ ArduPilot มีระบบหลบหลีกในตัว แต่พึ่งเซนเซอร์และต้องเปิดใช้เอง
  • SLAM สร้างแผนที่และหาตำแหน่งพร้อมกัน และประเมินด้วย ATE หลังจัดแนว

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

  1. ใน A* ถ้า และ ค่า ของช่องนั้นเท่าใด
  2. ความเร็ว 4 m/s เวลาหน่วง 0.5 s หน่วง 2 m/s² ระยะหยุดเท่าใด
  3. ทำไมต้องขยายสิ่งกีดขวางก่อนหาเส้นทาง
  4. ค่าเริ่มต้น CP_DIST = −1 ใน PX4 หมายความว่าอะไร
  5. ความคลาดเคลื่อนของตำแหน่งสามจุดคือ 0, 3 และ 4 m ATE RMSE เท่าใด
เฉลย
  1. m
  2. โดรนมีขนาด จุดศูนย์กลางลำต้องห่างสิ่งกีดขวางอย่างน้อยรัศมีลำบวกระยะปลอดภัย
  3. Collision Prevention ปิดอยู่
  4. m

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

ต้นทุนของ A*
ระยะหยุด
ATE RMSE

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

  1. Elfes, A. (1989). Using occupancy grids for mobile robot perception and navigation. Computer, 22(6), 46–57. link
  2. Hart, P. E., Nilsson, N. J., & Raphael, B. (1968). A formal basis for the heuristic determination of minimum cost paths. IEEE Transactions on Systems Science and Cybernetics, 4(2), 100–107. link
  3. PX4 Autopilot. Collision prevention. PX4 user guide (main). link
  4. ArduPilot Dev Team. Object avoidance. ArduPilot Copter documentation. link
  5. Cadena, C., Carlone, L., Carrillo, H., Latif, Y., Scaramuzza, D., Neira, J., Reid, I., & Leonard, J. J. (2016). Past, present, and future of simultaneous localization and mapping: Toward the robust-perception age. IEEE Transactions on Robotics, 32(6), 1309–1332. link
  6. Campos, C., Elvira, R., Gómez Rodríguez, J. J., Montiel, J. M. M., & Tardós, J. D. (2021). ORB-SLAM3: An accurate open-source library for visual, visual–inertial, and multimap SLAM. IEEE Transactions on Robotics, 37(6), 1874–1890. link
  7. Sturm, J., Engelhard, N., Endres, F., Burgard, W., & Cremers, D. (2012). A benchmark for the evaluation of RGB-D SLAM systems. In 2012 IEEE/RSJ International Conference on Intelligent Robots and Systems (pp. 573–580). link
  8. Zhang, Z., & Scaramuzza, D. (2018). A tutorial on quantitative trajectory evaluation for visual(-inertial) odometry. In 2018 IEEE/RSJ International Conference on Intelligent Robots and Systems (pp. 7244–7251). link

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

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

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

ปฏิบัติการเข้มข้นในแล็บและภาคสนาม บันทึกผลลงสมุดปฏิบัติการ

หลักฐานการเรียนรู้: สมุดปฏิบัติการที่อาจารย์ลงนาม

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

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

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