Bienvenido al maravilloso mundo de la planificación del movimiento no holonómico . Recomiendo hacer esto usando un planificador de ruta de rejilla reticular . Otras alternativas incluyen la RRT kinodinámica y la optimización de la trayectoria . Los sistemas no holonómicos incluyen automóviles, botes, monociclos o cualquier cosa en la que el vehículo no pueda viajar en la dirección que desee. La planificación de estos sistemas es mucho más difícil que los sistemas holonómicos y hasta ~ 2000 estuvo al borde de la investigación académica. Hoy en día hay muchos algoritmos para elegir que funcionan decentemente.
Así es como funciona.
Estado
La configuración q de su automóvil es en realidad un estado 3D que contiene la posición x, y de su automóvil y su orientación t . Los nodos en su algoritmo A * son en realidad vectores 3D.
class Node
{
// The position and orientation of the car.
float x, y, theta;
}
Comportamiento
¿Y qué hay de los bordes?
Eso es un poco más difícil, porque su automóvil podría elegir una infinidad de formas de girar la rueda. Por lo tanto, podemos hacer que esta accesible a un planificador rejilla reticular mediante la restricción del número de acciones que el coche puede tomar para un conjunto discreto, A . En aras de la simplicidad, supongamos que el automóvil no acelera, sino que puede cambiar su velocidad instantáneamente. En nuestro caso, A puede ser el siguiente:
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;
}
Ahora, podemos crear un conjunto discreto de acciones que el automóvil puede tomar en cualquier momento. Por ejemplo, una derecha dura mientras presiona el gas al máximo durante 0,5 segundos se vería así:
Action turnRight;
turnRight.speed = 1;
turnRight.wheelDirection = 1;
turnRight.dt = 0.5;
Poner el auto en reversa y retroceder se vería así:
Action reverse;
reverse.speed = -1;
reverse.wheelDirection = 0;
reverse.dt = 0.5;
Y su lista de acciones se vería así:
List<Action> actions = { turnRight, turnLeft, goStraight, reverse ...}
También necesita una forma de definir cómo una acción tomada en un nodo da como resultado un nuevo nodo. Esto se llama la dinámica hacia adelante del sistema.
// 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);
}
Celdas de rejilla discretas
Ahora, para construir la cuadrícula de celosía, todo lo que tenemos que hacer es dividir los estados del automóvil en celdas de cuadrícula discretas. Esto los convierte en nodos discretos que pueden ser seguidos por A *. Esto es muy importante porque, de lo contrario, A * no tendría forma de saber si dos estados del automóvil son realmente iguales para poder compararlos. Al dividir los valores de las celdas de la cuadrícula entera, esto se vuelve 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;
}
Ahora, podemos hacer un plan A * donde GridCells son los nodos, las Acciones son los bordes entre los nodos, y el Inicio y el Objetivo se expresan en términos de GridCells. La heurística entre dos GridCells es la distancia en x e y más la distancia angular en theta.
Siguiendo el camino
Ahora que tenemos una ruta en términos de GridCells y Actions entre ellas, podemos escribir un seguidor de ruta para el automóvil. Como las celdas de la cuadrícula son discretas, el automóvil saltaría entre las celdas. Entonces tendremos que suavizar el movimiento del automóvil a lo largo del camino. Si su juego usa un motor de física, esto se puede lograr escribiendo un controlador de dirección que intente mantener el automóvil lo más cerca posible del camino. De lo contrario, puede animar el camino utilizando curvas bezier o simplemente promediando los pocos puntos más cercanos en el camino.