Willkommen in der wundervollen Welt der nicht-holonomen Bewegungsplanung. Ich empfehle dies mit einem Gittergitterpfadplaner . Andere Alternativen umfassen die kinodynamische RRT und die Trajektorienoptimierung . Zu den nicht holonomen Systemen gehören Autos, Boote, Einräder oder alles, wo das Fahrzeug nicht in die gewünschte Richtung fahren kann. Die Planung für diese Systeme ist viel schwieriger als für holonome Systeme und befand sich bis 2000 am Rande der akademischen Forschung. Heutzutage gibt es viele Algorithmen zur Auswahl, die anständig funktionieren.
So funktioniert das.
Zustand
Die Konfiguration q Ihres Autos ist tatsächlich ein 3D-Zustand, der die x-, y-Position und die Ausrichtung t des Autos enthält . Die Knoten in Ihrem A * -Algorithmus sind tatsächlich 3D-Vektoren.
class Node
{
// The position and orientation of the car.
float x, y, theta;
}
Aktionen
Was ist also mit den Kanten?
Das ist etwas schwieriger, weil Ihr Auto tatsächlich unendlich viele Möglichkeiten wählen kann, das Rad zu drehen. Wir können dies also einem Gittergitterplaner zugänglich machen, indem wir die Anzahl der Aktionen, die das Auto ausführen kann, auf einen diskreten Satz A beschränken . Nehmen wir der Einfachheit halber an, dass das Auto nicht beschleunigt, sondern seine Geschwindigkeit sofort ändern kann. In unserem Fall kann A wie folgt sein:
class Action
{
// The direction of the steering wheel.
float wheelDirection;
// The speed to go at in m/s.
float speed;
// The time that it takes to complete an action in seconds.
float dt;
}
Jetzt können wir eine diskrete Reihe von Aktionen erstellen, die das Auto jederzeit ausführen kann. Zum Beispiel würde ein hartes Recht, während das Gas 0,5 Sekunden lang voll gedrückt wird, so aussehen:
Action turnRight;
turnRight.speed = 1;
turnRight.wheelDirection = 1;
turnRight.dt = 0.5;
Das Rückwärtsfahren und Rückwärtsfahren des Autos würde folgendermaßen aussehen:
Action reverse;
reverse.speed = -1;
reverse.wheelDirection = 0;
reverse.dt = 0.5;
Und Ihre Liste der Aktionen würde so aussehen:
List<Action> actions = { turnRight, turnLeft, goStraight, reverse ...}
Sie müssen auch definieren, wie eine an einem Knoten ausgeführte Aktion zu einem neuen Knoten führt. Dies wird als Vorwärtsdynamik des Systems bezeichnet.
// These forward dynamics are for a dubin's car that can change its
// course instantaneously.
Node forwardIntegrate(Node start, Action action)
{
// the speed of the car in theta, x and y.
float thetaDot = action.wheelDirection * TURNING_RADIUS;
// the discrete timestep in seconds that we integrate at.
float timestep = 0.001;
float x = start.x;
float y = start.y;
float theta = start.theta;
// Discrete Euler integration over the length of the action.
for (float t = 0; t < action.dt; t += timestep)
{
theta += timestep * thetaDot;
float xDot = action.speed * cos(theta);
float yDot = action.speed * sin(theta);
x += timestep * xDot;
y += timestep * yDot;
}
return Node(x, y, theta);
}
Diskrete Gitterzellen
Nun, das Gitternetz zu bauen, alles , was wir tun müssen , um Hash die Zustände des Fahrzeugs in einzelne Rasterzellen. Dies macht sie zu diskreten Knoten, denen A * folgen kann. Dies ist sehr wichtig, da A * sonst nicht wissen könnte, ob zwei Fahrzeugzustände tatsächlich gleich sind, um sie zu vergleichen. Durch Hashing auf ganzzahlige Gitterzellenwerte wird dies trivial.
GridCell hashNode(Node node)
{
GridCell cell;
cell.x = round(node.x / X_RESOLUTION);
cell.y = round(node.y / Y_RESOLUTION);
cell.theta = round(node.theta / THETA_RESOLUTION);
return cell;
}
Jetzt können wir einen A * -Plan erstellen, in dem GridCells die Knoten sind, Aktionen die Kanten zwischen Knoten sind und Start und Ziel in GridCells ausgedrückt werden. Die Heuristik zwischen zwei GridCells ist der Abstand in x und y plus der Winkelabstand in Theta.
Dem Pfad folgen
Jetzt, da wir einen Pfad in Bezug auf GridCells und Aktionen zwischen ihnen haben, können wir einen Pfadfolger für das Auto schreiben. Da die Gitterzellen diskret sind, würde das Auto zwischen die Zellen springen. Wir müssen also die Bewegung des Autos auf dem Weg glätten. Wenn Ihr Spiel eine Physik-Engine verwendet, können Sie dies erreichen, indem Sie einen Lenkungsregler schreiben, der versucht, das Auto so nah wie möglich am Pfad zu halten. Andernfalls können Sie den Pfad mithilfe von Bezierkurven oder einfach durch Mitteln der nächsten Punkte im Pfad animieren.