はじめに
単位二重四元数が表す剛体変換を行列に変換することを考えます。
剛体変換には、「回転後、平行移動」と「平行移動後、回転」の2通りがありました。
それぞれで2通りの方法で行列に変換し、それが一致することを示そうと思います。なので、4通りの導出を以下に記載します。
剛体変換を「回転後、平行移動」すると見立てた場合
回転後、平行移動すると見立てた場合、 を使って、 は以下のようにも書けます。
このとき、 は を使って、以下のように表せます。
また、 を で変換すると、以下のようになります。
の導出については、二重四元数による剛体変換 の記事を参照してください。を の成分を使って表します。
に対応する平行移動ベクトルは、以下のようになります。
より、平行移動に対する行列は以下のようになります。
と に対応する回転行列を掛けたものが求める行列です。( に対応する回転行列については、四元数による回転の記事を参照してください。)
で行列は求まったのですが、もう1つ別の手法で求めてみます。
を式変形します。
を計算します。(下で を計算しておりそれと同じ感じで計算できると思うので、ここは計算を省略します。)あとは から までの計算を行うと、同じ計算結果 が求まります。
このときに、先に平行移動するので平行移動行列は右側で、回転行列が左側であることに注意してください。
剛体変換を「平行移動後、回転」すると見立てた場合
平行移動後、回転すると見立てた場合、 を使って、 は以下のようにも書けます。
このとき、 は を使って、以下のように表せます。
また、 を で変換すると、以下のようになります。
の導出については、二重四元数による剛体変換 の記事を参照してください。を の成分を使って表します。
を計算します。
を計算します。
を計算します。
を計算します。
を計算します。
を計算します。
を計算します。
を計算します。
を に代入します。
は と一致します。
よって、後は「回転後、平行移動」すると見立てた場合と同じです。
ここで、式 をもう一度見てみます。
は だけ先に平行移動して、その後 で回転するという意味でもあります。はすでに で計算済みです。
あとは から までの計算を行うと、同じ計算結果 が求まります。
このときに、先に平行移動するので平行移動行列は右側で、回転行列が左側であることに注意してください。
SymPyで検算
「回転後、平行移動」すると見立てた場合と「平行移動後、回転」すると見立てた場合で計算結果は同じになりましたが、
念のためSymPyで検算もしておきました。
4通り計算しています。
代入メソッドsubsや因数分解メソッドfactorの使い方が良くないかもしれません。(今回式をキレイにするのに要素ごとに、因数分解して代入とやらなければうまくいきませんでした。)
from sympy.algebras.quaternion import Quaternion from sympy import * init_printing() def get_translate_matrix(t): m = simplify(t).to_Matrix() m[0] = factor(m[0]).subs(a_dot_b, 0) m[1] = factor(m[1]).subs(a_norm2, 1) m[1] = factor(m[1]).subs(a_dot_b, 0) m[2] = factor(m[2]).subs(a_norm2, 1) m[2] = factor(m[2]).subs(a_dot_b, 0) m[3] = factor(m[3]).subs(a_norm2, 1) m[3] = factor(m[3]).subs(a_dot_b, 0) nm = Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]]) nm = nm.col_insert(3, Matrix([m[1], m[2], m[3], 1])) nm = simplify(nm) return nm def refine_matrix(m): m0 = m[0].subs(w_a ** 2 + x_a ** 2, 1 - y_a ** 2- z_a ** 2) m5 = m[5].subs(w_a ** 2 + y_a ** 2, 1 - x_a ** 2- z_a ** 2) m10 = m[10].subs(w_a ** 2 + z_a ** 2, 1 - x_a ** 2- y_a ** 2) m3 = factor(m[3]).subs(a_norm2, 1) m3 = factor(m3) m7 = factor(m[7]).subs(a_norm2, 1) m7 = factor(m7) m11 = factor(m[11]).subs(a_norm2, 1) m11 = factor(m11) return Matrix([ [m0, m[1], m[2], m3], [m[4], m5, m[6], m7], [m[8], m[9], m10, m11], [m[12], m[13], m[14], m[15]], ]) var('w_a,x_a,y_a,z_a,w_b,x_b,y_b,z_b') a_norm2 = w_a ** 2 + x_a ** 2 + y_a ** 2 + z_a ** 2 a_dot_b = w_a * w_b + x_a * x_b + y_a * y_b + z_a * z_b a = Quaternion(w_a, x_a, y_a, z_a) b = Quaternion(w_b, x_b, y_b, z_b) ac = a.conjugate() r = a rc = ac # get rotation matrix m_r = r.to_rotation_matrix() m_r = m_r.subs(a_norm2, 1) m_r = m_r.row_insert(3, Matrix([[0, 0, 0]])) m_r = m_r.col_insert(3, Matrix([0, 0, 0, 1])) # rotate and translate t = 2 * b * ac rctr = rc * t * r m_t = get_translate_matrix(t) m_rctr = get_translate_matrix(rctr) display('rotate and translate(m_t * m_r)', refine_matrix(m_t * m_r)) display('rotate and translate(m_r * m_rctr)', refine_matrix(m_r * m_rctr)) # translate and rotate t = 2 * ac * b rtrc = r * t * rc m_t = get_translate_matrix(t) m_rtrc = get_translate_matrix(rtrc) display('translate and rotate(m_r * m_t)', refine_matrix(m_r * m_t)) display('translate and rotate(m_rtrc * m_r)', refine_matrix(m_rtrc * m_r))
実行結果