Skip to content

Commit

Permalink
Isaac ROS 0.30.0 (DP3)
Browse files Browse the repository at this point in the history
jaiveersinghNV committed Apr 6, 2023
1 parent d2027fd commit b876656
Showing 21 changed files with 526 additions and 101 deletions.
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Ignore Python files in linguist
*.py linguist-detectable=false

# Images
*.gif filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
@@ -17,5 +20,6 @@
*.so.* filter=lfs diff=lfs merge=lfs -text

# ROS Bags
**/resources/**/*.zstd filter=lfs diff=lfs merge=lfs -text
**/resources/**/*.db3 filter=lfs diff=lfs merge=lfs -text
**/resources/**/*.yaml filter=lfs diff=lfs merge=lfs -text
45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,25 +4,33 @@

## Overview

This ROS2 node uses the NVIDIA GPU-accelerated AprilTags library to detect AprilTags in images and publish their poses, IDs, and additional metadata. This has been tested on ROS2 (Humble) and should build and run on x86_64 and aarch64 (Jetson). It is modeled after and comparable to the ROS2 node for [CPU AprilTags detection](https://github.com/christianrauch/apriltag_ros.git).
Isaac ROS AprilTag contains a ROS 2 package for detection of [AprilTags](https://april.eecs.umich.edu/software/apriltag#:~:text=AprilTag%20is%20a%20visual%20fiducial,tags%20relative%20to%20the%20camera.), a type of fiducial marker that provides a point of reference or measure. AprilTag detections are GPU-accelerated for high performance.

For more information on the Isaac GEM that this node is based off of, see the latest Isaac SDK documentation [here](https://docs.nvidia.com/isaac/packages/fiducials/doc/apriltags.html).
<div align="center"><img alt="graph of nodes with AprilTag" src="resources/isaac_ros_apriltag_nodegraph.png" width="800px"/></div>

For more information on AprilTags themselves, including the paper and the reference CPU implementation, click [here](https://april.eecs.umich.edu/software/apriltag.html).
A common graph of nodes connects from an input camera through rectify and resize to AprilTag. Rectify warps the input camera image into a rectified, undistorted output image; this node may not be necessary if the camera driver provides rectified camera images. Resize is often used to downscale higher resolution cameras into the desired resolution for AprilTags if needed. The input resolution to AprilTag is selected by the required detection distance for the application, as a minimum number of pixels are required to perform an AprilTag detection and classification. For example, an 8mp input image of 3840×2160 may be much larger than necessary and a 4:1 downscale to 1920x1080 could make more efficienct use of compute resources and satisfy the required detection distance of the application. Each of the green nodes in the above diagram is GPU accelerated, allowing for a high-performance compute graph from Argus Camera to ApriTag. For USB and Ethernet cameras, the graph is accelerated from Rectify through AprilTag

<div align="center"><img alt="visual of AprilTag output message information" src="resources/apriltagdetection_message_illustration.png" width="700px"/></div>

As illustrated above, detections are provided in an output array for the number of AprilTag detections in the input image. Each entry in the array contains the ID (two-dimensional bar code) for the AprilTag, the four corners ((x0, y0), (x1, y1), (x2, y2), (x3, y3)) and center (x, y) of the input image, and the pose of the AprilTag.

> **Note**: This package is a GPU-accelerated drop-in replacement for the [CPU version of ROS Apriltag](https://github.com/christianrauch/apriltag_ros)
<!-- Split blockquote -->
> **Note**: For more information, including the paper and the reference CPU implementation, refer to the [AprilTag page](https://april.eecs.umich.edu/software/apriltag.html)
### Isaac ROS NITROS Acceleration

This package is powered by [NVIDIA Isaac Transport for ROS (NITROS)](https://developer.nvidia.com/blog/improve-perception-performance-for-ros-2-applications-with-nvidia-isaac-transport-for-ros/), which leverages type adaptation and negotiation to optimize message formats and dramatically accelerate communication between participating nodes.

## Performance

The performance results of benchmarking the prepared pipelines in this package on supported platforms are below:
The following table summarizes the per-platform performance statistics of sample graphs that use this package, with links included to the full benchmark output. These benchmark configurations are taken from the [Isaac ROS Benchmark](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark#list-of-isaac-ros-benchmarks) collection, based on the [`ros2_benchmark`](https://github.com/NVIDIA-ISAAC-ROS/ros2_benchmark) framework.

| Pipeline | AGX Orin | Orin Nano | x86_64 w/ RTX 3060 Ti |
| ------------------------ | ------------------ | ---------------- | --------------------- |
| Isaac ROS Apriltag(720p) | 248 fps <br> 5.5ms | 82 fps <br> 14ms | 600 fps <br> 2ms |
| Sample Graph | Input Size | AGX Orin | Orin NX | Orin Nano 8GB | x86_64 w/ RTX 3060 Ti |
| -------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [AprilTag Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts//isaac_ros_apriltag_node.py) | 720p | [108 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_node-agx_orin.json)<br>11 ms | [65.5 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_node-orin_nx.json)<br>17 ms | [47.1 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_node-orin_nano_8gb.json)<br>23 ms | [242 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_node-x86_64_rtx_3060Ti.json)<br>4.7 ms |
| [AprilTag Graph](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts//isaac_ros_apriltag_graph.py) | 720p | [117 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_graph-agx_orin.json)<br>12 ms | [70.5 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_graph-orin_nx.json)<br>17 ms | [51.9 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_graph-orin_nano_8gb.json)<br>24 ms | [270 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_apriltag_graph-x86_64_rtx_3060Ti.json)<br>5.0 ms |

These data have been collected per the methodology described [here](https://github.com/NVIDIA-ISAAC-ROS/.github/blob/main/profile/performance-summary.md#methodology).

## Table of Contents

@@ -50,28 +58,28 @@ These data have been collected per the methodology described [here](https://gith
- [ROS Topics Published](#ros-topics-published)
- [Troubleshooting](#troubleshooting)
- [Isaac ROS Troubleshooting](#isaac-ros-troubleshooting)
- [Updates](#updates)
- [Updates](#updates)

## Latest Update

Update 2022-10-19: Updated OSS licensing
Update 2023-04-05: Source available GXF extensions

## Supported Platforms

This package is designed and tested to be compatible with ROS2 Humble running on [Jetson](https://developer.nvidia.com/embedded-computing) or an x86_64 system with an NVIDIA GPU.
This package is designed and tested to be compatible with ROS 2 Humble running on [Jetson](https://developer.nvidia.com/embedded-computing) or an x86_64 system with an NVIDIA GPU.

> **Note**: Versions of ROS2 earlier than Humble are **not** supported. This package depends on specific ROS2 implementation features that were only introduced beginning with the Humble release.
> **Note**: Versions of ROS 2 earlier than Humble are **not** supported. This package depends on specific ROS 2 implementation features that were only introduced beginning with the Humble release.
| Platform | Hardware | Software | Notes |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Jetson | [Jetson Orin](https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/) <br> [Jetson Xavier](https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-agx-xavier/) | [JetPack 5.0.2](https://developer.nvidia.com/embedded/jetpack) | For best performance, ensure that [power settings](https://docs.nvidia.com/jetson/archives/r34.1/DeveloperGuide/text/SD/PlatformPowerAndPerformance.html) are configured appropriately. |
| x86_64 | NVIDIA GPU | [Ubuntu 20.04+](https://releases.ubuntu.com/20.04/) <br> [CUDA 11.6.1+](https://developer.nvidia.com/cuda-downloads) |
| Platform | Hardware | Software | Notes |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Jetson | [Jetson Orin](https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/) <br> [Jetson Xavier](https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-agx-xavier/) | [JetPack 5.1.1](https://developer.nvidia.com/embedded/jetpack) | For best performance, ensure that [power settings](https://docs.nvidia.com/jetson/archives/r34.1/DeveloperGuide/text/SD/PlatformPowerAndPerformance.html) are configured appropriately. |
| x86_64 | NVIDIA GPU | [Ubuntu 20.04+](https://releases.ubuntu.com/20.04/) <br> [CUDA 11.8+](https://developer.nvidia.com/cuda-downloads) |

### Docker

To simplify development, we strongly recommend leveraging the Isaac ROS Dev Docker images by following [these steps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_common/blob/main/docs/dev-env-setup.md). This will streamline your development environment setup with the correct versions of dependencies on both Jetson and x86_64 platforms.

> **Note:** All Isaac ROS Quickstarts, tutorials, and examples have been designed with the Isaac ROS Docker images as a prerequisite.
> **Note**: All Isaac ROS Quickstarts, tutorials, and examples have been designed with the Isaac ROS Docker images as a prerequisite.
## Quickstart

@@ -231,10 +239,11 @@ ros2 launch isaac_ros_apriltag isaac_ros_apriltag.launch.py --ros-args -p size:=

For solutions to problems with Isaac ROS, please check [here](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_common/blob/main/docs/troubleshooting.md).

# Updates
## Updates

| Date | Changes |
| ---------- | --------------------------------------------------------------------------------------- |
| 2023-04-05 | Source available GXF extensions |
| 2022-10-19 | Updated OSS licensing |
| 2022-08-31 | Update to be compatible with JetPack 5.0.2 |
| 2022-06-30 | Update to use NITROS for improved performance |
17 changes: 10 additions & 7 deletions docs/tutorial-isaac-sim.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@

## Overview

This tutorial walks you through a pipeline to estimate the 6DOF pose of [AprilTags](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_apriltag) using images from Isaac Sim.
This tutorial walks you through a graph to estimate the 6DOF pose of [AprilTags](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_apriltag) using images from Isaac Sim.

Last validated with [Isaac Sim 2022.2.1](https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/release_notes.html#id1)

## Tutorial Walkthrough

@@ -24,21 +26,22 @@ This tutorial walks you through a pipeline to estimate the 6DOF pose of [AprilTa
source install/setup.bash
```

4. Launch the pre-composed pipeline launchfile:
4. Launch the pre-composed graph launchfile:

```bash
ros2 launch isaac_ros_apriltag isaac_ros_apriltag_isaac_sim_pipeline.launch.py
```

5. Install and launch Isaac Sim following the steps in the [Isaac ROS Isaac Sim Setup Guide](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_common/blob/main/docs/isaac-sim-sil-setup.md)
6. Open up the Isaac ROS Common USD scene (using the "content" window) located at:
6. Open up the Isaac ROS Common USD scene (using the *Content* tab) located at:

`omniverse://localhost/NVIDIA/Assets/Isaac/2022.1/Isaac/Samples/ROS2/Scenario/carter_warehouse_apriltags_worker.usd`
```text
http://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/2022.2.1/Isaac/Samples/ROS2/Scenario/carter_warehouse_apriltags_worker.usd
```

Wait for it to load completely.
> **Note:** To use a different server, replace `localhost` with `<your_nucleus_server>`
And wait for it to load completely.
7. Press **Play** to start publishing data from Isaac Sim.
<div align="center"><img src="../resources/Isaac_sim_april_tag.png" width="800px"/></div>
<div align="center"><img src="../resources/Isaac_sim_play.png" width="800px"/></div>

8. In a separate terminal, run RViz to visualize the AprilTag detections:

10 changes: 5 additions & 5 deletions docs/tutorial-usb-cam.md
Original file line number Diff line number Diff line change
@@ -4,13 +4,13 @@

## Overview

This tutorial walks you through a pipeline to estimate the 6DOF pose of [AprilTags](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_apriltag) using images streamed from a USB camera.
This tutorial walks you through a graph to estimate the 6DOF pose of [AprilTags](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_apriltag) using images streamed from a USB camera.

## Tutorial Walkthrough

1. Complete the [Quickstart section](../README.md#quickstart) in the main README.
2. Calibrate the camera following instructions in the [camera calibration tutorial](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_common/blob/main/docs/camera-calibration.md). For step 11, use `isaac_ros_apriltag` in place of `<isaac_ros_package>`
3. Clone the ROS2 usb_cam package
3. Clone the ROS 2 usb_cam package

```bash
cd ~/workspaces/isaac_ros-dev/src && git clone -b ros2 https://github.com/ros-drivers/usb_cam
@@ -50,6 +50,6 @@ This tutorial walks you through a pipeline to estimate the 6DOF pose of [AprilTa
rviz2 -d /workspaces/isaac_ros-dev/src/isaac_ros_apriltag/isaac_ros_apriltag/rviz/usb_cam.rviz
```

> **Note:** Your camera vendor may offer a specific ROS2-compatible camera driver package and you can use that in place of the usb_cam package.

> **Important:** If you are using a different camera driver package ensure that the camera stream publishes `Image` and `CameraInfo` pairs to the topics `/image` and `/camera_info`, respectively.
> **Note**: Your camera vendor may offer a specific ROS 2-compatible camera driver package and you can use that in place of the usb_cam package.
<!-- Split blockquote -->
> **Important**: If you are using a different camera driver package ensure that the camera stream publishes `Image` and `CameraInfo` pairs to the topics `/image` and `/camera_info`, respectively.
68 changes: 25 additions & 43 deletions isaac_ros_apriltag/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -15,69 +15,51 @@
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.5)
project(isaac_ros_apriltag LANGUAGES C CXX CUDA)

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
cmake_minimum_required(VERSION 3.23.2)
project(isaac_ros_apriltag LANGUAGES C CXX)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Default to Release build
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
endif()
message( STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}" )

set(CUDA_MIN_VERSION "11.4")

execute_process(COMMAND uname -m COMMAND tr -d '\n'
OUTPUT_VARIABLE ARCHITECTURE
)
message( STATUS "Architecture: ${ARCHITECTURE}" )

find_package(ament_cmake_auto REQUIRED)
ament_auto_find_build_dependencies()

# VPI (workaround for inherited dependency from isaac_ros_image_proc)
find_package(vpi REQUIRED)
# Dependencies
find_package(Eigen3 3.3 REQUIRED NO_MODULE)

# Eigen
find_package(Eigen3 REQUIRED)
find_package(Threads REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})
# nvapriltags (resolve path from ament_index)
ament_index_get_resource(NVAPRILTAGS_RELATIVE_PATH nvapriltags isaac_ros_nitros)
ament_index_get_prefix_path(AMENT_INDEX_PREFIX_PATHS)
foreach(PREFIX_PATH IN LISTS AMENT_INDEX_PREFIX_PATHS)
if(EXISTS "${PREFIX_PATH}/${NVAPRILTAGS_RELATIVE_PATH}")
set(NVAPRILTAGS "${PREFIX_PATH}/${NVAPRILTAGS_RELATIVE_PATH}")
break()
endif()
endforeach()

# CUDA
find_package(CUDA ${CUDA_MIN_VERSION} REQUIRED)
include_directories(${CUDA_INCLUDE_DIRS})
link_directories(${CUDA_TOOLKIT_ROOT_DIR}/lib64)
message( STATUS "Found nvapriltags at ${NVAPRILTAGS}")

# Install config directory
install(
DIRECTORY config
DESTINATION share/${PROJECT_NAME}
)

# apriltag_node
# ApriltagNode
ament_auto_add_library(apriltag_node SHARED src/apriltag_node.cpp)
target_compile_definitions(apriltag_node
PRIVATE "COMPOSITION_BUILDING_DLL"
)
target_link_libraries(apriltag_node ${CUDA_LIBRARIES})
rclcpp_components_register_nodes(apriltag_node "nvidia::isaac_ros::apriltag::AprilTagNode")
set(node_plugins "${node_plugins}nvidia::isaac_ros::apriltag::AprilTagNode;$<TARGET_FILE:apriltag_node>\n")
target_link_libraries(apriltag_node Eigen3::Eigen)

### Install extensions built from source

# Fiducials
add_subdirectory(gxf/fiducials)
install(TARGETS gxf_fiducials DESTINATION share/${PROJECT_NAME}/gxf/lib/fiducials)

### End extensions

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

find_package(launch_testing_ament_cmake REQUIRED)
add_launch_test(test/isaac_ros_apriltag_pipeline_test.py)

endif()

ament_auto_package(INSTALL_TO_SHARE launch)
ament_auto_package(INSTALL_TO_SHARE config launch)
Empty file.
73 changes: 73 additions & 0 deletions isaac_ros_apriltag/gxf/fiducials/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

project(gxf_fiducials LANGUAGES C CXX)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-fPIC -w)
endif()

# Dependencies
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
find_package(isaac_ros_nitros_april_tag_detection_array_type REQUIRED)
find_package(GXF ${ISAAC_ROS_GXF_VERSION} MODULE REQUIRED
COMPONENTS
core
cuda
multimedia
serialization
std
)
include(YamlCpp)

# nvApriltag
add_library(nvapriltags STATIC IMPORTED)

execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE)
message( STATUS "Architecture: ${ARCHITECTURE}" )
if( ${ARCHITECTURE} STREQUAL "x86_64" )
set(ARCH_GXF_PATH "lib_x86_64_cuda_11_8")
elseif( ${ARCHITECTURE} STREQUAL "aarch64" )
set(ARCH_GXF_PATH "lib_aarch64_jetpack51")
endif()
set_property(TARGET nvapriltags PROPERTY IMPORTED_LOCATION ${NVAPRILTAGS}/${ARCH_GXF_PATH}/libapril_tagging.a)

# Fiducials extension
add_library(gxf_fiducials SHARED
components/cuda_april_tag_detector.cpp
fiducials.cpp
)
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)
target_include_directories(gxf_fiducials
PUBLIC
include
PRIVATE
${NVAPRILTAGS}/nvapriltags
${isaac_ros_nitros_april_tag_detection_array_type_INCLUDE_DIRS}
)
target_link_libraries(gxf_fiducials
PRIVATE
Eigen3::Eigen
${isaac_ros_nitros_april_tag_detection_array_type_LIBRARIES}
nvapriltags
PUBLIC
GXF::cuda
GXF::multimedia
GXF::serialization
GXF::std
yaml-cpp
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#include "extensions/fiducials/components/cuda_april_tag_detector.hpp"

#include <cuda_runtime.h>
#include <string>
#include <utility>
#include <vector>

#include "nvAprilTags.h"
#include "engine/core/image/image.hpp"
#include "engine/gems/image/utils.hpp"
#include "extensions/fiducials/messages/fiducial_message.hpp"
#include "gxf/multimedia/video.hpp"
#include "gxf/std/timestamp.hpp"

namespace nvidia {
namespace isaac {

namespace {

// Gets corner points as a tensor
gxf::Expected<gxf::Tensor> CornersToTensor(const float2 corners[4],
gxf::Handle<gxf::Allocator> allocator) {
gxf::Tensor tensor;
return tensor.reshape<double>(gxf::Shape{4, 2}, gxf::MemoryStorageType::kHost, allocator)
.and_then([&]() { return tensor.data<double>(); })
.map([&](double* points) {
const int stride = tensor.shape().dimension(0);
for (int i = 0; i < stride; i++) {
points[i] = corners[i].y;
points[i + stride] = corners[i].x;
}
return std::move(tensor);
});
}

// Converts pose from april tag detection library to Pose3
::isaac::Pose3d DetectionToPose3d(const float translation[3], const float rot_flat[9]) {
// Rotation matrix from nvAprilTags is column major
::isaac::Matrix3d rot_matrix;
rot_matrix << rot_flat[0], rot_flat[3], rot_flat[6],
rot_flat[1], rot_flat[4], rot_flat[7],
rot_flat[2], rot_flat[5], rot_flat[8];
return ::isaac::Pose3d{
::isaac::SO3d::FromQuaternion(::isaac::Quaterniond(rot_matrix)),
::isaac::Vector3d(translation[0], translation[1], translation[2])
};
}

} // namespace

struct CudaAprilTagDetector::AprilTagData {
// Handle used to interface with the stereo library.
nvAprilTagsHandle april_tags_handle;
// CUDA buffers to store the input image.
nvAprilTagsImageInput_t input_image;
// Camera intrinsics
nvAprilTagsCameraIntrinsics_t cam_intrinsics;
// Output vector of detected Tags
std::vector<nvAprilTagsID_t> tags;
// CUDA stream
cudaStream_t main_stream;
};

CudaAprilTagDetector::CudaAprilTagDetector() {}

CudaAprilTagDetector::~CudaAprilTagDetector() {}

gxf_result_t CudaAprilTagDetector::registerInterface(gxf::Registrar* registrar) {
if (!registrar) { return GXF_ARGUMENT_NULL; }
gxf::Expected<void> result;
result &= registrar->parameter(
camera_image_, "camera_image", "Camera Image",
"Undistorted RGB camera image with intrinsics");
result &= registrar->parameter(
april_tags_, "april_tags", "April Tags",
"AprilTag fiducial");
result &= registrar->parameter(
allocator_, "allocator", "Allocator",
"Memory allocator for keypoints tensor");
result &= registrar->parameter(
max_tags_, "max_tags", "Max Tags",
"Maximum number of AprilTags that can be detected",
50);
result &= registrar->parameter(
tag_dimensions_, "tag_dimensions", "Tag Dimensions",
"AprilTag tag dimensions",
0.18);
result &= registrar->parameter(
tag_family_, "tag_family", "Tag Family",
"AprilTag tag family",
std::string("tag36h11"));
result &= registrar->parameter(
video_buffer_name_, "video_buffer_name", "Video Buffer Name",
"Name of the VideoBuffer component to use",
gxf::Registrar::NoDefaultParameter(), GXF_PARAMETER_FLAGS_OPTIONAL);
result &= registrar->parameter(
camera_model_name_, "camera_model_name", "Camera Model Name",
"Name of the CameraModel component to use",
gxf::Registrar::NoDefaultParameter(), GXF_PARAMETER_FLAGS_OPTIONAL);
return gxf::ToResultCode(result);
}

gxf_result_t CudaAprilTagDetector::initialize() {
if (tag_family_.get() != "tag36h11") {
GXF_LOG_ERROR("CudaAprilTagDetector only supports tag36h11 AprilTags");
return GXF_PARAMETER_OUT_OF_RANGE;
}
impl_ = MakeUniqueNoThrow<AprilTagData>();
return impl_ != nullptr ? GXF_SUCCESS : GXF_OUT_OF_MEMORY;
}

gxf_result_t CudaAprilTagDetector::deinitialize() {
impl_ = nullptr;
return GXF_SUCCESS;
}

gxf_result_t CudaAprilTagDetector::tick() {
// Receive message entity
auto entity = camera_image_->receive();
if (!entity) {
return gxf::ToResultCode(entity);
}

// Get frame
auto video_buffer_name = video_buffer_name_.try_get();
const char* frame_name = video_buffer_name ? video_buffer_name->c_str() : nullptr;
auto frame = entity->get<gxf::VideoBuffer>(frame_name);
if (!frame) {
return gxf::ToResultCode(frame);
}

// Get intrinsics
auto camera_model_name = camera_model_name_.try_get();
const char* intrinsics_name = camera_model_name ? camera_model_name->c_str() : nullptr;
auto intrinsics = entity->get<gxf::CameraModel>(intrinsics_name);
if (!intrinsics) {
return gxf::ToResultCode(intrinsics);
}

if (impl_->april_tags_handle == nullptr) {
// Initalize AprilTag library parameters
auto result = createAprilTagDetector(intrinsics.value());
if (!result) {
return gxf::ToResultCode(result);
}
}

if (frame.value()->video_frame_info().color_format !=
gxf::VideoFormat::GXF_VIDEO_FORMAT_RGB) {
GXF_LOG_ERROR("CudaAprilTagDetector only supports RGB images");
return GXF_FAILURE;
}

if (frame.value()->storage_type() != gxf::MemoryStorageType::kDevice) {
GXF_LOG_ERROR("CudaAprilTagDetector only supports CUDA images");
return GXF_FAILURE;
}

// Avoid memory copy by running detection on image view
impl_->input_image.width = frame.value()->video_frame_info().width;
impl_->input_image.height = frame.value()->video_frame_info().height;
impl_->input_image.pitch = frame.value()->video_frame_info().color_planes[0].stride;
impl_->input_image.dev_ptr = reinterpret_cast<uchar3*>(frame.value()->pointer());

// Run AprilTags detection
uint32_t num_tags;
const int error = nvAprilTagsDetect(impl_->april_tags_handle, &(impl_->input_image),
impl_->tags.data(), &num_tags, max_tags_, impl_->main_stream);
if (error != 0) {
GXF_LOG_ERROR("Call to nvAprilTagsDetect failed with error code %d", error);
return GXF_FAILURE;
}

return gxf::ToResultCode(
CreateFiducialListMessage(context(), num_tags)
.map([&](FiducialListMessageParts message) -> gxf::Expected<void> {
for (uint32_t i = 0; i < num_tags; i++) {
const nvAprilTagsID_t& tag = impl_->tags[i];
message.info[i].value()->type = FiducialInfo::Type::kAprilTag;
message.info[i].value()->id = tag_family_.get() + "_" + std::to_string(tag.id);
auto keypoints = CornersToTensor(tag.corners, allocator_);
if (!keypoints) {
return gxf::ForwardError(keypoints);
}
*message.keypoints[i].value() = std::move(keypoints.value());
*message.pose[i].value() = DetectionToPose3d(tag.translation, tag.orientation);
}
return april_tags_->publish(message.entity);
}));
}

gxf_result_t CudaAprilTagDetector::stop() {
if (impl_->april_tags_handle != nullptr) {
cudaStreamDestroy(impl_->main_stream);
nvAprilTagsDestroy(impl_->april_tags_handle);
}
return GXF_SUCCESS;
}

gxf::Expected<void> CudaAprilTagDetector::createAprilTagDetector(
gxf::Handle<gxf::CameraModel> intrinsics) {
if (!intrinsics) {
return gxf::Unexpected{GXF_ARGUMENT_NULL};
}

// Get camera intrinsics
const float fx = intrinsics->focal_length.x;
const float fy = intrinsics->focal_length.y;
const float cx = intrinsics->principal_point.x;
const float cy = intrinsics->principal_point.y;
impl_->cam_intrinsics = {fx, fy, cx, cy};

// Create AprilTags detector instance and get handle
const int error = nvCreateAprilTagsDetector(&(impl_->april_tags_handle),
intrinsics->dimensions.x, intrinsics->dimensions.y,
nvAprilTagsFamily::NVAT_TAG36H11,
&(impl_->cam_intrinsics), tag_dimensions_);
if (error != 0) {
GXF_LOG_ERROR("Call to nvCreateAprilTagsDetector failed with error code %d", error);
return gxf::Unexpected{GXF_FAILURE};
}

// Create stream for detection
cudaStreamCreate(&(impl_->main_stream));

// Allocate the output vector to contain detected AprilTags.
impl_->tags.resize(max_tags_);

return gxf::Success;
}

} // namespace isaac
} // namespace nvidia
35 changes: 35 additions & 0 deletions isaac_ros_apriltag/gxf/fiducials/fiducials.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#include "extensions/fiducials/components/cuda_april_tag_detector.hpp"
#include "extensions/fiducials/gems/fiducial_info.hpp"
#include "gxf/std/extension_factory_helper.hpp"

GXF_EXT_FACTORY_BEGIN()

GXF_EXT_FACTORY_SET_INFO(0xd8d7816ec0485ad4, 0xff795a414bd445ca, "Fiducials",
"Extension containing components for working with fiducials",
"Isaac SDK", "1.0.0", "LICENSE");

GXF_EXT_FACTORY_ADD_0(0xe91d3fa6a42b85ff, 0x966f4e80c607ca9e,
nvidia::isaac::FiducialInfo,
"Holds fiducial meta information.");

GXF_EXT_FACTORY_ADD(0xeacaaee8b4f923b8, 0xc5c90679ce99113c,
nvidia::isaac::CudaAprilTagDetector, nvidia::gxf::Codelet,
"Detects AprilTags in images with nvAprilTags library.");

GXF_EXT_FACTORY_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <memory>
#include <string>

#include "gxf/multimedia/camera.hpp"
#include "gxf/std/allocator.hpp"
#include "gxf/std/codelet.hpp"
#include "gxf/std/receiver.hpp"
#include "gxf/std/transmitter.hpp"

namespace nvidia {
namespace isaac {

// Detects AprilTags in images with nvAprilTags library
class CudaAprilTagDetector : public gxf::Codelet {
public:
// Explicitly declare constructors and destructors
// to get around forward declaration of AprilTagsData
CudaAprilTagDetector();
~CudaAprilTagDetector();

gxf_result_t registerInterface(gxf::Registrar* registrar) override;
gxf_result_t initialize() override;
gxf_result_t deinitialize() override;

gxf_result_t start() override { return GXF_SUCCESS; }
gxf_result_t tick() override;
gxf_result_t stop() override;

private:
// Initializes AprilTag detector with camera intrinsics
gxf::Expected<void> createAprilTagDetector(gxf::Handle<gxf::CameraModel> intrinsics);

gxf::Parameter<gxf::Handle<gxf::Receiver>> camera_image_;
gxf::Parameter<gxf::Handle<gxf::Transmitter>> april_tags_;
gxf::Parameter<gxf::Handle<gxf::Allocator>> allocator_;
gxf::Parameter<int> max_tags_;
gxf::Parameter<double> tag_dimensions_;
gxf::Parameter<std::string> tag_family_;
gxf::Parameter<std::string> video_buffer_name_;
gxf::Parameter<std::string> camera_model_name_;

// Hide the AprilTagData implementation details
struct AprilTagData;
std::unique_ptr<AprilTagData> impl_;
};

} // namespace isaac
} // namespace nvidia
8 changes: 4 additions & 4 deletions isaac_ros_apriltag/package.xml
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>isaac_ros_apriltag</name>
<version>0.20.0</version>
<version>0.30.0</version>
<description>CUDA-accelerated Apriltag detection and pose estimation.</description>

<maintainer email="hemals@nvidia.com">Hemal Shah</maintainer>
@@ -38,15 +38,15 @@
<depend>sensor_msgs</depend>
<depend>tf2_msgs</depend>
<depend>tf2_ros</depend>
<depend>image_transport</depend>
<depend>cv_bridge</depend>
<depend>eigen</depend>
<depend>isaac_ros_apriltag_interfaces</depend>
<depend>isaac_ros_image_proc</depend>
<depend>isaac_ros_nitros</depend>
<depend>isaac_ros_nitros_april_tag_detection_array_type</depend>
<depend>isaac_ros_nitros_camera_info_type</depend>
<depend>isaac_ros_nitros_image_type</depend>

<build_depend>isaac_ros_common</build_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>isaac_ros_test</test_depend>
23 changes: 12 additions & 11 deletions isaac_ros_apriltag/src/apriltag_node.cpp
Original file line number Diff line number Diff line change
@@ -62,16 +62,17 @@ constexpr char APP_YAML_FILENAME[] = "config/apriltag_node.yaml";
constexpr char PACKAGE_NAME[] = "isaac_ros_apriltag";

const std::vector<std::pair<std::string, std::string>> EXTENSIONS = {
{"isaac_ros_nitros", "gxf/std/libgxf_std.so"},
{"isaac_ros_nitros", "gxf/multimedia/libgxf_multimedia.so"},
{"isaac_ros_nitros", "gxf/serialization/libgxf_serialization.so"},
{"isaac_ros_nitros", "gxf/cuda/libgxf_cuda.so"},
{"isaac_ros_nitros", "gxf/libgxf_sight.so"},
{"isaac_ros_nitros", "gxf/libgxf_atlas.so"},
{"isaac_ros_nitros", "gxf/libgxf_isaac_messages.so"},
{"isaac_ros_nitros_april_tag_detection_array_type", "gxf/libgxf_fiducials.so"},
{"isaac_ros_nitros", "gxf/libgxf_message_compositor.so"},
{"isaac_ros_nitros", "gxf/tensorops/libgxf_tensorops.so"}
{"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"},
{"isaac_ros_gxf", "gxf/lib/multimedia/libgxf_multimedia.so"},
{"isaac_ros_gxf", "gxf/lib/serialization/libgxf_serialization.so"},
{"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"},
{"isaac_ros_gxf", "gxf/lib/libgxf_gxf_helpers.so"},
{"isaac_ros_gxf", "gxf/lib/libgxf_sight.so"},
{"isaac_ros_gxf", "gxf/lib/libgxf_atlas.so"},
{"isaac_ros_gxf", "gxf/lib/libgxf_isaac_messages.so"},
{"isaac_ros_gxf", "gxf/lib/libgxf_message_compositor.so"},
{"isaac_ros_image_proc", "gxf/lib/image_proc/libgxf_tensorops.so"},
{"isaac_ros_apriltag", "gxf/lib/fiducials/libgxf_fiducials.so"}
};
const std::vector<std::string> PRESET_EXTENSION_SPEC_NAMES = {
"isaac_ros_apriltag",
@@ -206,7 +207,7 @@ AprilTagNode::AprilTagNode(const rclcpp::NodeOptions & options)

void AprilTagNode::preLoadGraphCallback()
{
RCLCPP_DEBUG(get_logger(), "[AprilTagNode] pretLoadGraphCallback().");
RCLCPP_DEBUG(get_logger(), "[AprilTagNode] preLoadGraphCallback().");
}

void AprilTagNode::postLoadGraphCallback()
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@

@pytest.mark.rostest
def generate_test_description():
"""Generate launch description with all ROS2 nodes for testing."""
"""Generate launch description with all ROS 2 nodes for testing."""
apriltag_node = ComposableNode(
package='isaac_ros_apriltag',
plugin='nvidia::isaac_ros::apriltag::AprilTagNode',
3 changes: 0 additions & 3 deletions resources/Isaac_sim_app_launcher.png

This file was deleted.

3 changes: 0 additions & 3 deletions resources/Isaac_sim_april_tag.png

This file was deleted.

3 changes: 3 additions & 0 deletions resources/Isaac_sim_play.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions resources/apriltagdetection_message_illustration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions resources/isaac_ros_apriltag_nodegraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions resources/rosbags/quickstart.bag/metadata.yaml
Git LFS file not shown
4 changes: 2 additions & 2 deletions resources/rosbags/quickstart.bag/quickstart.bag_0.db3
Git LFS file not shown
4 changes: 2 additions & 2 deletions resources/rosbags/quickstart.bag/quickstart.bag_0.db3.zstd
Git LFS file not shown

0 comments on commit b876656

Please sign in to comment.