Blender+Pythonを用いてクォータニオンから姿勢表示しよう

はじめに

 3次元の姿勢を表現する方法の1つに, クォータニオン(四元数)があります. オイラー角と比較して, 特異点が存在しない, 計算量が少なくすむことが多いなどのメリットが存在しますが, 直感的に理解しづらいのが難点です. 

 そこで今回は, クォータニオンの時系列データを記したCSVデータを読み込んで, 3D CGソフトのBlenderでぐるぐる回す方法を説明します. 本記事で用いているBlenderのバージョンは2.92であり, PCのOSはWindows10です.

Blenderをダウンロード&インストールする

 Blenderの公式サイトからダウンロードし, その後インストールします. 特に迷う部分はないと思います.
https://www.blender.org/

Blenderを起動し, さわってみる

 Bldenderを起動すると, 図1のような画面が表示されます. 中央に立方体が配置され, 選択状態にあります. この画面の右下に, 立方体の位置や姿勢の設定が表示されています. 

f:id:tgkhtknk:20211215235858p:plain

図1 Blenderの起動時の画面

 右下を拡大したのが図2です. 初期設定では原点に配置され, 姿勢はxyzオイラー角の[0° 0° 0°]^Tになっています. そこのプルダウン部分をクリックすると, Quaternion(WXYZ)を選択することができます.

(ところで, Bldnerでxyzオイラー角とされているのは一般的な表記ではzyxオイラー角であり, 同様に例えばzxyオイラー角とされているのはyxzオイラー角が一般的な表記だと思うのですが, 本題ではないので深入りしないことにします.) 

f:id:tgkhtknk:20211215235902p:plain

図2 立方体オブジェクトの設定(初期値)

クォータニオンの定義を確認する

 Blenderのおけるクォータニオンは, WXYZと書かれていることから分かる通り, 1つ目が実部で2~4つ目が虚部です. つまり, ノルムが1のベクトル[x y z]^Tを軸にθ [rad]回転させるときのクォータニオン

f:id:tgkhtknk:20211216002002p:plain,

と表されます. よって, x軸回りにπ/4回転させるときは, 

f:id:tgkhtknk:20211216002938p:plain

です. これを試しにBlenderに入力してみます. 図3の赤枠部分のように入力すると, x軸(赤色の線)を軸に45°回転します. 

f:id:tgkhtknk:20211216120401p:plain

図3 クォータニオンの確認

Pythonコードを書く準備をする

図4のように, 画面上部のタブから赤枠内の"scripting"をクリックし, Pythonコードを書くための画面に移ります. その後, 緑色枠内の"New"をクリックすると, コードを書けるようになります. 

f:id:tgkhtknk:20211216135213p:plain

図4 scripting画面の初期状態

 次に, 必要なライブラリをインストールします. 今回は, osとbpyとpandasを用います.  osとbpyBlender内蔵Pythonには標準で入っているはずですので, pandasのインストールを行います. こちらのサイトを参考にインストールしてください. Blender内蔵Pythonへインストールする必要があるため, Pythonに慣れてる方もこのサイトを見た方がいいです. 

Blender内蔵Pythonにライブラリを追加する方法 | suemura's portfolio

 その後, きちんとインストールできたか確認します. 図5のように, Blenderテキストエディタ領域に

import os, bpy

import pandas as pd

と入力して, 赤枠で囲った実行ボタンを押します. 左下の緑枠で囲った領域に, 緑色のチェックボックスの後に"bpy.ops.text.run_script()"と表示されていれば成功です. 

f:id:tgkhtknk:20211218211440p:plain

図5 スクリプト実行

一方でエラーが発生した場合は, 図6のように赤いバツ印が表示されます. エラーメッセージの詳細はToggle System Consoleというのを開かないと見られません. 図7のようにBlender左上のWindowからToggle System Consoleをクリックしてください. 

f:id:tgkhtknk:20211218175458p:plain

図6 エラー発生時

f:id:tgkhtknk:20211218175527p:plain

図7 Toggle System Consoleの場所

f:id:tgkhtknk:20211218180407p:plain

図8 Toggle System Console

図8がToggle System Consoleです. 今回はBlender内蔵Pythonにインストールしていないsimplekmlをわざとimportさせようとしたため, simplekmlがないぞとメッセージが出ています.

クォータニオンのデータを用意する

図9のような時間間隔が同一のCSV形式のデータを用意します. 図9では, 見やすいようにエクセルで開いています. 1列目が実部, 2~4列目が虚部です. 行と列が逆だとPythonコードが読み取ってくれなくなります. 今回は時間間隔が0.001 sの1000 Hzのデータを用意しました. 

f:id:tgkhtknk:20211218181838p:plain

図9 CSVデータ

余談ですが, エクセルでデータを作成してCSV形式で保存する際には注意が必要です. 図10のように, CSV (コンマ区切り)をクリックしてください. 上の方にあるCSV UTF-8 (コンマ区切り)を選択して保存すると, Pythonコードが読み取ってくれなくなります.

f:id:tgkhtknk:20211218181918p:plain

図10 CSV形式での保存

Pythonコードを書き実行する

 図11が私が用いたコードです. 内容を解説します.

・5, 6行目でライブラリをimport

・8行目でCSVファイルがあるディレクトリを指定(バックスラッシュを二重にすることに注意)

・9行目でCSVファイルのファイル名を指定

・11~13行目で現在ある全てのオブジェクトを消去(消去しないと面倒が発生する可能性あり)

・15~17行目で, 原点に大きさ(1, 1, 3)の直方体を配置

・18行目でその直方体の角度表現をクォータニオンに設定(11~13行目で消去を行わないと, 作成した直方体の名称が Cube ではない可能性がある)

・20行目で現在アクティブなオブジェクト=Cubeをobjに設定

 

・22行目から1フレームごとに繰り返すfor文に入る

・今回は1000 Hzのクォータニオンデータを用いる. 20個に1つのデータを使用し, 50 Hzにする. 

・23行目でk+1フレーム目を指定

・24~27行目でCSVファイルから読み取ったデータをq0~q3に代入

・28行目でCubeを回転させ, 29行目でキーフレームを挿入する. 

・31行目では, old_typeに現在の編集エリアのタイプ(テキストエディタ)を代入

・32行目で編集エリアをグラフエディタに移動し, 33行目でキーフレーム間に補完をリニアに設定(今回は1フレームごとに設定しているので, 31~34行目はなくてもよい)

・34行目でテキストエディタに戻る

f:id:tgkhtknk:20211218192113p:plain

図11 コード

ディレクトリ, ファイル名, CSVデータのサンプリング周期と作成する動画のフレームレートにより適宜8, 9, 22, 24~27行目を書き換え, Pythonコードを実行します. ただし, Blenderを日本語化していると18行目でエラーを吐くことに注意してください. 対処法はある気がするのですが, このコードは英語でないと動きません.

 Scripting画面からLayout画面に戻ると, 図12のように直方体ができています. また, 赤枠で囲ったCubeの角度を表す部分が黄色になっています. これがキーフレームが挿入された状態です.

 さらに, 画面下の緑色で囲ったタイムラインにオレンジ色の四角形がたくさんあります. これは1つ1つがその時刻ごとにキーフレームが挿入されていることを表します. 

 今回用いたデータでは404フレーム目までキーフレームが挿入されましたが, デフォルトの設定ではタイムラインが250で最後になっています. そこで, 青枠で囲った250を404に変更します. 

f:id:tgkhtknk:20211218195453p:plain

図12 スクリプト実行後のLayout画面

⑦ロケットっぽい形のオブジェクトを入れる

 直方体がぐるぐる回ってもいまいちなので, ロケットっぽい形のオブジェクトを導入します. 今回は, 事前に作成しておいたSTLファイルをimortします. 図13のように, File→Import→Stl をクリックします.

f:id:tgkhtknk:20211218200411p:plain

図13 STLファイルのimoport

そうすると, 図14のように原点にimportしたオブジェクト(以下Rocketと呼ぶ)が配置されます. Rocketの角度が意図した初期姿勢と異なる場合は, 初期姿勢に変更してください. なお, 図12では404フレーム目でしたが, 図14では1フレーム目に移動しています. ここから, RocketがCubeと一緒に回転してくれるように設定を行います. 

f:id:tgkhtknk:20211218200905p:plain

図14 STLファイルのimport後

 まず, Cubeをクリックします. その後, 図15のようにCubeの角度と大きさを変更します. 角度は初期姿勢(Rocketと同じ姿勢)にします. RocketがCubeと一緒に回転するようにするための作業上の都合で一時的にするだけなので, キーフレームの挿入(iキーを押す)はしないでください.

 次に, CubeがRocketの中に収まるようにCubeの大きさを変更します. これは動画を作成するときに見栄えをよくするための作業です.

f:id:tgkhtknk:20211218201425p:plain

図15 Cubeの大きさと角度の変更

 次に, 親子関係を設定します. まずRocketをクリックします. そのまま, Shiftキーを押しながらCubeをクリックします. (Rocketの中に隠れてしまっていますが, あるはずのところをクリックすれば問題なくできます) 必ずRocketを先, Cubeを後にクリックします. その後, Ctrlキーをおしながらpを押すと, 図16のようになるのでObject (Keep Transform)をクリックします. こうすることでCubeが親, Rocketが子となり, RocketはCubeと一緒に回るようになります. 

f:id:tgkhtknk:20211218202139p:plain

図16 親子関係の設定

⑧フレームレートの設定

 今回はフレームレートが50 fps (50 Hz)になるようにCSVデータのサンプリングレートとPythonコードでのダウンサンプリングを定めたので, Blender上でも50 fpsに設定します. 図17のように, 画面右側にある赤枠で囲ったプリンターが画像を出しているアイコンをクリックします. その後, 緑色枠で囲った部分のデフォルトで24になっているFrame Rateを50に変更します. 

f:id:tgkhtknk:20211218203923p:plain

図17 フレームレートの設定

⑨映像を見てみる

 図18の赤枠で囲った, タイムラインの上部にある再生ボタンをクリックすれば, Rocketが回る様子を映像で見ることができます. ただし, リアルタイムに描画してるので50fpsで表示してくれるとは限りません.

 きちんと動画に出力する方法は下2つの記事を参考にしてください. 他にもググればいくらでも出てくるので説明しません. (記事終盤になり疲れてきたし) 

【Blender2.9】カメラの使い方と機能:レンダリングには必要技術! | Vtuberの解剖学

Blender2.8で【静止画】【動画】をレンダリングして保存する方法

f:id:tgkhtknk:20211218204558p:plain

図18 再生ボタン

⑩実際のロケットの動画と見比べてみる

この動画は, 2019年11月に打ち上げたC-43Jの動画と, それに搭載した基板のデータからBlenderで作成した動画の比較動画です. いい感じですね!!(最後の方ちょっとずれてるけど, ドリフト・基板とカメラ間の時刻誤差・撮影地点から見てロケットが移動しているの複合によるものかな)

youtu.be