한 벡터에서 다른 벡터로의 회전을 나타내는 쿼터니언 찾기


105

두 개의 벡터 u와 v가 있습니다. u에서 v 로의 회전을 나타내는 쿼터니언을 찾는 방법이 있습니까?

답변:


115
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);

q를 정규화하는 것을 잊지 마십시오.

Richard는 고유 한 회전이 없다는 점에 대해 옳지 만 위의 내용은 "가장 짧은 원호"를 제공해야합니다. 이는 아마도 필요한 것입니다.


30
이것은 평행 벡터의 경우를 처리하지 않는다는 점에 유의하십시오 (동일한 방향 또는 반대 방향을 가리키는 둘 다). crossproduct이러한 경우에는 유효하지 않으므로 먼저 및를 각각 확인 dot(v1, v2) > 0.999999하고 dot(v1, v2) < -0.999999평행 벡터에 대해 식별 쿼트를 반환하거나 반대 벡터에 대해 180도 회전 (모든 축에 대해)을 반환해야합니다.
sinisterchipmunk

11
이것의 좋은 구현은에서 찾을 수 있습니다 ogre3d 소스 코드
주앙 포르텔 라를

4
@sinisterchipmunk 실제로 v1 = v2이면 외적은 (0,0,0)이되고 w는 양수가되어 정체성으로 정규화됩니다. gamedev.net/topic/… 에 따르면 v1 = -v2 및 그 근처에서도 잘 작동합니다.
jpa

3
이 기술이 어떻게 작동하게 되었습니까? 하나 sqrt((v1.Length ^ 2) * (v2.Length ^ 2))v1.Length * v2.Length. 현명한 결과를 내기 위해 이것의 변형을 얻을 수 없었습니다.
Joseph Thomson

2
예, 작동합니다. 소스 코드를 참조하십시오 . L61은 벡터가 반대 방향을 향하면 처리합니다 (PI를 반환하고 그렇지 않으면 @jpa의 발언에 따라 ID를 반환합니다). L67은 병렬 벡터를 처리합니다. 수학적으로 불필요하지만 더 빠릅니다. L72는 Polaris878의 대답이며 두 벡터가 모두 단위 길이 (sqrt를 피함)라고 가정합니다. 단위 테스트를 참조하십시오 .
sinisterchipmunk 2013

63

반 방향 벡터 솔루션

나는 임 브론디 르가 제시하려한다고 생각하는 해결책을 생각해 냈습니다. (사소한 실수가 있었지만, 아마도 불길한 다람쥐가 그것을 확인하는 데 어려움을 겪었을 것입니다).

다음과 같이 축을 중심으로 한 회전을 나타내는 쿼터니언을 구성 할 수 있다고 가정합니다.

q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == sin(angle / 2) * axis.z

그리고 두 정규화 된 벡터의 내적과 외적은 다음과 같습니다.

dot     == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z

에서 회전으로 보는 UV 것은 우리가 직접 도트 및 벡터 곱의 결과와 같은 회전을 나타내는 쿼터니언을 생성 할 수있는 것처럼 수직 벡터 주위 세타 (벡터 사이의 각도)로 회전시킴으로써 달성 될 수 있고, 그것은 본다 ; 그러나 그것이 의미하는 바는 theta = angle / 2 입니다. 즉, 그렇게하면 원하는 회전의 두 배가됩니다.

한가지 해결책 사이 벡터 반쯤를 계산하는 UV 와 도트의 외적 사용 U반 방향 의 회전 나타내는 쿼터니언 구성하는 벡터를 두 번 사이의 각도 U반 방향 벡터를, 그것은 우리를 v 까지 데려다줍니다 !

u == -v 이고 고유 한 중간 벡터를 계산할 수 없게 되는 특별한 경우 가 있습니다. 이것은 우리를 u 에서 v로 데려 갈 수있는 무한히 많은 "가장 짧은 호"회전을 감안할 때 예상되며, 우리의 특별한 경우 솔루션으로 u (또는 v )에 직교하는 벡터 주위로 180도 회전해야합니다 . 이것은 표준화의 외적 취함으로써 이루어진다 U를 다른 벡터로 하지 평행 U .

의사 코드는 다음과 같습니다 (실제로 특별한 경우는 부동 소수점 부정확성을 설명해야합니다. 아마도 절대 값이 아닌 임계 값에 대해 내적을 확인하여).

또한 u == v 일 때 특별한 경우 가 없다는 점에 유의하십시오 (아이덴티티 쿼터니언이 생성됨-직접 확인하고 확인하십시오).

// N.B. the arguments are _not_ axis and angle, but rather the
// raw scalar-vector components.
Quaternion(float w, Vector3 xyz);

Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
  // It is important that the inputs are of equal length when
  // calculating the half-way vector.
  u = normalized(u);
  v = normalized(v);

  // Unfortunately, we have to check for when u == -v, as u + v
  // in this case will be (0, 0, 0), which cannot be normalized.
  if (u == -v)
  {
    // 180 degree rotation around any orthogonal vector
    return Quaternion(0, normalized(orthogonal(u)));
  }

  Vector3 half = normalized(u + v);
  return Quaternion(dot(u, half), cross(u, half));
}

orthogonal함수는 주어진 벡터에 직교하는 벡터를 반환합니다. 이 구현에서는 가장 직교하는 기저 벡터가있는 외적을 사용합니다.

Vector3 orthogonal(Vector3 v)
{
    float x = abs(v.x);
    float y = abs(v.y);
    float z = abs(v.z);

    Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS);
    return cross(v, other);
}

Half-Way Quaternion 솔루션

이것은 실제로 받아 들여진 답변에 제시된 솔루션이며 중간 벡터 솔루션보다 약간 더 빠른 것 같습니다 (내 측정에 의해 ~ 20 % 더 빠르지 만 내 말을 받아들이지는 않습니다). 저와 같은 다른 사람들이 설명에 관심이있을 경우를 대비하여 여기에 추가합니다.

기본적으로 반쪽 벡터를 사용하여 쿼터니언을 계산하는 대신 필요한 회전의 두 배 (다른 솔루션에 설명 된대로)를 발생시키는 쿼터니언을 계산하고이 각도와 0도 사이의 중간에있는 쿼터니언을 찾을 수 있습니다.

앞서 설명했듯이 필요한 회전의 두 배에 대한 쿼터니언은 다음과 같습니다.

q.w   == dot(u, v)
q.xyz == cross(u, v)

제로 회전의 쿼터니언은 다음과 같습니다.

q.w   == 1
q.xyz == (0, 0, 0)

중간 쿼터니언을 계산하는 것은 벡터와 마찬가지로 단순히 쿼터니언을 합산하고 결과를 정규화하는 문제입니다. 그러나 벡터의 경우와 마찬가지로 쿼터니언의 크기가 같아야합니다. 그렇지 않으면 결과가 더 큰 크기를 가진 쿼터니언쪽으로 치우칩니다.

두 벡터의 내적과 내적으로 구성된 쿼터니언은 다음 곱과 같은 크기를 갖습니다 length(u) * length(v). 네 가지 구성 요소를 모두이 요소로 나누는 대신, 대신 단위 쿼터니언을 확장 할 수 있습니다. 그리고를 사용하여 받아 들여지는 대답이 겉보기에 복잡한 이유가 궁금하다면 sqrt(length(u) ^ 2 * length(v) ^ 2), 벡터의 제곱 길이가 길이보다 계산하는 것이 더 빠르기 때문에 하나의 sqrt계산을 저장할 수 있습니다 . 결과는 다음과 같습니다.

q.w   = dot(u, v) + sqrt(length_2(u) * length_2(v))
q.xyz = cross(u, v)

그런 다음 결과를 정규화하십시오. 의사 코드는 다음과 같습니다.

Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
  float k_cos_theta = dot(u, v);
  float k = sqrt(length_2(u) * length_2(v));

  if (k_cos_theta / k == -1)
  {
    // 180 degree rotation around any orthogonal vector
    return Quaternion(0, normalized(orthogonal(u)));
  }

  return normalized(Quaternion(k_cos_theta + k, cross(u, v)));
}

12
+1 : 좋아요! 이것은 매력으로 작용했습니다. 받아 들여진 대답이어야합니다.
Rekin

1
쿼터니언 구문은 일부 예 (Quaternion (xyz, w) 및 Quaternion (w, xyz))에서 전환됩니다. 또한 마지막 코드 블록에서 라디안과 각도가 혼합되어 각도를 표현하는 것 같습니다 (180 대 k_cos_theta + k).
Guillermo Blasco 2014 년

1
Quaternion (float, Vector3)은 스칼라 벡터에서 생성되는 반면 Quaternion (Vector3, float)은 축 각도에서 생성됩니다. 아마도 혼란 스러울 수도 있지만 옳다고 생각합니다. 그래도 틀렸다고 생각되면 정정하십시오!
Joseph Thomson

작동했습니다! 감사! 그러나 위의 작업을 수행하기 위해 유사하고 잘 설명 된 또 다른 링크 를 찾았습니다 . 내가 기록을 위해 공유해야한다고 생각 했어;)
sinner

1
@JosephThomson 하프 웨이 쿼터니언 솔루션은 여기 에서 나온 것 같습니다 .
legends2k

6

언급 된 문제는 잘 정의되어 있지 않습니다. 주어진 벡터 쌍에 대해 고유 한 회전이 없습니다. 예를 들어, u = <1, 0, 0> 및 v = <0, 1, 0> 인 경우를 고려하십시오 . u에서 v 로의 한 회전 은 z 축을 중심 으로 한 pi / 2 회전입니다. u에서 v 로의 또 다른 회전 은 벡터 <1, 1, 0> 주위의 파이 회전 입니다.


1
실제로 무한한 수의 가능한 답이 있지 않습니까? "from"벡터를 "to"벡터와 정렬 한 후에도 결과를 축을 중심으로 자유롭게 회전 할 수 있기 때문입니까? 이 선택을 제한하고 문제를 잘 정의하기 위해 일반적으로 어떤 추가 정보를 사용할 수 있는지 알고 있습니까?
더그 소매점을

5

순수한 쿼터니언을 사용하여 벡터를 표현하지 않는 이유는 무엇입니까? 아마도 먼저 정규화하는 것이 좋습니다.
q 1 = (0 u x u y u z ) '
q 2 = (0 v x v y v z )'
q 1 q rot = q 2
q 1 -1로 미리 곱하기
q rot = q 1 -1 q 2
여기서 q 1 -1 = q 1 conj / q norm
이것은 "왼쪽 분할"이라고 생각할 수 있습니다. 원하는 것이 아닌 오른쪽 분할 :
q rot, right = q 2 -1 q 1


2
나는 길을 잃었다. q1에서 q2 로의 회전이 q_2 = q_rot q_1 q_rot ^ -1로 계산되지 않습니까?
yota

4

나는 Quaternion을 잘 못합니다. 그러나 나는 이것에 대해 몇 시간 동안 어려움을 겪었고 Polaris878 솔루션을 작동시킬 수 없었습니다. v1 및 v2 사전 정규화를 시도했습니다. 정규화 q. q.xyz 정규화. 그러나 나는 그것을 이해하지 못한다. 결과는 여전히 나에게 올바른 결과를주지 못했습니다.

결국 나는 해결책을 찾았지만. 다른 사람에게 도움이된다면 여기에 내 작업 (python) 코드가 있습니다.

def diffVectors(v1, v2):
    """ Get rotation Quaternion between 2 vectors """
    v1.normalize(), v2.normalize()
    v = v1+v2
    v.normalize()
    angle = v.dot(v2)
    axis = v.cross(v2)
    return Quaternion( angle, *axis )

v1과 v2가 v1 == v2 또는 v1 == -v2 (일부 허용 오차 포함)와 같이 평행 한 경우 특별한 경우를 만들어야합니다. 여기서 솔루션은 Quaternion (1, 0,0,0) (회전 없음)이어야합니다. 또는 Quaternion (0, * v1) (180도 회전)


나는 작동하는 구현을 가지고 있지만 이것은 당신의 것이 더 예쁘기 때문에 정말로 작동하기를 원했습니다. 불행히도 모든 테스트 케이스가 실패했습니다. 내 테스트는 모두 다음과 같습니다 quat = diffVectors(v1, v2); assert quat * v1 == v2.
sinisterchipmunk

angle내적에서 그 가치를 얻기 때문에 이것이 전혀 작동하지 않을 것 입니다.
sam hocevar 2014

Quaternion () 함수는 어디에 있습니까?
June Wang

3

일부 답변은 외적이 0 일 가능성을 고려하지 않는 것 같습니다. 아래 스 니펫은 각도 축 표현을 사용합니다.

//v1, v2 are assumed to be normalized
Vector3 axis = v1.cross(v2);
if (axis == Vector3::Zero())
    axis = up();
else
    axis = axis.normalized();

return toQuaternion(axis, ang);

toQuaternion다음과 같이 구현할 수 있습니다 :

static Quaternion toQuaternion(const Vector3& axis, float angle)
{
    auto s = std::sin(angle / 2);
    auto u = axis.normalized();
    return Quaternion(std::cos(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}

Eigen 라이브러리를 사용하는 경우 다음을 수행 할 수도 있습니다.

Quaternion::FromTwoVectors(from, to)

toQuaternion(axis, ang)-> 당신은 무엇을 지정하는 것을 잊었다ang
Maksym Ganenko

두 번째 매개 변수는 angle라디안 단위로 측정되는 쿼터니언의 축 각도 표현의 일부입니다.
Shital Shah

한 벡터에서 다른 벡터로 회전하기 위해 쿼터니언을 얻으라는 요청을 받았습니다. 각도가 없습니다. 먼저 계산해야합니다. 답은 각도 계산을 포함해야합니다. 건배!
Maksym Ganenko 19

이것은 C ++입니까? ux () 란 무엇입니까?
June Wang

예, 이것은 C ++입니다. u는 Eigen 라이브러리의 벡터 유형입니다 (사용하는 경우).
Shital Shah

2

알고리즘 관점에서 가장 빠른 솔루션은 의사 코드를 찾습니다.

 Quaternion shortest_arc(const vector3& v1, const vector3& v2 ) 
 {
     // input vectors NOT unit
     Quaternion q( cross(v1, v2), dot(v1, v2) );
     // reducing to half angle
     q.w += q.magnitude(); // 4 multiplication instead of 6 and more numerical stable

     // handling close to 180 degree case
     //... code skipped 

        return q.normalized(); // normalize if you need UNIT quaternion
 }

단위 쿼터니언이 필요한지 확인하십시오 (보통 보간에 필요함).

참고 : 비 단위 쿼터니언은 일부 작업에 단위보다 빠르게 사용할 수 있습니다.

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