SzIProjekt/route-planning.md

198 lines
5.5 KiB
Markdown
Raw Normal View History

2020-04-28 23:13:38 +02:00
# *Sztuczna inteligencja - projekt zespołowy - Autonomiczny Traktor*
#### autorzy: Aleksandra Werda, Natalia Wiśniewska, Kinga Jagodzińska, Aleksandra Jonas
***
## Planowanie ruchu
- schemat procedury przeszukiwania grafu stanów z uwzględnieniem kosztu
- strategia A*
___
Drugim zadaniem dotyczącym projektu jest zastosowanie strategii przeszukiwania przestrzeni stanów do problemu planowania ruchu agenta na kracie.
## Heurystyka
- założenie — odległość pomiędzy sąsiadującymi polami wynosi 2, tyle samo co koszt wjazdu na puste pole
- s — pole, na którym jesteśmy
- f — pole końcowe
- dla s = f program kończy pracę, bo znajdujemy się już w położeniu końcowym, dlatego pominęłyśmy ten przypadek w definicji heurystyki.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
def hscore(self, s, f):
if f > s:
a_h = (f - s) // 5
else:
a_h = (s - f) // 5
if f % 5 >= s % 5:
b_h = f % 5 - s % 5
else:
b_h = s % 5 - f % 5 + 1
return 2 * m.sqrt(a_h * 2 + b_h * 2)
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Wpierw obliczamy wysokość trójkąta, jaki tworzą obecne i końcowe pole, w celu wyznaczenia przeciwprostokątnej — odległość między s i f.
## Funkcja następnika
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Tworzymy listę, do której będziemy kolejno generować sąsiadów dla danego x.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
def neighbours(self):
self.neighbours = list(range(25))
self.neighbours[0] = [1, 5]
self.neighbours[4] = [3, 9]
self.neighbours[20] = [15, 21]
self.neighbours[24] = [19, 23]
for x in range(1, 4):
self.neighbours[x] = [x - 1, x + 5, x + 1]
for x in range(5, 16, 5):
self.neighbours[x] = [x - 5, x + 1, x + 5]
for x in range(9, 20, 5):
self.neighbours[x] = [x - 5, x - 1, x + 5]
for x in range(21, 24):
self.neighbours[x] = [x - 1, x - 5, x + 1]
for x in [6, 7, 8, 11, 12, 13, 16, 17, 18]:
self.neighbours[x] = [x - 5, x - 1, x + 1, x + 5]
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
## Główna pętla strategii przeszukiwania
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Zaczynamy od znalezienia w open_set pola o najniższym f.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
while open_set:
# Szukanie pola w open_set z najniższym f
temp1 = max(self.f_score) + 1
x = 0
for i in range(len(open_set)):
if self.f_score[open_set[i]] <= temp1:
x = open_set[i]
temp1 = self.f_score[open_set[i]]
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Jeżeli to pole odpowiada temu, do którego chcemy dojść — wywołujemy funkcję tworzącą ścieżkę z pola wyjściowego. Póki nie — pomijamy.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
if x == koniec:
return self.reconstruct_path(self.came_from, koniec)
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Usuwamy x z open_set i wrzucamy do closed_set, aby upewnić się, że nie będziemy go ponownie sprawdzać.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
open_set.remove(x)
closed_set.append(x)
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Następnie sprawdzamy sąsiadów:
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
for y in self.neighbours[x]:
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
To tutaj chcieliśmy mieć pewność, że nie trafimy ponownie na x.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
if y in closed_set:
continue
```
2020-04-28 23:14:54 +02:00
2020-04-29 00:45:02 +02:00
Chcemy również sprawdzić, czy jeżeli y jest już w open_set, to nie istnieje krótsze przejście z pola startowego przez inne pole na y, niż bezpośrednio na pole y.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
tentative_g_score = self.g_score[x] + self.game.fields[y][3]
if y not in open_set:
open_set.append(y)
tentative_is_better = True
elif tentative_g_score < self.g_score[y]:
tentative_is_better = True
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
Wyliczamy g i f — przewidywany dystans od startu do celu, dla y.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
if tentative_is_better == True:
self.came_from[y] = x
self.g_score[y] = tentative_g_score
self.f_score[y] = self.g_score[y] + self.hscore(y, koniec)
```
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
## Tworzenie ścieżki
Przechodzimy po tablicy poprzedników, aż dojdziemy do początku — pola, które nie ma poprzednika.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
def reconstruct_path(self, came_from, current):
total_path = [current]
while came_from[current] != 0:
current = came_from[current]
total_path.insert(0, current)
return total_path
```
## Koszt podróży na pola uprawne
Zmiany w projekcie zaszły również w pliku run.py, gdzie generujemy losowo pola — każdy kolor odpowiada innej uprawie i etapie jej wzrostu.
Mamy tutaj tablicę tablic. Każda z nich zawiera kolejno informacje o: rodzaju upraw, glebie, nawodnieniu pola oraz koszcie podróży na pole z daną uprawą.
2020-04-28 23:14:54 +02:00
2020-04-28 23:13:38 +02:00
```
def randomize_field(self):
for x in range(25):
temp = []
# nasiona
temp.append(random.choice(["żyto", "jęczmień", "owies", "marchew", "rzodkiew", "pietruszka", "puste"]))
# gleba
temp.append(random.choice([True, False]))
# woda
temp.append(random.choice([True, False]))
# # growth rate
# temp.append(random)
# # cost
if temp[0] == "żyto":
temp.append(10)
elif temp[0] == "jęczmień":
temp.append(12)
elif temp[0] == "owies":
temp.append(8)
elif temp[0] == "marchew":
temp.append(14)
elif temp[0] == "rzodkiew":
temp.append(7)
elif temp[0] == "pietruszka":
temp.append(6)
elif temp[0] == "puste":
temp.append(2)
else:
temp.append(0)
self.fields.append(temp)
```