์์ ๊ฐ๋
์๋ก์ด ๊ฐ๋
์ธ Reciprocal Velocity Obstacle(์ดํ RVO)๋ฅผ ์ ์ํจ
๊ฐ ์์ด์ ํธ๊ฐ ๋ค๋ฅธ ์์ด์ ํธ์ ๋ช
์์ ์ผ๋ก ํต์ ํ์ง ์๊ณ ๋
๋ฆฝ์ ์ผ๋ก ์์ง์ด๋ ๊ฒฝ์ฐ๋ฅผ ๊ณ ๋ คํจ
๋ค๋ฅธ ๊ฐ์ฒด๋ค์ด ์ ์ฌํ ์ถฉ๋ ํํผ ์ถ๋ก ์ ์๋ฌต์ ์ผ๋ก ๊ฐ์ ํจ์ผ๋ก์จ ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ฐ์์ ํ๋์ ๊ณ ๋ คํจ
์ ์ ์ฅ์ ๋ฌผ๊ณผ ์ด๋ ์ฅ์ ๋ฌผ์ ๋ชจ๋ ํฌํจํ๋ ์ธ๊ตฌ ๋ฐ๋๊ฐ ๋์ ํ๊ฒฝ์์ ์๋ฐฑ ๊ฐ์ ์์ด์ ํธ๋ฅผ ํ์ํ๋ ๊ฒ์ ๊ฐ๋
์ ์ ์ฉํ๊ณ , ๋์ ์ ์ธ ์๋๋ฆฌ์ค์์ ์ค์๊ฐ ๋ฐ ํ์ฅ ๊ฐ๋ฅํ ์ฑ๋ฅ์ ๋ฌ์ฑํ๋ค๋ ๊ฒ์ ๋ณด์ฌ์ค
์ฌ์ฉํ๋ ORCA ๊ฐ๋
์ ๊ฒฝ์ฐ ๋ก๋ณดํฑ์ค์์ ๋ก๋ด๊ฐ์ ์ถฉ๋์ ๋ง๊ธฐ ์ํด์๋ ์ฐ์
์์ ํ๊ณ ์ ํ ๊ฒ
Velocity Obstacle(VO)์์ ๋ฐ์ํ๋ ์ง๋ํ์์ ์์ ํ๊ณ ์ ์ ์ํจ
ํต์ฌ ๋ด์ฉ
์์ง์ด๋ ์ฅ์ ๋ฌผ๋ค ์ฌ์ด์์์ ์์ฐ์ค๋ฌ์ด ์์ง์์ ์ํด ๋์ ๋ VO(Velocity Obstacle) ๊ฐ๋ ์ ํ์ฅ
ORCA(Optimal Reciprocal Collision Avoidance)๋ผ๋ ๊ฐ๋ ์ ๋์ ํ์ฌ ๊ฐ ๊ฐ์ฒด๊ฐ์ ์๋๋ฅผ ๊ณ์ฐํ ํ ๊ทธ ์ฑ ์์ ๊ฐ๊ฐ ๋ฐํ์ฉ ๊ฐ์ง๊ฒ ๋ง๋ค์ด์ ๊ตฐ์ค์ ๋ญ์น๋ ํ์์ ์ค์ด๋ค๋๋ก ๋ง๋๋ ๊ฒ
๊ฒฐ๊ตญ ๋ณ๋ชฉ ํ์(Bottleneck)์ ๊ฐ์ ํ๊ธฐ ์ํด์ ์ ์
- ๋ฏผ์ฝ์ฐ์คํค ํฉ (๋ฏผ์ฝํ์คํค ๋ง์ )
๊ธฐํํ์์, ์ ํด๋ฆฌ๋ ๊ณต๊ฐ์ ์์น๋ฒกํฐ A์ B์ ๋ ์งํฉ์ ๋ฏผ์ฝ์คํค ํฉ(ํฝ์ฐฝ)์ A์ ์๋ ๋ชจ๋ ๋ฒกํฐ๋ฅผ B์ ์๋ ๊ฐ๊ฐ์ ๋ฒกํฐ์ ๋ํด์ ๋ง๋ค์ด์ง๋ค.
๋ฏผ์ฝํ์คํค ๋ง์
์ ์ฅ์ ๋ฌผ ์ฌ์ด๋ก ์ง๋๋ ๋ชจ์
๊ณํ์ ์ฌ์ฉ๋๋ค.
์ถ๊ฐ ์ฐธ๊ณ ์๋ฃ
์ค์ ์๋ RVO2 Library
๐กRVOSimulator-> doStep()
doStep์ ์คํํ๋ฉด ํํ๋ ์์ด ์งํ๋๋ค.
void RVOSimulator::doStep(float powfScalar)
{
kdTree_->buildAgentTree();
this->powfScalar = powfScalar;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
agents_[i]->computeNeighbors();
agents_[i]->computeNewVelocity();
//agents_[i]->computeNewVelocityE();
}
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
agents_[i]->update();
}
globalTime_ += timeStep_;
}
๋จผ์ kdTree์ ๋ํ ์ ๋ณด๋ค์ ๋ค์ ์์ฑํ๋ค.
void KdTree::buildAgentTree()
{
if (agents_.size() < sim_->agents_.size()) {
for (size_t i = agents_.size(); i < sim_->agents_.size(); ++i) {
agents_.push_back(sim_->agents_[i]);
}
agentTree_.resize(2 * agents_.size() - 1);
}
if (!agents_.empty()) {
buildAgentTreeRecursive(0, agents_.size(), 0);
}
}
ํ์ฌ agent์ ์์ ์๋ฎฌ๋ ์ด์ ์์ ๊ฐ์ง๊ณ ์๋ agent์ ์๊ฐ ๋ณ๊ฒฝ๋ ๋ (์ฆ, ์ถ๊ฐ๋ ๋) ์๋ก์ด agent๋ฅผ vector์ ์ถ๊ฐํ๋ค.
๊ทธ๋ฆฌ๊ณ Agent kd-tree๋ฅผ ์์ฑํ๋ค.
void KdTree::buildAgentTreeRecursive(size_t begin, size_t end, size_t node)
{
agentTree_[node].begin = begin;
agentTree_[node].end = end;
agentTree_[node].minX = agentTree_[node].maxX = agents_[begin]->position_.x();
agentTree_[node].minY = agentTree_[node].maxY = agents_[begin]->position_.y();
for (size_t i = begin + 1; i < end; ++i) {
agentTree_[node].maxX = std::max(agentTree_[node].maxX, agents_[i]->position_.x());
agentTree_[node].minX = std::min(agentTree_[node].minX, agents_[i]->position_.x());
agentTree_[node].maxY = std::max(agentTree_[node].maxY, agents_[i]->position_.y());
agentTree_[node].minY = std::min(agentTree_[node].minY, agents_[i]->position_.y());
}
if (end - begin > MAX_LEAF_SIZE) {
/* No leaf node. (leaf node : ๋ถ๋ชจ๊ฐ ์๋ ์ต์์ ๋
ธ๋) */
const bool isVertical = (agentTree_[node].maxX - agentTree_[node].minX > agentTree_[node].maxY - agentTree_[node].minY);
const float splitValue = (isVertical ? 0.5f * (agentTree_[node].maxX + agentTree_[node].minX) : 0.5f * (agentTree_[node].maxY + agentTree_[node].minY));
size_t left = begin;
size_t right = end;
while (left < right) {
while (left < right && (isVertical ? agents_[left]->position_.x() : agents_[left]->position_.y()) < splitValue) {
++left;
}
while (right > left && (isVertical ? agents_[right - 1]->position_.x() : agents_[right - 1]->position_.y()) >= splitValue) {
--right;
}
if (left < right) {
std::swap(agents_[left], agents_[right - 1]);
++left;
--right;
}
}
if (left == begin) {
++left;
++right;
}
agentTree_[node].left = node + 1;
agentTree_[node].right = node + 2 * (left - begin);
buildAgentTreeRecursive(begin, left, agentTree_[node].left);
buildAgentTreeRecursive(left, end, agentTree_[node].right);
}
}
๐กAgent -> computeNeighbors()
void Agent::computeNeighbors()
{
obstacleNeighbors_.clear();
float rangeSq = sqr(timeHorizonObst_ * maxSpeed_ + radius_);
sim_->kdTree_->computeObstacleNeighbors(this, rangeSq);
agentNeighbors_.clear();
if (maxNeighbors_ > 0) {
rangeSq = sqr(neighborDist_);
sim_->kdTree_->computeAgentNeighbors(this, rangeSq);
}
}
๋จผ์ Agent์ ์ด์๋ค(๋๋ ๊ฐ์ด ์์ง์ด๊ณ ์๋ค๊ณ ๊ฐ์ ํ๋ Agent๋ค์ ๋ํด ๊ณ์ฐ์ ํ๋ค.
rangeSq๋ ์๋์ timeHorizonObst(์ต์ )์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ก ๋ง๋ sqr(๋ฃจํธ)๋ฅผ ์ ์ฉํ ๋ฐ์ง๋ฆ์ด๋ค.
ํด๋น ๋ณ์๋ฅผ kdTree->computeAgentNeighbors() ํจ์๋ก ๋๊ฒจ์ค๋ค.
์ด์ค์ RVO๋ Nearest Neighbor ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋๋ฏํ๋ค.
computeObstacleNeighbors๋ ์์ง์ด์ง ์๋ ๋ฐฉํด๋ฌผ์ ๊ณ์ฐํ ๋ ์ฌ์ฉํ๋ ค๊ณ ๊ณ์ฐํ๋ค.
๐กk-d Tree
k์ฐจ์ ๊ณต๊ฐ์ ์ ๋ค์ ๊ตฌ์กฐํํ๋ ๊ณต๊ฐ ๋ถํ ์๋ฃ ๊ตฌ์กฐ
๋ณดํต ๋ค์ฐจ์ ๊ณต๊ฐ์์ ํ์ํ ๋ ์ฌ์ฉ๋๋ ๊ธฐ๋ฒ
Range Search, Nearest neighbor ๋ฑ์ ๋ฌธ์ ์ ์ ์ฉ
๐กkNN
์ฃผ์ด์ง Point์ ๊ฐ์ฅ ๊ฐ๊น์ด(์ฃผ์ด์ง set์์) ์ ์ ์ฐพ๋ ๊ฒ
1. k ์ด์์ ๊ธฐ์ค์ ์ ์
2. test ์ ์ ๊ฐ๋ณ ํฌ์ธํธ์ ๋ํด์ k ๊ฐ์ ์ด์์ ์ฐพ์
3. ์ด์์ output์ ๊ฐ์ง๊ณ ์์ธก๊ฐ์ ์์ฑ
void KdTree::computeAgentNeighbors(Agent *agent, float &rangeSq) const
{
queryAgentTreeRecursive(agent, rangeSq, 0);
}
kd-tree ๋ด์์ ์์น๋ฅผ ์ฐพ์์ insertํด์ค๋ค.
์ฐพ์ agentTree๊ฐ 10๊ฐ ์ดํ๋ฉด ๊ทธ๋๋ก ์ฝ์ ํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด kd-tree ๋ด์์ ์์น๋ฅผ ๋ค์ ์ ํํ๊ฒ ํ์ํ๋ค.
kNN์์ ์ฃผ์ด์ง set์ด 10๊ฐ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
void KdTree::queryAgentTreeRecursive(Agent *agent, float &rangeSq, size_t node) const
{
//agentTree์ ํฌ๊ธฐ๊ฐ 10๋ณด๋ค ์๋ค๋ฉด agent์ ์ด์์ ํด๋น agent๋ฅผ insert
if (agentTree_[node].end - agentTree_[node].begin <= MAX_LEAF_SIZE) {
for (size_t i = agentTree_[node].begin; i < agentTree_[node].end; ++i) {
agent->insertAgentNeighbor(agents_[i], rangeSq);
}
}
else {
const float distSqLeft = sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minX - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].left].maxX)) + sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minY - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].left].maxY));
const float distSqRight = sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minX - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].right].maxX)) + sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minY - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].right].maxY));
if (distSqLeft < distSqRight) {
if (distSqLeft < rangeSq) {
queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
if (distSqRight < rangeSq) {
queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
}
}
}
else {
if (distSqRight < rangeSq) {
queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
if (distSqLeft < rangeSq) {
queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
}
}
}
}
}
๐กAgent -> computeNewVelocity()
์ฃผ๋ณ ์ด์์ ๊ฒฐ์ ํ์์ผ๋ฉด ์๋ก์ด ์๋๋ฅผ ํ์ํ๋ค.
์๋ก์ด ์๋๋ฅผ ์ํด ORCA ๋ผ์ธ์ ์๋กญ๊ฒ ๋ง๋ ๋ค.
ํ๋ ฌ์์ ์ ๋๊ฐ์ ๋ํ์ ๋ฉด์ ์ ์๋ฏธํ๋ค.
const Obstacle *obstacle1 = obstacleNeighbors_[i].second;
const Obstacle *obstacle2 = obstacle1->nextObstacle_;
const Vector2 relativePosition1 = obstacle1->point_ - position_;
const Vector2 relativePosition2 = obstacle2->point_ - position_;
/*
* Check if velocity obstacle of obstacle is already taken care of by
* previously constructed obstacle ORCA lines.
* ์ฅ์ ๋ฌผ์ ์๋ ์ฅ์ ๋ฌผ์ด ์ด์ ์ ๊ตฌ์ถ๋ ์ฅ์ ๋ฌผ ORCA ๋ผ์ธ์ ์ํด
* ์ด๋ฏธ ์ฒ๋ฆฌ๋์๋์ง ํ์ธํฉ๋๋ค.
*/
bool alreadyCovered = false;
for (size_t j = 0; j < orcaLines_.size(); ++j) {
//det : ํ๋ ฌ์, radius_ : agent์ ๋ฐ์ง๋ฆ
if (det(invTimeHorizonObst * relativePosition1 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVO_EPSILON
&& det(invTimeHorizonObst * relativePosition2 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVO_EPSILON) {
alreadyCovered = true;
break;
}
}
if (alreadyCovered) {
continue;
}
๋จผ์ ๊ตฌ์ถ๋ ์ฅ์ ๋ฌผ ORCA ๋ผ์ธ์ ์ํด ์ด๋ฏธ ์ฒ๋ฆฌ๋์๋์ง ํ์ธํ๊ณ , ์ฒ๋ฆฌ ๋์๋ค๋ฉด ์๋ตํ๋ค.
๊ทธ๋ ์ง ์๋ค๋ฉด ์ฅ์ ๋ฌผ ORCA ๋ผ์ธ์ ๋ง๋ ๋ค.
/* Create obstacle ORCA lines. */
for (size_t i = 0; i < obstacleNeighbors_.size(); ++i) {
/* Not yet covered. Check for collisions. */
const float distSq1 = absSq(relativePosition1);
const float distSq2 = absSq(relativePosition2);
const float radiusSq = sqr(radius_);
const Vector2 obstacleVector = obstacle2->point_ - obstacle1->point_;
const float s = (-relativePosition1 * obstacleVector) / absSq(obstacleVector);
const float distSqLine = absSq(-relativePosition1 - s * obstacleVector);
Line line;
if (s < 0.0f && distSq1 <= radiusSq) {
/* Collision with left vertex. Ignore if non-convex. */
if (obstacle1->isConvex_) {
line.point = Vector2(0.0f, 0.0f);
line.direction = normalize(Vector2(-relativePosition1.y(), relativePosition1.x()));
orcaLines_.push_back(line);
}
continue;
}
else if (s > 1.0f && distSq2 <= radiusSq) {
/* Collision with right vertex. Ignore if non-convex
* or if it will be taken care of by neighoring obstace */
if (obstacle2->isConvex_ && det(relativePosition2, obstacle2->unitDir_) >= 0.0f) {
line.point = Vector2(0.0f, 0.0f);
line.direction = normalize(Vector2(-relativePosition2.y(), relativePosition2.x()));
orcaLines_.push_back(line);
}
continue;
}
else if (s >= 0.0f && s < 1.0f && distSqLine <= radiusSq) {
/* Collision with obstacle segment. */
line.point = Vector2(0.0f, 0.0f);
line.direction = -obstacle1->unitDir_;
orcaLines_.push_back(line);
continue;
}
//์๋ ์ฝ๋๋ค ์คํ
//...
/*
* No collision.
* Compute legs. When obliquely viewed, both legs can come from a single
* vertex. Legs extend cut-off line when nonconvex vertex.
* ์ถฉ๋์ ์์ต๋๋ค.
* leg๋ฅผ ๊ณ์ฐํ์ธ์. ๋น์ค๋ฌํ ๋ณผ ๋, ๋ leg๋ ํ๋์ ๊ผญ์ง์ ์์ ๋์ฌ ์ ์์ต๋๋ค.
* leg๋ ๋ณผ๋กํ์ง ์์ ๊ผญ์ง์ ์์ ์ ๋จ์ ์ ์ฐ์ฅํฉ๋๋ค.
*/
Vector2 leftLegDirection, rightLegDirection;
if (s < 0.0f && distSqLine <= radiusSq) {
/*
* Obstacle viewed obliquely so that left vertex
* defines velocity obstacle.
* ์ผ์ชฝ ๊ผญ์ง์ ์ด ์๋ ์ฅ์ ๋ฌผ์ ์ ์ํ๋๋ก ๋น์ค๋ฌํ ๋ณด์ด๋ ์ฅ์ ๋ฌผ.
*/
if (!obstacle1->isConvex_) {
/* Ignore obstacle. */
continue;
}
obstacle2 = obstacle1;
const float leg1 = std::sqrt(distSq1 - radiusSq);
leftLegDirection = Vector2(relativePosition1.x() * leg1 - relativePosition1.y() * radius_, relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
rightLegDirection = Vector2(relativePosition1.x() * leg1 + relativePosition1.y() * radius_, -relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
}
else if (s > 1.0f && distSqLine <= radiusSq) {
/*
* Obstacle viewed obliquely so that
* right vertex defines velocity obstacle.
* ์ค๋ฅธ์ชฝ ๊ผญ์ง์ ์ด ์๋ ์ฅ์ ๋ฌผ์ ์ ์ํ๋๋ก ๋น์ค๋ฌํ ๋ณด์ด๋ ์ฅ์ ๋ฌผ.
*/
if (!obstacle2->isConvex_) {
/* Ignore obstacle. */
continue;
}
obstacle1 = obstacle2;
const float leg2 = std::sqrt(distSq2 - radiusSq);
leftLegDirection = Vector2(relativePosition2.x() * leg2 - relativePosition2.y() * radius_, relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
rightLegDirection = Vector2(relativePosition2.x() * leg2 + relativePosition2.y() * radius_, -relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
}
else {
/* Usual situation. */
if (obstacle1->isConvex_) {
const float leg1 = std::sqrt(distSq1 - radiusSq);
leftLegDirection = Vector2(relativePosition1.x() * leg1 - relativePosition1.y() * radius_, relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
}
else {
/* Left vertex non-convex; left leg extends cut-off line. */
leftLegDirection = -obstacle1->unitDir_;
}
if (obstacle2->isConvex_) {
const float leg2 = std::sqrt(distSq2 - radiusSq);
rightLegDirection = Vector2(relativePosition2.x() * leg2 + relativePosition2.y() * radius_, -relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
}
else {
/* Right vertex non-convex; right leg extends cut-off line. */
rightLegDirection = obstacle1->unitDir_;
}
}
/*
* Legs can never point into neighboring edge when convex vertex,
* take cutoff-line of neighboring edge instead. If velocity projected on
* "foreign" leg, no constraint is added.
*/
const Obstacle *const leftNeighbor = obstacle1->prevObstacle_;
bool isLeftLegForeign = false;
bool isRightLegForeign = false;
if (obstacle1->isConvex_ && det(leftLegDirection, -leftNeighbor->unitDir_) >= 0.0f) {
/* Left leg points into obstacle. */
leftLegDirection = -leftNeighbor->unitDir_;
isLeftLegForeign = true;
}
if (obstacle2->isConvex_ && det(rightLegDirection, obstacle2->unitDir_) <= 0.0f) {
/* Right leg points into obstacle. */
rightLegDirection = obstacle2->unitDir_;
isRightLegForeign = true;
}
/* Compute cut-off centers. */
const Vector2 leftCutoff = invTimeHorizonObst * (obstacle1->point_ - position_);
const Vector2 rightCutoff = invTimeHorizonObst * (obstacle2->point_ - position_);
const Vector2 cutoffVec = rightCutoff - leftCutoff;
/* Project current velocity on velocity obstacle. */
/* Check if current velocity is projected on cutoff circles. */
const float t = (obstacle1 == obstacle2 ? 0.5f : ((velocity_ - leftCutoff) * cutoffVec) / absSq(cutoffVec));
const float tLeft = ((velocity_ - leftCutoff) * leftLegDirection);
const float tRight = ((velocity_ - rightCutoff) * rightLegDirection);
if ((t < 0.0f && tLeft < 0.0f) || (obstacle1 == obstacle2 && tLeft < 0.0f && tRight < 0.0f)) {
/* Project on left cut-off circle. */
const Vector2 unitW = normalize(velocity_ - leftCutoff);
line.direction = Vector2(unitW.y(), -unitW.x());
line.point = leftCutoff + radius_ * invTimeHorizonObst * unitW;
orcaLines_.push_back(line);
continue;
}
else if (t > 1.0f && tRight < 0.0f) {
/* Project on right cut-off circle. */
const Vector2 unitW = normalize(velocity_ - rightCutoff);
line.direction = Vector2(unitW.y(), -unitW.x());
line.point = rightCutoff + radius_ * invTimeHorizonObst * unitW;
orcaLines_.push_back(line);
continue;
}
/*
* Project on left leg, right leg, or cut-off line, whichever is closest
* to velocity.
*/
const float distSqCutoff = ((t < 0.0f || t > 1.0f || obstacle1 == obstacle2) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (leftCutoff + t * cutoffVec)));
const float distSqLeft = ((tLeft < 0.0f) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (leftCutoff + tLeft * leftLegDirection)));
const float distSqRight = ((tRight < 0.0f) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (rightCutoff + tRight * rightLegDirection)));
if (distSqCutoff <= distSqLeft && distSqCutoff <= distSqRight) {
/* Project on cut-off line. */
line.direction = -obstacle1->unitDir_;
line.point = leftCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
orcaLines_.push_back(line);
continue;
}
else if (distSqLeft <= distSqRight) {
/* Project on left leg. */
if (isLeftLegForeign) {
continue;
}
line.direction = leftLegDirection;
line.point = leftCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
orcaLines_.push_back(line);
continue;
}
else {
/* Project on right leg. */
if (isRightLegForeign) {
continue;
}
line.direction = -rightLegDirection;
line.point = rightCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
orcaLines_.push_back(line);
continue;
}
const size_t numObstLines = orcaLines_.size();
const float invTimeHorizon = 1.0f / timeHorizon_;
/* Create agent ORCA lines. */
for (size_t i = 0; i < agentNeighbors_.size(); ++i) {
const Agent *const other = agentNeighbors_[i].second;
const Vector2 relativePosition = other->position_ - position_;
const Vector2 relativeVelocity = velocity_ - other->velocity_;
const float distSq = absSq(relativePosition);
const float combinedRadius = radius_ + other->radius_;
const float combinedRadiusSq = sqr(combinedRadius);
Line line;
Vector2 u;
if (distSq > combinedRadiusSq) {
/* No collision. */
const Vector2 w = relativeVelocity - invTimeHorizon * relativePosition;
/* Vector from cutoff center to relative velocity. */
const float wLengthSq = absSq(w);
const float dotProduct1 = w * relativePosition;
if (dotProduct1 < 0.0f && sqr(dotProduct1) > combinedRadiusSq * wLengthSq) {
/* Project on cut-off circle. */
const float wLength = std::sqrt(wLengthSq);
const Vector2 unitW = w / wLength;
line.direction = Vector2(unitW.y(), -unitW.x());
u = (combinedRadius * invTimeHorizon - wLength) * unitW;
}
else {
/* Project on legs. */
const float leg = std::sqrt(distSq - combinedRadiusSq);
if (det(relativePosition, w) > 0.0f) {
/* Project on left leg. */
line.direction = Vector2(relativePosition.x() * leg - relativePosition.y() * combinedRadius, relativePosition.x() * combinedRadius + relativePosition.y() * leg) / distSq;
}
else {
/* Project on right leg. */
line.direction = -Vector2(relativePosition.x() * leg + relativePosition.y() * combinedRadius, -relativePosition.x() * combinedRadius + relativePosition.y() * leg) / distSq;
}
const float dotProduct2 = relativeVelocity * line.direction;
u = dotProduct2 * line.direction - relativeVelocity;
}
}
else {
/* Collision. Project on cut-off circle of time timeStep. */
const float invTimeStep = 1.0f / sim_->timeStep_;
/* Vector from cutoff center to relative velocity. */
const Vector2 w = relativeVelocity - invTimeStep * relativePosition;
const float wLength = abs(w);
const Vector2 unitW = w / wLength;
line.direction = Vector2(unitW.y(), -unitW.x());
u = (combinedRadius * invTimeStep - wLength) * unitW;
}
line.point = velocity_ + 0.5f * u;
orcaLines_.push_back(line);
}
size_t lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, newVelocity_);
if (lineFail < orcaLines_.size()) {
linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, newVelocity_);
}
๊ณ์ฐ๋ ORCA ๋ผ์ธ๋ค๊ณผ Linear Programming์ ์ด์ฉํด์ ์๋ก์ด ์๋๋ฅผ ์ป๋๋ค.
๐กLinear Programming
๋ค์ํ ์ ์ฝ ์กฐ๊ฑด์ ๊ณ ๋ คํ๋ฉด์ ์ ํ ํจ์๋ฅผ ์ต๋ํํ๊ฑฐ๋ ์ต์ํํ๋ ์ํ์ ๋ชจ๋ธ๋ง ๊ธฐ์
size_t lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, newVelocity_);
if (lineFail < orcaLines_.size()) {
linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, newVelocity_);
}
result == newVelocity_๋ ๊ฐ๋ค.
size_t linearProgram2(const std::vector<Line> &lines, float radius, const Vector2 &optVelocity, bool directionOpt, Vector2 &result)
{
if (directionOpt) {
//์ต์ ํ๋์ด ์๋ ๋ฐฉํฅ. ์ด ๊ฒฝ์ฐ ์ต์ ํ ์๋๋ ๋จ์ ๊ธธ์ด์
๋๋ค.
result = optVelocity * radius;
}
else if (absSq(optVelocity) > sqr(radius)) {
/* ๊ฐ์ฅ ๊ฐ๊น์ด ์ ๊ณผ ๋ฐ๊นฅ์ชฝ ์์ ์ต์ ํํฉ๋๋ค. */
result = normalize(optVelocity) * radius;
}
else {
/* ๊ฐ์ฅ ๊ฐ๊น์ด ์ ๊ณผ ์์ชฝ ์์ ์ต์ ํํฉ๋๋ค. */
result = optVelocity;
}
for (size_t i = 0; i < lines.size(); ++i) {
//๋ ์ ์ด ๊ฐ์ผ๋ฉด 0์ ์ถ๋ ฅ
if (det(lines[i].direction, lines[i].point - result) > 0.0f) { // ํํ์ฌ๋ณํ์ ๋ฉด์
/* ๊ฒฐ๊ณผ๊ฐ ์ ์ฝ์กฐ๊ฑด i๋ฅผ ๋ง์กฑํ์ง ์์ต๋๋ค. ์๋ก์ด ์ต์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํฉ๋๋ค. */
const Vector2 tempResult = result;
if (!linearProgram1(lines, i, radius, optVelocity, directionOpt, result)) {
result = tempResult;
return i;
}
}
}
return lines.size();
}
bool linearProgram1(const std::vector<Line> &lines, size_t lineNo, float radius, const Vector2 &optVelocity, bool directionOpt, Vector2 &result)
{
//์ค์นผ๋ผ ๊ณฑ, ๋ฒกํฐ์ ๋ด์
const float dotProduct = lines[lineNo].point * lines[lineNo].direction;
const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(lines[lineNo].point);
if (discriminant < 0.0f) {
/* ์ต๋ ์๋ ์์ line lineNo๋ฅผ ์์ ํ ๋ฌดํจํํฉ๋๋ค. */
return false;
}
const float sqrtDiscriminant = std::sqrt(discriminant);
float tLeft = -dotProduct - sqrtDiscriminant;
float tRight = -dotProduct + sqrtDiscriminant;
for (size_t i = 0; i < lineNo; ++i) {
//๋ถ๋ชจ
const float denominator = det(lines[lineNo].direction, lines[i].direction);
//๋ถ์
const float numerator = det(lines[i].direction, lines[lineNo].point - lines[i].point);
//fabs() : ์ ๋๊ฐ ์ฐ์ฐ
if (std::fabs(denominator) <= RVO_EPSILON) {
/* Lines lineNo๊ณผ i๋ (๊ฑฐ์) ๋๋ํ๋ค. */
if (numerator < 0.0f) {
return false;
}
else {
continue;
}
}
const float t = numerator / denominator;
if (denominator >= 0.0f) {
/* Line i bounds line lineNo on the right. */
tRight = std::min(tRight, t);
}
else {
/* Line i bounds line lineNo on the left. */
tLeft = std::max(tLeft, t);
}
if (tLeft > tRight) {
return false;
}
}
if (directionOpt) {
/* ์ต์ ํ ๋ฐฉํฅ */
if (optVelocity * lines[lineNo].direction > 0.0f) {
/* ์ค๋ฅธ์ชฝ์ผ๋ก ๊ทน๋จ์ ์ผ๋ก. */
result = lines[lineNo].point + tRight * lines[lineNo].direction;
}
else {
/* ์ผ์ชฝ์ผ๋ก ๊ทน๋จ์ ์ผ๋ก. */
result = lines[lineNo].point + tLeft * lines[lineNo].direction;
}
}
else {
/* ๊ฐ์ฅ ๊ฐ๊น์ด ์ง์ ์ ์ต์ ํํฉ๋๋ค. */
const float t = lines[lineNo].direction * (optVelocity - lines[lineNo].point);
if (t < tLeft) {
result = lines[lineNo].point + tLeft * lines[lineNo].direction;
}
else if (t > tRight) {
result = lines[lineNo].point + tRight * lines[lineNo].direction;
}
else {
result = lines[lineNo].point + t * lines[lineNo].direction;
}
}
return true;
}
void linearProgram3(const std::vector<Line> &lines, size_t numObstLines, size_t beginLine, float radius, Vector2 &result)
{
float distance = 0.0f;
for (size_t i = beginLine; i < lines.size(); ++i) {
if (det(lines[i].direction, lines[i].point - result) > distance) {
/* ๊ฒฐ๊ณผ๊ฐ ์ i์ ์ ์ฝ ์กฐ๊ฑด์ ๋ง์กฑํ์ง ์์ต๋๋ค. */
std::vector<Line> projLines(lines.begin(), lines.begin() + static_cast<ptrdiff_t>(numObstLines));
for (size_t j = numObstLines; j < i; ++j) {
Line line;
float determinant = det(lines[i].direction, lines[j].direction);
if (std::fabs(determinant) <= RVO_EPSILON) {
/* Line i ๊ณผ line j ๋ ๋๋ํ๋ค */
if (lines[i].direction * lines[j].direction > 0.0f) {
/* Line i ๊ณผ line j ๋ ๊ฐ์ ๋ฐฉํฅ์ ๊ฐ๋ฆฌํจ๋ค */
continue;
}
else {
/* Line i ๊ณผ line j ๋ ๋ฐ๋ ๋ฐฉํฅ์ ๊ฐ๋ฆฌํจ๋ค */
line.point = 0.5f * (lines[i].point + lines[j].point);
}
}
else {
line.point = lines[i].point + (det(lines[j].direction, lines[i].point - lines[j].point) / determinant) * lines[i].direction;
}
line.direction = normalize(lines[j].direction - lines[i].direction);
projLines.push_back(line);
}
const Vector2 tempResult = result;
if (linearProgram2(projLines, radius, Vector2(-lines[i].direction.y(), lines[i].direction.x()), true, result) < projLines.size()) {
/* ์์น์ ์ผ๋ก ์ด๋ฐ ์ผ์ ์์ด์ผ ํฉ๋๋ค.
* ๊ทธ ๊ฒฐ๊ณผ๋ ์ ์์ ์ด ์ ํ ํ๋ก๊ทธ๋จ์ ์คํ ๊ฐ๋ฅ ์์ญ์ ์ด๋ฏธ ์กด์ฌํฉ๋๋ค.
* ์คํจํ ๊ฒฝ์ฐ ์์ ๋ถ๋ ์์์ ์ค์ฐจ๋ก ์ธํด ๋ฐ์ํ๋ฉฐ ํ์ฌ ๊ฒฐ๊ณผ๋ ๊ทธ๋๋ก
* ์ ์ง๋ฉ๋๋ค.
*/
result = tempResult;
}
distance = det(lines[i].direction, lines[i].point - result);
}
}
}
์ฐ๊ด ํค์๋
half plane, ORCA, Linear Programming, Velocity Obstacle, ๋ฏผ์ฝ์ฐ์คํค ํฉ
* ์ฅ์ ๋ฌผ์์ ๊ฑธ๋ฆฌ๋ ํ์์ด ๋ง์ด ๋์ค๋ ์ด์ ๋ RVO ์์ฒด๊ฐ ์์ง์ด๋ Obstacle๋ค์ด ์๋ก ์ฑ ์์ ๋ฐํ์ฉ ๋๋์ด ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ๊ณ ์ ์ฅ์ ๋ฌผ์์๋ ํจ๊ณผ๊ฐ ๋ ๋๋ ๊ฒ์ผ๋ก ์ถ์ธก*
์ฐธ๊ณ ์ฌ์ดํธ
'๊ณต๋ถ > Crowd Simulation' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Velocity Obstacle(VO)๋? (0) | 2020.01.17 |
---|