Moduł 05 · Planowanie grafowe

Planowanie deterministyczne i grafowe

Dijkstra, A*, ARA*, AD*, D* Lite, Theta*, visibility graphs, Voronoi, state lattice. Animacje ekspansji wg f-cost.

TL;DR

Planery grafowe traktują przestrzeń konfiguracyjną jako dyskretny graf: węzły to konkretne konfiguracje (lub komórki gridu), krawędzie to dopuszczalne ruchy z kosztami. Problem znalezienia ścieżki staje się klasycznym shortest path search.

  • Dijkstra (1959) — eksploruje wg g(n)g(n) (koszt od startu). Bez heurystyki. Optymalny, ale wolny dla dużych grafów.
  • A* (Hart, Nilsson, Raphael 1968) — Dijkstra + heurystyka h(n)h(n) (estymata kosztu do goal). Eksploruje wg f(n)=g(n)+h(n)f(n) = g(n) + h(n). Optymalny gdy hh jest admissible (h(n)h(n)h(n) \leq h^*(n)).
  • Weighted A* (Pohl 1970) — f=g+whf = g + w h,w1w \geq 1. Szybsza eksploracja, gwarancja costwoptimal\text{cost} \leq w \cdot \text{optimal}.
  • Greedy Best-First — tylko hh, bez gg. Bardzo szybki, ALE nie-optymalny i może utknąć.
  • Theta* (Nash 2007) — any-angle: ścieżka NIE musi iść po krawędziach gridu. Lepiej wygląda (bez „ząbków"), krótsza.
  • D* Lite, ARA*, AD* — re-planning dla scen dynamicznych lub anytime planning.

Granica: curse of dimensionality. Grid 30×30 ma 900 komórek; grid 30⁷ (7-DOF Panda) ma 2.2 × 10¹⁰ — eksploracja niewykonalna. Dlatego grid-based dobrze działa dla mobile robots (2-3D), a manipulator wymaga sampling-based (moduł 06).

Live animation — 5 algorytmów na tej samej mapie

Wszystkie warianty operują na tym samym 30×30 gridzie z 8-connected neighbors. Wybierz algorytm, obserwuj ekspansję krok-po-kroku:

  • Komórki zamknięte (closed) kolorowane wg f(n)f(n) — gradient zielony (mały f) → żółty → czerwony (duży f). Pokazuje, w jakim porządku algorytm „myśli".
  • Komórki otwarte (frontier) niebieskie — czekają na ekspansję.
  • Pomarańczowa kropka — aktualnie ekspandowana komórka.
  • Fioletowa linia — aktualna ścieżka od startu do bieżącego węzła (rekonstrukcja przez parent pointery).

Eksperymenty do wypróbowania:

  • Porównaj Dijkstra z A* na tej samej mapie — A* eksploruje znacznie mniej komórek (expand: ... u góry tabeli).
  • Włącz Weighted A*, podnieś wagę z 1 do 5 — ekspansja staje się węższa (kierunkowa), ścieżka czasem dłuższa niż optymalna.
  • Greedy na mapie „maze" — szybki, ale często znajduje suboptymalną drogę (idzie wprost ku goal, nie uwzględnia kosztu dotychczasowego).
  • Theta* daje ścieżki any-angle (nie po krawędziach gridu) — wyraźnie krótsza długość niż A* na tej samej mapie.

A* (f=g+h) — iter 4

koszt: 44.456 · expand: 543
algorytm
mapa
obstacleopen set (frontier)closed (kolor = f-cost)currentstartgoalpath

A* — anatomia algorytmu

Pseudokod:

open ← priority queue: [start]
g[start] = 0
while open not empty:
  n ← pop min by f(n) = g(n) + h(n)
  if n == goal: return reconstruct_path(parent)
  mark n closed
  for each neighbor m of n:
    new_g = g[n] + cost(n, m)
    if new_g < g[m]:
      g[m] = new_g
      parent[m] = n
      open.push(m, priority = new_g + h(m))

Kluczowe pojęcia:

  • g(n)g(n) = actual cost od startu do nn (znane z eksploracji).
  • h(n)h(n) = heurystyka — estymata kosztu od nn do goal (nieznana dokładnie).
  • f(n)=g(n)+h(n)f(n) = g(n) + h(n) = estimated total cost ścieżki przez nn.

Warunki na heurystykę

  • Admissibility: h(n)h(n)h(n) \leq h^*(n) (nigdy nie przeszacowuje rzeczywistej odległości). Gwarantuje optymalność A*.
  • Consistency / monotonia: h(n)c(n,m)+h(m)h(n) \leq c(n, m) + h(m) dla każdej krawędzi (n,m)(n, m). Implikuje admissibility. Dodatkowo gwarantuje, że nigdy nie ponownie ekspandujemy zamkniętej komórki (więcej efektywności).
  • Jeśli h0h \equiv 0: A* = Dijkstra.
  • Im hh „dokładniejsze" (bliższe hh^*), tym mniej komórek A* musi rozwinąć. W skrajności gdy h=hh = h^*, A* idzie prosto ścieżkę optymalną bez jakiejkolwiek eksploracji.

Typowe heurystyki dla grid

  • Manhattan: dc+dr|dc| + |dr| — admissible tylko dla 4-connected.
  • Octile: max(dc,dr)+(21)min(dc,dr)\max(|dc|, |dr|) + (\sqrt{2} - 1) \min(|dc|, |dr|) — admissible dla 8-connected (używamy w naszej animacji).
  • Euclidean: dc2+dr2\sqrt{dc^2 + dr^2} — admissible, ale luźniejsza (mniejsze h ⇒ więcej eksploracji).

Weighted A*, Greedy, anytime planning

Weighted A* (Pohl 1970): zamień f=g+hf = g + h na f=g+whf = g + w \cdot h, w1w \geq 1. Większa waga przesuwa balans w stronę heurystyki:

  • w=1w = 1 ⇒ klasyczny A* (optymalny).
  • w>1w > 1 ⇒ ścieżka znaleziona szybciej (mniej eksploracji) kosztem optymalności. Gwarancja: costwcost\text{cost} \leq w \cdot \text{cost}^*.
  • ww \to \infty ⇒ Greedy Best-First (tylko hh, ignoruje gg).

Anytime planners (ARA*, AD*) wykorzystują tę własność iteracyjnie: zaczynają z dużym ww (szybkie ale suboptymalne), potem kolejne uruchomienia zmniejszają ww i poprawiają ścieżkę. W każdym momencie mamy „najlepsze co znaleźliśmy do tej pory" — przydatne dla ograniczonego budżetu czasowego (autopilot UAV).

ARA* (Likhachev, Gordon, Thrun 2003) — Anytime Repairing A*. Reużywa open set i wartości gg między iteracjami, zamiast restartować.

AD* (Likhachev et al. 2005) — Anytime Dynamic A*. Łączy anytime z replanning (D* Lite).

Re-planning dla scen dynamicznych: D* Lite

Klasyczny A* zakłada statyczną scenę. W praktyce (mobilny robot z czujnikiem laser) świat odkrywany jest stopniowo — za każdym razem trzeba przeplanować.

D* Lite(Koenig & Likhachev 2002) — inkrementalny A*: zachowuje stan eksploracji, gdy odkryta zostanie nowa przeszkoda tylko aktualizuje to, co konieczne. Czas re-planning ~ O(Δk)O(\Delta \cdot k) gdzie Δ\Delta = liczba dotkniętych komórek, kk = stała.

Standard dla mobile robots z SLAMem (Curiosity, Perseverance — D* Lite jako warstwa planowania nad mapą Marsa).

LPA*(Lifelong Planning A*, Koenig & Likhachev 2004) — uogólnienie D* Lite; aktualizuje plan przy ZMIANIE kosztu dowolnej krawędzi (nie tylko dodaniu przeszkód).

D* Lite — inkrementalne replanning po nowej przeszkodzie

Kliknij na ścieżce (fioletowa) by dodać przeszkodę. Demo porównuje (1) A* odpalony od zera, (2) D* Lite — aktualizuje tylko podzbiór komórek, których ścieżka do startu przechodziła przez nową przeszkodę (+ ich sąsiadów).
Plan początkowy A* (expand = 132)
fioletowa ścieżka → kliknij na niej by dodać obstacle
A* od zera (expand = —)
wszystkie expanded cells = przeszukane od zera
D* Lite affected set (|A ∪ B| = 0)
czerwone = potomkowie zablokowanej komórki + ich sąsiedzi
← Kliknij komórkę na fioletowej ścieżce by uruchomić replanning →
Pedagogiczna uwaga: Pełny D* Lite (Koenig & Likhachev 2002) używa wartości rhs(s) (one-step lookahead) i specjalnego klucza k(s) = [min(g,rhs) + h + k_m, min(g,rhs)] w priorytetowej kolejce. Tutaj pokazujemy tylko WYNIK — który podzbiór komórek D* Lite by zaktualizował. „Affected" = potomkowie zablokowanej komórki w drzewie parent (ich ścieżka do startu przebiegała przez nowo zablokowaną komórkę, więc muszą szukać nowych rodziców).

Theta* — any-angle planning

Klasyczny A* na 8-connected gridzie zwraca ścieżki tylko wzdłuż 8 dyskretnych kierunków. Dla otwartych przestrzeni daje charakterystyczne „ząbki" — wizualnie nienaturalne i suboptymalne.

Theta* (Nash, Daniel, Koenig, Felner 2007) modyfikuje update kroku A*: gdy odkrywamy sąsiada mm, sprawdzamy czy parent obecnego węzła ma line-of-sight do mm. Jeśli tak, ustawiamy tego parent'a dziadka jako rodzica mm (zamiast bezpośredniego ojca). Daje ścieżki, które idą w „dowolnym kącie", nie tylko po krawędziach.

Theta* zachowuje optymalność tylko w przybliżeniu (gwarantuje 1.05optimum\leq 1.05 \cdot \text{optimum}), ale ścieżki są znacznie krótsze i naturalniejsze niż A*. Włącz w animacji powyżej — porównaj.

Lazy Theta*, Field D* — dalsze warianty z optymalizacjami line-of-sight checks.

Visibility graphs i Voronoi diagrams

Dla scen z polygonalnymi przeszkodami (mapy 2D wektorowe, nie grid raster) istnieją dwie klasyczne reprezentacje:

Visibility graph

Węzły = wierzchołki wszystkich przeszkód + start + goal. Krawędzie = wszystkie pary węzłów widoczne (segment nie przecina żadnej przeszkody). Po zbudowaniu — Dijkstra/A* w O(E).

Twierdzenie: optymalna ścieżka euklidesowa w 2D z polygonalnymi przeszkodami składa się z segmentów łączących wierzchołki przeszkód (i start/goal). Visibility graph ZAWIERA tę ścieżkę. Dlatego daje dokładnie optymalne wyniki — nie tylko aproksymację jak grid.

Koszt: konstrukcja O(n² log n) (sprawdzanie widoczności dla par wierzchołków). Dla 1000 wierzchołków = 1 sekunda. Skaluje się kiepsko dla skomplikowanych map.

Voronoi diagram

Decompozycja przestrzeni na regiony „najbliższe" do każdej przeszkody. Krawędzie diagramu są w środku „korytarzy" między przeszkodami — to są najbezpieczniejsze drogi (najdalej od czegokolwiek).

Algorytm: zbuduj Voronoi (np. Fortune sweep, O(n log n)). Połącz start/goal z najbliższą krawędzią. Szukaj ścieżki w grafie krawędzi Voronoi.

Plusy: maksymalna clearance (bezpieczne dla mobile robots).
Minusy: ścieżki są dłuższe niż optymalne; trudno dla scen 3D.

State lattice planners — dla pojazdów nieholonomicznych

Standardowy grid zakłada że robot może w każdej komórce iść w 8 dowolnych kierunków. Dla samochodu (czy łodzi, drona ze stałym kątem skrętu) to nieprawda — istniejeminimalny promień skrętu, dynamika ogranicza zbiór osiągalnych konfiguracji w jednym kroku.

State lattice: węzły gridu to stany dynamiczne (pozycja, orientacja), krawędzie tomotion primitives — predefiniowane wykonalne trajektorie:

  • Jechać prosto 5 m
  • Skręcić w lewo o 45° z promieniem 10 m
  • Skręcić w prawo o 45° z promieniem 10 m
  • itp. (typowo 5-15 motion primitives)

Po zdefiniowaniu primitives — A* / Dijkstra na rozszerzonym grafie. Pickatka: tylko wybrane przejścia z każdego stanu, ale każde spełnia ograniczenia dynamiczne.

Klasyczne implementacje: SBPL (Search-Based Planning Library, Maxim Likhachev). Stosowane w autonomicznych samochodach (Carnegie Mellon, DARPA Urban Challenge), traktorach Deere.

Curse of dimensionality — gdzie graf-based pęka

Liczba komórek w jednolitym gridzie wymiaru dd z NN komórkami na oś:

grid=Nd|\text{grid}| = N^d
SystemDOFKomórki (N=30)Pamięć (1 byte/cell)Czas A* (rough)
Robot mobilny 2D (x, y)2900900 B0.1 ms
Auto z heading (x, y, θ)327 00027 KB10 ms
Quadrotor (x, y, z, ψ)4810 000800 KB0.5 s
Panda 7-DOF722 mld22 GBnigdy
Humanoid 30-DOF3010⁴⁴10⁴⁴ Bśmierć cieplna Wszechświata

Stąd planery grafowe są standardem dla 2-4 DOF (mobile robots, auta, drony), a dla manipulatorów nieprzydatne — zostaje sampling-based (moduł 06) lub trajektoryjna optymalizacja (moduł 07).

Hybrydy: SBPL + manipulation — dyskretyzuj tylko wymiar najistotniejszy (np. pose chwytaka), pozostałe joints rozwiązuj analitycznie (IK). Działa dla 4-6 DOF.

Ściąga

Pipeline A*

  1. open ← {start}, g[start] = 0
  2. while open ≠ ∅:
  3.   n ← argmin f(n) z open
  4.   if n = goal → return path
  5.   close n
  6.   for każdy sąsiad m: jeśli g[n]+c(n,m) < g[m]: update

Warunki na h

  • Admissible: h(n) ≤ h*(n) → optymalność
  • Consistent: h(n) ≤ c(n,m)+h(m) → brak re-ekspansji
  • h=0h = 0 → Dijkstra
  • h=hh = h^* → idealna ścieżka bez eksploracji

Standardowe heurystyki grid

  • Manhattan: 4-connected
  • Octile: 8-connected ← standard
  • Euclidean: 8-connected, luźniejsza

Warianty

  • Weighted A* (w>1): szybsze, suboptymalne ≤ w·optimum
  • Greedy (w→∞): bardzo szybkie, brak gwarancji
  • Theta*: any-angle, krótsze ścieżki
  • D* Lite: incremental replanning
  • ARA* / AD*: anytime, iteracyjnie zmniejszają w

Granice

Grid: |grid| = N^d. Działa dla d ≤ 4. Powyżej — sampling (06) lub trajopt (07).

Referencje

  • Dijkstra, „A Note on Two Problems in Connexion with Graphs" (Numerische Mathematik, 1959).
  • Hart, Nilsson, Raphael, „A Formal Basis for the Heuristic Determination of Minimum Cost Paths" (IEEE SSC 1968) — oryginalny A*.
  • Pohl, „First results on the effect of error in heuristic search" (Machine Intelligence 1970) — Weighted A*.
  • Koenig & Likhachev, „D* Lite" (AAAI 2002).
  • Likhachev, Gordon, Thrun, „ARA*: Anytime A* with Provable Bounds on Sub-Optimality" (NIPS 2003).
  • Nash, Daniel, Koenig, Felner, „Theta*: Any-Angle Path Planning on Grids" (AAAI 2007).
  • Pivtoraiko, Knepper, Kelly, „Differentially constrained mobile robot motion planning in state lattices" (J. Field Robotics 2009).
  • SBPL (Search-Based Planning Library, Likhachev et al.): sbpl/sbpl.
  • LaValle, Planning Algorithms (CUP 2006), rozdział 2: Discrete Planning.