Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EKF: Enable compensation for static pressure positional error #7264

Merged
merged 9 commits into from
Jul 31, 2017
2 changes: 2 additions & 0 deletions msg/ekf2_innovations.msg
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ float32 beta_innov_var # synthetic sideslip innovation variance
float32[2] flow_innov_var # flow innovation variance
float32 hagl_innov_var # innovation variance from the terrain estimator for the height above ground level measurement (m^2)
float32[3] output_tracking_error # return a vector containing the output predictor angular, velocity and position tracking error magnitudes (rad), (m/s), (m)
float32[2] drag_innov # drag specific force innovation (m/s/s)
float32[2] drag_innov_var # drag specific force innovation variance (m/s/s)**2
83 changes: 79 additions & 4 deletions src/modules/ekf2/ekf2_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#include <uORB/topics/vehicle_land_detected.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/sensor_selection.h>
#include <uORB/topics/sensor_baro.h>

#include <ecl/EKF/ekf.h>

Expand Down Expand Up @@ -183,6 +184,9 @@ class Ekf2 : public control::SuperBlock, public ModuleBase<Ekf2>
math::LowPassFilter2p _lp_pitch_rate; ///< Low pass filter applied to pitch rates published on the control_state message
math::LowPassFilter2p _lp_yaw_rate; ///< Low pass filter applied to yaw rates published on the control_state message

// Used to correct baro data for positional errors
Vector3f _vel_body_wind = {}; // XYZ velocity relative to wind in body frame (m/s)

Ekf _ekf;

parameters *_params; ///< pointer to ekf parameter struct (located in _ekf class instance)
Expand Down Expand Up @@ -319,6 +323,22 @@ class Ekf2 : public control::SuperBlock, public ModuleBase<Ekf2>
control::BlockParamFloat
_mag_bias_alpha; ///< maximum fraction of the learned magnetometer bias that is saved at each disarm

// Multi-rotor drag specific force fusion
control::BlockParamExtFloat
_drag_noise; ///< observation noise variance for drag specific force measurements (m/sec**2)**2
control::BlockParamExtFloat _bcoef_x; ///< ballistic coefficient along the X-axis (kg/m**2)
control::BlockParamExtFloat _bcoef_y; ///< ballistic coefficient along the Y-axis (kg/m**2)

// Corrections for static pressure position error where Ps_error = Ps_meas - Ps_truth
// Coef = Ps_error / Pdynamic, where Pdynamic = 1/2 * density * TAS**2
control::BlockParamFloat _aspd_max; ///< upper limit on airspeed used for correction (m/s**2)
control::BlockParamFloat
_K_pstatic_coef_xp; ///< static pressure position error coefficient along the positive X body axis
control::BlockParamFloat
_K_pstatic_coef_xn; ///< static pressure position error coefficient along the negative X body axis
control::BlockParamFloat _K_pstatic_coef_y; ///< static pressure position error coefficient along the Y body axis
control::BlockParamFloat _K_pstatic_coef_z; ///< static pressure position error coefficient along the Z body axis

};

Ekf2::Ekf2():
Expand Down Expand Up @@ -430,8 +450,15 @@ Ekf2::Ekf2():
_mag_bias_z(this, "EKF2_MAGBIAS_Z", false),
_mag_bias_id(this, "EKF2_MAGBIAS_ID", false),
_mag_bias_saved_variance(this, "EKF2_MAGB_VREF", false),
_mag_bias_alpha(this, "EKF2_MAGB_K", false)

_mag_bias_alpha(this, "EKF2_MAGB_K", false),
_drag_noise(this, "EKF2_DRAG_NOISE", false, _params->drag_noise),
_bcoef_x(this, "EKF2_BCOEF_X", false, _params->bcoef_x),
_bcoef_y(this, "EKF2_BCOEF_Y", false, _params->bcoef_y),
_aspd_max(this, "EKF2_ASPD_MAX", false),
_K_pstatic_coef_xp(this, "EKF2_PCOEF_XP", false),
_K_pstatic_coef_xn(this, "EKF2_PCOEF_XN", false),
_K_pstatic_coef_y(this, "EKF2_PCOEF_Y", false),
_K_pstatic_coef_z(this, "EKF2_PCOEF_Z", false)
{

}
Expand Down Expand Up @@ -463,6 +490,7 @@ void Ekf2::run()
int vehicle_land_detected_sub = orb_subscribe(ORB_ID(vehicle_land_detected));
int status_sub = orb_subscribe(ORB_ID(vehicle_status));
int sensor_selection_sub = orb_subscribe(ORB_ID(sensor_selection));
int sensor_baro_sub = orb_subscribe(ORB_ID(sensor_baro));

px4_pollfd_struct_t fds[2] = {};
fds[0].fd = sensors_sub;
Expand All @@ -486,6 +514,8 @@ void Ekf2::run()
vehicle_attitude_s ev_att = {};
vehicle_status_s vehicle_status = {};
sensor_selection_s sensor_selection = {};
sensor_baro_s sensor_baro = {};
sensor_baro.pressure = 1013.5; // initialise pressure to sea level

while (!should_exit()) {
int ret = px4_poll(fds, sizeof(fds) / sizeof(fds[0]), 1000);
Expand Down Expand Up @@ -684,7 +714,36 @@ void Ekf2::run()
uint32_t balt_time_ms = _balt_time_sum_ms / _balt_sample_count;

if (balt_time_ms - _balt_time_ms_last_used > (uint32_t)_params->sensor_interval_min_ms) {
// take mean across sample period
float balt_data_avg = _balt_data_sum / (float)_balt_sample_count;

// estimate air density assuming typical 20degC ambient temperature
orb_copy(ORB_ID(sensor_baro), sensor_baro_sub, &sensor_baro);
const float pressure_to_density = 100.0f / (CONSTANTS_AIR_GAS_CONST * (20.0f - CONSTANTS_ABSOLUTE_NULL_CELSIUS));
float rho = pressure_to_density * sensor_baro.pressure;
_ekf.set_air_density(rho);

// calculate static pressure error = Pmeas - Ptruth
// model position error sensitivity as a body fixed ellipse with different scale in the positive and negtive X direction
float max_airspeed_sq = _aspd_max.get();
max_airspeed_sq *= max_airspeed_sq;
float K_pstatic_coef_x;

if (_vel_body_wind(0) >= 0.0f) {
K_pstatic_coef_x = _K_pstatic_coef_xp.get();

} else {
K_pstatic_coef_x = _K_pstatic_coef_xn.get();
}

float pstatic_err = 0.5f * rho * (K_pstatic_coef_x * fminf(_vel_body_wind(0) * _vel_body_wind(0), max_airspeed_sq) +
_K_pstatic_coef_y.get() * fminf(_vel_body_wind(1) * _vel_body_wind(1), max_airspeed_sq) +
_K_pstatic_coef_z.get() * fminf(_vel_body_wind(2) * _vel_body_wind(2), max_airspeed_sq));

// correct baro measurement using pressure error estimate and assuming sea level gravity
balt_data_avg += pstatic_err / (rho * 9.80665f);

// push to estimator
_ekf.setBaroData(1000 * (uint64_t)balt_time_ms, balt_data_avg);
_balt_time_ms_last_used = balt_time_ms;
_balt_time_sum_ms = 0;
Expand Down Expand Up @@ -823,6 +882,12 @@ void Ekf2::run()
ctrl_state.y_vel = v_b(1);
ctrl_state.z_vel = v_b(2);

// Calculate velocity relative to wind in body frame
float velNE_wind[2] = {};
_ekf.get_wind_velocity(velNE_wind);
v_n(0) -= velNE_wind[0];
v_n(1) -= velNE_wind[1];
_vel_body_wind = R_to_body * v_n;

// Local Position NED
float position[3];
Expand Down Expand Up @@ -860,17 +925,25 @@ void Ekf2::run()
&& airspeed.timestamp > 0) {
ctrl_state.airspeed = airspeed.indicated_airspeed_m_s;
ctrl_state.airspeed_valid = true;

} else {
ctrl_state.airspeed = sqrtf(v_n(0) * v_n(0) + v_n(1) * v_n(1) + v_n(2) * v_n(2));
ctrl_state.airspeed_valid = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it not be set to true? Same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these two cases we no longer know what the wind speed is, so the ground speed is used as a surrogate and the validity is set to false.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it's just confusing if the value is set, but the valid flag is set to false. I'd add a comment, otherwise it's good to merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

}

} else if (_airspeed_mode.get() == control_state_s::AIRSPD_MODE_EST) {
if (_ekf.local_position_is_valid()) {
ctrl_state.airspeed = sqrtf(velocity[0] * velocity[0] + velocity[1] * velocity[1] + velocity[2] * velocity[2]);
ctrl_state.airspeed = sqrtf(v_n(0) * v_n(0) + v_n(1) * v_n(1) + v_n(2) * v_n(2));
ctrl_state.airspeed_valid = true;
}

} else if (_airspeed_mode.get() == control_state_s::AIRSPD_MODE_DISABLED) {
// do nothing, airspeed has been declared as non-valid above, controllers
// airspeed has been declared as non-valid above, controllers
// will handle this assuming always trim airspeed
if (_ekf.local_position_is_valid()) {
ctrl_state.airspeed = sqrtf(v_n(0) * v_n(0) + v_n(1) * v_n(1) + v_n(2) * v_n(2));
ctrl_state.airspeed_valid = false;
}
}

// publish control state data
Expand Down Expand Up @@ -1159,6 +1232,7 @@ void Ekf2::run()
_ekf.get_beta_innov(&innovations.beta_innov);
_ekf.get_flow_innov(&innovations.flow_innov[0]);
_ekf.get_hagl_innov(&innovations.hagl_innov);
_ekf.get_drag_innov(&innovations.drag_innov[0]);

_ekf.get_vel_pos_innov_var(&innovations.vel_pos_innov_var[0]);
_ekf.get_mag_innov_var(&innovations.mag_innov_var[0]);
Expand All @@ -1167,6 +1241,7 @@ void Ekf2::run()
_ekf.get_beta_innov_var(&innovations.beta_innov_var);
_ekf.get_flow_innov_var(&innovations.flow_innov_var[0]);
_ekf.get_hagl_innov_var(&innovations.hagl_innov_var);
_ekf.get_drag_innov_var(&innovations.drag_innov_var[0]);

_ekf.get_output_tracking_error(&innovations.output_tracking_error[0]);

Expand Down
98 changes: 97 additions & 1 deletion src/modules/ekf2/ekf2_params.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,15 +541,17 @@ PARAM_DEFINE_INT32(EKF2_REC_RPL, 0);
* 2 : Set to true to inhibit IMU bias estimation
* 3 : Set to true to enable vision position fusion
* 4 : Set to true to enable vision yaw fusion
* 5 : Set to true to enable multi-rotor drag specific force fusion
*
* @group EKF2
* @min 0
* @max 28
* @max 63
* @bit 0 use GPS
* @bit 1 use optical flow
* @bit 2 inhibit IMU bias estimation
* @bit 3 vision position fusion
* @bit 4 vision yaw fusion
* @bit 5 multi-rotor drag fusion
*/
PARAM_DEFINE_INT32(EKF2_AID_MASK, 1);

Expand Down Expand Up @@ -1056,3 +1058,97 @@ PARAM_DEFINE_FLOAT(EKF2_RNG_A_HMAX, 5.0f);
* @max 5.0
*/
PARAM_DEFINE_FLOAT(EKF2_RNG_A_IGATE, 1.0f);

/**
* Specific drag force observation noise variance used by the multi-rotor specific drag force model.
* Increasing it makes the multi-rotor wind estimates adjust more slowly.
*
* @group EKF2
* @min 0.5
* @max 10.0
* @unit (m/sec**2)**2
* @decimal 2
*/
PARAM_DEFINE_FLOAT(EKF2_DRAG_NOISE, 2.5f);

/**
* X-axis ballistic coefficient used by the multi-rotor specific drag force model.
* This should be adjusted to minimise variance of the X-axis drag specific force innovation sequence.
*
* @group EKF2
* @min 1.0
* @max 100.0
* @unit kg/m**2
* @decimal 1
*/
PARAM_DEFINE_FLOAT(EKF2_BCOEF_X, 25.0f);

/**
* Y-axis ballistic coefficient used by the multi-rotor specific drag force model.
* This should be adjusted to minimise variance of the Y-axis drag specific force innovation sequence.
*
* @group EKF2
* @min 1.0
* @max 100.0
* @unit kg/m**2
* @decimal 1
*/
PARAM_DEFINE_FLOAT(EKF2_BCOEF_Y, 25.0f);

/**
* Upper limit on airspeed along individual axes used to correct baro for position error effects
*
* @group EKF2
* @min 5.0
* @max 50.0
* @unit m/s
* @decimal 1
*/
PARAM_DEFINE_FLOAT(EKF2_ASPD_MAX, 20.0f);

/**
* Static pressure position error coefficient for the positive X axis
* This is the ratio of static pressure error to dynamic pressure generated by a positive wind relative velocity along the X body axis.
* If the baro height estimate rises during forward flight, then this will be a negative number.
*
* @group EKF2
* @min -0.5
* @max 0.5
* @decimal 2
*/
PARAM_DEFINE_FLOAT(EKF2_PCOEF_XP, 0.0f);

/**
* Static pressure position error coefficient for the negative X axis.
* This is the ratio of static pressure error to dynamic pressure generated by a negative wind relative velocity along the X body axis.
* If the baro height estimate rises during backwards flight, then this will be a negative number.
*
* @group EKF2
* @min -0.5
* @max 0.5
* @decimal 2
*/
PARAM_DEFINE_FLOAT(EKF2_PCOEF_XN, 0.0f);

/**
* Pressure position error coefficient for the Y axis.
* This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Y body axis.
* If the baro height estimate rises during sideways flight, then this will be a negative number.
*
* @group EKF2
* @min -0.5
* @max 0.5
* @decimal 2
*/
PARAM_DEFINE_FLOAT(EKF2_PCOEF_Y, 0.0f);

/**
* Static pressure position error coefficient for the Z axis.
* This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Z body axis.
*
* @group EKF2
* @min -0.5
* @max 0.5
* @decimal 2
*/
PARAM_DEFINE_FLOAT(EKF2_PCOEF_Z, 0.0f);