가속이 제한된 물체의 경로는 어떻게 계산합니까?


9

예를 들어, 차가 있고 차에 특정 최소 회전 반경이 있고 차를 지점 a에서 지점 b로 운전하고 싶지만 차량이 지점 b를 향하지 않는다고 가정합니다. b 지점까지의 경로를 어떻게 계산합니까? b 지점에서 방향을 지정할 수 있으면 좋을 것입니다 (차도로 운전 한 다음 차고로 가져 오기를 원함). 옆으로 향하고 있습니다 :)

문서 (또는 이름조차도)에 대한 포인터는 완벽하게 괜찮을 것입니다. 아무것도 찾지 못했습니다.

내 시도에서는 간단한 경우에 작동하지만 지점 b가 최소 회전 반경보다 가까운 경우와 같이 비참하게 실패합니다.

예를 들어, 이와 유사한 경로 (굵은 체 경로)를 어떻게 결정 하시겠습니까?

설명을위한 곡선 형 경로

편집 : 내 실제 문제에는 간단한 경로 제한이 있지만 이미 작동하는 A * 알고리즘이 있지만 순간적으로 제목을 변경할 수 있으므로 갑자기 자동차가 90도 회전하는 것을 보는 것이 어리석은 것처럼 보입니다. 그들이 전환점에 도달하면 한푼에.


gamedev.stackexchange.com/questions/86881/… 그러나 3D 공간 설정 방법에 대한 답변을 잘 모르겠습니다
xaxxon

"이상적으로이 알고리즘은 속도 변화를 처리 할 수있다"최소 회전 반경은 속도 변화에 따른 속도와 관련이 있습니까, 아니면 어느 한 차량에 대해 일정합니까?
DMGregory

그 부분을 삭제하겠습니다. 내가하고있는 일에 대해서는 "그란 관광"보다 "심시티"가 더 많습니다. 나는 왜 당신이 그것을 묻는 지 이해하고 그것을 추가 할 때 내가 무엇을 생각하고 있는지 잘 모르겠습니다.
xaxxon

베 지어 곡선 다이어그램은 이 가속도를 제한하는 경로 계획과 관련된 또 다른 해답을 상기 시켰습니다. 경우 가속은 회전 반경이 아닌 방향성 로켓 추진기처럼 모델링되었지만 여전히 유용한 아이디어를 촉발 할 수 있습니다.
DMGregory

답변:


7

나는 이것에 대한 완전한 방정식을 아직 다루지 않았지만, 여기에 문제를 둘러싼 머리를 감싸는 데 도움이되는 시각 자료가 있습니다. 그것은 어떤 기하학으로 요약됩니다 :

회전 반경을 나타내는 원이있는 자동차. ( Kenney를 통한 자동차 아이콘 )

주어진 시작점과 방향에서 최소 회전 반경 (왼쪽, 오른쪽)으로 두 개의 원을 그릴 수 있습니다. 이것들은 우리의 길을 향한 가장 촉박 한 시작점을 설명합니다.

원하는 끝 위치와 방향에 대해 동일한 작업을 수행 할 수 있습니다. 이 원들은 우리의 길로가는 가장 끝을 묘사합니다.

이제 문제는 시작 원 중 하나를 끝 원 중 하나에 결합하여 각 접선을 따라 키스하는 경로를 찾는 것입니다.

(이것은 질문에서 언급되지 않은 사이에 장애물을 찾을 필요가 없다고 가정합니다. Stormwind의 답변은 이러한 유형의 문제에 대한 탐색 그래프 정보를 사용하는 방법에 대해 알 수 있습니다. 통과하기 위해 아래의 방법을 계획의 각 세그먼트에 적용 할 수 있습니다.)

단순성을 위해 직선을 사용하면 다음과 같은 결과가 나타납니다.

자동차가 취할 수있는 다양한 경로를 보여주는 다이어그램.

이것은 우리에게 제한적인 경우를 제공합니다. 이 방법으로 경로를 찾으면 시작 또는 끝 원 중 하나 또는 둘 모두를 인위적으로 부풀려서 두 원이 키스하는 지점까지 덜 직접적이지만 부드러운 경로를 얻을 수 있습니다.

이러한 경로 계산

한 번의 회전 방향에 대한 사례를 해결합시다. 우회전하면 경로가 시작됩니다.

우회전의 중심은 다음과 같습니다.

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 to goal.left_circle, start.left_circle "crossing"to goal.right_circle (그리고 다른 두 개는 원을 전환). 여기에 "외부"경로가 있습니다 : youtube.com/watch?v=99e5Wm8OKb0 여기에 "교차"경로가 있습니다 : youtube.com/watch?v=iEMt8mBheZU
xaxxon

1

이는 탐색을위한 나머지 데이터 모델에 따라 다릅니다. 즉. 편리한 데이터, 쉽게 추가 할 수있는 데이터 및 소비 방법

수중 교통 시스템에서 유사한 시나리오를 수행하고

  • 당신은 게임 루프에 있습니다
  • 노드 경로 시스템이 있습니다
  • 자동차는 자신의 힘과 조향을 사용하여 "내부에서"스스로 제어하는 ​​자율적 인 물체처럼 행동합니다
  • 당신의 차는 레일처럼 움직이지 않습니다

당신은 아래와 같은 것을 가질 수 있습니다 (사진의 유치한 모습을 용서해주십시오)

여기에 이미지 설명을 입력하십시오

(빨간색 사각형은 노드이고, 빨간색 선은 노드 상호 연결입니다. 노드 1-9에 드라이브를 제공하는 경로 찾기 솔버를 사용했다고 가정하고, 노드 4-9는 그림에 표시되어 있으며 녹색 선으로 표시된 노드를 통과하려고합니다 , 노드 # 9의 차고에; 그러나 녹색 선으로 정확하게 가고 싶지 않고 대신 오른쪽 차선에 자연스럽게 머물면서 부드럽게 움직입니다.

각 노드에는 다양한 목적을 위해 예를 들어 반경 또는 여러 노드 를 보유하는 메타 데이터 가 있습니다. 그중 하나는 파란색 원으로, 차량에 대한 조준 안내를 제공합니다 .

에서 어떤 경우 , 차량 요구하는 다음 두 개의 노드 점을 인식 (다음) P와 P (다음 + 1), 그리고 자신의 위치. 당연히 차도 위치가 있습니다. 자동차는 P (next)의 파란색 메타 데이터 원의 오른쪽 접선 을 목표로합니다 . 따라서 자동차는 반대 방향으로 가고 있으므로 충돌하지 않습니다. 접선을 목표로하면 자동차는 어느 방향에서든 원에 접근 할 수 있으며 항상 올바르게 유지합니다. 이것은 여러 가지 방법으로 개선 될 수있는 대략적인 기본 원리입니다.

P (next + 1)은 거리를 결정하는 데 필요합니다. 자동차가 P (next)에 도달하거나 메타 데이터의 반경 안에 들어가면 P (next + 1)의 거리 에 따라 조향각을 조정할 수 있습니다 . 즉. 가까이 있으면 많이 돌리고 멀리 있으면 조금 돌리십시오. P (next)와 P (next + 1)의 오른쪽 접선을 기준으로 자동차와 헬프 라인 사이의 거리를 계산하고 그에 의한 수정과 같은 다른 규칙 및 에지 조건도 있어야합니다. -점선 (그림 위)과 점선 (그림 아래) 선을 유지하려는 의지.

차로서 어떠한 경우에도, 하나 개의 노드를 통과 , 그것은 그것을 잊고 다음 두 사람을보고 시작합니다 .

당신의 질문에. 분명히, 노드 7에 도달하면 (위 그림에서 아래 그림에서 노드 2로 표시) 충분히 회전 할 수 없습니다 .

여기에 이미지 설명을 입력하십시오

한 가지 가능한 솔루션이하기 도움이 라인을 구축 하고 목표 모든 시간을 유지 한 다음 자신의 물리 설정에 따라 차량 이동 (지정된 속도로 가속이 역 느리게, 고려에서 브레이크를 노드 메타 데이터 speedlimits 가지고 주어진 또는 계산 G 등). 말했듯이,이 시나리오에서 자동차는 자율적이며 자기 묘사 적이며 자기 운반 대상입니다.

녹색 도움말 줄 1,2,3을 갖습니다 . 차가 자홍색 원에 도달하면 오른쪽으로 회전합니다. 이 시점에서 이미 성공하지 못했음을 계산할 수 있습니다 (최대 회전 속도를 알고 곡선을 계산할 수 있으며 헬프 라인 2와 3을 모두 통과 함을 알 수 있습니다). 스티어링을 오른쪽으로 돌리고 (물리적으로 증가하여) 전방으로 주행하고 도움말 라인 3에 도달하면 속도를 늦 춥니 다 (사용 임계 값, f (dist to helpline) 등). 이 경우 도움을 줄 3으로 이동 , 모드로 스티어링을 돌려 완전 반대 . 도움말 라인 4에 도달 할 때까지 되돌립니다.(노드 1과 2 사이의 연결선- "라인 알고리즘의 포인트"에 대한 Google). 속도에 도달하면 속도를 늦추고 다시 주행 모드 로 들어가서 휠을 돌리십시오. 도로가 깨끗해질 때까지 반복하십시오. 이번에는 추가로 1 개의 운동으로 충분했습니다.

이것은 일반적인 아이디어입니다 : 게임 루프 도중 또는 게임 작업 대기열 시스템을 확인할 때 :

  • 에 차량의 위치, 속도, 각도 등을 확인 현재 에지 한계 및 목표 ,
  • 경우 에 아직 도달하지 , 당신이 무엇을하고 있는지를 계속 (하자 물리학으로 이동, 차량이 회전하고 기어가 있습니다). 예를 들어 0.1 초에 발생하도록 que 시스템에 새로운 검사를 삽입하십시오.
  • 도달 하면 새 조건을 계산하고 데이터를 설정 한 후 시작하십시오 . 예를 들어 0.1 초 내에 que 시스템에서 수행 할 새 검사를 삽입하십시오.
  • 루프주기를 완료하십시오. 계속하고 반복하십시오.

노드와 자동차에 충분한 데이터를 제공함으로써 움직임과 지속이있을 것입니다.

편집 : 추가 : 자연스럽게 미세 조정이 필요합니다. 시뮬레이션 동작에는 다른 도움말 라인, 메타 데이터, 서클 등이 필요할 수 있습니다. 이것은 하나의 가능한 해결책에 대한 아이디어를 줄 것입니다.


답을 읽으려면 약간의 시간이 걸립니다. 일반 경로 찾기가 이미 설정되어 작동하지만 개체가 언제든지 가속 할 수 있습니다.
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

이 코드가 도움이되었지만 여기에 표시되지 않은 일부 부분에 대해 궁금한 점이 있으면 의견을 게시하면 하루나 이틀 안에 내용을 볼 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.