# 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`

The calibration data remains in `usb_camera_calibration.yaml`; it is not mixed
with ROS runtime parameters. The shared base launch resolves its installed path
and applies it to the camera node after loading the runtime YAML. Both launch
variants therefore receive the same calibration path without a source-tree
absolute path.

## 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. Resolve the installed `usb_camera_calibration.yaml` path.
4. Start one `hobot_usb_cam` node with the YAML file as its parameter source and
   the resolved calibration resource path.

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