From 7cb2ac28bafb9114575932bb8143ce67357ab472 Mon Sep 17 00:00:00 2001 From: Silvan Fuhrer Date: Thu, 2 Jul 2026 14:42:34 +0200 Subject: [PATCH] feat(commander): add NAV_RCL_ACT 7 (Hold without failsafe) (#27792) Signed-off-by: Silvan --- docs/en/config/safety.md | 11 +++++++- src/modules/commander/Commander.cpp | 30 +++++++++++++++++++++ src/modules/commander/Commander.hpp | 10 ++++++- src/modules/commander/commander_params.yaml | 7 +++++ src/modules/commander/failsafe/failsafe.cpp | 4 +++ src/modules/commander/failsafe/failsafe.h | 1 + 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/en/config/safety.md b/docs/en/config/safety.md index da15707ab5..2ca024d559 100644 --- a/docs/en/config/safety.md +++ b/docs/en/config/safety.md @@ -128,9 +128,18 @@ Additional (and underlying) parameter settings are shown below. | ----------------------------------------------------------------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [COM_RC_LOSS_T](../advanced_config/parameter_reference.md#COM_RC_LOSS_T) | Manual Control Loss Timeout | Time after last setpoint received from the selected manual control source after which manual control is considered lost. This must be kept short because the vehicle will continue to fly using the last known stick position until the timeout triggers. | | [COM_FAIL_ACT_T](../advanced_config/parameter_reference.md#COM_FAIL_ACT_T) | Failsafe Reaction Delay | Delay in seconds between failsafe condition being triggered (`COM_RC_LOSS_T`) and failsafe action (RTL, Land, Hold). In this state the vehicle waits in hold mode for the manual control source to reconnect. This might be set longer for long-range flights so that intermittent connection loss doesn't immediately invoke the failsafe. It can be to zero so that the failsafe triggers immediately. | -| [NAV_RCL_ACT](../advanced_config/parameter_reference.md#NAV_RCL_ACT) | Failsafe Action | Disabled, Loiter, Return, Land, Disarm, Terminate. | +| [NAV_RCL_ACT](../advanced_config/parameter_reference.md#NAV_RCL_ACT) | Failsafe Action | Disabled, Loiter, Return, Land, Disarm, Terminate, Hold mode (no failsafe). | | [COM_RCL_EXCEPT](../advanced_config/parameter_reference.md#COM_RCL_EXCEPT) | RC Loss Exceptions | Set modes in which manual control loss is ignored. | +### Hold Mode (No Failsafe) + +The `Hold mode (no failsafe)` action (`NAV_RCL_ACT = 7`) is a special case that does _not_ enter the failsafe. +Instead, if manual control is lost while actively flying a manual mode (such as Position, Altitude, or Stabilized), the vehicle switches to [Hold mode](../flight_modes_mc/hold.md) as if the user had commanded it: there is no failsafe state and no alarming failsafe notification. +This is intended for operations where a switch to Hold on manual control loss is expected and should not be surfaced as a failsafe (for example when operating multiple drones with one GCS). + +If Hold cannot be entered (for example without a valid position estimate), the normal failsafe takes over and escalates from there (Return, Land, Descend, or Terminate as applicable). +Manual control loss in any non-manual (auto/offboard) mode is ignored with this setting. + ## Data Link Loss Failsafe The Data Link Loss failsafe is triggered if the connection to the last MAVLink ground station like QGroundControl is lost. diff --git a/src/modules/commander/Commander.cpp b/src/modules/commander/Commander.cpp index fad9692382..e3a63cc33e 100644 --- a/src/modules/commander/Commander.cpp +++ b/src/modules/commander/Commander.cpp @@ -2027,6 +2027,8 @@ void Commander::run() manualControlCheck(); + manualControlLossModeSwitch(); + offboardControlCheck(); // data link checks which update the status @@ -3145,6 +3147,34 @@ void Commander::manualControlCheck() } } +void Commander::manualControlLossModeSwitch() +{ + // NAV_RCL_ACT value that switches to Hold as a regular mode change instead of triggering the failsafe. + // Kept in sync with gcs_connection_loss_failsafe_mode::Hold_mode_no_failsafe (private to the failsafe). + static constexpr int32_t NAV_RCL_ACT_HOLD_NO_FAILSAFE = 7; + + const bool manual_control_lost = _failsafe_flags.manual_control_signal_lost; + + // Only act on the moment manual control is lost while actively flying a manual mode. Using an edge avoids + // repeatedly overriding the pilot if they command a different mode while manual control stays lost. + if (manual_control_lost && !_manual_control_lost_prev + && isArmed() + && _param_nav_rcl_act.get() == NAV_RCL_ACT_HOLD_NO_FAILSAFE + && _vehicle_control_mode.flag_control_manual_enabled) { + + // Force the switch to Hold as a regular mode change (no failsafe, no alarming notification). + // force=true skips the mode availability check on purpose: if Hold cannot actually run (e.g. without a + // valid position estimate), the failsafe mode-fallback escalates from there (Hold -> RTL -> Land/Descend/Terminate). + _user_mode_intention.change(vehicle_status_s::NAVIGATION_STATE_AUTO_LOITER, ModeChangeSource::User, false, true); + + mavlink_log_info(&_mavlink_log_pub, "Manual control lost: switching to Hold\t"); + events::send(events::ID("commander_rc_loss_hold_no_failsafe"), {events::Log::Info, events::LogInternal::Info}, + "Manual control lost: switching to Hold"); + } + + _manual_control_lost_prev = manual_control_lost; +} + void Commander::offboardControlCheck() { if (_offboard_control_mode_sub.update()) { diff --git a/src/modules/commander/Commander.hpp b/src/modules/commander/Commander.hpp index 59d9559f91..7d6f27a6c4 100644 --- a/src/modules/commander/Commander.hpp +++ b/src/modules/commander/Commander.hpp @@ -147,6 +147,12 @@ private: void manualControlCheck(); + /** + * When NAV_RCL_ACT is set to "Hold mode (no failsafe)", switch from an active manual flight mode to Hold + * on manual control loss as a regular mode change, without triggering the failsafe or an alarming notification. + */ + void manualControlLossModeSwitch(); + void offboardControlCheck(); /** @@ -277,6 +283,7 @@ private: bool _mode_switch_mapped{false}; float _last_manual_throttle{-1.f}; + bool _manual_control_lost_prev{false}; ///< previous manual_control_signal_lost state, for edge detection bool _arm_tune_played{false}; bool _have_taken_off_since_arming{false}; @@ -343,6 +350,7 @@ private: (ParamFloat) _param_com_cpu_max, (ParamBool) _param_com_arm_on_boot, (ParamInt) _param_com_fltmode_boot, - (ParamInt) _param_com_arm_traff + (ParamInt) _param_com_arm_traff, + (ParamInt) _param_nav_rcl_act ) }; diff --git a/src/modules/commander/commander_params.yaml b/src/modules/commander/commander_params.yaml index 8e35eecdc4..bffa75b73f 100644 --- a/src/modules/commander/commander_params.yaml +++ b/src/modules/commander/commander_params.yaml @@ -415,6 +415,12 @@ parameters: long: |- The manual control loss failsafe will only be entered after a timeout, set by COM_RC_LOSS_T in seconds. + + "Hold mode (no failsafe)" does not trigger the failsafe: instead, if manual control + is lost while actively flying a manual mode, the vehicle switches to Hold as a regular + mode change (no failsafe state, no alarming notification). If Hold cannot be entered + (e.g. without a valid position estimate), the normal failsafe takes over and escalates + from there (Return/Land/Descend/Terminate as applicable). type: enum values: 1: Hold mode @@ -422,6 +428,7 @@ parameters: 3: Land mode 5: Terminate 6: Disarm + 7: Hold mode (no failsafe) default: 2 COM_RCL_EXCEPT: description: diff --git a/src/modules/commander/failsafe/failsafe.cpp b/src/modules/commander/failsafe/failsafe.cpp index 60d7e54211..de7e8d24cc 100644 --- a/src/modules/commander/failsafe/failsafe.cpp +++ b/src/modules/commander/failsafe/failsafe.cpp @@ -53,6 +53,10 @@ FailsafeBase::ActionOptions Failsafe::fromNavDllOrRclActParam(int param_value) switch (gcs_connection_loss_failsafe_mode(param_value)) { case gcs_connection_loss_failsafe_mode::Disabled: + + // No failsafe action: for NAV_RCL_ACT this is handled by Commander switching to Hold as a regular + // mode change (see Commander::manualControlLossModeSwitch()). + case gcs_connection_loss_failsafe_mode::Hold_mode_no_failsafe: default: options.action = Action::None; break; diff --git a/src/modules/commander/failsafe/failsafe.h b/src/modules/commander/failsafe/failsafe.h index 8a0e506e50..7b0f07eeed 100644 --- a/src/modules/commander/failsafe/failsafe.h +++ b/src/modules/commander/failsafe/failsafe.h @@ -104,6 +104,7 @@ private: Land_mode = 3, Terminate = 5, Disarm = 6, + Hold_mode_no_failsafe = 7, ///< No failsafe: Commander switches to Hold as a regular mode change (NAV_RCL_ACT only) }; enum class command_after_quadchute : int32_t {