想象一下类似汽车的运动,其中实体无法打开一角钱。为了便于讨论,请说在高速行驶时它们可以每秒旋转90度。在许多情况下,这将更改最佳路径,并因此更改寻路。它甚至可能使“常规”路径完全无法遍历。
是否有任何寻路算法或运动计划算法可以牢记这一点,或者是否有简单的方法来适应流行的算法?
想象一下类似汽车的运动,其中实体无法打开一角钱。为了便于讨论,请说在高速行驶时它们可以每秒旋转90度。在许多情况下,这将更改最佳路径,并因此更改寻路。它甚至可能使“常规”路径完全无法遍历。
是否有任何寻路算法或运动计划算法可以牢记这一点,或者是否有简单的方法来适应流行的算法?
Answers:
欢迎来到非完整运动计划的美好世界。我建议使用点阵网格路径规划器进行此操作。其他替代方案包括运动动力学RRT和轨迹优化。非完整系统包括汽车,轮船,单轮脚踏车,或什至是车辆无法沿任何方向行驶的任何事物。计划这些系统比完整系统要困难得多,直到2000年才成为学术研究的边缘。如今,有很多算法可供选择,以使其工作得体。
运作方式如下。
州
你的车的配置q实际上是包含汽车的x,y位置的3D状态和它的方向牛逼。A *算法中的节点实际上是3D向量。
class Node
{
// The position and orientation of the car.
float x, y, theta;
}
动作
那么边缘呢?
这有点困难,因为您的汽车实际上可以选择无数种方法来转动方向盘。因此,我们可以通过将汽车可以执行的动作数量限制为离散集合A,从而使点阵网格规划器可以访问此函数。为了简单起见,让我们假设汽车没有加速,而是可以瞬间改变其速度。在我们的情况下,A可以如下:
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;
}
现在,我们可以创建汽车可以随时执行的一系列离散操作。例如,在完全按下加油口0.5秒钟的同时,硬硬的右键将如下所示:
Action turnRight;
turnRight.speed = 1;
turnRight.wheelDirection = 1;
turnRight.dt = 0.5;
将汽车倒退并备份将如下所示:
Action reverse;
reverse.speed = -1;
reverse.wheelDirection = 0;
reverse.dt = 0.5;
您的操作列表如下所示:
List<Action> actions = { turnRight, turnLeft, goStraight, reverse ...}
您还需要一种方法来定义在节点上执行的操作如何导致新节点。这称为系统的前向动力学。
// 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);
}
离散网格单元
现在,要构造晶格网格,我们要做的就是将汽车的状态散列到离散的网格单元中。这会将它们变成离散的节点,后跟A *。这是非常重要的,因为否则A *将无法知道两个汽车状态是否实际上是相同的以便进行比较。通过散列为整数网格单元格值,这变得微不足道。
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;
}
现在,我们可以执行一个A *计划,其中GridCells是节点,Actions是节点之间的边缘,并且开始和目标均以GridCells表示。两个GridCell之间的启发式方法是x和y中的距离加上theta中的角距离。
遵循路径
现在我们已经有了关于GridCells和Actions的路径,我们可以为汽车编写路径跟随器了。由于网格单元是离散的,因此汽车会在单元之间跳跃。因此,我们将不得不平滑汽车沿路径的运动。如果您的游戏使用的是物理引擎,则可以通过编写一个转向控制器来尝试使汽车尽可能地靠近路径来实现。否则,您可以使用贝塞尔曲线或仅通过平均路径中最近的几个点来为路径设置动画。
大多数路径查找算法可在任意图形上工作,而不受几何图形的限制。
因此,您需要做的是将汽车的方向添加到每个探索的节点,并限制实际连接的节点。
我的想法,还没有检验!
您还应该能够执行此操作而不必先完成路径,所以:ergo:在A *期间处理转弯,可能会更好地进行优化,但是也可能会出现问题和故障,我真的不知道,但不幸的是,我没有时间自己测试。