sample_depth_estimation from the qrb_ros_samples catalog. Instead of installing the packaged sample, you build the ROS 2 node yourself — subscribing to a camera topic, running a quantized TFLite model on the Hexagon HTP NPU via the Qualcomm QNN delegate, and publishing a colorized depth image plus the raw inverse-depth map. The goal is to show end-to-end how the pieces fit so you can build your own nodes for any model.
Why build your own instead of using the sample? The sample catalog is a great starting point, but you’ll eventually hit a model or pipeline shape it doesn’t cover. This page walks through the same pattern the samples use — QNN delegate loading, preprocessing, inference, postprocess, publish — wired against stock
sensor_msgs / cv_bridge and a model from Qualcomm AI Hub. Once you’ve seen it once, you can swap MiDaS v2 for any AI Hub model and reuse the same scaffolding. Both this page and sample_depth_estimation target the same Hexagon HTP NPU — this one just exposes every wire.Where each stage runs
| Stage | Where it runs | Notes |
|---|---|---|
| Camera capture | ISP | Camera hardware block. |
| Color convert (YUYV → BGR) | CPU | Done inside v4l2_camera. Can be offloaded to GPU via IM SDK GStreamer plugins. |
| Preprocess (resize, normalize) | CPU | cv2.resize + NumPy in midas_tflite.py. |
| Inference | NPU (Hexagon HTP) | via the QNN TFLite delegate (libQnnTFLiteDelegate.so, backend htp). This is the Qualcomm differentiator. |
| Postprocess (colorize) | CPU | cv2.applyColorMap. |
| Publish | CPU | rclpy + cv_bridge. |
How this differs from a stock ROS 2 TFLite node
- Stock TFLite runs on CPU (or OpenCL GPU at best). The Hexagon HTP NPU is only reachable through the Qualcomm QNN delegate or the QNN SDK, which is what this pipeline loads.
- Every node boundary is a memcpy.
cv_bridge+v4l2_cameraallocate and copy the full frame on each hop. For a hardware‑to‑hardware pipeline (camera ISP → NPU) that copy is avoidable — seeqrb_ros_transportfor DMA‑buf fd passing. - NVIDIA Isaac ROS / Intel OpenVINO packages target different silicon (NVIDIA GPU / Intel VPU) and won’t run on Qualcomm hardware.
/image_raw, not a specific robot platform.
Pre and post processing in this workflow — image decoding, resizing, color conversion, and visualization — runs on the CPU. For GPU‑accelerated pre and post processing, use the GStreamer plugins in IM SDK.
Install prerequisites
Install the Python TFLite runtime, verify the QNN TFLite delegate, and install the camera driver.TFLite runtimeIf QNN TFLite delegate — confirm the shared library is present:If the file is missing, install the Qualcomm AI SDK or QNN runtime package for your device before continuing. The library must be at
ai-edge-litert is unavailable for your platform, fall back to tflite-runtime:/usr/lib/libQnnTFLiteDelegate.so (the default path the node loads from).Camera driverGet the model
All models on Qualcomm AI Hub are compiled and validated for your specific target device before you download them.
- Go to https://aihub.qualcomm.com/models/midas.
- Select Export and choose your target device — IQ‑8275 EVK for IQ8, or IQ‑9075 EVK for IQ9.
- Select TFLite as the runtime and INT8 quantization (
w8a8). - Download the exported
.tflitefile — it will be namedmidas-midas-v2-w8a8.tflite.
Scaffold the package
ros2 pkg create generates all the boilerplate — package.xml, setup.cfg, setup.py, the ament resource marker, and __init__.py.Navigate to your workspace root before running these commands. All paths in the remaining steps are relative to your workspace root.
The package name must be the first positional argument after
ros2 pkg create — place it immediately after create, before any flags. Putting it after --dependencies causes the CLI to treat it as another dependency, and the command will fail with no package created.Update package.xml
ros2 pkg create added the ROS dependencies already. Append these two Python system dependencies inside the <package> block:rosdep install to resolve them:Replace setup.py
The generated
setup.py needs updated data_files to install the launch file, config, and model.Write the source files
The package has two source files: a TFLite wrapper class that handles delegate loading, preprocessing, inference, and visualization; and a ROS 2 node that wires the camera subscription, runs inference, and publishes results.
midas_depth_ros/midas_tflite.py
midas_depth_ros/midas_tflite.py
TFLite wrapper that handles QNN delegate loading, input preprocessing, inference, and depth colorization:
midas_depth_ros/midas_depth_node.py
midas_depth_ros/midas_depth_node.py
ROS 2 node that wires the camera subscription, inference, and topic publishing together:
Write the launch and config files
| Parameter | Options | Notes |
|---|---|---|
use_qnn_delegate | true / false | true runs on the HTP NPU; false falls back to CPU |
qnn_backend | htp, gpu, cpu | htp targets the Hexagon NPU |
colormap | inferno, magma, viridis, plasma, jet, turbo, hot, bone | Colormap applied to the published depth visualization |
Topics
| Direction | Topic | Type | Notes |
|---|---|---|---|
| sub | /image_raw | sensor_msgs/Image bgr8 | Camera input from v4l2_camera |
| pub | /midas/depth_image | sensor_msgs/Image bgr8 | Colorized inverse-depth for RViz |
| pub | /midas/depth | sensor_msgs/Image 32FC1 | Raw inverse-depth (higher = closer) |
Visualizing in RViz
Add an Image display and set the topic to/midas/depth_image. The colorized output maps closer objects to brighter values with the default inferno colormap.
Next steps
- Adapt this scaffolding to another model. Swap the MiDaS export in Step 2 for any TFLite model from Qualcomm AI Hub and adjust preprocessing in
midas_tflite.py. The delegate loading, topic wiring, and launch/config files carry over unchanged. - Want to avoid the per-frame CPU copy between the camera and this node? See
qrb_ros_transportfor zero-copy DMA-buf passing. - Prefer the packaged version of this pipeline?
sample_depth_estimationinqrb_ros_samplesships the same pipeline pre-wired — use it when you want to run depth estimation without building a node yourself.

