🔄

クォータニオンって結局何なのか:式の意味がわかるまで

に公開

左手首の角度を測ろうとして数ヶ月詰まった話で、クロストークの話を書きました。「回内・回外すると背屈・掌屈の値まで動いてしまう」という問題です。

このシリーズで扱う動きについて、簡単に整理しておきます。

動き 読み 内容
回内 かいない 前腕を内側に回す(手のひらが下を向く)
回外 かいがい 前腕を外側に回す(手のひらが上を向く)
背屈 はいくつ 手首を手の甲側に曲げる
掌屈 しょうくつ 手首を手のひら側に曲げる

原因を調べるなかで「ジンバルロックを避けるにはクォータニオンを使え」という話が出てきました。生成AIに相談しながらクォータニオン送信に切り替えました。でもクロストークは消えませんでした。

「クォータニオンを使っているのに、なぜ混ざる?」

しばらく詰まり続けました。


クォータニオンにすれば解決すると思っていた

生成AIに相談しながら、クォータニオン送信に切り替えました。IMUとして使っているM5StickC Plus2(以降M5)側でオイラー角まで変換するのをやめ、クォータニオン(w, x, y, z)をそのままBLE送信してJetson側で角度を計算する構成です。

値の挙動は変わりました。でもクロストークは消えませんでした。

「クォータニオンを使っているのに、なぜ混ざる?」

しばらく詰まり続けました。


専門家に問い合わせた

1月末、専門家に問い合わせメールを送りました。内容は「クォータニオンを使っているのに回内・回外が背屈・掌屈に影響してしまう」というものです。

数日後に回答が来ました。その中に、こういう式がありました。

Q_rel = Qf⁻¹ · Qh

前腕のクォータニオンを Qf、手の甲を Qh とすると、手の相対的な姿勢は Q_rel = Qf⁻¹ · Qh で算出します。これにより、前腕がどう動いても、手首の動きは前腕基準のローカル座標系で捉えられます。

最初に見たとき、式の意味がわかりませんでした。


そもそもIMUが出力しているものは何か

式を理解するには、まずIMUが何を出力しているかを整理する必要がありました。

このシステムでは左腕に2つのIMUを装着しています。前腕に1つ(Qf)、手の甲に1つ(Qh)です。

IMU取付イメージ
前腕IMU(Qf)と手IMU(Qh)の装着位置。y軸が前腕長軸・指先方向、x軸が親指方向。

IMUセンサが生データとして出力するのは加速度(m/s²)と角速度(deg/s)です。これらはセンサ自身の座標系での値です。「センサが今どっちに動いているか」「どっちに回転しているか」をセンサ自身の目線で表したものです。

この生データをそのまま使っても「腕がどっちを向いているか」はわかりません。そこでセンサフュージョンという処理が必要になります。


センサフュージョンが何をやっているか

回転行列の記事で、センサ座標系から重力基準の座標系への変換を説明しました。センサフュージョンは、まさにその変換を内部でやっているものです。

加速度と角速度の生データを受け取り、重力方向を基準にした座標系での「姿勢」をクォータニオンとして出力します。

センサ生データ(加速度・角速度)
  = センサ座標系
    ↓ センサフュージョン(Madgwickフィルタ等)
クォータニオン出力
  = 重力基準の座標系

このシステムでは当時Madgwickフィルタを使っていました。M5StickCで使えるArduinoライブラリとして存在していたもので、何をするアルゴリズムかも深く理解しないまま組み込んでいました。

当時のJetsonのコードが受け取るクォータニオンは、すでに重力基準の座標系に変換済みでした。前腕IMUと手IMUがそれぞれ独立にこの処理をしていて、コードにはこう表れます。

Qf = 「重力基準の座標系から見た前腕の姿勢」
Qh = 「重力基準の座標系から見た手の姿勢」

なぜ重力基準のままでは手首の角度が測れないか

ここが問題の核心です。

「手首が背屈しているかどうか」は、重力基準の座標系では判断できません。腕がどの方向を向いていようと、前腕に対して手が曲がっているかどうかが知りたいからです。

ゴルフスイングを例にとると、アドレスの位置とトップの位置では腕全体の向きが大きく変わります。どちらも手首はまっすぐのつもりでも、重力基準で見た Qh の値は全然違います。腕全体の向きが変わったからです。

さらに前腕を回内・回外すると、手も物理的に一緒に回ります。すると Qh が変化します。手首は何もしていないのに、です。これがクロストークの原因でした。

必要なのは「重力基準ではなく、前腕を基準にして見た手の向き」です。


Q_rel = Qf⁻¹ · Qh の意味

まずクォータニオンとは何かを簡単に整理します。

クォータニオンは4つの数値で1つの回転を表します。q = [w, x, y, z] という形式で、wが「どれくらい回転しているか」、x・y・zが「どの軸まわりに回転するか」の方向成分です。回転行列(3×3=9個の数値)より少ない情報量で、かつジンバルロックが起きないという特性があります。

逆クォータニオン(Qf⁻¹)とは

「逆方向の回転」を表します。Qf が「ある方向への回転」なら、Qf⁻¹ は「その回転を打ち消す操作」です。

式の意味を日本語にすると

Q_rel = Qf⁻¹ · Qh を日本語にするとこうなります。

「重力基準で見ていた目線を、前腕基準の目線に切り替えてから手を見る」

もう少し具体的に言うと:

  • Qf⁻¹ で「前腕がどう向いているかという情報を打ち消す」
  • その状態で Qh を掛けると「前腕を基準にして見た手の向き」が得られる

前腕がどう動いても、この式で計算した Q_rel は「前腕に対して手がどれだけ動いたか」だけを反映します。手首の曲がり具合だけが取り出せる、というわけです。

座標系の変換として整理すると

変換前:

  • Qf = 重力基準の座標系で見た前腕の姿勢
  • Qh = 重力基準の座標系で見た手の姿勢

変換後:

  • Q_rel = 前腕基準の座標系で見た手の姿勢

Qf⁻¹ を掛けることで、「重力基準」から「前腕基準」に座標系が乗り換わります。

クォータニオンの数学的な背景をもっと深く知りたい方は、こちらの解説動画まとめが参考になります。私自身もまだ勉強中です。


式を使ってみた

実装を変えて、この式で Q_rel を計算するようにしました。値の挙動が変わり、この時点では解決したと思っていました。

ただその後も問題は続きます。それは次回に書きます。


まとめ

  • IMUセンサの生データはセンサ座標系。センサフュージョンを通すと重力基準の座標系のクォータニオンになる
  • Qf(前腕)と Qh(手)はどちらも重力基準で出力されている
  • 重力基準のままでは「前腕に対する手の向き」がわからない、これがクロストークの原因
  • Q_rel = Qf⁻¹ · Qh は「重力基準から前腕基準への座標の乗り換え」をやっている
  • 式を使えるようになっても、まだ問題は残っていた

このシリーズは、ゴルフコーチングウェアラブルデバイスの開発過程で詰まった経験を振り返りながら書いています。理論の再学習と開発ログを織り交ぜています。


このシリーズの記事

GitHubで編集を提案

Discussion