์ƒˆ์†Œ์‹

๊ณต๋ถ€/Crowd Simulation

Reciprocal Velocity Obstacle(RVO) : RVO2

  • -
728x90

์•ž์„  ๊ฐœ๋…

 

Velocity Obstacle(VO)๋ž€?

๋…ผ๋ฌธ ์ฐธ๊ณ  : Motion Planning in Dynamic Environments using Velocity Obstacles - 1998๋…„๋„ ๋ฐœํ–‰ ๋…ผ๋ฌธ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ- ์†๋„ ์žฅ์• ๋ฌผ์€ ๋ฐ”๋€Œ๋Š” ์‹œ๊ฐ„ ํ™˜๊ฒฝ์„ ์œ„ํ•œ configuration space obstacle์˜ ํ™•์žฅ ๊ฐœ๋…์ด๋‹ค. ์ฆ‰ ์‹ค์‹œ๊ฐ„ ํ™˜๊ฒฝ์—

dana3711.tistory.com


์ƒˆ๋กœ์šด ๊ฐœ๋…์ธ Reciprocal Velocity Obstacle(์ดํ•˜ RVO)๋ฅผ ์ œ์•ˆํ•จ
๊ฐ ์—์ด์ „ํŠธ๊ฐ€ ๋‹ค๋ฅธ ์—์ด์ „ํŠธ์™€ ๋ช…์‹œ์ ์œผ๋กœ ํ†ต์‹ ํ•˜์ง€ ์•Š๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ์›€์ง์ด๋Š” ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•จ

๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์ด ์œ ์‚ฌํ•œ ์ถฉ๋Œ ํšŒํ”ผ ์ถ”๋ก ์„ ์•”๋ฌต์ ์œผ๋กœ ๊ฐ€์ •ํ•จ์œผ๋กœ์จ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋ฐ˜์‘์  ํ–‰๋™์„ ๊ณ ๋ คํ•จ
์ •์  ์žฅ์• ๋ฌผ๊ณผ ์ด๋™ ์žฅ์• ๋ฌผ์„ ๋ชจ๋‘ ํฌํ•จํ•˜๋Š” ์ธ๊ตฌ ๋ฐ€๋„๊ฐ€ ๋†’์€ ํ™˜๊ฒฝ์—์„œ ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์—์ด์ „ํŠธ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์— ๊ฐœ๋…์„ ์ ์šฉํ•˜๊ณ , ๋„์ „์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‹ค์‹œ๊ฐ„ ๋ฐ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์คŒ
์‚ฌ์šฉํ•˜๋Š” ORCA ๊ฐœ๋…์˜ ๊ฒฝ์šฐ ๋กœ๋ณดํ‹ฑ์Šค์—์„œ ๋กœ๋ด‡๊ฐ„์˜ ์ถฉ๋Œ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ๋„ ์“ฐ์ž„

 

์ˆ˜์ •ํ•˜๊ณ ์ž ํ•œ ๊ฒƒ

Velocity Obstacle(VO)์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ง„๋™ํ˜„์ƒ์„ ์ˆ˜์ •ํ•˜๊ณ ์ž ์ œ์ž‘ํ•จ

 

ํ•ต์‹ฌ ๋‚ด์šฉ

์›€์ง์ด๋Š” ์žฅ์• ๋ฌผ๋“ค ์‚ฌ์ด์—์„œ์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ์›€์ง์ž„์„ ์œ„ํ•ด ๋„์ž…๋œ VO(Velocity Obstacle) ๊ฐœ๋…์˜ ํ™•์žฅ

ORCA(Optimal Reciprocal Collision Avoidance)๋ผ๋Š” ๊ฐœ๋…์„ ๋„์ž…ํ•˜์—ฌ ๊ฐ ๊ฐ์ฒด๊ฐ„์˜ ์†๋„๋ฅผ ๊ณ„์‚ฐํ•œ ํ›„ ๊ทธ ์ฑ…์ž„์„ ๊ฐ๊ฐ ๋ฐ˜ํ‹ˆ์”ฉ ๊ฐ€์ง€๊ฒŒ ๋งŒ๋“ค์–ด์„œ ๊ตฐ์ค‘์˜ ๋ญ‰์น˜๋Š” ํ˜„์ƒ์„ ์ค„์–ด๋“ค๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ

๊ฒฐ๊ตญ ๋ณ‘๋ชฉ ํ˜„์ƒ(Bottleneck)์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ œ์ž‘

 

- ๋ฏผ์ฝ”์šฐ์Šคํ‚ค ํ•ฉ (๋ฏผ์ฝ”ํ”„์Šคํ‚ค ๋ง์…ˆ)

๊ธฐํ•˜ํ•™์—์„œ, ์œ ํด๋ฆฌ๋“œ ๊ณต๊ฐ„์˜ ์œ„์น˜๋ฒกํ„ฐ A์™€ B์˜ ๋‘ ์ง‘ํ•ฉ์˜ ๋ฏผ์ฝ”์Šคํ‚ค ํ•ฉ(ํŒฝ์ฐฝ)์€ A์— ์žˆ๋Š” ๋ชจ๋“  ๋ฒกํ„ฐ๋ฅผ B์— ์žˆ๋Š” ๊ฐ๊ฐ์˜ ๋ฒกํ„ฐ์— ๋”ํ•ด์„œ ๋งŒ๋“ค์–ด์ง„๋‹ค.
 

 
 
๋ฏผ์ฝ”ํ”„์Šคํ‚ค ๋ง์…ˆ์€ ์žฅ์• ๋ฌผ ์‚ฌ์ด๋กœ ์ง€๋‚˜๋Š” ๋ชจ์…˜๊ณ„ํš์— ์‚ฌ์šฉ๋œ๋‹ค.

 

์ถ”๊ฐ€ ์ฐธ๊ณ  ์ž๋ฃŒ

 

Minkowski sum of convex polygons - Algorithms for Competitive Programming

Minkowski sum of convex polygons Definition Consider two sets $A$ and $B$ of points on a plane. Minkowski sum $A + B$ is defined as $\{a + b| a \in A, b \in B\}$. Here we will consider the case when $A$ and $B$ consist of convex polygons $P$ and $Q$ with t

cp-algorithms.com

 

๋ฏผ์ฝ”ํ”„์Šคํ‚ค ๋ง์…ˆ - ์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „

์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „. ๋นจ๊ฐ„ ๋„ํ˜•์€ ํŒŒ๋ž€ ๋„ํ˜•๊ณผ ์ดˆ๋ก์ƒ‰ ๋„ํ˜•์˜ ๋ฏผ์ฝ”ํ”„์Šคํ‚ค ๋ง์…ˆ์ด๋‹ค. ๊ธฐํ•˜ํ•™์—์„œ, ์œ ํด๋ฆฌ๋“œ ๊ณต๊ฐ„์˜ ์œ„์น˜๋ฒกํ„ฐ A์™€ B์˜ ๋‘ ์ง‘ํ•ฉ์˜ ๋ฏผ์ฝ”ํ”„์Šคํ‚ค ํ•ฉ(ํŒฝ์ฐฝ์ด๋ผ๊ณ ๋„ ์•Œ๋ ค์ ธ

ko.wikipedia.org

 

 

์‹ค์ œ ์ž‘๋™ 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 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•œ ๊ตฐ์ค‘ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์˜ˆ์ œ ๋ชจ์Šต

 

* ์žฅ์• ๋ฌผ์—์„œ ๊ฑธ๋ฆฌ๋Š” ํ˜„์ƒ์ด ๋งŽ์ด ๋‚˜์˜ค๋Š” ์ด์œ ๋Š” RVO ์ž์ฒด๊ฐ€ ์›€์ง์ด๋Š” Obstacle๋“ค์ด ์„œ๋กœ ์ฑ…์ž„์„ ๋ฐ˜ํ‹ˆ์”ฉ ๋‚˜๋ˆ„์–ด ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ๊ณ ์ • ์žฅ์• ๋ฌผ์—์„œ๋Š” ํšจ๊ณผ๊ฐ€ ๋œ ๋‚˜๋Š” ๊ฒƒ์œผ๋กœ ์ถ”์ธก*

 

 

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

 

[Algorithm] Linear Programming (1)

์ด ๊ธ€์€ ํฌ์Šคํ… ์˜ค์€์ง„ ๊ต์ˆ˜๋‹˜์˜ CSED331 ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ˆ˜์—…์˜ ๊ฐ•์˜ ๋‚ด์šฉ๊ณผ ์ž๋ฃŒ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

rntlqvnf.github.io

 

Optimal Reciprocal Collision Avoidance (ORCA)

We present a formal approach to reciprocal collision avoidance, where multiple independent mobile robots or agents need to avoid collisions with each other without communication among agents while moving in a common workspace. Our formulation, optimal reci

gamma.cs.unc.edu

 

Collision Avoidance

Description   Collision avoidance is a fundamental problem in many areas such as robotics and animation. To that end, we developed new techniques focused on providing fast and robust collision avoidance for multiple agents moving around obstacles and each

gamma.cs.unc.edu

 

Reciprocal Velocity Obstacles for Real-Time Multi-Agent Navigation

ABSTRACT We propose a new concept --- the "Reciprocal Velocity Obstacle" --- for real-time multi-agent navigation. We consider the case in which each agent navigates independently without explicit communication with other agents. Our formulation is an exte

gamma.cs.unc.edu

 

๋ฐ˜์‘ํ˜•

'๊ณต๋ถ€ > Crowd Simulation' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Velocity Obstacle(VO)๋ž€?  (0) 2020.01.17
Contents

ํฌ์ŠคํŒ… ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค

์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต๊ฐ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.