쿼터니언으로 vector3 회전


25

주어진 쿼터니언으로 vector3를 회전하려고합니다.

나는 이것이 사실이라는 것을 안다

v=qvq1

나는 이 인 역수 라는 것을 알고 있지만 벡터를 곱하기 위해 벡터의 곱셈을 쿼터니언에 어떻게 매핑합니까?q1qmagnitude(q)

를 행렬로 취급 하고 와 를 행렬로 변환 한 다음 를 행렬에서 벡터 로 변환 할 수 있지만 벡터를 얻는 것보다 조금 위에있는 것처럼 보입니다. 사용할 수있는 깔끔한 구현이 있습니까?vqqv

답변:


36

Nathan Reed와 teodron이 노출 한 것처럼 단위 길이 쿼터니언 q 로 벡터 v 를 회전시키는 방법 은 다음과 같습니다.

1) 순수 사원 수 만들기 페이지 . 이것은 단순히 네 번째 좌표 0을 추가하는 것을 의미합니다.

p=(vx,vy,vz,0)p=(v,0)

2) q를 사전 곱하고 켤레 q *를 곱한 후 :

p=q×p×q

3) 이것은 또 다른 순수한 쿼터니언을 만들어 벡터로 되돌릴 수 있습니다.

v=(px,py,pz)

이 벡터 이며 회전 .vvq


이것은 작동하지만 최적아닙니다 . 쿼터니언 곱셈은 톤과 톤의 연산을 의미합니다. 나는 이것과 같은 다양한 구현에 대해 호기심이 많았고 , 어디에서 왔는지 찾기로 결정했습니다. 내 결과는 다음과 같습니다.

또한 설명 할 수 Q를 3 차원 벡터의 조합으로서 U 스칼라 S :

q=(ux,uy,uz,s)q=(u,s)

쿼터니언 곱셈 의 규칙에 따르면 단위 길이 쿼터니언의 켤레가 단순히 역수이므로 다음과 같이됩니다.

p=qpq=(u,s)(v,0)(u,s)=(sv+u×v,uv)(u,s)=((uv)(u)+s(sv+u× v)+(sv+u×v)×(u),)=((uv)u+s2v+s(u×v)+sv×(u)+(u×v)×(u),)

스칼라 부분 (ellipses)은 여기에 설명 된대로 0이 됩니다 . 흥미로운 것은 벡터 부분, 일명 우리의 회전 벡터 v ' 입니다. 몇 가지 기본 벡터 ID를 사용하여 단순화 할 수 있습니다 .

v=(uv)u+s2v+s(u×v)+s(u×v)+u×(u×v)=(uv)u+s2v+2s(u×v)+(uv)u(uu)v=2(uv)u+(s2uu)v+2s(u×v)

이것은 지금 훨씬 더 최적입니다 ; 두 개의 도트 제품, 교차 제품 및 몇 가지 추가 기능 : 작업의 절반 정도. 소스 코드에서 일반적인 벡터 수학 라이브러리를 가정하면 다음과 같이됩니다.

void rotate_vector_by_quaternion(const Vector3& v, const Quaternion& q, Vector3& vprime)
{
    // Extract the vector part of the quaternion
    Vector3 u(q.x, q.y, q.z);

    // Extract the scalar part of the quaternion
    float s = q.w;

    // Do the math
    vprime = 2.0f * dot(u, v) * u
          + (s*s - dot(u, u)) * v
          + 2.0f * s * cross(u, v);
}

A와 오프 모자 더 나은 서면 답변. 그리고 대부분의 성능 괴물이 내장 연산을 사용하여 벡터 연산을 수행하는 경향이 있다는 점을 고려하면 속도가 빨라집니다 (특히 인텔 아키텍처에서 일반 쿼터니언 곱셈의 경우에도).
teodron

최종 결과는 Rodrigues의 회전 공식 과 유사 합니다. 어쨌든 동일한 기본 벡터를 갖습니다. 계수가 일치하는지 확인하기 위해 삼각 관계를 파헤쳐 야합니다.
Nathan Reed

@NathanReed 이것은 동일한 결과를 얻는 또 다른 방법 인 것 같습니다. 또한 이것이 일치하는지 알고 싶습니다. 지적 해 주셔서 감사합니다!
Laurent Couvidou

1
나는 GLM의 구현을 점검하고 있었고 다음과 같이 조금 다르게 구현 된 것 같습니다. vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0f이것은 유사한 최적화입니까? 다소 비슷해 보이지만 동일하지는 않습니다. 즉, 도트 제품이 아닌 교차 제품 만 사용합니다. 원본 소스 코드는 공식 GLM 저장소의 type_quat.inl 파일 에서 찾을 수 있습니다.이 파일operator* 에는 쿼터니언과 벡터 ( vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v))
j00hi

7

우선, q ^ (-1)은 -q / magnitude (q)가 아닙니다. 그것은 q * / (magnitude (q)) ^ 2입니다 (q *는 켤레입니다; 이것은 실제 요소를 제외한 모든 구성 요소를 무효화합니다). 물론 모든 쿼터니언이 이미 정규화되어 있으면 회전 시스템에있을 경우 그 크기만큼 나눌 수 있습니다.

벡터의 곱셈에 대해서는 쿼트의 실제 성분을 0으로 설정하고 ijk 성분을 벡터의 xyz로 설정하여 벡터를 쿼터니언으로 확장하면됩니다. 그런 다음 쿼터니언 곱셈을 수행하여 v '를 얻은 다음 ijk 구성 요소를 다시 추출합니다. v '의 실수 부분은 항상 0에서 부동 소수점 오류를 더하거나 뺀 값이어야합니다.


5

첫 번째 관찰 :의 반대 q는 아닙니다 -q/magnitude(q). 그것은 완전히 잘못되었습니다. 쿼터니언을 사용한 회전은 이러한 4D 복소수의 등가물이 단일 규범을 가지며 따라서 4D 공간에서 S3 단위 구에 있다는 것을 의미합니다. 쿼트가 단일이라는 사실은 그 표준이 norm(q)^2=q*conjugate(q)=1그렇다는 것을 의미하며, 그 쿼트의 역수가 공액임을 의미합니다.

단위 쿼터니언이 q=(w,x,y,z)= (cos (t), sin (t) v )로 작성되면 해당 켤레는 conjugate(q)=(w,-x,-y,-z)= (cos (t),-sin (t) v )이며, 여기서 t 는 회전 각도의 절반이고 v 회전 축은 물론 단위 벡터입니다.

해밀턴 친구가 더 큰 차원의 복잡한 숫자를 사용하기로 결정했을 때, 그는 또한 좋은 속성을 발견했습니다. 예를 들어, 완전히 순수한 쿼터니언 q=(0,x,y,z)(스칼라 부분 w 없음) 을 사용하는 경우 해당 쓰레기를 벡터로 간주 할 수 있습니다 (실제로 사람들이 S3 구의 적도라고 부르는 것에 대한 쿼트, 즉 S2 구입니다! 요즘 우리가 19 세기 사람들이 기술적으로 얼마나 손상을 입 었는지 생각해 보면 마음이 구부러진 다. 해밀턴은 그 벡터를 그 쿼트 형태로 가져 왔습니다. 쿼트 v=(0,x,y,z)의 기하학적 특성을 고려한 일련의 실험을했습니다.

INPUT: _v=(x,y,z)_ a random 3D vector to rotate about an __u__ unit axis by an angle of _theta_

OUTPUT: q*(0,_v_)*conjugate(q)

어디에

 q = (cos(theta/2), sin(theta/2)*u)
 conjugate(q) = inverse(q) = (cos(theta/2), -sin(theta/2)*u)
 norm(q)=magnitude(q)=|q|=1

관찰 : q * (0, v) * conj (q)는 (0, v ') 형식의 다른 쿼트 여야합니다. 왜 이런 일이 발생하는지에 대한 복잡한 설명은하지 않겠지 만,이 방법을 통해 순수한 가상의 쿼터니언 (또는 우리의 경우 벡터!)을 회전 시키면 비슷한 종류의 객체를 얻을 수 있습니다 : 순수한 상상의 쿼트. 그리고 당신은 그 가상의 결과를 당신의 결과로 받아들입니다. 거기에는 너트 쉘에 쿼터니언이있는 멋진 회전 세계가 있습니다.

참고 : 남용 된 문구로 뛰어 들어가는 사람 : 쿼트는 짐벌 락을 피하기 때문에 좋습니다. 먼저 상상력을 풀어야합니다 !! 쿼트는 단순한 "우아한"수학적 장치이며 다른 접근 방식을 사용하여 완전히 피할 수 있습니다. 축 앵글 접근과 완전히 기하학적으로 동등한 것을 알 수 있습니다.

코드 : 내가 상상 하는 C ++ 라이브러리 는 다소 단순하지만, 3D 그래픽 실험가가 그것을 배우기 위해 15 분 이상 낭비 할 필요가없는 모든 매트릭스, 벡터 및 쿼트 연산이 있습니다. 여기에서 작성한 것을 테스트 할 수 있습니다. C ++ 초보자가 아닌 경우 15 분 안에. 행운을 빕니다!


메모 +1 나는 대부분의 사람들이 시도하면 실제 짐벌 잠금을 달성 할 수 없을 것이라고 내기를 걸었다. 그것은 회전을 수행 할 때 예기치 않은 행동에 대한 모든 문구를 잡았습니다.
Steve H

대부분의 사람들은 적절한 짐벌 메커니즘을 구축 할 수 없으며 3 개의 회전 행렬을 함께 연결하면 자동으로 "오일러 각도"표현으로 끝날 것이라고 생각합니다. 역 운동학을 수행하려고 할 때 중복성을 경험할 수있는 조인트 (실제로 원하는 방향을 생성하는 데 필요한 것보다 더 많은 자유도를 가짐). 글쎄요, 그것은 또 다른 주제입니다. 그러나 저는 CG 프로그래머들 사이에서이 "
전통적인

Nitpickery : 축 각도는 두 표현이 SO (3)의 모든 회전을 고유하게 나타낼 수 있다는 점에서 동일하지만 (일반적인 이중 덮개를 모듈로 사용) 모듈 사이에는 거의 사소한 변환이 있습니다. 매트릭스가 아닌 다른 모든 표현보다 작성하기가 훨씬 쉽다는 장점이 있습니다.
Steven Stadnicki

특히 연산자 오버로드를 사용할 때 객체 지향 프로그래밍 언어에서 훌륭한 동작으로 인해 작성하기가 더 쉽다는 장점이 있습니다. 확실하지 않지만 아마도 구형 보간 속성도 축 각도에 대해 유지됩니다 (SQUAD는 제외하고?!).
teodron


-1

나는 이것을 손으로 해결하려고 노력했으며 다음 방정식 / 방법을 생각해 냈습니다.

// inside quaterion class
// quaternion defined as (r, i, j, k)
Vector3 rotateVector(const Vector3 & _V)const{
    Vector3 vec();   // any constructor will do
    vec.x = 2*(r*_V.z*j + i*_V.z*k - r*_V.y*k + i*_V.y*j) + _V.x*(r*r + i*i - j*j - k*k);
    vec.y = 2*(r*_V.x*k + i*_V.x*j - r*_V.z*i + j*_V.z*k) + _V.y*(r*r - i*i + j*j - k*k);
    vec.z = 2*(r*_V.y*i - r*_V.x*j + i*_V.x*k + j*_V.y*k) + _V.z*(r*r - i*i - j*j + k*k);
    return vec;
}

누군가가 mt deriviation을 살펴 본다면 http://pastebin.com/8QHQqGbv를 사용 했다면 측면 스크롤을 지원하는 텍스트 편집기로 복사하는 것이 좋습니다.

내 표기법에서 나는 q ^ (-1)을 사용하여 켤레, 역수 및 다른 식별자를 의미했지만 후속 할 수 있기를 바랍니다. 나는 벡터의 실제 부분을 증명할 때 특히 대부분이 사라질 것이라고 생각합니다.

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