개체가 한 푼도 켤 수없는 자동차와 같은 움직임을 상상해보십시오. 토론을 위해 속도가 빠르면 초당 90도 회전 할 수 있다고 가정 해보십시오. 이것은 대부분의 경우 최적의 경로와 경로 찾기를 변경합니다. 심지어 '통상적 인'경로를 통과하는 것이 완전히 불가능해질 수도 있습니다.
이것을 명심할 수있는 길 찾기 알고리즘이나 움직임 계획 알고리즘이 있습니까?
개체가 한 푼도 켤 수없는 자동차와 같은 움직임을 상상해보십시오. 토론을 위해 속도가 빠르면 초당 90도 회전 할 수 있다고 가정 해보십시오. 이것은 대부분의 경우 최적의 경로와 경로 찾기를 변경합니다. 심지어 '통상적 인'경로를 통과하는 것이 완전히 불가능해질 수도 있습니다.
이것을 명심할 수있는 길 찾기 알고리즘이나 움직임 계획 알고리즘이 있습니까?
답변:
홀로 노믹 모션 계획 의 놀라운 세계에 오신 것을 환영합니다 . 격자 그리드 경로 플래너를 사용하여이 작업을 수행하는 것이 좋습니다 . 다른 대안으로는 키노 다이내믹 RRT 및 궤적 최적화가 있습니다. 비 홀로 노믹 시스템에는 자동차, 보트, 외발 자전거 또는 차량이 원하는 방향으로 이동할 수없는 모든 것이 포함됩니다. 이러한 시스템에 대한 계획은 홀로 노믹 시스템보다 훨씬 어렵고 ~ 2000 년까지 학술 연구의 가장자리에 있습니다. 요즘에는 어떤 알고리즘을 적절하게 선택할지 선택할 수 있습니다.
작동 방식은 다음과 같습니다.
상태
자동차의 구성 q 는 실제로 자동차의 x, y 위치 및 방향 t를 포함하는 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;
}
이제 GridCells가 노드이고 Actions가 노드 사이의 가장자리이며 Start와 Goal이 GridCells로 표시되는 A * 계획을 수행 할 수 있습니다. 두 GridCell 사이의 휴리스틱은 x와 y의 거리에 세타의 각도 거리를 더한 값입니다.
경로를 따라
이제 GridCell과 Actions 사이에 경로가 있으므로 자동차의 경로 추종자를 작성할 수 있습니다. 그리드 셀이 분리되어 있기 때문에 자동차는 셀 사이를 뛰어 넘을 것입니다. 따라서 경로를 따라 자동차의 움직임을 부드럽게해야합니다. 게임에서 물리 엔진을 사용하는 경우 가능한 한 차를 경로에 가깝게 유지하려는 조향 컨트롤러를 작성하면됩니다. 그렇지 않으면 베 지어 곡선을 사용하거나 경로에서 가장 가까운 몇 점을 평균화하여 경로에 애니메이션을 적용 할 수 있습니다.
대부분의 경로 찾기 알고리즘은 형상의 제한없이 임의의 그래프에서 작동합니다.
따라서 각 탐색 노드에 자동차의 방향을 추가하고 실제로 연결된 노드를 제한해야합니다.
내 생각은 그들을 테스트하지 않았습니다!
당신은 또한 경로를 먼저 완료하지 않고도 이것을 할 수 있어야합니다. 직접 테스트 할 시간이 없습니다.
상담원이 자동차를 완전히 제어 할 수 있으면 다른 방법으로 차를 제어하십시오. 선을 처음부터 끝까지 연결 한 다음 Dennis의 답변과 유사하게 각 턴을 탐색 할 수있는 속도를 파악하십시오.
고정 점에서 베 지어 곡선을 그리지 마십시오. 속도 손실을 최소화하려면 전체 라인을 움직여야합니다. 따라서 더 먼 거리에 여분의 노드를 삽입 한 다음 에너지 최소화 또는 유사한 전략을 위해 이동하십시오. 자세한 내용은 (가급적 sim 또는 semi-sim) 레이싱 게임에서 AI 라인 생성을 조사해야합니다.
AI 라인 시스템을 실행 한 후에는 A * 검색을 실행하고 각 경로에 대해 최소한 한 모서리 앞으로 이동 한 다음 AI 라인을 계산하여 시간을 예측하십시오. 이것이 비용 함수일 것입니다.