Files
ardupilot/libraries/AP_Camera
..
2026-09-23 08:59:12 +10:00

Camera identity and MAVLink links

AP_Camera supports native MAVLink cameras and cameras controlled through other interfaces, including mixtures of the two. The central rule is that managing a native MAVLink camera must not advertise it as a second, flight-controller-owned camera. Camera identity must stay consistent across discovery, control and status.

Camera identities

Camera GCS-visible identity Control endpoint
Native MAVLink camera Original camera system/component IDs Camera system/component IDs
Non-MAVLink camera Flight controller plus camera instance where supported Flight controller, with the command's camera selector

For native cameras, AP_Camera_MAVLinkCamV2 preserves both system and component IDs. This applies to forwarded heartbeats, camera information, video streams, thermal data, tracking status and extended-parameter replies. Stream IDs and storage IDs identify resources within a camera; they do not identify the camera. Two cameras can therefore both advertise stream 1 without colliding. gimbal_device_id describes the associated gimbal, not the camera's identity.

Even when a message has a camera_device_id, native camera messages retain the original identity and payload. In particular, PARAM_EXT_VALUE/PARAM_EXT_ACK have no camera-instance field that could distinguish their sources if they were all re-emitted as the FC component.

Non-MAVLink cameras keep the existing FC-owned interface. Protocols with camera_device_id use the one-based camera slot to identify FC-attached cameras, including information, settings, capture status, field of view and video stream information. Mount-backed information and settings use the camera slot even when CAMn_MNT_INST selects a different mount. AP_Camera retains the zero-based CAMERA_INFORMATION.lens_id and CAMERA_FEEDBACK.cam_idx for legacy consumers. These FC-owned identifiers do not replace native camera component IDs.

Configuration and discovery

Set CAMn_TYPE=6 for each native camera managed by the MAVLink camera-v2 backend. CAMn_COMPID=0 (the default) associates camera slot 1 with MAV_COMP_ID_CAMERA (100) and slot 2 with MAV_COMP_ID_CAMERA2 (101). Set CAMn_COMPID to a non-zero component ID (7–255) to override that slot's mapping, then reboot. The backend learns the system ID and link from a camera heartbeat with the selected component ID. This also applies to mixed backend types: a native camera in slot 2 defaults to component 101 but can use another component ID via CAM2_COMPID. The current frontend supports two slots. Native camera slots must select distinct component IDs, including IDs selected by the zero default. If IDs collide, only the first slot can discover that ID; the later slot reports a warning. Reserved IDs 1–6 and out-of-range IDs also report a warning and disable discovery until the configuration is corrected. The legacy CAMn_TYPE=5 MAVLink trigger backend is not this camera-v2 interface; it does not discover or expose a native camera-v2 endpoint.

Only one camera/gimbal unit per physical MAVLink link is supported. Additional units use separate links and distinct camera identities. Multiple video streams from a single camera are supported. Use MAVLink 2 for the camera protocol.

The backend starts searching after ten seconds. Once associated, it relays the camera's actual heartbeats on isolated links; it does not manufacture a permanent heartbeat for a disconnected camera. A GCS can discover the native endpoint and request its CAMERA_INFORMATION directly. The FC continues to advertise only its own FC heartbeat, not a second camera heartbeat for the same device.

Command selectors and legacy compatibility

Live commands and mission items use the same camera selector resolution. IDs 1–6 select one-based configured slots (including legacy aliases for native cameras); IDs 7–255 select the native camera configured with that component ID. The current frontend has two slots. Unknown, fractional, infinite, negative and out-of-range selectors are rejected instead of wrapping to another camera.

Command Camera selector
Image start/stop capture, set camera source, stop tracking Parameter 1
Set zoom/focus Parameter 3
Set trigger distance, track point Parameter 4
Track rectangle Parameter 5 (COMMAND_INT.x)
Video start capture Parameter 3
Video stop capture Parameter 2

For formerly reserved selector fields, NaN means unset, preserving older GCS commands. This includes zoom/focus, trigger distance, tracking and the video selectors. Image capture and camera-source IDs remain explicit numeric IDs. ACKs for accepted commands with an explicitly selected FC-owned camera identify the one-based camera slot in COMMAND_ACK.result_param2. Rejected commands keep the protocol meaning of that field. Native-camera commands and unselected commands use zero there; native replies retain their own source component ID.

With a nonzero video camera selector, parameter 1 is the stream ID (zero means all streams on that camera), and start parameter 2 is the status frequency. Both fields survive mission storage and execution. Previously stored video missions retain their original camera-slot behavior when loaded.

Video compatibility exception: a zero or NaN video camera selector retains the historical interpretation of parameter 1 as a one-based camera slot; a zero slot selects the primary camera. In this form all streams on that camera are controlled, and status frequency remains ignored. A zero-filled legacy command is indistinguishable from a new command selecting all cameras. To preserve legacy GCS behavior, send a separate command with a nonzero selector for each camera when controlling several cameras or an individual stream. In particular, VIDEO_START_CAPTURE(p1=2, p3=0) still selects camera slot 2, not stream 2.

For image capture, zoom/focus, trigger distance and source selection, zero keeps the existing all-camera behavior. Tracking with no selector keeps the primary camera behavior. Native tracking/source commands through the FC are not implemented by the camera-v2 backend; address the native camera directly for those operations. Selection does not add capabilities to a backend.

Gimbal control accepts native component IDs and legacy one-based mount slots.

Gimbal command/message Mount selector
DO_GIMBAL_MANAGER_PITCHYAW, DO_GIMBAL_MANAGER_CONFIGURE Parameter 7
DO_SET_ROI_LOCATION, DO_SET_ROI_NONE, DO_SET_ROI_WPNEXT_OFFSET Parameter 1
DO_SET_ROI_SYSID Parameter 2
GIMBAL_MANAGER_SET_ATTITUDE, GIMBAL_MANAGER_SET_PITCHYAW gimbal_device_id

Manager information/status and camera-to-gimbal associations advertise native component IDs for MAVLink gimbals and numbered IDs for other mounts. Cached native attitude status uses the gimbal's source identity and original attitude frame. Before native discovery the manager advertises the configured component ID, so its identity does not change when discovery completes.

Mount compatibility exception: selector zero continues to mean the primary mount, not all mounts. ROI_LOCATION and ROI_NONE with a zero or NaN selector keep the vehicle-specific yaw behavior; explicitly selected live or mission ROI commands affect only the selected mount. Legacy DO_SET_ROI still uses parameter 1 as an ROI mode. Mission gimbal pitch/yaw and next-waypoint ROI offsets also resolve component IDs. ROI_SYSID uses parameter 2 to select the mount, with NaN treated as the old unspecified selector. Target system IDs must be 1–255.

MAVn_OPTIONS applies to a MAVLink instance, not directly to SERIALn. Instances are assigned in serial-port order among ports using MAVLink. Check the mapping when configuring a camera port.

Camera link Addressed traffic Broadcast traffic
Normal/broadcast Normal MAVLink routing Normal MAVLink forwarding
Unicast, bit 4 (value 16) Exact learned system/component routes Isolated, except camera-backend relays to normal links
Legacy private, bit 1 (value 2) Requests can enter on an exact route; replies cannot normally leave Isolated, except camera-backend relays to normal links

Unicast is recommended for full GCS access. It prevents other links' broadcasts from reaching the device, and prevents device broadcasts from reaching other links through the generic router. It still processes incoming messages locally and learns routes. Addressed requests and replies, including MAVFTP and COMMAND_ACK, can traverse the link normally.

Unicast links send the FC heartbeat but do not start the normal FC telemetry streams. A camera or gimbal requests the inputs it needs with MAV_CMD_SET_MESSAGE_INTERVAL or MAV_CMD_REQUEST_MESSAGE. Event-driven FC broadcasts, such as home/origin changes, are also suppressed; explicit requests for those messages still work.

Private mode is not equivalent to unicast: it blocks addressed replies such as MAVFTP responses and command acknowledgements. Relaying camera broadcasts does not remove this restriction. Setting both bits retains the private restrictions.

Camera-owned broadcast relay

Camera protocol messages often have no destination fields, even when they answer an addressed request. The generic router does not have camera-specific exceptions. AP_Camera_MAVLinkCamV2::handle_message() selects the supported broadcasts and resend_message() provides the shared relay path:

  • Accept messages from the associated camera system/component IDs and input link.
  • Relay only when that link is private or unicast. Normal links already forward the original packet and must not get an additional backend copy.
  • Send to active normal MAVLink links, excluding private, unicast and high-latency destinations. Do not reflect messages onto the camera's link.
  • Preserve the original packet, including source identity, payload and signature. Relaying is best-effort when the destination's transmit buffer is full.

The relay covers discovery (HEARTBEAT, CAMERA_INFORMATION), settings, storage, capture status and image notifications, field of view, video stream information and status, thermal range, image/geographic tracking status, and extended-parameter values and acknowledgements. Adding a message requires dispatching it to AP_Camera and adding it to the backend's message handling, not changing routing policy. Unrecognised device broadcasts remain isolated; this is not an unrestricted forwarding exception for everything sent by a camera.

Requests, capabilities and cached replies

A GCS sends native camera commands to the discovered camera system/component IDs. This includes zoom/focus, point/rectangle/stop tracking, message requests and message interval changes. The camera supplies the command result and capability flags. An addressed command is not translated into an FC camera command by the router. ArduPilot's existing mission/RC camera controls can independently use the configured backend.

For thermal and tracking telemetry, the GCS requests the message or stream at the camera endpoint. A targeted request does not give the resulting broadcast a destination field; the backend relay carries that response across a unicast link.

The GCS uses cam_definition_uri to locate the XML file. A comp=NN selector in an FTP URI identifies the file server, not the parameter or tracking endpoint. PARAM_EXT_REQUEST_LIST, PARAM_EXT_REQUEST_READ and PARAM_EXT_SET address the camera's discovered identity. Their broadcast replies retain that identity, so concurrent transactions with different cameras remain distinguishable.

The FC also supports requests for cached camera information, capture status and video stream information. These replies use the original camera system/component IDs, not the FC IDs, and retain camera timestamps and native gimbal associations. Cached camera information falls back to the configured CAMn_MNT_INST association if the camera reports no gimbal and belongs to the FC's system. A non-zero native association is never replaced, and raw forwarded camera packets are unchanged. This fallback is available through FC cache requests, not direct camera requests. Cached packets are finalized using the outgoing channel's sequence and signing state. They do not change the global MAVLink identity. The cache bounds video streams and resumes multi-message replies when transmit space becomes available.

Cached replies and original camera packets use different sequence counters under the same source identity. A GCS's sequence-based packet-loss estimate can therefore show apparent loss even when delivery is complete. Direct camera requests avoid adding FC-generated cached packets to that sequence space. Native gimbal attitude replies have the same caveat: the FC can resend status less than one second old using the gimbal's system/component IDs and original attitude frame. On a broadcast link these cached replies may arrive alongside the original gimbal stream, with different sequence counters under the same identity.

Capture status expires after three seconds without an update. Cached capture status also includes interval capture scheduled by ArduPilot. Until remote status is available, the native backend does not invent FC-owned capture status. It also does not emit the generic FC-owned settings or field-of-view messages for native cameras: those are obtained from the camera itself. Explicit FC requests are a compatibility path; direct camera requests provide the camera's full capabilities without the limitations of the FC cache. An accepted FC message request schedules a send; it does not guarantee that a backend can supply that message. In particular, request native settings and field-of-view messages directly from the camera rather than through the FC.

Tests

The Copter autotests exercise two-camera relay identity, discovery and addressed control, exclusion of unrelated devices and isolated destinations, broadcast-link duplicate prevention, mixed native/non-MAVLink cameras, cached reply identity and backpressure, and real MT11 MAVFTP directory listing and XML download over unicast. Selector tests cover live commands and mission upload/download/execution, legacy NaN values with SITL floating-point traps enabled, video stream selection, native gimbal IDs and cached attitude frames, and FC-owned camera ACK identity. Normal, unicast and private links are exercised. The video fixtures are independent of the routing design.