※ 弊社インターンの作成記事になります
はじめに
ここ最近、「Physical AI」という言葉を耳にすることが一気に増えましたね。
これは、AIの知能を現実での行動に結びつけるものです。アメリカの工場などでは、すでにたくさんの導入事例があります。
このPhysical AIにおける大きな課題の一つが「データ取得」です。
例えばLLMは、インターネット上に大量のデータがあります。だからこそ、ここ数年で一気に進化してきた、という背景もありますね。
一方、Physical AIにはそこまで充実したデータがありません。そこで「どこからデータを補うか」が問題になります。その手法の一つが、VRゴーグルなどで自分の動きをロボットに追従させ、そのデータを取る、というものです。
というわけで今回は、そんな”全身テレオペ”をシミュレーション上でやってみました!

こんな感じです笑。ジャンプもお手のもの
基本的には、NVIDIA GR00T-WholeBodyControl 公式 の内容を参考にしています。
公式の想定している環境と少し違ったのもあって、はまりどころがいくつかあったので、その辺含めて話していけたらと思います!
この記事を読むと、VRテレオペの基本的なやり方がわかります。参考程度にどうぞ!
この記事でわかること
- VRテレオペの全体像 — PICO+モーショントラッカーで、シミュレータ上のG1を全身操作する仕組み
- 環境構築 — TensorRT・MuJoCo・GEAR-SONIC のビルドと、5プロセスの一括起動スクリプト
- PICO接続 — XRoboToolkit の設定と、リモートサーバーへ Tailscale で繋ぐ方法
- ハマりどころ —
Lost LowStateの正体(DDS)、共有IPの罠、接地(9)の3つ
システム構成
最終的なテレオペレーションの起動構成は以下のようになりました。
| レイヤー | 中身 |
|---|---|
| VR側 | PICO 4 ヘッドセット + コントローラ2 + モーショントラッカー2(足首)+ XRoboToolkit |
| サーバー側 | NVIDIA製GPU 搭載の Ubuntu マシン(今回は会社の遠隔サーバーに Tailscale + NoMachine で接続) |
| ソフト | gear_sonic_deploy:C++ deployバイナリ + MuJoCoシミュレータ + PICO streamer |
このGPUですが、NVIDIA製でないとダメなようです(普通のMacのGPUなどは不可)。理由は、このパイプラインが CUDA と TensorRT を使っていて、これらが NVIDIA 専用だからです。
データの流れを図示すると以下のような感じです。

環境
| 項目 | 内容 |
|---|---|
| GPU | NVIDIA RTX 3090(24GB) |
| OS | Ubuntu 22.04 |
| CUDA / TensorRT | CUDA 12.8 / TensorRT 10.13(x86_64はバージョン厳守) |
| VR | PICO 4 + モーショントラッカー×3 |
| 接続 | Tailscale |
先ほどGPUの話をしましたが、NVIDIA製であれば必ずしもこの型番である必要はありません。十分なVRAMがあれば問題なしです。
モーショントラッカーは、足首に2つ・腰に1つの計3つです。
最初はNoMachineで遠隔操作したままテレオペしようとしたのですが、遅延が大きすぎてシミュレータが一瞬で落ちてしまい、断念しました(ログを見たら500msくらいの遅延がありました笑)。
セットアップ
1. リポジトリを取得
まずは GitHub からコードを clone します。
git clone https://github.com/NVlabs/GR00T-WholeBodyControl.git cd GR00T-WholeBodyControl
# モデルやメッシュなど大きいファイルは Git LFS 管理なので、これも実行しておく
git lfs pull
2. ビルド
続いて deploy プログラムをビルドします。流れは 「依存を入れる → 環境変数を通す → ビルド」の3ステップです。
前提として、先に TensorRT をダウンロードして
TensorRT_ROOTを通しておく必要があります(前述のとおりバージョン厳守。x86_64 は 10.13)。これを忘れると、次のsetup_env.sh/just buildで TensorRT を見つけられず、うまくコードが動きません。
cd gear_sonic_deploy # clone した GR00T-WholeBodyControl の直下にある deploy フォルダ
# ① ビルドに必要なシステム依存(cmake・各種ライブラリ等)を一括インストール
chmod +x scripts/install_deps.sh # 実行権限がなければ付与
./scripts/install_deps.sh
# ② TensorRTのパスなど、ビルド/実行に必要な環境変数をまとめてセット
source scripts/setup_env.sh
# ③ ビルド実行(just は make のようなコマンドランナー)
just build
各コマンドがやっていること:
install_deps.sh… cmake などのビルドツールや依存ライブラリを apt などでまとめて導入setup_env.sh… TensorRT の場所や各種パスを環境変数に設定(これを忘れるとビルドが TensorRT を見つけられない)just build…just(=make的なタスクランナー)で C++ の deploy バイナリを生成
ハマりどころ
- ROS2 が入っていると CycloneDDS が衝突する → ビルド前に
export HAS_ROS2=0を指定して回避(ROS2側のDDSを使わせないようにする)- 一部のソースで
-fpermissiveが必要 →-DCMAKE_CXX_FLAGS="-fpermissive"を付け、厳しめのC++エラーを警告扱いに緩めてビルドを通す
3. モデル取得
python download_from_hf.py # ONNX policy / encoder / planner を配置
4. DDS設定(※自分がハマったところ)
ここは、僕が一番ハマったポイントです。
まず前提として、通常この設定は必要ありません。 NVIDIA の公式ドキュメントにも、sim(run_sim_loop)と deploy を起動するだけで、両者は CycloneDDS を通じて自動的に見つけ合うということになっています(DDSの設定ファイルなんて登場しません)。DDSはネットワークのマルチキャストを使っていますが、普通のPCならそれが問題なく通るからです。
ところが、僕の環境は少し特殊でした。 会社のリモートサーバー上で作業していたのですが、DDSがまともに使えるネットワークインターフェースが実質 lo(ループバック)だけ、という状況であることが判明しました。しかし lo はマルチキャスト非対応なので、sim と deploy が互いを発見できず、deploy が起動直後に Lost LowState(=ロボの状態が返ってこない)を出して安全停止してしまいました。
同じような状況の人は少ないと思いますが(笑)、解決策を提示しておきます。
解決策は「マルチキャストをやめて、localhost のユニキャストで直接つなぐ」ようCycloneDDSに指示すること。 下のXMLを用意し、環境変数で読み込ませます。
用語ミニ解説
- マルチキャスト:「誰かいますかー?」とネットワークに一斉に呼びかけて相手を探す方式(1対多)。DDSはデフォルトでこれを使う
- ユニキャスト:「このアドレスの相手」と1対1で直接指定して通信する方式。相手が分かっていれば確実
- ループバック(
lo/localhost):自分自身へ向かう通信路。外のネットワークには出ず、同じマシンの中だけで完結する要するに今回は「一斉呼びかけ(マルチキャスト)が使えない環境なので、”相手は同じマシンの localhost にいる” と直接指定(ユニキャスト)し、通信も外に出さず同マシン内(ループバック)で完結させる」設定にした、というわけです。
こんなのは知ってる人がほとんどかも知れませんが、、、
備忘録程度に思っていただけると
~/cyclonedds_localhost.xml
<?xml version="1.0" encoding="UTF-8"?>
<CycloneDDS xmlns="https://cdds.io/config">
<Domain id="any">
<General>
<Interfaces><NetworkInterface name="lo" presence_required="false"/></Interfaces>
<AllowMulticast>false</AllowMulticast>
</General>
<Discovery>
<ParticipantIndex>auto</ParticipantIndex>
<Peers><Peer address="localhost"/></Peers>
</Discovery>
</Domain>
</CycloneDDS>
# 上のXMLをCycloneDDSに読ませる(後述の起動スクリプト先頭にも入れておくと確実) export CYCLONEDDS_URI="file://$HOME/cyclonedds_localhost.xml"
この設定を行うことで sim ↔ deploy がループバック経由で発見・通信できるようになり、Lost LowState 問題が解決しました。
5. 一括起動スクリプト
システム構成の「ソフト」の欄をみていただけたら分かると思いますが、PC側で立ち上げなければいけないソフトが多く(deploy,mujocoなど)、全てを毎回自分で立ち上げるのは相当面倒ですよね。
ということで、5つのプロセスを順番に立ち上げるスクリプトを作ってみました。
その一部抜粋を以下に記します。
スクリプト内で使っている.venv_teleopは、リポジトリルートでbash install_scripts/install_pico.shを実行すると作られるテレオペ用のPython環境(初回のみ)。MuJoCo・XRoboToolkit SDK・Unitree SDK2 などがまとめて入ります。これを作っていないと、下のスクリプトのsource .venv_teleop/bin/activateでコケます。
#!/bin/bash
export CYCLONEDDS_URI="file://$HOME/cyclonedds_localhost.xml"
# 起動時に残骸を掃除(ポート衝突防止)
pkill -9 -f run_sim_loop; pkill -9 -f g1_deploy_onnx; pkill -9 -f pico_manager; pkill -9 -f socat
sleep 2
# 1) XRoboToolkit PC Service
gnome-terminal --title="1-service" -- bash -c "/opt/apps/roboticsservice/runService.sh"
# 2) socat(PICOからの接続をローカルへ転送)
gnome-terminal --title="2-socat" -- bash -c "socat TCP-LISTEN:60061,bind=$(tailscale ip -4),fork,reuseaddr TCP:127.0.0.1:60061"
# 3) MuJoCo
gnome-terminal --title="3-sim" -- bash -c "source .venv_teleop/bin/activate && python gear_sonic/scripts/run_sim_loop.py"
sleep 10
# 4) deploy(GEAR-SONICポリシー)
gnome-terminal --title="4-deploy" -- bash -c "cd gear_sonic_deploy && echo y | ./deploy.sh --input-type zmq_manager sim"
sleep 7
# 5) PICO streamer
gnome-terminal --title="5-pico" -- bash -c "source .venv_teleop/bin/activate && python gear_sonic/scripts/pico_manager_thread_server.py --manager --vis_vr3pt --vis_smpl"
こんな感じですね。自分でも作ってみたかったら、元のNVIDIAの公式ページとこの記事をAIに読み込ませれば作れると思います、笑。
下の画像のような感じで、複数のターミナルの窓とmujocoの画面が立ち上がります(少しみにくいですが)。

PICO接続
事前準備:PICO側に入れておくもの
PC側の準備はあらかた終わったので、次はPICO側の設定です。
まずテレオペを始める前に、次の2つのソフトを入れておきます。
- XRoboToolkit(PICOアプリ) … 頭・コントローラ・トラッカーの動きをPCへ送るアプリ。PICOのブラウザからAPKを落として入れます(要・デベロッパーモード)。
- 対になる形で、サーバー側には PC Service(
.deb) を入れて起動しておきます。これが受け口なので、動いていないとPICOは接続できません。
- 対になる形で、サーバー側には PC Service(
- Tailscale … 今回はサーバーがリモート(別ネットワーク)にあるため必要でした。公式手順は「PICOとPCを同じWi-Fiに繋いでLAN内のIPを入れる」前提ですが、リモートサーバーだと同じLANに乗れません。そこでPICOとサーバーの両方を Tailscale(同じ tailnet)に入れて、仮想的に同じネットワーク扱いにします
ローカルにサーバーがある人は Tailscale は不要です。僕は遠隔でサーバーと繋げて作業してたので必要でした。公式の手順通りにPICOとサーバーを同じWi-Fiに繋いで、そのLAN内のIPを入れればOKです。
各インストールの詳細手順は公式が丁寧です: VR Teleop Setup (PICO)
接続と送信の設定
XRoboToolkit(PICOアプリ)側の設定:
| 設定 | 値 |
|---|---|
| head | ON |
| controller | ON |
| hand | OFF |
| send | ON(これがデータ送信スイッチ。ONで流れ出す) |
| Pico Motion Tracker | Full body(足首トラッカーで全身を追従させるモード) |
| Remote Vision / Data Collection | OFF(今回は不要) |

設定画面はこんな感じですね。この画像の状態ではセットアップは終わっていないので、ここからモーショントラッカーを接続してpcと繋げていくという感じです。
テレオペ実行
deploy窓でInit Doneを確認 → MuJoCo上でロボットが立つ- MuJoCoの窓をクリックして
9を押す → ロボットを地面に落とす - コントローラで
A+B+X+Y(起動+初回フルキャリブレーション) - 腕をロボの姿勢に合わせて
A+X(POSEモード=全身追従) - 体を動かす → ロボットが追従!
2番を忘れると、ロボットの操作が効かず暴れ出します、、、笑
(MuJoCo上ではデフォルトでロボットが宙に浮いており、そのままテレオペすると踏ん張れずにおかしな挙動になります)
主なコントローラ操作
| 動作 | ボタン |
|---|---|
| 起動 / 緊急停止 | A+B+X+Y |
| POSE(全身追従)切替 / リセット | A+X |
| VR_3PT(上半身のみ追従・脚はプランナー) | 左スティック押し込み |
| ハンド開閉 | トリガー |
結果
シミュレーション上でのVRテレオペ、達成できました!!!
VRゴーグルを被って腕を上げれば G1 も腕を上げ、体を捻れば追従します。

画面左上を見るとしっかりログも取れていますね!
まとめ
- 本質は「
deploy.sh ... sim+ PICO を XRoboToolkit で繋ぐ」だけ。ではありますが、 DDS 設定・IP・ロボットの接地 などハマりどころがいっぱいありました。とはいえ説明を聞くだけではわからないと思うので、自分でやる時はとりあえずハマってみてください笑 - 次回は実機( Unitree G1 本体)でのテレオペをやろうと思っています!お楽しみに!

これは余談ですが、姿勢制御が効いていなくて倒れ続けてしまうG1君です