ROS 2 UVC camera node for Husarion robots — native H.264/MJPEG/raw frames + Opus audio.
10K+
ROS 2 UVC camera node for Husarion robots — opens a USB camera via V4L2 and publishes H.264 / MJPEG / raw frames onto ROS 2 topics, plus optional Opus listen-in audio. Natively-encoded H.264 / MJPEG pass straight through; a raw capture is re-encoded on the host to H.264 / MJPEG (hardware VAAPI / NVENC / v4l2m2m when available, else software libx264 — see the hardware-acceleration guide below). It can also ingest an upstream ROS image / H.264 topic (e.g. an OAK) and re-wrap or re-encode it.
Multi-arch: linux/amd64 + linux/arm64.
husarion/camera:X.Y.Z — pinned production tag (recommended)husarion/camera:latest — tracks the most recent releaseEach X.Y.Z is one snapshot validated on Husarion hardware. See the Tags tab above for all available versions.
services:
camera:
image: husarion/camera:0.7.1
network_mode: host
ipc: host
env_file:
- /home/husarion/config/common/.env # ROS_DOMAIN_ID, RMW, namespace
environment:
- CAMERA_DEVICE=/dev/video0
- CAMERA_FORMAT=h264
- CAMERA_WIDTH=1920
- CAMERA_HEIGHT=1080
- CAMERA_FPS=30
devices:
- /dev/snd:/dev/snd
# /dev/dri only on hosts that have a Mesa userspace render node
# (Intel / AMD desktops, NVIDIA via the container toolkit). Pi
# 4 has no /dev/dri — listing it makes Docker reject the
# service on start. See "Hardware video acceleration" below.
- /dev/dri:/dev/dri
volumes:
- /dev:/dev
device_cgroup_rules:
- "c 81:* rmw" # V4L2
- "c 116:* rmw" # ALSA
group_add: ["video", "audio"]
restart: unless-stopped
~/image_raw/h264 — foxglove_msgs/CompressedVideo (H.264, both native-h264 and raw-mode re-encode)~/image_raw/compressed — sensor_msgs/CompressedImage (MJPEG)~/image_raw — sensor_msgs/Image (raw passthrough; lazy-published only when a subscriber attaches)~/image_raw/h264/parameter_sets — SPS+PPS sidecar (native h264 mode only; raw-mode prepends in-band)~/audio (Opus) + latched ~/audio_info~/capabilities, ~/active_mode, ~/connected, ~/camera_info~/set_format, ~/set_mmap_buffers, ~/pan/set, ~/tilt/set, ~/zoom/set, ~/ptz/home~ is the ROS namespace the container runs in (set via ROS_NAMESPACE env).
The most-used env vars:
| Var | Default | Notes |
|---|---|---|
CAMERA_DEVICE | /dev/video0 | V4L2 device path |
CAMERA_FORMAT | h264 | h264 / mjpeg / raw (initial only; runtime override via ~/set_format) |
CAMERA_WIDTH × CAMERA_HEIGHT × CAMERA_FPS | 1920×1080@30 | Initial only |
CAMERA_AUDIO_DEVICE | unset | Set to e.g. plughw:CARD=GUV3100,DEV=0 to enable audio listen-in. Empty = no audio. |
CAMERA_ENCODER | auto | In-process re-encoder for raw mode. auto walks H.264: h264_vaapi → h264_nvenc → h264_v4l2m2m → libx264; MJPEG: mjpeg_vaapi → mjpeg_qsv → mjpeg_v4l2m2m → mjpeg. Pin a concrete name (libx264, h264_vaapi, h264_v4l2m2m, …) to override. |
CAMERA_ENCODER_VAAPI_DEVICE | (libav default) | Path to the VAAPI render node (e.g. /dev/dri/renderD128). |
ROS_NAMESPACE | / | Standard ROS 2; the node publishes everything under it. |
Native-h264 cameras (the GUV3100 and similar) already publish H.264 frames the camera node passes through bit-for-bit — no encoder runs, no host GPU needed. Hardware acceleration only matters in raw mode (where the camera publishes uncompressed YUYV/NV12 and the node re-encodes to H.264 / MJPEG inline) or when you flip a native-H.264 camera to a different resolution it can't capture natively.
The CAMERA_ENCODER=auto picker probes the available HW paths in priority order at first browser-subscribe and logs the choice (encoder selected: h264_v4l2m2m (h264) …). If auto lands on libx264 despite hardware being present, the host setup below isn't complete.
volumes:
- /dev:/dev
devices:
- /dev/dri:/dev/dri
group_add: ["video"]
That's it. auto picks h264_vaapi against /dev/dri/renderD128. Override the device path explicitly via CAMERA_ENCODER_VAAPI_DEVICE if you have multiple render nodes.
bcm2835-codec)The Pi 4's stateful M2M H.264 encoder is at /dev/video11. It only appears when the firmware loads the full VideoCore image — by default Raspberry Pi OS uses the cut-down image with codecs stripped.
# On the host:
sudo sed -i 's/^gpu_mem=16$/gpu_mem=128/' /boot/firmware/config.txt
sudo reboot
# After reboot:
ls /dev/video11 # must exist
v4l2-ctl --list-devices | grep codec # bcm2835-codec-encode at /dev/video11
In your compose service, omit /dev/dri (the Pi 4 has no Mesa userspace render node — listing it makes Docker reject the container with "no such file or directory"). The wide /dev:/dev bind-mount + c 81:* rmw cgroup rule are enough to expose /dev/video11 to the encoder; auto picks h264_v4l2m2m.
If the encoder log still says libx264 despite /dev/video11 existing, bcm2835-codec's output-format negotiation has failed for the specific width/height/fourcc combo. Pin CAMERA_ENCODER=h264_v4l2m2m and try CAMERA_RAW_FOURCC=YUYV.
Install nvidia-container-toolkit on the host, then in compose:
deploy:
resources:
reservations:
devices:
- driver: nvidia
capabilities: [video, compute]
auto picks h264_nvenc. As elsewhere, /dev/dri:/dev/dri is fine to keep — VAAPI on the integrated GPU stays available as a fallback.
Jetson devices expose the hardware encoder through V4L2 M2M (/dev/nvhost-msenc + the nvv4l2encoder GStreamer element). The camera image's bundled FFmpeg can use it via h264_v4l2m2m if the device nodes are passed through:
volumes:
- /dev:/dev
runtime: nvidia # if the host has nvidia-container-runtime configured
group_add: ["video"]
Pin CAMERA_ENCODER=h264_v4l2m2m if auto doesn't latch on (the priority list tries h264_nvenc first, which on Jetson needs CUDA runtime that isn't always available inside the container).
This path is supported best-effort; if your specific Jetson model falls back to libx264, file an issue with the encoder-probe log and we'll dig in.
libx264Always available, costs CPU. On a Pi 4 without gpu_mem=128, raw 1080p@30 burns most of the available cores; switch to native h264 capture (CAMERA_FORMAT=h264) instead, or drop to 720p.
Built and maintained by Husarion — robotics research + commercial UGV platforms. For pre-bundled per-robot install scripts (compose stack + native husarion-agent install), see the public release index: https://github.com/husarion/husarion-cockpit-releases.
License: Apache-2.0.
Content type
Image
Digest
sha256:a85e3a934…
Size
409 MB
Last updated
9 days ago
docker pull husarion/camera