Hex Flow Node for Camera (USB, Dummy, RealSense, Berxel)
Project description
HEX FLOW NODE CAMERA
📖 Overview
What is hex_flow_node_camera
hex_flow_node_camera provides hex-flow nodes for reading camera streams from USB (V4L2), RealSense, Berxel, and dummy (simulated) cameras. Each node captures frames from the hardware via hex_driver_camera, serializes them into FlatBuffer messages using hex_util_robot, and publishes at the configured rate (default 500 Hz) over Zenoh via hex_flow_core.
What problem it solves
- Hardware abstraction: Abstracts different camera backends (V4L2 USB, Intel RealSense, Berxel ToF) into structured, rate-controlled image topics over Zenoh.
- Unified interface: All camera types share the same topic and message schema for color images, making it easy to swap cameras without changing downstream consumer code.
- Depth support: RealSense and Berxel nodes additionally publish depth streams alongside color, with automatic colormap conversion in test/viewer nodes.
- Simulation friendly: The dummy camera node generates test pattern frames without any hardware, enabling offline development and integration testing.
- Flexible deployment: Supports device auto-detection (serial number), topic remapping, and environment variable configuration — no code changes needed for different hardware setups.
Target users
- Engineers integrating USB cameras (V4L2), Intel RealSense, or Berxel depth cameras into HEXFELLOW robot systems.
- Researchers requiring synchronized color + depth streams for perception and mapping.
- Developers building on the hex-flow framework who need a working camera input layer.
- Teams wanting a simulated camera source for testing vision pipelines offline.
Nodes
| Node | Description | Publishes | Subscribes |
|---|---|---|---|
hex-flow-cam-usb |
USB V4L2 camera color stream reader | color |
- |
hex-flow-cam-dummy |
Simulated camera (test pattern generator) | color |
- |
hex-flow-cam-realsense |
Intel RealSense color & depth stream reader | color, depth |
- |
hex-flow-cam-berxel |
Berxel ToF color & depth stream reader | color, depth |
- |
hex-flow-cam-test-usb |
OpenCV viewer for USB camera | - | color |
hex-flow-cam-test-dummy |
OpenCV viewer for dummy camera | - | color |
hex-flow-cam-test-realsense |
OpenCV viewer for RealSense (color + depth) | - | color, depth |
hex-flow-cam-test-berxel |
OpenCV viewer for Berxel (color + depth) | - | color, depth |
Prerequisites
USB camera nodes require read/write access to /dev/video*. Ensure your user is in the video group:
sudo usermod -aG video $USER
# Log out and back in for the change to take effect
For RealSense cameras, the librealsense2 runtime must be installed. For Berxel cameras, the vendor SDK must be available (see berxel_py_wrapper documentation).
📦 Installation
Requirements
- Python >= 3.10
- OS: Ubuntu (or other Linux)
- Core dependencies:
hex_flow_core>= 0.0.0, < 0.1.0hex_driver_camera>= 0.1.0, < 0.2.0hex_util_msg>= 0.0.0, < 0.1.0hex_util_runtime>= 0.0.0, < 0.1.0hex_util_robot>= 0.0.0, < 0.1.0
Install hex-flow-cli
For Ubuntu or any Debian-based system, install Zenoh and hex-flow CLI:
sudo apt update
sudo apt install -y curl gpg
curl -L https://download.eclipse.org/zenoh/debian-repo/zenoh-public-key | sudo gpg --dearmor --yes --output /etc/apt/keyrings/zenoh-public-key.gpg
echo "deb [signed-by=/etc/apt/keyrings/zenoh-public-key.gpg] https://download.eclipse.org/zenoh/debian-repo/ /" | sudo tee -a /etc/apt/sources.list > /dev/null
sudo apt update
sudo apt install zenoh curl
curl -fsSL https://raw.githubusercontent.com/hexfellow/hex-flow/main/install.sh | sh
For other systems, please install zenohd yourself, then run the install script.
Install hex-flow-node-camera from PyPI
# Base install (USB + Dummy cameras)
pip install hex_flow_node_camera
# With RealSense support
pip install hex_flow_node_camera[realsense]
# With Berxel support
pip install hex_flow_node_camera[berxel]
# With all camera backends
pip install hex_flow_node_camera[all]
Install hex-flow-node-camera from source
We provide a venv.sh script to create a virtual environment with all dependencies installed. However, you need to install uv first. For uv installation, please refer to uv official installation guide.
curl -LsSf https://astral.sh/uv/install.sh | sh
Then you can use our venv.sh to create a virtual environment with all dependencies installed:
git clone https://github.com/hexfellow/hex_flow_node_camera.git
cd hex_flow_node_camera
./venv.sh
📑 Python Config API
The package provides helper functions that return NodeConfig objects for easy integration into your LaunchConfig.
from hex_flow_core import LaunchConfig
from hex_flow_node_camera import (
default_cam_usb_node,
default_cam_usb_test_node,
default_cam_realsense_node,
default_cam_realsense_test_node,
)
config = LaunchConfig(
local_only=True,
enable_tui=True,
log_to_file=True,
save_path="/tmp/cam_test.yml",
)
nodes = {
"cam_usb": default_cam_usb_node(
name="cam_usb",
cam_path="/dev/video2",
width=640,
height=480,
frame_rate=30,
rate_hz=500.0,
required=True,
remap_dict={"color": "cam_usb/color"},
),
"test_cam_usb": default_cam_usb_test_node(
name="test_cam_usb",
rate_hz=30.0,
required=False,
remap_dict={"color": "cam_usb/color"},
),
}
config.set_nodes(nodes)
print(config.export())
default_cam_usb_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"cam_usb" |
Node name and remap prefix |
frame_rate |
int |
30 |
Camera sensor frame rate |
height |
int |
480 |
Image height in pixels |
width |
int |
640 |
Image width in pixels |
cam_buffer_size |
int |
8 |
Internal camera driver buffer size |
sens_ts |
bool |
True |
Use sensor timestamp when available |
cam_path |
str |
"/dev/video0" |
V4L2 device path |
exposure |
int |
100 |
Exposure value |
temperature |
int |
4000 |
White balance temperature |
color_encoding |
str |
"bgr8" |
Color image encoding (e.g. "bgr8", "rgb8") |
rate_hz |
float |
500.0 |
Publishing rate in Hz |
required |
bool |
True |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {name}/color |
default_cam_usb_test_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"test_cam_usb" |
Node name |
rate_hz |
float |
30.0 |
Display refresh rate in Hz |
required |
bool |
False |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {cam_name}/color |
cam_name |
str |
"cam_usb" |
Camera node to subscribe to |
default_cam_dummy_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"cam_dummy" |
Node name and remap prefix |
frame_rate |
int |
30 |
Camera sensor frame rate |
height |
int |
480 |
Image height in pixels |
width |
int |
640 |
Image width in pixels |
cam_buffer_size |
int |
8 |
Internal camera driver buffer size |
sens_ts |
bool |
True |
Use sensor timestamp when available |
color_encoding |
str |
"bgr8" |
Color image encoding |
rate_hz |
float |
500.0 |
Publishing rate in Hz |
required |
bool |
True |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {name}/color |
default_cam_dummy_test_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"test_cam_dummy" |
Node name |
rate_hz |
float |
30.0 |
Display refresh rate in Hz |
show_color_ts |
bool |
False |
Overlay timestamp text on the displayed image |
required |
bool |
False |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {cam_name}/color |
cam_name |
str |
"cam_dummy" |
Camera node to subscribe to |
default_cam_realsense_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"cam_realsense" |
Node name and remap prefix |
frame_rate |
int |
30 |
Camera sensor frame rate |
height |
int |
480 |
Image height in pixels |
width |
int |
640 |
Image width in pixels |
cam_buffer_size |
int |
8 |
Internal camera driver buffer size |
sens_ts |
bool |
True |
Use sensor timestamp when available |
serial_number |
str |
"b0" |
RealSense device serial number |
color_encoding |
str |
"bgr8" |
Color image encoding |
depth_encoding |
str |
"mono16" |
Depth image encoding |
rate_hz |
float |
500.0 |
Publishing rate in Hz |
required |
bool |
True |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {name}/color, {name}/depth |
default_cam_realsense_test_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"test_cam_realsense" |
Node name |
rate_hz |
float |
30.0 |
Display refresh rate in Hz |
required |
bool |
False |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {cam_name}/color, {cam_name}/depth |
cam_name |
str |
"cam_realsense" |
Camera node to subscribe to |
default_cam_berxel_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"cam_berxel" |
Node name and remap prefix |
frame_rate |
int |
30 |
Camera sensor frame rate |
height |
int |
400 |
Image height in pixels |
width |
int |
640 |
Image width in pixels |
cam_buffer_size |
int |
8 |
Internal camera driver buffer size |
sens_ts |
bool |
True |
Use sensor timestamp when available |
serial_number |
str |
"b0" |
Berxel device serial number |
exposure |
int |
10000 |
Exposure value |
gain |
int |
100 |
Gain value |
color_encoding |
str |
"bgr8" |
Color image encoding |
depth_encoding |
str |
"mono16" |
Depth image encoding |
rate_hz |
float |
500.0 |
Publishing rate in Hz |
required |
bool |
True |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {name}/color, {name}/depth |
default_cam_berxel_test_node
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
"test_cam_berxel" |
Node name |
rate_hz |
float |
30.0 |
Display refresh rate in Hz |
required |
bool |
False |
Required for launch |
hidden |
bool |
False |
Hidden from TUI |
remap_dict |
dict |
None |
Custom remap; defaults to {cam_name}/color, {cam_name}/depth |
cam_name |
str |
"cam_berxel" |
Camera node to subscribe to |
💡 Examples
Ready-to-run config scripts are provided in the example/ directory. Each script prints a launch YAML to stdout, intended for use with hexflow run:
USB Camera
# /dev/video2, 640x480, 30 FPS sensor, 500 Hz publish, 30 Hz test display
hexflow run example/usb_test.launch.py
Dummy Camera (simulated)
# No hardware needed, generates test patterns
hexflow run example/dummy_test.launch.py
RealSense
# Color + depth, 500 Hz publish, 30 Hz test display
hexflow run example/realsense_test.launch.py
Berxel
# Color + depth, 500 Hz publish, 30 Hz test display
hexflow run example/berxel_test.launch.py
YAML Examples
USB Camera (500 Hz)
nodes:
- name: cam_usb
build: pip install hex_flow_node_camera
run: hex-flow-cam-usb
required: true
remap:
color: cam_usb/color
env:
FRAME_RATE: "30"
HEIGHT: "480"
WIDTH: "640"
CAM_BUFFER_SIZE: "8"
SEN_TS: "True"
CAM_PATH: "/dev/video2"
EXPOSURE: "100"
TEMPERATURE: "4000"
COLOR_ENCODING: "bgr8"
RATE_HZ: "500"
- name: test_cam_usb
build: pip install hex_flow_node_camera
run: hex-flow-cam-test-usb
required: false
remap:
color: cam_usb/color
env:
RATE_HZ: "30"
Dummy Camera (500 Hz)
nodes:
- name: cam_dummy
build: pip install hex_flow_node_camera
run: hex-flow-cam-dummy
required: true
remap:
color: cam_dummy/color
env:
FRAME_RATE: "30"
HEIGHT: "480"
WIDTH: "640"
CAM_BUFFER_SIZE: "8"
SEN_TS: "True"
COLOR_ENCODING: "bgr8"
RATE_HZ: "500"
- name: test_cam_dummy
build: pip install hex_flow_node_camera
run: hex-flow-cam-test-dummy
required: false
remap:
color: cam_dummy/color
env:
RATE_HZ: "30"
SHOW_COLOR_TS: "False"
RealSense (500 Hz)
nodes:
- name: cam_realsense
build: pip install hex_flow_node_camera[realsense]
run: hex-flow-cam-realsense
required: true
remap:
color: cam_realsense/color
depth: cam_realsense/depth
env:
FRAME_RATE: "30"
HEIGHT: "480"
WIDTH: "640"
CAM_BUFFER_SIZE: "8"
SEN_TS: "True"
SERIAL_NUMBER: "0"
COLOR_ENCODING: "bgr8"
DEPTH_ENCODING: "mono16"
RATE_HZ: "500"
- name: test_cam_realsense
build: pip install hex_flow_node_camera
run: hex-flow-cam-test-realsense
required: false
remap:
color: cam_realsense/color
depth: cam_realsense/depth
env:
RATE_HZ: "30"
Berxel (500 Hz)
nodes:
- name: cam_berxel
build: pip install hex_flow_node_camera[berxel]
run: hex-flow-cam-berxel
required: true
remap:
color: cam_berxel/color
depth: cam_berxel/depth
env:
FRAME_RATE: "30"
HEIGHT: "400"
WIDTH: "640"
CAM_BUFFER_SIZE: "8"
SEN_TS: "True"
SERIAL_NUMBER: "b0"
EXPOSURE: "10000"
GAIN: "100"
COLOR_ENCODING: "bgr8"
DEPTH_ENCODING: "mono16"
RATE_HZ: "500"
- name: test_cam_berxel
build: pip install hex_flow_node_camera
run: hex-flow-cam-test-berxel
required: false
remap:
color: cam_berxel/color
depth: cam_berxel/depth
env:
RATE_HZ: "30"
Message Types (FlatBuffer)
All topics use FlatBuffer messages from hex_util_msg.
color — HexCamColor
| Field | Type | Description |
|---|---|---|
ts_ns |
int64 |
Timestamp in nanoseconds |
data |
[uint8] |
Raw image data (encoded per encoding) |
height |
int32 |
Image height in pixels |
width |
int32 |
Image width in pixels |
encoding |
string |
Image encoding string (e.g. "bgr8") |
Schema: msgs/msg_cam/color_image.fbs
depth — HexCamDepth
| Field | Type | Description |
|---|---|---|
ts_ns |
int64 |
Timestamp in nanoseconds |
data |
[uint8] |
Raw depth image data |
height |
int32 |
Depth image height in pixels |
width |
int32 |
Depth image width in pixels |
encoding |
string |
Depth encoding string (e.g. "mono16") |
Schema: msgs/msg_cam/depth_image.fbs
Environment Variables
All Nodes
| Variable | Type | Default | Description |
|---|---|---|---|
HEX_FLOW_NODE_NAME |
str |
constructor arg | Overrides node name (handled by hex_flow_core) |
HEX_FLOW_REMAP |
str |
{} |
JSON dict for topic remapping (handled by hex_flow_core) |
RUST_LOG |
str |
info |
Log level for envlog |
Camera Nodes (USB / Dummy / RealSense / Berxel)
| Variable | Type | Default | Description |
|---|---|---|---|
FRAME_RATE |
int |
30 |
Camera sensor frame rate |
HEIGHT |
int |
480 (Berxel: 400) |
Image height in pixels |
WIDTH |
int |
640 |
Image width in pixels |
CAM_BUFFER_SIZE |
int |
8 |
Internal camera driver buffer size |
SEN_TS |
bool |
True |
Use sensor timestamp when available |
COLOR_ENCODING |
str |
"bgr8" |
Color image encoding |
RATE_HZ |
float |
500.0 |
Publishing rate in Hz |
USB-specific
| Variable | Type | Default | Description |
|---|---|---|---|
CAM_PATH |
str |
"/dev/video0" |
V4L2 device path |
EXPOSURE |
int |
100 |
Exposure value |
TEMPERATURE |
int |
4000 |
White balance temperature |
RealSense-specific
| Variable | Type | Default | Description |
|---|---|---|---|
SERIAL_NUMBER |
str |
"0" |
RealSense device serial number |
DEPTH_ENCODING |
str |
"mono16" |
Depth image encoding |
Berxel-specific
| Variable | Type | Default | Description |
|---|---|---|---|
SERIAL_NUMBER |
str |
"b0" |
Berxel device serial number |
EXPOSURE |
int |
10000 |
Exposure value |
GAIN |
int |
100 |
Gain value |
DEPTH_ENCODING |
str |
"mono16" |
Depth image encoding |
Test Nodes
| Variable | Type | Default | Description |
|---|---|---|---|
RATE_HZ |
float |
30.0 |
Display refresh rate in Hz |
SHOW_COLOR_TS |
bool |
False |
Overlay timestamp on image (dummy only) |
Architecture
All camera nodes follow the same pattern:
-
Camera initialization — creates a camera driver instance (
HexCamUsbCallback,HexCamDummyCallback,HexCamRealsenseCallback, orHexCamBerxelCallback) fromhex_driver_camera, configured with params such as resolution, frame rate, and device path/serial number. -
Callback registration — user-provided callback functions are registered with the driver. Each callback receives a state dict
{"ts_ns": int, "data": numpy.ndarray}when a new frame arrives from the camera hardware. -
FlatBuffer serialization — within each callback, the frame data and timestamp are serialized into a FlatBuffer message (
HexCamColororHexCamDepth) using helpers fromhex_util_robot(build_color_msg(),build_depth_msg()). The binary payload is then published viahex_flow_core.Node.pub(). -
Rate-controlled publish loop — using
HexRatefromhex_util_runtime, the node publishes at the configured rate (default 500 Hz) while the camera driver's capture thread feeds frames asynchronously into the callbacks.
Test nodes subscribe via hex_flow_core.Node.create_sub() and poll with Node.get(topic, latest=True) at a lower rate (default 30 Hz), deserializing the FlatBuffer and displaying frames with OpenCV (cv2.imshow()). For depth-capable cameras (RealSense, Berxel), test nodes also display a color-mapped depth visualization using depth_to_cmap().
This architecture decouples the variable-rate hardware frame capture from the fixed-rate publish output, ensuring consistent state updates regardless of actual camera frame rate.
📄 License
Apache License 2.0. See LICENSE.
🌟 Star History
👥 Contributors
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hex_flow_node_camera-0.0.1.tar.gz.
File metadata
- Download URL: hex_flow_node_camera-0.0.1.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eee88d33c7f6ecd79e1f9e18d6dd353903b7045e6b01ca3b8654abdce5d709cb
|
|
| MD5 |
d001875195fe6b62576adc86ff1da414
|
|
| BLAKE2b-256 |
19bc7c33b00841def93ca0d4c3c801277123b7f324df2875f636f9cbb9a0c9c4
|
File details
Details for the file hex_flow_node_camera-0.0.1-py3-none-any.whl.
File metadata
- Download URL: hex_flow_node_camera-0.0.1-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92a60f8f5706ceb557d0872468d48e311ecf89ea013af7fd6288595c3a9a782f
|
|
| MD5 |
21067f2bf4ee530d05de03ea1f8157f1
|
|
| BLAKE2b-256 |
c4aded2b6a3d5013bdf95eee663131fc695eafd0d700f56818f393bf1457f53e
|