From afa97475f0f96d71f70719ff94b39cf81bf8da2d Mon Sep 17 00:00:00 2001 From: Julian Oes Date: Wed, 22 Jul 2026 20:03:50 +1200 Subject: [PATCH] feat(camera_feedback): gate CAMERA_IMAGE_CAPTURED by param (#27920) * feat(camera_feedback): gate CAMERA_IMAGE_CAPTURED by param Some cameras implementing the MAVLink Camera Protocol (e.g. reached via TRIG_INTERFACE=MAVLink) report CAMERA_IMAGE_CAPTURED themselves. The autopilot's camera_feedback also emitted this message, so the ground station saw duplicate capture messages for every shot. Add a bool 'report' field to the camera_capture message, set from the new CAM_CAP_REPORT parameter, and gate the CAMERA_IMAGE_CAPTURED stream on it. When reporting is disabled the capture is still published and logged for geotagging; only the MAVLink message to the ground station is suppressed. CAM_CAP_REPORT applies live (no reboot); it only gates a MAVLink message, unlike CAM_CAP_FBACK which reconfigures the capture pin. * fix(camera_feedback): update docs --- docs/en/camera/fc_connected_camera.md | 4 +++ docs/en/camera/mavlink_v1_camera.md | 8 +++++ msg/CameraCapture.msg | 1 + src/modules/camera_feedback/CMakeLists.txt | 2 ++ .../camera_feedback/CameraFeedback.cpp | 29 +++++++++++++++++-- .../camera_feedback/CameraFeedback.hpp | 5 ++++ .../camera_feedback_params.yaml | 17 +++++++++++ .../mavlink/streams/CAMERA_IMAGE_CAPTURED.hpp | 5 ++++ 8 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/modules/camera_feedback/camera_feedback_params.yaml diff --git a/docs/en/camera/fc_connected_camera.md b/docs/en/camera/fc_connected_camera.md index 69ce7ee7378..7b92cafe451 100644 --- a/docs/en/camera/fc_connected_camera.md +++ b/docs/en/camera/fc_connected_camera.md @@ -179,6 +179,10 @@ PX4 emits the MAVLink [CAMERA_TRIGGER](https://mavlink.io/en/messages/common.htm If camera capture is configured, the timestamp from the camera capture driver is used, otherwise the triggering timestamp. ::: +PX4 also reports each capture to the ground station as a [CAMERA_IMAGE_CAPTURED](https://mavlink.io/en/messages/common.html#CAMERA_IMAGE_CAPTURED) message. +This is controlled by [CAM_CAP_REPORT](../advanced_config/parameter_reference.md#CAM_CAP_REPORT), which is enabled by default and only needs to be disabled for cameras that report captures themselves (see [Capture Reporting](../camera/mavlink_v1_camera.md#capture-reporting)). +Captures are logged for geotagging either way. + ## Testing Trigger Functionality :::warning diff --git a/docs/en/camera/mavlink_v1_camera.md b/docs/en/camera/mavlink_v1_camera.md index 9f6c97ecb23..d2d08214ec7 100644 --- a/docs/en/camera/mavlink_v1_camera.md +++ b/docs/en/camera/mavlink_v1_camera.md @@ -113,3 +113,11 @@ You can also [set the parameters directly](../advanced_config/parameters.md): - [TRIG_INTERFACE](../advanced_config/parameter_reference.md#TRIG_INTERFACE) — `3`: MAVLink ::: + +### Capture Reporting + +Some MAVLink cameras emit [CAMERA_IMAGE_CAPTURED](https://mavlink.io/en/messages/common.html#CAMERA_IMAGE_CAPTURED) themselves each time an image is captured. +By default PX4 emits this message for every capture as well, so a ground station would see each capture reported twice. + +Set [CAM_CAP_REPORT](../advanced_config/parameter_reference.md#CAM_CAP_REPORT) to `0` to stop PX4 reporting captures, leaving the camera as the only source of the message. +Captures are still logged for geotagging when reporting is disabled. diff --git a/msg/CameraCapture.msg b/msg/CameraCapture.msg index 141bb2eb69c..796dc1871d7 100644 --- a/msg/CameraCapture.msg +++ b/msg/CameraCapture.msg @@ -7,3 +7,4 @@ float32 alt # Altitude (AMSL) float32 ground_distance # Altitude above ground (meters) float32[4] q # Attitude of the camera relative to NED earth-fixed frame when using a gimbal, otherwise vehicle attitude int8 result # 1 for success, 0 for failure, -1 if camera does not provide feedback +bool report # Report this capture to the ground station (CAMERA_IMAGE_CAPTURED); it is always logged regardless diff --git a/src/modules/camera_feedback/CMakeLists.txt b/src/modules/camera_feedback/CMakeLists.txt index 152f2c1eae5..cacdc49b7b7 100644 --- a/src/modules/camera_feedback/CMakeLists.txt +++ b/src/modules/camera_feedback/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( CameraFeedback.hpp DEPENDS px4_work_queue + MODULE_CONFIG + camera_feedback_params.yaml ) diff --git a/src/modules/camera_feedback/CameraFeedback.cpp b/src/modules/camera_feedback/CameraFeedback.cpp index 14eb5df90b6..877d7079794 100644 --- a/src/modules/camera_feedback/CameraFeedback.cpp +++ b/src/modules/camera_feedback/CameraFeedback.cpp @@ -46,6 +46,12 @@ CameraFeedback::CameraFeedback() : if (_p_cam_cap_fback != PARAM_INVALID) { param_get(_p_cam_cap_fback, (int32_t *)&_cam_cap_fback); } + + _p_cam_cap_report = param_find("CAM_CAP_REPORT"); + + if (_p_cam_cap_report != PARAM_INVALID) { + param_get(_p_cam_cap_report, &_cam_cap_report); + } } bool @@ -70,6 +76,17 @@ CameraFeedback::Run() return; } + // Apply CAM_CAP_REPORT changes live (it only gates a MAVLink message, so no + // reboot needed). Checked here because it is only used when publishing below. + if (_parameter_update_sub.updated()) { + parameter_update_s pupdate; + _parameter_update_sub.copy(&pupdate); + + if (_p_cam_cap_report != PARAM_INVALID) { + param_get(_p_cam_cap_report, &_cam_cap_report); + } + } + camera_trigger_s trig{}; while (_trigger_sub.update(&trig)) { @@ -151,6 +168,12 @@ CameraFeedback::Run() capture.result = 1; + // Cameras that report captures themselves (e.g. implementing the MAVLink + // Camera Protocol) set CAM_CAP_REPORT to 0 so we don't emit a duplicate + // CAMERA_IMAGE_CAPTURED. + // The capture is still published and logged for geotagging regardless. + capture.report = (_cam_cap_report != 0); + _capture_pub.publish(capture); } } @@ -202,8 +225,10 @@ If camera capture is enabled, then trigger information from the camera capture p otherwise trigger information at the point the camera was commanded to trigger is published (from the `camera_trigger` module). -The `CAMERA_IMAGE_CAPTURED` message is then emitted (by streaming code) following `CameraCapture` updates. -`CameraCapture` topics are also logged and can be used for geotagging. +The `CAMERA_IMAGE_CAPTURED` message is then emitted (by streaming code) following `CameraCapture` updates, +unless `CAM_CAP_REPORT` is disabled (for cameras that report captures themselves, e.g. cameras +implementing the MAVLink Camera Protocol). `CameraCapture` topics are always logged and can be used +for geotagging regardless. ### Implementation diff --git a/src/modules/camera_feedback/CameraFeedback.hpp b/src/modules/camera_feedback/CameraFeedback.hpp index 42aa9866c14..9437d780a9a 100644 --- a/src/modules/camera_feedback/CameraFeedback.hpp +++ b/src/modules/camera_feedback/CameraFeedback.hpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -85,9 +86,13 @@ private: uORB::Subscription _gpos_sub{ORB_ID(vehicle_global_position)}; uORB::Subscription _att_sub{ORB_ID(vehicle_attitude)}; uORB::Subscription _gimbal_sub{ORB_ID(gimbal_device_attitude_status)}; + uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; uORB::Publication _capture_pub{ORB_ID(camera_capture)}; param_t _p_cam_cap_fback; int32_t _cam_cap_fback{0}; + + param_t _p_cam_cap_report; + int32_t _cam_cap_report{1}; }; diff --git a/src/modules/camera_feedback/camera_feedback_params.yaml b/src/modules/camera_feedback/camera_feedback_params.yaml new file mode 100644 index 00000000000..8f98420b990 --- /dev/null +++ b/src/modules/camera_feedback/camera_feedback_params.yaml @@ -0,0 +1,17 @@ +module_name: camera_feedback +parameters: +- group: Camera Control + definitions: + CAM_CAP_REPORT: + description: + short: Report captures to the ground station + long: | + Whether the autopilot reports captures to the ground station by emitting + CAMERA_IMAGE_CAPTURED. + + Disable for cameras that report captures themselves (e.g. cameras + implementing the MAVLink Camera Protocol) to avoid duplicate + CAMERA_IMAGE_CAPTURED messages. Capture events are still published on the + camera_capture topic and logged for geotagging regardless of this setting. + type: boolean + default: 1 diff --git a/src/modules/mavlink/streams/CAMERA_IMAGE_CAPTURED.hpp b/src/modules/mavlink/streams/CAMERA_IMAGE_CAPTURED.hpp index fede0f3c27d..dada6d1ee10 100644 --- a/src/modules/mavlink/streams/CAMERA_IMAGE_CAPTURED.hpp +++ b/src/modules/mavlink/streams/CAMERA_IMAGE_CAPTURED.hpp @@ -64,6 +64,11 @@ private: camera_capture_s capture; if ((_mavlink->get_free_tx_buf() >= get_size()) && _capture_sub.update(&capture)) { + if (!capture.report) { + // Camera reports captures itself (CAM_CAP_REPORT=0); don't duplicate. + return false; + } + mavlink_camera_image_captured_t msg{}; msg.time_boot_ms = capture.timestamp / 1000;