MuJoCoのビューアが開かない・固まるときの原因は、ほぼ次の3つに絞られます。「表示先がない(クラウドやSSH先にDISPLAYがない)」「macOSでlaunch_passiveをpythonから直接呼んでいる」「自分で書いたループでsync()を呼んでいない」。**ビューア(Viewer)**とは、MuJoCoに標準搭載された、物理シミュレーションの様子をリアルタイムで3D表示する可視化ウィンドウのことです。この記事では、症状別の原因と直し方を、公式ドキュメントで確認した内容と筆者が実際に再現した結果で整理します。
この記事で分かること
- 「DISPLAY environment variable is missing」エラーは、クラウドやSSH先などGUIの表示先がない環境で起きる。Xvfbで仮想ディスプレイを用意すれば解決する(本記事で実際に再現・確認済み)
- macOSで自作スクリプトから launch_passive を呼ぶ場合、python ではなく mjpython で実行する必要がある(公式ドキュメント)
- 画面が固まったまま動かないときは、自作ループ内で viewer.sync() を呼び忘れているケースが多い
対象読者:MuJoCo入門を終えて、自分のPC・サーバーでビューアが動かず困っている人
執筆:ゆるふわフィジカルAI編集部 最終更新日:2026年10月16日
まず症状を確認する
| 症状 | 多い原因 | 対処 |
|---|---|---|
| エラーで即終了する | DISPLAYがない(クラウド・SSH先) | Xvfbで仮想ディスプレイ/ローカルPCで実行 |
| macOSで何も起きない・落ちる | pythonからlaunch_passiveを呼んでいる | mjpythonで実行する |
| ウィンドウは開くが真っ暗・固まる | sync()を呼んでいない/ロック漏れ | ループ内で毎回sync()を呼ぶ |
次の章から、それぞれの原因と直し方を詳しく見ていきます。
症状1: 「DISPLAY environment variable is missing」で終了する
これは、ビューア用のウィンドウを表示する画面(ディスプレイ)が、今のマシンにないことが原因です。クラウドの仮想マシンやコンテナ、SSH接続先のLinuxサーバーでMuJoCo入門のコードをそのまま動かすと、この状態になります。
筆者も、GUIのないLinuxコンテナで次のコードを実行し、実際にこのエラーを再現しました。
import mujoco
import mujoco.viewer
model = mujoco.MjModel.from_xml_string("<mujoco/>")
data = mujoco.MjData(model)
mujoco.viewer.launch_passive(model, data)GLFWError: (65550) b'X11: The DISPLAY environment variable is missing'
ERROR: could not initialize GLFWMuJoCoのビューアはGLFWというライブラリでウィンドウを開きますが、GLFWはLinux上ではX11(画面を表示する仕組み)を前提にしているため、DISPLAYがないと初期化自体に失敗します。
直し方: Xvfbで仮想ディスプレイを用意する
Linuxサーバーやコンテナで「画面はいらないが、ビューアのコードだけ動かしたい」場合は、仮想ディスプレイソフトXvfbを使います。xvfb-runで包むだけです。
xvfb-run -a python your_script.py実際にこの方法で、上と同じコードのlaunch_passiveが正常に起動することを確認しました(viewer launched OK)。ただし実機の画面には何も映らないので、見た目を確認したいなら後述のオフスクリーン描画か、ディスプレイのあるPCで動かす方法を選んでください。
見るためではなく記録するためなら、ビューアを使わない選択もある
動画や画像として結果を残したいだけなら、ウィンドウを開くviewerではなく、mujoco.Rendererによるオフスクリーン描画が向いています。環境変数MUJOCO_GLにegl(GPUがある場合)やosmesa(CPUのみ)を設定して使います。筆者の手元のGPUなし環境では、対応するグラフィックライブラリ(EGL/OSMesa)自体が入っていなかったため実行までは確認できませんでしたが、該当ライブラリを導入した環境向けの一般的な方法として紹介します。
症状2: macOSで実行しても何も起きない・すぐ終わる
自分で書いたPythonスクリプトの中でmujoco.viewer.launch_passive()を呼んでいる場合、macOSではpythonコマンドの代わりにmjpythonコマンドで実行する必要があります。これは公式ドキュメントに明記されているmacOS特有の制約で、理由は「ウィンドウの描画はメインスレッドで行う必要があるが、通常のpythonでは別スレッドになってしまう」ためです1。
# macOSでlaunch_passiveを使うスクリプトを動かすとき
mjpython your_script.pymjpythonはmujocoパッケージに同梱されていて、公式ドキュメントでは「pythonコマンドの完全な代替として使え、同じコマンドライン引数がそのまま使える」と説明されています1。ターミナルで直接python -m mujoco.viewerを実行して様子がおかしいときも、mjpython -m mujoco.viewerに置き換えて試してみてください。
症状3: ウィンドウは開くが真っ暗・固まったまま動かない
MuJoCo入門のようにビューアへドラッグ&ドロップするだけなら自動で描画されますが、自分でシミュレーションループを書いてlaunch_passiveに渡している場合、ループの中でviewer.sync()を呼んでいないと画面が更新されません。公式ドキュメントは次のように説明しています。
ユーザースクリプトは、ビューアが物理状態の変化を反映できるように
sync()を呼び出す必要がある2
正しい最小ループは次の形です。
import mujoco
import mujoco.viewer
model = mujoco.MjModel.from_xml_string("""
<mujoco>
<worldbody>
<light pos="0 0 3"/>
<geom type="plane" size="2 2 0.1"/>
<body pos="0 0 1"><freejoint/><geom type="box" size="0.1 0.1 0.1"/></body>
</worldbody>
</mujoco>
""")
data = mujoco.MjData(model)
with mujoco.viewer.launch_passive(model, data) as viewer:
while viewer.is_running():
mujoco.mj_step(model, data)
viewer.sync() # これを忘れると画面が固まったままさらに、カメラの向きや表示オプション(viewer.camやviewer.opt)を自分のスクリプトから直接書き換える場合は、sync()の外側でviewer.lock()を使ってロックを取ってから変更する必要があります。ビューアは専用スレッドで動いているため、ロックなしで触ると競合が起きます3。
python -m mujoco.viewer と launch_passive の違い
どちらも同じビューアですが、使い方の目的が違います。
python -m mujoco.viewer(ターミナルから直接)
- 引数なしなら空のウィンドウが開く
- MJCFファイルをドラッグ&ドロップして確認
- `--mjcf=path`で直接開くことも可能
- 手早く1つのモデルを見るのに向く
launch_passive()(スクリプトの中から)
- 自分のシミュレーションループに組み込める
- `sync()`で毎ステップ描画を更新
- カメラ操作等は`lock()`で保護
- 強化学習やデータ収集スクリプトに向く
「とりあえずモデルの見た目を確認したい」なら前者、「自分のコードでステップを進めながら可視化したい」なら後者、というのが基本的な使い分けです。
よくある質問
Q. 「DISPLAY environment variable is missing」エラーが出ました。どうすればいいですか?
A. GUIを表示できる画面がない環境(クラウドのVM、コンテナ、SSH接続先など)でビューアを開こうとしたことが原因です。手元にディスプレイのあるPCで実行するか、仮想ディスプレイのXvfbを使って「xvfb-run -a python your_script.py」のように実行してください。
Q. macOSで python -m mujoco.viewer や自作スクリプトを実行しても何も起きません。
A. launch_passiveを使うスクリプトは、macOSではpythonではなくmjpythonコマンドで実行する必要があります。mjpythonはmujocoパッケージに同梱されており、pythonの完全な代替として同じ引数で使えます。
Q. ビューアのウィンドウは開くのに画面が固まったまま動きません。
A. 自分で書いたシミュレーションループの中でviewer.sync()を呼んでいないことが多いです。mj_step()で物理を進めたら、毎ステップ必ずviewer.sync()を呼んで画面に反映させてください。
まとめ
- 「DISPLAY environment variable is missing」は表示先がない環境特有のエラー。Xvfbで仮想ディスプレイを用意すれば起動できる(実際に確認済み)
- macOSで自作スクリプトから
launch_passiveを使うときはpythonではなくmjpythonで実行する(公式ドキュメント) - 画面が固まるときは、ループ内の
sync()忘れをまず疑う - 「見た目を素早く確認したい」なら
python -m mujoco.viewerへのドラッグ&ドロップ、「自分のコードに組み込みたい」ならlaunch_passive
もう少し詳しく(背景)
MuJoCoの標準ビューアはGLFWでウィンドウを作成しますが、GLFWはLinux上ではX11プロトコルに依存するため、X11サーバーに接続できない(=DISPLAYがない)環境では初期化できません4。これはMuJoCo固有の制約ではなく、GLFWを使うアプリ全般に共通する挙動です。macOSのmjpython要件は、Apple製OS特有のGUIイベント処理の都合(描画はメインスレッドでなければならない)に対応するためのものです1。launch_passiveが別スレッドでビューアを動かす設計になっているのは、ユーザーのPythonコード(強化学習の学習ループなど)をブロックせずに裏側で可視化を続けられるようにするためで、その代わりにsync()とlock()でメインのシミュレーションコードとの整合性を取る仕組みになっています23。
次の一歩 🌸
ビューアが動くようになったら、MuJoCo入門で振り子のシミュレーションから練習しましょう。Python環境そのものが不安ならPython環境構築の第一歩へ。シミュレーションでの練習を実機につなげる考え方はSim-to-Realの実践、本格的なGPUシミュレータが必要になったらNVIDIA Isaac Sim入門も合わせてどうぞ。
参考資料・出典
- MuJoCo Python Bindings documentationMuJoCo公式Pythonドキュメント。mjpython・sync()・lock()の説明と、launch_passiveのmacOS制約を記載
- dm_control Issue #123GLFWがX11(ディスプレイ)を前提とし、ヘッドレス環境では使えないことについてのメンテナによる説明
- MUJOCO_INSTALLATION.md (pytorch/rl)ヘッドレス環境でのオフスクリーン描画(MUJOCO_GL=egl等)の設定例
ゆるふわポータルでは、一次情報・公的資料にもとづき、専門用語をできるだけやさしく翻訳することを心がけています。内容に誤りや古い情報がある場合は、お問い合わせからご連絡ください。
執筆:ゆるふわフィジカルAI編集部 最終更新日:2026年10月16日
Footnotes
-
MuJoCo公式Pythonドキュメント。macOSでは
launch_passiveを使うユーザースクリプトをmjpythonで実行する必要があると明記。mjpythonはpythonコマンドの完全な代替として同じ引数で使える。 ↩ ↩2 ↩3 -
MuJoCo公式Pythonドキュメント。「ユーザースクリプトは、ビューアが物理状態の変化を反映できるようにsync()を呼び出す必要がある」と説明。 ↩ ↩2
-
MuJoCo公式Pythonドキュメント。ビューアは専用スレッドで動作するため、物理・可視化状態(cam, opt, pert等)を変更する前にユーザースクリプト側で
lock()によるロックを取る必要があると説明。 ↩ ↩2 -
GLFW(ウィンドウ作成ライブラリ)はLinux上でX11プロトコルに依存する。X11サーバーに接続できない環境(DISPLAY未設定のクラウドVM・コンテナ等)では初期化に失敗する。 ↩
ゆるふわフィジカルAI