理系的な戯れ

理工学系とくにロボットやドローンに関する計算・プログラミング等の話題を扱って、そのようなことに興味がある人たちのお役に立てればと思っております。

MuJoCo入門:みんなの物理シミュレーションチュートリアル

第1章:MuJoCoとは何か?

物理シミュレーションエンジンは、ロボティクスや強化学習の研究・開発において不可欠なツールです。その中でも MuJoCo(Multi-Joint dynamics with Contact) は、高精度で効率的なシミュレーションを提供することで広く使われています。

本章では、MuJoCoの基本的な概要や歴史、そしてどのような場面で使われるのかをわかりやすく解説します。


1.1 MuJoCoの概要

MuJoCoは、「連続的な接触と制御を含む多関節系の物理的シミュレーション」を高精度に、かつ高速に行うことができるエンジンです。もともとはエモリー大学の Emanuel Todorov 教授によって開発され、その後 Google(現在の DeepMind)に引き継がれ、オープンソースとして公開されています。

現在の最新版MuJoCoは、Pythonバインディングを公式にサポートしており、mujoco パッケージとして利用可能です。これは従来の mujoco-py よりも簡潔で安定しており、今後はこちらを使用するのが推奨されています。


1.2 MuJoCoの強みと特徴

MuJoCoは、他の物理エンジンと比べて以下のような特徴を持っています:

  • 高精度かつ高速な物理計算 → 特に剛体同士の接触・摩擦をリアルに再現できる

  • 柔軟なモデル定義(MJCF形式) → XMLベースの記述で複雑な構造体も定義可能

  • 強化学習に最適化されたAPI設計 → ステップ更新や状態取得が高速で、学習に使いやすい

  • Pythonから簡単に操作可能 → 最新版では import mujoco だけで始められる


1.3 どんな分野で使われているか?

MuJoCoは以下のような分野で多く使われています:

  • ロボティクス研究 実機での実験が難しい初期段階で、ロボットの動作や制御を事前に検証可能

  • 強化学習(Reinforcement Learning) OpenAIやDeepMindをはじめとする多くの研究機関がMuJoCoを用いた学習環境を開発 (例:Ant, HalfCheetah, Humanoidなど)

  • 神経科学・バイオメカニクス 筋骨格モデルや運動の再現にMuJoCoが使われるケースもある


1.4 他の物理エンジンとの比較

以下は、MuJoCoとよく比較される物理エンジンとの違いです:

エンジン 特徴 MuJoCoとの主な違い
PyBullet 無料・オープンソース、ロボットも豊富 接触精度や制御の安定性はMuJoCoが上
Isaac Gym GPU対応で高速、大規模学習向け 導入・構築がやや複雑
Unity ML-Agents 3Dゲーム向け。視覚的には優れるが物理は簡易 精密制御には不向き

MuJoCoは「研究用途におけるリアルな物理再現と計算効率のバランスが非常に優れている」という点で特に評価されています。


1.5 なぜMuJoCoを学ぶべきか?

MuJoCoは一見すると専門的に思えますが、実はとても教育的で、ロボティクスや制御工学を学ぶには最適なツールです。物理シミュレーションを通じて、力、トルク、質量、摩擦といった概念を「見て」「触って」「動かして」理解できます。

また、強化学習に興味がある学生にとっては、MuJoCoはその「土台」となる環境を構築するための入り口とも言えるでしょう。


🔍 まとめ

MuJoCoは、ロボットや動物のような「多関節系」の動作をリアルかつ高速にシミュレートするために設計されたエンジンです。 学生にとっても、制御の基礎や機械学習との接点を学ぶ貴重な体験を提供してくれます。


第2章:MuJoCoのインストールと環境構築(Python公式バインディング)

MuJoCoを使い始めるには、Python環境に最新版のMuJoCoとその可視化ツールを導入する必要があります。本章では、Mac / Windows / Linux いずれの環境でも使えるインストール手順をわかりやすく説明し、最後にサンプルコードで動作確認を行います。


2.1 前提条件の確認

まずは、MuJoCoをインストールする前に以下の条件を満たしていることを確認してください。

  • Python 3.9以上
  • pip が使えること
  • 仮想環境(venv または conda)の利用を推奨
  • gccclang などのCコンパイラ(LinuxやMacでは必要なことがあります)

※仮想環境を使う理由: システム全体に影響を与えず、必要なパッケージを独立して管理できるからです。

# 仮想環境の作成と有効化(例: venv)
python3 -m venv mujoco-env
source mujoco-env/bin/activate  # Windowsなら mujoco-env\Scripts\activate

2.2 MuJoCoのインストール(pip経由)

最新版MuJoCoはPyPIからインストール可能で、次のコマンドを実行するだけでOKです:

pip install mujoco

これで、物理シミュレーション本体とPythonバインディングが導入されます。

さらに、標準のGUIビジュアライザを使いたい場合は、追加で glfwPyQt6 を入れる必要があります。

pip install glfw PyQt6

ただし、MacではPyQt6がうまく動作しないことがあるため、GLFWを使ったカスタムビジュアライザを後ほど紹介します。


2.3 MuJoCoのインストール場所とモデルの保存場所

最新版のMuJoCoは~/仮想環境名/lib/python??.?/site-packages/mujocoにインストールされるはずです。

また、さまざまなネットにあるサンプルのモデルなどはダウンロードされませんので注意が必要です。このため、ネット上にあるサンプルプログラムはモデルファイルが存在しないためエラーになります。

必要なモデルは、後述するように、コードの中に埋め込んで記述するか、自分でダウンロードしてフォルダに配置し、サンプルプログラムでは、ファイルの場所やファイル名が一致するように修正する必要があります。


2.4 テストコードでインストール確認

以下のコードで、MuJoCoが正しく動作するかをチェックしましょう:

import mujoco
import numpy as np

# サンプルXMLモデルを読み込む
model = mujoco.MjModel.from_xml_string("""
<mujoco>
  <worldbody>
    <body>
      <geom type="sphere" size="0.1" pos="0 0 0"/>
    </body>
  </worldbody>
</mujoco>
""")

# データオブジェクト作成
data = mujoco.MjData(model)

# シミュレーションを10ステップ実行
for _ in range(10):
    mujoco.mj_step(model, data)

print("MuJoCoのシミュレーションが正常に実行されました。")

このコードがエラーなく実行されれば、インストールは成功です。


2.5 トラブルシューティング(よくあるエラー)

エラー内容 原因と対策
ModuleNotFoundError: No module named 'mujoco' pip install mujoco を忘れている
RuntimeError: Failed to initialize OpenGL MacでPyQt6が正しく動作していない(GLFW使用を検討)
Qt関連の描画エラー PyQt6がインストールされていない/互換性がない
mujoco.mj_step でSegmentation Fault Linuxでの依存ライブラリの不足(libGL, libosmesa等)

✅ まとめ

  • 最新のMuJoCoは、pip install mujoco で簡単に導入可能
  • Mac環境では可視化周りで問題が出る場合があるため、GLFWの使用がおすすめ
  • 動作確認には、シンプルなXMLモデルと mj_step の利用が便利

第3章:シミュレーションを動かしてみよう(Mac対応・GLFW可視化)

MuJoCoをインストールしたら、次は実際にシミュレーションを動かしてみましょう。ここでは、基本的な物理シミュレーションの実行方法と、ビジュアライズ(視覚化)手法について解説します。

特にMacユーザーが直面しやすい可視化の問題にも配慮し、標準的な方法とGLFWによる代替方法の両方を紹介します。


3.1 モデルの読み込みと初期化

MuJoCoでは、シミュレーション対象となる構造体を MJCF形式(XML) で記述し、それをPythonから読み込むことでシミュレーションを開始します。

以下は、最も簡単な「空中に浮いた球体」のモデルを定義し、読み込むコードです:

import mujoco
import numpy as np

xml = """
<mujoco>
  <worldbody>
    <body name="ball" pos="0 0 0">
      <geom type="sphere" size="0.1" rgba="1 0 0 1"/>
    </body>
  </worldbody>
</mujoco>
"""

model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

MjModel はモデル定義(質量、リンク構造など)を保持し、MjData は現在の状態(位置、速度など)を管理するオブジェクトです。


3.2 標準ビジュアライザ(viewer.launch_passive

MuJoCoには、シミュレーションの状態を可視化するための 標準ビジュアライザ が用意されています。これは、mujoco.viewer モジュールに含まれる Qt ベースのツールです。

以下は、先ほどのモデルを表示する最小構成のコードです:

import mujoco
import numpy as np
from mujoco import viewer

xml = """
<mujoco>
  <worldbody>
    <body name="ball" pos="0 0 0">
      <geom type="sphere" size="0.1" rgba="1 0 0 1"/>
    </body>
  </worldbody>
</mujoco>
"""

model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

with viewer.launch_passive(model, data) as v:
    while v.is_running():
        mujoco.mj_step(model, data)
        v.sync()

✅ 特徴

  • 視点操作(ズーム・回転)や再生制御がGUI上で可能
  • クロスプラットフォーム(Windows/Linuxでは安定)

⚠️ 注意点(Macユーザーへ)

  • Mac環境ではPyQt6やOpenGL周りの依存関係でクラッシュすることがあります
  • 特に Apple Silicon(M1/M2)では表示されない、落ちる、ウィンドウが固まる といった不具合が報告されています

そのため、Macでは以下で紹介するGLFWによる可視化方法をおすすめします


3.3 GLFWを使ったMuJoCo可視化(Mac対応)

MuJoCoは、OpenGLレンダリング用の低レベルAPIを公開しています。これを利用して、自前でGLFWウィンドウを開き、シーンを描画することができます。

以下に最小構成の実装例を示します:

🔧 必要パッケージのインストール

pip install glfw

📦 実装例コード

import mujoco
import glfw
import numpy as np

# 初期化
xml = """
<mujoco>
  <worldbody>
    <body name="ball" pos="0 0 0">
      <geom type="sphere" size="0.1" rgba="1 0 0 1"/>
    </body>
  </worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

# GLFWの初期化
if not glfw.init():
    raise RuntimeError("GLFW初期化に失敗しました")

window = glfw.create_window(800, 600, "MuJoCo GLFW Viewer", None, None)
glfw.make_context_current(window)

# 可視化関連オブジェクトの初期化
scene = mujoco.MjvScene(model, maxgeom=1000)
cam = mujoco.MjvCamera()
opt = mujoco.MjvOption()
con = mujoco.MjrContext(model, mujoco.mjtFontScale.mjFONTSCALE_100)

while not glfw.window_should_close(window):
    mujoco.mj_step(model, data)

    viewport_width, viewport_height = glfw.get_framebuffer_size(window)
    viewport = mujoco.MjrRect(0, 0, viewport_width, viewport_height)

    mujoco.mjv_updateScene(model, data, opt, None, cam, mujoco.mjtCatBit.mjCAT_ALL, scene)
    mujoco.mjr_render(viewport, scene, con)

    glfw.swap_buffers(window)
    glfw.poll_events()

glfw.terminate()

✅ メリット

  • Macでも安定動作(QtやPyQtの不具合に左右されない)
  • 視点操作やカメラ設定もプログラム的に制御可能
  • 複数ウィンドウ表示や録画拡張にも対応しやすい

⚠️ 注意点

  • カメラ制御(マウスによるズーム・回転)などは自作が必要
  • MjvScene, MjvCamera, MjrContext などMuJoCo特有の構造体に慣れる必要がある

🔍 まとめ

  • 標準の mujoco.viewer は簡単に可視化できるが、Macでは不安定なことがある
  • GLFWとMuJoCoの低レベルAPIを組み合わせることで、安定した独自ビジュアライザを作成できる
  • 可視化の安定性が重要なプロジェクトでは、GLFW方式の採用をおすすめ

第4章:MJCF(MuJoCo XML)でモデルを作る

MuJoCoで独自のシミュレーションを行うには、「何をどう動かすか」を記述するモデルファイルが必要です。MuJoCoではそのモデル定義に MJCF(MuJoCo XMLフォーマット) を使います。

本章では、MJCFの基本構造を理解し、シンプルなロボットモデル(2リンクアーム)を実際に作成することで、カスタムシミュレーションの第一歩を踏み出します。


4.1 MJCFとは?

MJCF(MuJoCo XML Model Format)は、MuJoCo専用のXML記述フォーマットです。物体の構造、ジョイント(関節)、力の印加、センサなど、物理的なシミュレーションに必要な情報をすべて定義できます。

MuJoCoでは、センサの値を読み取ることで状態の推定や学習アルゴリズムへのフィードバックが可能です。<sensor> タグを使えば、関節角度、角速度、トルク、接触力など、さまざまな物理量をシミュレーション中に取得できます。センサーの具体的な使い方については、次章で詳しく解説します。

主な構成要素(タグ):

タグ 役割
<worldbody> モデルの物理世界の構成
<body> 剛体の定義(位置、形状など)
<geom> 衝突判定用の形状(球、箱など)
<joint> 関節の定義(回転やスライド)
<actuator> モーターなどの駆動系
<sensor> センサーの定義(位置、速度、力など)

4.2 最小モデルの例:球体1個

以下は、空中に球体を1つ浮かべただけの最小モデルです:

<mujoco>
  <worldbody>
    <body pos="0 0 0">
      <geom type="sphere" size="0.1" rgba="0.8 0.2 0.2 1"/>
    </body>
  </worldbody>
</mujoco>
  • <body pos="0 0 0">:位置 (x, y, z)
  • <geom type="sphere" size="0.1">:半径0.1の球体

このモデルを使うだけで、物理的な落下や接触をシミュレーションできます。


4.3 実践:2リンクアームを作ってみよう

今度は、回転関節を持つ2つのリンクを連結した「2リンクアーム」のモデルを定義してみます。これはロボットアームの基本構造です。

<mujoco>
  <worldbody>
    <body name="link1" pos="0 0 0.1">
      <geom size="0.05 0.3" type="capsule" fromto="0 0 0 0 0 0.6" rgba="0.2 0.6 0.8 1"/>
      <joint name="joint1" type="hinge" axis="0 1 0" pos="0 0 0"/>
      
      <body name="link2" pos="0 0 0.6">
        <geom size="0.05 0.3" type="capsule" fromto="0 0 0 0 0 0.6" rgba="0.8 0.4 0.2 1"/>
        <joint name="joint2" type="hinge" axis="0 1 0" pos="0 0 0"/>
      </body>
    </body>
  </worldbody>
</mujoco>

解説:

  • リンク1・2はネストした<body>タグで表現され、それぞれが関節(<joint>)で回転可能。
  • fromto="0 0 0 0 0 0.6" により、リンクの端点座標を指定し、カプセル型(両端が丸い円柱)の形状を定義。
  • axis="0 1 0" はy軸回りに回転する関節(このモデルではリンクの長手方向がz軸方向)。

4.4 XMLファイルとして保存・読み込み

上記のモデルをXMLファイルとして保存し、MuJoCoで読み込むには次のようにします:

ファイル保存例(twolink.xml):

touch twolink.xml
# 中身に上記XMLを貼り付けて保存

Pythonから読み込む:

model = mujoco.MjModel.from_xml_path("twolink.xml")
data = mujoco.MjData(model)

4.5 よくあるエラーとヒント

問題 原因と対策
"Missing worldbody" エラー <worldbody> タグの記述忘れ
関節が動かない <joint> が定義されていない / <actuator> が必要
geomが表示されない size 属性の記述ミス、rgba の透明度(最後の値)が0
"unknown element" エラー タグ名の綴り間違いや構文ミス

✅ まとめ

  • MJCFはMuJoCo独自のXML記述フォーマットで、非常に柔軟にモデルを定義できる
  • <body>, <geom>, <joint> を理解すれば、基本的な構造体は作成可能
  • モデル定義とシミュレーションは完全に分離しており、設計と制御を独立して開発できる点がMuJoCoの強み

第5章:Pythonからの制御プログラム入門

MuJoCoのシミュレーションでは、Pythonコードを通じて関節の制御(アクチュエーション)センサー値の取得が行えます。本章では、Python APIを使ってモデルを動かすための基本的な考え方と具体的なコード例を紹介します。

ロボットの制御や強化学習に進むための、非常に重要なステップです。


5.1 MuJoCoにおける制御の基本構造

MuJoCoのシミュレーションは、次の3つのコンポーネントの連携によって動作します:

  1. MjModel:シミュレーションの静的情報(関節の種類、形状、質量など)
  2. MjData:シミュレーション中に変化する状態(関節角、速度、入力トルクなど)
  3. 制御入力(data.ctrl:アクチュエータを介してモーターに加えるコマンド値

5.2 アクチュエータの定義(MJCF)

アクチュエータの基本構文

<actuator>
  <motor joint="joint1" ctrlrange="-1.0 1.0" gear="100"/>
  <motor joint="joint2" ctrlrange="-1.0 1.0" gear="100"/>
</actuator>

この定義により、Pythonから次のように制御できるようになります:

data.ctrl[0] = 0.5  # joint1 にコマンド入力
data.ctrl[1] = -0.5 # joint2 にコマンド入力

⚙️ data.ctrl の値は「入力信号」=コマンド

MuJoCoにおける data.ctrl[i] の値は、**アクチュエータに与える指令値(制御入力)**です。これ自体は単位のない抽象的な値であり、実際に関節に印加されるトルク(または力)は gear によってスケーリングされます


🔍 gearパラメータの意味と物理的な関係

  • gear="100" とした場合、コントロール値1.0に対して100のトルクが印加されることを意味します。
  • つまり、実際に出力されるトルクは以下の式で計算されます:
τ = gear × ctrl

ここで:

  • τ(tau) は関節に加えられるトルク
  • gearmotor タグの gear 属性値
  • ctrldata.ctrl[i] の値

🔧 例:gearの違いによる出力の違い

gear値 data.ctrl[0] 実際のトルク
100 0.5 50
200 0.5 100
100 -1.0 -100

このように、gear の値を大きくすると、同じ data.ctrl に対してより強いトルクが関節に加えられます。


💡 注意点

  • gear の値を大きくしすぎると、関節の動きが急激になりすぎてシミュレーションが不安定になることがあります(数値爆発)。
  • MuJoCoのシミュレーションはステップサイズが固定なので、トルクの大きさと動作安定性のバランスが重要です。
  • 制御入力は、ctrlrange で制限することで暴走を防げます。

🧠 アクチュエーターまとめ

  • data.ctrl[i] はアクチュエータへの指令値
  • 実際のトルク・力は gear × ctrl によって計算される
  • gear を調整することで、動きの強さ・速度・滑らかさを制御できる

✅ アクチュエータ付き2リンクアームの完全なXMLサンプル

<mujoco model="two_link_arm">
  <worldbody>
    <body name="link1" pos="0 0 0.1">
      <geom type="capsule" fromto="0 0 0 0 0 0.6" size="0.05" rgba="0.2 0.6 0.8 1"/>
      <joint name="joint1" type="hinge" axis="0 1 0" pos="0 0 0"/>
      
      <body name="link2" pos="0 0 0.6">
        <geom type="capsule" fromto="0 0 0 0 0 0.6" size="0.05" rgba="0.8 0.4 0.2 1"/>
        <joint name="joint2" type="hinge" axis="0 1 0" pos="0 0 0"/>
      </body>
    </body>
  </worldbody>

  <actuator>
    <motor joint="joint1" ctrlrange="-1.0 1.0" gear="100"/>
    <motor joint="joint2" ctrlrange="-1.0 1.0" gear="100"/>
  </actuator>

</mujoco>

💡 使い方

  1. 上記をテキストファイルにコピーし、twolink_with_actuator.xml として保存
  2. Pythonで以下のように読み込む:
model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")
data = mujoco.MjData(model)

🔍 このXMLに含まれるもの

  • 2つのリンクと2つの関節(joint1, joint2
  • それぞれに対応するモーター(<actuator>

5.3 Pythonからの制御ループ

もっともシンプルな制御シミュレーションプログラム

以下は、2リンクアームの各関節を簡単な周期関数で制御する例です:

import mujoco
import numpy as np
import time

model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")
data = mujoco.MjData(model)

duration = 5.0  # 秒
t0 = time.time()

while time.time() - t0 < duration:
    t = time.time() - t0
    data.ctrl[0] = 0.1*np.sin(t)     # 関節1に周期的な入力:ゲイン0.1
    data.ctrl[1] = 0.1*np.cos(t)     # 関節2にも入力:ゲイン0.1
    mujoco.mj_step(model, data)

このコードでは、サイン・コサイン関数を用いて関節に動きを与えています。簡易的な制御ですが、動作確認に最適です。

注:このサンプルは表示コードが記述されていませんので、実行しても何も表示されません。

可視化付き制御シミュレーションプログラム

import mujoco
import glfw
import numpy as np
import time

model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")
data = mujoco.MjData(model)

duration = 5  # 秒
t0 = time.time()

# シミュレーションの可視化
# GLFWの初期化
if not glfw.init():
    raise RuntimeError("GLFW初期化に失敗しました")

window = glfw.create_window(800, 600, "MuJoCo GLFW Viewer", None, None)
glfw.make_context_current(window)

# 可視化関連オブジェクトの初期化
scene = mujoco.MjvScene(model, maxgeom=1000)
cam = mujoco.MjvCamera()
opt = mujoco.MjvOption()
con = mujoco.MjrContext(model, mujoco.mjtFontScale.mjFONTSCALE_100)

while time.time() - t0 < duration:
    t = time.time() - t0
    data.ctrl[0] = 0.1*np.sin(t)     # 関節1に周期的な入力:ゲイン0.1
    data.ctrl[1] = 0.1*np.cos(t)     # 関節2にも入力:ゲイン0.1
    mujoco.mj_step(model, data)

    viewport_width, viewport_height = glfw.get_framebuffer_size(window)
    viewport = mujoco.MjrRect(0, 0, viewport_width, viewport_height)

    mujoco.mjv_updateScene(model, data, opt, None, cam, mujoco.mjtCatBit.mjCAT_ALL, scene)
    mujoco.mjr_render(viewport, scene, con)

    glfw.swap_buffers(window)
    glfw.poll_events()

glfw.terminate()

‼️超重要

data.ctrl[0]data.ctrl[1]ゲイン0.1を片方を0にしたり、それぞれの値を色々変えてみて動作がどう変わるのかや、正弦関数ではない定常値を入れてみたりして、動作がどう変わるのか確認して理解を深めてください。


5.4 センサーの定義とデータ取得

MuJoCoでは、センサーを定義することでシミュレーション中の物理量を観測できます。

✅ センサー定義例(MJCF)

<sensor>
  <jointpos joint="joint1"/>
  <jointvel joint="joint1"/>
  <jointpos joint="joint2"/>
  <jointvel joint="joint2"/>
</sensor>

✅ Pythonからの取得方法

センサーデータは data.sensordata に格納され、センサー定義の順に並びます:

angle1 = data.sensordata[0]
velocity1 = data.sensordata[1]
angle2 = data.sensordata[2]
velocity2 = data.sensordata[3]

5.5 qpos / qvelsensordata の使い分け

MuJoCoでは関節状態を2通りの方法で取得できます:

方法 特徴と使いどころ
data.qpos / data.qvel 内部状態に直接アクセス。高速でシンプル。制御器向け。
data.sensordata <sensor> で定義された観測値。ログ取得や強化学習向け。

どちらを使うべきか?

  • プロトタイプやPID制御の実装時qpos, qvel で素早く動作確認
  • 強化学習やログ記録:センサーを定義して sensordata を使うことで柔軟に対応可能

5.6 PID制御の例

MuJoCoでよく使われる制御方式の一つに PID制御 があります。ここでは関節1を目標角度 q_target に近づける制御を例示します:

可視化なしバージョン

import mujoco
import numpy as np
import time

model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")
data = mujoco.MjData(model)

duration = 5  # 秒
t0 = time.time()
#
# PID制御のパラメータ初期化
q_target = np.pi/4  # 45度
Kp = 10
Kd = 2

while time.time() - t0 < duration:
    t = time.time() - t0

    # PID制御の計算
    q = data.qpos[0]
    dq = data.qvel[0]
    torque = Kp * (q_target - q) - Kd * dq
    data.ctrl[0] = torque
    mujoco.mj_step(model, data)

このように data.qpos, data.qvel で状態を読み取り、PID計算を行って data.ctrl にトルクを与えることで制御できます。

可視化ありバージョン

import mujoco
import glfw
import numpy as np
import time

model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")
data = mujoco.MjData(model)
# 初期状態のリセット(初期化)
mujoco.mj_resetData(model, data)

# シミュレーションの可視化
# GLFWの初期化
if not glfw.init():
    raise RuntimeError("GLFW初期化に失敗しました")

window = glfw.create_window(800, 600, "MuJoCo GLFW Viewer", None, None)
glfw.make_context_current(window)

# 可視化関連オブジェクトの初期化
scene = mujoco.MjvScene(model, maxgeom=1000)
cam = mujoco.MjvCamera()
opt = mujoco.MjvOption()
con = mujoco.MjrContext(model, mujoco.mjtFontScale.mjFONTSCALE_100)

#初期フレームを描画:これがないと動画が途中から始まる
glfw.show_window(window)
mujoco.mj_step(model, data)
viewport_width, viewport_height = glfw.get_framebuffer_size(window)
viewport = mujoco.MjrRect(0, 0, viewport_width, viewport_height)
mujoco.mjv_updateScene(model, data, opt, None, cam, mujoco.mjtCatBit.mjCAT_ALL, scene)
mujoco.mjr_render(viewport, scene, con)
glfw.swap_buffers(window)
glfw.poll_events()

# 初期フレームが見えるように待機
time.sleep(0.5)

duration = 15  # 秒
t0 = time.time()
#
# PID制御のパラメータ初期化
q_target = np.pi/4  # 45度
Kp = 10
Kd = 1

while time.time() - t0 < duration:
    t = time.time() - t0

    # PID制御の計算
    q = data.qpos[0]
    dq = data.qvel[0]
    torque = Kp * (q_target - q) - Kd * dq
    data.ctrl[0] = torque
    mujoco.mj_step(model, data)

    viewport_width, viewport_height = glfw.get_framebuffer_size(window)
    viewport = mujoco.MjrRect(0, 0, viewport_width, viewport_height)

    mujoco.mjv_updateScene(model, data, opt, None, cam, mujoco.mjtCatBit.mjCAT_ALL, scene)
    mujoco.mjr_render(viewport, scene, con)

    glfw.swap_buffers(window)
    glfw.poll_events()

glfw.terminate()

✅ まとめ

  • MuJoCoでは、アクチュエータとセンサーをXMLで定義し、Pythonから data.ctrldata.sensordata を使って制御・観測を行う
  • サイン波やPID制御のようなシンプルなロジックでも、リアルなロボット動作の確認が可能
  • 本章で扱った基礎を押さえることで、次章の「強化学習との連携」へスムーズに進めます

第6章: MuJoCoで actuator / sensor のインデックスを取得する方法

MuJoCoでは、data.ctrldata.sensordata のような配列が使われていますが、そのインデックスがどの actuator や sensor に対応しているかを知るには明示的な方法が必要です。

以下に、actuator や sensor のインデックスを取得する具体的な方法を解説します。

6.1. アクチュエータ(data.ctrl)のインデックスを取得

MuJoCoのアクチュエータは、モデル定義(MJCF)で定義された順に data.ctrl の配列に割り当てられます。

▶️ インデックスを取得するコード例:

import mujoco

model = mujoco.MjModel.from_xml_path("twolink_with_actuator.xml")

# アクチュエータ名の一覧(順序付き)
actuator_names = [model.names[i] for i in model.name_actuatoradr]

# それぞれのインデックス
for i, name in enumerate(actuator_names):
    print(f"index={i}, actuator name={name}")

🔍 備考

  • model.nu はアクチュエータの数
  • data.ctrl[i]i 番目のアクチュエータに対応

6.2 センサー(data.sensordata)のインデックスを取得

センサーも、MJCFで定義した順番に data.sensordata に格納されます。 ただし注意すべきは、センサー1つが複数次元を持つことがある点です(例:velocimeterは3次元)。

▶️ センサーインデックスとオフセットを確認する方法:

for i in range(model.nsensor):
    name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_SENSOR, i)
    sensor_type = model.sensor_type[i]
    data_index = model.sensor_adr[i]
    dim = model.sensor_dim[i]
    print(f"sensor {i}: name={name}, type={sensor_type}, index={data_index}, dim={dim}")

出力例:

sensor 0: name=joint1_pos, type=2, index=0, dim=1
sensor 1: name=joint1_vel, type=3, index=1, dim=1
sensor 2: name=joint2_pos, type=2, index=2, dim=1
sensor 3: name=joint2_vel, type=3, index=3, dim=1

🔍 備考

  • model.sensor_adr[i] は、data.sensordata における 開始インデックス
  • model.sensor_dim[i] は、センサーの次元数(例:3軸加速度なら3)

6.3 センサー名 → インデックス を辞書化したい場合

sensor_dict = {
    mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_SENSOR, i): (model.sensor_adr[i], model.sensor_dim[i])
    for i in range(model.nsensor)
}

print(sensor_dict["joint1_pos"])  # (0, 1)

🧠 まとめ

要素 方法 配列 関連関数/属性
アクチュエータ名 → index model.name_actuatoradr, model.names data.ctrl model.nu
センサー名 → index mj_id2name, model.sensor_adr[i] data.sensordata model.sensor_dim[i]

この知識を使えば、Python側でセンサーデータや制御入力を名前で管理・ログ・可視化することができ、実験が格段にやりやすくなります。


第7章:強化学習との連携と研究応用

MuJoCoは、物理シミュレーションの精度・効率の高さから、強化学習(Reinforcement Learning, RL)分野で最も広く使われている物理エンジンの一つです。

この章では、最新の強化学習ライブラリである Gymnasium を通じてMuJoCo環境を使う方法、そして研究応用の実例をわかりやすく解説します。


7.1 GymからGymnasiumへ:環境APIの現在

かつて強化学習といえば OpenAI Gym が定番でしたが、現在では GymnasiumFarama Foundationが開発)がその後継として活発にメンテナンスされています。

✅ Gymnasiumの特徴

  • Gymと完全互換のAPI
  • MuJoCoベースのロボット環境(Ant, Hopper, Walker2dなど)を提供
  • PyPIで簡単にインストール可能

7.2 GymnasiumでMuJoCo環境を使う

🔧 インストール

まずは Gymnasium 本体と MuJoCo対応の環境セットをインストールします:

pip install gymnasium
pip install gymnasium-robotics

これで、MuJoCo + 強化学習環境のセットアップが完了します。


▶️ 環境の起動と実行ループ

以下は MuJoCo ベースの環境「HalfCheetah-v5」を起動し、ランダムな行動でエピソードを1つ回す例です:

import gymnasium as gym

env = gym.make("HalfCheetah-v5", render_mode="human")
observation, info = env.reset()

for _ in range(1000):
    action = env.action_space.sample()  # ランダムな行動
    observation, reward, terminated, truncated, info = env.step(action)
    if terminated or truncated:
        observation, info = env.reset()

env.close()

🧠 補足:環境名の末尾「-v5」について

  • -v5Gymnasiumバージョンの環境であることを示します。
  • -v2 は旧Gym(OpenAI)、-v5 はGymnasiumに準拠したものです。

7.3 代表的なMuJoCo環境(学習タスク)

環境名 説明
Ant-v5 4脚歩行ロボット。安定して歩く学習が課題。
HalfCheetah-v5 高速で地面を蹴って前進する2脚型。
Hopper-v5 1本足ジャンプロボット。バランスが鍵。
Humanoid-v5 高自由度の人型ロボット。最難関。

これらはすべてMuJoCoベースで、連続空間(観測・行動が浮動小数)を対象とした強化学習タスクです。


7.4 強化学習アルゴリズムとの統合

Gymnasium環境は以下のような学習アルゴリズムと簡単に統合できます:

ライブラリ 代表的アルゴリズム
Stable-Baselines3 PPO, DDPG, TD3, SAC
CleanRL 研究向けシンプル実装
RLlib (Ray) 分散学習、クラスタ対応

🔧 例:Stable-Baselines3でのPPO学習(Ant環境)

pip install "stable-baselines3[extra]"
from stable_baselines3 import PPO
from stable_baselines3.common.env_util import make_vec_env

env = make_vec_env("Ant-v5", n_envs=4)
model = PPO("MlpPolicy", env, verbose=1)
model.learn(total_timesteps=1_000_000)

7.5 研究応用の例

MuJoCoは多くのロボティクス・強化学習研究で利用されています。以下に代表的な事例を挙げます:

🔬 研究事例

  • OpenAI(2017〜) PPOやHER(Hindsight Experience Replay)の論文でAnt/Humanoid環境を使用
  • DeepMind(2022) MuJoCoオープンソース化とともに、多関節ロボットの模倣学習研究を多数発表
  • MIT・スタンフォードなどの大学研究室 バイオメカニクス、筋骨格モデリング、模倣学習などにMuJoCoを活用

7.6 応用Tips:ログ・可視化・安定性

  • シミュレーションの可視化render_mode="human" でリアルタイム表示
  • 動画保存:Gymnasiumの RecordVideo ラッパーで自動保存可能
  • ログ取得:センサー出力(sensordata)をCSV保存して解析
  • 安定化のために

    • gear を適切に設定する
    • timestep を小さくして精度向上(例:0.001)
    • friction, damping を適切に与える

✅ まとめ

  • MuJoCoは現在、Gymnasiumとの連携を通じて強化学習環境として最も標準的な選択肢
  • 安定かつ高速な物理シミュレーションにより、複雑なロボットタスクの学習が可能
  • Stable-Baselines3 などのライブラリと組み合わせることで、学術・産業研究の幅広い応用が可能

第8章:おわりに

本チュートリアル「MuJoCo入門:みんなの物理シミュレーションチュートリアル」では、学部生や初学者の方がMuJoCoを安心して使い始められるように、基礎から応用まで体系的に解説してきました。


8.1 ここまでの学びを振り返る

  • 第1章〜第2章では、MuJoCoの概要と環境構築を通して、ツールの基本的な使い方を学びました。
  • 第3章〜第4章では、シミュレーションの実行とMJCF形式によるモデル定義を実践しました。
  • 第5章では、Pythonからの制御方法を理解し、アクチュエータとセンサーの扱い方を学びました。
  • 第6章では、GymnasiumとMuJoCoを組み合わせて、強化学習の環境として活用する方法を紹介しました。
  • 第7章では、indexの取得など、実践的かつ重要なAPI知識を補足しました。

8.2 初学者がつまずきやすいポイント

MuJoCoは高機能な反面、以下のような初学者向けの注意点もあります:

よくあるつまずき 回避のヒント
PyQtやGLFWで可視化に失敗 MacではGLFWでの手動描画を推奨
シミュレーションが発散する gearctrlrangeを適切に設定
センサーデータの順番が不明 sensor_adrsensor_dimで確認可能

こうしたトラブルを「知っていて対処できる」と学習効率は大きく変わります。本記事がそうした知識の橋渡しとなれば幸いです。


8.3 今後のステップに向けて

MuJoCoは以下のような発展的な研究・応用にも対応しています:

  • 模倣学習や強化学習研究(DMP, PPO, SACなど)
  • 複雑なマルチボディシステム(二足歩行ロボットなど)
  • バイオメカニクスや神経科学(筋骨格モデルなど)
  • 空中ロボット(ドローン)のモデリングと制御検証

8.4 感謝と参考資料

本記事はGoogle DeepMindが提供する公式MuJoCoリファレンス、およびGymnasiumやStable-Baselines3などのオープンソースコミュニティに支えられています。感謝です。

🔗 参考リンク:

これらのドキュメントやサンプルコードは、さらに深く学びたい読者にとって強力な学習資源となります。


📘 最後に

MuJoCoを学ぶことは、「仮想世界の中で現実の物理を扱う力」を手に入れることでもあります。 その力を使って、あなたのアイデアを動くロボットや賢いエージェントに変えてください。

地上のロボットだけでなく、空を飛ぶドローンのようなシステムもMuJoCoなら実現可能です。 本チュートリアルがその第一歩となれば嬉しく思います。