0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NVIDIA Isaac Sim で Unitree G1を踊らす

0
Posted at

おことわり

本件は、私一人では到底実現できなかったことを、Claude Codeに相談しながら進めたものです。
また、本記事の大部分は、Claude Codeが作成した文章を私が加筆・修正し、さらにClaude Codeによる校正を経て仕上げています。
コードおよびコードの解説はすべてClaude code 作です。

やったこと

前提条件

  • NVIDIA RTX 系 GPU(VRAM 8GB 以上推奨)+ 最新のドライバ
    • ※私の環境は RTX 5070(Blackwell 世代 / RTX 50 シリーズ)です。この世代は後述のとおり CUDA 12.8 対応の PyTorch(cu128 ビルド)が必須で、そこだけ通常と手順が違います。
  • ディスク空き容量 50GB 程度
  • Git
  • Conda(Miniconda で可)

Windows の「長いパスの有効化」

管理者 PowerShell(スタートボタンを右クリック >「ターミナル(管理者)」をクリック)で、一度だけ以下のコマンドを実行します。

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

Miniconda のインストール

PowerShell で次を実行してください。

winget install -e --id Anaconda.Miniconda3

インストール後、そのままでは conda コマンドは PATH に入らないので、初期化を1回だけ実行します。

& "$env:USERPROFILE\miniconda3\Scripts\conda.exe" init powershell

その後、PowerShell を一度閉じて開き直してください。プロンプトの先頭に (base) と表示されれば準備完了です。

インストール

1. Python 環境を作る(Isaac Sim 5.1 / Python 3.11)

Isaac Sim 5.1 は Python 3.11 が必要です。

conda create -n env_isaac51 python=3.11 -y
conda activate env_isaac51

プロンプト先頭が (env_isaac51) になったことを確認してください。

python -m pip install --upgrade pip

2. Isaac Sim 5.1 のインストール

pip install "isaacsim[all,extscache]==5.1.0" --extra-index-url https://pypi.nvidia.com

数GBのダウンロードで数十分かかります。「Successfully installed ...」が出るまで待ってください。

3. PyTorch を GPU 版に揃える(RTX 5070 で特に重要)

今回 torch 2.7.0 が依存パッケージとして自動で入りましたが、これが GPU 対応版(CUDA 12.8)か CPU 版かで手順が変わります。

RTX 5070(Blackwell)ならではのポイント
RTX 50 シリーズ(Blackwell / GPU アーキテクチャ sm_120)は、CUDA 12.8 以降に対応した PyTorch(+cu128 ビルド)でないと GPU が認識されません。古い CUDA ビルドや CPU 版のままだと、torch.cuda.is_available()False になったり、実行時に「no kernel image is available」系のエラーで動きません。そのため、下記の確認と(必要なら)cu128 への差し替えが RTX 5070 では必須の作業になります。

次のコマンドで確認してください。

python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
  • 2.7.0+cu128 True のように表示 → GPU 版が入っています。次の差し替えは不要、そのまま起動確認へ進めます。
  • 2.7.0+cpuFalse と表示 → CPU 版なので差し替えが必要です。次のコマンドを実行してください。
pip install --force-reinstall torch==2.7.0 torchvision==0.22.0 torchaudio==2.7.0 --index-url https://download.pytorch.org/whl/cu128

torch の GPU 版(2.7.0+cu128)は入ったものの、差し替えで一部パッケージのバージョンがズレることがあります。以下のコマンドで、ズレたパッケージを Isaac Sim 指定のバージョンに戻してください(torch の GPU 版はそのまま残ります)。

pip install numpy==1.26.0 Pillow==11.3.0 typing_extensions==4.12.2 filelock==3.13.1 fsspec==2024.6.1 markupsafe==2.1.3 networkx==3.3 sympy==1.13.3

実行後、2つ確認します。

python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

2.7.0+cu128 True ならOK(numpy を戻しても torch は GPU 版のまま動きます)。

python -c "import numpy; print(numpy.__version__)"

1.26.0 ならOK。

両方確認できたら、いよいよ起動確認です。

起動

python -c "import isaacsim; isaacsim.main()"
  • 初回はライセンス同意の確認が出ます(yes で承諾)。
  • その後、シェーダーコンパイルとアセット取得で起動まで10分以上かかることがあります。真っ暗なウィンドウやフリーズ風の状態も正常なので待ってください。
  • GUI が開いて空のビューポートが表示されれば成功です。

Isaac Sim が立ち上がる

image.png

ダンス再生

Unitree G1 を配置

image.png

ちなみに、IssacSim はUnitreeを含めて44社のロボットのシミュレーションができます。(BostonDynamics, Boster Robotics, iRobot, Kawasaki、Yaskawaなど)

スクリーンショット 2026-07-21 194926.png

モーションは GUI では付けられません(コードで動かします)

Isaac Sim の GUI には、ロボットにダンスの動きをドラッグ&ドロップで貼り付けるような機能はありません。今回のモーションは「1フレームずつの関節角度が入った CSV」を読み込み、物理ステップごとに各関節へ角度を書き込む、というコードで再生しています。そのため、再生には必ず下記のスクリプトを実行する必要があります(メニュー操作だけでは動きません)。

モーションデータはどこから持ってきたか

使っているのは Unitree LAFAN1 Retargeting Datasetmotions/dance1_subject1.csv)です。

  • 元データは Ubisoft の LAFAN1(人間のモーションキャプチャデータセット)。
  • それを Unitree が G1 / H1 などの実機の関節構成に リターゲット(変換)した CSV として配布しているものです(HuggingFace: unitreerobotics/LAFAN1_Retargeting_Dataset)。
  • CSV は1行が1フレームで、列の並びは ルート位置(3) + ルート姿勢クォータニオン xyzw(4) + 関節角度(29) = 計36列dance1_subject1.csv は 30FPS のデータです。

コード

  1. メニューの Window → Script Editor を開く

  2. 下のコードを全部貼り付ける

    ※実行前に Stage パネルで g1 をクリックして Prim Path を確認してください。/World/g1 でなければ、コード内の ROBOT_PRIM をその値に書き換えます。

  3. Run(Ctrl+Enter)を押す → 自動で再生が始まり、G1 が踊り出します


import asyncio
import traceback
import numpy as np
from isaacsim.core.api import World
from isaacsim.core.api.robots import Robot

CSV = r"C:\projects\Unitree-dancing\motions\dance1_subject1.csv"
ROBOT_PRIM = "/World/g1"
FPS = 30.0

JOINT_NAMES = [
    "left_hip_pitch_joint", "left_hip_roll_joint", "left_hip_yaw_joint",
    "left_knee_joint", "left_ankle_pitch_joint", "left_ankle_roll_joint",
    "right_hip_pitch_joint", "right_hip_roll_joint", "right_hip_yaw_joint",
    "right_knee_joint", "right_ankle_pitch_joint", "right_ankle_roll_joint",
    "waist_yaw_joint", "waist_roll_joint", "waist_pitch_joint",
    "left_shoulder_pitch_joint", "left_shoulder_roll_joint",
    "left_shoulder_yaw_joint", "left_elbow_joint",
    "left_wrist_roll_joint", "left_wrist_pitch_joint", "left_wrist_yaw_joint",
    "right_shoulder_pitch_joint", "right_shoulder_roll_joint",
    "right_shoulder_yaw_joint", "right_elbow_joint",
    "right_wrist_roll_joint", "right_wrist_pitch_joint", "right_wrist_yaw_joint",
]

data = np.loadtxt(CSV, delimiter=",")
root_pos = data[:, 0:3]
quat = data[:, 3:7][:, [3, 0, 1, 2]]   # CSVはxyzw → Isaacはwxyz に並べ替え
joints = data[:, 7:36]                  # 8列目以降の29関節
n = len(data)
duration = (n - 1) / FPS
state = {"t": 0.0, "err": False}


async def setup():
    try:
        World.clear_instance()
        world = World(stage_units_in_meters=1.0)
        robot = world.scene.add(Robot(prim_path=ROBOT_PRIM, name="dancer"))
        await world.reset_async()
        world.get_physics_context().set_gravity(0.0)   # 倒れないよう重力オフ
        print("重力オフ完了")

        dof_index = {nm: i for i, nm in enumerate(robot.dof_names)}
        idx = [dof_index[nm] for nm in JOINT_NAMES if nm in dof_index]
        cols = [c for c, nm in enumerate(JOINT_NAMES) if nm in dof_index]
        ndof = len(robot.dof_names)
        print(f"マッピング済み関節: {len(idx)}/{len(JOINT_NAMES)} (全DOF: {ndof})")

        def on_step(dt):
            if state["err"]:
                return
            try:
                state["t"] = (state["t"] + dt) % duration
                f = state["t"] * FPS
                i0 = int(f)
                i1 = min(i0 + 1, n - 1)
                a = f - i0
                full = np.array(robot.get_joint_positions(), dtype=float)
                full[idx] = (1 - a) * joints[i0, cols] + a * joints[i1, cols]
                robot.set_joint_positions(full)
                robot.set_joint_velocities(np.zeros(ndof))
                q0, q1 = quat[i0], quat[i1]
                if np.dot(q0, q1) < 0:
                    q1 = -q1
                q = (1 - a) * q0 + a * q1
                q = q / np.linalg.norm(q)
                robot.set_world_pose(
                    position=(1 - a) * root_pos[i0] + a * root_pos[i1],
                    orientation=q,
                )
                robot.set_linear_velocity(np.zeros(3))
                robot.set_angular_velocity(np.zeros(3))
            except Exception:
                state["err"] = True
                print("=== 再生中エラー ===")
                traceback.print_exc()

        if world.physics_callback_exists("dance"):
            world.remove_physics_callback("dance")
        world.add_physics_callback("dance", on_step)
        await world.play_async()
        print("再生開始")
    except Exception:
        print("=== セットアップエラー ===")
        traceback.print_exc()


asyncio.ensure_future(setup())

このコードのポイントは以下です。

  • data = np.loadtxt(CSV, ...) 以降:CSV を読み込み、列を「ルート位置 / 姿勢クォータニオン / 29関節」に切り分けています。quat[3, 0, 1, 2] は、CSV の xyzw 並びを Isaac Sim の wxyz 並びに直すための並べ替えです。
  • JOINT_NAMESdof_index の対応付け:CSV の関節の並び順を、実際のロボット(USD)の DOF 名と突き合わせて、どの列をどの関節へ入れるか(idx / cols)を作っています。
  • on_step(dt)(物理ステップごとに毎フレーム呼ばれる関数)が再生の心臓部:経過時間 t から今のフレーム位置を計算し、前後フレームを線形補間(クォータニオンは正規化つき補間)して、set_joint_positionsset_world_pose でロボットに書き込みます。これを毎ステップ繰り返すことで踊って見えます。
  • set_gravity(0.0) と速度を毎回ゼロにしているのは、物理で倒れたり暴れたりしないよう、あくまで「決めたポーズを再生する(キネマティック的な)」方針にしているためです。

他のダンス(モーション)をやりたいときも手順は同じ?

はい、同じ手順で差し替えられます。変えるのは基本 1 か所だけです。

  • コード先頭の CSV = r"...\dance1_subject1.csv" を、別のモーション CSV のパスに書き換えるだけです(例:dance2_subject3.csv など)。あとは同じように Run するだけで、そのモーションが再生されます。

注意点:

  • 差し替える CSV が 同じ Unitree LAFAN1 Retargeting Dataset の G1 用であれば、列の並び(36列・29関節)が共通なのでコード側の修正は不要です。
  • 別のデータセットや別ロボット(H1 など)の CSV は、関節の数や並び順、列構成が違うため、JOINT_NAMES や列の切り出し(data[:, 7:36] など)を、そのデータに合わせて直す必要があります。
  • FPS が 30 以外のデータなら FPS = 30.0 の値も合わせてください。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?