ジョイントの実装と計算方法

自作のActionScript3.0用の物理エンジン、
OimoPhysicsにいくつかジョイントを実装しました。

>> Launch Joints demo

 

実装したのは、距離ジョイント、ボールジョイント、ヒンジジョイントの3つです。

 

距離ジョイントは上のデモでは使われていませんが、
剛体同士の距離を一定に保つものです。

2Dの物理エンジンでは多用されていますが、3Dの物理エンジンでは
そもそも実装されていないことが多く(BulletやODEなど)、あまり使い所がないのかもしれません。

実装は一番簡単でした。
が、重心から外してつけると途端に不安定になります。

角速度から得られる相対速度と、実際の動きとの食い違いが原因っぽいですが、
対処のしようがないことが発覚したので放置。

 

ボールジョイントは、肩の関節のような動きをするジョイントです。
3Dならではのジョイントで、こちらはよく使われています。

これも実装はそこまで大変ではありませんでした。

内部では、接続点の相対速度 = 0 というシンプルな拘束を解いているのですが、
与える加速度から必要な力を逆算する必要が出てきます。
少し難しくなりますが、逆算の方法を書いておきます(読み飛ばしてもらっても構いません)。

 

剛体の質量を m 、慣性テンソル(回転に対する質量のようなもの)を I とします。
ここである相対位置 r から、剛体に F の力を与えたとします。
すると剛体の加速度 lv と角加速度 av が次のように発生します。
(× はベクトルの外積を表します)

lv = F / m
av = (r × F) / I

加速度はそのままですが、
角加速度は相対位置と力の外積によって計算されるのが剛体の特徴です。

また、速度と角速度が分かっている時に、
相対位置 r での速度 v は次のように求められます。

v = lv + (av × r)

速度に角速度と相対位置の外積を足し合わせたものです。
角速度は軸に沿った回転の速度であって並進速度ではないので、
ここで外積を使って局所的な並進速度に変換している訳ですね。

これらの式によって、
ある相対位置 rF の力を加えた時に発生する r 上での加速度が計算できます。

v = lv + (av × r)

lv = F / m
av = (r × F) / I

を代入し、

v = lv + (av × r)
v = (F / m) + (((r × F) / I) × r)

が得られます。
ここからこの式を F = ... の形に変形します。
r 上で v の加速度を発生させたいときに必要な力 F を求めるためです。

v = (F / m) + (((r × F) / I) × r)
v = (F / m) - (r × ((r × F) / I))

外積順序を反転させ、 r × の形に揃える

しかしここで問題が発生します。
それは ベクトルの外積は結合法則が成り立たない というものです。
このままでは括弧の中から F を外に出してやることができず、
F = ... の形に変形することができません。

そこで、ベクトルの外積を行列の乗算に変換することを考えます。
r × F[r ×]F という行列とベクトルの乗算になるとすると、
行列 [r ×] は、外積の定義から次のように表せます。

│0, -rz, ry
│rz, 0, -rx
│-ry, rx, 0│

一般に行列は分配法則や結合法則を満たすので、
これで結合法則の呪縛から逃れることができます。
※注:交換法則は満たさない

これを使うと、さっきの式は、

v = (F / m) - (r × ((r × F) / I))
v = (F / m) - ([r ×](([r ×]F) / I))

と書き換えることができます。
せっかくなので、 / m/ I も同じように行列にしてみましょう。

/ m は単なる実数での除算なので、スケーリングとみなすことができます。
よって、行列 [/ m] 次のように表せます。

│1/m, 0, 0│
│0, 1/m, 0│
│0, 0, 1/m│

また、 / I は行列での除算なので、
行列 [/ I]I の逆行列 I-1 になります。

これらの行列をさっきの式に代入します。

v = (F / m) - ([r ×](([r ×]F) / I))
v = ([/ m]F) - ([r ×]([/ I]([r ×]F)))

ここではベクトルを列ベクトルとして扱ってきたので、
ベクトルと行列の乗算の順序は、全て 行列×ベクトル となっていることに注意してください。
ベクトルを3行1列の行列とみなすことで、行列と同じ扱いをするためです。

後は括弧を外しつつ F を外に出していきます。

v = ([/ m]F) - ([r ×]([/ I]([r ×]F)))
v = [/ m]F - [r ×][/ I][r ×]F
v = ([/ m] - [r ×][/ I][r ×])F

ついに F が外に出ました。
後は F = ... の形に直すだけです。

([/ m] - [r ×][/ I][r ×])F = v
両辺を交換
F = v / ([/ m] - [r ×][/ I][r ×])
行列で除算
F = ([/ m] - [r ×][/ I][r ×])-1v
逆行列に

ようやく F が求まりました。
物理エンジン内ではこの式を使い、与えるべき力を求めています。
相対速度 = 0 の拘束なら、 v に -相対速度 を代入して F を求める

 

長くなってしまいましたが、最後にヒンジジョイントについてです。

ヒンジジョイントは、その名の通りヒンジ(蝶番)を再現するジョイントです。
ドアや人形の肘や膝などに使われます。

使い勝手がよく、なかなか強力なジョイントですが、
軸を曲げようとするような力にはかなり軟弱で、
安定性を保つのにえらく苦労しました…。

上のデモで、ぶら下がっている箱を横から押してみると、
曲がるべきでない方向に簡単にぐにょりとなるのが分かります。

残念ながら、沢山つなげて使うには、ヒンジジョイントはあまり向かないようです。

 

ソースコードはこちら(GitHub)に置いてあります。
まだ開発途中ですが、よければ試してみてください。
コンパイルにはStage3Dの動く環境とAGALMiniAssemblerが必要です

3 Responses to “ジョイントの実装と計算方法”

  1. nanasi より:

    あけましておめでとうございます
    これからもがんばってください

  2. syounen より:

    だいぶ遅れましたが明けましておめでとうございます。
    僕は、毎日の習慣のようにつぶつぶ2を中心に遊んでいます。
    すごく楽しいのでこれからも頑張ってください。応援しています

Leave a Reply