
NVIDIA GEAR-SONICでUnitree G1の実機を全身テレオペしてみた
こんにちは、atsu です。
NVIDIAのヒューマノイド全身制御モデル GEAR-SONIC を使って、Unitree G1の実機を全身テレオペしてみました。PICO 4とフルボディトラッカーで取った人間の全身の動きを、G1がバランスを取りながら物理追従する構成です。実機の関節状態(rt/lowstate)で追従を確認できたところまでの記録と、実機で気をつけた点を紹介します。
先に結果を書くと、実機のG1が転倒せずに立位を保ったまま、操作者の全身の動きに追従するところまで動きました。ハンド(Inspire)のグリップ操作も並走できています。
なお本記事は 2026-08上旬時点 の検証です。SONICのポリシー・deploy・シミュレーション・VRテレオペのコードは公開リポジトリのものを使っています。本文では再現できる粒度の、公開リポジトリのコマンド・構成パターン・実測値を中心に紹介します。
GEAR-SONICとは
GEAR-SONIC は、NVIDIAが公開しているヒューマノイドの全身制御(Whole-Body Control)基盤モデルです。人間の全身姿勢を参照モーションとして受け取り、ロボットがバランスを取りながらそれに追従するポリシーとして動作します。コードとモデルは NVlabs/GR00T-WholeBodyControl で公開されており、ONNX形式のポリシー + C++推論のデプロイスタック、MuJoCoシミュレーションハーネス、VRテレオペのコードが含まれます。
参照モーションの入力源は差し替え可能で、事前に用意したモーションクリップの再生でも、今回のようにVR機材から流すリアルタイムの人間の姿勢でも動きます。テキストからモーションを生成するモデル(Kimodo)と組み合わせる研究パイプラインも試していますが、それは本記事の対象外です。
システム構成
今回の構成の全体像です。
| 機材 | 役割 |
|---|---|
| Unitree G1(Inspireハンド搭載) | 追従する実機。本体29自由度 + 5指ハンド |
| PICO 4 + PICO Motion Tracker ×3 | 操作者の全身姿勢の取得(頭 + 両手コントローラ + 両足首・腰のトラッカー) |
| NVIDIA DGX Spark | SONICポリシーの推論ホスト(aarch64)。G1とは有線LAN(DDS)接続 |
| USBケーブル(PICO 4 ⇔ DGX Spark) | RNDIS(USB経由のネットワーク接続)。今回の環境ではWi-Fiで安定せず、USB有線接続にしました(後述) |
ヘッドセット側では XRoboToolkit のクライアントアプリが動き、Body Tracking APIで取得した全身姿勢をSMPL形式(人体の姿勢を関節パラメータで表す標準的な人体モデル形式)でホストに送信します。ホスト側での受信はXRoboToolkit PC Serviceというデーモンが仲介します(図では省略。セットアップの節で触れます)。
ホスト側はその姿勢をSONICのdeployプロセス(C++/TensorRT)に流し、deployがDDSの低レベル指令(rt/lowcmd)でG1の29関節を50 Hzで制御します。図中のテレオペマネージャとハンドブリッジが今回の自作部分(未公開)で、それ以外は公開リポジトリの構成要素です。
ヘッドセットの要件について補足すると、公式のVR Teleop SetupページのRequired Hardwareは「PICO 4 / PICO 4 Proヘッドセット + コントローラ ×2 + Motion Tracker ×2」です。今回は手持ちの無印PICO 4(Android 10 / SDK 29)に、クライアントアプリのSDK 29向けビルドをインストールして使いました。トラッカーは公式要件の足首2個に加えて腰に1個足した3点構成です(テレオペマネージャの腰トラッキングオプションを使用)。
なお公式のテレオペチュートリアルには、足首トラッカーの視認性を確保するためタイトなズボン/レギンスを着用するよう安全上の注意があります。緩い服だとトラッキングが不安定になり、ロボットが想定外の動きをする原因になるためです。
セットアップの要点(aarch64ホスト特有の対応)
セットアップの大枠は公式の手順どおりです。install_scripts/install_pico.sh でテレオペ用Python環境(XRoboToolkit SDK + CycloneDDS + unitree_sdk2_python)が入り、deployはDockerコンテナ内でビルドします。初回起動時にTensorRTエンジンが生成され、2回目以降はキャッシュされます。
DGX Sparkがaarch64であることに起因して、いくつか追加対応が必要でした。
1. XRoboToolkit PC Serviceはheadless arm64版 + ICU 70の同梱で動かす
XRoboToolkit SDKは、PC上のサービスデーモン(127.0.0.1:60061)と通信できないと全ゼロの姿勢を返します。公式セットアップではリポジトリ同梱のarm64版debを sudo dpkg -i でインストールする案内ですが、今回はsudo不要・GUI不要で運用したかったため、XRoboToolkit-PC-Serviceのリリースにあるheadless arm64版のdebを展開するだけの方法にしました。ただし同梱のQt 6.6.2がICU 70を要求し、Ubuntu 24.04はICU 74なので、ICU 70のdebを展開してライブラリパスに足す必要がありました。
# サービス本体(headless arm64)を展開
curl -sL -o /tmp/xrt.deb https://github.com/XR-Robotics/XRoboToolkit-PC-Service/releases/download/v1.0.0/XRoboToolkit-PC-Service-headless_1.0.0.0_arm64.deb
dpkg-deb -x /tmp/xrt.deb ~/xrobotoolkit_service
# 同梱Qt用のICU 70(jammy arm64)を展開
curl -sL -o /tmp/libicu70.deb https://ports.ubuntu.com/ubuntu-ports/pool/main/i/icu/libicu70_70.1-2_arm64.deb
dpkg-deb -x /tmp/libicu70.deb ~/xrobotoolkit_service/icu70
apt経由と違いGPG検証が働かない取得方法なので、展開前に sha256sum でUbuntuのパッケージページに記載のチェックサムと照合することをおすすめします。
起動時に LD_LIBRARY_PATH にアプリのライブラリ + ICU 70のディレクトリを通し、QT_QPA_PLATFORM=offscreen を設定します。ss -ltn でポート60061がLISTENになっていれば成功です。
2. aarch64で必要だったパッチ2件
- CycloneDDSの設定XMLに含まれる
<Tracing>ブロックがaarch64環境でバッファあふれを起こすため、unitree_sdk2pyの設定文字列から削除 - PyTorch 2.6のデフォルト
torch.load(weights_only=True)で全身姿勢の変換コードが読み込みに失敗するため、weights_only=Falseを明示。ただしweights_only=Falseはpickle経由の任意コード実行を許すため、入手経路を信頼できるファイル以外には使わないでください(今回読み込むのは自環境で用意したチェックポイントです)
まずシミュレーションで検証する
SONICのモデルカードにも「デプロイ前にシミュレーションでテストすること」とあるので、実機の前にMuJoCoで全経路を検証しました。GR00T-WholeBodyControlにはG1のMuJoCoハーネス(run_sim_loop.py)が同梱されており、deployをループバックで繋ぐと、実機と同じ経路(姿勢ストリーム→ポリシー→lowcmd)を物理シミュレーションで確認できます。
シミュレーション段階で、右肩0.97 rad・右肘1.83 radの追従、脚は膝が0.03〜2.45 radの範囲で動くことを確認しました。脚も動くということは、実機ではしゃがみ・踏み出し・転倒があり得るということなので、実機は落下保護(ハーネス)と手元の非常停止を用意してから臨みます。
全身テレオペのモード選択 — POSEモードで全身追従になる
実装を読んで分かったポイントとして、deployのポリシーには観測エンコーダのモードが3つあります(policy/release/observation_config.yaml)。
| モード(公式名) | 追従範囲 |
|---|---|
| モーション再生(PLANNER) | 事前に用意した参照モーションを追従 |
| VR 3点(VR_3PT) | 頭 + 両手の3点入力で上半身のみ操作者に追従。脚はバランス制御 |
| SMPL全身(POSE) | 脚・腰・腕すべて操作者に追従 |
(実際にはOFFなどを含む5モード構成ですが、追従に関わる3つに簡略化しています)
どのモードになるかは、ストリームされる姿勢メッセージのプロトコルバージョンで決まります(v1→モーションベース、v2/v3→SMPL全身)。テレオペマネージャのPOSEモード(全身姿勢をv3で送信するモード)を使うと、コード変更なしにSMPL全身追従になります。最初はVR 3点モードで腕だけ動かして慣らし、その後POSEモードで全身に進む順番にしました。
G1実機で動かす
実機での手順は大きく3段階です。
- deployを起動(低遅延モデル指定)。"Init done" が出るまでロボットは動かない
- テレオペマネージャを起動。PICO 4からの姿勢ストリーム受信を確認
- コントローラで2段階エンゲージ(テレオペ制御をロボットに繋ぐ操作):
A+B+X+Y … deployが制御を開始(PLANNER。ロボットがバランスを取り始める)
A+X … 全身追従開始(POSE。ここから操作者の全身に追従)
※ A+B+X+Yをもう一度押すとOFFに戻る(公式の非常停止手段のひとつ)
なお、deploy稼働中はリモコンの緊急停止(L2+B)が効きません。動かす前に後述の「注意点5」の停止手順を必ず確認してください。
エンゲージ時は、操作者は公式のキャリブレーション姿勢で立ちます。直立して足を揃え、上腕は体側に付け、前腕を前方に90°曲げたL字にし、頭を水平に保つ姿勢です。公式ドキュメントでは、この姿勢と実際の姿勢がずれたままPOSEに入るとロボットが急激に動く原因になるとして警告されています。POSEに入った後は、操作者(自分)が動くとG1がついてきます。腕を上げれば腕が上がり、腰をひねれば腰が回り、足踏みすれば脚が動きます。初めて全身がついてきたときはかなり感動しました。
追従しているかの確認は実機の関節状態で行います。 deployのログは入力が凍結していても正常に見えることがあるため、rt/lowstate(G1の実関節状態のDDSトピック)を購読して、操作中に関節の可動域が0.1 rad以上変化しているかで判定しました。今回の検証では主要関節が1〜2 radの範囲で追従し、転倒・モーター高温警告なしで完走しています。
ハンドはボディの29自由度とは独立したパイプラインで、テレオペマネージャがコントローラのグリップ/トリガー入力を「閉じ率」としてZMQで配信し、G1オンボードのブリッジがInspireハンドのDDSトピック(rt/inspire/cmd)に中継します。グリップを握るたびに+10 %閉じ、トリガーで−10 %開く階段式にしたところ、全身操作と並行しても把持状態を保持しやすくなりました。
実機で気をつけた点
検証中につまずいた点と対処です。同じ構成を組む際の参考にどうぞ。
1. エンゲージは必ず2段階で行う
制御開始(PLANNER)を飛ばして直接POSEに入るオプションもあるのですが、私の環境では制御が始まらないままモーション再生系が異常終了する挙動(exit 134)になりました。A+B+X+Y → A+Xの2段階の手順が安定して動作しました。
2. ヘッドセットを顔から外したら、FullBodyトラッキングの再キャリブレーションが必須
今回の構成では、ヘッドセットを外すとPICOのFullBody(全身)トラッキングの状態が失われ、再装着しただけでは復帰しませんでした。しかも凍結中も姿勢フレーム自体は約88 fpsで流れ続ける(フレーム番号だけ進み、中身が固定。実測)ため、受信側の見かけは正常です。ロボットが動かなくなったら、まずアプリでFullBody再キャリブレーション + 送信ONをやり直すのが正解でした。凍結判定は「ペイロード全体のハッシュ」ではなく関節グループごとの値の変化で見る必要があります(タイムスタンプが毎フレーム変わるため、ハッシュでは凍結が検出できません)。
3. 今回の環境ではWi-FiではなくUSB(RNDIS)接続で安定した
公式の要件は「高速・低遅延のWi-Fi接続」ですが、オフィスのWi-Fi環境(RTTジッタ約90 ms)では全身姿勢ストリームが途切れ、追従が凍結しました。USBケーブルでのRNDIS接続にしてから安定しています。全身モードは連続した姿勢ストリームが前提なので、私の環境ではリンク品質が実質的な前提条件になりました。
4. 立位バランスは足首が熱くなる — 短時間で停止
立位バランスを保持しているだけで、足首関節(Ankle Pitch/Roll)が約30秒で82〜94 ℃まで上がりました(腰ロールで126 ℃に達した例もあります)。吊り下げて足が浮いた状態より、足を接地して体重を支えさせたほうが発熱は緩やかでした(94 ℃→約76 ℃)。短く動かして止めて冷却するの運用と、高温警告の監視をおすすめします。
5. 停止手順を先に決めておく — deploy稼働中はリモコンの緊急停止が効かない
deployは起動時にG1の標準制御サービスを解放して低レベル制御を占有するため、稼働中はリモコンのL2+B(ダンピング)が効きません。公式の停止手段は「コントローラのA+B+X+Y再押下(OFFに戻る)」と「deployのターミナルでOキー」の2つで、まずこれを使います。加えて今回の構成では、deployプロセスにSIGTERMを送ると終了処理でダンピング指令が送られ脱力停止することを確認し、ソフト停止の経路として組み込みました。最終手段は物理的な非常停止です。docker kill(SIGKILL)はダンピングを送らずに止めるため使ってはいけません。この挙動は動かす前に把握しておくべきでした。
運用を楽にする工夫: プリフライトチェックリストUI
この構成は「PICO 4側アプリの送信設定」「トラッキング状態」「ハンド中継」「deployのフェーズ」など、エンゲージ前に確認すべき項目が12個ほどあり、どれか1つ切れていると「ロボットが動かない」という同じ症状になります。切り分けを毎回手作業でやるのは非効率だったので、全項目を操作順に並べて「今やること」を1行だけ強調するダッシュボード(ブラウザUI)を自作しました。この種の多段パイプラインでは「どこで止まっているか」を上から順に見られる仕組みを作ると、実機時間の浪費が大きく減りました。
できていないこと
- ヘッドセット内FPV(一人称視点)は未完です。 G1のカメラ映像をDDS経由で取得してMJPEG配信するところまでは動いています(1080p/62 fps)が、それをXRoboToolkitのRemote Vision機能でヘッドセット内に表示する部分は、送信プロトコルを合わせてもヘッドセット側が表示されないままで、原因を特定できていません。現状は外部モニタで代用しています
- 長時間の連続運用は足首の発熱の都合で未検証です。現状は1分〜5分程度の連続稼働です。モータに負荷の高い姿勢は、1分程度でモータが高音になります。
まとめ
- GEAR-SONIC(GR00T-WholeBodyControl)+ PICO 4 + Motion Tracker + DGX Sparkの構成で、実機G1の全身テレオペ(脚・腰・腕 + ハンド)が動きました
- 全身追従はストリームのプロトコルバージョンで決まるSMPLモードで実現でき、コード変更は不要でした
公開されているスタックを軸に、実機の全身テレオペをここまで動かすことができました。GEAR-SONICでの全身テレオペを検討している方の参考になれば幸いです。









