ROS2 (C++) package that discovers every connected USB webcam and Intel RealSense camera and forwards their data as ROS2 topics:
- USB cameras → RGB image stream (
sensor_msgs/Image). - RealSense cameras → RGB image, depth (aligned to color), colored point
cloud, and camera intrinsics (
sensor_msgs/CameraInfo, read from hardware by the official driver). - All forwarded topic names are written to a text file for downstream consumers to read.
Designed for Ubuntu / ROS2 (Humble or Jazzy). Discovery happens at launch time, so re-run the launch after plugging/unplugging cameras.
| Source | Driver | Topics |
|---|---|---|
| USB (UVC) webcam | built-in usb_camera_node (OpenCV/V4L2) |
image only — no intrinsics (by design) |
| RealSense | official realsense2_camera |
image + depth + pointcloud + intrinsics |
| — | built-in topic_recorder_node |
writes the topic list to a file |
Why this split: RealSense ships factory-calibrated intrinsics that the official package exposes automatically; generic USB webcams have none, and intrinsics were intentionally left out for USB (see Future work to add YAML-based intrinsics later).
<serial> is the RealSense's full serial number (sanitized to alnum/underscore), so each
camera gets a unique namespace and unique TF frames — multiple RealSense units coexist
without topic or TF conflicts.
| Camera | Prefix | Example topics |
|---|---|---|
USB i |
/usb_cam_<i>/ |
image_raw (sensor_msgs/Image) |
RealSense <serial> |
/cam_<serial>/ |
color/image_raw, color/camera_info (intrinsics), aligned_depth_to_color/image_raw (16-bit depth), depth/camera_info, depth/color/points |
Exact path depth (
/cam_<serial>/color/...vs/cam_<serial>/cam_<serial>/color/...) depends on the realsense-ros version; both start with/cam_and are recorded. Runros2 topic listto confirm exact names on your system.
By default, ~/Desktop/camera2topic_topics.txt, one topic per line:
/usb_cam_0/image_raw [sensor_msgs/msg/Image]
/cameras/cam_831612060238/color/image_raw [sensor_msgs/msg/Image]
...
Override with the output_file launch argument.
# ROS2 (Humble or Jazzy) already installed + sourced.
sudo apt update
sudo apt install -y ros-<distro>-cv-bridge ros-<distro>-image-transport \
ros-<distro>-realsense2-camera ros-<distro>-realsense2-camera-msgs \
libopencv-dev v4l-utils
# RealSense udev rules (so /dev/video nodes get correct attributes):
# https://github.com/IntelRealSense/librealsense/blob/master/doc/distribution_linux.mdcd ~/colcon_ws/src
git clone <this-repo-url> camera2topic
cd ~/colcon_ws
rosdep install -i --from-path src -y
colcon build --packages-select camera2topic
source install/setup.bash# Forward ALL connected USB + RealSense cameras and write the topic file:
ros2 launch camera2topic all_cameras.launch.py
# Common overrides:
ros2 launch camera2topic all_cameras.launch.py output_file:=/tmp/topics.txt
ros2 launch camera2topic all_cameras.launch.py color_profile:=1920,1080,30 depth_profile:=1280,720,30
ros2 launch camera2topic all_cameras.launch.py enable_pointcloud:=false
ros2 launch camera2topic all_cameras.launch.py usb_width:=1280 usb_height:=720 usb_fps:=30
ros2 launch camera2topic all_cameras.launch.py usb_only:=true # skip RealSense
ros2 launch camera2topic all_cameras.launch.py realsense_only:=true # skip USB
ros2 launch camera2topic all_cameras.launch.py refresh_period:=5.0 # refresh file every 5s (hot-plug)
# Subsets for debugging:
ros2 launch camera2topic usb_cameras.launch.py
ros2 launch camera2topic realsense_cameras.launch.pycat ~/Desktop/camera2topic_topics.txt # the recorded topic list
ros2 topic list # cross-check
ros2 run rqt_image_view rqt_image_view # view USB + RealSense color
ros2 topic list | grep cam_ # find the exact RealSense topic paths first
ros2 topic echo /cameras/cam_<serial>/color/camera_info --once # RealSense intrinsics (K matrix)
ros2 topic echo /cameras/cam_<serial>/aligned_depth_to_color/image_raw --once # depth (Z16)
ros2 topic hz /usb_cam_0/image_raw # frame rate- USB: enumerates
/dev/video*, reads udev attributes withudevadm infoand/sys/class/video4linux/.../name. RealSense nodes are excluded byID_VENDOR_ID == 8086(Intel) and a case-insensitiverealsensename match. Metadata-only nodes are dropped viaID_V4L_CAPABILITIES(falling back tov4l2-ctl --list-formats-ext). Stable/dev/v4l/by-id/*paths are preferred. - RealSense: parses serial numbers from
rs-enumerate-devices --compact, falling back topyrealsense2. Each camera is launched with its ownserial_no+camera_namespaceso multiple RealSense units coexist.
Both helpers degrade gracefully: missing tools → empty list → that source is simply skipped, launch still succeeds.
- USB cameras publish no
camera_infoby design. RealSense intrinsics come from the official package (read from the camera's EEPROM). - Hot-plugging: re-run the launch, or set
refresh_periodso the topic file updates (USB/RealSense node sets themselves are fixed at launch time). - Don't rely on stable
/dev/videoNnumbers — they shift on replug; the discovery code re-scans every launch and prefersby-idpaths for USB. - RealSense
serial_nois passed with inner quotes (a quirk ofrealsense-ros); this is handled inlaunch/common.py.
- YAML-based intrinsics for USB cameras (load a calibration per device).
image_transportcompressed streams.- Lifecycle / composable-node packaging.