Ebook Robotics · RL–UAV
Lecture 8 — RRT (Rapidly-exploring Random Tree)
Chương B** · không gian liên tục, không lưới hóa nổi — lấy mẫu để tìm đường
Chế độ Slide · phím ← → · F toàn màn · Esc về đọc · S mở slide
Chương B** · không gian liên tục, không lưới hóa nổi — lấy mẫu để tìm đường
Chương B · không gian liên tục, không lưới hóa nổi — lấy mẫu để tìm đường
| # | Câu hỏi | Trả lời ngắn |
|---|---|---|
| 1 | Tại sao cần? | Lưới/A* chết vì curse of dimensionality (Lec 6–7). Cần tìm đường trong liên tục. |
| 2 | So với cái khác? | Vs A*: sampling, không tối ưu mặc định. Vs RRT*: có tiệm cận tối ưu. Vs RL: một path/map vs một policy/phân phối map. |
| 3 | Giải quyết gì & thế nào? | Mọc cây: sample → nearest → steer \(\eta\) → collision check. |
| 4 | Kết quả ra sao? | Waypoint thô → làm mượt (Lec 9) → track; hoặc dataset demo cho IL/RL. |
| 5 | Nên dùng khi nào? | Nhiều chiều, vật cản phức tạp, cần path nhanh; sinh demo. |
| 6 | Không nên khi nào? | Cần path tối ưu ngay (dùng RRT*/opt); môi trường cực hẹp mà goal bias quá lớn; quên kinodynamic (Lec 10). |
🖼️ Hình nhanh: Ném mồi ngẫu nhiên → kéo cành cây về phía vùng trống (Voronoi bias); thỉnh thoảng ném đúng goal.
Hình gốc hoặc minh họa cho ebook — không cắt từ slide Princeton. Dùng Present: bật Slide · F toàn màn.
S──┬──● ####
│ └──● #### ● = nút cây
● \ ####
│ ●──●
└──● \
\ ●──●──● G
●
★ probabilistically complete; đường thường gấp khúc ≠ tối ưu
Phố đặc nhiều ngõ hẹp, góc yaw–vị trí không lưới hóa nổi: cách làm là mọc cây từ start thay vì quét hết ô.
Mỗi vòng: ném một điểm ngẫu nhiên vào không gian (mồi nhử), tìm nút cây gần nhất, mọc một đoạn ngắn \(\eta\) về phía mồi nếu không đụng. Vùng trống càng lớn càng dễ “câu” được mẫu → cây tự ưu tiên loang nơi rộng (Voronoi bias). Thỉnh thoảng ném mồi ngay goal (bias ~5–10%) để ngoặt về đích.
Path tìm được thường gấp khúc như đi theo lối tắt tạm — chưa đẹp bay; Lec 9 (min-snap) làm mượt trước khi giao tracker.
🇻🇳 Chuyện thật (sân B): Trong phố đặc Hà Nội / HCM, C-space đầy hẻm và góc chết — lưới mịn nổ bộ nhớ trước khi tìm ra đường.
RRT mọc cây ngẫu nhiên qua khe hẹp: vài nghìn mẫu có thể nối start–goal nơi BFS trên lưới quá thô hoặc quá nặng.
Cái giá: path gấp như đi tắt qua ngõ — góc 90° cứng, quadrotor không bay nổi nếu đưa thẳng xuống động lực.
Chốt: RRT = khả năng tìm đường trong không gian liên tục; trên chuyến bay, path thô phải qua làm mượt (Lec 9) trước khi bám. Chi tiết sân:../00C.
T.init(x_start)
for i = 1..N:
x_rand ← sample (thỉnh thoảng = x_goal) # goal bias
x_near ← nút trong T gần x_rand nhất
x_new ← x_near + η · hướng(x_near → x_rand) # steer
if đoạn [x_near, x_new] free: # collision check
T.add(x_new, cha = x_near)
if ‖x_new − x_goal‖ < ε: return path (lần ngược cha)
| Khái niệm | Ý nghĩa |
|---|---|
| Probabilistically complete | Có đường ⇒ P(tìm ra) → 1 khi \(N\to\infty\) |
| Không tối ưu (RRT thường) | Path gấp, vòng; cần smoothing hoặc RRT* |
| RRT* | Rewire lân cận → tiệm cận tối ưu, chậm hơn |
| RRT-Connect | Hai cây start↔goal — nhanh thực tế |
| Chi phí thật | ~90% thời gian = collision check |
[RRT waypoints thô] ──► [shortcut / RRT*] ──► [min-snap Lec9] ──► [LQR/PID track]
│
▼
dataset (map, path) ──► imitation / warm-start RL
η lớn → mọc nhanh, dễ đụng / bỏ lỡ khe hẹp
η nhỏ → chậm, tỉ mỉ hơn
goal bias lớn → về đích sớm nhưng kém khám phá (kẹt ngõ hẹp)
goal bias nhỏ → khám phá rộng, chậm chạm goal
"""
RRT 2D tránh đĩa tròn. Goal bias 8%.
In ra số vòng và độ dài path khi chạm gần goal.
"""
import numpy as np
rng = np.random.default_rng(0)
obstacles = [(5, 5, 1.6), (7, 2, 1.2), (3, 8, 1.4)] # (cx, cy, radius)
def free(p):
"""True nếu điểm p không nằm trong vật cản (đã 'phồng' sẵn bằng radius)."""
return all(np.hypot(p[0] - cx, p[1] - cy) > r for cx, cy, r in obstacles)
def seg_free(a, b, k=20):
"""Kiểm tra đoạn thẳng a→b bằng k mẫu trên đoạn."""
for t in np.linspace(0, 1, k):
if not free(a + t * (b - a)):
return False
return True
start, goal = np.array([0.5, 0.5]), np.array([9.5, 9.5])
nodes = [start]
parent = {0: None}
eta = 0.6
success = False
for i in range(4000):
x_rand = goal if rng.random() < 0.08 else rng.uniform(0, 10, size=2)
dists = [np.linalg.norm(n - x_rand) for n in nodes]
j = int(np.argmin(dists))
direction = x_rand - nodes[j]
length = np.linalg.norm(direction) + 1e-9
x_new = nodes[j] + eta * direction / length
if free(x_new) and seg_free(nodes[j], x_new):
nodes.append(x_new)
parent[len(nodes) - 1] = j
if np.linalg.norm(x_new - goal) < 0.5:
idx, path = len(nodes) - 1, []
while idx is not None:
path.append(nodes[idx])
idx = parent[idx]
path.reverse()
plen = sum(
np.linalg.norm(path[t] - path[t + 1])
for t in range(len(path) - 1)
)
print(f"Thành công sau {i} vòng | {len(path)} nút | dài ≈ {plen:.2f}")
success = True
break
if not success:
print("Chưa chạm goal — tăng N, chỉnh eta/goal bias, hoặc dùng RRT-Connect.")
🧪 Kỳ vọng demo:
- Với seed 0: thường inThành công sau … vòng | … nút | dài ≈ …(số thay đổi nếu đổi seed).
- Path gấp khúc, không phải đường ngắn Euclid — bình thường.
- Tăng goal bias quá mạnh trong map hẹp hơn có thể chậm hơn hoặc fail.
★ Collision check chiếm phần lớn thời gian khi scale lên 3D.
| ✅ Nên | ❌ Không | Phủ / gió / pin | |
|---|---|---|---|
| RRT | Cao chiều, path nhanh, sinh demo | Claim path tối ưu | Waypoint thô cho coverage tour |
| RRT* / smooth | Cần gần tối ưu / bay đẹp hơn | Cần solution cực nhanh mỗi ms | Giảm mét thừa → pin |
| Goal bias | ~5–10% thực dụng | Bias rất lớn trong ngõ hẹp | Kẹt trước cụm chè/cột |
| vs RL | Oracle / dataset | Thay policy đa map + gió non-stationary | Hybrid planner + track |
eta và goal bias ảnh hưởng ra sao? ⚡ Nhớ một câu: RRT mọc cây bằng lấy mẫu — đầy đủ theo xác suất, không tối ưu mặc định; đẹp bay nhờ smooth / RRT* / min-snap.
🔗 Sang bài sau mang theo: Waypoint RRT gấp khúc → Lec 9 differential flatness + min-snap biến polyline thành quỹ đạo bay được.
Trước: Lec07 · Sau: Lec09 — Differential Flatness