Files
dora-zed/README.md
T
cesarandClaude Opus 5 617b8ceb50 add depth configuration parity with the reference driver
parse_depth_mode returned NEURAL for any unrecognised string, so a typo
silently changed depth quality, and NEURAL_PLUS and NEURAL_LIGHT could not be
selected at all. All six modes are now reachable and an unknown name exits
non-zero listing the accepted values.

Parsing moves into Config::from_env() and main reads the configuration before
init_dora_node(), so a bad value fails with its own message instead of after
the node has registered with the dataflow.

Add ZED_CONFIDENCE_THRESHOLD (60) and ZED_TEXTURE_CONFIDENCE_THRESHOLD (100),
which were never set, and change the defaults to 1080 / 200 mm / 600 mm to
match the reference driver perception was tuned against. The resolved settings
are logged at startup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7g2cBJ5e3r15ZbJ9WZn8c
2026-09-08 09:20:01 -03:00

6.6 KiB
Raw Blame History

dora-zed

Dora node for ZED stereo cameras. Publishes BGR images, point clouds and camera intrinsics.

Usage

Add to your dataflow:

nodes:
  - id: zed-camera
    git: ssh://git@gitea.liria.cl:5001/liria/dora-zed.git
    tag: v0.2.0
    build: scripts/build.sh
    path: bin/dora_zed
    inputs:
      tick: dora/timer/millis/100
    outputs:
      - image_bgr
      - depth
      - point_cloud
      - camera_info

See dataflow.yml for a complete runnable example with a camera display node.

Dependencies

Dependency Version Notes
dora-rs 1.0+ Verified against dora-cli 1.0.1; the C++ bindings are built from source at configure time
CMake 3.21+
ZED SDK 5.x Bundles OpenCV; default path /usr/local/zed
CUDA Toolkit — Required by ZED SDK
Rust toolchain stable Builds dora-rs C++ bindings

Installation scripts

Platform Script
Ubuntu 24.04 x86_64 sudo scripts/install_linux_x64.sh
NVIDIA Jetson (ARM64) sudo scripts/install_linux_arm.sh
Windows x64 (PowerShell admin) scripts/install_windows.ps1

Windows: Install the ZED SDK manually from stereolabs.com.

Local build

git clone ssh://git@gitea.liria.cl:5001/liria/dora-zed.git
cd dora-zed
scripts/build.sh

Binary installed to bin/dora_zed.

scripts/build.sh is also the build: command in the dataflow. dora 1.0 runs build commands directly rather than through a shell, so cd, && and command substitution are not available there; keeping the sequence in one script is what makes the dataflow build work. It configures a Release build, which is also what links the optimized dora bindings.

Containerized build

Dockerfile provides the full toolchain — CUDA, ZED SDK and Rust — on Ubuntu 24.04, the newest release Stereolabs ships a ZED SDK for. No camera is needed.

docker build -t dora-zed-build .
docker run --rm --gpus all --user "$(id -u):$(id -g)" \
    -v "$PWD:/src" -v dora-zed-cargo:/usr/local/cargo/registry dora-zed-build

The container runs the same scripts/build.sh.

The first run clones dora-rs and compiles it with Cargo; the named volume keeps that cache across runs. --user keeps build/ and bin/ owned by you rather than root.

x86-64 only. On Jetson, run scripts/install_linux_arm.sh on the device and build natively — there is no ARM64 image here.

Needs an NVIDIA driver and the NVIDIA Container Toolkit on the host, as the ZED SDK does anyway. The image sets NVIDIA_DRIVER_CAPABILITIES=compute,utility,video: libsl_zed.so links against NVDEC (libnvcuvid) and NVENC (libnvidia-encode), which the container runtime mounts from the host only when video is in the capability list.

Configuration

All behavior is controlled via environment variables.

Camera

Variable Default Description
ZED_RESOLUTION 1080 720, 1080, or 2000
ZED_FPS 15 Frame rate
ZED_DEPTH_MODE NEURAL NEURAL, NEURAL_PLUS, NEURAL_LIGHT, PERFORMANCE, QUALITY, or NONE. An unrecognised value is a startup error, not a fallback.
ZED_DEPTH_MIN_MM 200 Minimum depth (mm)
ZED_DEPTH_MAX_MM 600 Maximum depth (mm)
ZED_CONFIDENCE_THRESHOLD 60 Reject depth less certain than this (1–100, lower is stricter)
ZED_TEXTURE_CONFIDENCE_THRESHOLD 100 Reject depth on surfaces less textured than this (1–100)
ZED_DEPTH_FILL false Interpolate depth over holes. Overrides both confidence thresholds.
ZED_FLIP ON ON or OFF
ZED_WARMUP_FRAMES 30 Frames to discard on startup
ZED_RETRY_INTERVAL_SECONDS 5 Reconnection interval (s)

The resolved depth mode, both confidence thresholds and the depth range are logged at startup. The defaults match the reference camera driver the perception stack was tuned against, so depth quality on the robot matches what it expects.

Point cloud

ZED_PC_MODE Shape Description
FULL (default) [H, W, 4] Full resolution grid (~14.7 MB/frame at 720p)
HALF [H/2, W/2, 4] Stride-2 downsample (~4× smaller)

Inputs

Input Description
tick Triggers a camera grab. Connect to dora/timer/millis/N.

Outputs

Output Type Description
image_bgr uint8 [H, W, 3] BGR image. Metadata: shape, encoding=bgr8, intrinsics, timestamps.
depth float32 [H, W] Per-pixel depth in millimetres, published on every frame image_bgr is. Metadata: dtype, layout=HW, shape, units=mm, intrinsics, timestamps. H and W match image_bgr for the same frame_id. Missing depth is 0 — the payload contains no inf and no nan. Holes are not interpolated unless ZED_DEPTH_FILL is set.
point_cloud float32 [*, 4] XYZRGBA point cloud. Shape depends on ZED_PC_MODE. Metadata: units=mm, intrinsics, timestamps.
camera_info float32 [4] [fx, fy, cx, cy]. Metadata: distortion coefficients, timestamps.

Consumers that need 3D back-project depth with the published intrinsics rather than consuming point_cloud; the point cloud is not a prerequisite for 3D.

Timestamps

Every output carries both clocks, as dora timestamp metadata parameters:

Key Epoch Unit Source
device_timestamp_ns Unix epoch (UTC) nanoseconds sl::Camera::getTimestamp(TIME_REFERENCE::IMAGE) — when the camera captured the frame
host_timestamp_ns Unix epoch (UTC) nanoseconds std::chrono::system_clock on the host — when this node published the frame

Both are needed. The device timestamp is the capture instant and is the value two nodes reading the same frame agree on; the host timestamp is the one the rest of the dataflow and the session recorder share. On a Jetson the wall clock can step when NTP synchronises after boot, so the device timestamp is recorded alongside the host one rather than in place of it.

The node calls sl::setTimestampClock(SYSTEM_CLOCK) before opening the camera, which is what makes the device timestamp a Unix-epoch value rather than an SDK-default clock.

Every output for a frame also carries the same frame_id, an integer counter starting at 0.