Quaternion

永井 忠一 2024.11.30


参照した資料

クォータニオンの微積分

クォータニオン \( q(t) \) の時間微分\[ \begin{align} \frac{d q(t)}{dt} = \dot{q}(t) &= \lim_{h\to 0}\frac{q(t+h) - q(t)}{h} \\ & = \lim_{h\to 0}\frac{\begin{pmatrix} n_x\sin\frac{\dot\theta h}{2} \\ n_y\sin\frac{\dot\theta h}{2} \\ n_z\sin\frac{\dot\theta h}{2} \\ \cos\frac{\dot\theta h}{2} \end{pmatrix}q(t) - q(t)}{h} \\ & = \lim_{h\to 0}\frac{\begin{pmatrix} n_x\sin\frac{\dot\theta h}{2} \\ n_y\sin\frac{\dot\theta h}{2} \\ n_z\sin\frac{\dot\theta h}{2} \\ \cos\frac{\dot\theta h}{2} \end{pmatrix}q(t) - \begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}q(t)}{h} \\ & = \lim_{h\to 0}\frac{\left(\begin{pmatrix} n_x\sin\frac{\dot\theta h}{2} \\ n_y\sin\frac{\dot\theta h}{2} \\ n_z\sin\frac{\dot\theta h}{2} \\ \cos\frac{\dot\theta h}{2} \end{pmatrix} - \begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}\right)q(t)}{h} \\ & = \lim_{h\to 0}\frac{\begin{pmatrix} n_x\sin\frac{\dot\theta h}{2} \\ n_y\sin\frac{\dot\theta h}{2} \\ n_z\sin\frac{\dot\theta h}{2} \\ \cos\left(\frac{\dot\theta h}{2}\right) - 1 \end{pmatrix}q(t)}{h} \\ & = \lim_{h\to 0}\begin{pmatrix} \frac{n_x\sin\frac{\dot\theta h}{2}}{h} \\ \frac{n_y\sin\frac{\dot\theta h}{2}}{h} \\ \frac{n_z\sin\frac{\dot\theta h}{2}}{h} \\ \frac{\cos\left(\frac{\dot\theta h}{2}\right) - 1}{h} \end{pmatrix}q(t) \\ & = \begin{pmatrix} \lim\limits_{h\to 0}\frac{n_x\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{n_y\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{n_z\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{\cos\left(\frac{\dot\theta h}{2}\right) - 1}{h} \end{pmatrix}q(t) \\ &= \begin{pmatrix} \frac{n_x\dot\theta}{2} \\ \frac{n_y\dot\theta}{2} \\ \frac{n_z\dot\theta}{2} \\ 0 \end{pmatrix}q(t) \\ &= \frac{1}{2}\begin{pmatrix} n_x\dot\theta \\ n_y\dot\theta \\ n_z\dot\theta \\ 0 \end{pmatrix}q(t) = \frac{1}{2}\begin{pmatrix} \omega_x \\ \omega_y \\ \omega_z \\ 0 \end{pmatrix}q(t) \end{align} \]

limit の計算(Maxima による記号計算)

Maxima
$ maxima

Maxima 5.46.0 https://maxima.sourceforge.io
using Lisp GNU Common Lisp (GCL) GCL 2.6.14 git tag Version_2_6_15pre7
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
The function bug_report() provides bug reporting information.

(%i1) limit(n_x*sin((\\dot\\theta/2)*h)/h, h, 0);
                                \dot\theta n_x
(%o1)                           --------------
                                      2
(%i2) limit(n_y*sin((\\dot\\theta/2)*h)/h, h, 0);
                                \dot\theta n_y
(%o2)                           --------------
                                      2
(%i3) limit(n_z*sin((\\dot\\theta/2)*h)/h, h, 0);
                                \dot\theta n_z
(%o3)                           --------------
                                      2
(%i4) limit((cos((\\dot\\theta/2)*h) - 1)/h, h, 0);
(%o4)                                  0
\[ \begin{pmatrix} \lim\limits_{h\to 0}\frac{n_x\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{n_y\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{n_z\sin\frac{\dot\theta h}{2}}{h} \\ \lim\limits_{h\to 0}\frac{\cos\left(\frac{\dot\theta h}{2}\right) - 1}{h} \end{pmatrix} = \begin{pmatrix} \frac{n_x\dot\theta}{2} \\ \frac{n_y\dot\theta}{2} \\ \frac{n_z\dot\theta}{2} \\ 0 \end{pmatrix} \]

クォータニオン積分(Quaternion integration)

《天下り》\[ q(t) = \int \dot q(t)\, dt = \int w\, dt + \left(\int x\, dt\right)i + \left(\int y\, dt\right)j + \left(\int z\, dt\right)k \]

離散の式\[ q(t) = \sum \dot q(t)\varDelta t = \sum w\varDelta t + \left(\sum x\varDelta t\right)i + \left(\sum y\varDelta t\right)j + \left(\sum z\varDelta t\right)k \]


Quaternion

クォータニオンは数。次の式で書かれる\[ q = w + x i + y j + z k \]

また、\[ q = (w, x, y, z) \]という形でも書かれる。\( w \) の位置には、流儀がある\[ q = (x, y, z, w) \]

縦に書くこともある\[ q = \left(\begin{array}{c} w \\ x \\ y \\ z \end{array}\right), \quad q = \left(\begin{array}{c} x \\ y \\ z \\ w \end{array}\right) \]

呼び方:

Quaternion クラス

ライブラリ、パッケージのインストール

Linux apt
$ sudo apt install libglm-dev libbullet-dev
$ sudo apt install octave-quaternion

ドキュメント

クォータニオンの演算

クォータニオンの和

C/C++ 言語
OpenGL Mathematics (GLM)Bullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f), b(.5f, .6f, .7f, .8f);
	show(a + b);
}

$ g++ -Wall quat_addition_glm.cpp && ./a.out
0.600000 + 0.800000i + 1.000000j + 1.200000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f), b(.6f, .7f, .8f, .5f);
	show(a + b);
}

$ g++ -Wall quat_addition_bt.cpp && ./a.out
0.600000 + 0.800000i + 1.000000j + 1.200000k

Octave の quaternion パッケージを利用して検算

Octave
$ octave --no-gui
GNU Octave, version 8.4.0
Copyright (C) 1993-2023 The Octave Project Developers.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.

Octave was configured for "x86_64-pc-linux-gnu".

Additional information about Octave is available at https://www.octave.org.

Please contribute if you find this software useful.
For more information, visit https://www.octave.org/get-involved.html

Read https://www.octave.org/bugs.html to learn how to submit bug reports.
For information about changes from previous versions, type 'news'.


octave:1> pkg list quaternion
Package Name  | Version | Installation directory
--------------+---------+-----------------------
  quaternion  |   2.4.0 | /usr/share/octave/packages/quaternion-2.4.0

octave:2> pkg load quaternion
octave:3> quaternion(.1, .2, .3, .4) + quaternion(.5, .6, .7, .8)
ans = 0.6 + 0.8i + 1j + 1.2k

ゼロ・クォータニオン(和の単位元)\( (0, 0, 0, 0) \)

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f), zero(0.f, 0.f, 0.f, 0.f);
	show(a + zero);
}

$ g++ -Wall quat_zero_glm.cpp && ./a.out
0.100000 + 0.200000i + 0.300000j + 0.400000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f), zero(0.f, 0.f, 0.f, 0.f);
	show(a + zero);
}

$ g++ -Wall quat_zero_bt.cpp && ./a.out
0.100000 + 0.200000i + 0.300000j + 0.400000k
Octave
octave:1> pkg load quaternion
octave:2> quaternion(.1, .2, .3, .4) + quaternion(0., 0., 0., 0.)
ans = 0.1 + 0.2i + 0.3j + 0.4k

クォータニオンの符号反転。単項のマイナス(和の逆元)

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	show(-a);
}

$ g++ -Wall quat_negative_glm.cpp && ./a.out
-0.100000 - 0.200000i - 0.300000j - 0.400000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	show(-a);
}

$ g++ -Wall quat_negative_bt.cpp && ./a.out
-0.100000 - 0.200000i - 0.300000j - 0.400000k
Octave
octave:1> pkg load quaternion
octave:2> -quaternion(.1, .2, .3, .4)
ans = -0.1 - 0.2i - 0.3j - 0.4k

クォータニオンの積

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f), b(.5f, .6f, .7f, .8f);
	show(a*b);
	show(b*a);
}

$ g++ -Wall quat_product_glm.cpp && ./a.out
-0.600000 + 0.120000i + 0.300000j + 0.240000k
-0.600000 + 0.200000i + 0.140000j + 0.320000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f), b(.6f, .7f, .8f, .5f);
	show(a*b);
	show(b*a);
}

$ g++ -Wall quat_product_bt.cpp && ./a.out
-0.600000 + 0.120000i + 0.300000j + 0.240000k
-0.600000 + 0.200000i + 0.140000j + 0.320000k
Octave
octave:1> pkg load quaternion
octave:2> quaternion(.1, .2, .3, .4)*quaternion(.5, .6, .7, .8)
ans = -0.6 + 0.12i + 0.3j + 0.24k
octave:3> quaternion(.5, .6, .7, .8)*quaternion(.1, .2, .3, .4)
ans = -0.6 + 0.2i + 0.14j + 0.32k

(※クォータニオンの積は非可換)

恒等クォータニオン(identity quaternion)(クォータニオンの積の単位元)\( (1, 0, 0, 0) \)

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f), identity(1.f, 0.f, 0.f, 0.f);
	show(identity*a);
	show(a*identity);
}

$ g++ -Wall quat_identity_glm.cpp && ./a.out
0.100000 + 0.200000i + 0.300000j + 0.400000k
0.100000 + 0.200000i + 0.300000j + 0.400000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f), identity(0.f, 0.f, 0.f, 1.f);
	show(identity*a);
	show(a*identity);
}

$ g++ -Wall quat_identity_bt.cpp && ./a.out
0.100000 + 0.200000i + 0.300000j + 0.400000k
0.100000 + 0.200000i + 0.300000j + 0.400000k
Octave
octave:1> pkg load quaternion
octave:2> quaternion(1., 0., 0., 0.)*quaternion(.1, .2, .3, .4)
ans = 0.1 + 0.2i + 0.3j + 0.4k
octave:3> quaternion(.1, .2, .3, .4)*quaternion(1., 0., 0., 0.)
ans = 0.1 + 0.2i + 0.3j + 0.4k

また、identity quaternion を得るための「quat_identity() 関数、getIdentity() メソッド」がある

(※GLM では「Experimental extensions」扱いなので、『glm/gtx/quaternion.hpp』をインクルードする必要がある)

クォータニオンのノルム(norm)\[ \lvert q \rvert = \sqrt{w^2 + x^2 + y^2 + z^2} \]

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>

#include <glm/gtc/quaternion.hpp>

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	printf("%f\n", glm::length(a));
}

$ g++ -Wall quat_norm_glm.cpp && ./a.out
0.547723
#include <stdio.h>

#include <bullet/LinearMath/btQuaternion.h>

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	printf("%f\n", a.length());
}

$ g++ -Wall quat_norm_bt.cpp && ./a.out
0.547723
Octave
octave:1> pkg load quaternion
octave:2> norm(quaternion(.1, .2, .3, .4))
ans = 0.5477

また、GLM と Bullet LinearMath ともに、\( \lvert q \rvert^2\) を求める「length2() 関数、メソッド」がある

(※GLM で length2() 関数は「Experimental extensions」扱いなので、使用するためには『glm/gtx/quaternion.hpp』をインクルードする必要がある)

単位クォータニオン(unit quaternion)\[ w^2 + x^2 + y^2 + z^2 = 1 \](ノルムが1のクォータニオン)

クォータニオンの正規化

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	show(glm::normalize(a));
	printf("%f\n", glm::length(glm::normalize(a)));
}

$ g++ -Wall quat_normalization_glm.cpp && ./a.out
0.182574 + 0.365148i + 0.547723j + 0.730297k
1.000000
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	show(a.normalize());
	printf("%f\n", a.length());
}

$ g++ -Wall quat_normalization_bt.cpp && ./a.out
0.182574 + 0.365148i + 0.547723j + 0.730297k
1.000000
Octave
octave:1> pkg load quaternion
octave:2> unit(quaternion(.1, .2, .3, .4))
ans = 0.1826 + 0.3651i + 0.5477j + 0.7303k
octave:3> norm(unit(quaternion(.1, .2, .3, .4)))
ans = 1.0000

(Bullet LinearMath には、イミュータブル版の「normalized() メソッド」も用意されている)

共役クォータニオン(conjugate quaternion)\[ q^* = w - x i - y j - z k \](虚部の符号を反転したもの)

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	show(glm::conjugate(a));
}

$ g++ -Wall quat_conjugate_bt.cpp && ./a.out
0.100000 - 0.200000i - 0.300000j - 0.400000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	show(a.inverse());
}

$ g++ -Wall quat_conjugate_glm.cpp && ./a.out
0.100000 - 0.200000i - 0.300000j - 0.400000k
Octave
octave:1> pkg load quaternion
octave:2> conj(quaternion(.1, .2, .3, .4))
ans = 0.1 - 0.2i - 0.3j - 0.4k

(※Bullet LinearMath では、「inverse() メソッド」は共役クォータニオンを求める)

逆クォータニオン。クォータニオンの逆数(クォータニオンの積の逆元)\[ q^{-1} = \frac{q^*}{\lvert q \rvert^2} \]

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	show(glm::inverse(a));
}

$ g++ -Wall quat_inverse_glm.cpp && ./a.out
0.333333 - 0.666667i - 1.000000j - 1.333333k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	show(a.inverse()/a.length2());
}

$ g++ -Wall quat_inverse_bt.cpp && ./a.out
0.333333 - 0.666667i - 1.000000j - 1.333333k
Octave
octave:1> pkg load quaternion
octave:2> inv(quaternion(.1, .2, .3, .4))
ans = 0.3333 - 0.6667i - 1j - 1.333k

(逆クォータニオンを求めるメソッドが用意されていない場合には、\( {q^*}/{\lvert q \rvert^2} \) として求める)

もし、\( q \) が単位クォータニオンならば、\( \lvert q \rvert = 1 \) より\[ q^{-1} = q^* \]となる

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f);
	a = glm::normalize(a);
	show(glm::inverse(a));
	show(glm::conjugate(a));
}

$ g++ -Wall quat_unit_glm.cpp && ./a.out
0.182574 - 0.365148i - 0.547723j - 0.730297k
0.182574 - 0.365148i - 0.547723j - 0.730297k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f);
	a.normalize();
	show(a.inverse()/a.length2());
	show(a.inverse());
}

$ g++ -Wall quat_unit_bt.cpp && ./a.out
0.182574 - 0.365148i - 0.547723j - 0.730297k
0.182574 - 0.365148i - 0.547723j - 0.730297k
Octave
octave:1> pkg load quaternion
octave:2> inv(unit(quaternion(.1, .2, .3, .4)))
ans = 0.1826 - 0.3651i - 0.5477j - 0.7303k
octave:3> conj(unit(quaternion(.1, .2, .3, .4)))
ans = 0.1826 - 0.3651i - 0.5477j - 0.7303k

ゼロ・クォータニオンとの積

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat a(.1f, .2f, .3f, .4f), zero(0.f, 0.f, 0.f, 0.f);
	show(zero*a);
	show(a*zero);
}

$ g++ -Wall quat_zero-product_glm.cpp && ./a.out
0.000000 + 0.000000i + 0.000000j + 0.000000k
0.000000 + 0.000000i + 0.000000j + 0.000000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion a(.2f, .3f, .4f, .1f), zero(0.f, 0.f, 0.f, 0.f);
	show(zero*a);
	show(a*zero);
}

$ g++ -Wall quat_zero-product_bt.cpp && ./a.out
0.000000 + 0.000000i + 0.000000j + 0.000000k
0.000000 + 0.000000i + 0.000000j + 0.000000k
Octave
octave:1> pkg load quaternion
octave:2> quaternion(0., 0., 0., 0.)*quaternion(.1, .2, .3, .4)
ans = 0 + 0i + 0j + 0k
octave:3> quaternion(.1, .2, .3, .4)*quaternion(0., 0., 0., 0.)
ans = 0 + 0i + 0j + 0k

(零因子は存在しない)

クォータニオンによる3次元の回転

《→ 以前のページ

単位ベクトル \( \vec n \) が表す任意の回転軸に関する \( \theta \) の回転を表すクォータニオンは\[ q = \left(cos\frac{\theta}{2},\, \vec n\sin\frac{\theta}{2}\right) \]のように書ける(「\( {\theta}/{2} \)」は、『half angle』と呼ばれる)

クォータニオンを使って位置ベクトルを回転\[ v^\prime = qvq^* \]ここで、\( v \) は位置ベクトル \( \vec v \) を純粋クォータニオン \( v = (0,\, \vec v) \) で表現したもの(実部が0のクォータニオンを純粋クォータニオン。もしくは、純虚クォータニオン(pure imaginary quaternion)

(\( q^* \) は \( q \) の共役クォータニオン)

演算の結果 \( v^\prime \) は実部が必ず0になり(純粋クォータニオンになる)、虚部には変換後の3次元座標(位置ベクトル)が格納される

回転軸と回転角を指定して、回転を表現するクォータニオンを作成

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::vec3 axis = glm::normalize(glm::vec3(1.f, 2.f, 3.f)); // must be normalized
	float angle = M_PI/4;
	glm::quat rot = glm::angleAxis(angle, axis/* normalized axis */);
	show(rot);
}

$ g++ -Wall quat_axis-angle_glm.cpp && ./a.out
0.923880 + 0.102276i + 0.204553j + 0.306829k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btVector3 axis = btVector3(1.f, 2.f, 3.f);
	float angle = M_PI/4;
	btQuaternion rot = btQuaternion(axis, angle);
	show(rot);
}

$ g++ -Wall quat_axis-angle_bt.cpp && ./a.out
0.923880 + 0.102276i + 0.204553j + 0.306829k
Octave
octave:1> pkg load quaternion
octave:2> axis = [1, 2, 3];
octave:3> angle = pi/4;
octave:4> rot2q(axis, angle)
warning: rot2q: ||axis|| != 1, normalizing
warning: called from
    rot2q at line 86 column 5

ans = 0.9239 + 0.1023i + 0.2046j + 0.3068k

octave:5> axis = axis/norm(axis);
octave:6> rot2q(axis, angle)
ans = 0.9239 + 0.1023i + 0.2046j + 0.3068k

(Bullet LinearMath では、btQuaternion クラスのコンストラクターで、回転軸のベクトルの正規化を行っている)

回転を表すクォータニオンのノルムは1

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

int main(int argc, char *argv[]) {
	glm::quat roll60 = glm::angleAxis((float)(M_PI/3), glm::vec3(1.f, 0.f, 0.f));
	glm::quat pitch60 = glm::angleAxis((float)(M_PI/3), glm::vec3(0.f, 1.f, 0.f));
	glm::quat yaw60 = glm::angleAxis((float)(M_PI/3), glm::vec3(0.f, 0.f, 1.f));
	printf("%f\n", glm::length(roll60));
	printf("%f\n", glm::length(pitch60));
	printf("%f\n", glm::length(yaw60));
	printf("%f\n", glm::length(roll60*roll60));
	printf("%f\n", glm::length(roll60*roll60*roll60));
	printf("%f\n", glm::length(pitch60*pitch60));
	printf("%f\n", glm::length(pitch60*pitch60*pitch60));
	printf("%f\n", glm::length(yaw60*yaw60));
	printf("%f\n", glm::length(yaw60*yaw60*yaw60));
	printf("%f\n", glm::length(pitch60*roll60));
	printf("%f\n", glm::length(yaw60*pitch60*roll60));
}

$ g++ -Wall quat_rotation_glm.cpp && ./a.out
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

int main(int argc, char *argv[]) {
	btQuaternion roll60 = btQuaternion(btVector3(1.f, 0.f, 0.f), (float)(M_PI/3));
	btQuaternion pitch60 = btQuaternion(btVector3(0.f, 1.f, 0.f), (float)(M_PI/3));
	btQuaternion yaw60 = btQuaternion(btVector3(0.f, 0.f, 1.f), (float)(M_PI/3));
	printf("%f\n", roll60.length());
	printf("%f\n", pitch60.length());
	printf("%f\n", yaw60.length());
	printf("%f\n", (roll60*roll60).length());
	printf("%f\n", (roll60*roll60*roll60).length());
	printf("%f\n", (pitch60*pitch60).length());
	printf("%f\n", (pitch60*pitch60*pitch60).length());
	printf("%f\n", (yaw60*yaw60).length());
	printf("%f\n", (yaw60*yaw60*yaw60).length());
	printf("%f\n", (pitch60*roll60).length());
	printf("%f\n", (yaw60*pitch60*roll60).length());
}

$ g++ -Wall quat_rotation_bt.cpp && ./a.out
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
1.000000
Octave
octave:1> pkg load quaternion
octave:2> roll60 = rot2q([1, 0, 0], pi/3);
octave:3> pitch60 = rot2q([0, 1, 0], pi/3);
octave:4> yaw60 = rot2q([0, 0, 1], pi/3);
octave:5> norm(roll60)
ans = 1
octave:6> norm(pitch60)
ans = 1
octave:7> norm(yaw60)
ans = 1
octave:8> norm(roll60*roll60)
ans = 1
octave:9> norm(roll60*roll60*roll60)
ans = 1
octave:10> norm(pitch60*pitch60)
ans = 1
octave:11> norm(pitch60*pitch60*pitch60)
ans = 1
octave:12> norm(yaw60*yaw60)
ans = 1
octave:13> norm(yaw60*yaw60*yaw60)
ans = 1
octave:14> norm(pitch60*roll60)
ans = 1
octave:15> norm(yaw60*pitch60*roll60)
ans = 1

クォータニオンの内積

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat roll90 = glm::angleAxis((float)(M_PI/2), glm::vec3(1.f, 0.f, 0.f));
	glm::quat pitch90 = glm::angleAxis((float)(M_PI/2), glm::vec3(0.f, 1.f, 0.f));
	glm::quat yaw90 = glm::angleAxis((float)(M_PI/2), glm::vec3(0.f, 0.f, 1.f));
	show(yaw90*pitch90*roll90);
	show(pitch90);
	printf("%f\n", glm::dot(yaw90*pitch90*roll90, pitch90));
	printf("----\n");
	show(roll90);
	show(roll90*roll90*roll90*roll90*roll90);
	printf("%f\n", glm::dot(roll90*roll90*roll90*roll90*roll90, roll90));
	printf("%f\n", fabs(glm::dot(roll90*roll90*roll90*roll90*roll90, roll90)));
}

$ g++ -Wall quat_dot-product_glm.cpp && ./a.out
0.707107 + 0.000000i + 0.707107j + 0.000000k
0.707107 + 0.000000i + 0.707107j + 0.000000k
1.000000
----
0.707107 + 0.707107i + 0.000000j + 0.000000k
-0.707107 - 0.707107i + 0.000000j + 0.000000k
-1.000000
1.000000
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion roll90 = btQuaternion(btVector3(1.f, 0.f, 0.f), (float)(M_PI/2));
	btQuaternion pitch90 = btQuaternion(btVector3(0.f, 1.f, 0.f), (float)(M_PI/2));
	btQuaternion yaw90 = btQuaternion(btVector3(0.f, 0.f, 1.f), (float)(M_PI/2));
	show(yaw90*pitch90*roll90);
	show(pitch90);
	printf("%f\n", (yaw90*pitch90*roll90).dot(pitch90));
	printf("----\n");
	show(roll90);
	show(roll90*roll90*roll90*roll90*roll90);
	printf("%f\n", (roll90*roll90*roll90*roll90*roll90).dot(roll90));
	printf("%f\n", fabs((roll90*roll90*roll90*roll90*roll90).dot(roll90)));
}

$ g++ -Wall quat_dot-product_bt.cpp && ./a.out
0.707107 + 0.000000i + 0.707107j + 0.000000k
0.707107 + 0.000000i + 0.707107j + 0.000000k
1.000000
----
0.707107 + 0.707107i + 0.000000j + 0.000000k
-0.707107 - 0.707107i + 0.000000j + 0.000000k
-1.000000
1.000000
Octave
octave:1> pkg load quaternion
octave:2> roll90 = rot2q([1, 0, 0], pi/2);
octave:3> pitch90 = rot2q([0, 1, 0], pi/2);
octave:4> yaw90 = rot2q([0, 0, 1], pi/2);
octave:5> yaw90*pitch90*roll90
ans = 0.7071 + 5.551e-17i + 0.7071j + 5.551e-17k
octave:6> pitch90
pitch90 = 0.7071 + 0i + 0.7071j + 0k
octave:7> (yaw90*pitch90*roll90)*pitch90'
ans = 1 + 7.85e-17i + 1.11e-16j + 6.163e-33k
octave:8> ((yaw90*pitch90*roll90)*pitch90').w
ans = 1.0000
octave:9> disp("----")
----
octave:10> roll90
roll90 = 0.7071 + 0.7071i + 0j + 0k
octave:11> roll90*roll90*roll90*roll90*roll90
ans = -0.7071 - 0.7071i + 0j + 0k
octave:12> (roll90*roll90*roll90*roll90*roll90)*roll90'
ans = -1 + 2.776e-16i + 0j + 0k
octave:13> ((roll90*roll90*roll90*roll90*roll90)*roll90').w
ans = -1
octave:14> abs((roll90*roll90*roll90*roll90*roll90)*roll90')
ans = 1

(Octave の dot() 関数はクォータニオンの内積を計算できない。Octave で「'」演算子は複素共役転置を求める演算子でクォータニオンに対しても使用できるため、「'」演算子を利用して内積を計算している。また、「.w」は実部へのアクセッサー)

(※クォータニオンの内積が1に近ければ同じ回転(姿勢)をしているが、クォータニオンには二価性があるため、fabs() 関数で絶対値をとってから内積の結果を評価する)

クォータニオンの二価性

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	glm::quat roll90 = glm::angleAxis((float)(M_PI/2), glm::vec3(1.f, 0.f, 0.f));
	glm::quat rot = glm::quat(1.f, 0.f, 0.f, 0.f); // initial attitude
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	// ...
}

$ g++ -Wall quat_two-valued_glm.cpp && ./a.out
1.000000 + 0.000000i + 0.000000j + 0.000000k
-1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 + 0.000000i + 0.000000j + 0.000000k
-1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 + 0.000000i + 0.000000j + 0.000000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

int main(int argc, char *argv[]) {
	btQuaternion roll90 = btQuaternion(btVector3(1.f, 0.f, 0.f), (float)(M_PI/2));
	btQuaternion rot = btQuaternion(0.f, 0.f, 0.f, 1.f); // initial attitude
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	rot = roll90*roll90*roll90*roll90*rot;
	show(rot);
	// ...
}

$ g++ -Wall quat_two-valued_bt.cpp && ./a.out
1.000000 + 0.000000i + 0.000000j + 0.000000k
-1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 + 0.000000i + 0.000000j + 0.000000k
-1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 + 0.000000i + 0.000000j + 0.000000k
Octave
octave:1> pkg load quaternion
octave:2> roll90 = rot2q([1, 0, 0], pi/2);
octave:3> rot = quaternion(1, 0, 0, 0)
rot = 1 + 0i + 0j + 0k
octave:4> rot = roll90*roll90*roll90*roll90*rot
rot = -1 + 2.776e-16i + 0j + 0k
octave:5> rot = roll90*roll90*roll90*roll90*rot
rot = 1 - 5.551e-16i + 0j + 0k
octave:6> rot = roll90*roll90*roll90*roll90*rot
rot = -1 + 8.327e-16i + 0j + 0k
octave:7> rot = roll90*roll90*roll90*roll90*rot
rot = 1 - 1.11e-15i + 0j + 0k

(Octave の表示では、浮動小数点数の演算誤差が見えているが、非常に小さい値になっているので無視できる)

(※回転(姿勢)を表すクォータニオンは、初期の 0° のものと、360° 回転したものとでは表現が異なっている。720° 回転して(2回転して)もとの表現に戻る)

回転行列とクォータニオンとの相互変換

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>
#include <glm/mat3x3.hpp>

static void show(const glm::quat &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.w, MY_SIGN(q.x), fabsf(q.x), MY_SIGN(q.y), fabsf(q.y), MY_SIGN(q.z), fabsf(q.z));
#undef MY_SIGN
}

static void show(const glm::mat3x3 &r) {
	printf("%f %f %f\n", r[0][0], r[1][0], r[2][0]);
	printf("%f %f %f\n", r[0][1], r[1][1], r[2][1]);
	printf("%f %f %f\n", r[0][2], r[1][2], r[2][2]);
}

int main(int argc, char *argv[]) {
	glm::quat identity(1.f, 0.f, 0.f, 0.f);
	glm::mat3x3 r = mat3_cast(identity);
	show(r);
	glm::quat q = quat_cast(r);
	show(q);
#if 1
	r = mat3_cast(-identity);
	show(r);
	q = quat_cast(r);
	show(q);
#endif
}

$ g++ -Wall quat_matrix_glm.cpp && ./a.out
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
1.000000 + 0.000000i + 0.000000j + 0.000000k
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>
#include <bullet/LinearMath/btMatrix3x3.h>

static void show(const btQuaternion &q) {
#define MY_SIGN(x) ((x) >= 0.f ? '+' : '-')
	printf("%f %c %fi %c %fj %c %fk\n",
	       q.getW(), MY_SIGN(q.getX()), fabsf(q.getX()), MY_SIGN(q.getY()), fabsf(q.getY()), MY_SIGN(q.getZ()), fabsf(q.getZ()));
#undef MY_SIGN
}

static void show(const btMatrix3x3 &r) {
	printf("%f %f %f\n", r.getRow(0)[0], r.getRow(0)[1], r.getRow(0)[2]);
	printf("%f %f %f\n", r.getRow(1)[0], r.getRow(1)[1], r.getRow(1)[2]);
	printf("%f %f %f\n", r.getRow(2)[0], r.getRow(2)[1], r.getRow(2)[2]);
}

int main(int argc, char *argv[]) {
	btQuaternion identity(0.f, 0.f, 0.f, 1.f);
	btMatrix3x3 r(identity);
	show(r);
	btQuaternion q;
	r.getRotation(q);
	show(q);
#if 1
	r = btMatrix3x3(-identity);
	show(r);
	r.getRotation(q);
	show(q);
#endif
}

$ g++ -Wall quat_matrix_bt.cpp && ./a.out
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
1.000000 + 0.000000i + 0.000000j + 0.000000k
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
1.000000 + 0.000000i + 0.000000j + 0.000000k
Octave
octave:1> pkg load quaternion
octave:2> rotm2q([1 0 0; 0 1 0; 0 0 1])
ans = 1 + 0i + 0j + 0k

(Bullet LinearMath には、btMatrix3x3 クラスのモディファイアとして「setRotation() メソッド」も用意されている)

(Octave の quaternion パッケージには、クォータニオンを回転行列へ変換する関数は用意されていない)

(※クォータニオンを回転行列へ変換することで、クォータニオンの二価性は消失している)

回転行列とクォータニオンによる回転とが一致していることを確認

C/C++ 言語
GLMBullet LinearMath
#include <stdio.h>
#include <math.h>

#include <glm/gtc/quaternion.hpp>
#include <glm/mat3x3.hpp>
#include <glm/vec3.hpp>

static void show(const glm::mat3x3 &r) {
	printf("%f %f %f\n", r[0][0], r[1][0], r[2][0]);
	printf("%f %f %f\n", r[0][1], r[1][1], r[2][1]);
	printf("%f %f %f\n", r[0][2], r[1][2], r[2][2]);
}

int main(int argc, char *argv[]) {
	const float theta = M_PI/6;
	glm::mat3x3 roll(glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, cosf(theta), sinf(theta)), glm::vec3(0.f, -sinf(theta), cosf(theta)));
	show(roll);
	printf("\n");
	roll = mat3_cast(glm::angleAxis(theta, glm::vec3(1.f, 0.f, 0.f)));
	show(roll);
	printf("----\n");
	glm::mat3x3 pitch(glm::vec3(cosf(theta), 0.f, -sinf(theta)), glm::vec3(0.f, 1.f, 0.f), glm::vec3(sinf(theta), 0.f, cosf(theta)));
	show(pitch);
	printf("\n");
	pitch = mat3_cast(glm::angleAxis(theta, glm::vec3(0.f, 1.f, 0.f)));
	show(pitch);
	printf("----\n");
	glm::mat3x3 yaw(glm::vec3(cosf(theta), sinf(theta), 0.f), glm::vec3(-sinf(theta), cosf(theta), 0.f), glm::vec3(0.f, 0.f, 1.f));
	show(yaw);
	printf("\n");
	yaw = mat3_cast(glm::angleAxis(theta, glm::vec3(0.f, 0.f, 1.f)));
	show(yaw);
}

$ g++ -Wall quat_rotation-matrix_glm.cpp && ./a.out
1.000000 0.000000 0.000000
0.000000 0.866025 -0.500000
0.000000 0.500000 0.866025

1.000000 0.000000 0.000000
0.000000 0.866025 -0.500000
0.000000 0.500000 0.866025
----
0.866025 0.000000 0.500000
0.000000 1.000000 0.000000
-0.500000 0.000000 0.866025

0.866025 0.000000 0.500000
0.000000 1.000000 0.000000
-0.500000 0.000000 0.866025
----
0.866025 -0.500000 0.000000
0.500000 0.866025 0.000000
0.000000 0.000000 1.000000

0.866025 -0.500000 0.000000
0.500000 0.866025 0.000000
0.000000 0.000000 1.000000
#include <stdio.h>
#include <math.h>

#include <bullet/LinearMath/btQuaternion.h>
#include <bullet/LinearMath/btMatrix3x3.h>

static void show(const btMatrix3x3 &r) {
	printf("%f %f %f\n", r.getRow(0)[0], r.getRow(0)[1], r.getRow(0)[2]);
	printf("%f %f %f\n", r.getRow(1)[0], r.getRow(1)[1], r.getRow(1)[2]);
	printf("%f %f %f\n", r.getRow(2)[0], r.getRow(2)[1], r.getRow(2)[2]);
}

int main(int argc, char *argv[]) {
	const float theta = M_PI/6;
	btMatrix3x3 roll(1.f, 0.f, 0.f, // row major
			 0.f, cosf(theta), -sinf(theta),
			 0.f, sinf(theta), cosf(theta));
	show(roll);
	printf("\n");
	roll = (btMatrix3x3)(btQuaternion(btVector3(1.f, 0.f, 0.f), theta));
	show(roll);
	printf("----\n");
	btMatrix3x3 pitch(cosf(theta), 0.f, sinf(theta), // row major
			  0.f, 1.f, 0.f,
			  -sinf(theta), 0.f, cosf(theta));
	show(pitch);
	printf("\n");
	pitch = (btMatrix3x3)(btQuaternion(btVector3(0.f, 1.f, 0.f), theta));
	show(pitch);
	printf("----\n");
	btMatrix3x3 yaw(cosf(theta), -sinf(theta), 0.f, // row major
			sinf(theta), cosf(theta), 0.f,
			0.f, 0.f, 1.f);
	show(yaw);
	printf("\n");
	yaw = (btMatrix3x3)(btQuaternion(btVector3(0.f, 0.f, 1.f), theta));
	show(yaw);
}

$ g++ -Wall quat_rotation-matrix_bt.cpp && ./a.out
1.000000 0.000000 0.000000
0.000000 0.866025 -0.500000
0.000000 0.500000 0.866025

1.000000 0.000000 0.000000
0.000000 0.866025 -0.500000
0.000000 0.500000 0.866025
----
0.866025 0.000000 0.500000
0.000000 1.000000 0.000000
-0.500000 0.000000 0.866025

0.866025 0.000000 0.500000
0.000000 1.000000 0.000000
-0.500000 0.000000 0.866025
----
0.866025 -0.500000 0.000000
0.500000 0.866025 0.000000
0.000000 0.000000 1.000000

0.866025 -0.500000 0.000000
0.500000 0.866025 0.000000
0.000000 0.000000 1.000000

《以前のページ → X, Y, Z 軸まわりの回転行列

姿勢を表すクォータニオンを角速度に関連付ける微分方程式

角速度ベクトルがグローバル座標系で表現される場合:\[ \frac{d q}{dt} = \frac{1}{2}\omega q \]ここで、角速度は、純粋クォータニオンで \( \omega = (0, \omega_x, \omega_y, \omega_z) = (0,\, \vec\omega) \) と書かれている

角速度ベクトルが回転する物体のローカル座標系で表現されている場合:\[ \frac{d q}{dt} = \frac{1}{2}q\omega \]

教科書

参考資料


© 2024 Tadakazu Nagai