diff --git a/docs/superpowers/specs/2026-07-23-unified-usb-camera-launch-design.md b/docs/superpowers/specs/2026-07-23-unified-usb-camera-launch-design.md new file mode 100644 index 0000000..d9c3441 --- /dev/null +++ b/docs/superpowers/specs/2026-07-23-unified-usb-camera-launch-design.md @@ -0,0 +1,78 @@ +# Unified USB Camera Launch Design + +## Goal + +Make the camera behavior of `hobot_usb_cam.launch.py` and +`hobot_usb_cam_websocket.launch.py` identical. The WebSocket variant must add +only the WebSocket service. + +## Configuration + +Add `config/usb_camera.yaml` as the single source of runtime camera parameters: + +- `video_device`: `/dev/video0` +- `image_width`: `1280` +- `image_height`: `720` +- `framerate`: `30` +- `pixel_format`: `mjpeg` +- `io_method`: `mmap` +- `zero_copy`: `false` +- `frame_id`: `default_usb_cam` +- `camera_calibration_file_path`: the existing + `config/usb_camera_calibration.yaml` + +The calibration data remains in `usb_camera_calibration.yaml`; it is not mixed +with ROS runtime parameters. + +## Launch Structure + +`hobot_usb_cam.launch.py` will: + +1. Declare one `camera_config` launch argument whose default is the installed + `config/usb_camera.yaml` path. +2. Start the shared-memory environment once. +3. Start one `hobot_usb_cam` node with the YAML file as its parameter source. + +`hobot_usb_cam_websocket.launch.py` will: + +1. Include `hobot_usb_cam.launch.py` without overriding camera parameters. +2. Include the WebSocket launch configured for `/image`, MJPEG input, image-only + display, and a 30 FPS output limit. + +It will not start `hobot_codec`, duplicate the shared-memory launch, or carry +camera parameter literals. + +## Data Flow + +Without WebSocket: + +`/dev/video0 -> hobot_usb_cam -> /image (CompressedImage)` + +With WebSocket: + +`/dev/video0 -> hobot_usb_cam -> /image (CompressedImage) -> WebSocket` + +No RGB conversion or second JPEG encode is performed. + +## Validation + +An automated structural check will verify that: + +- the YAML contains the required 1280x720, 30 FPS, native MJPEG parameters; +- the base launch loads that YAML; +- the WebSocket launch includes the base launch; +- the WebSocket launch does not define a camera node, codec node, or camera + parameter overrides. + +After building, each launch will be started separately. Both must publish one +`sensor_msgs/msg/CompressedImage` topic at approximately 30 FPS with decoded +dimensions of 1280x720. Only the WebSocket launch may create the WebSocket node +and port 8080 service. + +## Operational Notes + +The two launch files cannot run concurrently because they both own +`/dev/video0`. Runtime validation is sequential and stops the first launch +before starting the second. Any separately started `usb_cam_node_exe` must also +be stopped before validation because V4L2 permits only one active capture owner +for this camera.