PyODEのサンプル

Pythonでカーシムを作る!?

tags:python, 3d, tips
created:2007-02-05T20:24:24

PyODEのサンプル。 PyODEで車の挙動を再現してみました。

スクリーンショット

../../../_images/ode_test.png

インタラクティブウインドウのスライダーで加減速やハンドリングができるようになっています。

利用したパッケージ

VPython : http://vpython.org/
Windows版インストーラ
PyODE : http://pyode.sourceforge.net/
Windows版インストーラ

Python2.4.xとともに以上のパッケージをあらかじめインストールしておいてください。

ソースコード

長いので、「 ソース 」を参照。

目的

PyODEを学習目的でいじってみました。

本当はビジュアライズをPyOgreで実現したかったんですが、 VPythonのほうがカットアンドトライが楽でしたので一旦VPythonを用いて作成することに。

ODEのドキュメントの更新は滞っているようで、 細かい部分はいろいろいじってみないとわかりませんでした。

その辺はPythonの内省機構にかなり助けられました。

不明なことがあれば「help(クラスやメソッド)」で!

ODEの特徴

  • 運動力学処理系 質点と質点の関連付け拘束による運動シミュレーション
  • 衝突検出処理系 形状の入力と衝突検出グループコントロール

以上の処理系が完全に分離してあります。

また、視覚化部分と合わせて3処理系の組み合わせで 3Dモデルの運動シミュレーションを実現することになります。

なので、固定障害物や地面を実現するには衝突検出処理系と視覚化処理系を組み合わせ、 動く物体を3処理系組み合わせて実装します。

ODEには視覚化処理系が提供されていません。

任意の視覚化処理系を一緒に用いる必要がありますが、OpenGL系との相性がよさそうです。

PyOpenGLでも、PyOgreでもいいのでしょうが、今回はVPythonの手軽さに負けました。

用いたクラス群
処理系 対象
運動力学処理系 ode.World ode.Body
衝突検出処理系 ode.Space ode.GeomBox ode.GeomCCylinder
視覚化処理系 visual.frame visual.box visual.cylinder

車モデルの構成

箱型ボディに4つのシリンダー型車輪をヒンジ2というジョイントで結合しました。

サスペンションの実現

ヒンジ2ジョイントは本家ドキュメントによると

../../../_images/hinge2.jpg

このようなイメージです。

おおまかに用いた設定は以下のようになりました。

joint = ode.Hinge2Joint(world)
joint = ode.Hinge2Joint(world)
joint.attach(body1, body2)
joint.setAnchor((x1,y1,z1))
joint.setAxis1((0,1,0))
joint.setAxis2((1,0,0))
joint.setParam(ode.ParamHiStop, max_angle)
joint.setParam(ode.ParamLoStop, min_angle)
joint.setParam(ode.ParamCFM, 1.0E-4)
joint.setParam(ode.ParamStopCFM, 1.0E-8)
joint.setParam(ode.ParamStopERP, 0.8)
joint.setParam(ode.ParamSuspensionCFM, 1.0E-4) # <- サスやわらかさ(0で剛体)
joint.setParam(ode.ParamFudgeFactor, 2.0)      # <- 粘性係数
joint.setParam(ode.ParamVel, 0.0)              # <- 目標角速度
joint.setParam(ode.ParamFMax, 100000.0)        # <- 最大トルク

サスペンションとしては以下の2点が重要なパラメータです。

  • 「ParamSuspensionCFM」
  • 「ParamFudgeFactor」

前者はばね定数の逆数のように。後者はオイルダンパーの粘度のように効果が現れます。

ERPは誤差修正を開始する基準のようなもので、1.0だと誤差を許さない。 (0.2~0.8が推奨値らしい) CFMというのは差分を修正する力のようなもので「やわらかさ」に例えることができます。

どちらもあんまり厳しく硬くすると 発散 [1] します。

FudgeFactorは油の中のような粘度の係数です。

「ParamHiStop」、「ParamLoStop」は最大操舵角のを決定するのに使えます。

すべての角度指定はラジアン単位です。

注釈

物理エンジン系では、単位系を規定しないが角度はラジアンに決めるというのが多いようです。

一度自力でこね回すとわかりますが、ラジアンは非常に計算させやすいのです。

物理計算系に踏み込む人はdegでの角度指定は卒業してくださいね~。

衝突の処理

衝突演算にはジオメトリインスタンスを作成します。

形状には以下のようなものがあります。

  • Sphere
  • CappedCylinder
  • Ray
  • Plane
  • Box
  • TriangleMesh

車体にはBoxを、車輪にCappedCylinderを使いました。

CappedCylinderは以下のようになっているのが難点。

../../../_images/capped_cylinder.png

なので、横にも転がってしまいますね。

でも球とキャップつきシリンダーの衝突判定はそんなに変わらないほど軽量なのです。

キャップなしシリンダー(ODE標準では実装されていない)や 箱の方が処理が重たいので多用には注意!

カテゴライズ

geom.setCategoryBits(0x80000000)
geom.setCollideBits(0xffffffff)

といったように32bitの各ビットでカテゴライズと、衝突検出する対象を設定できます。

今回の例では、

  • ボディと地面にはビット1をON
  • タイヤにはビット2をON
  • 障害物にはビット3をON

というようにカテゴライズ。

  • タイヤと障害物と地面はそれぞれ当たり判定あり。
  • タイヤとボディは当たり判定なし。
  • ボディと地面は当たり判定あり。
  • 障害物と地面は当たり判定なし。

といったように細かく指定できます。

当たり判定処理はあっという間にCPUパワーを消費してしまうので、 大規模なシミュレータやゲームを作成するときは 極力検出対象の組み合わせが最低限になるよう適切なカテゴライズをすることが 大変重要になるでしょう。

試してみてね

サンプルをうまく動かしてスピードに乗った状態で障害物に乗り上げても大して車がはねないことがわかります。

試しにサスペンションを硬くしてみてね。

サスペンションの重要性が良くわかると思います。

また粘性係数をゼロにすると、揺れが一向に収まらないこともわかります。

こんなに揺れ続ける車に乗ったら酔いまくりですね。

感想

かなりお手軽に3次元モデル運動シミュレーションが実現できることがわかりました。

もう少しソースは整理しないといけないなぁとは思いますが。。。

しかし、これだけのものをサンデープログラミングで 作れちゃうのは以前では考えもしませんでしたね~。


[1]制御工学の用語です。多くはガタガタ振動状態になったり空間のかなたに吹っ飛んでいったりします。