DSZI_2020_Projekt/Raporty/route-planning.md

3.7 KiB

Raport przygotowały: Natalia Plitta, Marta Roszak, Kinga Molik, Sara Kowalska
Raportowany okres: 5 kwietnia - 26 kwietnia 2020
Niniejszy raport poświęcony jest przekazaniu informacji na temat stanu projektu grupowego realizowanego na przedmiot Sztuczna Inteligencja w roku akademickim 2019/2020.

Ruch agenta został zaimplementowany za pomocą algorytmu A* z następującymi warunkami:

  • agent wykonuje: krok do przodu/ obrót w prawo/ obrót w lewo;
  • agent nie może wejść na pole "zajęte" przez stolik;
  • koszt wejścia na puste pole to 1;
  • koszt wejścia na pole z kałużą to 3.

Koszt F obliczany jest z sumy kosztu G i kosztu H.

Koszt H obliczany jest z funkcji:

def heuristic(current, goal):    
	dx = abs(current[0] - goal[0])    
	dy = abs(current[1] - goal[1])    
	return math.sqrt(dx * dx + dy * dy)

Która oblicza odległość do punku końcowego przy pomocy wzoru Euklidesa.

Metoda aStar, wywoływana przez metodę goByAStar, wytycza ścieżkę, którą będzie poruszał się kelner. W pierwszej kolejności sprawdzane jest, czy nasz punkt docelowy jest osiągalny. Jeżeli tak, to zaczynamy wytyczanie ścieżki. Tworzymy węzły (obiekty klasy Node) startowy i końcowy oraz listy pomocnicze 'open' i 'closed'.

start_node = Node(None, start)
start_node.g = start_node.h = start_node.f = 0
end_node = Node(None, end)
end_node.g = end_node.h = end_node.f = 0

open_list = []
closed_list = []
open_list.append(start_node)

Dopóki lista 'open' ma dostępne pola, jest możliwa dalsza droga. Porównujemy koszty F i za każdym razem uaktualniamy obecny punkt, w którym koszt jest najmniejszy.

Jeżeli nasz obecny węzeł jest węzłem końcowym, tzn. osiągnęliśmy nasz cel, metoda zwraca nam ścieżkę zbudowaną z odwróconej listy "kroków" (stanów) do klasy kelner.

Jeśli nie osiągnęliśmy celu, przechodzimy do analizowania sąsiadów obecnej pozycji, najpierw sprawdzamy, czy punkt ten jest osiągalny :

for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0)]:    
    node_position = (current_node.position[0] + new_position[0],current_node.position[1] + new_position[1])    
    if node_position[0] >= (rows - 1) or node_position[0] <= 0 or node_position[1] >= (rows - 1) or node_position[1] <= 0:                
        continue    
    if node_position in cantwalk:
        continue    
new_node = Node(current_node, node_position)    
children.append(new_node)

Tworzymy listę 'children'- potencjalnych następnych kroków.

Zmienna 'didBreak' pozwala nam na kontynuacje algorytmu bez redundantnego analizowania węzłów.

Jeśli nie analizowaliśmy danego węzła, obliczamy dla niego dany koszt G

  • jeśli jest on nieosiągalny - koszt jest zbyt duży do dalszego podążania w tym kierunku;
  • jeśli jest to "kałuża", to koszt wynosi 3;
  • jeśli jest to "zwykła płytka", koszt wynosi 1.

Następnie obliczamy koszt H i koszt F (jako sumę G i H) dla danego węzła 'child' i dodajemy go do listy 'open'.

if child.position in cantwalk:    
	child.g = current_node.g + 99999
elif child.position in puddles:    
	child.g = current_node.g + 3
else:    
	child.g = current_node.g + 1
child.h = heuristic(child.position, end_node.position)
child.f = child.g + child.h

Kontrolowane jest również, czy dany krok nie oddalałby agenta od celu:

for open_node in open_list:
   if child == open_node and child.g > open_node.g:
    didBreak = True
    break

   if didBreak:
    continue

open_list.append(child)

Gdy wytyczona zostanie ścieżka, metoda goByAStar przy pomocy dostępnych akcji (rotateRight, rotateLeft i goForward), "wykonuje" odpowiednie ruchy, zgodne z wytyczoną ścieżką.