Главная/Блог/Гайд/NVIDIA IsaacTeleop: Глубокое погружение…
Гайд10 мин чтения · 4 октября 2026 г.

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга

Разбираем архитектуру NVIDIA IsaacTeleop: от типов данных до кастомных ретаргетеров. Полный гайд по созданию систем телеуправления роботами без шлема.

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга

В мире робототехки и физического искусственного интеллекта (Physical AI) разрыв между виртуальным управлением и реальным миром часто преодолевается с помощью технологий дополненной и виртуальной реальности (XR). Однако для разработчиков, работающих в условиях ограниченных ресурсов или специфических требований к безопасности, покупка дорогостоящего оборудования или настройка сложных сред симуляции не всегда является оптимальным решением. Здесь на сцену выходит NVIDIA IsaacTeleop — фреймворк, который позволяет преобразовывать данные отслеживания рук и контроллеров в команды для симулированных и реальных роботов. Но что скрывается под капотом этого инструмента? Как именно данные от датчиков превращаются в осмысленные действия робота?

В этой статье мы не просто посмотрим на документацию, а полностью разберем движок ретаргетинга (retargeting engine), лежащий в основе IsaacTeleop. Мы пойдем по пути разработчика: вместо подключения шлема VR мы будем генерировать синтетические данные с помощью NumPy, писать собственные алгоритмы обработки и собирать граф вычислений, который будет работать на обычном процессоре. Это позволит нам понять каждую деталь процесса — от строгой типизации данных до тонкой настройки параметров в реальном времени. Такой подход дает невероятную гибкость и понимание того, как строятся современные системы управления роботами.

010. Установка и проверка окружения

Прежде чем углубляться в архитектуру, необходимо подготовить среду. NVIDIA IsaacTeleop доступен через PyPI, и для наших целей достаточно установить стабильную версию колеса (wheel) с дополнительным пакетом retargeters-lite. Этот пакет добавляет только необходимые зависимости, такие как SciPy, не перегружая систему лишними модулями.

terminalpython
import subprocess
import sys

subprocess.run(
    [sys.executable, "-m", "pip", "install", "-q", "isaacteleop[retargeters-lite]==1.4.145"],
    check=True
)

После установки важно убедиться, что все компоненты загружены корректно. Мы можем проверить версию пакета, интерпретатора Python и библиотеки NumPy. Кроме того, полезно вывести список всех доступных модулей верхнего уровня и типы сообщений из схемы (schema). Это поможет нам понять, какие данные могут поступать от трекеров. Важно отметить: в этом руководстве не используется ни один шлем, ни среда симуляции. Все входные данные будут создаваться вручную, что делает процесс полностью воспроизводимым на любом CPU.

021. Типовая контрактация: TensorGroupType и TensorGroup

Основой взаимодействия в IsaacTeleop является строгая система типов. Движок оперирует понятиями TensorGroupType (упорядоченный список типизированных слотов) и TensorGroup (контейнер времени выполнения, хранящий одно значение для каждого слота). Эта архитектура обеспечивает безопасность данных и предсказуемость поведения узлов (nodes) в графе вычислений.

Рассмотрим два основных типа входных данных:

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга
  • HandInput: Содержит четыре массива NumPy для 26 суставов руки в порядке OpenXR. Это включает позиции, ориентации, радиусы и флаги валидности.
  • ControllerInput: Содержит четырнадцать слотов для поз, кнопок и осей. Доступ к ним осуществляется через сгенерированные перечисления (IntEnum), а не через «магические числа», что значительно упрощает чтение кода.

Ключевой особенностью системы является валидация при записи. Если вы попытаетесь записать массив типа float64 в слот, ожидающий float32, система выдаст ошибку TypeError непосредственно в момент записи. Аналогично, попытка прочитать слот, в который никто не записывал данные, вызовет исключение, а не вернет устаревшие или нулевые значения. Это предотвращает скрытые баги, связанные с неинициализированными данными.

Также существует тип OptionalType, который маркирует входные данные, которые трекер может не доставить. Соответствующий OptionalTensorGroup изначально находится в состоянии «отсутствует» (absent). Он становится «присутствующим» (present) только после первой записи. Это механизм, с помощью которого downstream-узлы узнают, что рука покинула зону отслеживания или контроллер был отключен.

032. Синтетические данные отслеживания: рука и контроллер в NumPy

Поскольку у нас нет физического шлема, нам нужно сгенерировать данные, которые имитируют работу трекера. Мы создадим две функции: make_hand и make_controller.

Синтез руки

Функция make_hand выстраивает 26 суставов в порядке OpenXR вокруг позиции запястья. Она создает четыре цепочки пальцев и одну цепочку большого пальца, отходящие от ладони. Ключевой параметр здесь — расстояние щипка (pinch distance) между кончиком большого пальца и указательного. Это число, которое мы контролируем, станет основой для последующих шагов ретаргетинга.

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга
terminalpython
import numpy as np
from isaacteleop.retargeting_engine.tensor_types import HandInput, HandInputIndex

def make_hand(pinch_m, wrist=np.array([0.30, 1.05, 0.45], dtype=np.float32)):
    """Синтетическая правая рука в формате HandInput: 26 суставов в порядке OpenXR."""
    pos = np.zeros(26, dtype=np.float32)
    pos[HandInputIndex.WRIST] = wrist
    pos[HandInputIndex.PALM] = wrist + np.array([0.0, 0.0, 0.06], dtype=np.float32)
    
    # Расположение плюсневых костей
    fingers = {
        HandInputIndex.INDEX_METACARPAL: 0.03,
        HandInputIndex.MIDDLE_METACARPAL: 0.01,
        HandInputIndex.RING_METACARPAL: 0.01,
        HandInputIndex.LITTLE_METACARPAL: 0.03
    }
    for base, x_off in fingers.items():
        for k in range(5):
            pos[base + k] = wrist + np.array([x_off, 0.0, 0.05 * k, 0.02], dtype=np.float32)
            
    # Большой палец
    pos[HandInputIndex.THUMB_METACARPAL] = wrist + np.array([0.05, 0.01, 0.02, 0.015], dtype=np.float32)
    pos[HandInputIndex.THUMB_TIP] = pos[HandInputIndex.INDEX_TIP] + np.array([0.0, 0.0, pinch_m], dtype=np.float32)
    
    # Создание TensorGroup
    g = HandInput()
    g[HandInputIndex.JOINT_POSITIONS] = pos
    g[HandInputIndex.JOINT_ORIENTATIONS] = np.tile(np.asarray([0.0, 0.0, 0.0, 1.0], dtype=np.float32), (26, 1))
    g[HandInputIndex.JOINT_RADII] = np.full(26, 0.01, dtype=np.float32)
    g[HandInputIndex.JOINT_VALID] = np.ones(26, dtype=np.uint8)
    return g

Синтез контроллера

Функция make_controller заполняет все слоты ControllerInput: позы захвата (grip) и прицеливания (aim) с флагами валидности, четыре кнопки, оси джойстика, а также аналоговые значения сжатия (squeeze) и спускового крючка (trigger). Оба объекта возвращают обычные TensorGroup, которые видит любой ретаргетер, независимо от того, пришли ли числа из OpenXR или были сгенерированы NumPy.

043. Написание ретаргетера: обнаружение щипка с настраиваемыми параметрами

Ретаргетер — это подкласс BaseRetargeter, который объявляет спецификации входных (input_spec) и выходных (output_spec) данных, а также реализует метод _compute_fn. Фреймворк берет на себя рутину: заполнение отсутствующих опциональных входных данных, валидацию типов, синхронизацию параметров и только затем вызывает наш код.

Давайте создадим простой, но показательный ретаргетер PinchRetargeter. Его задача — измерять расстояние между большим и указательным пальцами и выдавать результат: расстояние в сантиметрах и булево значение «щипок выполнен».

💡
Совет по архитектуре. Обратите внимание, как используются параметры. Они объявляются в __init__ с помощью FloatParameter и BoolParameter. Фреймворк автоматически создает ParameterState, который позволяет изменять эти значения «на лету» из пользовательского интерфейса или другого потока. Функция sync_fn гарантирует, что новое значение будет применено к экземпляру перед каждым вызовом _compute_fn.
terminalpython
from isaacteleop.retargeting_engine.interface import BaseRetargeter, ParameterState, FloatParameter, BoolParameter
from isaacteleop.retargeting_engine.tensor_types import FloatType, BoolType

class PinchRetargeter(BaseRetargeter):
    """Расстояние от большого пальца до указательного -> (distance_cm, is_pinching)."""
    def __init__(self, name, config_file=None):
        params = {
            "threshold_cm": FloatParameter("Pinching when closer than this", default_value=3.0, min_value=0.5, max_value=10.0),
            "use_distal_joints": BoolParameter("Measure between distal joints, not tips", default_value=False)
        }
        super().__init__(name, ParameterState(name, params), config_file)

    def input_spec(self):
        return {"hand_right": OptionalType(HandInput)}

    def output_spec(self):
        return TensorGroupType("pinch", FloatType("distance_cm"), BoolType("is_pinching"))

    def _compute_fn(self, inputs, outputs, context):
        hand = inputs["hand_right"]
        if hand.is_none:
            # Трекинг потерян: сообщаем об этом, не гадая
            outputs["pinch"]["distance_cm"] = -1.0
            outputs["pinch"]["is_pinching"] = False
            return

        pos = np.from_dlpack(hand[HandInputIndex.JOINT_POSITIONS])
        thumb_idx = HandInputIndex.THUMB_DISTAL if self.use_distal_joints else HandInputIndex.THUMB_TIP
        index_idx = HandInputIndex.INDEX_DISTAL if self.use_distal_joints else HandInputIndex.INDEX_TIP
        
        d_cm = float(np.linalg.norm(pos[thumb_idx] - pos[index_idx]) * 100.0)
        outputs["pinch"]["distance_cm"] = d_cm
        outputs["pinch"]["is_pinching"] = bool(d_cm < self.threshold_cm)

В этом коде есть важный нюанс: если рука не отслеживается (hand.is_none), мы возвращаем -1.0 для расстояния и False для щипка. Это явное сообщение о потере данных, а не попытка угадать состояние. Параметры threshold_cm и use_distal_joints можно менять в реальном времени, что позволяет тонко настраивать чувствительность системы без перезапуска.

054. Встроенные ретаргетеры: гистерезис и поза SE(3)

Помимо кастомных решений, IsaacTeleop предоставляет мощные встроенные ретаргетеры. Два из них особенно важны для управления манипуляторами: GripperRetargeter и Se3Retargeter.

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга

GripperRetargeter: Управление захватом

Этот ретаргетер преобразует расстояние щипка в команду для захвата робота (обычно -1 для закрытия, +1 для открытия, как ожидает Isaac Lab). Ключевая особенность — гистерезис. Захват закрывается, если расстояние меньше 3 см, и открывается, если оно больше 5 см. В промежутке между 3 и 5 см состояние сохраняется. Это предотвращает «дребезг» (chattering) команды, когда рука пользователя находится на границе чувствительности. Кроме того, если присутствует контроллер, он имеет приоритет над данными руки: нажатие триггера на контроллере может закрыть захват даже при открытой руке.

Se3AbsRetargeter и Se3RelRetargeter: Позиционирование

Se3AbsRetargeter отображает позу захвата контроллера на целевую позицию конца эффектора (end-effector). Он применяет конфигурационные смещения (например, поворот на 90 градусов по оси Roll) и может игнорировать вращение по осям X и Y, оставляя толькоYaw. Если данные о позах становятся невалидными, ретаргетер удерживает последнюю известную позу, вместо того чтобы передавать нулевую кватернионную ориентацию, что могло бы вызвать резкие скачки в симуляции.

Se3RelRetargeter работает иначе: он выдает дельты (изменения) позиции и ориентации. Это полезно для плавного перемещения объектов. Например, если контроллер движется на +2 см по оси X каждый кадр, ретаргетер масштабирует это значение (например, в 10 раз) и сглаживает с помощью экспоненциального скользящего среднего (EMA). Это обеспечивает плавность движений, устраняя дрожание рук оператора.

065. Сборка графа вычислений: от листьев к корню

Финальный шаг — объединение всех компонентов в единый граф. В IsaacTeleop граф состоит из узлов (nodes), соединенных ребрами. Листьевыми узлами являются источники данных (например, наши синтетические CountingInput), внутренними узлами — ретаргетеры, а корневым узлом — комбайнер выходных данных.

Мы создаем узлы CountingInput для контроллера и руки, чтобы отслеживать, сколько раз каждый узел был вызван. Затем мы создаем ретаргетеры Se3AbsRetargeter для позы эффектора и GripperRetargeter для захвата. С помощью метода connect мы связываем выходы источников с входами ретаргетеров.

NVIDIA IsaacTeleop: Глубокое погружение в движок ретаргетинга
terminalpython
from isaacteleop.retargeting_engine.interface import ValueInput, OutputCombiner
from isaacteleop.retargers import TensorReorderer

class CountingInput(ValueInput):
    """Лист ValueInput, который считает количество вызовов."""
    def __init__(self, name, tensor_type):
        super().__init__(name, tensor_type)
        self.calls = 0

    def _compute_fn(self, inputs, outputs, context):
        self.calls += 1
        super()._compute_fn(inputs, outputs, context)

def build_pipeline():
    controller = CountingInput("controller_right", OptionalType(ControllerInput))
    hand = CountingInput("hand_right", OptionalType(HandInput))
    
    ee_pose = Se3AbsRetargeter(Se3RetargeterConfig(input_device="controller_right"), name="ee_pose")
    ee_pose.connect("controller_right", controller.output("value"))
    
    gripper = GripperRetargeter(GripperRetargeterConfig(hand_side="right"), name="gripper")
    gripper.connect("controller_right", controller.output("value"))
    gripper.connect("hand_right", hand.output("value"))
    
    # Комбайнер собирает все выходы
    combiner = OutputCombiner([ee_pose, gripper])
    return combiner, controller, hand

Этот граф позволяет запускать вычисления пошагово. Мы можем подать синтетические данные, получить результат, изменить параметры и запустить снова. Система автоматически управляет состоянием, валидацией и синхронизацией, позволяя нам сосредоточиться на логике преобразования данных.

076. Маппинг контроллера на дexterous hand и сохранение параметров

Одним из самых мощных аспектов IsaacTeleop является возможность сложного маппинга. Например, мы можем сопоставить простой контроллер с сложной многопальцевой рукой (dexterous hand). Это требует создания кастомного ретаргетера, который интерпретирует нажатия кнопок и оси джойстика как команды для отдельных суставов или групп суставов робота.

Параметры, такие как пороги срабатывания, коэффициенты сглаживания и смещения, могут быть сохранены в конфигурационных файлах. Это позволяет разработчику настроить систему один раз, а затем загружать эти настройки при каждом запуске, обеспечивая воспроизводимость экспериментов. Кроме того, благодаря архитектуре ParameterState, эти параметры могут быть изменены в реальном времени через внешний интерфейс, что делает отладку и тонкую настройку интуитивно понятными.

087. Практические примеры и отладка

При работе с такими системами важно понимать, как отлаживать граф. Поскольку каждый узел может быть вызван независимо, полезно использовать узлы-счетчики, как CountingInput, чтобы убедиться, что данные проходят через все预期的 пути. Если узел не вызывается, возможно, есть проблема с подключением или условием валидности.

Также стоит обратить внимание на обработку ошибок. Если трекер теряет руку, OptionalTensorGroup становится невалидным, и ретаргетеры должны корректно обрабатывать это состояние. В нашем примере PinchRetargeter возвращает -1, а Se3AbsRetargeter удерживает последнюю позу. Это предотвращает резкие скачки в симуляции, которые могли бы привести к «смерти» робота или неестественным движениям.

09Что это значит на практике

Разбор архитектуры NVIDIA IsaacTeleop показывает, что создание систем телеуправления роботами — это не просто подключение шлема к симулятору. Это сложный процесс проектирования графов вычислений, где каждый узел отвечает за конкретную трансформацию данных. Использование NumPy для генерации синтетических данных позволяет разработчикам тестировать и отлаживать свои алгоритмы на любом компьютере, без дорогостоящего оборудования.

Строгая типизация и система валидации обеспечивают надежность кода, предотвращая скрытые ошибки. Возможность настраивать параметры в реальном времени делает систему гибкой и адаптивной. А наличие встроенных ретаргетеров для захвата и позиционирования ускоряет разработку, позволяя сосредоточиться на специфических задачах, таких как маппинг на сложные руки.

Для российских разработчиков и исследователей этот подход особенно актуален. Он позволяет создавать сложные системы управления роботами, используя открытые инструменты и локальные вычислительные ресурсы, без зависимости от облачных сервисов или специфического оборудования. IsaacTeleop предоставляет мощный фундамент для развития Physical AI, делая передовые технологии доступными для широкого круга разработчиков.

Источник: MarkTechPost ↗