LeRobot documentation
Isaac Teleop
Isaac Teleop
Controla tu robot con NVIDIA Isaac Teleop, un
framework de teleoperación multimodal. Isaac Teleop controla una única TeleopSession desde una
variedad de dispositivos de entrada: controladores XR (VR), seguimiento de manos, seguimiento corporal
completo, guantes Manus, pedales de pie y más.
En LeRobot, Isaac Teleop se incluye como un ejemplo autónomo en examples/isaac_teleop_to_so101/.
Cada dispositivo de entrada de Isaac Teleop es su propio subclase de Teleoperator en el paquete isaac_teleop del ejemplo, compartiendo un mismo ciclo de vida de sesión (ver IsaacTeleopTeleoperator). Los
dispositivos disponibles hoy son el controlador XR (XRController) y un brazo líder SO-101 con retroalimentación de fuerza (SO101LeaderArm); los guantes Manus y el seguimiento de manos/cuerpo completo son los
próximos dispositivos naturales. Esta guía se centra en el controlador XR; el brazo líder SO-101 se resume
en Ejecutar el ejemplo.
En esta guía aprenderás:
- Cómo un dispositivo de Teleop de Isaac controla el objetivo del efector final (EE) de un robot
- Cómo el clutch (apretar/agarrar en el controlador XR) activa la teleoperación sin dar tirones al brazo
- Cómo ejecutar el ejemplo de teleoperación SO‑101 y ajustar el movimiento / la pinza / la IK
Instalación
El ejemplo se encuentra en el repositorio de LeRobot (no forma parte del paquete pip lerobot), por lo que debes clonar el repositorio e instalarlo desde el código fuente. La referencia de instalación y uso canónica y siempre actualizada es el README.md del ejemplo; en resumen:
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 se publica en PyPI público (solo Linux). El extra cloudxr incluye las vinculaciones del runtime de CloudXR; retargeters-lite es la ruta del retargeter basado en scipy que se resuelve tanto en x86_64 como en ARM (en aarch64, por ejemplo, un DGX Spark, el extra completo retargeters no se resuelve debido a sus fijaciones de dex-retargeting/nlopt, por lo que no es el predeterminado aquí). En x86_64, además, puedes instalar la pila completa de retargeter:
uv pip install "isaacteleop[retargeters]~=1.3.131"Configurar CloudXR y conectar un visor
Isaac Teleop transmite el visor a tu máquina a través de NVIDIA CloudXR, que proporciona el runtime de OpenXR al que se conecta la sesión. De forma predeterminada, LeTeleop inicia automáticamente el runtime de CloudXR cuando llamas a teleop_device.connect(); ya no es necesario ejecutar python -m isaacteleop.cloudxr y source cloudxr.env en una shell separada. Solo necesitas un visor compatible conectado y los puertos de firewall de CloudXR abiertos. Sigue el Quick Start de Isaac Teleop para los detalles de emparejamiento del visor y de firewall.
Primera ejecución (EULA). La primera vez que se inicie, debe aceptar la EULA de NVIDIA CloudXR. El inicio automático solicita la aceptación en stdin, por lo que en un equipo sin interfaz gráfica se quedará bloqueado esperando la entrada. Inicialice la EULA una sola vez, de forma interactiva, con:
python -m isaacteleop.cloudxr --accept-eula # one-time: accept the CloudXR EULADespués de eso, connect() inicia el runtime de forma no interactiva. La ejecución se bloquea durante ~30s mientras el runtime se inicia.
Configuración. Dos campos de IsaacTeleopConfig (compartidos por cada dispositivo) controlan esto:
auto_launch_cloudxr(por defectoTrue) — siconnect()inicia el runtime. EstablezcaFalsecuando CloudXR ya esté en ejecución externamente.cloudxr_env_file(valor predeterminadoNone) — un perfil de dispositivo CloudXR opcional.envque selecciona el transporte del visor (por ejemplo, un perfil de Apple Vision Pro). Esta es la entrada del lanzador; no es el archivo de salida~/.cloudxr/run/cloudxr.envque el flujo manual antiguo indicabasourcear.Nonemantiene el perfil auto-WebRTC predeterminado, aunque el ejemplo SO-101 lo sobrescribe con eldefault.envque se incluye junto ateleoperate.py, a menos que pases--teleop.cloudxr_env_file.
Excluirse. Para omitir el inicio automático (si CloudXR ya está en ejecución), establezca auto_launch_cloudxr=False o exporte:
export LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1La variable de entorno tiene prioridad sobre el campo de configuración: si se establece LEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1, se omite el inicio automático incluso cuando auto_launch_cloudxr=True. Esta variable es independiente de ISAACLAB_CXR_SKIP_AUTOLAUNCH de Isaac Lab: establecer una no afecta a la otra.
Un teleoperador por proceso. El runtime de CloudXR configura el entorno a nivel de proceso (un singleton), por lo que debe ejecutar un único teleoperador de Isaac Teleop por proceso.
Apagando. Llame siempre a teleop_device.disconnect() al salir, incluso en caso de Ctrl-C. Envuelva su bucle de teleoperación en try/finally y llame a disconnect() en el finally. Esto desmonta la sesión de OpenXR antes que el runtime de CloudXR, que es el orden requerido; el hook de atexit del launcher solo recoge el runtime y no ejecuta el __exit__ de la sesión, por lo que, sin una llamada explícita a disconnect(), una ejecución interrumpida se apaga en el orden incorrecto.
teleop_device.connect()
try:
while True:
action = teleop_device.get_action()
# ... drive the robot ...
finally:
teleop_device.disconnect()Consulte Requisitos del sistema para conocer las versiones compatibles de SO / GPU / CloudXR y los visores.
Cómo funciona
El controlador XR es un dispositivo de entrada de Isaac Teleop. XRController es un lector deliberadamente delgado: expone la pose de agarre cruda del controlador, que ya se ha rebaseado estáticamente al marco base del robot, junto con los valores analógicos de squeeze y trigger. No tiene retargeters ni lógica de clutch propia. El clutch (enganche + rebaseado de delta sobre el EE) y el mapeo del agarre residen aguas abajo en el bucle de ejemplo, que luego alimenta el pipeline de IK cartesiano en lazo cerrado existente de LeRobot, el mismo que utiliza el teleoperador con el teléfono. Las partes específicas del dispositivo son XRController, el Clutch del bucle y MapXRControllerActionToRobotAction; todo lo que está aguas abajo (EEBoundsAndSafety, InverseKinematicsEEToJoints) es compartido, y un dispositivo futuro (p. ej. guantes Manus) sustituiría su propio teleop_<device>.py + procesador reutilizando el resto.
XRController._build_pipeline conecta el ControllersSource de Isaac Teleop, que se rebasea estáticamente al marco base del robot mediante el ControllerTransform nativo (base_T_anchor), y expone el flujo de controlador transformado tal cual. get_action() lee la pose del agarre, el apretón y el gatillo directamente de él; la sesión siempre se avanza en RUNNING (no hay ningún retargeter de embrague que lo controle).
La clase Clutch (en examples/isaac_teleop_to_so101/isaac_teleop/clutch.py, controlada por el
bucle en common.py) replica el SO101ClutchRetargeter de Isaac Teleop, pero se ejecuta dentro del bucle para que
el dispositivo pueda mantenerse como un lector ligero:
- Fija su origen de activación en el borde de activación del apretón (el fotograma en el que el apretón cruza por primera vez
clutch_threshold) y rebasea tanto la posición como la orientación en torno a él, de modo que la activación no teletransporte el brazo.Clutch.rebasedevuelve el objetivo absoluto del marco base como un par(pos, quat), que el bucle concatena en elee_posede 7D que se alimenta al procesador. - El disparador analógico se convierte en un
closednessde la pinza en[0, 1](0 = abierto, 1 = cerrado), proporcional a la presión del disparador, queMapXRControllerActionToRobotActionmapea a un objetivo de mandíbula.
Consulte la interfaz de retargeting y el resumen de la arquitectura de Isaac Teleop para ver cómo se componen los nodos de origen y los retargeters.
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 targetsEl embrague: propiedad del bucle de ejemplo
A diferencia de la tubería de teléfono (que divide el clutch entre MapPhoneActionToRobotAction y EEReferenceAndDelta), el clutch de XR reside por completo en la clase Clutch del bucle de ejemplo. Emite una pose EE absoluta, por lo que no hay una etapa EEReferenceAndDelta ni acumulación de deltas en el procesador: MapXRControllerActionToRobotAction es un mapeo puro y sin estado por cuadro.
El embrague fija su origen de acoplamiento en el borde de acoplamiento de la presión (el momento en que la presión cruza clutch_threshold) y controla el EE desde el movimiento relativo a ese origen, de modo que el brazo no se teletransporta al acoplarse. En cada acoplamiento —ya sea al inicio o un re-acoplamiento a mitad de la tarea—, la posición de origen se fija a partir de la cinemática directa sobre las articulaciones medidas del brazo, por lo que el origen coincide con la posición física real del brazo incluso si se movió mientras estaba desacoplado, y el acoplamiento es libre de saltos. La orientación de origen conserva la última rotación comandada: el brazo de 5 GDL solo sigue la orientación de forma suave, por lo que fijar la orientación medida de la muñeca inyectaría su desfase de seguimiento en el comando en cada re-acoplamiento.
Controles
- Apriete / agarre — el clutch (deadman). Manténgalo presionado más allá de
clutch_thresholdpara activar la teleoperación; suéltelo para pausar. Cada activación vuelve a capturar el origen, por lo que puede reposicionar su mano mientras está en pausa y volver a activar sin que el brazo salte (estilo índice/clutch). - Gatillo — el agarre, controlado de forma analógica. La mandíbula sigue el gatillo de manera proporcional: un gatillo a medio pulsar deja la mandíbula a medio cerrar — mediante un valor de cierre en
[0, 1](0 = abierto, 1 = cerrado) que se mapea a un objetivo absoluto de la articulación del agarre. - Orientación del controlador — la muñeca. El clutch rebasea la orientación del controlador
(engage‑relative, base‑frame) en un objetivo de orientación suave de IK que la muñeca sigue junto con
la posición. En el 5‑DOF SO‑101, la muñeca sigue la mano solo parcialmente por diseño — consulte
orientation_weighta continuación.
Comenzar
Paso 1: Crear el teleoperador
# 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() devuelve la pose del controlador en el marco base sin procesar, no un objetivo rebaseado con clutch: grip_pos (3,) [x, y, z] [m] y grip_quat (4,) [qx, qy, qz, qw] en el marco base del robot, además de los valores analógicos escalares squeeze y trigger en [0, 1]. El Clutch del bucle de ejemplo convierte estos valores en la ee_pose absoluta, y el bucle aplica un umbral al squeeze en comparación con clutch_threshold para activarlo.
Paso 2: Conectar
Al llamar a teleop_device.connect() primero se inicia automáticamente el runtime de CloudXR (a menos que hayas optado por no hacerlo; consulta Configurar CloudXR y conectar un visor; esto bloquea durante ~30 s y, en la primera ejecución, solicita la EULA en stdin), y luego se inicia la TeleopSession de Isaac Teleop (abre la sesión de OpenXR y detecta los controladores). Los controladores de XR se autocalibran, por lo que no hay un paso de calibración manual: el clutch se encarga de recentrar cada vez que lo activas. Combina connect() con un try/finally que llame a disconnect() para que la sesión se cierre antes de que el runtime termine al salir o con Ctrl-C.
Paso 3: Ejecutar el ejemplo
El ejemplo asume que configuraste tu robot (SO‑101 follower) y estableciste el puerto serie correcto.
El URDF del robot y sus mallas se obtienen automáticamente en la primera ejecución: el dispositivo XR descarga
el URDF del SO-101 desde el bucket de Hugging Face lerobot/robot-urdfs en la caché de LeRobot (HF_LEROBOT_HOME/robot-urdfs/so101/) y lo reutiliza después, por lo que no hay
ningún paso de descarga separado:
python -m examples.isaac_teleop_to_so101.teleoperate --robot.type=so101_follower --robot.port=/dev/ttyACM0 \
--robot.id=so101_follower_arm --teleop.type=xr_controllerLa CLI es de estilo lerobot-teleoperate (draccus): --robot.* configura el seguidor SO-101 y --teleop.type selecciona el dispositivo de entrada de Isaac (xr_controller | so101_leader), con --teleop.* como sus ajustes de dispositivo. --teleop.type=xr_controller ejecuta la ruta del controlador XR descrita
anteriormente. El contrato de seguridad de inicio: de forma predeterminada, mueve todos los articulaciones a una pose de reinicio predeterminada durante --reset_duration segundos (--reset_to_origin=false mantiene el brazo en su posición actual), y luego establece la posición inicial del embrague a partir de la pose medida del brazo para que el primer acoplamiento sea sin saltos; el seguidor solo se
comanda mientras el embrague está acoplado.
Personalización de la pose de reinicio. La pose de reinicio se incluye como valor predeterminado integrado (una pose cómoda de rango medio) y funciona sin configuración adicional: no es necesario grabar nada. Para adaptarla a tu configuración, mueve el brazo a la pose deseada y ejecuta python -m examples.isaac_teleop_to_so101.override_reset_pose --id <robot.id>; esto escribe las articulaciones actuales en un archivo por brazo en la caché de LeRobot
(HF_LEROBOT_HOME/reset_poses/<robot.name>/<robot.id>.json, con la misma clave que la calibración), que luego tiene prioridad sobre el valor predeterminado integrado en la siguiente ejecución. Dado que se almacena en la caché local del usuario (no en el repositorio), tu personalización permanece en tu máquina, y tanto teleoperate como record la respetan cuando se lanzan con el mismo --robot.id.
El otro dispositivo, --teleop.type=so101_leader, replica al seguidor 1:1 desde un brazo líder SO-101 retroaccionable cuyos articulaciones se transmiten mediante el plugin nativo so101_leader de Isaac Teleop (sin embrague, sin IK: el líder y el seguidor comparten la cinemática del SO-101).
El binario so101_leader_plugin es un plugin de C++ que no forma parte del paquete pip isaacteleop: se compila desde el árbol de fuentes de Isaac Teleop. Siga las instrucciones de Compilar Isaac Teleop desde el código fuente (en resumen, desde su clon de Isaac Teleop: cmake -B build && cmake --build build --parallel && cmake --install build); la compilación instala los plugins en <IsaacTeleop>/install/plugins/, por lo que el binario se encuentra en install/plugins/so101_leader/so101_leader_plugin, que es la ruta de --launch_plugin indicada a continuación. Consulte el propio README.md del plugin (junto al binario) para conocer los detalles de su serie/calibración.
Apunta --teleop.port al puerto serie del líder físico y --launch_plugin al binario de ese plugin para que el script lo inicie después de que CloudXR esté en marcha:
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(Nota: so101_leader aquí es el líder de Isaac, resuelto contra el registro de dispositivos de teleoperación de Isaac, distinto del serial so101_leader de lerobot-teleoperate.) Cuando se establece un --teleop.port, la calibración de tick a radianes del plugin se infiere a partir de --teleop.id y se pasa al plugin como su tercer argumento posicional: el JSON en formato LeRobot ubicado en HF_LEROBOT_CALIBRATION/teleoperators/so_leader/<id>.json, el mismo archivo que utiliza el líder SO-101 por serial (lerobot-calibrate --teleop.type=so101_leader --teleop.id=<id>). Si falta, el script emite una advertencia y el plugin utiliza valores predeterminados integrados. Ejecute python -m examples.isaac_teleop_to_so101.teleoperate --help para ver todas las banderas. Su contrato de seguridad de inicio: de forma predeterminada, el seguidor se desplaza hacia la primera lectura del líder a lo largo de --align_duration segundos (--align=false para omitirlo) para que el brazo no se mueva bruscamente cuando comienza el espejo, y mientras el flujo del líder está desactualizado, el seguidor se mantiene en su pose medida.
La descarga del URDF utiliza huggingface_hub (ya una dependencia de LeRobot) contra el bucket público lerobot/robot-urdfs, por lo que no requiere inicio de sesión. Se almacena en caché en HF_LEROBOT_HOME/robot-urdfs/so101/; elimina esa carpeta para forzar una nueva descarga.
Luego, en su visor: presione y mantenga el agarre para activarlo, mueva el controlador para dirigir el brazo, gírelo/inclínelo para orientar la muñeca y presione el gatillo para cerrar el actuador (de forma proporcional: suéltelo para abrirlo).
Para grabar un conjunto de datos (no solo teleoperar), use record.py en la misma carpeta. Se despacha según --teleop.type (xr_controller | so101_leader) exactamente como teleoperate.py, por lo que cualquier dispositivo puede controlar al seguidor, y guarda las articulaciones comandadas en un conjunto de datos de LeRobot (banderas --dataset.* de estilo lerobot-record). Consulte la docstring de su módulo para la CLI completa y los atajos de teclado para la grabación.
Pasos y opciones importantes del pipeline
El clutch ya produce una pose absoluta del marco base, por lo que el lado del procesador es un camino de pose absoluta delgado: no hay remapeo de marcos, no hay acumulación de deltas y no hay una etapa EEReferenceAndDelta.
MapXRControllerActionToRobotActiones un mapeo sin estado por cuadro desde la salida del dispositivo al contrato de entrada de IK. Escribe la posición absoluta en el marco base, codifica la orientación absoluta como un objetivo rotvec e invierte el cierre para obtener un objetivo de pinza de motor: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 = openLa polaridad de la pinza (
100 = open, 0 = closed) es una convención de calibración de hardware en la fuente; invértala allí si la mandíbula se abre cuando debería cerrarse.EEBoundsAndSafetylimita el EE a un espacio de trabajo y aplica un límite de velocidad a los saltos por cuadro. El embrague sin teletransporte mantiene los cuadros pequeños, por lo quemax_ee_step_mdetecta principalmente fallos transitorios de seguimiento del controlador. El límite inferior de z es0.0(el plano de la mesa) para que un objetivo errático no lleve el EE por debajo de la mesa; x/y se mantienen en el suelto recuadro de[-1, 1]m. Estableceraise_on_jump=Falsepara que un cuadro que supere el límite se recorte y se emita una advertencia en lugar de lanzar una excepción: un error en medio del bucle dejaría el brazo sin control: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)resuelve la IK de Placo en bucle cerrado. SO‑101 es un brazo de 5 GDL, por lo que la IK está dominada por la posición; el pequeñoorientation_weightle permite seguir suavemente el objetivo de orientación contenido enee.w*para que la muñeca siga a la mano, mientras que el giro indeterminado permanece parcial por diseño. No existe ningúnGripperVelocityToJoint: la posición absolutaee.gripper_posse pasa directamente agripper.pos.initial_guess_current_joints=Falseinicia cada resolución desde la solución IK anterior en lugar de reiniciar desde las articulaciones medidas, de modo que la trayectoria de las articulaciones se mantiene continua entre fotogramas. Ajustaorientation_weighten el hardware: un valor demasiado alto compite con el seguimiento de posición, y uno demasiado bajo ignora la orden de orientación.
El ejemplo también aplica un control de seguridad a nivel de bucle: después de la rampa de reinicio al inicio (activada por defecto; pase --reset_to_origin=false para mantener el brazo en su posición), envía comandos al robot solo mientras el embrague esté acoplado y reenvía las articulaciones medidas mientras está desacoplado, de modo que al soltar el embrague el brazo se queda congelado en su posición.
Consulte la guía Procesadores para robots y teleoperadores para obtener más información sobre cómo adaptar la pipeline a otros robots.
Solución de problemas
ModuleNotFoundError: isaacteleop— el paqueteisaacteleopno está instalado en el entorno activo. Vuelve a ejecutar el comando de instalación al inicio de esta guía:uv pip install "isaacteleop[cloudxr,retargeters-lite]~=1.3.131".- No se encontraron controladores — asegúrese de que el runtime de CloudXR esté en ejecución, de que los puertos del firewall estén en la lista blanca y de que el visor esté conectado (consulte Configurar CloudXR y conectar un visor y el Quick Start de Isaac Teleop).
- Fallo al iniciar CloudXR automáticamente —
connect()lanza unRuntimeErrorsi el runtime no se inicia dentro del tiempo de espera de arranque. Comprueba los registros del lanzador en~/.cloudxr/logs. Causas comunes: el EULA nunca se aceptó (ejecutapython -m isaacteleop.cloudxr --accept-eulauna vez, de forma interactiva: el inicio automático solicita confirmación en stdin y se queda bloqueado en modo headless), o el runtime ya está en ejecución externamente (estableceLEROBOT_CLOUDXR_SKIP_AUTOLAUNCH=1oauto_launch_cloudxr=Falsepara omitir el inicio automático). - El brazo no se mueve — el embrague es de tipo *deadman*: debes mantener la presión de apretar/agarrar por encima de
clutch_threshold. Reduce el umbral si la presión de tu controlador se registra con poca intensidad. - El movimiento parece desalineado — confirma la orientación del visor/espacio de juego. El flujo del controlador se rebasea en el marco de referencia de la base del robot mediante la transformación
base_T_anchorenXRControllerConfig(predeterminado: convención estándar OpenXR → ejes del robot); ajústala si tu marco de anclaje es diferente.
Más información
Documentación de NVIDIA Isaac Teleop (inicio de la documentación, GitHub):
- Inicio rápido — instala, ejecuta el servidor de CloudXR, conecta un visor y ejecuta un ejemplo de teleoperación.
- TeleopSession —
la API de sesión que envuelve
XRController. - Interfaz de retargeting y visión general de la arquitectura: cómo los nodos de origen y los retargeters se componen en un pipeline.
- Compilar desde el código fuente —
compila
isaacteleop(y sus complementos de C++, incluido el complementoso101_leaderutilizado anteriormente) a partir de una copia local. - Requisitos del sistema y la documentación del SDK de CloudXR: plataformas compatibles, GPUs, versiones del runtime de CloudXR/OpenXR y visores.