Python/MuJoCo入門!1自由度ばねマスダンパーをモデリング

  • このエントリーをはてなブックマークに追加

 sim2realの分野でよく使われるMuJoCoはPythonにpip installして使うことができます。ここでは、当ブログでお馴染みの1自由度ばねマスダンパーをモデリングしてシミュレーションする方法を通してMuJoCoに入門します。さらに、MuJoCoで計算した結果をPythonで動画にする方法を学びます。

こんにちは。wat(@watlablog)です。sim2realの世界に入門したいので、今回はMuJoCoをPythonで触ってみます!

MuJoCoとは?

 MuJoCoMulti-Joint dynamics with Contact)は、関節でつながった剛体(ロボットなど)が環境と接触・摩擦しながら動く状況を、高速かつ高精度に解くことを重視した物理エンジンです。ロボティクス、バイオメカ、強化学習など「大量試行が必要で、接触が絡む」研究開発でよく使われます。
・GitHub:https://github.com/google-deepmind/mujoco

 もともとはRoboti LLCにより開発・商用提供されていましたが、2021年10月にDeepMindがMuJoCoを買収し、研究コミュニティを支える目的で無償提供へ舵を切りました。
・DeepMindブログ:https://deepmind.google/blog/opening-up-a-physics-simulator-for-robotics/

Advertisements

 その後、2022年5月にコードベース全体のオープンソース化が完了し、GitHub上でApache-2.0ライセンスの下、DeepMind(Google DeepMind)がメンテナンスする形になっています。
・DeepMindブログ:https://deepmind.google/blog/open-sourcing-mujoco/

 さらに、MuJuCoは pip installで簡単にインストールでき、Pythonプログラミングで利用することも可能です。ここではMuJuCoをPythonで使う方法をまとめます。

動作環境

 この記事のコードは以下の環境で動作を確認しました。

Mac OS macOS Sonoma 14.5
チップ Apple M3
CPU 1.4[GHz]
メモリ 16[GB]

PythonでMuJoCoを使ってシミュレーション結果を得る

MuJoCoをpip installする

 MuJoCoはC++で書かれていいますが、pybind11を使ってC++のコードをPythonから簡単に呼び出せる形になっている mujocoというライブラリがPyPIにあります。以下のコマンドで pip installしましょう。

・参考:https://mujoco.readthedocs.io/en/3.1.5/python.html

モデルを.xmlでつくる

 いきなりロボットや倒立振子をモデリングする前に、この記事では入門的に下図に示す1自由度ばねマスダンパーモデルを作成して振動のシミュレーションを行います(自分にとってはこれが最も簡単なので)。

1自由度ばねマスダンパーモデル

 MuJoCoで使うモデルは .xmlファイルで作成します。このモデルはMJCFと呼ばれます。まずはリポジトリに空の oscillator.xmlをつくりましょう。ターミナルに次のコマンドを打ってつくるのがはやいと思います(作り方は自由です)。

MJCFの他に、ロボティクス分野でよく使われるURDFUnified Robot Description Format)も使えますが、ここでは説明しません。

  oscillator.xmlにモデル情報を記述します。内容は後述します。

 1行目はモデルのタイトルです。ここでは1自由度振動モデルを意味する 1d_oscillatorとしました。
 2行目は optionの設定です。それぞれ次の意味があります。MuJoCoは古くからのシミュレーションソフトにあるように単位系はユーザーが決める仕様です。MuJoCo側はユーザーが入力した数値を単に次元に合わせて計算するだけです。つまり、長さを[m]、時間を[s]で入力した場合速度は[m/s]になります。今回はSI単位系長さ=[m], 重さ=[kg], 時間=[s])を使いましょう。何も入力しないと設定されるデフォルト値もSI単位系とSI併用単位(°)が使われるので、SI単位系で書いておけば間違いないと思います。

  • gravity:重力
    0, 0, 0は3D空間\((x, y, z)\)のどの方向にも重力が働いていないことを表現しています。
  • timestep:シミュレーション時の時間刻み
    今回は0.001[s]単位で結果を出力するようになります。

 3行目から9行目に worldbodyがありますが、今回は1自由度なので bodyを一つだけ記述します。この bodyに以下の諸元を記述します。

  • name body jointの名前
  • pos:初期位置

  jointの項目でジョイントの設定を行います。MuJoCoでは諸元が入力されたモデルをジョイント(締結構造)で結合するモデリングを行います。この考え方はMBD(Multi-Body Dynamics)の分野でよく出てくるもので、以下の記事でも扱ったようにこの方法でモデルを記述するとラグランジュ法などを使った運動方程式の導出が機械的になって楽になります。
・参考:Python/SymPy:ラグランジュ法による運動方程式の自動導出

  • type jointの種類
    ここでは slide(並進1自由度ジョイント)をx方向指定で設定していますが、他にも回転1自由度ジョイント hinge、回転3自由度(球)ジョイント ball、6自由度ジョイント freeがあります。
    ↓以下のリンク先に各ジョイントの動きがわかりやすい動画もあります。
    ・参考:https://mujoco.readthedocs.io/en/stable/overview.html
  • stiffness:ばね定数
  • damping:粘性係数
  • springref:つり合い位置

 ジオメトリの設定を geomに記述します。今回は衝突問題を解きませんが、衝突問題の場合はこの形やサイズによって結果が変わります(球の場合は球の衝突判定になるなど)。

  • type:質点の形
  • size:サイズ
  • mass:質量

Pythonコードで計算する

 次のPythonコードを用意(名前は任意)します。まずはシミュレーションを実施して matplotlibによるグラフを表示するまでをコーディングしました。

MuJoCo特有のコードを見てみましょう。

MjModel と MjData

 MuJoCoでは、モデルの構造や各種パラメータ( body jointの構成、形状、質量・慣性、描画設定など)を MjModelが保持します。一方、シミュレーション中に変化する状態(一般化位置 qpos,一般化速度 qvel, 時間 timeなど)や計算結果・ワークスペースは MjDataに格納されます。ここではそれらのインスタンス化を行なっています。

jointインデックスの探査

      joint名(今回は x)からID番号を検索する .mj_name2idを使って番号を取得し、 qpos配列・ qvel配列のどこにその jointの値が入っているかインデックスを探査します。 jointの種類(今回は slide)によって要素数が異なり、さらに一般化位置 qposと一般化速度 qvelの中でも要素数が異なるので、シミュレーションをかける前にインデックスを一回だけ把握しておきます。

    初期化と.mj_forward

     最初の3行は初期化です。 MjDataの中の一般化位置と一般化速度を手動で書き換えています。また、手動で書き換えたのでワールド座標系との整合をとるために一度 .mj_forwardを走らせます。

    mujoco.mj_step(model, data)

     シミュレーションを1ステップずつ回す部分です。ただ、Pythonでforループを回すと内部的にはCの高速な物理計算がされているといってもPython→C呼び出しにおけるオーバーヘッド分は出てしまいます。どうやらこのオーバーヘッド分による損失を軽減する対策もいくつかあるようですが、それは後日にします。 mj_stepでステップ毎に計算する目的は、後でモデルベース制御強化学習に応用したいからです。

    結果を理論値と比較する

     上記コードを実行するとmatplotlibのグラフが表示されます。下図に上記コードに減衰自由振動の理論式と比較する処理を追加した結果を示します。
     MuJoCoによるシミュレーション結果と理論式が一致していることが確かめられました。

    MuJoCoによる結果と理論式との比較結果

     理論式は「Pythonで1自由度減衰系の自由振動シミュレーション」という記事で紹介しています。今回のMJCFモデルのパラメータに合わせて書くと次のPythonコードになります。

    MuJuCoのシミュレーション結果を動画にする

    GIF動画を作成する

     MuJuCoを pip installすると mujuco.viewerというGUIビューア(レンダリングはOpenGL)もインストールされます。 mujuco.viewerには色々な起動方法がありますが、ここではPythonコード内部でシミュレーション結果の画像を連番で取得し、最後にGIF動画にする方法を紹介します。

    画像の間引き

     GIF動画を作るための画像はすべて imgフォルダの中に蓄積されます。画像が多すぎると容量が大きくなりすぎてしまったり、実時間で再生するのが難しくなったりします。そこで、上記コードには画像を一定の秒数間隔で間引く設定を追加しました。 FRAME_DTの秒数を変更することで、何秒間隔で画像を取得するかを決めます。

    GIF画像作成関数

     GIF画像作成関数は以下の記事のコードをそのまま流用しています。
    Pythonで複数画像からGIFを作る時に便利な処理まとめ

    .xlmファイルでカメラの引きを設定

     上記コードを実行するとanimation.gifというGIF動画が作成されますが、今回のシミュレーションでは点の大きさが小さいのに対して変位は大きいため、画面に映らないほど大きく動きます。次のコードの2行目に記載の statisticを追加し、 extentの値を大きくするとカメラを引いた位置に設定できます。何も設定しないと、全体のサイズに対してMuJuCoが自動で画面のサイズを決定します。

    GIF動画の例

     こちらが上記コードで得られる動画です。

    MuJuCoで解析した1自由度減衰自由振動の動画

    まとめ

     この記事ではMuJoCoの入門として、すでに当WATLABブログでよく扱っている1自由度のばねマスダンパーモデルを対象としたシミュレーションを行いました。MJCFとして .xmlにモデルを記述する方法やPythonを使って計算するコードを紹介しました。
     MuJoCoは pip installで簡単にインストールできるので、簡単にPythonで呼び出すことができます。Pythonを使うことで動画の作成までコードで書けるため非常に使いやすいと思いました。

    MuJoCoに入門しました!これはPythonで扱える機構解析ソフトとしても非常に使いやすいと思います!
    Xでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

    SNSでもご購読できます。

    コメントを残す

    *