2つのベクトルuとvがあります。uからvへの回転を表すクォータニオンを見つける方法はありますか?
2つのベクトル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を正規化することを忘れないでください。
リチャードはユニークな回転がないことについては正しいですが、上記はおそらくあなたが必要とするものである「最短の弧」を与えるはずです。
sqrt((v1.Length ^ 2) * (v2.Length ^ 2))
簡略化しv1.Length * v2.Length
ます。私は、これのバリエーションを何も得られず、賢明な結果を生み出すことができませんでした。
私は、Imbrondirが提示しようとしていると考えられる解決策を思いつきました(マイナーなミスではありましたが、おそらくsinisterchipmunkが検証に失敗したのはそのためです)。
次のように、軸を中心とした回転を表すクォータニオンを作成できると仮定します。
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
また、2つの正規化ベクトルの内積と外積は次のとおりです。
dot == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z
uからvへの回転は、垂直ベクトルの周りをtheta(ベクトル間の角度)回転することで実現できますが、ドットとクロス積の結果からそのような回転を表すクォータニオンを直接作成できるように見えます; ただし、現状ではtheta = angle / 2であり、これを行うと必要な回転が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%高速ですが、私の言葉は無視します)。私のような他の人が説明に興味がある場合に備えて、ここに追加します。
基本的に、中間ベクトルを使用してクォータニオンを計算する代わりに、必要な回転が2倍になるクォータニオンを計算し(他のソリューションで詳しく説明されているように)、その度数とゼロ度の間のクォータニオンを見つけることができます。
前に説明したように、必要な回転の2倍の四元数は次のとおりです。
q.w == dot(u, v)
q.xyz == cross(u, v)
そして、ゼロ回転の四元数は次のとおりです。
q.w == 1
q.xyz == (0, 0, 0)
ハーフウェイクォータニオンの計算は、ベクトルの場合と同様に、クォータニオンを合計して結果を正規化するだけです。ただし、ベクトルの場合と同様に、クォータニオンは同じ大きさでなければなりません。そうでない場合、結果はより大きいマグニチュードを持つ四元数に向かって歪められます。
2つのベクトルのドットとクロス積から構成されるクォータニオンは、これらの積と同じ大きさになりますlength(u) * length(v)
。4つのコンポーネントすべてをこの係数で除算するのではなく、代わりに単位クォータニオンをスケールアップできます。そして、受け入れられた答えがを使用して問題を複雑にしているように見えるのはなぜか疑問に思っている場合sqrt(length(u) ^ 2 * length(v) ^ 2)
、それはベクトルの2乗の長さが長さよりも計算が速いため、1つの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への1回転は、z軸を中心としたpi / 2回転です。uからvへの別の回転は、ベクトル< 1、1、0 >を中心としたpi回転です。
純粋な四元数を使用してベクトルを表現しないのはなぜですか?最初にそれらを正規化することをお勧めします。
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 ノルム
これは「左部門」と考えることができます。右除算、これはあなたが望むものではありません:
q rot、right = q 2 -1 q 1
クォータニオンはあまり得意ではありません。しかし、私はこれに何時間も苦労し、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度の回転を返す必要があります。