Wie erstelle ich mit A * in einem Raster natürlich aussehende Pfade?

13

Ich habe dies gelesen: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

Aber es gibt einige Dinge, die ich nicht verstehe, zum Beispiel, in dem Artikel heißt es, dass man so etwas für die Wegfindung mit diagonalen Bewegungen verwenden soll:

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

Ich weiß nicht, wie man D setzt, um einen natürlich aussehenden Pfad wie in dem Artikel zu erhalten, ich setze D auf die niedrigsten Kosten zwischen benachbarten Quadraten, wie es heißt, und ich weiß nicht, was sie mit dem Zeug über die Heuristik gemeint haben sollen sei 4 * D, das scheint nichts zu ändern.

Dies ist meine heuristische Funktion und Bewegungsfunktion:

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

Ergebnis:

Bildbeschreibung hier eingeben

Den reibungslosen Segelweg wollen wir schaffen:

Bildbeschreibung hier eingeben

Der Rest meines Codes: http://pastebin.com/TL2cEkeX


Aktualisieren

Dies ist die beste Lösung, die ich bisher gefunden habe:

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

Ab dem zweiten Bild wird der gewünschte Pfad erstellt, Hindernisse werden jedoch nicht gut bewältigt (es kann an Wänden kriechen), und manchmal werden auf längeren Strecken keine optimalen Pfade erstellt.

Welche Verbesserungen und Optimierungen kann ich anwenden, um es zu verbessern?

Anonyme Einheit
quelle
2
Was ist, wenn Sie die kartesische Distanz als Heuristik verwenden?
Jimmy
2
Hier ist nur eine Idee: Erhöhen Sie die Kosten für den Wechsel von einem Plättchen zum nächsten, wenn sich der Agent in die gleiche Richtung bewegt.
Ali1S232
@ Jimmy Ich habe versucht, sqrt (pow (goal.x - node.x, 2) + pow (goal.y - node.y, 2)) und für meinen kleinen Beispielpfad gibt es genau das gleiche wie das Bild in meiner Frage zurück .
Anonymes Unternehmen

Antworten:

10

Ein * gibt den kürzesten Weg in der Grafik an. Wenn Sie ein Raster als Diagramm verwenden, gibt es häufig mehrere kürzeste Pfade. In Ihrem ersten Diagramm ist dies einer der kürzesten Wege. Dabei werden zuerst alle Axialbewegungen und danach alle Diagonalbewegungen ausgeführt. Aber das ist der gleiche Längenpfad, als würden Sie alle Diagonalen zuerst setzen oder axiale und diagonale Bewegungen mischen. Diese sind alle äquivalent kurz und welche A * -Auswahl davon abhängt, wie der Code geschrieben und wie das Diagramm dargestellt wird.

Ich denke, was Sie wollen, ist entweder:

  1. Sie müssen sich auf dem Raster bewegen, aber Sie möchten die axialen und diagonalen Schritte mischen, damit es besser aussieht. Ein Ansatz besteht darin, einen der anderen, gleich kurzen Wege zu wählen. Lesen Sie diese Heuristik-Seite weiter, um „Tie Breaking“ zu finden. Ein anderer Ansatz ist, wenn Sie Nachbarn bewerten, zufällig auszuwählen, welche zuerst bewertet werden soll, damit nicht immer eine vor der anderen ausgewählt wird. Ich nicht empfehlen euklidischen / cartesianischen Abstand zu verwenden , wenn Sie auf das Raster verschieben möchten; Es ist eine Fehlpaarung, die A * langsamer laufen lässt.
  2. Sie müssen sich nicht auf dem Raster bewegen und möchten sich in einer geraden Linie bewegen. Ein Ansatz besteht darin, die Pfade durch „Fadenziehen“ zu begradigen. Sie suchen nach Stellen, an denen sich der Pfad dreht, und zeichnen gerade Linien zwischen diesen Punkten. Ein anderer Ansatz besteht darin, dies auf das zugrunde liegende Diagramm selbst anzuwenden. Anstatt auf dem Raster nach Pfaden zu suchen, suchen Sie nach den Schlüsselpunkten auf der Karte und bewegen Sie sich dann entlang gerader Linien zwischen diesen Schlüsselpunkten. Sie können hier ein Beispiel sehen . Ein weiterer Ansatz ist der Theta * -Algorithmus .
amitp
quelle
Gute Antwort. Ich habe meine Frage mit einigen neuen Informationen aktualisiert. Ich hoffe, Sie können Ihre Antwort ein wenig spezifizieren.
Anonymes Unternehmen
Ich denke, das bisschen über Hindernisse wird erwartet; Auf der Seite Heuristik befindet sich ein Diagramm mit dem Titel „Weniger hübsch mit Hindernissen“. Die bahnbrechenden Ansätze helfen nicht viel bei Hindernissen. Einer der anderen Ansätze (wie Theta *) ist möglicherweise das, was Sie wollen.
3.
2

Mit dem A * -Algorithmus können Sie Pfadkanten unterschiedliche Kosten zuweisen. Sie können je nach Umständen auch Kosten zuweisen. Dies ist Ihr Hauptwerkzeug, um A * -Pfade so zu formen, dass sie so aussehen, wie Sie es möchten.

Wenn Sie lange Diagonalen abschrecken möchten, können Sie sie bestrafen. Fügen Sie ein kleines bisschen Kosten für jedes Mal hinzu, wenn der Pfad in die gleiche Richtung verläuft. Wenn Sie dies tun, versucht der Algorithmus automatisch, diagonale Schritte so gleichmäßig wie möglich über den gesamten Pfad zu verteilen. Stellen Sie nur sicher, dass diese zusätzlichen Kosten niemals höher sind als die Kosten für einen zusätzlichen Vorteil. Andernfalls führt der Algorithmus zu völlig unnötigen Umwegen, um gerade Linien zu vermeiden.

Eine gute Formel könnte sein:

cost = normal_cost * (1.1 - 0.1 / num_of_steps_in_the_same_direction)

Beachten Sie, dass hierfür die Pfadkosten als Gleitkommawerte und nicht als ganze Zahlen nachverfolgt werden müssen.

Philipp
quelle
1

Anpassung von A *

Wie Philipp sagte, sollten Sie Kosten hinzufügen, wenn sich die Richtung für lange Zeit nicht ändert. Die Funktion von Philipp kann jedoch schnell dazu führen, dass zusätzliche Kosten summiert werden, die höher sind als die Kosten für das Durchqueren einer zusätzlichen Kachel. Aber seine Schlüsselidee ist richtig!

Es scheint einfach zu sein, A * anzupassen, um "alle" optimalen Pfade (mit kürzester Länge) zu berechnen und dann einen von ihnen durch eine andere Heuristik auszuwählen. Aber es gibt ein Problem. Wenn Sie einen langen Weg haben, gibt es möglicherweise viele Lösungen mit optimaler Länge. Dies führt dazu, dass der A * -Algorithmus viel länger benötigt, um alle diese anderen Lösungen zu berechnen. Das liegt am Gitter. Sie können nicht um 80 Grad anstatt um 90 Grad gehen, was zu mehreren suboptimalen Lösungen anstelle einer optimalen Lösung führt. Stellen Sie sich als Vorstellungskraft eine Karte ohne Hindernisse vor. Die x-Distanz ist 2, die y-Distanz ist 3. Dies bedeutet, dass alle kürzesten Pfade 2 diagonale Bewegungen und 1 gerade Bewegung haben. Für diesen einfachen Pfad gibt es 3 gültige Kombinationen: SDD, DSD, DDS (wobei D = Diagonale, S = Gerade). Der echte "Spaß" fängt schon an, wenn man Wege mit zB hat 3 gerade und 2 diagonale Bewegungen: SSSDD, SSDSD, SSDDS, SDSSD, SDSDS, SDDSS, DSSSD, DSSDS, DSDSS, DDSSS (10 Variationen des kürzesten Pfades, wenn ich keinen verpasst habe). Ich denke, du hättest auf die Idee kommen sollen ...

Wir sollten dies beheben, indem wir die Kostenfunktion so anpassen, dass weniger Lösungen (oder sogar nur eine Lösung) "optimal" sind.

Kostenfunktion anpassen

Wenn Sie die Anpassung durchführen, wie Philipp in seiner Beispielformel vorschlägt, erhalten Sie viel bessere Ergebnisse, es treten jedoch noch einige Probleme auf. Die kürzeren / längeren "Teile" werden nicht gleichmäßig auf dem Pfad verteilt, was bedeutet, dass die Richtungsänderungen häufiger am Anfang des Pfades erfolgen oder umgekehrt.

Außerdem scheint ein Weg, auf dem der Schauspieler endlos "drehen" muss, suboptimal zu sein, wenn er von einem Menschen beobachtet wird. Da es Zeit braucht (um die Turn-Animation zu zeigen), muss es langsamer sein.

Anstatt Floats für die Kosten zu verwenden, können Sie jedoch ein "sekundäres Kosten-" oder ein sekundäres Sortierkriterium implementieren. Wenn die Primärkosten gleich sind, werden die Sekundärkosten verwendet, um abzuschätzen, welche Lösung vorzuziehen ist. Dies führt nicht versehentlich zu einer Erhöhung der Primärkosten (Streckenlänge im Rastermaß).

SDwarfs
quelle