LeRobot documentation

Isaac Teleop

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 управляет одной сессией TeleopSession с помощью различных устройств ввода: контроллеров XR (VR), отслеживания рук, отслеживания всего тела, перчаток Manus, педалей и других.

В LeRobot Isaac Teleop поставляется в виде автономного примера в examples/isaac_teleop_to_so101/. Каждое устройство ввода Isaac Teleop представляет собой собственный подкласс Teleoperator в пакете isaac_teleop примера, разделяя один жизненный цикл сессии (см. IsaacTeleopTeleoperator). Доступные на данный момент устройства — это XR-контроллер (XRController) и обратимо-управляемая рука-лидер SO-101 (SO101LeaderArm); перчатки Manus и отслеживание руки/всего тела являются естественными следующими устройствами. Это руководство сосредоточено на XR-контроллере; рука-лидер SO-101 описана в разделе Запуск примера.

В этом руководстве вы узнаете:

  • Как устройство Isaac Teleop управляет целевым положением конечного эффектора (EE) робота
  • Как clutch (сжатие/захват на контроллере 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 — полный дополнительный пакет retargeters не устанавливается из-за фиксированных версий dex-retargeting/nlopt, поэтому здесь он не является значением по умолчанию). На x86_64 можно дополнительно установить полный стек ретаргетеров:

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

Настройка CloudXR и подключение гарнитуры

Isaac Teleop транслирует изображение с гарнитуры на ваш компьютер через NVIDIA CloudXR, который предоставляет OpenXR-рантайм, к которому подключается сессия. По умолчанию LeTeleop автоматически запускает рантайм CloudXR при вызове teleop_device.connect() — вам больше не нужно запускать python -m isaacteleop.cloudxr и source cloudxr.env в отдельном окне терминала. Достаточно подключить поддерживаемую гарнитуру и открыть порты файрвола CloudXR. Подробности о сопряжении гарнитуры и настройке файрвола описаны в разделе Быстрый старт Isaac Teleop.

Первый запуск (EULA). При самом первом запуске необходимо принять EULA NVIDIA CloudXR. Автоматический запуск запрашивает его на stdin, поэтому на машине без графического интерфейса он будет зависать в ожидании ввода. Инициализируйте EULA один раз в интерактивном режиме с помощью:

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

После этого connect() запускает рантайм в неинтерактивном режиме. Запуск блокируется примерно на 30 секунд, пока рантайм не будет запущен.

Конфигурация. Два поля в IsaacTeleopConfig (общие для всех устройств) управляют этим:

  • auto_launch_cloudxr (по умолчанию True) — определяет, запускает ли connect() среду выполнения. Установите False, если CloudXR уже запущен извне.
  • cloudxr_env_file (по умолчанию None) — необязательный файл профиля устройства CloudXR .env, определяющий транспорт для гарнитуры (например, профиль для Apple Vision Pro). Это входной параметр лаунчера; это не файл выходных данных ~/.cloudxr/run/cloudxr.env, который, согласно старой ручной инструкции, нужно было source. Значение None сохраняет профиль auto-WebRTC по умолчанию — хотя в примере SO-101 он переопределяется в default.env, поставляемый рядом с teleoperate.py, если вы не передаёте --teleop.cloudxr_env_file.

Отказ от запуска. Чтобы пропустить автоматический запуск (если CloudXR уже запущен), установите auto_launch_cloudxr=False или экспортируйте:

export LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1

Переменная окружения имеет приоритет над полем конфигурации: если задано LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1, автоматический запуск пропускается, даже если auto_launch_cloudxr=True. Эта переменная не зависит от ISAACLAB_CXR_SKIP_AUTOLAUNCH в Isaac Lab — установка одной не влияет на другую.

Один телеоператор на процесс. Рантайм CloudXR настраивает окружение для всего процесса (синглтон), поэтому запускайте один телеоператор Isaac Teleop на процесс.

Остановка. Всегда вызывайте teleop_device.disconnect() при завершении — в том числе при Ctrl-C. Оберните цикл телеуправления в try/finally и вызовите disconnect() в блоке finally. Это завершает сессию OpenXR до рантайма CloudXR, что является обязательным порядком; хук atexit лаунчера лишь освобождает рантайм и не выполняет __exit__ сессии, поэтому без явного вызова disconnect() прерванный запуск завершится в неверном порядке.

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

См. Системные требования для списка поддерживаемых версий ОС / GPU / CloudXR и гарнитур.

Как это работает

Контроллер XR является одним из устройств ввода Isaac Teleop. XRController намеренно реализован как тонкий читатель: он предоставляет сырые данные о положении хвата контроллера — уже статически пересчитанные в систему координат базы робота — а также аналоговые значения сжатия и спуска. В нём нет ретаргеттеров и нет собственной логики сцепления. Сцепление (замок включения + дельта-пересчёт относительно EE) и отображение захвата находятся ниже по потоку в примере цикла, который затем передаёт данные в существующий замкнутый контур картезианского IK LeRobot — тот же, что использует оператор с телефоном. Специфичные для устройства части — это XRController, Clutch цикла и MapXRControllerActionToRobotAction; всё, что находится ниже по потоку (EEBoundsAndSafety, InverseKinematicsEEToJoints), является общим, и будущее устройство (например, перчатки Manus) заменит на свои собственные teleop_<device>.py + процессор, переиспользуя остальное.

XRController._build_pipeline подключает ControllersSource Isaac Teleop, который статически пересчитывается в базовую систему координат робота с помощью нативного ControllerTransform (base_T_anchor), и предоставляет преобразованный поток данных контроллера без изменений. get_action() считывает позу захвата, сжатие и спуск непосредственно из этого потока; сессия всегда выполняется в режиме RUNNING (нет переключателя для перенацеливания, который бы ограничивал доступ).

Класс Clutch (в файле examples/isaac_teleop_to_so101/isaac_teleop/clutch.py, управляемый циклом в common.py) повторяет логику SO101ClutchRetargeter из Isaac Teleop, но выполняется внутри цикла, чтобы устройство оставалось простым считывающим модулем:

  • Он фиксирует точку срабатывания на фронте сжатия engage edge (кадре, в котором сжатие впервые пересекает clutch_threshold), и пересчитывает относительно неё как положение, так и ориентацию, чтобы срабатывание не приводило к телепортации руки. Clutch.rebase возвращает абсолютную целевую точку в базовой системе координат в виде пары (pos, quat), которую цикл объединяет в 7-мерный ee_pose, передаваемый процессору.
  • Аналоговый триггер становится значением closedness в диапазоне [0, 1] (0 = открыто, 1 = закрыто), пропорциональным нажатию триггера, которое MapXRControllerActionToRobotAction преобразует в целевое положение челюстей.

См. интерфейс ретаргетинга и обзор архитектуры Isaac Teleop, чтобы узнать, как комбинируются исходные узлы и ретаргетеры.

  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 является чистым, безсостоятельным отображением для каждого кадра.

Сцепление фиксирует исходную точку включения на фронте срабатывания (момент, когда сжатие пересекает clutch_threshold) и управляет EE на основе движения, относительного к этой точке, чтобы рука не телепортировалась при включении. При каждом включении — как при старте, так и при повторном сцеплении в ходе задачи — базовое положение фиксируется по прямой кинематике на основе измеренных суставов руки, поэтому базовая точка совпадает с тем, где рука физически находится, даже если она двигалась в выключенном состоянии, и включение происходит без скачков. Базовая ориентация сохраняет последнюю заданную поворот: рука с 5 степенями свободы отслеживает ориентацию только мягко, поэтому фиксация измеренной ориентации запястья привнесла бы её смещение отслеживания в команду при каждом повторном сцеплении.

Управление

  • Сжатие / захват — тормоз (deadman). Удерживайте его выше clutch_threshold, чтобы включить телеуправление; отпустите, чтобы поставить на паузу. При каждом включении заново фиксируется исходная точка, поэтому во время паузы можно переместить руку и снова включить управление без рывка манипулятора (стиль index/clutch).
  • Спуск — захват, управляемый аналоговым сигналом. Пальцы следуют за спуском пропорционально — нажатие спуска наполовину приводит к закрытию пальцев наполовину — через значение закрытия в диапазоне [0, 1] (0 = открыто, 1 = закрыто), которое отображается в абсолютную целевую позицию сустава захвата.
  • Ориентация контроллера — это запястье. Механизм clutch пересчитывает ориентацию контроллера (относительно точки захвата, в базовой системе координат) в мягкую целевую ориентацию для 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] [м] и grip_quat (4,) [qx, qy, qz, qw] в базовой системе координат робота, а также скалярные аналоговые значения squeeze и trigger в диапазоне [0, 1]. В цикле примера Clutch преобразует их в абсолютную ee_pose, а значение squeeze сравнивается в цикле с clutch_threshold для активации сцепления.

Шаг 2: Подключение

Вызов teleop_device.connect() сначала автоматически запускает рантайм CloudXR (если вы не отключили эту опцию — см. Настройка CloudXR и подключение гарнитуры; этот процесс блокируется примерно на 30 секунд, а при первом запуске запрашивает согласие с EULA через stdin), затем запускает TeleopSession Isaac Teleop (открывает сессию OpenXR и обнаруживает контроллеры). Контроллеры XR калибруются автоматически, поэтому ручной этап калибровки не требуется — механизм clutch выполняет повторную центровку каждый раз при активации. Используйте connect() в паре с try/finally, в котором вызывается disconnect(), чтобы сессия корректно завершалась до выхода из рантайма при завершении работы или нажатии Ctrl-C.

Шаг 3: Запустите пример

В примере предполагается, что вы настроили робота (SO‑101 follower) и указали правильный последовательный порт.

При первом запуске URDF робота и его меши загружаются автоматически: XR-устройство скачивает URDF SO-101 из бакета Hugging Face lerobot/robot-urdfs в кэш 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 follower, а --teleop.type выбирает устройство ввода Isaac (xr_controller | so101_leader), при этом --teleop.* управляет параметрами этого устройства. --teleop.type=xr_controller запускает сценарий с XR-контроллером, описанный выше. Контракт безопасности при запуске: по умолчанию все суставы плавно переводятся в стандартную позу сброса в течение --reset_duration секунд (--reset_to_origin=false оставляет манипулятор в текущем положении), после чего начальное положение сцепки (clutch home) задаётся на основе измеренной позы манипулятора, чтобы первое включение сцепки происходило без рывков; команды на follower отправляются только при включённой сцепке.

Настройка позы сброса. Поза сброса поставляется как встроенное значение по умолчанию (удобная средняя поза) и работает сразу — вам не нужно ничего записывать. Чтобы адаптировать её под вашу конфигурацию, переведите манипулятор в нужную позу и выполните python -m examples.isaac_teleop_to_so101.override_reset_pose --id <robot.id>; команда записывает текущие значения суставов в файл для конкретного манипулятора в кэше LeRobot (HF_LEROBOT_HOME/reset_poses/<robot.name>/<robot.id>.json, с ключом, как при калибровке), который затем имеет приоритет над встроенным значением по умолчанию при следующем запуске. Поскольку файл находится в локальном кэше пользователя (а не в репозитории), ваше переопределение остаётся на вашем компьютере, и оба режима teleoperate и record учитывают его при запуске с тем же --robot.id.

Другое устройство, --teleop.type=so101_leader, зеркально повторяет движения исполнителя 1:1 с помощью заднего ведущего манипулятора SO-101, суставы которого передаются через нативный плагин so101_leader Isaac Teleop (без сцепления, без ИК — ведущий и исполнительный манипуляторы используют одну кинематику SO-101).

Бинарный файл so101_leader_plugin представляет собой плагин на C++, который не входит в состав пакета pip isaacteleop — его необходимо собрать из исходного кода 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, в отличие от последовательного so101_leader в lerobot-teleoperate.) Если задан --teleop.port, калибровка «тик → радиан» плагина выводится из --teleop.id и передается в плагин в качестве третьего позиционного аргумента — это JSON в формате LeRobot по пути HF_LEROBOT_CALIBRATION/teleoperators/so_leader/<id>.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 (флаги --dataset.* в стиле lerobot-record). Полный список CLI и горячие клавиши для записи с клавиатуры описаны в docstring модуля.

Важные этапы и параметры конвейера

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 рамками рабочей области и ограничивает скорость скачков на кадр. Функция clutch no‑teleport удерживает кадры небольшими, поэтому max_ee_step_m в основном ловит кратковременные сбои отслеживания контроллера. Нижний предел по z равен 0.0 (плоскость стола), чтобы случайная целевая точка не увела EE под стол; x/y остаются в широкой рамке [-1, 1] м. Установите 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) решает задачу замкнутого IK на базе Placo. Поскольку SO‑101 — это манипулятор с 5 степенями свободы, IK в основном ориентирован на позиционирование; небольшой вес orientation_weight позволяет мягко отслеживать целевую ориентацию, переданную в ee.w*, чтобы запястье следовало за рукой, при этом недоопределённый roll остаётся частичным по замыслу. Модуля 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, и процесс зависает в headless-режиме), либо среда выполнения уже запущена извне (установите LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1 или auto_launch_cloudxr=False, чтобы пропустить автоматический запуск).
  • Рука не двигается — сцепление работает как deadman: необходимо удерживать сжатие/захват выше clutch_threshold. Если сжатие вашего контроллера регистрируется слабо, уменьшите порог.
  • Движение кажется рассинхронизированным — проверьте ориентацию гарнитуры/игрового пространства. Поток данных с контроллера пересчитывается в систему координат базы робота с помощью преобразования base_T_anchor в XRControllerConfig (по умолчанию: стандартная конвенция OpenXR → конвенция осей робота); скорректируйте его, если ваша система координат якоря отличается.

Подробнее

Документация NVIDIA Isaac Teleop (главная страница документации, GitHub):

Update on GitHub