mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-08-15 22:10:35 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 959a38002f | |||
| 2deeab36a6 | |||
| 6fc9720e25 | |||
| 569849665f |
@@ -36,7 +36,6 @@ param set-default EKF2_MULTI_IMU 0
|
||||
param set-default EKF2_OF_CTRL 1
|
||||
param set-default EKF2_OF_N_MIN 0.05
|
||||
param set-default EKF2_RNG_A_HMAX 25
|
||||
param set-default EKF2_RNG_QLTY_T 0.1
|
||||
|
||||
param set-default SENS_FLOW_RATE 150
|
||||
param set-default SENS_IMU_MODE 1
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2020-2023 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file Sensor.hpp
|
||||
* Abstract class for sensors
|
||||
*
|
||||
* @author Mathieu Bresciani <brescianimathieu@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EKF_SENSOR_HPP
|
||||
#define EKF_SENSOR_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace estimator
|
||||
{
|
||||
namespace sensor
|
||||
{
|
||||
|
||||
class Sensor
|
||||
{
|
||||
public:
|
||||
virtual ~Sensor() {};
|
||||
|
||||
/*
|
||||
* run sanity checks on the current data
|
||||
* this has to be called immediately after
|
||||
* setting new data
|
||||
*/
|
||||
virtual void runChecks() {};
|
||||
|
||||
/*
|
||||
* return true if the sensor is healthy
|
||||
*/
|
||||
virtual bool isHealthy() const = 0;
|
||||
|
||||
/*
|
||||
* return true if the delayed sample is healthy
|
||||
* and can be fused in the estimator
|
||||
*/
|
||||
virtual bool isDataHealthy() const = 0;
|
||||
|
||||
/*
|
||||
* return true if the sensor data rate is
|
||||
* stable and high enough
|
||||
*/
|
||||
virtual bool isRegularlySendingData() const = 0;
|
||||
};
|
||||
|
||||
} // namespace sensor
|
||||
} // namespace estimator
|
||||
#endif // !EKF_SENSOR_HPP
|
||||
@@ -57,7 +57,7 @@ void Ekf::controlRangeHaglFusion(const imuSample &imu_sample)
|
||||
_range_sensor.setQualityHysteresis(_params.ekf2_rng_qlty_t);
|
||||
_range_sensor.setMaxFogDistance(_params.ekf2_rng_fog);
|
||||
|
||||
_range_sensor.runChecks(imu_sample.time_us, _R_to_earth);
|
||||
_range_sensor.runChecks(imu_sample.time_us, _R_to_earth, _control_status.flags.in_air);
|
||||
|
||||
if (_range_sensor.isDataHealthy()) {
|
||||
// correct the range data for position offset relative to the IMU
|
||||
@@ -76,17 +76,6 @@ void Ekf::controlRangeHaglFusion(const imuSample &imu_sample)
|
||||
_rng_consistency_check.update(_range_sensor.getDistBottom(), math::max(var, 0.001f), _state.vel(2),
|
||||
P(State::vel.idx + 2, State::vel.idx + 2), horizontal_motion, imu_sample.time_us);
|
||||
}
|
||||
|
||||
} else {
|
||||
// If we are supposed to be using range finder data but have bad range measurements
|
||||
// and are on the ground, then synthesise a measurement at the expected on ground value
|
||||
if (!_control_status.flags.in_air
|
||||
&& _range_sensor.isRegularlySendingData()
|
||||
&& _range_sensor.isDataReady()) {
|
||||
|
||||
_range_sensor.setRange(_params.ekf2_min_rng);
|
||||
_range_sensor.setValidity(true); // bypass the checks
|
||||
}
|
||||
}
|
||||
|
||||
_control_status.flags.rng_kin_consistent = _rng_consistency_check.isKinematicallyConsistent();
|
||||
|
||||
@@ -47,24 +47,25 @@ namespace estimator
|
||||
namespace sensor
|
||||
{
|
||||
|
||||
void SensorRangeFinder::runChecks(const uint64_t current_time_us, const matrix::Dcmf &R_to_earth)
|
||||
void SensorRangeFinder::runChecks(const uint64_t current_time_us, const matrix::Dcmf &R_to_earth, bool in_air)
|
||||
{
|
||||
updateSensorToEarthRotation(R_to_earth);
|
||||
updateValidity(current_time_us);
|
||||
}
|
||||
_is_sample_valid = true;
|
||||
|
||||
void SensorRangeFinder::updateSensorToEarthRotation(const matrix::Dcmf &R_to_earth)
|
||||
{
|
||||
// calculate 2,2 element of rotation matrix from sensor frame to earth frame
|
||||
// this is required for use of range finder and flow data
|
||||
_cos_tilt_rng_to_earth = R_to_earth(2, 0) * _sin_pitch_offset + R_to_earth(2, 2) * _cos_pitch_offset;
|
||||
}
|
||||
|
||||
void SensorRangeFinder::updateValidity(uint64_t current_time_us)
|
||||
{
|
||||
updateDtDataLpf(current_time_us);
|
||||
// Calculate a first order IIR low-pass filtered time of arrival between samples using a 2 second time constant.
|
||||
float alpha = 0.5f * _dt_update;
|
||||
_dt_data_lpf = _dt_data_lpf * (1.0f - alpha) + alpha * (current_time_us - _sample.time_us);
|
||||
|
||||
if (_is_faulty || isSampleOutOfDate(current_time_us) || !isDataContinuous()) {
|
||||
// Apply spike protection to the filter state.
|
||||
_dt_data_lpf = fminf(_dt_data_lpf, 4e6f);
|
||||
|
||||
bool is_continuous = _dt_data_lpf < 2e6f;
|
||||
bool is_out_of_date = (current_time_us - _sample.time_us) > 2 * RNG_MAX_INTERVAL;
|
||||
|
||||
if (is_out_of_date || !is_continuous) {
|
||||
_is_sample_valid = false;
|
||||
_is_regularly_sending_data = false;
|
||||
return;
|
||||
@@ -76,9 +77,23 @@ void SensorRangeFinder::updateValidity(uint64_t current_time_us)
|
||||
if (_is_sample_ready) {
|
||||
_is_sample_valid = false;
|
||||
|
||||
_time_bad_quality_us = _sample.quality == 0 ? current_time_us : _time_bad_quality_us;
|
||||
bool quality_ok = false;
|
||||
|
||||
if (!isQualityOk(current_time_us) || !isTiltOk() || !isDataInRange()) {
|
||||
// Mark quality as OK while on the ground
|
||||
if (!in_air) {
|
||||
_sample.rng = _rng_valid_min_val; // set to min val while on ground
|
||||
quality_ok = true;
|
||||
|
||||
} else {
|
||||
_time_bad_quality_us = _sample.quality == 0 ? current_time_us : _time_bad_quality_us;
|
||||
quality_ok = current_time_us - _time_bad_quality_us > _quality_hyst_us;
|
||||
}
|
||||
|
||||
bool in_range = (_sample.rng >= _rng_valid_min_val) && (_sample.rng <= _rng_valid_max_val);
|
||||
|
||||
bool tilt_ok = _cos_tilt_rng_to_earth > _range_cos_max_tilt;
|
||||
|
||||
if (!quality_ok || !tilt_ok || !in_range) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,31 +107,6 @@ void SensorRangeFinder::updateValidity(uint64_t current_time_us)
|
||||
}
|
||||
}
|
||||
|
||||
bool SensorRangeFinder::isQualityOk(uint64_t current_time_us) const
|
||||
{
|
||||
return current_time_us - _time_bad_quality_us > _quality_hyst_us;
|
||||
}
|
||||
|
||||
void SensorRangeFinder::updateDtDataLpf(uint64_t current_time_us)
|
||||
{
|
||||
// Calculate a first order IIR low-pass filtered time of arrival between samples using a 2 second time constant.
|
||||
float alpha = 0.5f * _dt_update;
|
||||
_dt_data_lpf = _dt_data_lpf * (1.0f - alpha) + alpha * (current_time_us - _sample.time_us);
|
||||
|
||||
// Apply spike protection to the filter state.
|
||||
_dt_data_lpf = fminf(_dt_data_lpf, 4e6f);
|
||||
}
|
||||
|
||||
inline bool SensorRangeFinder::isSampleOutOfDate(uint64_t current_time_us) const
|
||||
{
|
||||
return (current_time_us - _sample.time_us) > 2 * RNG_MAX_INTERVAL;
|
||||
}
|
||||
|
||||
inline bool SensorRangeFinder::isDataInRange() const
|
||||
{
|
||||
return (_sample.rng >= _rng_valid_min_val) && (_sample.rng <= _rng_valid_max_val);
|
||||
}
|
||||
|
||||
void SensorRangeFinder::updateStuckCheck()
|
||||
{
|
||||
if (!isStuckDetectorEnabled()) {
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
#ifndef EKF_SENSOR_RANGE_FINDER_HPP
|
||||
#define EKF_SENSOR_RANGE_FINDER_HPP
|
||||
|
||||
#include "Sensor.hpp"
|
||||
|
||||
#include <matrix/math.hpp>
|
||||
#include <lib/mathlib/math/filter/MedianFilter.hpp>
|
||||
|
||||
@@ -60,17 +58,15 @@ struct rangeSample {
|
||||
static constexpr uint64_t RNG_MAX_INTERVAL =
|
||||
200e3; ///< Maximum allowable time interval between range finder measurements (uSec)
|
||||
|
||||
class SensorRangeFinder : public Sensor
|
||||
class SensorRangeFinder
|
||||
{
|
||||
public:
|
||||
SensorRangeFinder() = default;
|
||||
~SensorRangeFinder() override = default;
|
||||
~SensorRangeFinder() = default;
|
||||
|
||||
void runChecks(uint64_t current_time_us, const matrix::Dcmf &R_to_earth);
|
||||
bool isHealthy() const override { return _is_sample_valid; }
|
||||
bool isDataHealthy() const override { return _is_sample_ready && _is_sample_valid; }
|
||||
bool isDataReady() const { return _is_sample_ready; }
|
||||
bool isRegularlySendingData() const override { return _is_regularly_sending_data; }
|
||||
void runChecks(uint64_t current_time_us, const matrix::Dcmf &R_to_earth, bool in_air = true);
|
||||
bool isDataHealthy() const { return _is_sample_ready && _is_sample_valid; }
|
||||
bool isRegularlySendingData() const { return _is_regularly_sending_data; }
|
||||
bool isStuckDetectorEnabled() const { return _stuck_threshold > 0.f; }
|
||||
|
||||
void setSample(const rangeSample &sample)
|
||||
@@ -115,23 +111,11 @@ public:
|
||||
float getDistBottom() const { return _sample.rng * _cos_tilt_rng_to_earth; }
|
||||
|
||||
void setDataReadiness(bool is_ready) { _is_sample_ready = is_ready; }
|
||||
void setValidity(bool is_valid) { _is_sample_valid = is_valid; }
|
||||
|
||||
float getValidMinVal() const { return _rng_valid_min_val; }
|
||||
float getValidMaxVal() const { return _rng_valid_max_val; }
|
||||
|
||||
void setFaulty(bool faulty = true) { _is_faulty = faulty; }
|
||||
|
||||
private:
|
||||
void updateSensorToEarthRotation(const matrix::Dcmf &R_to_earth);
|
||||
|
||||
void updateValidity(uint64_t current_time_us);
|
||||
void updateDtDataLpf(uint64_t current_time_us);
|
||||
bool isSampleOutOfDate(uint64_t current_time_us) const;
|
||||
bool isDataContinuous() const { return _dt_data_lpf < 2e6f; }
|
||||
bool isTiltOk() const { return _cos_tilt_rng_to_earth > _range_cos_max_tilt; }
|
||||
bool isDataInRange() const;
|
||||
bool isQualityOk(uint64_t current_time_us) const;
|
||||
void updateStuckCheck();
|
||||
void updateFogCheck(const float dist_bottom, const uint64_t time_us);
|
||||
|
||||
@@ -141,7 +125,6 @@ private:
|
||||
bool _is_sample_valid{}; ///< true if range finder sample retrieved from buffer is valid
|
||||
bool _is_regularly_sending_data{false}; ///< true if the interval between two samples is less than the maximum expected interval
|
||||
uint64_t _time_last_valid_us{}; ///< time the last range finder measurement was ready (uSec)
|
||||
bool _is_faulty{false}; ///< the sensor should not be used anymore
|
||||
|
||||
/*
|
||||
* Stuck check
|
||||
|
||||
@@ -103,26 +103,20 @@ void SensorRangeFinderTest::testTilt(const Eulerf &euler, bool should_pass)
|
||||
|
||||
if (should_pass) {
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
|
||||
} else {
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, setRange)
|
||||
{
|
||||
rangeSample sample{};
|
||||
sample.rng = 1.f;
|
||||
sample.time_us = 1e6;
|
||||
sample.quality = 9;
|
||||
const Dcmf attitude{Eulerf(0.f, 0.f, 0.f)};
|
||||
_range_finder.setSample(_good_sample);
|
||||
|
||||
_range_finder.setRange(sample.rng);
|
||||
_range_finder.setDataReadiness(true);
|
||||
_range_finder.setValidity(true);
|
||||
_range_finder.setRange(1.23);
|
||||
_range_finder.runChecks(_good_sample.time_us, attitude);
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, goodData)
|
||||
@@ -134,7 +128,6 @@ TEST_F(SensorRangeFinderTest, goodData)
|
||||
|
||||
// THEN: the data can be used for aiding
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, tiltExceeded)
|
||||
@@ -177,7 +170,6 @@ TEST_F(SensorRangeFinderTest, rangeMaxExceeded)
|
||||
|
||||
// THEN: the data should be marked as unhealthy
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, rangeMinExceeded)
|
||||
@@ -192,7 +184,6 @@ TEST_F(SensorRangeFinderTest, rangeMinExceeded)
|
||||
|
||||
// THEN: the data should be marked as unhealthy
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, outOfDate)
|
||||
@@ -208,7 +199,6 @@ TEST_F(SensorRangeFinderTest, outOfDate)
|
||||
|
||||
// THEN: the data should be marked as unhealthy
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, rangeStuck)
|
||||
@@ -229,7 +219,6 @@ TEST_F(SensorRangeFinderTest, rangeStuck)
|
||||
}
|
||||
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
new_sample.quality = 100;
|
||||
|
||||
@@ -244,13 +233,11 @@ TEST_F(SensorRangeFinderTest, rangeStuck)
|
||||
// because the sensor is "stuck"
|
||||
if (_range_finder.isStuckDetectorEnabled()) {
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
} else {
|
||||
// If stuck detector is disabled then the
|
||||
// data should instantly be marked as healthy
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
// BUT WHEN: we continue to send samples but with changing distance
|
||||
@@ -264,7 +251,6 @@ TEST_F(SensorRangeFinderTest, rangeStuck)
|
||||
// THEN: the data should be marked as healthy
|
||||
// because the sensor is not "stuck" anymore
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, qualityHysteresis)
|
||||
@@ -278,13 +264,11 @@ TEST_F(SensorRangeFinderTest, qualityHysteresis)
|
||||
_range_finder.setSample(new_sample);
|
||||
_range_finder.runChecks(new_sample.time_us, attitude);
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
new_sample.quality = _good_sample.quality;
|
||||
_range_finder.setSample(new_sample);
|
||||
_range_finder.runChecks(new_sample.time_us, attitude);
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
// AND: we need to put enough good data to pass the hysteresis
|
||||
const uint64_t dt = 3e5;
|
||||
@@ -298,7 +282,6 @@ TEST_F(SensorRangeFinderTest, qualityHysteresis)
|
||||
|
||||
// THEN: the data is again declared healthy
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, continuity)
|
||||
@@ -314,7 +297,6 @@ TEST_F(SensorRangeFinderTest, continuity)
|
||||
// THEN: the data should be marked as unhealthy
|
||||
// Note that it also fails the out-of-date test here
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
// AND WHEN: the data rate is acceptable
|
||||
dt_sensor_us = 3e5;
|
||||
@@ -324,11 +306,9 @@ TEST_F(SensorRangeFinderTest, continuity)
|
||||
// THEN: it should still fail until the filter converge
|
||||
// to the new datarate
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
updateSensorAtRate(_good_sample, duration_us, dt_update_us, dt_sensor_us);
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
}
|
||||
|
||||
TEST_F(SensorRangeFinderTest, distBottom)
|
||||
@@ -359,7 +339,6 @@ TEST_F(SensorRangeFinderTest, blockedByFog)
|
||||
updateSensorAtRate(_good_sample, duration_us, dt_update_us, dt_sensor_us);
|
||||
// THEN: the data should be marked as healthy
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
|
||||
// WHEN: sensor is then blocked by fog
|
||||
// range jumps to value below 2m
|
||||
@@ -369,7 +348,6 @@ TEST_F(SensorRangeFinderTest, blockedByFog)
|
||||
|
||||
// THEN: the data should be marked as unhealthy
|
||||
EXPECT_FALSE(_range_finder.isDataHealthy());
|
||||
EXPECT_FALSE(_range_finder.isHealthy());
|
||||
|
||||
// WHEN: the sensor is not blocked by fog anymore
|
||||
sample.rng = 5.f;
|
||||
@@ -378,7 +356,6 @@ TEST_F(SensorRangeFinderTest, blockedByFog)
|
||||
|
||||
// THEN: the data should be marked as healthy again
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
|
||||
// WHEN: the sensor is is not jumping to a value below 2m
|
||||
while (sample.rng > _min_range) {
|
||||
@@ -390,6 +367,4 @@ TEST_F(SensorRangeFinderTest, blockedByFog)
|
||||
|
||||
// THEN: the data should still be marked as healthy
|
||||
EXPECT_TRUE(_range_finder.isDataHealthy());
|
||||
EXPECT_TRUE(_range_finder.isHealthy());
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user