> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.stereolabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.stereolabs.com/_mcp/server.

# CUDA Zero-Copy Transport

Starting with v5.6.0, the ZED ROS 2 Wrapper supports the `rosidl::Buffer` message fields of ROS 2 Lyrical and publishes the ZED data with the **CUDA zero-copy transport**: your accelerated ROS 2 nodes receive the images, the depth maps, and the point clouds directly in GPU memory, without copies. In Isaac™ ROS 5, this transport replaces [NITROS](/docs/integrations/isaac-ros/legacy/subscribe-to-zed-topics/), used with Isaac™ ROS 3.

This page explains how the transport works and how to set it up. The [tutorials](#next-steps) then show how to use it in your own nodes.

## How the CUDA zero-copy transport works

In ROS 2 Lyrical, the `uint8[] data` field of messages such as `sensor_msgs/msg/Image` and `sensor_msgs/msg/PointCloud2` is a [`rosidl::Buffer`](https://nvidia-isaac-ros.github.io/concepts/rosidl_buffer/index.html), whose memory can live on the GPU. The `cuda` buffer backend shares this GPU memory between a publisher and its subscribers without copies.

Without the `cuda` buffer backend, every message of a GPU-accelerated pipeline crosses the CPU memory, even when both nodes process the data on the GPU. The ZED node copies the data from the GPU to the CPU (1), the middleware serializes the full payload (2), the middleware of the subscriber deserializes it (3), and the subscriber copies it back to the GPU (4) before running its CUDA kernels:

![CPU path of a ROS 2 message between the ZED node and an accelerated node](/_fern-files/stereolabs.docs.buildwithfern.com/f9ef804ef044cad2932c4f73350a0d6733f1af5435ea490fcbafe9f8f3261aa9/docs/software-integration/isaac-ros/images/rosidl-buffer-cpu-path.svg)

*Figure 1: the CPU path, used by the ZED ROS 2 Wrapper before v5.6.0, or with `general.buffer_backend: cpu`.*

With the `cuda` buffer backend, the `data` field of the message refers to the GPU memory filled by the ZED SDK. The middleware of the ZED node exports this memory as a small descriptor and sends it with the message. The middleware of the subscriber imports the descriptor and maps the same GPU memory, so the CUDA kernels of the subscriber read the data where the ZED SDK wrote it. The publisher middleware selects the transport for each subscriber: for the subscribers that cannot use the `cuda` backend, it copies the data to CPU memory and sends a standard message, so the ZED node does not need to know which subscribers accept CUDA buffers.

![CUDA zero-copy path of a ROS 2 message with the cuda buffer backend, and CPU fallback](/_fern-files/stereolabs.docs.buildwithfern.com/fbf55179f907b42352f085f6208055068e871a78906128e9a4279df245be068f/docs/software-integration/isaac-ros/images/rosidl-buffer-cuda-path.svg)

*Figure 2: the CUDA zero-copy path, with the automatic CPU fallback for the other subscribers. Diagrams adapted from the [NVIDIA® Technical Blog](https://developer.nvidia.com/blog/accelerating-a-ros-2-node-with-an-ai-agent-and-nvidia-isaac-ros/).*

### ZED data published with CUDA buffers

The ZED ROS 2 Wrapper publishes the images, the depth maps, the confidence maps, and the point clouds with CUDA buffers. The parameter `general.buffer_backend` controls this behavior: `cuda` (default) or `cpu`. See the [ZED Stereo Node](/docs/integrations/ros-2/zed-stereo-node/#cuda-zero-copy-transport) and the [ZED Monocular Node](/docs/integrations/ros-2/zed-monocular-node/#cuda-zero-copy-transport) documentation for details.

A subscriber receives the GPU memory of the ZED node only if it **accepts the `cuda` backend** and meets the [requirements](#requirements) below. All the other subscribers, such as RViz2, `ros2 bag`, Python nodes, and nodes on other hosts, receive standard CPU data, with no change on their side.

The ZED node publishes standard CPU data, for all the subscribers of a topic, in these cases:

* An rclcpp **Intra-Process Communication** (IPC) subscriber is connected: IPC delivers the messages without the buffer backend negotiation, so it could hand GPU memory to a node that cannot read it. To receive CUDA buffers, disable IPC on the ZED node and load your nodes in a separate component container, as shown in the [image tutorial](/docs/integrations/isaac-ros/cuda-zero-copy/images/#the-launch-file-explained).
* The topic uses the `TRANSIENT_LOCAL` durability, not supported by the `cuda` backend.
* A compressed `point_cloud_transport` plugin is subscribed to the point cloud: the uncompressed point cloud is then published with CPU data too.

The compressed `image_transport` topics always carry CPU data, and do not change how the uncompressed image topics are published.

> **Note**
>
> The CUDA zero-copy transport is not NITROS: there are no `/nitros` topics and no special publisher or subscriber classes. Your node subscribes to the standard ZED topics with a standard ROS 2 subscription, and declares that it accepts the `cuda` backend.

## Requirements

* An Isaac™ ROS 5 environment with ZED support, set up by following the [Setting up Isaac™ ROS 5](/docs/integrations/isaac-ros/setup-and-installation/) and [Use ZED with Isaac™ ROS](/docs/integrations/isaac-ros/use-zed-with-isaac-ros/) guides.
* The **ZED ROS 2 Wrapper v5.6.0** or newer, built with the `cuda_buffer` package installed.
* The **`cuda_buffer_backend`** plugin, built from source as described below.
* The **`rmw_fastrtps_cpp`** (Fast DDS) or the **`rmw_zenoh_cpp`** v0.10.5 or newer (Zenoh) ROS 2 middleware, as required by Isaac™ ROS 5: with other middleware implementations, or with an older version of `rmw_zenoh_cpp`, the ZED node publishes standard CPU data.
* The ZED node and the subscribers that receive the CUDA buffers must run on the same host, use the same GPU, and run as the same Linux user. With the Docker installation, run them in the same container.
* The nodes that process the ZED data on the GPU must subscribe with a history depth of `1`, and run in a different process than the ZED node, for example in a separate component container. See the [warning in the image tutorial](/docs/integrations/isaac-ros/cuda-zero-copy/images/#accept-the-cuda-buffer-backend) for the details.

## Set up the CUDA zero-copy transport

Run all the commands in the Isaac™ ROS environment. With the Docker installation, run `isaac-ros activate` to open a shell in the running container.

### Install the `cuda_buffer` package

The ZED ROS 2 Wrapper enables the CUDA zero-copy transport only if it finds the `cuda_buffer` package when it is built.

#### Docker

The `Dockerfile.00-zed_sdk` file of the [Use ZED with Isaac™ ROS](/docs/integrations/isaac-ros/use-zed-with-isaac-ros/#add-the-zed-sdk-to-the-isaac-ros-docker-image) guide installs the package. If your file does not contain the `ros-${ROS_DISTRO}-cuda-buffer` package yet, add it to the `apt-get install` command of the second `RUN` instruction, so that it persists across container restarts:

```dockerfile
       && apt-get install -y --no-install-recommends \
           ros-${ROS_DISTRO}-zed-description ros-${ROS_DISTRO}-zstd-image-transport \
           ros-${ROS_DISTRO}-cuda-buffer \
```

Exit all the shells of the running container, then rebuild the image and start the container:

```bash
isaac-ros activate --build-local
```

#### Bare Metal

```bash
sudo apt-get update
sudo apt-get install -y ros-lyrical-cuda-buffer
```

Check that the installed version comes from the Isaac™ ROS repository, which provides the build for CUDA 13.2. The output must contain `cuda-cudart-13-2`:

```bash
apt-cache show ros-lyrical-cuda-buffer=$(dpkg-query -W -f='${Version}' ros-lyrical-cuda-buffer) | grep Depends
```

### Build the `cuda_buffer_backend` plugin

The middleware uses the `cuda_buffer_backend` plugin to share the CUDA buffers between the nodes: both the publisher and the subscribers must load it. Build it from source in your workspace, with the version that matches the installed `cuda_buffer` package:

```bash
apt-cache policy ros-lyrical-cuda-buffer | grep Installed
```

For version `0.1.2`:

```bash
git clone --depth 1 -b 0.1.2 https://github.com/ros2/rosidl_buffer_backends.git /tmp/rosidl_buffer_backends
cp -r /tmp/rosidl_buffer_backends/cuda_buffer_backend/cuda_buffer_backend ${ISAAC_ROS_WS}/src/
rm -rf /tmp/rosidl_buffer_backends
cd ${ISAAC_ROS_WS}
colcon build --symlink-install --packages-select cuda_buffer_backend
source install/local_setup.bash
```

Only the `cuda_buffer_backend` package is copied in the workspace: the other packages of the repository are already installed, or are not required.

> **Warning**
>
> Do not install the `ros-lyrical-cuda-buffer-backend` Debian package. It is built for the NVIDIA® desktop drivers and the CUDA 12 runtime: on Jetson™ it tries to install desktop driver packages that conflict with JetPack and leaves the package manager in a broken state, and on x86_64 it installs a second CUDA toolkit.

### Build the ZED ROS 2 Wrapper

1. If the ZED ROS 2 Wrapper is not in your workspace yet, clone it as described in [Use ZED with Isaac™ ROS](/docs/integrations/isaac-ros/use-zed-with-isaac-ros/).

2. Build the wrapper. The `--cmake-clean-cache` option makes sure that a wrapper built before installing `cuda_buffer` detects it:

   ```bash
   cd ${ISAAC_ROS_WS}
   colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --cmake-clean-cache \
     --packages-up-to zed_wrapper
   source install/local_setup.bash
   ```

   The build output of the `zed_components` package must contain the line `* rosidl::Buffer CUDA zero-copy transport is available`.

3. Start the ZED node and check its log. The CUDA zero-copy transport is active when the node prints:

   ```text
   * Buffer transport: CUDA zero-copy [automatic CPU fallback for subscribers that cannot use it]
   ```

   Otherwise, the line explains why the node publishes standard CPU data, for example `the 'cuda' buffer backend plugin is not installed`.

## Use the CUDA zero-copy transport with Isaac™ ROS nodes

Isaac™ ROS 5 nodes subscribe to the standard ZED topics, as shown in the [ZED test](/docs/integrations/isaac-ros/use-zed-with-isaac-ros/#test-zed-with-isaac-ros). Whether they receive the ZED data as CUDA buffers depends on how each Isaac™ ROS package configures its subscriptions, and on the version of the `cuda_buffer` library it is built with. To verify the transport of your pipeline, subscribe to the output topics of the Isaac™ ROS nodes with the [image example](/docs/integrations/isaac-ros/cuda-zero-copy/images/), which logs the backend of each received message.

Your own nodes receive the CUDA buffers when they accept the `cuda` backend and read the data with the `cuda_buffer` API, as shown in the tutorials.

> **Warning**
>
> Set the input queue depth of the Isaac™ ROS nodes that receive CUDA buffers to `1`. The `input_qos_depth` parameter sets the depth of the subscription. The nodes that synchronize an image with its camera info, such as `ResizeNode`, also have an `input_queue_size` parameter for the synchronization queue. Both are `10` by default. The `cuda` buffer backend can reuse the GPU memory of a message about 100 ms after it is published: a message taken late from a longer queue can no longer be read, and the node stops with a `cuda_buffer_backend::CudaError` exception. In our tests on a Jetson™ AGX Thor with a ZED 2i at HD1080 and 30 FPS, the Isaac™ ROS image format converter stopped in 1 of 5 runs of 150 seconds with its default queue depth, and in none of the runs with `input_qos_depth` set to `1`. Read more in the [warning about the queue depth](/docs/integrations/isaac-ros/cuda-zero-copy/images/#accept-the-cuda-buffer-backend) of the image tutorial.

## Next steps

* [Subscribe to the ZED images with CUDA zero-copy](/docs/integrations/isaac-ros/cuda-zero-copy/images/): accept the `cuda` buffer backend in a subscription, check the backend of each message, read its GPU memory, and compare the CPU and CUDA transports.
* [Subscribe to the ZED point cloud with CUDA zero-copy](/docs/integrations/isaac-ros/cuda-zero-copy/point-cloud/): apply the same approach to the `PointCloud2` messages, and read the coordinates of a point on the GPU.
* [Object detection with Isaac™ ROS](/docs/integrations/isaac-ros/object-detection/): see the CUDA zero-copy transport at work in a complete Isaac™ ROS pipeline, from the ZED images to the TensorRT inference.