UnitreeG1

VRゴーグルによるUnitree G1のテレオペ

※ 弊社インターンの作成記事になります

はじめに

ここ最近、「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 専用だからです。

データの流れを図示すると以下のような感じです。

環境

項目内容
GPUNVIDIA RTX 3090(24GB)
OSUbuntu 22.04
CUDA / TensorRTCUDA 12.8 / TensorRT 10.13(x86_64はバージョン厳守)
VRPICO 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は接続できません。
  • Tailscale … 今回はサーバーがリモート(別ネットワーク)にあるため必要でした。公式手順は「PICOとPCを同じWi-Fiに繋いでLAN内のIPを入れる」前提ですが、リモートサーバーだと同じLANに乗れません。そこでPICOとサーバーの両方を Tailscale(同じ tailnet)に入れて、仮想的に同じネットワーク扱いにします

ローカルにサーバーがある人は Tailscale は不要です。僕は遠隔でサーバーと繋げて作業してたので必要でした。公式の手順通りにPICOとサーバーを同じWi-Fiに繋いで、そのLAN内のIPを入れればOKです。

各インストールの詳細手順は公式が丁寧です: VR Teleop Setup (PICO)

接続と送信の設定

XRoboToolkit(PICOアプリ)側の設定:

設定
headON
controllerON
handOFF
sendON(これがデータ送信スイッチ。ONで流れ出す)
Pico Motion TrackerFull body(足首トラッカーで全身を追従させるモード)
Remote Vision / Data CollectionOFF(今回は不要)

設定画面はこんな感じですね。この画像の状態ではセットアップは終わっていないので、ここからモーショントラッカーを接続してpcと繋げていくという感じです。


テレオペ実行

  1. deploy 窓で Init Done を確認 → MuJoCo上でロボットが立つ
  2. MuJoCoの窓をクリックして 9 を押す → ロボットを地面に落とす
  3. コントローラで A+B+X+Y(起動+初回フルキャリブレーション)
  4. 腕をロボの姿勢に合わせて A+XPOSEモード=全身追従)
  5. 体を動かす → ロボットが追従!

2番を忘れると、ロボットの操作が効かず暴れ出します、、、笑
(MuJoCo上ではデフォルトでロボットが宙に浮いており、そのままテレオペすると踏ん張れずにおかしな挙動になります)


主なコントローラ操作

動作ボタン
起動 / 緊急停止A+B+X+Y
POSE(全身追従)切替 / リセットA+X
VR_3PT(上半身のみ追従・脚はプランナー)左スティック押し込み
ハンド開閉トリガー


結果

シミュレーション上でのVRテレオペ、達成できました!!!
VRゴーグルを被って腕を上げれば G1 も腕を上げ、体を捻れば追従します。

画面左上を見るとしっかりログも取れていますね!


まとめ

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

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


参考リンク

関連記事

TOP