두 개의 벡터 u와 v가 있습니다. u에서 v 로의 회전을 나타내는 쿼터니언을 찾는 방법이 있습니까?
두 개의 벡터 u와 v가 있습니다. u에서 v 로의 회전을 나타내는 쿼터니언을 찾는 방법이 있습니까?
답변:
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);
q를 정규화하는 것을 잊지 마십시오.
Richard는 고유 한 회전이 없다는 점에 대해 옳지 만 위의 내용은 "가장 짧은 원호"를 제공해야합니다. 이는 아마도 필요한 것입니다.
sqrt((v1.Length ^ 2) * (v2.Length ^ 2))
는 v1.Length * v2.Length
. 현명한 결과를 내기 위해 이것의 변형을 얻을 수 없었습니다.
나는 임 브론디 르가 제시하려한다고 생각하는 해결책을 생각해 냈습니다. (사소한 실수가 있었지만, 아마도 불길한 다람쥐가 그것을 확인하는 데 어려움을 겪었을 것입니다).
다음과 같이 축을 중심으로 한 회전을 나타내는 쿼터니언을 구성 할 수 있다고 가정합니다.
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
에서 회전으로 보는 U 에 V 것은 우리가 직접 도트 및 벡터 곱의 결과와 같은 회전을 나타내는 쿼터니언을 생성 할 수있는 것처럼 수직 벡터 주위 세타 (벡터 사이의 각도)로 회전시킴으로써 달성 될 수 있고, 그것은 본다 ; 그러나 그것이 의미하는 바는 theta = angle / 2 입니다. 즉, 그렇게하면 원하는 회전의 두 배가됩니다.
한가지 해결책 사이 벡터 반쯤를 계산하는 U 와 V 와 도트의 외적 사용 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);
}
이것은 실제로 받아 들여진 답변에 제시된 솔루션이며 중간 벡터 솔루션보다 약간 더 빠른 것 같습니다 (내 측정에 의해 ~ 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)));
}
언급 된 문제는 잘 정의되어 있지 않습니다. 주어진 벡터 쌍에 대해 고유 한 회전이 없습니다. 예를 들어, u = <1, 0, 0> 및 v = <0, 1, 0> 인 경우를 고려하십시오 . u에서 v 로의 한 회전 은 z 축을 중심 으로 한 pi / 2 회전입니다. u에서 v 로의 또 다른 회전 은 벡터 <1, 1, 0> 주위의 파이 회전 입니다.
순수한 쿼터니언을 사용하여 벡터를 표현하지 않는 이유는 무엇입니까? 아마도 먼저 정규화하는 것이 좋습니다.
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
나는 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
.
angle
내적에서 그 가치를 얻기 때문에 이것이 전혀 작동하지 않을 것 입니다.
일부 답변은 외적이 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
angle
라디안 단위로 측정되는 쿼터니언의 축 각도 표현의 일부입니다.
알고리즘 관점에서 가장 빠른 솔루션은 의사 코드를 찾습니다.
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
}
단위 쿼터니언이 필요한지 확인하십시오 (보통 보간에 필요함).
참고 : 비 단위 쿼터니언은 일부 작업에 단위보다 빠르게 사용할 수 있습니다.
crossproduct
이러한 경우에는 유효하지 않으므로 먼저 및를 각각 확인dot(v1, v2) > 0.999999
하고dot(v1, v2) < -0.999999
평행 벡터에 대해 식별 쿼트를 반환하거나 반대 벡터에 대해 180도 회전 (모든 축에 대해)을 반환해야합니다.