Isaac SDK это современный фреймворк для разработки систем управления роботов, ориентированный на машинное обучение. Isaac SDK появился в начале 2019г. и уже имеет несколько релизов. Разрабатывается фреймворк компанией NVIDIA для своей встраиваемой платформы Jetson и компьютеров с GPU NVIDIA на борту. На другом железе Isaac SDK не поддерживается. Пользуясь тем, что никто еще тут про него не написал, попробую сделать это сам, раз уж имею какой-никакой опыт работы с ним.

Кроме того, совсем недавно вышла новая версия 2020.2. В которой появилось много нового. Примеры будут именно для этой версии.

Isaac SDK является аналогом ROS, и очень на него похож, однако, обладает существенными отличиями в реализации и архитектуре. Об этих отличиях чуть позже.

Содержание

  1. Архитектура

  2. Установка

  3. Сборка и запуск приложения

  4. Создание своего приложения

  5. WebSight

  6. PoseTree

    Заключение

    Источники

1. Архитектура

Архитектура системы управления в Isaac SDK представляет собой граф узлов (nodes) и их соединений (channels), как и в ROS. Однако, узлы не являются отдельными независимыми процессами, которые в ROS общаются посредством TCP/IP(за исключением nodelet). В Isaac SDK весь граф собирается в одно приложение, которое все сразу собирается и все сразу запускается. Передача данных по сети, при необходимости, делается отдельно. При этом, система получается априори децентрализованной.

Для тех, кто не знаком с ROS

Для тех, кто не знаком с ROS, поясню, что узлами(nodes) называются программы, которые пишутся вами, другими пользователями, или уже есть готовые. Каждая из этих программ решает одну конкретную задачу. Например, детектирование объектов на изображении делает один узел, а предварительную обработку изображения (фильтрация, ректификация и т.п.) делает другой узел. Получением видеопотока с камеры занимается третий узел и т.д.

Передача информации между этими узлами происходит с помощью каналов или топиков, информацию в которые узлы могут публиковать и на которую могут подписываться. Иными словами, используется модель Издатель-Подписчик (Publisher-Subscriber).

Программные инструменты для написания узлов и коммуникации между ними предоставляет ROS. Аналогично работает и Isaac SDK.

Для пользователя, разрабатывающего приложения, Isaac SDK предоставляет множество инструментов и готовых решений:

Взято из developer.nvidia.com
Взято из developer.nvidia.com

Isaac SDK включает в себя поддержку CUDA, TensorRT, OpenCV, Eigen, Gstreamer, различные библиотеки для выполнения вычислений на графическом ускорителе и прочее. И в процессе установки он их все скачает :)

Для ряда задач робототехники и машинного обучения уже есть готовые GEMs (high-performance algorithms), причем среди них есть и достаточно вкусные алгоритмы. Например, AprilTags и ORB дескриптор с GPU ускорением. Включены примеры для работы с моделями по детектированию объектов, людей, области проходимости и тд. Есть инструменты для планирования маршрутов и навигации роботов на 2d карте для колесных и шагающих роботов.

В дополнение к Isaac SDK, поставляется ПО IsaacSim, которое позволяет использовать симуляторы для роботов. Поддерживаются хорошо известный Unity, и новинка от NVIDIA - Omniverse IsaacSim. Мне не приходилось, пока что, с ними работать, поэтому в данном руководстве работа с симуляторами рассматриваться не будет.

2. Установка

Фреймворк для работы требует видеокарту NVIDIA c драйвером 440 (рекомендовано). CUDA устанавливать рекомендуется, однако bazel при первой же сборке скачает и будет использовать свою версию NVCC для сборки CUDA кода.

Для SDK 2020.2 на Jetson должен быть установлен JetPack 4.4.1, а для версии 2020.1: JetPack 4.3. Это необходимо для корректной кросс-компиляции на Jetson.

Весь фреймворк скачивается с официального сайта одной папкой, которую вы располагаете в удобном вам месте на рабочей машине. Например, в ~/isaac . Далее вам нужно установить зависимости на компьютере, за которым вы будете работать (далее будем называть этот компьютер рабочим компьютером) :

bob@desktop:~/isaac/engine/$ engine/build/scripts/install_dependencies.sh
Важно

Данный скрипт ни на одной моей Ubuntu 18.04 не сработал сразу без ошибок. Ни в версии 2020.1, ни в новой 2020.2 . К сожалению, вам придется в ручную смотреть что не смог установить скрипт и устанавливать это в ручную. Видимо, стоит его рассматривать только как пример, а не готовый к использованию инструмент.

Установка на Jetson, он же бортовой вычислитель робота (он находится с рабочим компьютером в локальной сети), делается следующей командой:

bob@desktop:~/isaac/engine/$ engine/build/scripts/install_dependencies_jetson.sh -u <jetson_username> -h <jetson_ip>
для более поздних версий

Заметим, для версии 2020.1 и более поздних скрипты располагаются немного в другом месте:

bob@desktop:~/isaac/$

На этом установка закончится.

Как можно заметить, на бортовой вычислитель робота (в д.с. это Jetson TX2, Nano, Xavier NX или любой x86_64 компьютер c NVIDIA GPU) исходники скачивать не нужно. Предполагается, что все ПО собирается через кросс-компиляцию и закачивается на робот по сети. Это очень удобно и настроено из коробки. Подробнее об установке можно найти в официальной документации.

3. Сборка приложения

Isaac SDK использует систему сборки bazel и все приложения, пакеты, компоненты собираются вместе с исходниками всего SDK. При этом, многие зависимости будут скачиваться при первой сборке, поэтому первая сборка будет не быстрой.

Сборка для рабочего компьютера происходит следующей командой (для примера использовано стандартное приложение ping_pong):

bob@desktop:~/isaac/sdk/$ bazel build //apps/tutorials/ping_pong:ping_pong

Запуск приложения на рабочей машине:

bob@desktop:~/isaac/sdk/$ bazel run //apps/tutorials/ping_pong:ping_pong

Во время такого запуска, если это необходимо, происходит и сборка.

Cборка для бортового вычислителя робота удобным образом происходит с помощью специального скрипта. Этот скрипт собирает приложение на рабочем компьютере(кросс компиляция), и заливает готовое к запуску приложение по ssh на бортовой вычислитель робота. Предварительно, официальная документация рекомендует настроить доступ по ssh без пароля:

bob@desktop:~/isaac/sdk/$ ssh-copy-id <username_on_robot>@<robot_ip>

Тогда использование скрипта сборки выглядит следующим образом:

bob@desktop:~/isaac/sdk/$ ./../engine/build/deploy.sh --remote_user <username_on_robot> -p //apps/tutorials/ping_pong:ping_pong-pkg -d jetpack43 -h <robot_ip>
Пояснения

Добавка -pkg здесь не случайна. Ее необходимо добавлять к названию приложения. jetpack43 - обозначение версии платформы бортового вычислителя. Для семейства Jetson, на данный момент, используется jetpack43, в ином случае можно выбрать х86_64 . Запускаемое приложение скачивается на бортовой вычислитель в /home//deploy/bob, где bob - имя пользователя на рабочем компьютере

Для более старых версий: bob@desktop:~/isaac/$ ./engine/build/deploy.sh

Для автоматического запуска приложения на бортовом вычислителе сразу после сборки, к команде добавляется аргумент --run.

Для ручного запуска, можно выполнить на бортовом вычислителе:

cd ~/deploy/bob/ping_pong-pkg
./apps/tutorials/ping_pong/ping_pong

4. Создание своего приложения

Начнем с того, что в Isaac SDK используются такие понятия как: пакеты(packages), узлы(nodes), каналы(channels), но и такие понятия как: приложения(applications) и компоненты(components).

Так вот, одной фразой иерархию можно описать так: Пакеты включают в себя приложения, которые состоят из узлов, которые состоят из компонентов. Узлы между собой общаются с помощью каналов. При этом, стоит заметить, что компоненты универсальны по отношению к узлам. Все "полезные" вычисления происходят в компонентах. Компоненты, находящиеся в составе одного узла блокируют друг-друга, а находящиеся в разных узлах - нет.

Каждый пакет состоит из BUILD файла - c инструкциями для сборщика bazel. BUILD ссылается на один или несколько .json, в которых описывается структура и связи узлов и компонентов приложения, а также настройки компонентов. Компоненты обычно описываются в виде cpp/hpp или предварительно скомпилированных .so файлов. Кроме С++ поддерживается Python, который можно использовать для написания компонентов или вместо json файла.

В качестве примера реализуем простейшее приложение "Ping Pong", в отличие от стандартного, наше приложение будет состоять из одного компонента и для Ping и для Pong. (а почему бы и нет)

Итак, начнем. Создадим директорию нового приложения в ~/isaac/sdk/packages/myapp. В этой директории создадим файлы:

  • BUILD

  • myapp.app.json

  • MyPingPong.cpp, MyPingPong.hpp

Рассмотрим структуру файла MyPingPong.hpp:

#pragma once
#include "engine/alice/alice_codelet.hpp" // ядро isaac sdk
#include "messages/ping.capnp.h" // нужен для PingProto

namespace isaac {
class MyPingPong: public isaac::alice::Codelet {
public:
    // Запускается при старте компонента
    void start() override;
    // В зависимости от реализации start(): запускается многократно, либо с заданным периодом, либо в блокирующем режиме, либо по событию приема нового сообщения.
    void tick() override;
    // Запускается по завершению работы компонента
    void stop() override;

    // Коммуникация с другими компонентами
    ISAAC_PROTO_TX(PingProto, ping);
    ISAAC_PROTO_RX(PingProto, pong);
    
    // Параметры запуска компонента
    // (тип , название, значение по умолчанию)
  	ISAAC_PARAM(bool, wait_ping, true);
    ISAAC_PARAM(std::string, message, "Текст сообщения");
};
}
ISAAC_ALICE_REGISTER_CODELET(isaac::MyPingPong);

И соответствующую реализацию в MyPingPong.cpp

#include "MyPingPong.hpp"

#include "engine/core/logger.hpp"

namespace isaac {

void MyPingPong::start() {

  if (get_wait_ping()) {
    LOG_INFO("Запуск в режиме Pong");
    // Запускать tick() по событию приема нового сообщения (rx_pong)
    tickOnMessage(rx_pong());
  } else {
    LOG_INFO("Запуск в режиме Ping");
    // Запускать tick() с периодом, определенным в унаследованном параметре tick_period
    tickPeriodically();
  }
}

void MyPingPong::tick() {
  
  if (!get_wait_ping()) {
    // Получение значения параметра message
    std::string msg = get_message();
    // Инициализация нового сообщения
    PingProto::Builder ping_proto = tx_ping().initProto();
    // Заполнение его данными
    ping_proto.setMessage(msg);
    // Отправка
    tx_ping().publish();
  } else {
    // Получение Proto обьекта входящего сообщения из канала pong
    PingProto::Reader pong_proto = rx_pong().getProto();
    // чтение из Proto обьекта
    const std::string pong_msg = pong_proto.getMessage();
    // расчет времени между отправкой и приемом сообщения
    int64_t latency_ns = this->getTickTimestamp()-rx_pong().pubtime();
    
    LOG_INFO("%s by %d ns", pong_msg.c_str(), latency_ns);
  }
}

void MyPingPong::stop() {
  LOG_WARNING("Завершение работы компонента");
}

}  // namespace isaac

Теперь разберем файл myapp.app.json

{
  "name": "myapp",
  "modules": [
    "//packages/myapp:myapp_components"
  ],
  "graph": {
    "nodes": [
      {
        "name": "ping",
        "components": [
          {
            "name": "message_ledger",
            "type": "isaac::alice::MessageLedger"
          },
          {
            "name": "ping",
            "type": "isaac::MyPingPong"
          }
        ]
      },
      {
        "name": "pong",
        "components": [
          {
            "name": "message_ledger",
            "type": "isaac::alice::MessageLedger"
          },
          {
            "name": "pong",
            "type": "isaac::MyPingPong"
          }
        ]
      }
    ],
    "edges": [
      {
        "source": "ping/ping/ping",
        "target": "pong/pong/pong"
      }
    ]
  },
  "config": {
    "ping" : {
      "ping" : {
        "tick_period" : "1Hz",
        "wait_ping": false,
        "message": "Пинг"
      }
    },
    "pong" : {
      "pong" : {
        "wait_ping": true
      }
    }
  }
}

name - название приложения, описываемого данным json файлом

modules - список модулей или компонентов, которые используются в приложении

graph.nodes - определение узлов, из которых будет состоять граф приложения

  • Каждый узел описывается его именем name, задаваемым произвольно и списком компонентов узла components.

  • Компоненты описываются именем name, тоже задаваемым произвольно и типом type, который должен соответствовать классу, описывающему соответствующий компонент (см. ISAAC_ALICE_REGISTER_CODELET(/* type */) в .hpp файле)

graph.edges - определение ребер графа, связывающих узлы графа между собой. Или связывающих входы (graph.eges[].source) и выходы (graph.eges[].target) компонентов приложения между собой.

graph.config["Название узла"]["Название компонента"] {...} - определение параметров каждого компонента.

Для сборки приложения, необходимо, кроме прочего, определить инструкции для сборщика bazel в файле .BUILD:

load("//engine/build:isaac.bzl", "isaac_app", "isaac_cc_module")

isaac_cc_module(
    name = "myapp_components",
    srcs = [
        "MyPingPong.cpp",
    ],
    hdrs = [
        "MyPingPong.hpp",
    ],
    visibility = ["//visibility:public"],
    deps = [],
)

isaac_app(
    name = "myapp",
    modules = [
        "//packages/myapp:myapp_components",
    ],
)

isaac_app() - определяет название, состав модулей и .json файл для запуска. По умолчанию, для имени myapp будет запускаться myapp.app.json. Но это можно определить в ручную параметром app_json_file . Более подробно о параметрах можно узнать в файле //engine/build:isaac.bzl, который используется для загрузки правил сборщика в начале BUILD файла.

isaac_cc_module() - определяет модуль или цель сборки. Пользователю необходимо указать исходные и заголовочные файлы в соответствующих srcs и hdrs параметрах, а deps позволяет подключить модули из других компонентов Isaac SDK. Заметим, что вместо srcs можно напрямую указывать предварительно скомпилированные .so файлы. В модуле можно собрать исходники сразу для нескольких компонентов, поэтому это и называется модулем.


На этом создание простейшего приложения закончено, осталось лишь его запустить:

bob@desktop:~/isaac/sdk/$ bazel run //packages/myapp:myapp

Приложение будет собрано и запущено. А в консоли, помимо прочих сообщений от Isaac SDK будет выведено:

2020-12-15 16:07:57.257 INFO  packages/myapp/MyPingPong.cpp@23: Запуск в режиме Ping
2020-12-15 16:07:57.257 INFO  packages/myapp/MyPingPong.cpp@19: Запуск в режиме Pong
2020-12-15 16:07:57.259 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 39365 ns
2020-12-15 16:07:58.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 56991 ns
2020-12-15 16:07:59.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 52947 ns
2020-12-15 16:08:00.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 39796 ns
2020-12-15 16:08:01.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 48428 ns
2020-12-15 16:08:02.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 45354 ns
2020-12-15 16:08:03.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 47189 ns
2020-12-15 16:08:04.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 51679 ns
2020-12-15 16:08:05.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 44236 ns
2020-12-15 16:08:06.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 32342 ns
2020-12-15 16:08:07.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 44825 ns
2020-12-15 16:08:08.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 54816 ns
2020-12-15 16:08:09.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 32964 ns
2020-12-15 16:08:10.257 INFO  packages/myapp/MyPingPong.cpp@45: Пинг by 55783 ns
^C  // << тут я нажал Сtrl+C
2020-12-15 16:08:10.742 INFO  engine/alice/application.cpp@274: Stopping application 'myapp' (instance UUID: '8ce1f328-3ed6-11eb-bd16-9306eae0ce97') ...
2020-12-15 16:08:10.753 WARN  packages/myapp/MyPingPong.cpp@50: Завершение работы компонента
2020-12-15 16:08:10.753 WARN  packages/myapp/MyPingPong.cpp@50: Завершение работы компонента

Если просмотреть лог, то можно найти много полезной информации, например, "посмертную" статистику работы приложения:

2020-12-20 18:35:38.743 INFO  external/com_nvidia_isaac_engine/engine/alice/backend/backend.cpp@148: Backend is shutting down... DONE
2020-12-20 18:35:38.743 INFO  external/com_nvidia_isaac_engine/engine/alice/backend/backend.cpp@152: codelets:
|=====================================================================================================================|
|                                           Job Statistics Report (regular)                                           |
|=====================================================================================================================|
| Name                                               |   Count | Time (Median - 90% - Max) [ms] | Load (%) | Late (%) |
|---------------------------------------------------------------------------------------------------------------------|
|                  isaac.alice.BufferAllocatorReport |       2 |     0.00 |     0.00 |     0.01 |    0.2 % |    0.0 % |
|                        isaac.alice.LifecycleReport |      13 |     0.00 |     0.00 |     2.11 |   51.0 % |    0.0 % |
|                   isaac.alice.MessagePassingReport |       2 |     1.11 |     1.11 |     2.13 |   42.8 % |    0.0 % |
|                                               ping |       4 |     0.06 |     0.06 |     0.08 |    3.7 % |    0.0 % |
|                                               pong |       4 |     0.05 |     0.05 |     0.06 |    2.4 % |    0.0 % |
|=====================================================================================================================|

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

Кроме данных компонентов, ядром предоставляются и другие. Автоматически, по крайней мере в 2020.2, они не запускаются. Рассмотрим пару самых, на мой взгляд, необходимых:

5. WebsightServer

WEB-сервер, необходимый для взаимодействия пользователя и приложения. Для тех, кто знаком с ROS, этот компонент можно назвать аналогом RViz, который объединен с RQt.

Для простого запуска Sight необходимо добавить модуль "sight" в инструкции сборщика BUILD и инструкции запуска myapp.app.json .

Изменить в BUILD:

isaac_app(
    name = "myapp",
    modules = [
        "//packages/myapp:myapp_components",
        "sight",
    ],
)

Изменить в myapp.app.json:

"modules": [
    "//packages/myapp:myapp_components",
    "sight"
],

Тогда, в логе запуска нашего приложения появится пару строк:

2020-12-15 17:11:32.478 INFO  packages/sight/WebsightServer.cpp@216: Sight webserver is loaded
2020-12-15 17:11:32.478 INFO  packages/sight/WebsightServer.cpp@217: Please open Chrome Browser and navigate to http://<ip address>:3000

Вот и все, открываем браузер и вводим http://localhost:3000. Этого достаточно, чтобы воспользоваться частью функционала Sight.

Для нашего приложения WebSight будет выглядеть так:

Но может он выглядеть гораздо более красочно:

О том, как сделать его таким, рассмотрим чуть позже. А сейчас рассмотрим его интерфейс.

Справа список узлов, раскрывая которые можно менять параметры компонентов каждого узла.

Если поменять Пинг на Пинг мод, и нажать Submit, то наш компонент ping будет выводить в консоль:

2020-12-15 17:37:05.482 INFO  packages/myapp/MyPingPong.cpp@48: Пинг мод by 29734 ns
2020-12-15 17:37:06.482 INFO  packages/myapp/MyPingPong.cpp@48: Пинг мод by 46147 ns
2020-12-15 17:37:08.482 INFO  packages/myapp/MyPingPong.cpp@48: Пинг мод by 31060 ns

Так можно менять любые параметры, и если логика компонента предусматривает, что значение параметра может быть изменено в процессе его работы, то это будет работать. Можно сказать, аналог dynamic_reconfigure из ROS.

Слева виден список окон:

Статистика (Statistics) - показывает статистику запущенных компонентов приложения.

статистика
статистика

Граф приложения (App Graph) - показывает граф узлов приложения и связи между ними. Аналог rqt_node_graph из ROS, но более скудный по функционалу. Например, он не показывает название каналов (аналог топиков в ROS), да и меньше инструментов по фильтрации отображения. Однако, тут показывается статус выполнения каждого из узлов и расположение узлов можно менять с помощью мышки. Это удобно.

  • серый цвет для еще не запущенных узлов

  • золотой для стартующих узлов

  • оранжевый для узлов, выполняющихся с частотой менее 0.1 Гц или находящихся на паузе.

  • зеленый для узлов, выполняющихся с частотой более 0.1 Гц.

  • красный для остановленных узлов.

граф связей узлов приложения myapp
граф связей узлов приложения myapp

PoseTree - окно, показывающее дерево зависимости систем координат в приложении. Аналог tf_tree в ROS. Работу с системами координат рассмотрим чуть позже. Изображение ниже показано для примера.

Пример дерева с/к
Пример дерева с/к

Virtual Gamepad - виджет WebSight позволяющий передавать сигналы джостика, мыши и клавиатуры в Isaac SDK, например, для ручного управления роботом по сети.

Пример окна для подключенного геймпада
Пример окна для подключенного геймпада

Для подключения джостика используется браузерный Gamepad API.

Для получения данных джостика из этого виджета, необходимо добавить в граф вашего приложения компонент isaac::navigation::VirtualGamepadBridge .

Подробнее о подключении в документации.

Replay Control Panel - виджет, позволяющий проигрывать ранее записанные данные каналов. Например, данные сенсоров. Аналог rosbag play из ROS.

Record Control Panel - виджет, позволяющий записывать данные публикуемые в каналы для последующего воспроизведения. Аналог rosbag record из ROS.

До сих пор, интерфейс позволяет получить доступ только к стандартному функционалу.

Однако, Sight позволяет выводить свою 2D/3D графику, строить графики изменения различных величин и интерактивные элементы.

5.1. Вывод своих данных в Sight

Вывод осуществляется с помощью функции show(). Для нее существует несколько реализаций, для различных типов данных и наборов аргументов:

show(...)

Реализация функции show():

// Helper function to show a variable with sight
template <typename T, std::enable_if_t<std::is_arithmetic<std::decay_t<T>>::value, int> = 0>
void show(const std::string& tag, T value) const {
  node()->sight().show(this, tag, getTickTime(), value);
}
// Helper function to show a variable with sight
template <typename T, std::enable_if_t<std::is_arithmetic<std::decay_t<T>>::value, int> = 0>
void show(const std::string& tag, double time, T value) const {
  node()->sight().show(this, tag, time, value);
}
// Helper function to show everything except a variable with sight
template <typename T, std::enable_if_t<!std::is_arithmetic<std::decay_t<T>>::value, int> = 0>
void show(const std::string& tag, T&& arg) const {
  node()->sight().show(this, tag, getTickTime(), std::forward<T>(arg));
}
// Helper function to show everything except a variable with sight
template <typename T, std::enable_if_t<!std::is_arithmetic<std::decay_t<T>>::value, int> = 0>
void show(const std::string& tag, double time, T&& arg) const {
  node()->sight().show(this, tag, time, std::forward<T>(arg));
}

Например, для построения графика величины задержки между отправкой и приемом сообщения ping/pong достаточно добавить одну строчку после объявления latency_ns в MyPingPong.cpp

show("latency", latency_ns);

Тогда, в sight появится доступный к использованию канал:

После чего, отобразить график можно либо нажав ПКМ по latency, либо с помощью Add plot.

Либо, сконфигурировать изначально в myapp.app.json (добавить в config).

"websight": {
  "WebsightServer": {
    "port": 3000,
    "ui_config": {
      "windows": {
        "imu_rpy": {
          "renderer": "plot",
          "channels": [
            { "name": "myapp/pong/pong/latency" }
          ]
        }
      }
    }
  }
}

Функция show позволяет осуществлять и более сложный рендеринг с помощью sight::Sop. Тогда, show() будет выглядеть примерно так:

sight::show("some_drawing", [&](sight::Sop& sop) {
  // различные операции с обьектом sop.
});

Подробнее на станице документации.

6. PoseTree

PoseTree это инструмент, упрощающий работу с преобразованием систем координат. Аналог TF из ROS.

Добавить новую систему координат можно путем определения матрицы преобразования из одной системы координат в другую. Данное преобразование можно добавить с помощью специального макроса в заголовочном файле компонента:

// Для двухмерного случая
ISAAC_POSE2(ping, pong);
// Для трехмерного случая
ISAAC_POSE3(ping, pong)

После этого, необходимо определить параметр ping_T_pong , например, следующим образом:

// Для двухмерного случая
double x=1,y=2,angle=3.14;
const Pose2d ping_T_pong{SO2d::FromAngle(angle), Vector2d{x, y}};
set_ping_T_pong(ping_T_pong, getTickTime());

// Для трехмерного случая
double x=1,y=2,z=3,angle=3.14;
Vector3d z_axis{0.0, 0.0, 1.0};
const Pose3d ping_T_pong{SO3d::FromAxisAngle(z_axis, angle),
                         Vector3d{x, y, z}};
set_ping_T_pong(ping_T_pong, getTickTime());

В результате, мы увидим в WebSight:

Соответственно, если необходимо получить преобразование между теми или иными с/к, достаточно просто использовать get_foo_T_bar(), где foo и bar названия систем координат.

// 2d
const Pose2d foo_T_bar = get_foo_T_bar(getTickTime());
// 3d
const Pose3d foo_T_bar = get_foo_T_bar(getTickTime());


Заключение

В заключение, хотелось бы выразить свое мнение относительно Isaac SDK. Isaac SDK разработан для профессионального использования и хорошо оптимизирован под обработку видео данных и машинное обучение. Отдельно замечу удобство использования CUDA кода рядом с обычным C++(упрощена передача данных CPU-GPU) и богатую библиотеку стандартных компонентов, ускоренных с помощью GPU. Однако, система сборки bazel может оказаться непривычной, особенно, если необходимо использовать сторонний код, собираемый cmake. Дополнительно, серьезным недостатком является пока еще сравнительно маленькое сообщество и форум, на котором отвечают не так быстро, как хотелось бы.

Оговорюсь, что сравнивать с ROS1 этот фреймворк не стоит. На мой взгляд, ROS1 не годится для профессионального использования. ROS2 другое дело. Сравнение было бы интересно. Вроде бы, на просторах интернета такого сравнения еще нет.

Хочу сказать спасибо моему другу и коллеге Александру, который подал мне идею написать эту статью и периодически пинал меня, чтобы я ее закончил.

Прошу поучаствовать в опросе. Было бы интересно узнать, как много людей на хабре знакомы или используют Isaac SDK.

Источники

Оф. документация

Форум

Ссылка на примеры в статье