見出し画像

Genesis 物理シミュレーション 完全チュートリアル

🎯 このチュートリアルの目的

Genesisは、ロボット工学や物理シミュレーションのためのオープンソースエンジンです。 このチュートリアルでは、クラウドGPU環境(vast.ai)でディスプレイなしでGenesisを動かし、 布・液体・雪などの物理シミュレーションを行う方法を、初心者向けに解説します。

1. 環境構築

1.1 前提条件

  • クラウドGPU: vast.ai

  • GPU: NVIDIA GPU(RTX 4090 推奨)

  • OS: Ubuntu 24.04

  • Python: 3.10以上

1.2 必須パッケージのインストール

PyTorchが必要なためVast.aiで、PyTorchを含んだTemplateを選んでください。

また、クラウド環境にはディスプレイがないため、EGL(組み込みグラフィックスライブラリ)を使います。

# システムパッケージをインストール
apt-get update
apt-get install -y libegl1 libegl-dev libgl1 libgles2 libglvnd-dev

なぜ必要?

  • libegl1: EGLランタイム(ディスプレイなしでOpenGLを使うため)

  • libgl1: OpenGLライブラリ

  • libgles2: OpenGL ES(組み込み向けOpenGL)

  • libglvnd-dev: GPU選択用ライブラリ

1.3 環境変数の設定

# 現在のセッションに設定
export PYOPENGL_PLATFORM=egl
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json

# 永続化(次回ログイン時も有効)
echo 'export PYOPENGL_PLATFORM=egl' >> ~/.bashrc
echo 'export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json' >> ~/.bashrc
source ~/.bashrc

各環境変数の意味:

1.4 Genesisのインストール

pip install genesis-world

1.5 インストール確認

import genesis as gs
gs.init(backend=gs.gpu)
print("Genesis installed successfully!")

2. 基本テスト:箱の落下

2.1 コンセプト

最初のテストとして、シンプルな箱が落下するシミュレーションを作ります。

学ぶこと:

  • シーンの作成

  • エンティティ(物体)の追加

  • カメラの設定

  • 動画の録画・保存

2.2 コード

"""
Step 1: 基本テスト - 箱の落下
初心者向け:最もシンプルなGenesisシミュレーション
"""

import genesis as gs

# ========================================
# 1. Genesis初期化
# ========================================
# backend=gs.gpu でGPUを使用
gs.init(backend=gs.gpu)

# ========================================
# 2. シーン作成
# ========================================
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=4e-3,        # タイムステップ(小さいほど精密、遅い)
        substeps=10,    # 1ステップあたりの内部計算回数
    ),
    show_viewer=False,  # ★重要:ヘッドレス環境ではFalse
    vis_options=gs.options.VisOptions(
        show_world_frame=True,  # 座標軸を表示
    ),
    renderer=gs.renderers.Rasterizer(),  # レンダラー
)

# ========================================
# 3. 地面を追加
# ========================================
plane = scene.add_entity(
    morph=gs.morphs.Plane(),  # 無限の平面
)

# ========================================
# 4. 箱を追加
# ========================================
box = scene.add_entity(
    morph=gs.morphs.Box(
        size=(0.2, 0.2, 0.2),  # 20cm x 20cm x 20cm
        pos=(0, 0, 1.0),       # 高さ1mの位置
    ),
    material=gs.materials.Rigid(),  # 剛体
)

# ========================================
# 5. カメラを追加
# ========================================
cam = scene.add_camera(
    res=(1280, 720),           # 解像度
    pos=(3.0, -3.0, 2.5),      # カメラ位置
    lookat=(0, 0, 0.5),        # 注視点
    fov=40,                    # 視野角
    GUI=False,                 # ★重要:ヘッドレスではFalse
)

# ========================================
# 6. シーンをビルド
# ========================================
# build()を呼ぶと、全ての設定が確定される
scene.build()

# ========================================
# 7. シミュレーション実行&録画
# ========================================
cam.start_recording()

for i in range(500):  # 500ステップ
    scene.step()      # 物理シミュレーションを1ステップ進める
    cam.render()      # カメラでレンダリング

# ========================================
# 8. 動画保存
# ========================================
cam.stop_recording(save_to_filename="step1_box_drop.mp4", fps=60)
print("✅ 完了: step1_box_drop.mp4")

2.3 実行方法

PYOPENGL_PLATFORM=egl python step1_basic.py

出力: step1_box_drop.mp4 - 箱が落下して地面で跳ねる動画


3. 布シミュレーション(PBD)

3.1 PBDとは?

PBD (Position Based Dynamics) は、布やロープなど柔らかい物体をシミュレートする手法です。

特徴:

  • 高速で安定

  • リアルタイム向け

  • 布、ロープ、ソフトボディに最適

3.2 布メッシュの作成

まず、布の形状を定義するメッシュファイル(.obj)を作成します。

"""
布メッシュ生成スクリプト
2m x 2m の平面メッシュを作成
"""

import numpy as np
import os

def create_cloth_mesh(filename, size=2.0, divisions=20):
    """
    Parameters:
        filename: 出力ファイル名
        size: 布のサイズ(メートル)
        divisions: 分割数(多いほど細かい)
    """
    
    half = size / 2
    vertices = []
    faces = []
    
    # 頂点を生成
    for j in range(divisions + 1):
        for i in range(divisions + 1):
            x = -half + (i / divisions) * size
            y = -half + (j / divisions) * size
            z = 0
            vertices.append((x, y, z))
    
    # 面を生成(三角形)
    for j in range(divisions):
        for i in range(divisions):
            # 4つの頂点インデックス
            v0 = j * (divisions + 1) + i
            v1 = v0 + 1
            v2 = v0 + (divisions + 1)
            v3 = v2 + 1
            
            # 2つの三角形で1つの正方形を構成
            faces.append((v0 + 1, v1 + 1, v3 + 1))  # OBJは1始まり
            faces.append((v0 + 1, v3 + 1, v2 + 1))
    
    # OBJファイルとして保存
    os.makedirs(os.path.dirname(filename), exist_ok=True)
    with open(filename, 'w') as f:
        f.write("# Cloth mesh\n")
        for v in vertices:
            f.write(f"v {v[0]} {v[1]} {v[2]}\n")
        for face in faces:
            f.write(f"f {face[0]} {face[1]} {face[2]}\n")
    
    print(f"✅ メッシュ生成完了: {filename}")
    print(f"   頂点数: {len(vertices)}, 面数: {len(faces)}")

# 実行
create_cloth_mesh("meshes/cloth.obj", size=2.0, divisions=20)

3.3 布シミュレーションコード

"""
Part 1: PBD布シミュレーション
4角を固定したハンモック状の布
"""

import genesis as gs

gs.init(backend=gs.gpu)

# シーン作成(PBDソルバー有効)
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=4e-3,
        substeps=10,
    ),
    pbd_options=gs.options.PBDOptions(),  # ★PBDソルバーを有効化
    show_viewer=False,
    vis_options=gs.options.VisOptions(
        show_world_frame=True,
    ),
    renderer=gs.renderers.Rasterizer(),
)

# 地面
scene.add_entity(morph=gs.morphs.Plane())

# 布を追加
cloth = scene.add_entity(
    material=gs.materials.PBD.Cloth(),  # ★PBD布マテリアル
    morph=gs.morphs.Mesh(
        file='meshes/cloth.obj',
        scale=1.0,
        pos=(0, 0, 1.0),  # 高さ1m
    ),
    surface=gs.surfaces.Default(
        color=(0.2, 0.4, 0.8, 1.0),  # 青色
        vis_mode='particle',          # 粒子表示(変形が見やすい)
    )
)

# カメラ
cam = scene.add_camera(
    res=(1280, 720),
    pos=(4.0, -4.0, 2.5),
    lookat=(0, 0, 0.5),
    fov=45,
    GUI=False,
)

# ビルド
scene.build()

# ========================================
# 布の4角を固定
# ========================================
# 固定したい角の座標(布の4隅)
corners = [
    (-1, -1, 1.0),  # 左下
    (1, 1, 1.0),    # 右上
    (-1, 1, 1.0),   # 左上
    (1, -1, 1.0),   # 右下
]

# 各角に最も近い粒子を見つけて固定
particle_indices = []
for corner in corners:
    idx = cloth.find_closest_particle(corner)
    particle_indices.append(idx)

# ★重要:fix_particles()はリストで渡す(複数形に注意!)
cloth.fix_particles(particle_indices)

# シミュレーション実行
cam.start_recording()

for i in range(1000):
    scene.step()
    cam.render()

cam.stop_recording(save_to_filename="cloth_hammock.mp4", fps=60)
print("✅ 完了: cloth_hammock.mp4")


3.4 実行結果

  • 布が重力で垂れ下がり、ハンモック状になる

  • 4角が固定されているため、中央が最も下がる


4. 液体シミュレーション(SPH)

4.1 SPHとは?

SPH (Smoothed Particle Hydrodynamics) は、流体を粒子の集まりとしてシミュレートする手法です。

特徴:

  • 水、油などの液体に最適

  • 粒子ベースで飛沫も表現可能

  • 計算コストは高め

4.2 液体シミュレーションコード

"""
Part 2: SPH液体シミュレーション
水のブロックが落下して広がる
"""

import genesis as gs

gs.init(backend=gs.gpu)

# シーン作成(SPHソルバー有効)
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=4e-3,
        substeps=10,
    ),
    sph_options=gs.options.SPHOptions(
        lower_bound=(-1.0, -1.0, 0.0),   # シミュレーション領域の下限
        upper_bound=(1.0, 1.0, 2.0),     # シミュレーション領域の上限
        particle_size=0.015,              # 粒子サイズ
    ),
    show_viewer=False,
    renderer=gs.renderers.Rasterizer(),
)

# 地面
scene.add_entity(morph=gs.morphs.Plane())

# 水ブロックを追加
water = scene.add_entity(
    material=gs.materials.SPH.Liquid(),  # ★SPH液体マテリアル
    morph=gs.morphs.Box(
        size=(0.3, 0.3, 0.3),  # 30cm立方体
        pos=(0, 0, 0.5),       # 高さ0.5m
    ),
    surface=gs.surfaces.Default(
        color=(0.3, 0.5, 0.9, 0.8),  # 青色(半透明)
        vis_mode='particle',
    )
)

# カメラ
cam = scene.add_camera(
    res=(1280, 720),
    pos=(2.5, -2.5, 1.5),
    lookat=(0, 0, 0.2),
    fov=50,
    GUI=False,
)

# ビルド
scene.build()

# シミュレーション実行
cam.start_recording()

for i in range(800):
    scene.step()
    cam.render()
    
    if (i + 1) % 200 == 0:
        print(f"進行中... {i+1}/800 ステップ")

cam.stop_recording(save_to_filename="liquid_splash.mp4", fps=60)
print("✅ 完了: liquid_splash.mp4")


4.3 パフォーマンス

  • FPS: 約8 FPS(粒子数27,000個)

  • SPHは近傍探索が計算負荷高いため、他のソルバーより遅い


5. 雪・砂シミュレーション(MPM)

5.1 MPMとは?

MPM (Material Point Method) は、雪・砂・粘土など粒状体をシミュレートする手法です。

特徴:

  • 大きな変形に強い

  • 雪の圧縮、砂の崩壊を表現可能

  • Disney映画「アナと雪の女王」で使用

5.2 MPMシミュレーションコード

"""
Part 3: MPMシミュレーション
雪と砂が落下して変形
"""

import genesis as gs

gs.init(backend=gs.gpu)

# シーン作成(MPMソルバー有効)
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=4e-3,
        substeps=100,  # ★重要:MPMは100以上推奨
    ),
    mpm_options=gs.options.MPMOptions(
        grid_density=64,  # グリッド密度
    ),
    show_viewer=False,
    renderer=gs.renderers.Rasterizer(),
)

# 地面
scene.add_entity(morph=gs.morphs.Plane())

# 雪ブロック
snow = scene.add_entity(
    material=gs.materials.MPM.Snow(),  # ★MPM雪マテリアル
    morph=gs.morphs.Box(
        size=(0.3, 0.3, 0.3),
        pos=(0, 0, 0.8),
    ),
    surface=gs.surfaces.Default(
        color=(0.95, 0.95, 1.0, 1.0),  # 白
        vis_mode='particle',
    )
)

# 砂ブロック
sand = scene.add_entity(
    material=gs.materials.MPM.Sand(),  # ★MPM砂マテリアル
    morph=gs.morphs.Box(
        size=(0.2, 0.2, 0.2),
        pos=(0.5, 0, 0.6),
    ),
    surface=gs.surfaces.Default(
        color=(0.9, 0.8, 0.5, 1.0),  # 黄色
        vis_mode='particle',
    )
)

# カメラ
cam = scene.add_camera(
    res=(1280, 720),
    pos=(2.5, -2.5, 1.5),
    lookat=(0.2, 0, 0.3),
    fov=50,
    GUI=False,
)

# ビルド
scene.build()

# シミュレーション実行
cam.start_recording()

for i in range(800):
    scene.step()
    cam.render()

cam.stop_recording(save_to_filename="snow_sand.mp4", fps=60)
print("✅ 完了: snow_sand.mp4")


5.3 ⚠️ 重要な発見:substepsの設定

教訓:MPMは高いsubsteps値が必要!


6. 統合シミュレーション

6.1 複数ソルバーの同時使用

布(PBD)+ 液体(SPH)+ 雪(MPM)を同時にシミュレート。

"""
Part 4: 統合シミュレーション
布・液体・雪を同時にシミュレート
"""

import genesis as gs

gs.init(backend=gs.gpu)

# 全ソルバーを有効化
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=4e-3,
        substeps=100,
    ),
    pbd_options=gs.options.PBDOptions(),
    sph_options=gs.options.SPHOptions(
        lower_bound=(-1.5, -1.5, 0.0),
        upper_bound=(1.5, 1.5, 2.0),
        particle_size=0.02,
    ),
    mpm_options=gs.options.MPMOptions(
        grid_density=64,
    ),
    show_viewer=False,
    renderer=gs.renderers.Rasterizer(),
)

# 地面
scene.add_entity(morph=gs.morphs.Plane())

# 布(PBD)
cloth = scene.add_entity(
    material=gs.materials.PBD.Cloth(),
    morph=gs.morphs.Mesh(
        file='meshes/cloth.obj',
        scale=0.8,
        pos=(0, 0, 0.4),
    ),
    surface=gs.surfaces.Default(
        color=(0.2, 0.4, 0.8, 1.0),
        vis_mode='particle',
    )
)

# 水(SPH)
water = scene.add_entity(
    material=gs.materials.SPH.Liquid(),
    morph=gs.morphs.Box(
        size=(0.15, 0.15, 0.15),
        pos=(0, 0, 0.9),
    ),
    surface=gs.surfaces.Default(
        color=(0.3, 0.5, 0.9, 0.8),
        vis_mode='particle',
    )
)

# 雪(MPM)
snow = scene.add_entity(
    material=gs.materials.MPM.Snow(),
    morph=gs.morphs.Box(
        size=(0.15, 0.15, 0.15),
        pos=(0.3, 0.3, 0.8),
    ),
    surface=gs.surfaces.Default(
        color=(0.95, 0.95, 1.0, 1.0),
        vis_mode='particle',
    )
)

# 2つのカメラ(斜めと真上)
cam1 = scene.add_camera(res=(1280, 720), pos=(3, -3, 2), lookat=(0, 0, 0.3), fov=45, GUI=False)
cam2 = scene.add_camera(res=(1280, 720), pos=(0, 0, 3), lookat=(0, 0, 0.3), fov=60, GUI=False)

# ビルド
scene.build()

# 布の4角を固定
corners = [(-0.4, -0.4, 0.4), (0.4, 0.4, 0.4), (-0.4, 0.4, 0.4), (0.4, -0.4, 0.4)]
particle_indices = [cloth.find_closest_particle(c) for c in corners]
cloth.fix_particles(particle_indices)

# シミュレーション実行(2カメラ同時録画)
cam1.start_recording()
cam2.start_recording()

for i in range(1000):
    scene.step()
    cam1.render()
    cam2.render()

cam1.stop_recording(save_to_filename="combined_side.mp4", fps=60)
cam2.stop_recording(save_to_filename="combined_top.mp4", fps=60)
print("✅ 完了!")


6.2 ⚠️ 発見された制限

原因: GenesisのPBD-SPH間の衝突検出が完全にはサポートされていない可能性


7. トラブルシューティング

7.1 よくあるエラーと解決策

7.2 API変更への対応(Genesis 0.3.7)

# ❌ 旧API(エラーになる)
cloth.fix_particle(idx)

# ✅ 新API(正しい)
cloth.fix_particles([idx])  # リストで渡す

8. 得られた知見とベストプラクティス

8.1 ソルバー別推奨設定

8.2 パフォーマンス最適化

  1. 粒子数を減らす: particle_size を大きく

  2. 解像度を下げる: res=(640, 360) など

  3. substepsを調整: 最小限で安定する値を探す

  4. ステップ数を減らす: 必要な動きが見えたら終了

8.3 録画のベストプラクティス

# 複数カメラで同時録画
cam1.start_recording()
cam2.start_recording()

for i in range(steps):
    scene.step()
    cam1.render()  # 両方レンダリング
    cam2.render()

cam1.stop_recording(save_to_filename="view1.mp4", fps=60)
cam2.stop_recording(save_to_filename="view2.mp4", fps=60)

8.4 表示モードの違い

8.5 ソルバー間の相互作用(制限事項)

実験から判明した重要な制限


📝 まとめ

学んだこと

  1. ヘッドレス環境ではEGL設定が必須

  2. ソルバー選択は用途に合わせて(PBD/SPH/MPM)

  3. MPMは高いsubsteps(100以上)が必要

  4. API変更に注意(fix_particle → fix_particles)

  5. ソルバー間の相互作用には制限がある

次のステップ

  • ロボットアームの追加

  • URDFモデルの読み込み

  • 強化学習との連携


📚 参考リンク


このチュートリアルは、2025年11月の実験に基づいています。 Genesis バージョン: 0.3.7


いいなと思ったら応援しよう!