深層学習の3、複数人体検出実験

概要

yolo_v3を用いて、動画から複数人体の検出を行ってみる。

実装

GPUの確認

!nvidia-smi
nvidia_tesla_k80_spec
nvidia_tesla_k80_spec

Darknetリポジトリのクローン作成

import os
os.environ['PATH'] += ':/usr/local/cuda/bin'
!git clone https://github.com/AlexeyAB/darknet/

コンパイル環境のインストール

!apt install gcc-5 g++-5 -y
!ln -s /usr/bin/gcc-5 /usr/local/cuda/bin/gcc 
!ln -s /usr/bin/g++-5 /usr/local/cuda/bin/g++

ライブラリのコンパイル

cd darknet
!sed -i ‘s/GPU=0/GPU=1/g’ Makefile
!sed -i ‘s/OPENCV=0/OPENCV=1/g’ Makefile
!make

yolov3重みのゲット

!wget https://pjreddie.com/media/files/yolov3.weights
!chmod a+x ./darknet

動画mp4ファイルのアップコード

from google.colab import files
uploaded = files.upload()

人体検出

!./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights -dont_show fifa.mp4 -i 0 -out_filename fifa.avi -thresh 0.7

Jupyter NotebookファイルはGithubへ公開 https://github.com/soarbear/YOLOv3_detection

結果

以下の画像をクリックすると、画面がyoutube動画サイトへ遷移する。

動画から人体検出
動画から人体検出

参考文献

https://pjreddie.com/darknet/yolo/

2+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

深層学習の2、LSTM Seq2Seqモデル実装

概要

株価、為替、天気、動画など時系列データの予測でよく使われるディープラーニングの代表的手法RNN(再帰型ニューラルネットワーク)の拡張バージョンに、LSTM(Long short-term memory)と呼ばれるモデルがある。今回は、LSTM Seq2Seq、Many to Manyモデルを実装して、円の第4象限の一部(訓練未実施)に対して、時系列データの予測を行ってみる。

環境

keras/LSTM, Google Colab CPU

モデル

lstm_seq2seq_model
lstm_seq2seq_model

以下モデルの概要を説明する。
・Sequentialは、あるレイヤの全ノードと、次レイヤの全ノードをつなぐRNNのモデル。
・RNNの第1 Layerとして、LSTMを追加する。第1引数は出力の次元数、activationは活性化関数で、input_shapeは入力データのフォーマット(in_time_steps x in_features)となる。このlayerがEncoderのように見える。
・RepeatVectorにより、出力を入力として繰り返す。ここでの繰り返し回数は予測範囲(out_vectors)となる。
・再度のLSTM、ただし、ここではシーケンス出力まさにmany outputつまり、return_sequencesをTrueに指定する。このlayerがDecoderのように見える。
・TimeDistributedを指定し、かつ、Dense(out_features)で、出力の次元数を指定する。
・最後にcompileメソッドで、学習時の最適化手法や、損失関数を指定する。ここでは最適化手法としてAdamを、損失関数としてMSE(Mean Squared Errorつまり平均2乗誤差)に指定する。

実装

# -*- coding: utf-8 -*-
# Brief: A Seq2Seq Model Implementation by keras Recurrent Neural Network LSTM.
# Author: Tateo_YANAGI @soarcloud.com
#
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers.recurrent import LSTM
from keras.layers.core import Dense, Activation
from keras.layers.wrappers import TimeDistributed
from keras.layers import RepeatVector

PI = 3.1415926535
EPOCHS = 100
TRAIN_SPLIT = 0.98

#
# Class Prediction for lstm model.
#
class Prediction:
  def __init__(self):
    # Array dimension of input&output:(batch_size, time_steps or vectors, features).
    self.batch_size = 48
    self.in_time_steps = 6
    self.in_features = 2
    self.out_vectors = 3
    self.out_features = 2
    self.hidden_neurons = 100 # Neurons of hidden layer.
    self.epochs = EPOCHS # Iteration times.
    self.train_split = TRAIN_SPLIT # Partition of learning dataset.
    self.activation_hidden = 'tanh' # Activation function for hidden units.
    self.activation_output = 'linear' # Activation function for output units.
    self.optimizer= 'adam' # Optimization function.
    self.loss = 'mse' # Loss(cost) function.

#
# Create dataset.
#
  def create_dataset(self, train_split=0.8, in_time_steps=10, out_vectors=4, in_features=2, out_features=2):
    a = np.array([[np.cos(0.0), np.sin(0.0)]])
    for i in range(1, int(2*PI*10)):
      a = np.append(a, np.array([[np.cos(i*0.1), np.sin(i*0.1)]]), axis=0)
    print(f'[Debug-0]\nlen(a):{len(a)}\na:\n{a}')

    # Create dataset for train and test
    # Initialize x_train and y_train.
    x_total = a[0:in_time_steps, 0:in_features]
    y_total = a[in_time_steps:in_time_steps+out_vectors, 0:in_features]

    # Calculate length for x_total and y_total.
    total_len = a.shape[0] - in_time_steps - out_vectors + 1
    print(f'[Debug-1]\ntotal_len:{total_len}')

    # Fill out x_total and y_total.
    for i in range(1, total_len):
      x_total = np.append(x_total, a[i:i+in_time_steps], axis=0)
      y_total = np.append(y_total, a[i+in_time_steps:i+in_time_steps+out_vectors], axis=0)
    print(f'[Debug-2]\nx_total.shape[0]):{x_total.shape[0]}\nx_total:\n{x_total}\ny_total.shape[0]:{y_total.shape[0]}\ny_total:\n{y_total}')

    # Reshape x_toal and y_total from 2D to 3D.
    x_total = np.reshape(x_total[0:x_total.shape[0]], (-1, in_time_steps, in_features))
    y_total = np.reshape(y_total[0:y_total.shape[0]], (-1, out_vectors, out_features))

    # Split dataset for train and test
    x_train = x_total[0:int(x_total.shape[0]*train_split)]
    y_train = y_total[0:int(x_total.shape[0]*train_split)]
    #x_test  = x_total[x_train.shape[0]:]
    #y_test  = y_total[y_train.shape[0]:]
    x_test  = x_total[-2:]
    y_test  = y_total[-2:]
    print(f'[Debug-3]\nx_train.shape[0]:{x_train.shape[0]}\nx_train:\n{x_train}\ny_train.shape[0]:{y_train.shape[0]}\ny_train:\n{y_train}')
    print(f'[Debug-4]\nx_test.shape[0]:{x_test.shape[0]}\nx_test:\n{x_test}\ny_test.shape[0]:{y_test.shape[0]}\ny_test:\n{y_test}')

    return x_train, y_train, x_test, y_test

#
# Create lstm model.
#
  def create_model(self) :
    model = Sequential()
    # Encoder
    model.add(LSTM(self.hidden_neurons, activation=self.activation_hidden, input_shape=(self.in_time_steps, self.in_features)))
    # Output used as Input.
    model.add(RepeatVector(self.out_vectors))
    # Decoder, output sequence
    model.add(LSTM(self.hidden_neurons, activation=self.activation_hidden, return_sequences=True))
    model.add(TimeDistributed(Dense(self.out_features, activation=self.activation_output)))
    #model.add(Activation(self.activation_output))   
    model.compile(optimizer=self.optimizer, loss=self.loss, metrics=['accuracy'])
    return model

  def train_model(self,model, x_train, y_train) :
    model.fit(x_train, y_train, epochs=self.epochs, verbose=2, batch_size=self.batch_size)
    return model

#
# main()
#
if __name__ == "__main__":
  predict = Prediction()
  train_in_data = None
  # Create dataset
  x_train, y_train, x_test, y_test = predict.create_dataset(predict.train_split, predict.in_time_steps,\
                                                            predict.out_vectors, predict.in_features, predict.out_features)
  # Create model
  model = predict.create_model()
  # Train model
  model = predict.train_model(model, x_train, y_train)
  print(model.summary())
  # Test
  predicted = model.predict(x_test, verbose=1)
  print(f'[info_1]predicted:\n{predicted}')
  # Plot result
  predicted = np.reshape(predicted, (-1, predict.out_features))
  x_test = np.reshape(x_test, (-1, predict.in_features))
  y_test = np.reshape(y_test, (-1, predict.out_features))
  x_train = np.reshape(x_train, (-1, predict.in_features))
  y_train = np.reshape(y_train, (-1, predict.out_features))
  plt.figure(figsize=(8, 8))
  plt.scatter(x_train[:,0], x_train[:,1])
  plt.scatter(x_test[:,0], x_test[:,1])
  plt.scatter(y_test[:,0], y_test[:,1])
  plt.scatter(predicted[:,0], predicted[:,1])
  plt.show()

※ソースコード公開→
https://github.com/soarbear/lstm_seq2seq_model_prediction

結果

lstm_seq2seq_model_prediction
lstm_seq2seq_model_prediction

今回、epochsとneuronsを増やすことで、上図のとおり精度において納得いく結果を得た。これでコスト関数、活性化関数の選定はじめ、ハイパーパラメータの調整が精度にいかに重要なことを分かる。熱伝播、振動、柔軟ロボット関節の駆動力など元々フーリエ変換、ラプラス変換、Z変換しか解けない偏微分方程式が、LSTMモデルを適用できたらどうかと今度試してみる。

参考文献

1、LSTMネットワークを理解する(英文原稿)、Christopher Olah氏
2、Keras公式資料

2+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

深層学習の1、上がる株みつかる

概要

株価、為替、天気、動画など時系列データの予測でよく使われるディープラーニングの代表的手法RNN(再帰型ニューラルネットワーク)の拡張バージョンに、LSTM(Long short-term memory)と呼ばれるモデルがある。今回はLSTM Many to Oneモデルを実装して、複数銘柄(例:10銘柄)の株価から翌日の上がる株を探ってみる。

環境

keras 2.2.5 LSTM
Google Colab CPU/GPU/TPU
Ubuntu 18.04.3 LTS
Python 3.6.8
Numpy 1.17.3
Pandas 0.25.2
sklearn 0.21.3

実装

Many-to-Oneモデルの例として、以下2銘柄から翌日の上がる株を探る。

# -*- coding: utf-8 -*-
import numpy
import pandas
import matplotlib.pyplot as plt

from sklearn import preprocessing
from keras.models import Sequential
from keras.layers.core import Dense, Activation
from keras.layers.recurrent import LSTM

class Predict:
  def __init__(self):
    self.length_of_sequences = 10
    self.in_out_neurons = 1
    self.hidden_neurons = 300
    self.batch_size = 32
    self.epochs = 100
    self.percentage = 0.8

  # データ用意
  def load_data(self, data, n_prev):
    x, y = [], []
    for i in range(len(data) - n_prev):
      x.append(data.iloc[i:(i+n_prev)].values)
      y.append(data.iloc[i+n_prev].values)
    X = numpy.array(x)
    Y = numpy.array(y)
    return X, Y

  # モデル作成
  def create_model(self) :
    Model = Sequential()
    Model.add(LSTM(self.hidden_neurons, batch_input_shape=(None, self.length_of_sequences, self.in_out_neurons), return_sequences=False))
    Model.add(Dense(self.in_out_neurons))
    Model.add(Activation("linear"))
    Model.compile(loss="mape", optimizer="adam")
    return Model

  # 学習
  def train(self, x_train, y_train) :
    Model = self.create_model()
    Model.fit(x_train, y_train, self.batch_size, self.epochs)
    return Model

if __name__ == "__main__":

  predict = Predict()
  nstocks = 2;

  # 銘柄毎に学習、予測、表示
  for istock in range(1, nstocks + 1):
    # データ準備
    data = None
    data = pandas.read_csv('/content/drive/My Drive/LSTM/csv/' + str(istock) + '_stock_price.csv')
    data.columns = ['date', 'open', 'high', 'low', 'close']
    data['date'] = pandas.to_datetime(data['date'], format='%Y-%m-%d')

    # 終値のデータを標準化
    data['close'] = preprocessing.scale(data['close'])
    data = data.sort_values(by='date')
    data = data.reset_index(drop=True)
    data = data.loc[:, ['date', 'close']]

    # 割合で学習、試験データ分割
    split_pos = int(len(data) * predict.percentage)
    x_train, y_train = predict.load_data(data[['close']].iloc[0:split_pos], predict.length_of_sequences)
    x_test, y_test = predict.load_data(data[['close']].iloc[split_pos:], predict.length_of_sequences)

    # 学習
    model = predict.train(x_train, y_train)

    # 試験
    predicted = model.predict(x_test)
    result = pandas.DataFrame(predicted)
    result.columns = [str(istock) + '_predict']
    result[str(istock) + '_actual'] = y_test

    # 表示
    result.plot()
    plt.show()

    # 翌日株価比較
    current = result.iloc[-1][str(istock) + '_actual']
    predictable = result.iloc[-1][str(istock) + '_predict']
    if (predictable - actual) > 0:
      print(f'{istock} stock price of the next day INcreases: {predictable-actual:.2f}, predictable:{predictable:.2f}, current:{current:.2f}')
    else:
      print(f'{istock} stock price of the next day DEcreases: {actual-predictable:.2f}, predictable:{predictable:.2f}, current:{current:.2f}')

またソースコード、csvファイルは、https://github.com/soarbear/stocks-lstm-kerasへ公開した。

結果

keras lstmで複数銘柄の株価予測
keras lstmで複数銘柄の株価予測

【追記】再現性

毎回学習してから予測の結果が変わるので、再現性、生産性がない。ニューロンの重みやバイアスの初期値など【Randomness in Initialization, Regularization, Layers, Optimization】がRandomであり、以下の例として、SEED_IDを固定することによって、予測結果の再現が確認できた。ただしepochsは、SEED_IDを固定しないRandomのほうよりlossがいかに最小まで収束するかを確かめる必要がある。

import numpy as np
import tensorflow as tf
import random as rn
import os
os.environ['PYTHONHASHSEED'] = '0'
from keras import backend as K
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
K.set_session(sess)
......
def fix_seed():
  np.random.seed(SEED_ID)
  rn.seed(SEED_ID)
  tf.set_random_seed(SEED_ID)
......
def create_model(self) :
  Model = Sequential()
  fix_seed()
  Model.add(LSTM(self.hidden_neurons, batch_input_shape=(None, self.length_of_sequences, self.in_out_neurons), return_sequences=False))
  fix_seed()
  Model.add(Dense(self.in_out_neurons))
  Model.add(Activation("linear"))
  Model.compile(loss="mape", optimizer="adam")
  return Model
......

また、2のKeras公式資料もあわせて確認しておく。
しかし、ニューロンの重みやバイアスの初期値が固定するより、ランダムのほうは結果の多様性が現われる。複数のモデル(30以上?)より交差検証の手法で良いモデルを選ぶのが、まさに統計の務めではないかと考えさせられる機会となる。

【追記】学習状況をスマホに記録

学習状況をスマホでモニタしたい際、アプリHyperdashが使える。以下コードを実行する。学習状況の履歴がスマホに残る。

!pip install hyperdash
from hyperdash import monitor_cell
!hyperdash signup --github
%%monitor_cell 'xxx'

参考文献

1、LSTMネットワークを理解する(英文原稿)、Christopher Olah氏
2、開発中にKerasを用いて再現可能な結果を得るには?、Keras公式資料

3+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

流体力学ベルヌーイの定理

概要

ベルヌーイの定理(Bernoulli’s theroem)は流体のエネルギ保存則(the conversation law of energy)で、固体のエネルギ保存則との相違点が圧力エネルギが含まれている。以下にまとめてみる。
物体のエネルギ保存則:
$$\frac{1}{2}mu^2+mgz = 一定\space [J] $$
ベルヌーイの定理(流体のエネルギ保存則):
単位体積あたりのエネルギ
$$ \frac{1}{2}ρu^2+ρgz+p = 一定\space [J/m^3 = Pa] $$
単位質量あたりのエネルギ
$$ \frac{1}{2}u^2+gz+\frac{p}{ρ} = 一定\space [J/kg] $$
単位重量あたりのエネルギ
$$ \frac{1}{2g}u^2+z+\frac{p}{ρg} = 一定\space [J/N = m] $$
ただし、\(\frac{1}{2}ρu^2、\frac{1}{2}u^2、\frac{1}{2g}u^2 \) = 運動エネルギ、\( ρgz、gz、z \) = 位置エネルギ、\( p、\frac{p}{ρ}、\frac{p}{ρg} \) = 圧力エネルギ、p = 圧力\([Pa]\)、ρ = 流体密度\([kg/m^3]\)、u = 平均流速\([m/s]\)、g = 重力加速度\([9.8m/s^2]\)

参考書籍

はじめての流体力学、田村恵万氏著、科学図書出版

1+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

オイラー角~ジンバルロック~クォータニオン

オイラー角

オイラー角とは、3次元ユークリッド空間中の2つの直交座標系(デカルト座標系 )の関係を表現する方法の一つである。ロボット、ドローンの姿勢(ポーズ、向き)を直観に表現するには、オイラー角を用いることがある。オイラー角またはオイラー回転は3つの角度の組に、3座標軸まわりの回転順序(計12通りもある)で表されるので、オイラー角の変数は4つである。ロボットが姿勢\(v\)(ローカル座標系\(XYZ\))から姿勢\(v’\)(ローカル座標系\(X^{\prime \prime}Y^{\prime \prime}Z^{\prime \prime})\)へ変わる際に、例えば\(X\)軸-\(Y’\)軸-\(Z^{\prime \prime}\)軸の順に\(XY’Z^{\prime \prime}\)系オイラー角 \(\alpha,\beta,\gamma\) で3回順次回転することで姿勢の転換を表すことが可能である。【姿勢\(v’\)の座標 = 回転行列・姿勢\(v\)の座標】より、姿勢\(v\)の座標から回転後の姿勢\(v’\)の座標が求められる。姿勢\(v\)の座標、姿勢\(v’\)の座標が分かれば回転行列からオイラー角が求められる場合ある。この場合、積 \( R_{x}( \alpha ) R_{y}( \beta ) R_{z}( \gamma )\) は、\(XY’Z^{\prime \prime}\)回り順で表したときのオイラー角が \( (\alpha,\beta,\gamma) \) であるような回転を表す回転行列\( R_{xyz}(\alpha,\beta,\gamma) \)である。

固定座標系・機体座標系・オイラー角(右手の法則)
固定座標系・機体座標系・オイラー角(右手の法則)

また、回転軸が固定座標軸機体座標軸により、オイラー角が以下の2種類ほどある。
・固定座標系の3つの軸を中心とした回転、座標軸は静止しているので、固定角という。
・機体座標系の3つの軸を中心とした回転で、回転中に座標軸がロボットとともに回転するため、オイラー角という。

ロボットやドローンが移動の際、固定座標系を参照できないと、姿勢の転換を表すオイラー角を使うしかない場合がある。

ジンバルロック現象、他の問題点

オイラー角の問題点として、オイラー角を使用すると、オイラー角そのものの定義より、ジンバルロック現象を起こしてしまう場合がある。もともとジンバル装置(静態軸ある)で起きる現象で、航空機(剛体)も起きる。但し、固定角にはジンバルロック現象が起きない【※訂正あり】。上記オイラー角の2番目の回転角度\( \beta = ±π/2 \)であれば、1番目回転\( \alpha \)と3番目の回転\( \gamma \)が同じ回転軸になって、つまり\( X \)軸と\( Z^{\prime \prime} \)軸が重なることになる。また回転行列より、\( \alpha – \gamma = C\)にすると、\( \alpha,\gamma \)の解は無限にあることと、1自由度が失われて、3次元空間と2次元空間の違いを考えると、このような3回転はベクトルを平面あるいは錐面に限定されて、すべての3次元ベクトルへ姿勢を変換されず、2自由度にロックされたようにしか考えられぬ現象をジンバルロック現象と呼ばれる。また1ジンバルを1自由度に当てはまると、ジンバルロックといわれるのは分かりやすいだろうか。\( \beta \neq ±π/2 \)であれば、例\( \beta = \pi /3 \)、\( (\alpha , \pi /3 , \gamma) \)の組み合わせであらゆるベクトルに変換可能となる。なお他にジンバルロック現象を起こさないオイラー角\( (\alpha,\beta,\gamma) \)に対して、\( (\alpha+π,π−\beta,\gamma+π) \)の回転で同じ姿勢になる問題点もある。

よって、以下の条件付きであれば、ジンバルロックが除けて、オイラー角でロボットの姿勢を表すのが一意になる。言い換えれば、回転行列がオイラー角と一対一に対応することになる。

・ \( \beta ∈ (-π/2, π/2), \alpha,\gamma ∈ [-π, π] \)

しかし、オイラー角\( (\alpha,\beta,\gamma) \)を制限するのは無理の場合、オイラー角から四元数(Quaternion、クォータニオン)の登場となる。

また、オイラー回転の2番目の回転軸が1番目の回転軸 x 3番目の回転軸の外積の方向にある。つまり、2番目の回転軸が1番目と3番目の回転軸と直交する。当然、1番目と3番目の回転軸が必ずしも直交ではないが、ただし1番目と3番目の回転軸の外積\(=0\)つまり重なるのであれば、ジンバルロックに導く要因となる。

四元数

姿勢あるいは回転の表現には、オイラー角の3回転よりも、単純に回転軸と回転軸まわりの1回転で済む場合、四元数つまり軸回りの回転を表す四元複素数\(( w + ix + jy + kz , w, x, y, z \)は実数、\( i,j,k \)は\(XYZ\)軸に対応する虚数単位\()\)が用いられる。四元数の変数の個数がオイラー角の回転つまり\( [\alpha:Roll,\beta:Pitch,\gamma:Yaw] \)の回転順と同様に4つであり、四元数を見るだけでは姿勢転換のイメージが難しいが、回転軸と回転角の表現に変わると一目瞭然になる。よって、一般論として3次元までの空間の回転の表現に必要な変数の数=空間の次元数+1となる。回転行列からオイラー角を求めるのと、逆に回転順とオイラー角から回転行列を求めるのが面倒だが、四元数を用いて回転を表現すると気持ちがすっきりになる。

例えば、以下のように、ベクトル\( v=iv_x + jv_y + kv_z\) で表すロボットの姿勢Vを原点を通る単位ベクトル\( a=ia_x + ja_y + ka_z \)を回転軸として、右ねじが進む方向に回転角\(θ\)だけ回転させて、ベクトル\( v’=iv’_x + jv’_y + kv’_z \)で表すロボットの姿勢V’になると、以下に優雅なる表現がある。$$ v’=q v \bar{q} \\ q=cos \frac{θ}{2} + a sin \frac{θ}{2} \\ \bar{q}=cos \frac{θ}{2} – a sin \frac{θ}{2} $$
但し、原点を通る回転軸\(a\)に合わせ、右手の法則に従う回転角\(θ ∈ [-π, π]\)、\(q\)は原点を通る回転軸\(a\)と回転角\(θ\)を組合せた回転ベクトルを表す、正規化した四元数(ノルムは\(1\))、3次元空間における任意の回転に\(q\)は必ず且つ唯一に存在する。\(q,\bar{q}\)は共役または、共軛な複素数、お互いの逆回転でもある。上記計算の結果となる回転後ベクトルの\( v’ \)は回転前ベクトルの\( v\)と同じく、必ず純複素数となって、つまり実数部はゼロになる。
\( ij=-ji=k, jk=-kj=i, ki=-ik=j \)
\( i^2=j^2=k^2=-1, ijk=kk=-1 \)
\( ||a||=1, \space ||q||=||\bar{q}||=1 \)

四元数による回転表現(右手系)
四元数による回転表現(右手の法則)

また、回転 \(q_1\)に引き続き、回転 \(q_2\)を行う場合、次のように書くと良い。
$$ v’=(q_{2}q_{1})v(\bar{q_{1}}\bar{q_{2}}) $$

ロボットの姿勢変換の表現に

\(v=(1,0,0)=i\)をロボットの初期姿勢として、回転軸\(a\)回り、\(\theta\)だけを回転すると、式\(v’=q v \bar{q}\)を使って姿勢変換後の姿勢\(v’\)を求めるように、ロボットの姿勢変換に生かせるようになる。また慣性航法などにも利用されている。なお、四元数はあくまで数なので幾何的にどの表現なのか興味深いが、とにかくひとの脳に想像可能な3次元空間ベクトルに混同しないように注意する。\(q\)はROSで表現すると、\(q.x=(a.x)sin \frac{θ}{2} \\
q.y=(a.y)sin \frac{θ}{2} \\
q.z=(a.z)sin \frac{θ}{2} \\
q.w=cos\frac{θ}{2}\)となる。

簡単な計算例

例:台車ロボットはZ軸回り180°回転してポーズを求めよう。
回転前ポーズ\(v\)を機体前面の法線方向ベクトル\(v=(1,0,0)=i\)に、回転軸\(a=(0,0,1)=k\)、回転角\(\theta=\pi\)として、回転ベクトルは\(q=cos \frac{θ}{2} + a sin \frac{θ}{2} = k\)となる。回転後ポーズは、
\(v’ = q v \bar{q} = (k) (i) (-k) = (k) (k) (i) \\= -i = (-1,0,0)\)と求められる。
検証として、\(z=0\)なので複素平面\(a+bi\)の回転として考えて、\( (1) (i) (i) = (-1, 0, 0) \)と分かる。\(z=0\)を補足すると、四元数の計算結果\((-1,0,0)\)と一致することが分かる。

余談

複素平面\(a+bi\)に実数軸あるのに、なぜ四元数の実数部に対応する座標系の軸はないとか、なぜ三元数は聞いたことはないとかの質問が出るかもしれないが、従来の複素平面\(a+bi\)から拡張された三元数\(a+bi+cj\)はどうも都合は悪くて、使い物にならないので、発明者のHamilton氏(英・数学物理学者)は悩んだすえ、新たに四元数に導いたそうだ。以来、三次元空間における姿勢変換の表現に大いに活用されて、四元数そのものの幾何表現は難問。深くまで掘り下げると、矢野忠先生の「四元数の発見」をご一読ください。

【※訂正】固定角にも、第2回転軸回りの回転角度によって、ジンバルロック現象が起きる。

参考文献

・wiki: オイラー角
・wiki: 四元数
・四元数まとめ資料 – 宇宙電波実験室

関連記事

9軸IMU 6軸/9軸フュージョン 低遅延 USB出力 補正済み ROS対応
ROS・Unity・ロボット・ドローン姿勢の回転表現
SLAM~拡張カルマンフィルタ
SLAM~Unscentedカルマンフィルタ
粒子フィルタ
6軸IMU~拡張カルマンフィルタ
研究開発用 台車型ロボット キット

1+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア