EKF2 split output predictor and publish attitude immediately (#9832)

* EKF2 split output predictor and publish attitude immediately

* ecl: incorporate magic number tidy up
This commit is contained in:
Daniel Agar
2018-07-09 08:11:24 +10:00
committed by Paul Riseborough
parent 58c8c50174
commit 850b1ad6a7
2 changed files with 224 additions and 224 deletions
+223 -223
View File
@@ -73,6 +73,7 @@
#include <uORB/topics/vehicle_magnetometer.h>
using math::constrain;
using namespace time_literals;
extern "C" __EXPORT int ekf2_main(int argc, char *argv[]);
@@ -109,6 +110,7 @@ private:
template<typename Param>
void update_mag_bias(Param &mag_bias_param, int axis_index);
bool publish_attitude(const sensor_combined_s &sensors);
bool publish_wind_estimate(const hrt_abstime &timestamp);
const Vector3f get_vel_body_wind();
@@ -711,6 +713,9 @@ void Ekf2::run()
_ekf.setIMUData(now, sensors.gyro_integral_dt, sensors.accelerometer_integral_dt, gyro_integral, accel_integral);
// publish attitude immediately (uses quaternion from output predictor)
publish_attitude(sensors);
// read mag data
bool magnetometer_updated = false;
orb_check(_magnetometer_sub, &magnetometer_updated);
@@ -1032,205 +1037,181 @@ void Ekf2::run()
const bool updated = _ekf.update();
perf_end(_perf_ekf_update);
// integrate time to monitor time slippage
if (_start_time_us == 0) {
_start_time_us = now;
_last_time_slip_us = 0;
} else if (_start_time_us > 0) {
_integrated_time_us += sensors.gyro_integral_dt;
_last_time_slip_us = (now - _start_time_us) - _integrated_time_us;
}
if (updated) {
// integrate time to monitor time slippage
if (_start_time_us == 0) {
_start_time_us = now;
_last_time_slip_us = 0;
filter_control_status_u control_status;
_ekf.get_control_mode(&control_status.value);
} else if (_start_time_us > 0) {
_integrated_time_us += sensors.gyro_integral_dt;
_last_time_slip_us = (now - _start_time_us) - _integrated_time_us;
}
// only publish position after successful alignment
if (control_status.flags.tilt_align) {
// generate vehicle local position data
vehicle_local_position_s &lpos = _vehicle_local_position_pub.get();
matrix::Quatf q;
_ekf.copy_quaternion(q.data());
lpos.timestamp = now;
// In-run bias estimates
float gyro_bias[3];
_ekf.get_gyro_bias(gyro_bias);
// Position of body origin in local NED frame
float position[3];
_ekf.get_position(position);
const float lpos_x_prev = lpos.x;
const float lpos_y_prev = lpos.y;
lpos.x = (_ekf.local_position_is_valid()) ? position[0] : 0.0f;
lpos.y = (_ekf.local_position_is_valid()) ? position[1] : 0.0f;
lpos.z = position[2];
{
// generate vehicle attitude quaternion data
vehicle_attitude_s att;
att.timestamp = now;
// Velocity of body origin in local NED frame (m/s)
float velocity[3];
_ekf.get_velocity(velocity);
lpos.vx = velocity[0];
lpos.vy = velocity[1];
lpos.vz = velocity[2];
q.copyTo(att.q);
_ekf.get_quat_reset(&att.delta_q_reset[0], &att.quat_reset_counter);
// vertical position time derivative (m/s)
_ekf.get_pos_d_deriv(&lpos.z_deriv);
att.rollspeed = sensors.gyro_rad[0] - gyro_bias[0];
att.pitchspeed = sensors.gyro_rad[1] - gyro_bias[1];
att.yawspeed = sensors.gyro_rad[2] - gyro_bias[2];
// Acceleration of body origin in local NED frame
float vel_deriv[3];
_ekf.get_vel_deriv_ned(vel_deriv);
lpos.ax = vel_deriv[0];
lpos.ay = vel_deriv[1];
lpos.az = vel_deriv[2];
// publish vehicle attitude data
if (_att_pub == nullptr) {
_att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
// TODO: better status reporting
lpos.xy_valid = _ekf.local_position_is_valid() && !_preflt_horiz_fail;
lpos.z_valid = !_preflt_vert_fail;
lpos.v_xy_valid = _ekf.local_position_is_valid() && !_preflt_horiz_fail;
lpos.v_z_valid = !_preflt_vert_fail;
} else {
orb_publish(ORB_ID(vehicle_attitude), _att_pub, &att);
}
}
// Position of local NED origin in GPS / WGS84 frame
map_projection_reference_s ekf_origin;
uint64_t origin_time;
// generate vehicle local position data
vehicle_local_position_s &lpos = _vehicle_local_position_pub.get();
// true if position (x,y,z) has a valid WGS-84 global reference (ref_lat, ref_lon, alt)
const bool ekf_origin_valid = _ekf.get_ekf_origin(&origin_time, &ekf_origin, &lpos.ref_alt);
lpos.xy_global = ekf_origin_valid;
lpos.z_global = ekf_origin_valid;
lpos.timestamp = now;
// Position of body origin in local NED frame
float position[3];
_ekf.get_position(position);
const float lpos_x_prev = lpos.x;
const float lpos_y_prev = lpos.y;
lpos.x = (_ekf.local_position_is_valid()) ? position[0] : 0.0f;
lpos.y = (_ekf.local_position_is_valid()) ? position[1] : 0.0f;
lpos.z = position[2];
// Velocity of body origin in local NED frame (m/s)
float velocity[3];
_ekf.get_velocity(velocity);
lpos.vx = velocity[0];
lpos.vy = velocity[1];
lpos.vz = velocity[2];
// vertical position time derivative (m/s)
_ekf.get_pos_d_deriv(&lpos.z_deriv);
// Acceleration of body origin in local NED frame
float vel_deriv[3];
_ekf.get_vel_deriv_ned(vel_deriv);
lpos.ax = vel_deriv[0];
lpos.ay = vel_deriv[1];
lpos.az = vel_deriv[2];
// TODO: better status reporting
lpos.xy_valid = _ekf.local_position_is_valid() && !_preflt_horiz_fail;
lpos.z_valid = !_preflt_vert_fail;
lpos.v_xy_valid = _ekf.local_position_is_valid() && !_preflt_horiz_fail;
lpos.v_z_valid = !_preflt_vert_fail;
// Position of local NED origin in GPS / WGS84 frame
map_projection_reference_s ekf_origin;
uint64_t origin_time;
// true if position (x,y,z) has a valid WGS-84 global reference (ref_lat, ref_lon, alt)
const bool ekf_origin_valid = _ekf.get_ekf_origin(&origin_time, &ekf_origin, &lpos.ref_alt);
lpos.xy_global = ekf_origin_valid;
lpos.z_global = ekf_origin_valid;
if (ekf_origin_valid && (origin_time > lpos.ref_timestamp)) {
lpos.ref_timestamp = origin_time;
lpos.ref_lat = ekf_origin.lat_rad * 180.0 / M_PI; // Reference point latitude in degrees
lpos.ref_lon = ekf_origin.lon_rad * 180.0 / M_PI; // Reference point longitude in degrees
}
// The rotation of the tangent plane vs. geographical north
matrix::Eulerf euler(q);
lpos.yaw = euler.psi();
lpos.dist_bottom_valid = _ekf.get_terrain_valid();
float terrain_vpos;
_ekf.get_terrain_vert_pos(&terrain_vpos);
lpos.dist_bottom = terrain_vpos - lpos.z; // Distance to bottom surface (ground) in meters
// constrain the distance to ground to _rng_gnd_clearance
if (lpos.dist_bottom < _rng_gnd_clearance.get()) {
lpos.dist_bottom = _rng_gnd_clearance.get();
}
lpos.dist_bottom_rate = -lpos.vz; // Distance to bottom surface (ground) change rate
_ekf.get_ekf_lpos_accuracy(&lpos.eph, &lpos.epv);
_ekf.get_ekf_vel_accuracy(&lpos.evh, &lpos.evv);
// get state reset information of position and velocity
_ekf.get_posD_reset(&lpos.delta_z, &lpos.z_reset_counter);
_ekf.get_velD_reset(&lpos.delta_vz, &lpos.vz_reset_counter);
_ekf.get_posNE_reset(&lpos.delta_xy[0], &lpos.xy_reset_counter);
_ekf.get_velNE_reset(&lpos.delta_vxy[0], &lpos.vxy_reset_counter);
// get control limit information
_ekf.get_ekf_ctrl_limits(&lpos.vxy_max, &lpos.vz_max, &lpos.hagl_min, &lpos.hagl_max);
// convert NaN to INFINITY
if (!PX4_ISFINITE(lpos.vxy_max)) {
lpos.vxy_max = INFINITY;
}
if (!PX4_ISFINITE(lpos.vz_max)) {
lpos.vz_max = INFINITY;
}
if (!PX4_ISFINITE(lpos.hagl_min)) {
lpos.hagl_min = INFINITY;
}
if (!PX4_ISFINITE(lpos.hagl_max)) {
lpos.hagl_max = INFINITY;
}
// publish vehicle local position data
_vehicle_local_position_pub.update();
if (_ekf.global_position_is_valid() && !_preflt_fail) {
// generate and publish global position data
vehicle_global_position_s &global_pos = _vehicle_global_position_pub.get();
global_pos.timestamp = now;
if (fabsf(lpos_x_prev - lpos.x) > FLT_EPSILON || fabsf(lpos_y_prev - lpos.y) > FLT_EPSILON) {
map_projection_reproject(&ekf_origin, lpos.x, lpos.y, &global_pos.lat, &global_pos.lon);
if (ekf_origin_valid && (origin_time > lpos.ref_timestamp)) {
lpos.ref_timestamp = origin_time;
lpos.ref_lat = ekf_origin.lat_rad * 180.0 / M_PI; // Reference point latitude in degrees
lpos.ref_lon = ekf_origin.lon_rad * 180.0 / M_PI; // Reference point longitude in degrees
}
global_pos.lat_lon_reset_counter = lpos.xy_reset_counter;
// The rotation of the tangent plane vs. geographical north
matrix::Quatf q;
_ekf.copy_quaternion(q.data());
matrix::Eulerf euler(q);
lpos.yaw = euler.psi();
global_pos.alt = -lpos.z + lpos.ref_alt; // Altitude AMSL in meters
lpos.dist_bottom_valid = _ekf.get_terrain_valid();
// global altitude has opposite sign of local down position
global_pos.delta_alt = -lpos.delta_z;
float terrain_vpos;
_ekf.get_terrain_vert_pos(&terrain_vpos);
lpos.dist_bottom = terrain_vpos - lpos.z; // Distance to bottom surface (ground) in meters
global_pos.vel_n = lpos.vx; // Ground north velocity, m/s
global_pos.vel_e = lpos.vy; // Ground east velocity, m/s
global_pos.vel_d = lpos.vz; // Ground downside velocity, m/s
global_pos.yaw = lpos.yaw; // Yaw in radians -PI..+PI.
_ekf.get_ekf_gpos_accuracy(&global_pos.eph, &global_pos.epv);
global_pos.dead_reckoning = _ekf.inertial_dead_reckoning();
global_pos.terrain_alt_valid = lpos.dist_bottom_valid;
if (global_pos.terrain_alt_valid) {
global_pos.terrain_alt = lpos.ref_alt - terrain_vpos; // Terrain altitude in m, WGS84
} else {
global_pos.terrain_alt = 0.0f; // Terrain altitude in m, WGS84
// constrain the distance to ground to _rng_gnd_clearance
if (lpos.dist_bottom < _rng_gnd_clearance.get()) {
lpos.dist_bottom = _rng_gnd_clearance.get();
}
global_pos.dead_reckoning = _ekf.inertial_dead_reckoning(); // True if this position is estimated through dead-reckoning
lpos.dist_bottom_rate = -lpos.vz; // Distance to bottom surface (ground) change rate
_vehicle_global_position_pub.update();
_ekf.get_ekf_lpos_accuracy(&lpos.eph, &lpos.epv);
_ekf.get_ekf_vel_accuracy(&lpos.evh, &lpos.evv);
// get state reset information of position and velocity
_ekf.get_posD_reset(&lpos.delta_z, &lpos.z_reset_counter);
_ekf.get_velD_reset(&lpos.delta_vz, &lpos.vz_reset_counter);
_ekf.get_posNE_reset(&lpos.delta_xy[0], &lpos.xy_reset_counter);
_ekf.get_velNE_reset(&lpos.delta_vxy[0], &lpos.vxy_reset_counter);
// get control limit information
_ekf.get_ekf_ctrl_limits(&lpos.vxy_max, &lpos.vz_max, &lpos.hagl_min, &lpos.hagl_max);
// convert NaN to INFINITY
if (!PX4_ISFINITE(lpos.vxy_max)) {
lpos.vxy_max = INFINITY;
}
if (!PX4_ISFINITE(lpos.vz_max)) {
lpos.vz_max = INFINITY;
}
if (!PX4_ISFINITE(lpos.hagl_min)) {
lpos.hagl_min = INFINITY;
}
if (!PX4_ISFINITE(lpos.hagl_max)) {
lpos.hagl_max = INFINITY;
}
// publish vehicle local position data
_vehicle_local_position_pub.update();
if (_ekf.global_position_is_valid() && !_preflt_fail) {
// generate and publish global position data
vehicle_global_position_s &global_pos = _vehicle_global_position_pub.get();
global_pos.timestamp = now;
if (fabsf(lpos_x_prev - lpos.x) > FLT_EPSILON || fabsf(lpos_y_prev - lpos.y) > FLT_EPSILON) {
map_projection_reproject(&ekf_origin, lpos.x, lpos.y, &global_pos.lat, &global_pos.lon);
}
global_pos.lat_lon_reset_counter = lpos.xy_reset_counter;
global_pos.alt = -lpos.z + lpos.ref_alt; // Altitude AMSL in meters
// global altitude has opposite sign of local down position
global_pos.delta_alt = -lpos.delta_z;
global_pos.vel_n = lpos.vx; // Ground north velocity, m/s
global_pos.vel_e = lpos.vy; // Ground east velocity, m/s
global_pos.vel_d = lpos.vz; // Ground downside velocity, m/s
global_pos.yaw = lpos.yaw; // Yaw in radians -PI..+PI.
_ekf.get_ekf_gpos_accuracy(&global_pos.eph, &global_pos.epv);
global_pos.terrain_alt_valid = lpos.dist_bottom_valid;
if (global_pos.terrain_alt_valid) {
global_pos.terrain_alt = lpos.ref_alt - terrain_vpos; // Terrain altitude in m, WGS84
} else {
global_pos.terrain_alt = 0.0f; // Terrain altitude in m, WGS84
}
global_pos.dead_reckoning = _ekf.inertial_dead_reckoning(); // True if this position is estimated through dead-reckoning
_vehicle_global_position_pub.update();
}
}
{
// publish all corrected sensor readings and bias estimates after mag calibration is updated above
float accel_bias[3];
_ekf.get_accel_bias(accel_bias);
sensor_bias_s bias;
bias.timestamp = now;
bias.accel_x = sensors.accelerometer_m_s2[0] - accel_bias[0];
bias.accel_y = sensors.accelerometer_m_s2[1] - accel_bias[1];
bias.accel_z = sensors.accelerometer_m_s2[2] - accel_bias[2];
// In-run bias estimates
float gyro_bias[3];
_ekf.get_gyro_bias(gyro_bias);
bias.gyro_x_bias = gyro_bias[0];
bias.gyro_y_bias = gyro_bias[1];
bias.gyro_z_bias = gyro_bias[2];
float accel_bias[3];
_ekf.get_accel_bias(accel_bias);
bias.accel_x_bias = accel_bias[0];
bias.accel_y_bias = accel_bias[1];
bias.accel_z_bias = accel_bias[2];
@@ -1239,6 +1220,11 @@ void Ekf2::run()
bias.mag_y_bias = _last_valid_mag_cal[1];
bias.mag_z_bias = _last_valid_mag_cal[2];
// TODO: remove from sensor_bias?
bias.accel_x = sensors.accelerometer_m_s2[0] - accel_bias[0];
bias.accel_y = sensors.accelerometer_m_s2[1] - accel_bias[1];
bias.accel_z = sensors.accelerometer_m_s2[2] - accel_bias[2];
if (_sensor_bias_pub == nullptr) {
_sensor_bias_pub = orb_advertise(ORB_ID(sensor_bias), &bias);
@@ -1246,47 +1232,45 @@ void Ekf2::run()
orb_publish(ORB_ID(sensor_bias), _sensor_bias_pub, &bias);
}
}
}
// publish estimator status
estimator_status_s status;
status.timestamp = now;
_ekf.get_state_delayed(status.states);
_ekf.get_covariances(status.covariances);
_ekf.get_gps_check_status(&status.gps_check_fail_flags);
_ekf.get_control_mode(&status.control_mode_flags);
_ekf.get_filter_fault_status(&status.filter_fault_flags);
_ekf.get_innovation_test_status(&status.innovation_check_flags, &status.mag_test_ratio,
&status.vel_test_ratio, &status.pos_test_ratio,
&status.hgt_test_ratio, &status.tas_test_ratio,
&status.hagl_test_ratio, &status.beta_test_ratio);
// publish estimator status
estimator_status_s status;
status.timestamp = now;
_ekf.get_state_delayed(status.states);
_ekf.get_covariances(status.covariances);
_ekf.get_gps_check_status(&status.gps_check_fail_flags);
status.control_mode_flags = control_status.value;
_ekf.get_filter_fault_status(&status.filter_fault_flags);
_ekf.get_innovation_test_status(&status.innovation_check_flags, &status.mag_test_ratio,
&status.vel_test_ratio, &status.pos_test_ratio,
&status.hgt_test_ratio, &status.tas_test_ratio,
&status.hagl_test_ratio, &status.beta_test_ratio);
status.pos_horiz_accuracy = _vehicle_local_position_pub.get().eph;
status.pos_vert_accuracy = _vehicle_local_position_pub.get().epv;
_ekf.get_ekf_soln_status(&status.solution_status_flags);
_ekf.get_imu_vibe_metrics(status.vibe);
status.time_slip = _last_time_slip_us / 1e6f;
status.nan_flags = 0.0f; // unused
status.health_flags = 0.0f; // unused
status.timeout_flags = 0.0f; // unused
status.pre_flt_fail = _preflt_fail;
status.pos_horiz_accuracy = _vehicle_local_position_pub.get().eph;
status.pos_vert_accuracy = _vehicle_local_position_pub.get().epv;
_ekf.get_ekf_soln_status(&status.solution_status_flags);
_ekf.get_imu_vibe_metrics(status.vibe);
status.time_slip = _last_time_slip_us / 1e6f;
status.nan_flags = 0.0f; // unused
status.health_flags = 0.0f; // unused
status.timeout_flags = 0.0f; // unused
status.pre_flt_fail = _preflt_fail;
if (_estimator_status_pub == nullptr) {
_estimator_status_pub = orb_advertise(ORB_ID(estimator_status), &status);
if (_estimator_status_pub == nullptr) {
_estimator_status_pub = orb_advertise(ORB_ID(estimator_status), &status);
} else {
orb_publish(ORB_ID(estimator_status), _estimator_status_pub, &status);
}
} else {
orb_publish(ORB_ID(estimator_status), _estimator_status_pub, &status);
}
if (updated) {
{
/* Check and save learned magnetometer bias estimates */
// Check if conditions are OK to for learning of magnetometer bias values
if (!vehicle_land_detected.landed && // not on ground
(vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED) && // vehicle is armed
(status.filter_fault_flags == 0) && // there are no filter faults
(status.control_mode_flags & (1 << 5))) { // the EKF is operating in the correct mode
!status.filter_fault_flags && // there are no filter faults
control_status.flags.mag_3D) { // the EKF is operating in the correct mode
if (_last_magcal_us == 0) {
_last_magcal_us = now;
@@ -1307,7 +1291,7 @@ void Ekf2::run()
}
// Start checking mag bias estimates when we have accumulated sufficient calibration time
if (_total_cal_time_us > 120 * 1000 * 1000ULL) {
if (_total_cal_time_us > 120_s) {
// we have sufficient accumulated valid flight time to form a reliable bias estimate
// check that the state variance for each axis is within a range indicating filter convergence
const float max_var_allowed = 100.0f * _mag_bias_saved_variance.get();
@@ -1390,9 +1374,7 @@ void Ekf2::run()
// set the max allowed yaw innovaton depending on whether we are not aiding navigation using
// observations in the NE reference frame.
filter_control_status_u _ekf_control_mask;
_ekf.get_control_mode(&_ekf_control_mask.value);
bool doing_ne_aiding = _ekf_control_mask.flags.gps || _ekf_control_mask.flags.ev_pos;
bool doing_ne_aiding = control_status.flags.gps || control_status.flags.ev_pos;
float yaw_test_limit;
@@ -1446,18 +1428,6 @@ void Ekf2::run()
}
}
} else if (_replay_mode) {
// in replay mode we have to tell the replay module not to wait for an update
// we do this by publishing an attitude with zero timestamp
vehicle_attitude_s att;
att.timestamp = now;
if (_att_pub == nullptr) {
_att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
} else {
orb_publish(ORB_ID(vehicle_attitude), _att_pub, &att);
}
}
// publish ekf2_timestamps
@@ -1491,10 +1461,44 @@ int Ekf2::getRangeSubIndex(const int *subs)
return -1;
}
bool Ekf2::publish_attitude(const sensor_combined_s &sensors)
{
if (_ekf.attitude_valid()) {
// generate vehicle attitude quaternion data
vehicle_attitude_s att;
att.timestamp = hrt_absolute_time();
const Quatf q{_ekf.calculate_quaternion()};
q.copyTo(att.q);
_ekf.get_quat_reset(&att.delta_q_reset[0], &att.quat_reset_counter);
// In-run bias estimates
float gyro_bias[3];
_ekf.get_gyro_bias(gyro_bias);
att.rollspeed = sensors.gyro_rad[0] - gyro_bias[0];
att.pitchspeed = sensors.gyro_rad[1] - gyro_bias[1];
att.yawspeed = sensors.gyro_rad[2] - gyro_bias[2];
int instance;
orb_publish_auto(ORB_ID(vehicle_attitude), &_att_pub, &att, &instance, ORB_PRIO_HIGH);
return true;
} else if (_replay_mode) {
// in replay mode we have to tell the replay module not to wait for an update
// we do this by publishing an attitude with zero timestamp
vehicle_attitude_s att = {};
int instance;
orb_publish_auto(ORB_ID(vehicle_attitude), &_att_pub, &att, &instance, ORB_PRIO_HIGH);
}
return false;
}
bool Ekf2::publish_wind_estimate(const hrt_abstime &timestamp)
{
bool published = false;
if (_ekf.get_wind_status()) {
float velNE_wind[2];
_ekf.get_wind_velocity(velNE_wind);
@@ -1510,17 +1514,13 @@ bool Ekf2::publish_wind_estimate(const hrt_abstime &timestamp)
wind_estimate.variance_north = wind_var[0];
wind_estimate.variance_east = wind_var[1];
if (_wind_pub == nullptr) {
_wind_pub = orb_advertise(ORB_ID(wind_estimate), &wind_estimate);
int instance;
orb_publish_auto(ORB_ID(wind_estimate), &_wind_pub, &wind_estimate, &instance, ORB_PRIO_DEFAULT);
} else {
orb_publish(ORB_ID(wind_estimate), _wind_pub, &wind_estimate);
}
published = true;
return true;
}
return published;
return false;
}
const Vector3f Ekf2::get_vel_body_wind()