【Blender 5.1】ジオメトリノードで作るマトリクスロボットアーム:Multiply Matricesで親子関係を再現する方法

blender
記事内に広告が含まれています。

Blenderのジオメトリノードでは、通常のオブジェクトのような親子関係は存在しません。
しかし、Transform(変換)を表す行列を掛け合わせていくことで、あたかも親子階層があるかのような動きを作ることができます。

この記事では、Multiply Matricesノードを使って

腕2本、ロボハンド1個の部品を連結したシンプルなロボットアームを構築しながら、

  • なぜ親子のように動くのか
  • 行列チェーンの正体は何か
  • 直感的なわかりやすいイメージ解説と、内部の行列計算の解説

を整理していきます。

右の動画は、今回解説しているマトリックスロボアームの仕組みを使って作成したアニメーションです。

マトリックスを使ってロボットアームを制御するって一見かなり難しそうに見えるんですが、とにかくトランスフォーム変換をつなげていけばできるので、仕組みさえわかってしまえば、ノード数も少なく簡単に構成することができます。

難しいと感じる方も、イメージで掴んで、とにかくトランスフォームをつなげていくんだなぁって思ってもらえれば意外と簡単にできちゃうんじゃないかなぁと思っています。

後半に行列計算の内部のことも言ってますが、気にせず、ノード組んで動かしてみてください。

スポンサーリンク

ノード構成の全体像

では具体的な超シンプルなロボットアームのノードを組んでみたので、そのノード構成図を使って解説していきます。

構造

  • 各パーツにローカルTransform(Combine Transform)があります。
  • パーツの配置用のPositionTransform(Combine Transform)もあります。
  • それをMultiply Matricesノードで順番に掛けます。
  • 最終的な行列をそれぞれのパーツのTransform Geometryに入れる

流れを簡略化すると:

Arm01Transform

Multiply

Arm02Transform

Multiply

HandTransform

ここで重要なのは
Multiply Matricesノードでかける順番です。

スポンサーリンク

直感的なイメージの理解

まず、内部の行列計算ではなく、簡単な直感的なイメージとして解説してみます。(おそらく、シンプルな動きだったら、このイメージだけを持ってもらえばジオメトリノードで実装できると思います。)

右の図の行列計算のように、

  • 回転×移動
  • 移動×回転

の結果は違います。

イメージとしては

  • 回転×移動
    • 回転したオブジェクトのローカル空間の方向に移動
  • 移動×回転
    • 移動したオブジェクトのローカル空間で回転

しているという感じです。

つまり、最初にかけてるトランスフォームでオブジェクトのローカル座標の方向や位置が決まっています。

というわけで、このイメージを使って最初のノード構成を見てみます。

  • Arm01
    最初のArm01TransformはArm01の動きを作っています。Arm01にそのまま入って、それがワールド空間での最終的な動きとして見えています。
  • Arm02
    • 順番
      Arm01Transform
      ×
      Arm02Position
      ×
      Arm02Transform
    • Arm01Transformで、Arm01が親となり、向きや位置が基準になります。その基準の中で、Arm02Positionでパーツの取り付け位置を決めます。さらにその中で、Arm02TransformによってArm02自身の回転やスケールが行われます。
  • Hand
    • 順番
      Arm01Transform × Arm02Position × Arm02Transform
      × HandPosition × HandTransform
    • Arm01Transform × Arm02Position × Arm02Transformの部分はArm02の向きや位置が基準となり(Arm02が親となっている)、その基準の中でHandPositionでHandの取り付け位置を決め、それによって変更された基準位置の中でHandTransformでHandの動きをつけています。

マトリックスは、親の変換を基準にして子へ順番に掛けていくことで、階層的な親子関係の動きを作っています。
Arm01の変換を基準にして、Arm02の位置をその上に乗せ、その上でArm02自身の回転やスケールが適用されています。
さらにその結果を基準にしてHandも同じように積み重なっていく。

こうして変換をつなげていくことで、親子関係のような動きが自然に表現できるのです。

スポンサーリンク

内部の行列計算

以上でマトリックスの掛け算のイメージ的な説明をしてみましたが、内部の行列計算としては何をしているかもまとめておきます。もっと複雑な仕組みを作ろうと思ったら、内部で実際行列がどのような計算をしているかを把握する必要があるかもしれません。

Arm01(基準になる親)

Arm01は一番上の階層なのでシンプルです。

  • Combine Transformで回転・位置を作る
  • それをそのままTransform Geometryに入れる

ここにはまだはいません。

Arm01_world = Arm01Transform

Arm01のトランスフォームをそのままワールド座標で動かしています。

正確に言うと、
Arm01のトランスフォームは、Arm01のローカル空間で定義された変換をワールド座標へ変換する行列です。

Arm02(親の影響を受ける)

Arm02 の最終行列は次のように組まれています:

MArm02=MArm01MPosMLocalM_{Arm02} = M_{Arm01} \cdot M_{Pos} \cdot M_{Local}

ここで:

  • MArm01M_{Arm01}​:親(Arm01)のワールド行列(Arm01Transform)
  • MPosM_{Pos}​:接続位置(Arm02Position)
  • MLocalM_{Local}:Arm02 自身のローカル変換(Arm02Transform)

実際の適用順序

行列は右から適用されます。つまり頂点 v\mathbf{v} に対しては:
v=MArm01MPosMLocalv\mathbf{v’} = M_{Arm01} \cdot M_{Pos} \cdot M_{Local} \cdot \mathbf{v}

となり、処理順は以下のようになります。

① ローカル変換(Arm02自身の回転・スケール)

v1=MLocalv\mathbf{v_1} = M_{Local} \cdot \mathbf{v}

  • 座標系:Arm02のローカル座標
  • 内容:その場で回転・スケール

まだ原点はArm02の原点のまま

② 位置オフセット(関節位置へ移動)

v2=MPosv1\mathbf{v_2} = M_{Pos} \cdot \mathbf{v_1}

  • 座標系:Arm02のローカル座標
  • 内容:接続位置へ平行移動(Arm01の先端に移動)

Position Transformは、Arm02のジオメトリをローカル空間内で平行移動します。
この時点ではまだ親の影響は受けておらず、あくまでArm02のローカル基準で位置が決まっているだけです。
その後、親の行列を掛けることで、この位置が上位の空間(今回はワールド空間)に配置されます。

掛け算の順番について

オブジェクトは原点を中心に回転するため、先に位置を移動してしまうと、その位置ごと回転してしまいます。
これを防ぐために、まずローカルで回転させ、その結果を関節位置へ移動する必要があります。
行列は右から適用されるため、この順序を実現するにはPosition Transformを①のローカル変換より前に配置します。

③ 親の変換(Arm01の影響)

v3=MArm01v2\mathbf{v_3} = M_{Arm01} \cdot \mathbf{v_2}

  • 座標系:上位の空間(Arm01が最上位であればワールド空間)
  • 内容:Arm01の回転・位置の影響を受ける。

この行列計算でArm01の親の空間へ変換しています。Arm01を回すと Arm02 も一緒に回る理由は、②までの計算全体を親の空間の行列を作用させることで親座標の変形と同時に動くからです。

Arm01のトランスフォームは、Arm01のローカル空間で定義された変換をワールド座標へ変換する行列です。
その行列を最後に掛けることで、Arm02のローカルで計算された位置や姿勢もまとめてワールド空間へ変換されます。
そのため、Arm01を動かすとArm02も同じ変換を受けて一緒に動きます。

行列は右から順に適用されるため、ローカルで決まった変換結果に対して、最後に親の変換がまとめて適用されます。

座標系の流れまとめ

Arm02ローカル座標
↓(M_Local)
Arm02ローカル(回転後)
↓(M_Pos)
Arm02ローカル(位置オフセット後)
↓(M_Arm01)
ワールド座標(※Arm01が最上位なら)

Arm02はローカル空間で回転したあと、その結果を関節位置へ移動し、最後に親の変換によって上位の空間に配置されます。

Hand(Arm02の子としての変換)

Hand の最終行列は次のように組まれます:

MHand=MArm01MArm02PosMArm02LocalMHandPosMHandLocalM_{Hand} = M_{Arm01} \cdot M_{Arm02Pos} \cdot M_{Arm02Local} \cdot M_{HandPos} \cdot M_{HandLocal}

ここで:

  • MArm01M_{Arm01}​:親(Arm01)のワールド行列(Arm01Transform)
  • MArm02PosM_{Arm02Pos}:Arm02の接続位置(Arm01→Arm02の関節位置)
  • MArm02LocalM_{Arm02Local}:Arm02自身のローカル変換(Arm02Transform)
  • MHandPosM_{HandPos}​:Handの接続位置(Arm02→Handの関節位置)
  • MHandLocalM_{HandLocal}:Hand自身のローカル変換(HandTransform)

実際の適用順序

行列は右から適用されるため、頂点 v\mathbf{v}v に対しては:

v=MArm01MArm02PosMArm02LocalMHandPosMHandLocalv\mathbf{v’} = M_{Arm01} \cdot M_{Arm02Pos} \cdot M_{Arm02Local} \cdot M_{HandPos} \cdot M_{HandLocal} \cdot \mathbf{v}

したがって処理は以下の順番になる:

① Handローカル変換(Hand自身の回転・スケール)

v1=MHandLocalv\mathbf{v_1} = M_{HandLocal} \cdot \mathbf{v}

  • 座標系:Handのローカル空間
  • 内容:その場で回転・スケール
  • まだHandの原点基準での変形
② Handの接続位置へ移動(HandPosition)

v2=MHandPosv1\mathbf{v_2} = M_{HandPos} \cdot \mathbf{v_1}

  • 座標系:Arm02ローカル空間内
  • 内容:HandをArm02の先端(関節位置)へ配置
  • この時点ではまだ「Arm02空間の中のHand」
③ Arm02ローカル変換の影響

v3=MArm02Localv2\mathbf{v_3} = M_{Arm02Local} \cdot \mathbf{v_2}

  • 座標系:Arm01ローカル空間内
  • 内容:Arm02自身の回転・スケールの影響を受ける
  • HandはArm02に固定されているので、ここで一緒に動く
④ Arm02の接続位置へ移動

v4=MArm02Posv3\mathbf{v_4} = M_{Arm02Pos} \cdot \mathbf{v_3}

  • 座標系:Arm01ローカル空間
  • 内容:Arm02がArm01の先端へ配置される
  • HandもまとめてArm01の末端位置へ運ばれる
⑤ Arm01の親変換(最終的なワールド変換)

v5=MArm01v4\mathbf{v_5} = M_{Arm01} \cdot \mathbf{v_4}

  • 座標系:ワールド空間
  • 内容:Arm01の位置・回転・スケールが全体に適用される
  • Arm01を動かすとArm02もHandもすべて一緒に動く

構造

この構造はこういう意味になる:

  • Handは「Arm02にぶら下がっている」
  • Arm02は「Arm01にぶら下がっている」
  • 各レベルで
    • 自分のローカル変換
    • 接続位置(Position)
      が積み重なっている

Handはまず自分自身の形をローカルで作り、その後Arm02の手先位置に配置されます。
さらにArm02の動きに従って全体が変形し、最終的にArm01の動きによってワールド空間に配置されます。
このように、各ノードの変換を右から順に積み重ねることで、階層的な親子構造の動きを実現しているのです。

スポンサーリンク

マトリクスチェーンシステムのまとめ

このロボットアームは一言でいうと、ローカル変換を親の行列に掛けていくシステムです。

通常の3Dでは:

  • 子の座標 → 親の座標 → ワールド座標

という変換が裏で行われています。

今回やっていることはそれを手動でやっているだけです:

Arm01_world = Arm01Transform
Arm02_world = Arm01_world × Arm02Transform
Hand_world = Arm02_world × HandTransform

Multiply Matricesのチェーン = 親子階層そのものです。

  • Transform = 行列
  • Multiply = 変換の合成(親子関係を表現する演算)
  • チェーン = 階層構造

つまり行列をつないでいくことで、リグの親子関係を再現しています。

この方法のメリット

右図はマトリクスを使わずに組んだ同じロボットアームのノード構成です。(位置調整も同じノードでやってるため少なく見えますが、省略してるだけです。)

マトリクス制御のメリット

  • ジオメトリノード内で完結する
  • オブジェクト階層に依存しない
  • 完全に数式として制御できる

❶は右図のように他の方法でもできるんですが、❷が重要で、マトリクスで制御すると、オブジェクトを階層化する必要がなくなります。

↑Transform Geometryをつなげる方法だと、オブジェクトを階層化し、固定する必要がある。

リグの原型をノードで作れるっていうのは、ジオメトリノードが好きな方にはかなり嬉しいことなんじゃないかと思います。完全に数式として制御できるので、より柔軟にプロシージャルな動きが作れます。有機的な?動きなど作れたら面白そうですよね。

スポンサーリンク

関連記事

  • ジオメトリノードのためのマトリックス入門|位置・回転・スケールを行列で理解する
    ジオメトリノードでマトリックスを使ったトランスフォーム変換をするために簡単に行列計算などを解説しています。
  • マトリックス系ノードの紹介
    マトリックス変換を便利に使うために、その他のマトリックス計算やトランスフォームに使えそうなノードを紹介していきます。
  • 回転系ノードについて解説
    インスタンスの回転、ベクトル回転、回転の補間やクォータニオンなど、回転系ノードの使い方を網羅。
  • カーブの接線・法線・従法線を解説:Tiltのトラブルを解決する
    カーブの接線・法線・従法線とは何かを解説し、Set Curve Normalノードを使ってねじれを修正する方法を説明

ジオメトリノード記事をまとめているガイド記事です。

コメント