Come realizzare percorsi dall'aspetto naturale con A * su una griglia?


13

Ho letto questo: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

Ma ci sono alcune cose che non capisco, ad esempio l'articolo dice di usare qualcosa del genere per l'individuazione di percorsi con movimento diagonale:

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * max(dx, dy)

Non so come impostare D per ottenere un percorso dall'aspetto naturale come nell'articolo, ho impostato D al costo più basso tra i quadrati adiacenti come si diceva, e non so cosa intendessero per roba euristica essere 4 * D, ciò non sembra cambiare nulla.

Questa è la mia funzione euristica e la funzione di spostamento:

def heuristic(self, node, goal):
    D = 5
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * max(dx, dy)

def move_cost(self, current, node):
   cross = abs(current.x - node.x) == 1 and abs(current.y - node.y) == 1
   return 7 if cross else 5

Risultato:

inserisci qui la descrizione dell'immagine

Il liscio percorso di navigazione che vogliamo che accada:

inserisci qui la descrizione dell'immagine

Il resto del mio codice: http://pastebin.com/TL2cEkeX


Aggiornare

Questa è la migliore soluzione che ho trovato finora:

def heuristic(node, start, goal):
    dx1 = node.x - goal.x
    dy1 = node.y - goal.y
    dx2 = start.x - goal.x
    dy2 = start.y - goal.y
    cross = abs(dx1*dy2 - dx2*dy1)

    dx3 = abs(dx1)
    dy3 = abs(dy1)

    return 5 + (cross*0.01) * (dx3+dy3) + (sqrt(2)-2) * min(dx3, dy3)

def move_cost(current, node):
    cross = abs(current.x - node.x) == 1 and abs(current.y - node.y) == 1
    return 7 if cross else 5

Produce il percorso desiderato dalla seconda immagine, ma non gestisce molto bene gli ostacoli (tende a strisciare sui muri) e non riesce a produrre percorsi ottimali a volte su distanze più lunghe.

Quali sono alcune modifiche e ottimizzazioni che posso applicare per migliorarlo?


2
E se usi la distanza cartesiana come euristica?
Jimmy,

2
qui è solo un'idea, aumentare il costo del passaggio da una tessera all'altra per ogni passo che l'agente si muove nella stessa direzione.
Ali1S232

@Jimmy ho provato sqrt (pow (goal.x - node.x, 2) + pow (goal.y - node.y, 2)) e per il mio piccolo percorso di esempio in realtà restituisce esattamente lo stesso dell'immagine nella mia domanda .
Entità anonima

Risposte:


10

A * ti dà il percorso più breve nel grafico. Quando si utilizza una griglia come il grafico ci sono spesso molteplici percorsi più brevi. Nel tuo primo diagramma, questo è uno dei percorsi più brevi. Mette al primo posto tutti i movimenti assiali e successivamente tutti i movimenti diagonali. Ma questo è lo stesso percorso di lunghezza se si posizionano prima tutte le diagonali o se si mescolano movimenti assiali e diagonali. Questi sono tutti equivalentemente brevi e quale A * sceglie dipende da come viene scritto il codice e da come viene rappresentato il grafico.

Penso che ciò che desideri sia:

  1. Devi muoverti sulla griglia, ma vuoi mescolare i passi assiale e diagonale in modo che appaia migliore. Un approccio è quello di scegliere uno degli altri percorsi ugualmente brevi; continua a leggere la pagina Euristica per trovare "pareggio". Un altro approccio è quando stai valutando i vicini, scegli casualmente quale valutare per primo in modo che non scelga sempre uno prima dell'altro. Io non suggerisco usando euclidea / distanza cartesiana se si desidera spostare sulla griglia; è una discrepanza che rende A * più lento.
  2. Non è necessario spostarsi sulla griglia e si desidera spostarsi in linea retta. Un approccio è quello di raddrizzare i percorsi usando il "tiro della corda". Stai cercando luoghi in cui il percorso gira e traccia linee rette tra quei punti. Un altro approccio è quello di applicare questo al grafico sottostante stesso. Invece di individuare i percorsi sulla griglia, cerca i punti chiave sulla mappa, quindi spostati lungo le linee rette tra i punti chiave. Puoi vedere un esempio qui . Ancora un altro approccio è l' algoritmo Theta * .

Buona risposta. Ho aggiornato la mia domanda con alcune nuove informazioni, spero che tu possa specificare un po 'la tua risposta.
Entità anonima,

Penso che ci si aspetti un po 'di ostacoli; c'è un diagramma nella pagina Euristica che si intitola "meno bello con ostacoli". Gli approcci alla rottura del pareggio non aiutano molto a superare gli ostacoli. Uno degli altri approcci (come Theta *) potrebbe essere quello che desideri.
entro il

2

L'algoritmo A * consente di assegnare costi diversi ai bordi del percorso. È inoltre possibile assegnare i costi in base alle circostanze. Questo è il tuo strumento principale per modellare i percorsi A * nell'aspetto nel modo in cui vuoi che appaiano.

Quando vuoi scoraggiare lunghe diagonali, puoi penalizzarle. Aggiungi un po 'di costo per ogni volta che il percorso va nella stessa direzione. Quando lo fai, l'algoritmo proverà automaticamente a distribuire i passaggi diagonali nel modo più uniforme possibile su tutto il percorso. Assicurati solo che questo costo aggiuntivo non sia mai più il costo di prendere un vantaggio aggiuntivo, altrimenti l'algoritmo inizierà a fare deviazioni completamente inutili solo per evitare linee rette.

Una buona formula potrebbe essere:

cost = normal_cost * (1.1 - 0.1 / num_of_steps_in_the_same_direction)

Si noti che ciò richiede che il tracciato del costo sia tracciato come valori in virgola mobile, non come numeri interi.


1

Adattare A *

Come ha detto Philipp, dovresti aggiungere dei costi quando la direzione non cambia da molto tempo. Ma la funzione di Philipp può portare rapidamente a sommare i costi aggiuntivi, che sono superiori al costo per attraversare una tessera aggiuntiva. Ma la sua idea chiave è corretta!

Sembra facile adattare A * per calcolare "tutti" percorsi ottimali (con la lunghezza più breve) e quindi selezionarne uno con un altro euristico. Ma c'è un problema. Se hai un percorso lungo, potrebbero esserci molte soluzioni con lunghezza ottimale. Questo fa sì che l'algoritmo A * impieghi molto più tempo per calcolare anche tutte queste altre soluzioni. Questo perché la griglia. Non è possibile camminare di 80 gradi invece di 90 gradi, il che porta a più soluzioni non ottimali invece di una soluzione ottimale. Per l'immaginazione, immagina una mappa senza ostacoli. La distanza x è 2, la distanza y è 3. Ciò significa che tutti i percorsi più brevi hanno 2 movimenti diagonali e 1 movimento dritto. Esistono 3 combinazioni valide: SDD, DSD, DDS (dove D = diagonale, S = dritto) per questo semplice percorso. Il vero "divertimento" inizia già quando si hanno percorsi con es 3 mosse dritte e 2 diagonali: SSSDD, SSDSD, SSDDS, SDSSD, SDSDS, SDDSS, DSSSD, DSSDS, DSDSS, DDSSS (10 varianti del percorso più breve, se non ne ho perso nessuna). Penso che avresti dovuto avere l'idea ...

Quindi dovremmo risolvere questo problema adattando la funzione di costo in modo che un minor numero di soluzioni (o anche solo una soluzione) siano "ottimali".

Adattamento della funzione di costo

Fare l'adattamento come suggerisce Philipp nella sua formula di esempio ti darà risultati molto migliori, ma ha ancora alcuni problemi. Non distribuirà uniformemente le "parti" più corte / più lunghe lungo il percorso, il che significa che i cambi di direzione saranno più spesso all'inizio del percorso o viceversa.

Inoltre, un percorso con l'attore infinito per "girare" sembra essere non ottimale se osservato da un essere umano. Dato che richiede tempo (per mostrare l'animazione della svolta) e quindi deve essere più lento.

Tuttavia, invece di utilizzare i float per i costi, è possibile implementare un "costo secondario" o criteri di ordinamento secondari. Se i costi primari sono gli stessi, il costo secondario viene utilizzato per stimare la soluzione da preferire. Ciò non causerà un aumento accidentale dei costi primari (lunghezza del percorso nella misura della griglia).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.