Sign inSign up

husarion/camera

By husarion

Updated 9 days ago

ROS 2 UVC camera node for Husarion robots — native H.264/MJPEG/raw frames + Opus audio.

Image
0

10K+

husarion/camera repository overview

husarion/camera

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.

Tags

  • husarion/camera:X.Y.Z — pinned production tag (recommended)
  • husarion/camera:latest — tracks the most recent release

Each X.Y.Z is one snapshot validated on Husarion hardware. See the Tags tab above for all available versions.

Quick start

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

Topics + services published

  • Frames:
    • ~/image_raw/h264foxglove_msgs/CompressedVideo (H.264, both native-h264 and raw-mode re-encode)
    • ~/image_raw/compressedsensor_msgs/CompressedImage (MJPEG)
    • ~/image_rawsensor_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 (optional): ~/audio (Opus) + latched ~/audio_info
  • Latched state: ~/capabilities, ~/active_mode, ~/connected, ~/camera_info
  • Services: ~/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).

Configuration

The most-used env vars:

VarDefaultNotes
CAMERA_DEVICE/dev/video0V4L2 device path
CAMERA_FORMATh264h264 / mjpeg / raw (initial only; runtime override via ~/set_format)
CAMERA_WIDTH × CAMERA_HEIGHT × CAMERA_FPS1920×1080@30Initial only
CAMERA_AUDIO_DEVICEunsetSet to e.g. plughw:CARD=GUV3100,DEV=0 to enable audio listen-in. Empty = no audio.
CAMERA_ENCODERautoIn-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.

Hardware video acceleration

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.

Intel / AMD x86 hosts — VAAPI
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.

Raspberry Pi 4 — V4L2 M2M (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.

NVIDIA x86 (desktop / workstation) — NVENC

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.

NVIDIA Jetson — V4L2 M2M

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.

Software fallback — libx264

Always 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.

About

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.

Tag summary

Content type

Image

Digest

sha256:a85e3a934

Size

409 MB

Last updated

9 days ago

docker pull husarion/camera