land_detector: multicopter ground contact fixes, improvements, and cleanup

- if "landed" and "maybe_landed" states are false then both the "hit_ground" and the "low_thrust" condition need to be true in order to detect landing
 - ground contact MC NAN setpoint workaround
 - ground contact additionally check acceleration setpoint
 - schedule with vehicle_local_position updates (most updates require valid local position)
 - don't allow LNDMC_Z_VEL_MAX to exceed MPC_LAND_SPEED
 - ground contact horizontal movement checks default to failed if estimates aren't available
This commit is contained in:
Daniel Agar 2020-08-02 13:37:23 -04:00 committed by GitHub
parent 63a23957b1
commit f5f2897486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 223 additions and 321 deletions

View File

@ -1575,7 +1575,6 @@ Commander::run()
/* Update land detector */
if (_land_detector_sub.updated()) {
_was_landed = _land_detector.landed;
bool was_falling = _land_detector.freefall;
_land_detector_sub.copy(&_land_detector);
// Only take actions if armed
@ -1596,12 +1595,6 @@ Commander::run()
_lvel_probation_time_us = _param_com_pos_fs_prob.get() * 1_s;
}
}
if (was_falling != _land_detector.freefall) {
if (_land_detector.freefall) {
mavlink_log_info(&mavlink_log_pub, "Freefall detected");
}
}
}
}

View File

@ -51,16 +51,10 @@ FixedwingLandDetector::FixedwingLandDetector()
_landed_hysteresis.set_hysteresis_time_from(true, FLYING_TRIGGER_TIME_US);
}
void FixedwingLandDetector::_update_topics()
{
LandDetector::_update_topics();
_airspeed_validated_sub.update(&_airspeed_validated);
}
bool FixedwingLandDetector::_get_landed_state()
{
// Only trigger flight conditions if we are armed.
if (!_actuator_armed.armed) {
if (!_armed) {
return true;
}
@ -83,19 +77,20 @@ bool FixedwingLandDetector::_get_landed_state()
_velocity_z_filtered = val;
}
airspeed_validated_s airspeed_validated{};
_airspeed_validated_sub.copy(&airspeed_validated);
// set _airspeed_filtered to 0 if airspeed data is invalid
if (!PX4_ISFINITE(_airspeed_validated.true_airspeed_m_s) || hrt_elapsed_time(&_airspeed_validated.timestamp) > 1_s) {
if (!PX4_ISFINITE(airspeed_validated.true_airspeed_m_s) || hrt_elapsed_time(&airspeed_validated.timestamp) > 1_s) {
_airspeed_filtered = 0.0f;
} else {
_airspeed_filtered = 0.95f * _airspeed_filtered + 0.05f * _airspeed_validated.true_airspeed_m_s;
_airspeed_filtered = 0.95f * _airspeed_filtered + 0.05f * airspeed_validated.true_airspeed_m_s;
}
// A leaking lowpass prevents biases from building up, but
// gives a mostly correct response for short impulses.
const matrix::Vector3f accel{_vehicle_acceleration.xyz};
const float acc_hor = sqrtf(accel(0) * accel(0) + accel(1) * accel(1));
const float acc_hor = matrix::Vector2f(_acceleration).norm();
_xy_accel_filtered = _xy_accel_filtered * 0.8f + acc_hor * 0.18f;
// Crude land detector for fixedwing.

View File

@ -61,7 +61,6 @@ public:
protected:
bool _get_landed_state() override;
void _update_topics() override;
private:
@ -71,8 +70,6 @@ private:
uORB::Subscription _airspeed_validated_sub{ORB_ID(airspeed_validated)};
airspeed_validated_s _airspeed_validated{};
float _airspeed_filtered{0.0f};
float _velocity_xy_filtered{0.0f};
float _velocity_z_filtered{0.0f};

View File

@ -57,21 +57,52 @@ LandDetector::~LandDetector()
void LandDetector::start()
{
_update_params();
ScheduleOnInterval(LAND_DETECTOR_UPDATE_INTERVAL);
ScheduleDelayed(50_ms);
_vehicle_local_position_sub.registerCallback();
}
void LandDetector::Run()
{
// push backup schedule
ScheduleDelayed(50_ms);
perf_begin(_cycle_perf);
if (_parameter_update_sub.updated()) {
if (_parameter_update_sub.updated() || (_land_detected.timestamp == 0)) {
parameter_update_s param_update;
_parameter_update_sub.copy(&param_update);
updateParams();
_update_params();
_total_flight_time = static_cast<uint64_t>(_param_total_flight_time_high.get()) << 32;
_total_flight_time |= static_cast<uint32_t>(_param_total_flight_time_low.get());
}
_actuator_armed_sub.update(&_actuator_armed);
actuator_armed_s actuator_armed;
if (_actuator_armed_sub.update(&actuator_armed)) {
_armed = actuator_armed.armed;
}
vehicle_acceleration_s vehicle_acceleration;
if (_vehicle_acceleration_sub.update(&vehicle_acceleration)) {
_acceleration = matrix::Vector3f{vehicle_acceleration.xyz};
}
_vehicle_local_position_sub.update(&_vehicle_local_position);
_vehicle_status_sub.update(&_vehicle_status);
_update_topics();
_update_state();
const hrt_abstime now_us = hrt_absolute_time();
_freefall_hysteresis.set_state_and_update(_get_freefall_state(), now_us);
_ground_contact_hysteresis.set_state_and_update(_get_ground_contact_state(), now_us);
_maybe_landed_hysteresis.set_state_and_update(_get_maybe_landed_state(), now_us);
_landed_hysteresis.set_state_and_update(_get_landed_state(), now_us);
_ground_effect_hysteresis.set_state_and_update(_get_ground_effect_state(), now_us);
const bool freefallDetected = _freefall_hysteresis.get_state();
const bool ground_contactDetected = _ground_contact_hysteresis.get_state();
@ -80,8 +111,6 @@ void LandDetector::Run()
const float alt_max = _get_max_altitude() > 0.0f ? _get_max_altitude() : INFINITY;
const bool in_ground_effect = _ground_effect_hysteresis.get_state();
const hrt_abstime now = hrt_absolute_time();
// publish at 1 Hz, very first time, or when the result has changed
if ((hrt_elapsed_time(&_land_detected.timestamp) >= 1_s) ||
(_land_detected.landed != landDetected) ||
@ -93,24 +122,23 @@ void LandDetector::Run()
if (!landDetected && _land_detected.landed && _takeoff_time == 0) { /* only set take off time once, until disarming */
// We did take off
_takeoff_time = now;
_takeoff_time = now_us;
}
_land_detected.timestamp = hrt_absolute_time();
_land_detected.landed = landDetected;
_land_detected.freefall = freefallDetected;
_land_detected.maybe_landed = maybe_landedDetected;
_land_detected.ground_contact = ground_contactDetected;
_land_detected.alt_max = alt_max;
_land_detected.in_ground_effect = in_ground_effect;
_land_detected.timestamp = hrt_absolute_time();
_vehicle_land_detected_pub.publish(_land_detected);
}
// set the flight time when disarming (not necessarily when landed, because all param changes should
// happen on the same event and it's better to set/save params while not in armed state)
if (_takeoff_time != 0 && !_actuator_armed.armed && _previous_armed_state) {
_total_flight_time += now - _takeoff_time;
if (_takeoff_time != 0 && !_armed && _previous_armed_state) {
_total_flight_time += now_us - _takeoff_time;
_takeoff_time = 0;
uint32_t flight_time = (_total_flight_time >> 32) & 0xffffffff;
@ -124,7 +152,7 @@ void LandDetector::Run()
_param_total_flight_time_low.commit_no_notification();
}
_previous_armed_state = _actuator_armed.armed;
_previous_armed_state = _armed;
perf_end(_cycle_perf);
@ -134,36 +162,4 @@ void LandDetector::Run()
}
}
void LandDetector::_update_params()
{
parameter_update_s param_update;
_parameter_update_sub.copy(&param_update);
updateParams();
_update_total_flight_time();
}
void LandDetector::_update_state()
{
const hrt_abstime now_us = hrt_absolute_time();
_freefall_hysteresis.set_state_and_update(_get_freefall_state(), now_us);
_ground_contact_hysteresis.set_state_and_update(_get_ground_contact_state(), now_us);
_maybe_landed_hysteresis.set_state_and_update(_get_maybe_landed_state(), now_us);
_landed_hysteresis.set_state_and_update(_get_landed_state(), now_us);
_ground_effect_hysteresis.set_state_and_update(_get_ground_effect_state(), now_us);
}
void LandDetector::_update_topics()
{
_actuator_armed_sub.update(&_actuator_armed);
_vehicle_acceleration_sub.update(&_vehicle_acceleration);
_vehicle_local_position_sub.update(&_vehicle_local_position);
}
void LandDetector::_update_total_flight_time()
{
_total_flight_time = static_cast<uint64_t>(_param_total_flight_time_high.get()) << 32;
_total_flight_time |= static_cast<uint32_t>(_param_total_flight_time_low.get());
}
} // namespace land_detector

View File

@ -56,11 +56,13 @@
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/topics/actuator_armed.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_acceleration.h>
#include <uORB/topics/vehicle_land_detected.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_status.h>
using namespace time_literals;
@ -70,11 +72,9 @@ namespace land_detector
class LandDetector : public ModuleBase<LandDetector>, ModuleParams, px4::ScheduledWorkItem
{
public:
LandDetector();
virtual ~LandDetector();
/** @see ModuleBase */
static int custom_command(int argc, char *argv[])
{
@ -99,12 +99,12 @@ protected:
/**
* Updates parameters.
*/
virtual void _update_params();
virtual void _update_params() {};
/**
* Updates subscribed uORB topics.
*/
virtual void _update_topics();
virtual void _update_topics() {};
/**
* @return true if UAV is in a landed state.
@ -136,17 +136,22 @@ protected:
*/
virtual bool _get_ground_effect_state() { return false; }
/** Run main land detector loop at this interval. */
static constexpr uint32_t LAND_DETECTOR_UPDATE_INTERVAL = 20_ms;
systemlib::Hysteresis _freefall_hysteresis{false};
systemlib::Hysteresis _landed_hysteresis{true};
systemlib::Hysteresis _maybe_landed_hysteresis{true};
systemlib::Hysteresis _ground_contact_hysteresis{true};
systemlib::Hysteresis _ground_effect_hysteresis{false};
actuator_armed_s _actuator_armed{};
vehicle_acceleration_s _vehicle_acceleration{};
vehicle_local_position_s _vehicle_local_position{};
vehicle_status_s _vehicle_status{};
matrix::Vector3f _acceleration{};
bool _armed{false};
bool _previous_armed_state{false}; ///< stores the previous actuator_armed.armed state
private:
void Run() override;
vehicle_land_detected_s _land_detected = {
.timestamp = 0,
@ -157,29 +162,19 @@ protected:
.landed = true,
};
vehicle_local_position_s _vehicle_local_position{};
uORB::Publication<vehicle_land_detected_s> _vehicle_land_detected_pub{ORB_ID(vehicle_land_detected)};
private:
void Run() override;
void _update_state();
void _update_total_flight_time();
bool _previous_armed_state{false}; ///< stores the previous actuator_armed.armed state
hrt_abstime _takeoff_time{0};
hrt_abstime _total_flight_time{0}; ///< total vehicle flight time in microseconds
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, "land_detector_cycle")};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")};
uORB::Publication<vehicle_land_detected_s> _vehicle_land_detected_pub{ORB_ID(vehicle_land_detected)};
uORB::Subscription _actuator_armed_sub{ORB_ID(actuator_armed)};
uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)};
uORB::Subscription _vehicle_acceleration_sub{ORB_ID(vehicle_acceleration)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::SubscriptionCallbackWorkItem _vehicle_local_position_sub{this, ORB_ID(vehicle_local_position)};
DEFINE_PARAMETERS_CUSTOM_PARENT(
ModuleParams,

View File

@ -67,32 +67,40 @@
#include "MulticopterLandDetector.h"
using matrix::Vector2f;
using matrix::Vector3f;
namespace land_detector
{
MulticopterLandDetector::MulticopterLandDetector()
{
_paramHandle.landSpeed = param_find("MPC_LAND_SPEED");
_paramHandle.landSpeed = param_find("MPC_LAND_SPEED");
_paramHandle.minManThrottle = param_find("MPC_MANTHR_MIN");
_paramHandle.minThrottle = param_find("MPC_THR_MIN");
_paramHandle.minThrottle = param_find("MPC_THR_MIN");
_paramHandle.useHoverThrustEstimate = param_find("MPC_USE_HTE");
_paramHandle.hoverThrottle = param_find("MPC_THR_HOVER");
_paramHandle.hoverThrottle = param_find("MPC_THR_HOVER");
// Use Trigger time when transitioning from in-air (false) to landed (true) / ground contact (true).
_freefall_hysteresis.set_hysteresis_time_from(false, FREEFALL_TRIGGER_TIME_US);
_ground_contact_hysteresis.set_hysteresis_time_from(false, GROUND_CONTACT_TRIGGER_TIME_US);
_landed_hysteresis.set_hysteresis_time_from(false, LAND_DETECTOR_TRIGGER_TIME_US);
_maybe_landed_hysteresis.set_hysteresis_time_from(false, MAYBE_LAND_DETECTOR_TRIGGER_TIME_US);
_landed_hysteresis.set_hysteresis_time_from(false, LAND_DETECTOR_TRIGGER_TIME_US);
}
void MulticopterLandDetector::_update_topics()
{
LandDetector::_update_topics();
actuator_controls_s actuator_controls;
_actuator_controls_sub.update(&_actuator_controls);
_vehicle_angular_velocity_sub.update(&_vehicle_angular_velocity);
_vehicle_control_mode_sub.update(&_vehicle_control_mode);
_vehicle_local_position_setpoint_sub.update(&_vehicle_local_position_setpoint);
if (_actuator_controls_sub.update(&actuator_controls)) {
_actuator_controls_throttle = actuator_controls.control[actuator_controls_s::INDEX_THROTTLE];
}
vehicle_control_mode_s vehicle_control_mode;
if (_vehicle_control_mode_sub.update(&vehicle_control_mode)) {
_flag_control_climb_rate_enabled = vehicle_control_mode.flag_control_climb_rate_enabled;
}
if (_params.useHoverThrustEstimate) {
hover_thrust_estimate_s hte;
@ -105,14 +113,18 @@ void MulticopterLandDetector::_update_topics()
void MulticopterLandDetector::_update_params()
{
LandDetector::_update_params();
_freefall_hysteresis.set_hysteresis_time_from(false, (hrt_abstime)(1e6f * _param_lndmc_ffall_ttri.get()));
param_get(_paramHandle.minThrottle, &_params.minThrottle);
param_get(_paramHandle.minManThrottle, &_params.minManThrottle);
param_get(_paramHandle.landSpeed, &_params.landSpeed);
if (_param_lndmc_z_vel_max.get() > _params.landSpeed) {
PX4_ERR("LNDMC_Z_VEL_MAX > MPC_LAND_SPEED, updating %.3f -> %.3f",
(double)_param_lndmc_z_vel_max.get(), (double)_params.landSpeed);
_param_lndmc_z_vel_max.set(_params.landSpeed);
_param_lndmc_z_vel_max.commit_no_notification();
}
int32_t use_hover_thrust_estimate = 0;
param_get(_paramHandle.useHoverThrustEstimate, &use_hover_thrust_estimate);
_params.useHoverThrustEstimate = (use_hover_thrust_estimate == 1);
@ -131,82 +143,120 @@ void MulticopterLandDetector::_update_params()
bool MulticopterLandDetector::_get_freefall_state()
{
if (_param_lndmc_ffall_thr.get() < 0.1f ||
_param_lndmc_ffall_thr.get() > 10.0f) { //if parameter is set to zero or invalid, disable free-fall detection.
return false;
}
if (_vehicle_acceleration.timestamp == 0) {
// _sensors is not valid yet, we have to assume we're not falling.
return false;
}
// norm of specific force. Should be close to 9.8 m/s^2 when landed.
const matrix::Vector3f accel{_vehicle_acceleration.xyz};
return (accel.norm() < _param_lndmc_ffall_thr.get()); // true if we are currently falling
return _acceleration.norm() < 2.f;
}
bool MulticopterLandDetector::_get_ground_contact_state()
{
// When not armed, consider to have ground-contact
if (!_actuator_armed.armed) {
return true;
const bool lpos_available = (hrt_elapsed_time(&_vehicle_local_position.timestamp) < 1_s);
// land speed threshold, 90% of MPC_LAND_SPEED
const float land_speed_threshold = 0.9f * math::max(_params.landSpeed, 0.1f);
bool vertical_movement = true;
if (lpos_available && _vehicle_local_position.v_z_valid) {
// Check if we are moving vertically - this might see a spike after arming due to
// throttle-up vibration. If accelerating fast the throttle thresholds will still give
// an accurate in-air indication.
float max_climb_rate = math::min(land_speed_threshold * 0.5f, _param_lndmc_z_vel_max.get());
if (hrt_elapsed_time(&_landed_time) < LAND_DETECTOR_LAND_PHASE_TIME_US) {
// Widen acceptance thresholds for landed state right after arming
// so that motor spool-up and other effects do not trigger false negatives.
max_climb_rate = _param_lndmc_z_vel_max.get() * 2.5f;
}
vertical_movement = (fabsf(_vehicle_local_position.vz) > max_climb_rate);
}
// land speed threshold
float land_speed_threshold = 0.9f * math::max(_params.landSpeed, 0.1f);
// Check if we are moving vertically - this might see a spike after arming due to
// throttle-up vibration. If accelerating fast the throttle thresholds will still give
// an accurate in-air indication.
bool vertical_movement = false;
if (hrt_elapsed_time(&_landed_time) < LAND_DETECTOR_LAND_PHASE_TIME_US) {
// Widen acceptance thresholds for landed state right after arming
// so that motor spool-up and other effects do not trigger false negatives.
vertical_movement = fabsf(_vehicle_local_position.vz) > _param_lndmc_z_vel_max.get() * 2.5f;
} else {
// Adjust max_climb_rate if land_speed is lower than 2x max_climb_rate
float max_climb_rate = ((land_speed_threshold * 0.5f) < _param_lndmc_z_vel_max.get()) ? (0.5f * land_speed_threshold) :
_param_lndmc_z_vel_max.get();
vertical_movement = fabsf(_vehicle_local_position.vz) > max_climb_rate;
}
// Check if we are moving horizontally.
_horizontal_movement = sqrtf(_vehicle_local_position.vx * _vehicle_local_position.vx
+ _vehicle_local_position.vy * _vehicle_local_position.vy) > _param_lndmc_xy_vel_max.get();
if (lpos_available && _vehicle_local_position.v_xy_valid) {
const Vector2f v_xy{_vehicle_local_position.vx, _vehicle_local_position.vy};
_horizontal_movement = v_xy.longerThan(_param_lndmc_xy_vel_max.get());
} else {
_horizontal_movement = false; // not known
}
// low thrust: 30% of throttle range between min and hover
const float sys_low_throttle = _params.minThrottle + (_params.hoverThrottle - _params.minThrottle) * 0.3f;
bool ground_contact = (_actuator_controls_throttle <= sys_low_throttle);
// if we have a valid velocity setpoint and the vehicle is demanded to go down but no vertical movement present,
// we then can assume that the vehicle hit ground
_in_descend = _is_climb_rate_enabled()
&& (_vehicle_local_position_setpoint.vz >= land_speed_threshold);
bool hit_ground = _in_descend && !vertical_movement;
if (_flag_control_climb_rate_enabled) {
vehicle_local_position_setpoint_s vehicle_local_position_setpoint;
if (_vehicle_local_position_setpoint_sub.update(&vehicle_local_position_setpoint)) {
// setpoints can briefly be NAN to signal resets, TODO: fix in multicopter position controller
const bool descend_vel_sp = PX4_ISFINITE(vehicle_local_position_setpoint.vz)
&& (vehicle_local_position_setpoint.vz >= land_speed_threshold);
const bool descend_acc_sp = PX4_ISFINITE(vehicle_local_position_setpoint.acceleration[2])
&& (vehicle_local_position_setpoint.acceleration[2] >= 100.f);
_in_descend = descend_vel_sp || descend_acc_sp;
}
// ground contact requires commanded descent until landed
if (!_maybe_landed_hysteresis.get_state() && !_landed_hysteresis.get_state()) {
ground_contact &= _in_descend;
}
} else {
_in_descend = false;
}
// When not armed, consider to have ground-contact
if (!_armed) {
return true;
}
// TODO: we need an accelerometer based check for vertical movement for flying without GPS
return (_has_low_thrust() || hit_ground) && (!_horizontal_movement || !_has_position_lock())
&& (!vertical_movement || !_has_altitude_lock());
return ground_contact && !_horizontal_movement && !vertical_movement;
}
bool MulticopterLandDetector::_get_maybe_landed_state()
{
// When not armed, consider to be maybe-landed
if (!_actuator_armed.armed) {
if (!_armed) {
return true;
}
if (_has_minimal_thrust()) {
if (_min_trust_start == 0) {
_min_trust_start = hrt_absolute_time();
// minimal throttle: initially 10% of throttle range between min and hover
float sys_min_throttle = _params.minThrottle + (_params.hoverThrottle - _params.minThrottle) * 0.1f;
// Determine the system min throttle based on flight mode
if (!_flag_control_climb_rate_enabled) {
sys_min_throttle = (_params.minManThrottle + 0.01f);
}
// Check if thrust output is less than the minimum throttle.
if (_actuator_controls_throttle <= sys_min_throttle) {
if (_min_thrust_start == 0) {
_min_thrust_start = hrt_absolute_time();
}
} else {
_min_trust_start = 0;
_min_thrust_start = 0;
return false;
}
float landThresholdFactor = 1.0f;
if (_freefall_hysteresis.get_state()) {
return false;
}
float landThresholdFactor = 1.f;
// Widen acceptance thresholds for landed state right after landed
if (hrt_elapsed_time(&_landed_time) < LAND_DETECTOR_LAND_PHASE_TIME_US) {
@ -214,32 +264,26 @@ bool MulticopterLandDetector::_get_maybe_landed_state()
}
// Next look if all rotation angles are not moving.
float max_rotation_scaled = math::radians(_param_lndmc_rot_max.get()) * landThresholdFactor;
vehicle_angular_velocity_s vehicle_angular_velocity{};
_vehicle_angular_velocity_sub.copy(&vehicle_angular_velocity);
const Vector2f angular_velocity{vehicle_angular_velocity.xyz[0], vehicle_angular_velocity.xyz[1]};
const float max_rotation_scaled = math::radians(_param_lndmc_rot_max.get()) * landThresholdFactor;
bool rotating = (fabsf(_vehicle_angular_velocity.xyz[0]) > max_rotation_scaled) ||
(fabsf(_vehicle_angular_velocity.xyz[1]) > max_rotation_scaled) ||
(fabsf(_vehicle_angular_velocity.xyz[2]) > max_rotation_scaled);
// Return status based on armed state and throttle if no position lock is available.
if (!_has_altitude_lock() && !rotating) {
// The system has minimum trust set (manual or in failsafe)
// if this persists for 8 seconds AND the drone is not
// falling consider it to be landed. This should even sustain
// quite acrobatic flight.
return (_min_trust_start > 0) && (hrt_elapsed_time(&_min_trust_start) > 8_s);
if (angular_velocity.norm() > max_rotation_scaled) {
return false;
}
// Ground contact, no thrust and no movement -> landed
return _ground_contact_hysteresis.get_state() && _has_minimal_thrust() && !rotating;
// If vertical velocity is available: ground contact, no thrust, no movement -> landed
if ((hrt_elapsed_time(&_vehicle_local_position.timestamp) < 1_s) && _vehicle_local_position.v_z_valid) {
return _ground_contact_hysteresis.get_state();
}
// Otherwise, landed if the system has minimum thrust (manual or in failsafe) and no rotation for at least 8 seconds
return (_min_thrust_start > 0) && (hrt_elapsed_time(&_min_thrust_start) > 8_s);
}
bool MulticopterLandDetector::_get_landed_state()
{
// When not armed, consider to be landed
if (!_actuator_armed.armed) {
return true;
}
// reset the landed_time
if (!_maybe_landed_hysteresis.get_state()) {
_landed_time = 0;
@ -248,6 +292,11 @@ bool MulticopterLandDetector::_get_landed_state()
_landed_time = hrt_absolute_time();
}
// When not armed, consider to be landed
if (!_armed) {
return true;
}
// if we have maybe_landed, the mc_pos_control goes into idle (thrust_sp = 0.0)
// therefore check if all other condition of the landed state remain true
return _maybe_landed_hysteresis.get_state();
@ -263,48 +312,6 @@ float MulticopterLandDetector::_get_max_altitude()
}
}
bool MulticopterLandDetector::_has_altitude_lock()
{
return (hrt_elapsed_time(&_vehicle_local_position.timestamp) < 1_s) && _vehicle_local_position.z_valid;
}
bool MulticopterLandDetector::_has_position_lock()
{
return _has_altitude_lock() && _vehicle_local_position.xy_valid;
}
bool MulticopterLandDetector::_is_climb_rate_enabled()
{
bool has_updated = (hrt_elapsed_time(&_vehicle_local_position_setpoint.timestamp) < 1_s);
return (_vehicle_control_mode.flag_control_climb_rate_enabled && has_updated
&& PX4_ISFINITE(_vehicle_local_position_setpoint.vz));
}
bool MulticopterLandDetector::_has_low_thrust()
{
// 30% of throttle range between min and hover
float sys_min_throttle = _params.minThrottle + (_params.hoverThrottle - _params.minThrottle) *
_param_lndmc_low_t_thr.get();
// Check if thrust output is less than the minimum auto throttle param.
return _actuator_controls.control[actuator_controls_s::INDEX_THROTTLE] <= sys_min_throttle;
}
bool MulticopterLandDetector::_has_minimal_thrust()
{
// 10% of throttle range between min and hover once we entered ground contact
float sys_min_throttle = _params.minThrottle + (_params.hoverThrottle - _params.minThrottle) * 0.1f;
// Determine the system min throttle based on flight mode
if (!_vehicle_control_mode.flag_control_climb_rate_enabled) {
sys_min_throttle = (_params.minManThrottle + 0.01f);
}
// Check if thrust output is less than the minimum auto throttle param.
return _actuator_controls.control[actuator_controls_s::INDEX_THROTTLE] <= sys_min_throttle;
}
bool MulticopterLandDetector::_get_ground_effect_state()
{
return _in_descend && !_horizontal_movement;

View File

@ -43,12 +43,10 @@
#pragma once
#include <uORB/topics/actuator_controls.h>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/hover_thrust_estimate.h>
#include <uORB/topics/vehicle_angular_velocity.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_control_mode.h>
#include <uORB/topics/vehicle_local_position_setpoint.h>
#include <uORB/topics/hover_thrust_estimate.h>
#include "LandDetector.h"
@ -76,23 +74,17 @@ protected:
float _get_max_altitude() override;
private:
/** Get control mode dependent pilot throttle threshold with which we should quit landed state and take off. */
float _get_takeoff_throttle();
/** Time in us that freefall has to hold before triggering freefall */
static constexpr hrt_abstime FREEFALL_TRIGGER_TIME_US = 300_ms;
bool _has_low_thrust();
bool _has_minimal_thrust();
bool _has_altitude_lock();
bool _has_position_lock();
bool _is_climb_rate_enabled();
/** Time in us that landing conditions have to hold before triggering a land. */
static constexpr hrt_abstime LAND_DETECTOR_TRIGGER_TIME_US = 300_ms;
/** Time in us that ground contact condition have to hold before triggering contact ground */
static constexpr hrt_abstime GROUND_CONTACT_TRIGGER_TIME_US = 350_ms;
/** Time in us that almost landing conditions have to hold before triggering almost landed . */
static constexpr hrt_abstime MAYBE_LAND_DETECTOR_TRIGGER_TIME_US = 250_ms;
/** Time in us that ground contact condition have to hold before triggering contact ground */
static constexpr hrt_abstime GROUND_CONTACT_TRIGGER_TIME_US = 350_ms;
/** Time in us that landing conditions have to hold before triggering a land. */
static constexpr hrt_abstime LAND_DETECTOR_TRIGGER_TIME_US = 300_ms;
/** Time interval in us in which wider acceptance thresholds are used after landed. */
static constexpr hrt_abstime LAND_DETECTOR_LAND_PHASE_TIME_US = 2_s;
@ -115,21 +107,17 @@ private:
} _params{};
uORB::Subscription _actuator_controls_sub{ORB_ID(actuator_controls_0)};
uORB::Subscription _vehicle_acceleration_sub{ORB_ID(vehicle_acceleration)};
uORB::Subscription _hover_thrust_estimate_sub{ORB_ID(hover_thrust_estimate)};
uORB::Subscription _vehicle_angular_velocity_sub{ORB_ID(vehicle_angular_velocity)};
uORB::Subscription _vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_local_position_setpoint_sub{ORB_ID(vehicle_local_position_setpoint)};
uORB::Subscription _hover_thrust_estimate_sub{ORB_ID(hover_thrust_estimate)};
actuator_controls_s _actuator_controls {};
vehicle_angular_velocity_s _vehicle_angular_velocity{};
vehicle_control_mode_s _vehicle_control_mode {};
vehicle_local_position_setpoint_s _vehicle_local_position_setpoint {};
bool _flag_control_climb_rate_enabled{false};
bool _hover_thrust_initialized{false};
hrt_abstime _min_trust_start{0}; ///< timestamp when minimum trust was applied first
float _actuator_controls_throttle{0.f};
hrt_abstime _min_thrust_start{0}; ///< timestamp when minimum trust was applied first
hrt_abstime _landed_time{0};
bool _in_descend{false}; ///< vehicle is desending
@ -138,9 +126,6 @@ private:
DEFINE_PARAMETERS_CUSTOM_PARENT(
LandDetector,
(ParamFloat<px4::params::LNDMC_ALT_MAX>) _param_lndmc_alt_max,
(ParamFloat<px4::params::LNDMC_FFALL_THR>) _param_lndmc_ffall_thr,
(ParamFloat<px4::params::LNDMC_FFALL_TTRI>) _param_lndmc_ffall_ttri,
(ParamFloat<px4::params::LNDMC_LOW_T_THR>) _param_lndmc_low_t_thr,
(ParamFloat<px4::params::LNDMC_ROT_MAX>) _param_lndmc_rot_max,
(ParamFloat<px4::params::LNDMC_XY_VEL_MAX>) _param_lndmc_xy_vel_max,
(ParamFloat<px4::params::LNDMC_Z_VEL_MAX>) _param_lndmc_z_vel_max

View File

@ -44,7 +44,6 @@
namespace land_detector
{
bool RoverLandDetector::_get_ground_contact_state()
{
return true;
@ -52,14 +51,11 @@ bool RoverLandDetector::_get_ground_contact_state()
bool RoverLandDetector::_get_landed_state()
{
_vehicle_status_sub.update(&_vehicle_status);
if (_vehicle_status.nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_LAND) {
return true; // If Landing has been requested then say we have landed.
} else {
return !_actuator_armed.armed; // If we are armed we are not landed.
return !_armed; // If we are armed we are not landed.
}
}

View File

@ -42,7 +42,6 @@
#pragma once
#include "LandDetector.h"
#include <uORB/topics/vehicle_status.h>
namespace land_detector
{
@ -57,12 +56,6 @@ protected:
bool _get_ground_contact_state() override;
bool _get_landed_state() override;
private:
// Program crashes when Subscriptor declared here
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
vehicle_status_s _vehicle_status{}; /**< vehicle status */
};
} // namespace land_detector

View File

@ -50,15 +50,13 @@ namespace land_detector
void VtolLandDetector::_update_topics()
{
MulticopterLandDetector::_update_topics();
_airspeed_validated_sub.update(&_airspeed_validated);
_vehicle_status_sub.update(&_vehicle_status);
}
bool VtolLandDetector::_get_maybe_landed_state()
{
// If in Fixed-wing mode, only trigger if disarmed
if ((_vehicle_status.timestamp != 0) && _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING) {
return !_actuator_armed.armed;
if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING) {
return !_armed;
}
return MulticopterLandDetector::_get_maybe_landed_state();
@ -67,17 +65,20 @@ bool VtolLandDetector::_get_maybe_landed_state()
bool VtolLandDetector::_get_landed_state()
{
// If in Fixed-wing mode, only trigger if disarmed
if ((_vehicle_status.timestamp != 0) && _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING) {
return !_actuator_armed.armed;
if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING) {
return !_armed;
}
// this is returned from the mutlicopter land detector
bool landed = MulticopterLandDetector::_get_landed_state();
// for vtol we additionally consider airspeed
if (hrt_elapsed_time(&_airspeed_validated.timestamp) < 1_s && PX4_ISFINITE(_airspeed_validated.true_airspeed_m_s)) {
airspeed_validated_s airspeed_validated{};
_airspeed_validated_sub.copy(&airspeed_validated);
_airspeed_filtered = 0.95f * _airspeed_filtered + 0.05f * _airspeed_validated.true_airspeed_m_s;
if (hrt_elapsed_time(&airspeed_validated.timestamp) < 1_s && PX4_ISFINITE(airspeed_validated.true_airspeed_m_s)) {
_airspeed_filtered = 0.95f * _airspeed_filtered + 0.05f * airspeed_validated.true_airspeed_m_s;
} else {
// if airspeed does not update, set it to zero and rely on multicopter land detector
@ -97,8 +98,8 @@ bool VtolLandDetector::_get_landed_state()
bool VtolLandDetector::_get_freefall_state()
{
bool free_fall_detected =
MulticopterLandDetector::_get_freefall_state(); // true if falling or in a parabolic flight (low gravity)
// true if falling or in a parabolic flight (low gravity)
bool free_fall_detected = MulticopterLandDetector::_get_freefall_state();
// only return a positive free fall detected if not in fixed-wing mode
return _vehicle_status.vehicle_type != vehicle_status_s::VEHICLE_TYPE_FIXED_WING && free_fall_detected;

View File

@ -42,7 +42,6 @@
#pragma once
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/vehicle_status.h>
#include "MulticopterLandDetector.h"
@ -62,12 +61,7 @@ protected:
bool _get_freefall_state() override;
private:
uORB::Subscription _airspeed_validated_sub{ORB_ID(airspeed_validated)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
airspeed_validated_s _airspeed_validated{};
vehicle_status_s _vehicle_status{};
bool _was_in_air{false}; /**< indicates whether the vehicle was in the air in the previous iteration */
float _airspeed_filtered{0.0f}; /**< low pass filtered airspeed */

View File

@ -54,8 +54,6 @@
namespace land_detector
{
extern "C" __EXPORT int land_detector_main(int argc, char *argv[]);
static char _currentMode[12];
int LandDetector::task_spawn(int argc, char *argv[])
@ -145,8 +143,7 @@ The module runs periodically on the HP work queue.
return 0;
}
int land_detector_main(int argc, char *argv[])
extern "C" __EXPORT int land_detector_main(int argc, char *argv[])
{
return LandDetector::main(argc, argv);
}

View File

@ -67,35 +67,6 @@ PARAM_DEFINE_FLOAT(LNDMC_XY_VEL_MAX, 1.5f);
*/
PARAM_DEFINE_FLOAT(LNDMC_ROT_MAX, 20.0f);
/**
* Multicopter specific force threshold
*
* Multicopter threshold on the specific force measured by accelerometers in m/s^2 for free-fall detection
*
* @unit m/s^2
* @min 0.1
* @max 10
* @decimal 2
*
* @group Land Detector
*/
PARAM_DEFINE_FLOAT(LNDMC_FFALL_THR, 2.0f);
/**
* Multicopter free-fall trigger time
*
* Seconds (decimal) that freefall conditions have to met before triggering a freefall.
* Minimal value is limited by LAND_DETECTOR_UPDATE_RATE=50Hz in landDetector.h
*
* @unit s
* @min 0.02
* @max 5
* @decimal 2
*
* @group Land Detector
*/
PARAM_DEFINE_FLOAT(LNDMC_FFALL_TTRI, 0.3);
/**
* Maximum altitude for multicopters
*
@ -113,21 +84,3 @@ PARAM_DEFINE_FLOAT(LNDMC_FFALL_TTRI, 0.3);
*
*/
PARAM_DEFINE_FLOAT(LNDMC_ALT_MAX, -1.0f);
/**
* Low throttle detection threshold.
*
* Defines the commanded throttle value below which the land detector
* considers the vehicle to have "low thrust". This is one condition that
* is used to detect the ground contact state. The value is calculated as
* val = (MPC_THR_HOVER - MPC_THR_MIN) * LNDMC_LOW_T_THR + MPC_THR_MIN
* Increase this value if the system takes long time to detect landing.
*
* @unit norm
* @min 0.1
* @max 0.9
* @decimal 2
* @group Land Detector
*
*/
PARAM_DEFINE_FLOAT(LNDMC_LOW_T_THR, 0.3);