mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-02 10:23:25 +08:00
Copter: migrate ground effect detection to AP_GroundEffect
Replaces Copter's built-in ground-effect detection with the AP_GroundEffect library and wires its AC_PosControl and per-cycle vehicle state. A stored GND_EFFECT_COMP=0 is migrated to GNDEFF_ALT=-1; the enabled default needs no migration. With the default GNDEFF_ALT of 0.5m touchdown_expected now also requires the vehicle to be near the ground (within 0.5m of the takeoff height, and within 20m horizontally of the launch point when no rangefinder is available), where the old code asserted it for any slow descent at any height. GNDEFF_ALT=0 restores the old behaviour.
This commit is contained in:
@@ -609,7 +609,9 @@ void Copter::throttle_loop()
|
||||
#endif
|
||||
|
||||
// compensate for ground effect (if enabled)
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
update_ground_effect_detector();
|
||||
#endif
|
||||
update_ekf_terrain_height_stable();
|
||||
}
|
||||
|
||||
|
||||
+2
-8
@@ -580,14 +580,6 @@ private:
|
||||
int16_t hover_roll_trim_scalar_slew;
|
||||
#endif
|
||||
|
||||
// ground effect detector
|
||||
struct {
|
||||
bool takeoff_expected;
|
||||
bool touchdown_expected;
|
||||
uint32_t takeoff_time_ms;
|
||||
float takeoff_alt_m;
|
||||
} gndeffect_state;
|
||||
|
||||
bool standby_active;
|
||||
|
||||
static const AP_Scheduler::Task scheduler_tasks[];
|
||||
@@ -774,7 +766,9 @@ private:
|
||||
#endif // HAL_ADSB_ENABLED || AP_ADSB_AVOIDANCE_ENABLED
|
||||
|
||||
// baro_ground_effect.cpp
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
void update_ground_effect_detector(void);
|
||||
#endif
|
||||
void update_ekf_terrain_height_stable();
|
||||
|
||||
// commands.cpp
|
||||
|
||||
@@ -629,12 +629,7 @@ const AP_Param::GroupInfo ParametersG2::var_info[] = {
|
||||
AP_GROUPINFO("THROW_TYPE", 4, ParametersG2, throw_type, (float)ModeThrow::ThrowType::Upward),
|
||||
#endif
|
||||
|
||||
// @Param: GND_EFFECT_COMP
|
||||
// @DisplayName: Ground Effect Compensation Enable/Disable
|
||||
// @Description: Ground Effect Compensation Enable/Disable
|
||||
// @Values: 0:Disabled,1:Enabled
|
||||
// @User: Advanced
|
||||
AP_GROUPINFO("GND_EFFECT_COMP", 5, ParametersG2, gndeffect_comp_enabled, 1),
|
||||
// 5 was GND_EFFECT_COMP, folded into AP_GroundEffect GNDEFF_ALT (<0 disables)
|
||||
|
||||
#if AP_COPTER_ADVANCED_FAILSAFE_ENABLED
|
||||
// @Group: AFS_
|
||||
@@ -1070,6 +1065,12 @@ const AP_Param::GroupInfo ParametersG2::var_info2[] = {
|
||||
AP_GROUPINFO("TKOFF_RPM_MAX", 7, ParametersG2, takeoff_rpm_max, 0),
|
||||
#endif
|
||||
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
// @Group: GNDEFF_
|
||||
// @Path: ../libraries/AP_GroundEffect/AP_GroundEffect.cpp
|
||||
AP_SUBGROUPINFO(ground_effect, "GNDEFF_", 24, ParametersG2, AP_GroundEffect),
|
||||
#endif
|
||||
|
||||
// @Param: FS_EKF_FILT
|
||||
// @DisplayName: EKF Failsafe filter cutoff
|
||||
// @Description: EKF Failsafe filter cutoff frequency. EKF variances are filtered using this value to avoid spurious failsafes from transient high variances. A higher value means the failsafe is more likely to trigger.
|
||||
@@ -1355,6 +1356,24 @@ void Copter::load_parameters(void)
|
||||
AP_Param::convert_old_parameters_scaled(pilot_conversion_info, ARRAY_SIZE(pilot_conversion_info), 0.01, 0);
|
||||
}
|
||||
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
// a stored GND_EFFECT_COMP=0 becomes GNDEFF_ALT=-1; the enabled default needs no migration
|
||||
// PARAMETER_CONVERSION - Added: Aug-2026 for Copter-4.8
|
||||
{
|
||||
AP_Int8 old_gndeff;
|
||||
const AP_Param::ConversionInfo info = {
|
||||
Parameters::k_param_g2, 5, AP_PARAM_INT8, nullptr
|
||||
};
|
||||
if (AP_Param::find_old_parameter(&info, &old_gndeff) && old_gndeff.get() == 0) {
|
||||
enum ap_var_type ptype;
|
||||
AP_Param *p = AP_Param::find("GNDEFF_ALT", &ptype);
|
||||
if (p != nullptr && ptype == AP_PARAM_FLOAT && !p->configured()) {
|
||||
((AP_Float *)p)->set_and_save(-1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // AP_GROUNDEFFECT_ENABLED
|
||||
|
||||
// setup AP_Param frame type flags
|
||||
AP_Param::set_frame_type_flags(AP_PARAM_FRAME_COPTER);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class ModeRTL;
|
||||
#if WEATHERVANE_ENABLED
|
||||
#include <AC_AttitudeControl/AC_WeatherVane.h>
|
||||
#endif
|
||||
#include <AP_GroundEffect/AP_GroundEffect.h>
|
||||
|
||||
// Global parameter class.
|
||||
//
|
||||
@@ -543,8 +544,10 @@ public:
|
||||
AP_Enum<ModeThrow::ThrowType> throw_type;
|
||||
#endif
|
||||
|
||||
// ground effect compensation enable/disable
|
||||
AP_Int8 gndeffect_comp_enabled;
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
// ground effect detector
|
||||
AP_GroundEffect ground_effect;
|
||||
#endif
|
||||
|
||||
#if AP_TEMPCALIBRATION_ENABLED
|
||||
// temperature calibration handling
|
||||
|
||||
@@ -1,71 +1,27 @@
|
||||
#include "Copter.h"
|
||||
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
void Copter::update_ground_effect_detector(void)
|
||||
{
|
||||
if(!g2.gndeffect_comp_enabled || !motors->armed()) {
|
||||
// disarmed - disable ground effect and return
|
||||
gndeffect_state.takeoff_expected = false;
|
||||
gndeffect_state.touchdown_expected = false;
|
||||
ahrs.set_takeoff_expected(gndeffect_state.takeoff_expected);
|
||||
ahrs.set_touchdown_expected(gndeffect_state.touchdown_expected);
|
||||
return;
|
||||
AP_GroundEffect &gndeff = g2.ground_effect;
|
||||
|
||||
// throw mode never wants the takeoff expected EKF code
|
||||
gndeff.enable_takeoff_comp(flightmode->mode_number() != Mode::Number::THROW);
|
||||
gndeff.set_high_vibrations(vibration_check.high_vibes);
|
||||
|
||||
// ALT_HOLD has manual attitude and no NE controller, so a near-level
|
||||
// attitude target stands in for "pilot is asking for slow horizontal"
|
||||
bool pilot_slow_horizontal = false;
|
||||
if (flightmode->mode_number() == Mode::Number::ALT_HOLD) {
|
||||
const Vector3f angle_target_rad = attitude_control->get_att_target_euler_rad();
|
||||
pilot_slow_horizontal = cosf(angle_target_rad.x) * cosf(angle_target_rad.y) > cosf(radians(7.5f));
|
||||
}
|
||||
gndeff.set_pilot_demanding_slow_horizontal(pilot_slow_horizontal);
|
||||
|
||||
// variable initialization
|
||||
uint32_t tnow_ms = millis();
|
||||
float des_speed_ne_ms = 0.0f;
|
||||
float des_climb_rate_ms = pos_control->get_vel_desired_U_ms();
|
||||
|
||||
if (pos_control->NE_is_active()) {
|
||||
des_speed_ne_ms = pos_control->get_vel_target_NED_ms().xy().length();
|
||||
}
|
||||
|
||||
// takeoff logic
|
||||
|
||||
if (flightmode->mode_number() == Mode::Number::THROW) {
|
||||
// throw mode never wants the takeoff expected EKF code
|
||||
gndeffect_state.takeoff_expected = false;
|
||||
} else if (motors->armed() && ap.land_complete) {
|
||||
// if we are armed and haven't yet taken off then we expect an imminent takeoff
|
||||
gndeffect_state.takeoff_expected = true;
|
||||
}
|
||||
|
||||
// get altitude estimate
|
||||
float pos_d_m = 0;
|
||||
UNUSED_RESULT(AP::ahrs().get_relative_position_D_origin_float(pos_d_m));
|
||||
|
||||
// if we aren't taking off yet, reset the takeoff timer, altitude and complete flag
|
||||
const bool throttle_up = flightmode->has_manual_throttle() && channel_throttle->get_control_in() > 0;
|
||||
if (!throttle_up && ap.land_complete) {
|
||||
gndeffect_state.takeoff_time_ms = tnow_ms;
|
||||
gndeffect_state.takeoff_alt_m = -pos_d_m;
|
||||
}
|
||||
|
||||
// if we are in takeoff_expected and we meet the conditions for having taken off
|
||||
// end the takeoff_expected state
|
||||
if (gndeffect_state.takeoff_expected && (tnow_ms - gndeffect_state.takeoff_time_ms > 5000 || (-pos_d_m - gndeffect_state.takeoff_alt_m) > 0.50)) {
|
||||
gndeffect_state.takeoff_expected = false;
|
||||
}
|
||||
|
||||
// landing logic
|
||||
Vector3f angle_target_rad = attitude_control->get_att_target_euler_rad();
|
||||
bool small_angle_request = cosf(angle_target_rad.x) * cosf(angle_target_rad.y) > cosf(radians(7.5f));
|
||||
Vector3f vel_ned_ms;
|
||||
bool xy_speed_low = AP::ahrs().get_velocity_NED(vel_ned_ms) && (vel_ned_ms.xy().length() < 1.25);
|
||||
bool xy_speed_demand_low = pos_control->NE_is_active() && des_speed_ne_ms <= 1.25;
|
||||
bool slow_horizontal = xy_speed_demand_low || (xy_speed_low && !pos_control->NE_is_active()) || (flightmode->mode_number() == Mode::Number::ALT_HOLD && small_angle_request);
|
||||
|
||||
bool descent_demanded = pos_control->D_is_active() && des_climb_rate_ms < 0.0f;
|
||||
bool slow_descent_demanded = descent_demanded && des_climb_rate_ms >= -1.00;
|
||||
bool speed_low_d_ms = AP::ahrs().get_velocity_D(vel_ned_ms.z, vibration_check.high_vibes) && fabsf(vel_ned_ms.z) <= 0.6f;
|
||||
bool slow_descent = (slow_descent_demanded || (speed_low_d_ms && descent_demanded));
|
||||
|
||||
gndeffect_state.touchdown_expected = slow_horizontal && slow_descent;
|
||||
|
||||
// Prepare the EKF for ground effect if either takeoff or touchdown is expected.
|
||||
ahrs.set_takeoff_expected(gndeffect_state.takeoff_expected);
|
||||
ahrs.set_touchdown_expected(gndeffect_state.touchdown_expected);
|
||||
gndeff.update(motors->armed(), ap.land_complete, throttle_up);
|
||||
}
|
||||
#endif // AP_GROUNDEFFECT_ENABLED
|
||||
|
||||
// update ekf terrain height stable setting
|
||||
// when set to true, this allows the EKF to stabilize the normally barometer based altitude using a rangefinder
|
||||
|
||||
@@ -457,6 +457,10 @@ void Copter::allocate_motors(void)
|
||||
}
|
||||
AP_Param::load_object_from_eeprom(pos_control, pos_control->var_info);
|
||||
|
||||
#if AP_GROUNDEFFECT_ENABLED
|
||||
g2.ground_effect.set_pos_control(*pos_control);
|
||||
#endif
|
||||
|
||||
#if AP_OAPATHPLANNER_ENABLED
|
||||
wp_nav = NEW_NOTHROW AC_WPNav_OA(*ahrs_view, *pos_control, *attitude_control);
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user