@@ -131,25 +131,74 @@ bool adjustFixedWingAltitudeFromRCInput(void)
131131// Position to velocity controller for Z axis
132132static void updateAltitudeVelocityAndPitchController_FW (timeDelta_t deltaMicros )
133133{
134- static pt1Filter_t velzFilterState ;
134+ static pt1Filter_t pitchFilterState ;
135135
136136 float desiredClimbRate = getDesiredClimbRate (posControl .desiredState .pos .z , deltaMicros );
137137
138138 // Reduce max allowed climb rate by 2/3 if performing loiter (stall prevention)
139- if (needToCalculateCircularLoiter && desiredClimbRate > 0.67f * navConfig ()-> fw .max_auto_climb_rate ) {
139+ if (navGetCurrentStateFlags () & NAV_CTL_HOLD && desiredClimbRate > 0.67f * navConfig ()-> fw .max_auto_climb_rate ) {
140140 desiredClimbRate = 0.67f * navConfig ()-> fw .max_auto_climb_rate ;
141141 }
142142
143143 // Here we use negative values for dive for better clarity
144144 const float maxClimbDeciDeg = DEGREES_TO_DECIDEGREES (navConfig ()-> fw .max_climb_angle );
145145 const float minDiveDeciDeg = - DEGREES_TO_DECIDEGREES (navConfig ()-> fw .max_dive_angle );
146146
147- // PID controller to translate desired climb rate error into pitch angle [decideg]
148- float currentClimbRate = navGetCurrentActualPositionAndVelocity ()-> vel .z ;
149- float targetPitchAngle = navPidApply2 (& posControl .pids .fw_alt , desiredClimbRate , currentClimbRate , US2S (deltaMicros ), minDiveDeciDeg , maxClimbDeciDeg , PID_DTERM_FROM_ERROR );
147+ // Default control based on climb rate (velocity)
148+ float targetValue = desiredClimbRate ;
149+ float measuredValue = navGetCurrentActualPositionAndVelocity ()-> vel .z ;
150+
151+ // Optional control based on altitude (position)
152+ if (pidProfile ()-> fwAltControlUsePos ) {
153+ static float currentDesiredPosZ = 0.0f ;
154+ static float trackingAltitude = 0.0f ;
155+ static bool altitudeRateControlActive = false;
156+
157+ float desiredAltitude = posControl .desiredState .pos .z ;
158+ float currentAltitude = navGetCurrentActualPositionAndVelocity ()-> pos .z ;
159+
160+ /* Determine if altitude rate control required based on magnitude of change in target altitude.
161+ * No rate control used during trackback to allow max climb rates based on pitch limits */
162+ if (fabsf (currentDesiredPosZ - desiredAltitude ) > 100.0f || !isPitchAdjustmentValid ) {
163+ altitudeRateControlActive = desiredClimbRate && fabsf (desiredAltitude - currentAltitude ) > navConfig ()-> fw .max_auto_climb_rate &&
164+ !posControl .flags .rthTrackbackActive ;
165+ trackingAltitude = currentAltitude ;
166+ }
167+ currentDesiredPosZ = desiredAltitude ;
168+
169+ if (posControl .flags .rocToAltMode == ROC_TO_ALT_CONSTANT || altitudeRateControlActive ) {
170+ /* Adjustment factor based on vertical acceleration used to better control altitude position change
171+ * driving vertical velocity control. Helps avoid lag induced overcontrol by PID loop. */
172+ static float absLastClimbRate = 0.0f ;
173+ float absClimbRate = fabsf (measuredValue );
174+ float accelerationZ = (absClimbRate - absLastClimbRate ) / US2S (deltaMicros );
175+ absLastClimbRate = absClimbRate ;
176+ float adjustmentFactor = constrainf (scaleRangef (accelerationZ , 0.0f , 200.0f , 1.0f , 0.0f ), 0.0f , 1.0f );
177+
178+ if (posControl .flags .rocToAltMode == ROC_TO_ALT_CONSTANT ) {
179+ posControl .desiredState .pos .z += adjustmentFactor * desiredClimbRate * US2S (deltaMicros );
180+ desiredAltitude = posControl .desiredState .pos .z ;
181+ } else {
182+ /* Disable rate control if no longer required, i.e. remaining altitude change is small */
183+ altitudeRateControlActive = fabsf (desiredAltitude - currentAltitude ) > MAX (fabsf (desiredClimbRate ), 50.0f );
184+
185+ /* Tracking altitude used to control altitude rate where changing posControl.desiredState.pos.z not possible */
186+ trackingAltitude += adjustmentFactor * desiredClimbRate * US2S (deltaMicros );
187+ desiredAltitude = trackingAltitude ;
188+ }
189+ } else {
190+ desiredClimbRate = 0 ;
191+ }
192+
193+ targetValue = desiredAltitude ;
194+ measuredValue = currentAltitude ;
195+ }
196+
197+ // PID controller to translate desired target error (velocity or position) into pitch angle [decideg]
198+ float targetPitchAngle = navPidApply2 (& posControl .pids .fw_alt , targetValue , measuredValue , US2S (deltaMicros ), minDiveDeciDeg , maxClimbDeciDeg , 0 );
150199
151200 // Apply low-pass filter to prevent rapid correction
152- targetPitchAngle = pt1FilterApply4 (& velzFilterState , targetPitchAngle , getSmoothnessCutoffFreq (NAV_FW_BASE_PITCH_CUTOFF_FREQUENCY_HZ ), US2S (deltaMicros ));
201+ targetPitchAngle = pt1FilterApply4 (& pitchFilterState , targetPitchAngle , getSmoothnessCutoffFreq (NAV_FW_BASE_PITCH_CUTOFF_FREQUENCY_HZ ), US2S (deltaMicros ));
153202
154203 // Reconstrain pitch angle (> 0 - climb, < 0 - dive)
155204 targetPitchAngle = constrainf (targetPitchAngle , minDiveDeciDeg , maxClimbDeciDeg );
@@ -171,6 +220,7 @@ void applyFixedWingAltitudeAndThrottleController(timeUs_t currentTimeUs)
171220 // Check if last correction was not too long ago
172221 if (deltaMicrosPositionUpdate < MAX_POSITION_UPDATE_INTERVAL_US ) {
173222 updateAltitudeVelocityAndPitchController_FW (deltaMicrosPositionUpdate );
223+ isPitchAdjustmentValid = true;
174224 }
175225 else {
176226 // Position update has not occurred in time (first iteration or glitch), reset altitude controller
@@ -180,8 +230,6 @@ void applyFixedWingAltitudeAndThrottleController(timeUs_t currentTimeUs)
180230 // Indicate that information is no longer usable
181231 posControl .flags .verticalPositionDataConsumed = true;
182232 }
183-
184- isPitchAdjustmentValid = true;
185233 }
186234 else {
187235 // No valid altitude sensor data, don't adjust pitch automatically, rcCommand[PITCH] is passed through to PID controller
0 commit comments