diff --git a/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md b/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md
index c62d09d67e1..a82c3d9ccf5 100644
--- a/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md
+++ b/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md
@@ -92,7 +92,51 @@ The RC RSSI indicator appears when RC signal information is available. It shows
### Gimbal
-The Gimbal indicator is shown when the vehicle supports the [MAVLink Gimbal Protocol](https://mavlink.io/en/services/gimbal_v2.html). It displays active gimbal status and provides access to gimbal controls and settings.
+The Gimbal indicator appears in the toolbar when the vehicle supports the [MAVLink Gimbal Protocol v2](https://mavlink.io/en/services/gimbal_v2.html). It displays the current gimbal status and provides controls for gimbal operation.
+
+#### Gimbal Status Display
+
+The toolbar indicator shows:
+
+* **Pitch angle** - Current pitch position in degrees
+* **Yaw value** - Either local yaw (relative to vehicle heading) or azimuth (absolute earth frame), depending on settings
+* **Yaw Lock status** - Visual indicator showing whether yaw lock is enabled or disabled
+ * **Yaw Lock (Locked icon)** - Gimbal yaw is locked to earth frame (maintains absolute heading)
+ * **Yaw Follow (Follow icon)** - Gimbal yaw follows vehicle body frame (rotates with vehicle)
+
+#### Gimbal Controls
+
+Clicking the gimbal indicator opens a dropdown panel with the following controls:
+
+##### Quick controls
+
+* **Center** - Points the gimbal forward (0° pitch, 0° yaw relative to vehicle)
+* **Tilt 90** - Points the gimbal straight down (-90° pitch)
+* **Point Home** - Points the gimbal towards the home position
+* **Retract** - Sets gimbal to retracted position. This is not supported by all gimbals.
+* **Yaw Lock/Unlock** - More info below
+* **Gimbal Control Acquisition** - More info below
+
+##### Yaw Lock Mode Toggle
+
+Toggle between two yaw control modes:
+
+* **Yaw Lock** - Gimbal maintains a fixed heading relative to earth/north. When the vehicle rotates, the gimbal counter-rotates to maintain its absolute heading.
+* **Yaw Follow** - Gimbal yaw is relative to the vehicle body. When the vehicle rotates, the gimbal rotates with it.
+
+##### Gimbal Control Acquisition
+
+If the option "Show Acquire/Release control button" is set, a new **Acquire/Release Control** button will appear
+* Click to request control from the current operator, or to release control if we have it.
+* Control is typically needed before sending gimbal commands. If not in control, **it will be requested automatically** when sending other commands.
+
+#### Yaw Lock Behavior with Center and Tilt 90 Commands
+
+By default, the Center and Tilt 90 buttons will also set yaw to follow mode. This is the typical user expectation for these commands.
+
+**Example**: If you're in Yaw Lock mode and press "Center", the gimbal will point forward relative to the vehicle (Yaw Follow mode) rather than maintaining its absolute earth heading.
+
+To change this behaviour and preserve last yaw lock status, you can enable the setting "Preserve yaw lock status on tilt 90 and center commands, don't force yaw follow".
### VTOL Transitions
diff --git a/src/Gimbal/GimbalController.cc b/src/Gimbal/GimbalController.cc
index b61e16fc2cf..af2766d1eeb 100644
--- a/src/Gimbal/GimbalController.cc
+++ b/src/Gimbal/GimbalController.cc
@@ -451,7 +451,32 @@ void GimbalController::centerGimbal()
qCCritical(GimbalControllerLog) << "gimbalYawStep: active gimbal is nullptr, returning";
return;
}
- sendPitchBodyYaw(0.0, 0.0, true);
+
+ const bool preserveYawLock = SettingsManager::instance()->gimbalControllerSettings()->preserveYawLockOnPositionCommands()->rawValue().toBool();
+ if (preserveYawLock && _activeGimbal->yawLock()) {
+ // Preserve yaw lock: send command in earth frame with yaw lock flag
+ sendPitchAbsoluteYaw(0.0, _vehicle->heading()->rawValue().toFloat(), true);
+ } else {
+ // Default behavior: send command in body frame (yaw follow)
+ sendPitchBodyYaw(0.0, 0.0, true);
+ }
+}
+
+void GimbalController::tilt90Gimbal()
+{
+ if (!_activeGimbal) {
+ qCDebug(GimbalControllerLog) << "tilt90Gimbal: active gimbal is nullptr, returning";
+ return;
+ }
+
+ const bool preserveYawLock = SettingsManager::instance()->gimbalControllerSettings()->preserveYawLockOnPositionCommands()->rawValue().toBool();
+ if (preserveYawLock && _activeGimbal->yawLock()) {
+ // Preserve yaw lock: send command in earth frame with yaw lock flag
+ sendPitchAbsoluteYaw(-90.0, _activeGimbal->absoluteYaw()->rawValue().toFloat(), true);
+ } else {
+ // Default behavior: send command in body frame (yaw follow)
+ sendPitchBodyYaw(-90.0, 0.0, true);
+ }
}
void GimbalController::gimbalOnScreenControl(float panPct, float tiltPct, bool clickAndPoint, bool clickAndDrag, bool /*rateControl*/, bool /*retract*/, bool /*neutral*/, bool /*yawlock*/)
diff --git a/src/Gimbal/GimbalController.h b/src/Gimbal/GimbalController.h
index 06e7c46fe3d..a6757c3b27e 100644
--- a/src/Gimbal/GimbalController.h
+++ b/src/Gimbal/GimbalController.h
@@ -52,6 +52,7 @@ public slots:
// These slots are conected with joysticks for button control
void gimbalYawLock(bool yawLock) { setGimbalYawLock(yawLock); }
Q_INVOKABLE void centerGimbal();
+ Q_INVOKABLE void tilt90Gimbal();
void gimbalPitchStart(int direction);
void gimbalYawStart(int direction);
void gimbalPitchStop();
diff --git a/src/Settings/GimbalController.SettingsGroup.json b/src/Settings/GimbalController.SettingsGroup.json
index 7f7e34f9e7f..142e8f60556 100644
--- a/src/Settings/GimbalController.SettingsGroup.json
+++ b/src/Settings/GimbalController.SettingsGroup.json
@@ -85,6 +85,13 @@
"default": 60,
"units": "deg/s",
"label": "Minimum gimbal speed for max zoom (deg/sec)"
+ },
+ {
+ "name": "preserveYawLockOnPositionCommands",
+ "shortDesc": "Preserve yaw lock state when using position commands",
+ "longDesc": "When enabled, gimbal position commands will maintain the current yaw lock state instead of automatically switching to yaw follow mode.",
+ "type": "bool",
+ "default": true
}
]
}
diff --git a/src/Settings/GimbalControllerSettings.cc b/src/Settings/GimbalControllerSettings.cc
index 864aec5e3ea..8f2d5bed342 100644
--- a/src/Settings/GimbalControllerSettings.cc
+++ b/src/Settings/GimbalControllerSettings.cc
@@ -33,3 +33,4 @@ DECLARE_SETTINGSFACT(GimbalControllerSettings, toolbarIndicatorShowAcquireReleas
DECLARE_SETTINGSFACT(GimbalControllerSettings, joystickButtonsSpeed)
DECLARE_SETTINGSFACT(GimbalControllerSettings, zoomMaxSpeed)
DECLARE_SETTINGSFACT(GimbalControllerSettings, zoomMinSpeed)
+DECLARE_SETTINGSFACT(GimbalControllerSettings, preserveYawLockOnPositionCommands)
\ No newline at end of file
diff --git a/src/Settings/GimbalControllerSettings.h b/src/Settings/GimbalControllerSettings.h
index 17fa0fe5133..842a0c06af1 100644
--- a/src/Settings/GimbalControllerSettings.h
+++ b/src/Settings/GimbalControllerSettings.h
@@ -24,4 +24,5 @@ class GimbalControllerSettings : public SettingsGroup
DEFINE_SETTINGFACT(joystickButtonsSpeed)
DEFINE_SETTINGFACT(zoomMaxSpeed)
DEFINE_SETTINGFACT(zoomMinSpeed)
+ DEFINE_SETTINGFACT(preserveYawLockOnPositionCommands)
};
diff --git a/src/Toolbar/GimbalIndicator.qml b/src/Toolbar/GimbalIndicator.qml
index df2545b966e..e17d1140886 100644
--- a/src/Toolbar/GimbalIndicator.qml
+++ b/src/Toolbar/GimbalIndicator.qml
@@ -191,7 +191,7 @@ Item {
Layout.fillWidth: true
text: qsTr("Tilt 90")
onClicked: {
- gimbalController.sendPitchBodyYaw(-90, 0)
+ gimbalController.tilt90Gimbal()
mainWindow.closeIndicatorDrawer()
}
}
@@ -311,6 +311,12 @@ Item {
text: qsTr("Show Acquire/Release control button")
fact: _gimbalControllerSettings.toolbarIndicatorShowAcquireReleaseControl
}
+
+ FactCheckBoxSlider {
+ Layout.fillWidth: true
+ text: qsTr("Preserve yaw lock status on tilt 90 and center commands, don't force yaw follow")
+ fact: _gimbalControllerSettings.preserveYawLockOnPositionCommands
+ }
}
}
}