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 5arrow-up-right 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-publisherarrow-up-right

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:

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.

Last updated