LeRobot documentation

Isaac Teleop

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.7.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Isaac Teleop

NVIDIA Isaac Teleopは、マルチモーダルな遠隔操作フレームワークです。Isaac Teleopは、XR(VR)コントローラー、ハンドトラッキング、フルボディトラッキング、Manusグローブ、フットペダルなど、さまざまな入力デバイスから単一のTeleopSessionを駆動します。

LeRobotでは、Isaac Teleopはexamples/isaac_teleop_to_so101/配下の自己完結型サンプルとして提供されています。 Isaac Teleopの各入力デバイスは、サンプルのisaac_teleopパッケージ内で独自のTeleoperatorサブクラスとして実装されており、1つのセッションライフサイクルを共有しています(IsaacTeleopTeleoperatorを参照)。現在利用可能なデバイスは、XRコントローラー(XRController)と、バックドライブ可能なSO-101リーダーアーム(SO101LeaderArm)です。Manusグローブや手/全身トラッキングは、次の自然なデバイス候補となります。本ガイドではXRコントローラーに焦点を当てており、SO-101リーダーについてはサンプルの実行の項で概要を説明しています。

このガイドでは、以下の内容を学習します:

  • Isaac Teleopデバイスがロボットの末端エフェクタ(EE)のターゲットをどのように駆動するか
  • XRコントローラーのクラッチ(スクイーズ/グリップ)が、アームを急激に動かすことなくテレオペレーションをどのように開始するか
  • SO-101の遠隔操作サンプルの実行方法と、モーション/グリップ/IKの調整

インストール

この例は LeRobot リポジトリにあり(lerobot pip パッケージの一部ではありません)ので、リポジトリをクローンしてソースからインストールしてください。常に最新かつ正規のインストールおよび使用法の参考資料は、この例の README.md です。要約すると:

git clone https://github.com/huggingface/lerobot.git
cd lerobot
uv pip install -e ".[feetech,kinematics,dataset]" "huggingface_hub>=1.5"
uv pip install "isaacteleop[cloudxr,retargeters-lite]~=1.3.131" "scipy>=1.14"

isaacteleop はパブリック PyPI に公開されています(Linux のみ)。cloudxr エクストラは CloudXR ランタイムのバインディングを含み、retargeters-lite は scipy ベースのリターゲッターのパスで、x86_64 と ARM の両方で解決できます(aarch64、例えば DGX Spark では、dex-retargeting/nlopt のピン留めの影響で完全な retargeters エクストラは解決できないため、ここではデフォルトとしていません)。x86_64 では、完全なリターゲッタースタックを追加でインストールできます:

uv pip install "isaacteleop[retargeters]~=1.3.131"

CloudXRを設定してヘッドセットを接続する

Isaac Teleop は、NVIDIA CloudXR を介してヘッドセットをあなたのマシンにストリーミングします。CloudXR は、セッションが接続する OpenXR ランタイムを提供します。デフォルトでは、teleop_device.connect() を呼び出すと、LeTeleop が CloudXR ランタイムを自動的に起動します。別々のシェルで python -m isaacteleop.cloudxr と source cloudxr.env を実行する必要はなくなりました。必要なものは、対応するヘッドセットの接続と、CloudXR のファイアウォールポートの開放だけです。ヘッドセットのペアリングとファイアウォールの詳細については、Isaac Teleop の クイックスタート を参照してください。

初回実行(EULA)。 最初の起動時には、NVIDIA CloudXR の EULA を受け入れる必要があります。自動起動では stdin 経由でプロンプトが表示されるため、ヘッドレス環境では入力待ちで停止してしまいます。EULA は一度だけ対話的にブートストラップしてください。

python -m isaacteleop.cloudxr --accept-eula   # one-time: accept the CloudXR EULA

その後、connect() がランタイムを非対話的に起動します。ランタイムの起動中は、約30秒間ブロックされます。

設定。IsaacTeleopConfig の2つのフィールド(すべてのデバイスで共有)がこれを制御します:

  • auto_launch_cloudxr (デフォルト: True) — connect() がランタイムを起動するかどうかを指定します。CloudXR が外部で既に実行されている場合は、False に設定してください。
  • cloudxr_env_file (デフォルトは None) — ヘッドセットのトランスポート (例: Apple Vision Pro プロファイル) を選択するための、任意の CloudXR デバイスプロファイル .env です。これはランチャーの 入力であり、旧来の手動フローで source するよう指示されていた ~/.cloudxr/run/cloudxr.env という 出力 ファイルではありません。 None の場合、デフォルトの自動 WebRTC プロファイルが維持されますが、SO-101 の例では、--teleop.cloudxr_env_file を渡さない限り、teleoperate.py の隣に同梱されている default.env に上書きされます。

オプトアウト。自動起動をスキップする(CloudXRが既に実行中)場合は、auto_launch_cloudxr=Falseを設定するか、以下をエクスポートしてください:

export LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1

環境変数が設定フィールドよりも優先されます: LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1が設定されている場合、auto_launch_cloudxr=Trueであっても自動起動はスキップされます。この変数はIsaac LabのISAACLAB_CXR_SKIP_AUTOLAUNCHとは独立しており、片方を設定してももう片方には影響しません。

プロセスごとに1つのテレオペレーター。 CloudXRランタイムは環境をプロセス全体で設定する(シングルトン)ため、プロセスごとにIsaac Teleopのテレオペレーターを1つだけ実行してください。

シャットダウン。 終了時には、Ctrl-C を含め、必ず teleop_device.disconnect() を呼び出してください。テレオペレーションのループを try/finally で囲み、finally 内で disconnect() を呼び出します。これにより、CloudXR ランタイムより 先に OpenXR セッションが破棄されますが、これが必須の順序です。ランチャーの atexit フックはランタイムの回収のみを行い、セッションの __exit__ は実行しないため、明示的な disconnect() がない場合、中断された実行は誤った順序でシャットダウンされます。

teleop_device.connect()
try:
    while True:
        action = teleop_device.get_action()
        # ... drive the robot ...
finally:
    teleop_device.disconnect()

サポートされているOS / GPU / CloudXRのバージョンおよびヘッドセットについては、システム要件を参照してください。

仕組み

XRコントローラーは、Isaac Teleopの入力デバイスの一つです。XRControllerは意図的に薄いリーダーとして設計されており、生のコントローラーグリップのポーズ(すでにロボットベース座標系へ静的にリベース済み)と、スクリーズおよびトリガーのアナログ値を公開します。リターゲッターもクラッチロジックも持ちません。クラッチ(エンゲージラッチとEEへのデルタリベース)およびグリップのマッピングは、下流のサンプルループに実装されています。このループは、スマートフォンテレオペレーターが使用するのと同じ、LeRobotの既存のクローズドループのデカルトIKパイプラインにデータを供給します。デバイス固有の部分はXRController、ループのClutch、およびMapXRControllerActionToRobotActionです。それ以降の下流(EEBoundsAndSafety、InverseKinematicsEEToJoints)は共有されており、将来のデバイス(例:Manusグローブ)は、teleop_<device>.pyとプロセッサを自前のものに差し替えるだけで、残りの部分を再利用できます。

XRController._build_pipeline は、Isaac Teleop の ControllersSource を接続します。このソースは、ネイティブの ControllerTransform (base_T_anchor) によってロボットベース座標系に静的にリベースされ、変換後のコントローラーストリームがそのまま公開されます。get_action() は、グリップのポーズ、スクリーズ、トリガーをこのストリームから直接読み取ります。セッションは常に RUNNING ステップで進行します(ゲートに使用するクラッチリターゲッターは存在しません)。

Clutch クラス(examples/isaac_teleop_to_so101/isaac_teleop/clutch.py にあり、common.py のループによって駆動される)は、Isaac Teleop の SO101ClutchRetargeter を模倣していますが、ループ内に存在するため、デバイスは薄いリーダーとして機能し続けることができます:

  • スクリューのengage edge(スクリューがclutch_thresholdを初めて超えるフレーム)でengage originを固定し、その位置を基準に位置と姿勢の両方を再基準化(rebase)するため、engageしてもアームが瞬間移動しません。Clutch.rebaseは絶対ベースフレームのターゲットを(pos, quat)のペアとして返し、ループはこれを連結して7次元のee_poseとしてプロセッサに渡します。
  • アナログトリガーは、トリガーの引き量に比例して[0, 1](0 = 開、1 = 閉)のclosedness(グリップの閉じ具合)となり、MapXRControllerActionToRobotActionがこれを顎の目標値にマッピングします。

ソースノードとリターゲッターの組み合わせ方については、Isaac TeleopのRetargetingインターフェースおよびアーキテクチャ概要を参照してください。

  VR controller (OpenXR)
        │
        ▼
  XRController.get_action()          ── raw base-frame grip_pos / grip_quat + squeeze + trigger
        │                                (TeleopSession always stepped RUNNING; clutch lives downstream)
        ▼
  Clutch.rebase(grip_pos, grip_quat) ── engage-relative delta applied to the EE home (pos + orient)
        │  ee_pose (7) / closedness       → absolute ee_pose; closedness = trigger
        ▼
  MapXRControllerActionToRobotAction ── absolute ee.x/y/z; ee.w* = orientation rotvec target;
        │  ee.x/y/z / ee.w* / ee.gripper_pos   ee.gripper_pos = (1 - closedness) * 100
        ▼
  EEBoundsAndSafety                ── workspace clip + per-frame step clamp (clamp+warn)
        │
        ▼
  InverseKinematicsEEToJoints      ── closed-loop Placo IK; position + soft-orientation
        │  (orientation_weight=0.01)   (passes ee.gripper_pos → gripper.pos)
        ▼
  SO-101 follower joint targets

クラッチ: サンプルループが所有

電話パイプライン(MapPhoneActionToRobotActionとEEReferenceAndDeltaにクラッチを分割する)とは異なり、XRクラッチは完全にサンプルループのClutchクラス内に存在します。これは絶対EE姿勢を出力するため、EEReferenceAndDeltaステージもプロセッサ内でのデルタ累積も存在しません。MapXRControllerActionToRobotActionは、純粋でステートレスなフレームごとのマッピングです。

クラッチは、engage edge(clutch_thresholdをsqueezeが越える瞬間)でengageの原点をラッチし、その原点に対するモーションのrelativeからEEを駆動するため、engage時にアームがテレポートしません。everyのengage(起動時およびタスク中の再クラッチ)において、ホームpositionはアームのmeasured jointsに対するフォワードキネマティクスからラッチされるため、disengage中にアームが移動していても、ホームはアームが物理的にある位置と等しくなり、engageはジャンプなしで実現されます。ホームorientationは最後にコマンドされた回転を保持します。5-DOFアームはorientationをソフトにのみ追跡するため、測定されたリストのorientationをラッチすると、その追跡オフセットが再クラッチのたびにコマンドに注入されてしまうためです。

コントロール

  • スクリュー / グリップ — クラッチ(デッドマン)。clutch_thresholdを超えて保持するとテレオペレーションが有効になり、離すと一時停止します。各回の有効化時に原点を再取得するため、一時停止中に手を再配置して、アームが跳ねることなく再有効化できます(インデックス / クラッチ方式)。
  • トリガー — アナログで制御されるグリップ。顎はトリガーの押下量に比例して追従します(トリガーを半分押すと顎は半分閉じます)。これは[0, 1]の閉度(0 = 開、1 = 閉)で表され、グリップの関節の絶対目標値にマッピングされます。
  • コントローラーの向き — 手首。クラッチは、コントローラーの向き(engage相対、ベース座標系)を、手首が位置と並行して追跡するソフトIKの向きターゲットとして再ベースします。5-DOF SO-101では、手首は手だけを部分的に追跡するように設計されています — 以下のorientation_weightを参照してください。

はじめる

ステップ1: テレオペレータの作成

# Run from the repo root so the `examples` package is importable.
from examples.isaac_teleop_to_so101.isaac_teleop import XRController, XRControllerConfig

teleop_config = XRControllerConfig(
    hand_side="right",      # "left" or "right" controller
    clutch_threshold=0.5,   # squeeze value above which the clutch engages
)
teleop_device = XRController(teleop_config)

XRController.get_action() は、クラッチでリベースされたターゲットではなく、生のベースフレームでのコントローラーのポーズを返します。grip_pos (3,) [x, y, z] [m] と grip_quat (4,) [qx, qy, qz, qw] はロボットベースフレームでの値であり、スカラーの squeeze と trigger は [0, 1] 範囲のアナログ値です。サンプルループの Clutch はこれらを絶対的な ee_pose に変換し、ループは clutch_threshold と比較してスリズを閾値処理することでクラッチをエンゲージします。

ステップ2: 接続

最初にteleop_device.connect()を呼び出すと、CloudXRランタイムが自動的に起動します(オプトアウトしていない場合 — CloudXRのセットアップとヘッドセットの接続を参照。この処理は約30秒ブロックし、初回実行時にはstdinでEULAの同意を求められます)。その後、Isaac TeleopのTeleopSessionが開始されます(OpenXRセッションを開き、コントローラーを検出します)。XRコントローラーは自己キャリブレーション機能を持つため、手動でのキャリブレーション手順は不要です — クラッチが各エンゲージ時にリセンターリングを処理します。セッションが終了時やCtrl-C時にランタイムより先に破棄されるよう、connect()をdisconnect()を呼び出すtry/finallyと組み合わせて使用してください。

ステップ3: サンプルの実行

この例では、ロボット(SO-101 フォローア)を設定し、正しいシリアルポートを設定していることを前提としています。

初回実行時に、ロボットのURDFとそのメッシュが自動的に取得されます。XRデバイスは、lerobot/robot-urdfs Hugging FaceバケットからSO-101のURDFをLeRobotキャッシュ(HF_LEROBOT_HOME/robot-urdfs/so101/)にダウンロードし、以降は再利用されるため、個別のダウンロード手順は不要です:

python -m examples.isaac_teleop_to_so101.teleoperate --robot.type=so101_follower --robot.port=/dev/ttyACM0 \
    --robot.id=so101_follower_arm --teleop.type=xr_controller

CLI は lerobot-teleoperate スタイル (draccus) です: --robot.* で SO-101 フォローアを設定し、--teleop.type で Isaac 入力デバイス (xr_controller | so101_leader) を選択します。デバイスの設定項目は --teleop.* です。 --teleop.type=xr_controller を指定すると、上記で説明した XR コントローラーのパスが実行されます。起動時の安全契約: デフォルトでは、--reset_duration 秒かけてすべての関節をデフォルトのリセット姿勢までスロー移動します (--reset_to_origin=false を指定すると、アームは現在の姿勢のまま保持されます)。その後、アームの測定された姿勢からクラッチのホームをシードすることで、最初のエンゲージ時にジャンプが発生しないようにします。フォローアへのコマンドは、クラッチがエンゲージされている間のみ発行されます。

リセットポーズのカスタマイズ。 リセットポーズは組み込みのデフォルトとして同梱されており(快適な中間範囲のポーズ)、そのまま動作します。そのため、何らかの記録を行う必要はありません。ご自身の環境に合わせて調整するには、アームを希望するポーズに逆運動学で移動させ、python -m examples.isaac_teleop_to_so101.override_reset_pose --id <robot.id> を実行してください。これにより、現在の関節値が LeRobot キャッシュ内のアームごとのファイル(HF_LEROBOT_HOME/reset_poses/<robot.name>/<robot.id>.json、キャリブレーションと同様のキー付け)に書き込まれ、次の実行時には組み込みデフォルトよりも優先的に使用されます。このファイルはリポジトリではなくユーザーローカルなキャッシュに保存されるため、オーバーライドはご自身のマシン上にのみ保持されます。同じ --robot.id で起動した場合、teleoperate と record の両方でこの設定が反映されます。

もう一方のデバイス、--teleop.type=so101_leaderは、Isaac Teleopのネイティブなso101_leaderプラグインによって関節がストリーミングされるバックドライブ可能なSO-101のリーダーアームから、フォロワーを1:1でミラーリングします(クラッチもIKも不要で、リーダーとフォロワーはSO-101の運動学を共有します)。

so101_leader_plugin バイナリは C++ プラグインであり、isaacteleop pip パッケージの一部ではありません。Isaac Teleop のソースツリーからビルドする必要があります。ソースから Isaac Teleop をビルドするの手順に従ってください(要約すると、Isaac Teleop のチェックアウト先から cmake -B build && cmake --build build --parallel && cmake --install build を実行します)。ビルドによりプラグインは <IsaacTeleop>/install/plugins/ 以下にインストールされるため、バイナリは install/plugins/so101_leader/so101_leader_plugin に配置されます。これは以下の --launch_plugin パスです。シリアルやキャリブレーションの詳細については、バイナリの隣にあるプラグイン独自の README.md を参照してください。

--teleop.port を物理リーダーのシリアルポートに、--launch_plugin をそのプラグインのバイナリに設定すると、CloudXR が起動した後にスクリプトがプラグインを起動します。

python -m examples.isaac_teleop_to_so101.teleoperate --robot.type=so101_follower --robot.port=/dev/ttyACM0 \
    --robot.id=so101_follower_arm --teleop.type=so101_leader \
    --teleop.port=/dev/ttyACM1 --teleop.id=so101_leader_arm \
    --launch_plugin=/code/Teleop/install/plugins/so101_leader/so101_leader_plugin

(ここで so101_leader は Isaac リーダーを指し、Isaac Teleop デバイスレジストリに対して解決されるもので、lerobot-teleoperate のシリアル so101_leader とは異なります。) --teleop.port が設定されている場合、プラグインの tick→ラジアン較正は --teleop.id から推定され、プラグインの第 3 位置引数として渡されます。これは HF_LEROBOT_CALIBRATION/teleoperators/so_leader/<id>.json にある LeRobot 形式の JSON ファイルであり、シリアル SO-101 リーダーが使用するのと同じファイルです (lerobot-calibrate --teleop.type=so101_leader --teleop.id=<id>)。このファイルが存在しない場合、スクリプトは警告を表示し、プラグインは組み込みのデフォルト値を使用します。すべてのフラグについては python -m examples.isaac_teleop_to_so101.teleoperate --help を実行してください。起動時のセーフティ契約は以下の通りです。デフォルトでは、ミラーリング開始時にアームが急激に動かないよう、フォロワーは --align_duration 秒かけてリーダーの最初の読み取り値にスローされます (--align=false でスキップ可能)。また、リーダーのストリームが古くなっている間、フォロワーは測定された姿勢で保持されます。

URDF の取得は、huggingface_hub(LeRobot の既存の依存関係)を使用して、パブリックの lerobot/robot-urdfs バケットに対して行われるため、ログインは不要です。キャッシュは HF_LEROBOT_HOME/robot-urdfs/so101/ 以下に保存されます。再ダウンロードを強制するには、このフォルダを削除してください。

次に、ヘッドセットで: グリップを握って保持してエンゲージし、コントローラーを動かしてアームを駆動し、ねじったり傾けたりしてリストの向きを調整し、トリガーを押してグリップを閉じます(比例動作 — 離すと開きます)。

データセットを記録する(テレオペレーションだけでなく)には、同じフォルダ内のrecord.pyを使用します。--teleop.type(xr_controller | so101_leader)に基づいてディスパッチされる点はteleoperate.pyと完全に同じであり、どちらのデバイスでもフォロワーを駆動できます。また、コマンドされた関節を LeRobot データセットに保存します(lerobot-recordスタイルの--dataset.*フラグ)。完全な CLI とキーボードによる記録ショートカットについては、モジュールのドキュメント文字列を参照してください。

重要なパイプラインのステップとオプション

Clutch はすでに絶対的なベースフレームの姿勢を生成するため、プロセッサ側は薄い絶対姿勢パスです。フレームのリマップも、デルタの累積も、EEReferenceAndDeltaステージもありません。

  • MapXRControllerActionToRobotAction は、デバイス出力から IK 入力契約へのステートレスなフレームごとのマッピングです。ベースフレーム絶対座標の位置を書き込み、絶対姿勢を rotvec ターゲットとしてエンコードし、クローズネスを反転してモーターグリップのターゲットとします。

    action["ee.x"], action["ee.y"], action["ee.z"] = ee_pose[:3]   # absolute, base frame [m]
    action["ee.wx"], action["ee.wy"], action["ee.wz"] = orient_rotvec  # orientation target (rotvec)
    action["ee.gripper_pos"] = (1 - closedness) * 100   # motor units; SO-101 calibrates 100 = open

    グリッパーの極性(100 = open, 0 = closed)はソース内のハードウェアキャリブレーションの慣例です。閉じるべきときに顎が開く場合は、ソース側で反転してください。

  • EEBoundsAndSafety は EE をワークスペース内にクランプし、フレームごとのジャンプをレート制限します。クラッチの no-teleport によりフレームは小さく保たれるため、max_ee_step_m は主に一時的なコントローラーのトラッキング不具合を検出します。z の下限は 0.0(テーブル面)であり、誤ったターゲットが EE をテーブルの下に押し下げることがないようにします。x/y は緩やかな [-1, 1] m のボックス内に保たれます。raise_on_jump=False を設定することで、制限超過のフレームは例外を送出するのではなく、クランプされ警告が発行されます。ループ途中でクラッシュするとアームが制御不能になるためです:

    EEBoundsAndSafety(
        end_effector_bounds={"min": [-1.0, -1.0, 0.0], "max": [1.0, 1.0, 1.0]},
        max_ee_step_m=0.10,
        raise_on_jump=False,
    )
  • InverseKinematicsEEToJoints(initial_guess_current_joints=False, orientation_weight=0.01) は、クローズドループの Placo IK を解算します。SO‑101 は 5 自由度のロボットアームであるため、IK は位置が主たる制約となります。小さな orientation_weight により、ee.w* に含まれる姿勢目標をソフトに追従させることで、手首が手の動きに追従します。一方、過小決定のロールは設計上、部分的にのみ追従します。GripperVelocityToJoint は存在しません。絶対的な ee.gripper_pos はそのまま gripper.pos に渡されます。initial_guess_current_joints=False を設定すると、各解算の初期値は計測された関節角度からではなく、前回の IK 解からウォームスタートされるため、関節の軌跡はフレーム間で連続性を保ちます。orientation_weight は実機で調整してください。値が高すぎると位置追従と競合し、低すぎると姿勢コマンドを無視してしまいます。

この例では、ループレベルでも安全性を制御しています。起動時のリセットスルー(デフォルトで有効 — アームの現在の位置を保持するには--reset_to_origin=falseを渡します)の後、クラッチがエンゲージされている間のみロボットにコマンドを送り、ディセージ中は測定された関節を再送信します。これにより、クラッチをリリースするとアームはその場で固定されます。

他のロボットへのパイプラインの適応について詳しくは、Processors for Robots and Teleoperatorsガイドを参照してください。

トラブルシューティング

  • ModuleNotFoundError: isaacteleop — アクティブな環境に isaacteleop パッケージがインストールされていません。このガイドの上部にあるインストールコマンドを再実行してください: uv pip install "isaacteleop[cloudxr,retargeters-lite]~=1.3.131"。
  • コントローラーが見つかりません — CloudXRランタイムが実行されていること、ファイアウォールのポートがホワイトリストに登録されていること、ヘッドセットが接続されていることを確認してください(CloudXRのセットアップとヘッドセットの接続およびIsaac Teleopのクイックスタートを参照)。
  • CloudXR の自動起動に失敗しました — ランタイムが起動タイムアウト内に起動しない場合、connect() は RuntimeError を送出します。~/.cloudxr/logs 以下のランチャーログを確認してください。一般的な原因として、EULA が未承認であること(python -m isaacteleop.cloudxr --accept-eula を対話モードで一度実行してください。自動起動は stdin でプロンプトを表示するため、ヘッドレス環境ではハングします)、またはランタイムが外部で既に起動していること(自動起動をスキップするには LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1 または auto_launch_cloudxr=False を設定してください)が考えられます。
  • アームが動かない — クラッチはデッドマン方式です。clutch_thresholdを超えてスクリュー/グリップを押し続けなければなりません。コントローラーのスクリューが弱く検出される場合は、閾値を下げてください。
  • モーションがずれているように感じられる — ヘッドセット/プレイスペースの向きを確認してください。コントローラーのストリームは、XRControllerConfig 上の base_T_anchor 変換によってロボットベース座標系に再基準化されます(デフォルト: 標準的な OpenXR → ロボット軸の慣例)。アンカー座標系が異なる場合は、これを調整してください。

詳細はこちら

NVIDIA Isaac Teleop ドキュメント(ドキュメントのホーム、GitHub):

Update on GitHub