# Custom Camera Publisher

Cyclops ships with a `usb-camera` service that auto-detects UVC cameras and publishes frames into the navigation stack. If your camera isn't UVC — for example a MIPI/CSI sensor or an IP camera — you replace that service with your own publisher.

### How Cyclops consumes camera data

Internally, Cyclops uses [Eclipse eCAL 5](https://eclipse-ecal.github.io/ecal/v5.12/index.html) as its pub/sub bus. MapMatcher subscribes to a single eCAL topic and decodes each message as a Cap'n Proto `vkc::Image`:

| Topic     | eCAL type     | Schema                       | Encoding        |
| --------- | ------------- | ---------------------------- | --------------- |
| `S1/cama` | `capnp:Image` | `vkc::Image` (`image.capnp`) | JPEG, grayscale |

Anything that publishes onto `S1/cama` with the right schema is a valid camera source. The bundled `usb-camera` service is one such publisher; your custom publisher is another.

### When you need a custom publisher

| Camera type                                 | Use                                                |
| ------------------------------------------- | -------------------------------------------------- |
| UVC USB camera                              | Bundled `usb-camera.service` — no integration work |
| MIPI / CSI sensor                           | Custom publisher                                   |
| GigE / IP camera                            | Custom publisher                                   |
| Vendor SDK (Allied Vision, FLIR, etc.)      | Custom publisher                                   |
| Any non-UVC source you already have running | Custom publisher                                   |

### Integration kit

We ship a standalone repo with everything needed to write a publisher without pulling in the SDK:

[**cyclops-camera-publisher**](https://github.com/Theseus-Dev/cyclops-camera-publisher)

Inside:

* `cyclops_camera_ecal.py` — Python helper. Build a `CyclopsCameraPublisher`, call `publish_frame(numpy_array, calibration)`.
* `cyclops_camera_ecal.{hpp,cpp}` — C++ helper with the same contract.
* `example_usb_camera.{py,cpp}` — runnable end-to-end examples (OpenCV → helper → eCAL).
* `example_image_subscriber.py` — verification subscriber that decodes `S1/cama` and prints calibration + timing.
* `capnp/` — the Cap'n Proto schemas (`image.capnp` and dependencies), so you can build standalone.
* `camera_params.example.yaml` — calibration template that mirrors the on-device YAML shape.

Read the repo README for the full wire contract, field-by-field requirements, and the steps for replacing the bundled service.

### Minimum your publisher must do

1. Capture frames from your camera however you like.
2. Encode each frame as grayscale JPEG.
3. Stamp `header.stampMonotonic` with the capture time in the Cyclops host clock domain (use `header.clockOffset` if your camera reports timestamps in another clock).
4. Fill `intrinsic.kb4` with calibrated KB4 intrinsics for your lens (`fx, fy, cx, cy, k1..k4`).
5. Set `extrinsic.bodyFrame` to the nadir-mount default `(w=0.00202, x=0.70568, y=0.70853, z=0.00170)` with zero translation.
6. Publish on eCAL topic `S1/cama` with type `capnp:Image`.

The helpers in the integration repo handle steps 2–6 for you. You bring camera bring-up (step 1)

### Verifying

Run the verification subscriber from any host on the same eCAL network:

```bash
python3 example_image_subscriber.py --topic S1/cama
```

A correctly-wired publisher prints `encoding=jpeg`, monotonically increasing `seq`, your expected `width x height`, intrinsics matching your calibration YAML, and `body_frame` matching the nadir-mount default.

### Calibration

External integrators provide camera calibration (intrinsics + the nadir-default extrinsic) and publish it on each frame. Vehicle calibration (`b_T_vb`) is unrelated — it's handled by the SDK and never flows through your publisher. See Camera Calibration for the calibration procedure.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.theseus.us/cyclops/developers/custom-camera-publisher.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
