mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-31 08:40:04 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 695c9a2ff4 | |||
| dc2bfca2b6 | |||
| 76fa2aeace | |||
| 6016b7ec19 | |||
| e1a4473118 |
@@ -200,6 +200,8 @@ void Ekf::resetVerticalPositionTo(const float new_vert_pos, float new_vert_pos_v
|
||||
_gps_hgt_b_est.setBias(_gps_hgt_b_est.getBias() + delta_z);
|
||||
_rng_hgt_b_est.setBias(_rng_hgt_b_est.getBias() + delta_z);
|
||||
|
||||
_terrain_vpos -= delta_z;
|
||||
|
||||
// Reset the timout timer
|
||||
_time_last_hgt_fuse = _time_delayed_us;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ void Ekf::fuseOptFlow()
|
||||
_innov_check_fail_status.flags.reject_optflow_Y = (_aid_src_optical_flow.test_ratio[1] > 1.f);
|
||||
|
||||
// if either axis fails we abort the fusion
|
||||
if (_aid_src_optical_flow.innovation_rejected) {
|
||||
if (_aid_src_optical_flow.innovation_rejected || !_aid_src_optical_flow.fusion_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
|
||||
// Accumulate autopilot gyro data across the same time interval as the flow sensor
|
||||
const Vector3f delta_angle(imu_delayed.delta_ang - (getGyroBias() * imu_delayed.delta_ang_dt));
|
||||
|
||||
if (_delta_time_of < 0.1f) {
|
||||
_imu_del_ang_of += delta_angle;
|
||||
_delta_time_of += imu_delayed.delta_ang_dt;
|
||||
@@ -110,7 +111,7 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
// compensate for body motion to give a LOS rate
|
||||
_flow_compensated_XY_rad = _flow_sample_delayed.flow_xy_rad - _flow_sample_delayed.gyro_xyz.xy();
|
||||
|
||||
} else if (!_control_status.flags.in_air) {
|
||||
} else if (!_control_status.flags.in_air && is_tilt_good && _control_status.flags.vehicle_at_rest) {
|
||||
|
||||
if (!is_delta_time_good) {
|
||||
// handle special case of SITL and PX4Flow where dt is forced to
|
||||
@@ -128,8 +129,6 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
_flow_compensated_XY_rad.setZero();
|
||||
}
|
||||
|
||||
updateOptFlow(_aid_src_optical_flow);
|
||||
|
||||
} else {
|
||||
_flow_compensated_XY_rad.setZero();
|
||||
}
|
||||
@@ -154,7 +153,7 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
const bool flight_condition_not_ok = _control_status.flags.in_air && !isTerrainEstimateValid();
|
||||
|
||||
const bool inhibit_flow_use = ((preflight_motion_not_ok || flight_condition_not_ok) && !is_flow_required)
|
||||
|| !_control_status.flags.tilt_align;
|
||||
|| !_control_status.flags.tilt_align;
|
||||
|
||||
// Handle cases where we are using optical flow but we should not use it anymore
|
||||
if (_control_status.flags.opt_flow) {
|
||||
@@ -166,13 +165,26 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the midpoint of the flow sample has fallen behind the fusion time horizon
|
||||
const bool flow_delayed = (_time_delayed_us > (_flow_sample_delayed.time_us - uint32_t(1e6f * _flow_sample_delayed.dt) / 2));
|
||||
|
||||
// use a relaxed time criteria to enable it to coast through bad range finder data
|
||||
const bool terrain_available = isTerrainEstimateValid() || isRecent(_time_last_hagl_fuse, (uint64_t)10e6);
|
||||
|
||||
// optical flow fusion mode selection logic
|
||||
if ((_params.fusion_mode & SensorFusionMask::USE_OPT_FLOW) // optical flow has been selected by the user
|
||||
&& !_control_status.flags.opt_flow // we are not yet using flow data
|
||||
&& !inhibit_flow_use) {
|
||||
|
||||
&& flow_delayed
|
||||
&& !inhibit_flow_use
|
||||
&& !isRecent(_aid_src_optical_flow.time_last_fuse, (uint64_t)2e6)
|
||||
&& terrain_available
|
||||
) {
|
||||
// set the flag and reset the fusion timeout
|
||||
ECL_INFO("starting optical flow fusion");
|
||||
updateOptFlow(_aid_src_optical_flow);
|
||||
|
||||
_innov_check_fail_status.flags.reject_optflow_X = false;
|
||||
_innov_check_fail_status.flags.reject_optflow_Y = false;
|
||||
|
||||
// if we are not using GPS or external vision aiding, then the velocity and position states and covariances need to be set
|
||||
if (!isHorizontalAidingActive()) {
|
||||
@@ -191,41 +203,55 @@ void Ekf::controlOpticalFlowFusion(const imuSample &imu_delayed)
|
||||
ECL_INFO("reset position to last known (%.3f, %.3f)", (double)_last_known_pos(0), (double)_last_known_pos(1));
|
||||
resetHorizontalPositionTo(_last_known_pos.xy(), 0.f);
|
||||
}
|
||||
|
||||
_aid_src_optical_flow.fusion_enabled = true;
|
||||
_aid_src_optical_flow.test_ratio[0] = 0.f;
|
||||
_aid_src_optical_flow.test_ratio[1] = 0.f;
|
||||
_aid_src_optical_flow.innovation_rejected = false;
|
||||
_aid_src_optical_flow.time_last_fuse = _time_delayed_us;
|
||||
|
||||
_control_status.flags.opt_flow = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_aid_src_optical_flow.time_last_fuse = _time_delayed_us;
|
||||
// otherwise enable opt_flow and continue to fusion
|
||||
_control_status.flags.opt_flow = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_control_status.flags.opt_flow) {
|
||||
// Wait until the midpoint of the flow sample has fallen behind the fusion time horizon
|
||||
if (_time_delayed_us > (_flow_sample_delayed.time_us - uint32_t(1e6f * _flow_sample_delayed.dt) / 2)) {
|
||||
// Fuse optical flow LOS rate observations into the main filter only if height above ground has been updated recently
|
||||
// but use a relaxed time criteria to enable it to coast through bad range finder data
|
||||
if (isRecent(_time_last_hagl_fuse, (uint64_t)10e6)) {
|
||||
|
||||
if (flow_delayed) {
|
||||
if (terrain_available) {
|
||||
// Fuse optical flow LOS rate observations into the main filter only if height above ground has been updated recently
|
||||
fuseOptFlow();
|
||||
_last_known_pos.xy() = _state.pos.xy();
|
||||
|
||||
// handle the case when we have optical flow, are reliant on it, but have not been using it for an extended period
|
||||
if (isTimedOut(_aid_src_optical_flow.time_last_fuse, _params.no_aid_timeout_max)
|
||||
&& !isOtherSourceOfHorizontalAidingThan(_control_status.flags.opt_flow)
|
||||
) {
|
||||
ECL_INFO("reset velocity to flow");
|
||||
_information_events.flags.reset_vel_to_flow = true;
|
||||
resetHorizontalVelocityTo(_flow_vel_ne, calcOptFlowMeasVar(_flow_sample_delayed));
|
||||
|
||||
// reset position, estimate is relative to initial position in this mode, so we start with zero error
|
||||
ECL_INFO("reset position to last known (%.3f, %.3f)", (double)_last_known_pos(0), (double)_last_known_pos(1));
|
||||
_information_events.flags.reset_pos_to_last_known = true;
|
||||
resetHorizontalPositionTo(_last_known_pos.xy(), 0.f);
|
||||
|
||||
_aid_src_optical_flow.fusion_enabled = true;
|
||||
_aid_src_optical_flow.test_ratio[0] = 0.f;
|
||||
_aid_src_optical_flow.test_ratio[1] = 0.f;
|
||||
_aid_src_optical_flow.innovation_rejected = false;
|
||||
_aid_src_optical_flow.time_last_fuse = _time_delayed_us;
|
||||
}
|
||||
}
|
||||
|
||||
_flow_data_ready = false;
|
||||
}
|
||||
|
||||
// handle the case when we have optical flow, are reliant on it, but have not been using it for an extended period
|
||||
if (isTimedOut(_aid_src_optical_flow.time_last_fuse, _params.no_aid_timeout_max)
|
||||
&& !isOtherSourceOfHorizontalAidingThan(_control_status.flags.opt_flow)) {
|
||||
|
||||
ECL_INFO("reset velocity to flow");
|
||||
_information_events.flags.reset_vel_to_flow = true;
|
||||
resetHorizontalVelocityTo(_flow_vel_ne, calcOptFlowMeasVar(_flow_sample_delayed));
|
||||
|
||||
// reset position, estimate is relative to initial position in this mode, so we start with zero error
|
||||
ECL_INFO("reset position to last known (%.3f, %.3f)", (double)_last_known_pos(0), (double)_last_known_pos(1));
|
||||
_information_events.flags.reset_pos_to_last_known = true;
|
||||
resetHorizontalPositionTo(_last_known_pos.xy(), 0.f);
|
||||
|
||||
_aid_src_optical_flow.time_last_fuse = _time_delayed_us;
|
||||
if (!terrain_available || !isRecent(_aid_src_optical_flow.time_last_fuse, (uint64_t)5e6)) {
|
||||
stopFlowFusion();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ void Ekf::initHagl()
|
||||
|
||||
// use the ground clearance value as our uncertainty
|
||||
_terrain_var = sq(_params.rng_gnd_clearance);
|
||||
|
||||
_time_last_hagl_fuse = _time_delayed_us;
|
||||
}
|
||||
|
||||
void Ekf::runTerrainEstimator(const imuSample &imu_delayed)
|
||||
@@ -399,15 +401,19 @@ void Ekf::controlHaglFakeFusion()
|
||||
&& !_hagl_sensor_status.flags.range_finder
|
||||
&& !_hagl_sensor_status.flags.flow) {
|
||||
|
||||
initHagl();
|
||||
if (_control_status.flags.vehicle_at_rest || isTimedOut(_time_last_hagl_fuse, (uint64_t)1e6)) {
|
||||
initHagl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Ekf::isTerrainEstimateValid() const
|
||||
{
|
||||
// we have been fusing range finder measurements in the last 5 seconds
|
||||
if (_hagl_sensor_status.flags.range_finder && isRecent(_time_last_hagl_fuse, (uint64_t)5e6)) {
|
||||
return true;
|
||||
if (isRecent(_time_last_hagl_fuse, (uint64_t)5e6)) {
|
||||
if (_hagl_sensor_status.flags.range_finder || !_control_status.flags.in_air) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// we have been fusing optical flow measurements for terrain estimation within the last 5 seconds
|
||||
|
||||
@@ -1971,7 +1971,7 @@ bool EKF2::UpdateFlowSample(ekf2_timestamps_s &ekf2_timestamps)
|
||||
.flow_xy_rad = Vector2f{-optical_flow.pixel_flow[0], -optical_flow.pixel_flow[1]},
|
||||
.gyro_xyz = Vector3f{-optical_flow.delta_angle[0], -optical_flow.delta_angle[1], -optical_flow.delta_angle[2]},
|
||||
.dt = 1e-6f * (float)optical_flow.integration_timespan_us,
|
||||
.quality = optical_flow.quality,
|
||||
.quality = optical_flow.quality
|
||||
};
|
||||
|
||||
if (Vector2f(optical_flow.pixel_flow).isAllFinite() && flow.dt < 1) {
|
||||
|
||||
@@ -140,6 +140,8 @@ TEST_F(EkfFlowTest, resetToFlowVelocityInAir)
|
||||
|
||||
// THEN: estimated velocity should match simulated velocity
|
||||
const Vector3f estimated_velocity = _ekf->getVelocity();
|
||||
estimated_velocity.print();
|
||||
simulated_velocity.print();
|
||||
EXPECT_TRUE(isEqual(estimated_velocity, simulated_velocity))
|
||||
<< "estimated vel = " << estimated_velocity(0) << ", "
|
||||
<< estimated_velocity(1);
|
||||
@@ -168,6 +170,7 @@ TEST_F(EkfFlowTest, resetToFlowVelocityOnGround)
|
||||
_sensor_simulator._flow.setData(flow_sample);
|
||||
_ekf_wrapper.enableFlowFusion();
|
||||
_sensor_simulator.startFlow();
|
||||
_sensor_simulator.startRangeFinder();
|
||||
_sensor_simulator.runSeconds(1.0);
|
||||
|
||||
// THEN: estimated velocity should match simulated velocity
|
||||
|
||||
@@ -183,7 +183,7 @@ TEST_F(EkfFusionLogicTest, fallbackFromGpsToFlow)
|
||||
const float max_ground_distance = 50.f;
|
||||
_ekf->set_optical_flow_limits(max_flow_rate, min_ground_distance, max_ground_distance);
|
||||
_sensor_simulator.startFlow();
|
||||
_sensor_simulator.startFlow();
|
||||
_sensor_simulator.startRangeFinder();
|
||||
_ekf_wrapper.enableFlowFusion();
|
||||
|
||||
_ekf->set_in_air_status(true);
|
||||
|
||||
@@ -156,6 +156,7 @@ TEST_F(EkfGpsTest, gpsHgtToBaroFallback)
|
||||
_sensor_simulator._flow.setData(_sensor_simulator._flow.dataAtRest());
|
||||
_ekf_wrapper.enableFlowFusion();
|
||||
_sensor_simulator.startFlow();
|
||||
_sensor_simulator.startRangeFinder();
|
||||
|
||||
_ekf_wrapper.enableGpsHeightFusion();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user