From d86a0c3c04a2ba2de6a4a5238fbe8091d76555cb Mon Sep 17 00:00:00 2001 From: Balduin Date: Mon, 13 Jul 2026 14:19:56 +0200 Subject: [PATCH] feat(vtol_att_control): increase front transition throttle to compensate for weight ratio Currently: - VT_F_TRANS_THR is always the literal throttle used - VT_ARSP_TRANS is scaled by sqrt(weight ratio =: WR) - Transition times (VT_F_TRANS_OL_TM, VT_TRANS_TIMEOUT, VT_TRANS_MIN_TM) are only scaled with air density (^1.5) but not weight This results in quite inconsistent behaviour. A heavier vehicle will: - Need a higher airspeed by sqrt(WR), but throttle and time params are the same - Acceleration decreases by WR assuming same force, and actual time to same airspeed too - So time to reach transition airspeed actually increases by WR^1.5 To fix this: - Scale throttle with sqrt(WR). Assuming quadratic throttle->thrust force mapping, This removes most of the discrepancy and reduces it to a factor of sqrt(WR) - This remaining factor is removed by scaling all transition times with sqrt(WR) as well --- src/modules/vtol_att_control/standard.cpp | 8 ++++-- .../vtol_att_control_params.yaml | 1 + src/modules/vtol_att_control/vtol_type.cpp | 28 +++++++++++++++---- src/modules/vtol_att_control/vtol_type.h | 19 ++++++++++--- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/modules/vtol_att_control/standard.cpp b/src/modules/vtol_att_control/standard.cpp index 0646222f8f74..f99fbe6fa341 100644 --- a/src/modules/vtol_att_control/standard.cpp +++ b/src/modules/vtol_att_control/standard.cpp @@ -204,15 +204,17 @@ void Standard::update_transition_state() roll_body = Eulerf(Quatf(_fw_virtual_att_sp->q_d)).phi(); } + const float transition_throttle = getFrontTransitionThrottle(); + if (_param_vt_psher_slew.get() <= FLT_EPSILON) { // just set the final target throttle value - _pusher_throttle = _param_vt_f_trans_thr.get(); + _pusher_throttle = transition_throttle; - } else if (_pusher_throttle <= _param_vt_f_trans_thr.get()) { + } else if (_pusher_throttle <= transition_throttle) { // ramp up throttle to the target throttle value const float dt = math::min((now - _last_time_pusher_transition_update) / 1e6f, 0.05f); _pusher_throttle = math::min(_pusher_throttle + - _param_vt_psher_slew.get() * dt, _param_vt_f_trans_thr.get()); + _param_vt_psher_slew.get() * dt, transition_throttle); _last_time_pusher_transition_update = now; } diff --git a/src/modules/vtol_att_control/vtol_att_control_params.yaml b/src/modules/vtol_att_control/vtol_att_control_params.yaml index 206b2a0f6491..5c433cd60e35 100644 --- a/src/modules/vtol_att_control/vtol_att_control_params.yaml +++ b/src/modules/vtol_att_control/vtol_att_control_params.yaml @@ -46,6 +46,7 @@ parameters: VT_F_TRANS_THR: description: short: Target throttle value for the transition to fixed-wing flight + long: Scaled by the square root of the weight ratio (WEIGHT_GROSS / WEIGHT_BASE). type: float default: 1.0 min: 0.0 diff --git a/src/modules/vtol_att_control/vtol_type.cpp b/src/modules/vtol_att_control/vtol_type.cpp index 8195df5f8b3a..cf3a25011703 100644 --- a/src/modules/vtol_att_control/vtol_type.cpp +++ b/src/modules/vtol_att_control/vtol_type.cpp @@ -539,21 +539,26 @@ float VtolType::pusher_assist() float VtolType::getFrontTransitionTimeFactor() const { - // assumptions: transition_time = transition_true_airspeed / average_acceleration (thrust) - // transition_true_airspeed ~ sqrt(rho0 / rh0) + // assumptions: + // transition_time = transition_true_airspeed / average_acceleration (thrust) + // transition_true_airspeed ~ sqrt(rho0 / rh0) * sqrt(weight_ratio) // average_acceleration ~ rho / rho0 - // transition_time ~ sqrt(rho0/rh0) * rho0 / rho + // independent of weight: standard VTOL throttle is weight scaled, + // tiltrotor and tailsitter weight scale via hover thrust + // transition_time ~ sqrt(rho0/rh0) * rho0 / rho * sqrt(weight_ratio) // low value: hot day at 4000m AMSL with some margin // high value: cold day at 0m AMSL with some margin const float rho = math::constrain(_attc->getAirDensity(), 0.7f, 1.5f); + float density_factor = 1.0f; + if (PX4_ISFINITE(rho)) { float rho0_over_rho = atmosphere::kAirDensitySeaLevelStandardAtmos / rho; - return sqrtf(rho0_over_rho) * rho0_over_rho; + density_factor = sqrtf(rho0_over_rho) * rho0_over_rho; } - return 1.0f; + return density_factor * sqrtf(getWeightRatio()); } float VtolType::getMinimumFrontTransitionTime() const @@ -574,7 +579,18 @@ float VtolType::getTransitionAirspeed() const { // Since the stall airspeed increases with vehicle weight, we increase the transition airspeed // by the same factor. + return sqrtf(getWeightRatio()) * _param_vt_arsp_trans.get(); +} + +float VtolType::getFrontTransitionThrottle() const +{ + // Thrust needs to scale with weight_ratio to keep the acceleration weight-independent. Assuming + // a quadratic throttle -> thrust map, this requires scaling the throttle by sqrt(weight_ratio). + return math::min(sqrtf(getWeightRatio()) * _param_vt_f_trans_thr.get(), 1.0f); +} +float VtolType::getWeightRatio() const +{ float weight_ratio = 1.0f; if (_param_weight_base.get() > FLT_EPSILON && _param_weight_gross.get() > FLT_EPSILON) { @@ -582,7 +598,7 @@ float VtolType::getTransitionAirspeed() const _param_weight_base.get(), kMinWeightRatio, kMaxWeightRatio); } - return sqrtf(weight_ratio) * _param_vt_arsp_trans.get(); + return weight_ratio; } float VtolType::getBlendAirspeed() const diff --git a/src/modules/vtol_att_control/vtol_type.h b/src/modules/vtol_att_control/vtol_type.h index 1709753bf3f7..72ff78cae90d 100644 --- a/src/modules/vtol_att_control/vtol_type.h +++ b/src/modules/vtol_att_control/vtol_type.h @@ -223,17 +223,17 @@ class VtolType : public ModuleParams mode get_mode() {return _common_vtol_mode;} /** - * @return Minimum front transition time scaled for air density (if available) [s] + * @return Minimum front transition time scaled for air density (if available) and vehicle weight [s] */ float getMinimumFrontTransitionTime() const; /** - * @return Front transition timeout scaled for air density (if available) [s] + * @return Front transition timeout scaled for air density (if available) and vehicle weight [s] */ float getFrontTransitionTimeout() const; /** - * @return Minimum open-loop front transition time scaled for air density (if available) [s] + * @return Minimum open-loop front transition time scaled for air density (if available) and vehicle weight [s] */ float getOpenLoopFrontTransitionTime() const; @@ -249,6 +249,12 @@ class VtolType : public ModuleParams */ float getTransitionAirspeed() const; + /** + * + * @return The target front transition throttle scaled for vehicle weight + */ + float getFrontTransitionThrottle() const; + virtual void parameters_update() = 0; @@ -366,10 +372,15 @@ class VtolType : public ModuleParams void stopBlendingThrottleAfterFrontTransition() { _throttle_blend_start_ts = 0; } /** - * @return Transition time scale factor for density. + * @return Transition time scale factor for density and vehicle weight. */ float getFrontTransitionTimeFactor() const; + /** + * @return Gross weight over base weight ratio, constrained to sane limits. + */ + float getWeightRatio() const; + }; #endif