如何计算加速度受限的对象的路径?


9

例如,假设我有一辆汽车,并且一辆汽车具有特定的最小转弯半径,并且我想将该汽车从a点驾驶到b点,但汽车没有面向b点。如何计算指向b点的路径?能够在b点处指定方向也是很好的(例如,您想开车到车道上然后驶入车库-如果您通过在草坪上行驶而进入车道并没有多大用处并面向侧面:)

指向文档(甚至只是名称)的指针就很好-我根本找不到任何东西。

在我的尝试中,它们在简单的情况下仍然有效,但在诸如b点比最小转弯半径更靠近a点的情况下却失败了。

例如,如何确定与此类似的路径(粗体路径):

仅出于说明目的的弯曲路径

编辑:在我的实际问题中,有一些简单的路径约束,但是我已经有一个可以运行的A *算法,但是它允许事物进行瞬时航向更改,因此看到汽车突然转90°看起来很傻当他们到达一个转折点时,一角钱。


gamedev.stackexchange.com/questions/86881/…但我不确定我是否了解如何设置3d空间的答案
xaxxon

“理想情况下,该算法将能够应对变化的速度”最小转弯半径是否随速度变化而根本相关,或者对于任何一辆汽车而言,恒定不变吗?
DMGregory

我将删除该部分。对于我正在做的事情,它比“格兰旅游”更像“模拟城市”。我了解您为什么要提出这个问题,而且我不确定添加新内容时的想法,因为我知道这无关紧要。
xaxxon

Bezier曲线图使我想起了另一个答案,这也与有限加速度的路径规划有关 –在这种情况下,加速度的建模类似于定向火箭推进器,而不是转弯半径,但它可能仍会激发一些有用的想法。
DMGregory

Answers:


7

我还没有解决所有的方程式,但是这里有一些视觉效果可以帮助我们解决这个问题。它归结为一些几何:

带有圆圈的汽车,指示其转弯半径。通过肯尼的汽车图标

从任何给定的起点和方向,我们都可以绘制两个具有最小转弯半径的圆-一个在左侧,一个在右侧。这些描述了通往我们道路的最紧要点。

我们可以对任何所需的最终位置和方向执行相同的操作。这些圆圈描述了我们道路的最可能的尽头。

现在,问题减少到找到一条路径,该路径将一个起始圆与一个终止圆连接,并沿切线相接。

(这是假设我们不需要在中间的障碍物上寻路,这在问题中没有提到。暴风城的答案进入了如何使用导航图信息解决这些类型的问题。一旦有了节点序列要通过,我们可以将以下方法应用于计划的每个部分。)

如果为简单起见,我们使用直线,则会得到以下内容:

该图显示了汽车可以采用的各种路径。

这给了我们极限的情况。通过这种方法找到路径后,您可以人为地增加一个或两个起点和终点的圆,以获得一条不太直接但更平滑的路径,直到两个圆吻在一起为止。

计算这些路径

让我们找出一个转向的案例-说我们从右转开始我们的道路。

我们右转圆的中心是:

startRightCenter = carStart.position + carStart.right * minRadius

我们称这条路径的直线部分的角度(从x轴正方向测量) pathAngle

如果我们从rightCenter离开转弯圆的点开始绘制矢量(在该点上我们必须面对pathAngle),则该矢量为...

startOffset = minRadius * (-cos(pathAngle), sin(pathAngle))

这意味着我们离开圆的点必须是...

departure = startRightCenter + startOffset

我们重新进入转弯圆的点取决于我们要以左转还是右转为终点:

// To end with a right turn:
reentry = endRightCenter + startOffset

// To end with a left turn: (crossover)
reentry = endLeftCenter - startOffset

现在,如果我们做我们的工作权,连线departurereentry应该垂直于startOffset

dot(reentry - departure,  startOffset) = 0

求解该方程式将为我们提供正确的角度。(我在这里使用复数,因为从技术上讲,有两个这样的角度,但是其中一个涉及反向驱动,这通常不是我们想要的)

让我们用右转右转案例代替:

dot(endRightCenter + startOffset - startRightCenter - startOffset, startOffset) = 0
dot(endRightCenter - startRightCenter, startOffset) = 0
pathAngle = atan2(endRightCenter - startRightCenter)

交叉的情况更加复杂-这是我还没有算完所有数学的情况。我暂不发布答案,以防在我确定其余详细信息时对您有用。

编辑:目标在最小转弯半径内

事实证明,即使目的地比我们的最小转弯距离更近,这种方法也经常开箱即用。再入圆中的至少一部分结束于转弯半径之外,只要我们不介意它有点像椒盐脆饼一样,就可以找到一条可行的路径...

在规划到一个接近目的地的路径时,展示了一些选项。

如果我们不喜欢这样走的路(或者如果不可行-我没有详尽检查每个案例-可能没有不可能的案例),我们总是可以向前或向后行驶,直到找到合适的道路如上图所示,在开始和结束圆圈之间接吻。


这是一种思考的好方法,圆上的切线很容易使用。到目前为止,我只收集了您的答案,但是我采用的每种方法都存在的一个问题是,目标是否位于起点的转弯圆内。
xaxxon

我知道要处理的最简单的策略是,将目标扭转到您的目标之一,然后再转向目标。使用目标方向时,您会倒转,直到开始和结束转弯的圆圈在某处接吻。我将添加一个图表以可视化这种情况。
DMGregory

2
一个月后(又分神),我开始工作了。我计算了4个切线-“外部”和“内部”(或“交叉”)切线。因此,将start.left_circle转到Goal.left_circle,将start.left_circle“穿越”到goal.right_circle(然后另两个仅切换圆圈)。这是“外部”路径:youtube.com/watch?
v=99e5Wm8OKb0

1

这在很大程度上取决于导航的其余数据模型。就是 您可以使用哪些数据,可以轻松添加哪些数据以及如何使用它们。

从水上交通系统中得出类似的假设,并假设

  • 你在游戏循环中
  • 你有一个节点路径系统
  • 您的汽车表现得像自主物体,利用自己的力量和转向“从内部”控制自己
  • 您的汽车不会像在铁轨上一样移动

您可能会遇到以下情况(请原谅我的图片幼稚外观)

在此处输入图片说明

(红色正方形是节点,红色线是节点的互连。假设您使用了寻路求解器,使节点1-9驶过;在图中看到的节点4-9想要通过绿线指示的节点通过,到达9号节点的车库;但是,您不想精确地走在绿线处,而是自然地留在右侧车道上并进行平稳的机动)。

每个节点将具有用于各种目的的例如保存半径或多个半径的元数据。其中一个是蓝色圆圈,它为汽车提供了瞄准指引

任何情况下,车辆都需要知道接下来的两个节点点 P(next)和P(next + 1)及其位置。当然,汽车也有位置。汽车对准P(下一个)蓝色元数据圆的右侧切线。朝相反方向行驶的汽车也是如此,因此它们不会发生碰撞。瞄准切线意味着汽车可以从任何方向接近圆弧并始终保持正确。这是一个粗略的基本原理,可以通过许多方式加以改进。

需要P(next + 1)来确定距离-当汽车到达P(next)或进入其元数据的某个半径范围内时,它可以根据P(next + 1)的距离来调整转向角。就是 如果距离很近,则转动很多;如果距离很远,则转动很小。显然,还需要其他规则和边缘条件,例如,根据P(next)和P(next + 1)的右切线计算汽车与帮助线之间的距离,并由此进行校正-将留在虚线(上图)和虚线(下图)上的意愿。

无论如何,当汽车经过一个节点时,它会忘记它并开始查看接下来的两个节点

对你的问题。显然,到达节点7时(在上图中,在下图中被视为节点2),它不能足够转动

在此处输入图片说明

一种可能的解决方案是始终构建一些帮助热线保持目标,然后让汽车按照其自身的物理设置移动(以指定的速率加速,倒退得更慢,考虑节点元数据的速度限制,以给定或计算的速度刹车G等)。如前所述,在这种情况下,汽车是自主的,自我描述的,自行携带的物体。

有绿色的帮助线1,2,3。当汽车到达洋红色圆圈时,它开始向右转。此时,您已经可以计算出它不会成功(您知道最大转弯速率,可以计算出曲线,并且可以看到它会同时越过帮助线2和3)。完全右转并使其前进(以物理增量为单位),并在到达帮助线3时使其减速(接近-使用阈值,f(与帮助线的距离)等)。当它帮助线3上时,进入倒车模式,将转向转向完全相反的方向。让它反向直到到达帮助线4(节点1和2之间的连接线-谷歌搜索“线算法的边”)。减速后,再次进入前进行驶模式,转动方向盘。重复直到路畅通无阻-显然这一次多了一次操作就足够了。

这是一般的想法:在游戏循环中,或在检查游戏任务系统时:

  • 根据当前的边缘限制和目标检查轿厢位置,速度,角度等,
  • 如果尚未达到,请继续执行您的操作(让物理学家移动它;汽车具有rpm和齿轮)。在您的查询系统中插入新的支票,例如发生0.1 s。
  • 如果达到,请计算新条件,设置数据并开始。插入一个新的检查以在que系统中发生,例如在0.1 s内完成。
  • 完成循环周期-继续,重复一次。

通过为节点和汽车提供足够的数据,将实现运动和连续性。

编辑:并添加:这自然需要进行微调。您的行为模拟可能需要不同的帮助热线,元数据,圆圈等。不过,这将给出一个可能的解决方案的想法。


我需要一点时间来阅读您的答案。我已经设置了通用的寻路并且可以正常工作,但是它允许对象在任何时候进行无限加速。
xaxxon

随机地,我实际上有一些与您所描述的非常接近的东西。紫色的“移动”线是完全按照以下两条直线从程序上生成的: youtube.com/watch?v=EyhBhrkmRiY, 但在“紧”情况下不起作用,并且该曲线未用于实际的寻路。
xaxxon

0

我最终按照DMGregory的建议进行了工作,并且效果很好。这是一些相关代码(尽管不是独立的),可用于计算两种切线样式。我确定这段代码效率不高,而且在所有情况下都可能不正确,但到目前为止对我来说仍然有效:

bool Circle::outer_tangent_to(const Circle & c2, LineSegment & shared_tangent) const {
    if (this->direction != c2.direction) {
        return false;
    }
    if (this->radius != c2.radius) {
        // how to add it: http://mathworld.wolfram.com/Circle-CircleTangents.html
        // just subtract smaller circle radius from larger circle radius and find path to center
        //  then add back in the rest of the larger circle radius
        throw ApbException("circles with different length radius not supported");
    }

    auto vector_to_c2 = c2.center - this->center;
    glm::vec2 normal_to_c2;
    if (this->direction == Circle::CW) {
        normal_to_c2 = glm::normalize(glm::vec2(-vector_to_c2.y, vector_to_c2.x));
    } else {
        normal_to_c2 = glm::normalize(glm::vec2(vector_to_c2.y, -vector_to_c2.x));
    }

    shared_tangent = LineSegment(this->center + (normal_to_c2 * this->radius),
                                 c2.center + (normal_to_c2 * this->radius));
    return true;
}


bool Circle::inner_tangent_to(const Circle & c2, LineSegment & tangent) const {

    if (this->radius != c2.radius) {
        // http://mathworld.wolfram.com/Circle-CircleTangents.html
        // adding this is non-trivial
        throw ApbException("inner_tangents doesn't support circles with different radiuses");
    }

    if (this->direction == c2.direction) {
        // inner tangents require opposing direction circles
        return false;
    }

    auto vector_to_c2 = c2.center - this->center;
    auto distance_between_circles = glm::length(vector_to_c2);

    if ( distance_between_circles < 2 * this->radius) {
//      throw ApbException("Circles are too close and don't have inner tangents");
        return false;
    } else {
        auto normalized_to_c2 = glm::normalize(vector_to_c2);
        auto distance_to_midpoint = glm::length(vector_to_c2) / 2;
        auto midpoint = this->center + (vector_to_c2 / 2.0f);

        // if hypotenuse is oo then cos_angle = 0 and angle = 90˚
        // if hypotenuse is radius then cos_angle = r/r = 1 and angle = 0
        auto cos_angle = radius / distance_to_midpoint;
        auto angle = acosf(cos_angle);

        // now find the angle between the circles
        auto midpoint_angle = glm::orientedAngle(glm::vec2(1, 0), normalized_to_c2);

        glm::vec2 p1;
        if (this->direction == Circle::CW) {
            p1 = this->center + (glm::vec2{cos(midpoint_angle + angle), sin(midpoint_angle + angle)} * this->radius);
        } else {
            p1 = this->center + (glm::vec2{cos(midpoint_angle - angle), sin(midpoint_angle - angle)} * this->radius);
        }

        auto tangent_to_midpoint = midpoint - p1;
        auto p2 = p1 + (2.0f * tangent_to_midpoint);
        tangent = {p1, p2};

        return true;
    }
};

这是上面运行中的代码的两部影片:

这是“外部”路径:http: //youtube.com/watch?v=99e5Wm8OKb0,这是“交叉”路径:http: //youtube.com/watch?v=iEMt8mBheZU

如果此代码有帮助,但是您对此处未显示的某些部分有疑问,请发表评论,我应该在一两天内看到它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.