あるベクトルから別のベクトルへの回転を表す四元数を見つける


105

2つのベクトル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を正規化することを忘れないでください。

リチャードはユニークな回転がないことについては正しいですが、上記はおそらくあなたが必要とするものである「最短の弧」を与えるはずです。


30
これは平行ベクトル(同じ方向または両方の方向を指す)の場合を処理しないことに注意してください。crossproductこれらのケースでは有効ではないため、最初にdot(v1, v2) > 0.999999dot(v1, v2) < -0.999999をそれぞれチェックして、平行ベクトルの場合はアイデンティティクアットを返すか、反対のベクトルの場合は(任意の軸を中心に)180度の回転を返す必要があります。
sinisterchipmunk

11
これの良い実装を見つけることができogre3dソースコード
ジョアンポルテラを

4
@sinisterchipmunk実際には、v1 = v2の場合、クロス積は(0,0,0)になり、wは正になり、正規化して正規化されます。gamedev.net/topic/…によれば、v1 = -v2とそのすぐ近くでも問題なく動作するはずです。
jpa 2012

3
誰かがこのテクニックをどのように機能させましたか?1つは、にsqrt((v1.Length ^ 2) * (v2.Length ^ 2))簡略化しv1.Length * v2.Lengthます。私は、これのバリエーションを何も得られず、賢明な結果を生み出すことができませんでした。
ジョセフ・トムソン

2
はい、これでうまくいきます。ソースコードを参照してください。L61は、ベクトルが反対方向を向いている場合に処理します(PIを返すか、@ jpaの発言に従ってIDを返します)。L67は並列ベクトルを処理します。数学的には不要ですが高速です。L72はPolaris878の答えです。両方のベクトルが単位長であると仮定します(sqrtを避けます)。単体テストも参照してください。
sinisterchipmunk

63

ハーフウェイベクトルソリューション

私は、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)));
}

12
+1:すばらしい!これは魅力として働いた。受け入れられる答えでなければなりません。
Rekin

1
Quaternion構文は、いくつかの例(Quaternion(xyz、w)およびQuaternion(w、xyz))で切り替えられます。最後のコードブロックでは、ラジアンと度が混合されて角度を表現しているようです(180対k_cos_theta + k)。
ギジェルモブラスコ2014年

1
Quaternion(float、Vector3)はスカラーベクトルからの構築ですが、Quaternion(Vector3、float)は軸角度からの構築です。おそらく混乱する可能性がありますが、私はそれが正しいと思います。それでも間違っていると思われる場合は修正してください!
ジョセフトムソン

出来た!ありがとう!しかし、私は上記の操作を実行するための別の同様でよく説明されたリンクを見つけました。私は記録のために共有すべきだと思った;)
罪人

1
@JosephThomsonハーフクォータニオン解はここから来ているようです
legends2k 2014

6

前述の問題は明確に定義されていません。特定のベクトルのペアに固有の回転はありません。たとえば、u = <1、0、0>およびv = < 0、1、0 >の場合を考えます。uからvへの1回転は、z軸を中心としたpi / 2回転です。uからvへの別の回転は、ベクトル< 1、1、0 >を中心としたpi回転です。


1
実際、無限の可能性のある答えはありませんか?「from」ベクトルを「to」ベクトルと位置合わせした後でも、結果をその軸を中心に自由に回転させることができるからですか?この選択を制約し、問題を明確にするために通常使用できる追加情報を知っていますか?
Doug McClean 2013年

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 ノルム
これは「左部門」と考えることができます。右除算、これはあなたが望むものではありません:
q rot、right = q 2 -1 q 1


2
私は迷っています。q1からq2への回転は、q_2 = q_rot q_1 q_rot ^ -1として計算されていませんか?
ヨータ

4

クォータニオンはあまり得意ではありません。しかし、私はこれに何時間も苦労し、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

2番目のパラメーターはangle、ラジアンで測定された、クォータニオンの軸角度表現の一部です。
Shital Shah

四元数をあるベクトルから別のベクトルに回転させるように求められました。角度はありません。最初に計算する必要があります。あなたの答えは角度の計算を含むべきです。乾杯!
Maksym Ganenko

これは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.