Plánování na mřížce
rastrové mapy
Dnes budeme pokračovat v plánování cesty tentokrát ovšem za použití rastrových map či pravděpodobnostních mřízek. Mnoho algoritmů zde popsaných máte příležitost potkat také v počítačové grafice. Jejich podobnost vyplývá z toho, že i na pravděpodobnostní mřížku je možné nahlížet jako na obrázek. Dozvíme se, i co je to potenciálové pole či algoritmus A*. Závěrem si ukážeme jednu z možností, jak plánovací algoritmy vhodně integrovat do řídícího systému robota.
Rastrové mapy
V minulé přednášce jsme si ukázali někteté z tzv. exaktních plánovacích
algorimů. Jejich hlavní výhodou byla jejich exaktnost, neboli to, že jejich výsledkem
vždy bylo přesně to, co jsme potřebovali (tj. nejkratší cesta, nejbezpečnější cesta
atd). V praxi je ale třeba brát tuto jejich exaktnost s rozumnou rezervou, protože
typicky narazíme na nádherný příklad GIGO principu. GIGO princip popisuje velice
jednoduchou věc, která bude angličtinářům jasná již z pouhého rozepsání zkratky —
Garbage In, Garbage Out (garbage = odpadky, nesmysly). Dalo by se také říci,
že kvalita výsledku velmi závisí na kvalitě vstupních dat.
multimap |
Nabízí se zde tedy možnost reprezentovat prostředí pomocí mřížky, kde je každá
buňka označena jako volný prostor nebo překážka. Velikost rastru nám automaticky
definuje přesnost, s jakou počítáme.
Plánovací algoritmus nad mřížkou se tedy snaží najít posloupnost volných buněk,
která nám definuje, kudy se má robot pohybovat.
Mřížka |
Pravěpodobnostní mřízky
Rastrové mapy se také přímo nabízejí pro reprezentaci nejistoty v příjimaném
měření. Rozšíříme-li uloženou informaci v jedné buňce z hodnoty 0/1 na hodnot
několik, můžeme reprezentovat jistotu s jakou je daná buňka obsazená/volná
(získáme jakýsi šedotónový obrázek). Je možné vyhradit jednu hodnotu například
pro hodnotu „nevím”, pokud celá mapa není dopředu známa.
Takto reprezentovná mapa také umožňuje poměrně přímočarou integraci dat z
několika různých senzorových systémů.
Plánování na mřížce
Jak nejlépe využít připravená data při hledání cesty? Je možné použít některé
principy vyvinuté pro vektorové mapy? Jak moc se vlastně liší plánování nad mřížkou
a nad vektorovou mapou? Tyto otázky se nyní pokusíme zodpovědět.
Potenciálová pole
Lokální minimum |
V nejjednodušším případě můžeme jako hodnotu buňky dosadit její eukleidovskou
vzdálenosti do cíle. Po této inicializaci použijeme pro pohyb robota následující
algoritmus: „Ze sousedních buněk vyber tu s nejnižšší hodnotou přesuň
se do ní. Pokračuj dokud nedorazíš do cíle nebo dokud neuvízneš.”
Záplavové vyplňování |
Náš algoritmus tedy vylepšíme. Hodnoty buňkám přiřadíme pomocí algoritmu
záplavového vyplňování. V angličtině se s ním můžete také setkat pod názvy
wavefront-expansion nebo flood fill. Začneme v cíli a opět
mu přiřadíme hodnotu 0. Poté opakujeme následující postup: vezmi všechny
neočíslované volné buňky sousedící s již očíslovanou buňkou a přiřaď jim
hodnotu o jedna vyšší. Opakuj, dokud není očíslovaný i cíl nebo dokud nevyčerpáme
všechny neočíslované buňky.
Tento algoritmus definuje potenciálové pole, které má pouze jedno minimum a to
v hledaném cíli. Navíc určí všechny buňky, ze kterých se lze do tohoto cíle dostat.
Číslo přiřazené dané buňce udává vzdálenost od cíle (počet buněk, kterými se musí
projít).
Grafový přístup
Zkušenějším programátorům zajisté neunikla podobnost posledního zmiňovaného
algoritmu s grafovým algoritmem procházení do šířky (anglicky
breath-first search). Typická implementace procházení do šířky používá
frontu pro uchování ještě nezpracovaných vrcholů. Příklad v pseudokódu:
q.insert(xg) while (q ≠ ∅) do x := q.getFirst() if (x == xs) return SUCCESS forall u ∈ e(x,u) if (u not visited) Mark u as visited q.insert(u) return FAILURE
Jako frontu používáme q,
xg
je cílová
pozice a xs pozice startovní. Z fronty vždy vybereme
prvek x a všechny vrcholy u, do kterých vede hrana a
jsou nenavštívené, přidáme do fronty. Předpokládáme, že virtuální hrany
existují vždy mezi sousedními vrcholy.Tento algoritmus dělí vrcholy/buňky do tří kategorií:
- nenavštívené (unvisited)
- ještě nemají přiřazenu vzdálenost — na počátku všechny buňky mimo xg
- mrtvé (dead)
- již navšívené a všichni jejich sousedi byli také zpracováni — v jistém smyslu jsou tyto buňky opravdu mrtvé, protože již nijak nepřispějí k hledání cesty
- živé (alive)
- již navštívené, ale má ještě nenavštívené sousedy — na počátku pouze xg — prvky fronty q
Dijkstra
Přechod z buňky do buňky nemusí vždy stát pouze jednu jednotku. V takovém
případě můžeme s výhodou uplatnit Dijkstrův algortimus pro hledání nejkratší
cesty v grafu. Jediná modifikace spočívá v náhradě obyčejné fronty frontou
prioritní, která třídí vrcholy podle jejich vzdálenosti.
A*
Algoritmus A* je modifikací Dijkstrova algoritmu. Dijkstrův algoritmus nám
našel všechny možné buňky, ze kterých se můžeme do našeho cíle dostat.
Důsledkem tohoto chování je, že algoritmus prochází zbytečně mnoho buněk než
najde
xs
. Algoritmus A* zlepšuje dále toto chování
další úpravou třídění vrcholů ve frontě. Vrcholy nejsou tříděny pouze podle
jejich vzdálenosti od xg
, ale podle součtu této
vzdálenosti s odhadem vzdálenosti od xs
. Pokud je
tento odhad vždy odhadem spodním (tj. nikdy není vyšší než skutečná vzdálenost
do xs
— můžeme použít třeba obyčejnou
eukleidovskou vzdálenost), pak i tento algoritmus vždy najde cestu nejkratší.
V limitním případě, kdy je odhad vzdálenosti vždy 0, algoritmus degeneruje na
základní Dijkstrův algoritmus.B*
Pokud frontu třídíme pouze podle odhadu vzdálenosti do
xs
získáme ještě agresivnější algoritmus, u kterého
ale již není garantováno, že nalezne opravdu cestu nejkratší. V jednoduchých
prostředích ho lze ale s úspěchem využít.Pseudo-Voronoi diagramy
Ne vždy nám vyhovuje nejkratší cesta. Někdy se například chceme držet
daleko od překážek. Již známe Voronoi diagramy pro body a polygony. Je možné
je definovat i nad mřížkou?
Můžem spustit procházení do šířky z každé překážky zvlášť. Voronoi hranu
definujem tam, kde se nám setkají vlny ze dvou různých překážek.
Takto předzpracovanou strukturu můžeme s výhodou využít i pro
opakované hledání cest. Ze startu i z cíle spustíme prohledávání do šířky a
počkáme, než nám najde cestu na některou Voronoi hranu. Poté postupujeme po
hranách až do části obsahující cíl, kde nám druhé prohledávání do šírky
definuje zbylou část cesty.
Která cesta je nejlepší?
Zatím jsme se věnovali pouze hledání nějaké (případně nejkratší) cesty
na mapě reprezentované mřížkou s hodnotami 0/1. Jak tedy do našich algoritmů
začleníme fakt, že si tou mapou vlastně nejsme až tak moc jisti? Potom
nám to také vzniká otázka, kterou jsme museli částečně řešit i v předcházející
části — a to která cesta je vlastně ta nejlepší? Je to ta nejkratší
(graf viditelnosti), nejbezpečnější (Voronoi diagramy), nejrychlejší, nejjistější
(pouze přes známé volné prostředí) nebo nějaká jejich kombinace?
Potenciály podruhé
Jednou z metod jak získat cestu beroucí v úvahu výše zmiňované požadavky
zároveň je alternativní definice potenciálového pole inspirovaná šířením tepla
v prostředí. Cíl definujeme jako zdroj tepla (vždy má hodnotu 1), překážky pak
mrazí (vždy hodnota 0). Ostatní buňky mají svoji hodnotu definovanou tak, aby
stejné množství tepla přijaly i odevzdaly. Označíme-li proměnnou U(i,j) stav
buňky na pozici (i,j), tak lze rovnovážný stav příjmu a výdaje popsat takto:
(U(i-1, j)-U(i,j)) + (U(i+1, j)-U(i,j)) + (U(i, j-1)-U(i,j)) + (U(i,
j+1)-U(i,j)) = 0. Po spočítání potenciálového pole se pak robot snaží jít
směrem nejvyššího nárustu tepla.
Výpočet rovnovážného stavu odpovídá řešení soustavy rovnic, kde U(cil) = 1,
U(překážka) = 0 a zmiňovaná rovnice pro všechny ostatní buňky. Problém této
soustavy je, že je hodně veliká - počet rovnic odpovídá počtu buněk. Řeší se tedy
pomocí numerické matematiky, konkrétně Gauss-Siedelovou metodou. Jedná se o
iterační metodu, kdy postupně provádíme korekce jednotlivých proměnných:
U(i,j) = ¼ (U(i-1, j) + U(i+1, j) + U(i, j-1) + U(i, j+1)).
Úlohu nyní můžeme dále rozšiřovat. Podobně jako v počítačové hře Warlords,
kde se jednotlivé postavičky pohybují rychlostí v závislosti na obtížnosti
terénu, můžeme i my jednotlivým buňkám přidat koeficient propustnosti. V
Gauss-Siedelově metodě tedy k průměru sousedů použijeme extra koeficient
M(i,j) odpovídající propustnosti: U(i,j) = M(i,j) ¼ (U(i-1, j) + U(i+1, j) +
U(i, j-1) + U(i, j+1)). Pokud nechceme, aby se robot pohyboval blízko
překážkám, tak zvolíme hodnotu M(i,j) pro přilehlé buňky blízké nule (0
odpovídá absolutní neprůchodnosti). Obdobně můžeme vážit dosud nezmapované
části prostoru, a preferovat tak hledání cesty spíše ve známých oblastech.
Problémy
Asi hlavní problém této potenciálové metody je její pomalý výpočet. Rozhodně
není real-time a nelze tedy používat k jednoduchému vyhýbání se překážkám. Na
druhou stranu má metoda ale pouze jedno maximum, takže pokud cesta existuje,
tak bude nalezena.
Druhý problém se týká samotných výpočtů. Dochází zde totiž k expenenciálnímu
poklesu hodnot, takže i proměnná typu double stačí maximálně na
1000 kroků. To lze řešit rozšířenou aritmetikou, ale v důsledku je pak celá
metoda ještě pomalejší.
Související odkazy
- Motion Planning Using Potential Fields
- článek na serveru GameDev.net, potenciálové pole v kontextu programování her, 2000
- Interactive Motion Planning Using Hardware-Accelerated Computation of Generalized Voronoi Diagrams
- jak počítat diskrétní aproximaci Voronoi diagramu pomocí grafické karty (pdf,avi), 1999
- UNC Geometry Group: Video
- soubor animací plánovacích algoritmů
- Petr Štěpán: Vnitřní reprezentace prostředí pro autonomní roboty (disertační práce)
- A* Algorithm Tutorial
- přehledné vysvětlení algoritmu včetně vzorové implementace v C++, 1999
- A* Pathfinding for Beginners
- úvod do A* pro uplné začátečníky, včetně vzorové implementace v C++, 2003
- Potential Fields Tutorial
- základy (lokálních) potenciálových polí