mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
Decreases the message size from 780 to 280 bytes. In particular, all modules using sensor_combined must use the integral now. The sensor value can easily be reconstructed by dividing with dt. Voters now need to be moved into sensors module, because error count and priority is removed from the topic. Any module that requires additional data from a sensor can subscribe to the raw sensor topics. At two places, values are set to zero instead of subscribing to the raw sensors (with the assumption that no one reads them): - mavlink mavlink_highres_imu_t::abs_pressure - sdlog2: sensor temperatures
883 lines
22 KiB
C++
883 lines
22 KiB
C++
#include "BlockLocalPositionEstimator.hpp"
|
|
#include <systemlib/mavlink_log.h>
|
|
#include <fcntl.h>
|
|
#include <systemlib/err.h>
|
|
#include <matrix/math.hpp>
|
|
#include <cstdlib>
|
|
|
|
orb_advert_t mavlink_log_pub = nullptr;
|
|
|
|
// timeouts for sensors in microseconds
|
|
static const uint32_t EST_SRC_TIMEOUT = 10000; // 0.01 s
|
|
|
|
// required standard deviation of estimate for estimator to publish data
|
|
static const uint32_t EST_STDDEV_XY_VALID = 2.0; // 2.0 m
|
|
static const uint32_t EST_STDDEV_Z_VALID = 2.0; // 2.0 m
|
|
static const uint32_t EST_STDDEV_TZ_VALID = 2.0; // 2.0 m
|
|
static const bool integrate = true; // use accel for integrating
|
|
|
|
// minimum flow altitude
|
|
static const float flow_min_agl = 0.3;
|
|
|
|
BlockLocalPositionEstimator::BlockLocalPositionEstimator() :
|
|
// this block has no parent, and has name LPE
|
|
SuperBlock(NULL, "LPE"),
|
|
// subscriptions, set rate, add to list
|
|
_sub_armed(ORB_ID(actuator_armed), 1000 / 2, 0, &getSubscriptions()),
|
|
_sub_att(ORB_ID(vehicle_attitude), 1000 / 100, 0, &getSubscriptions()),
|
|
// flow 10 hz
|
|
_sub_flow(ORB_ID(optical_flow), 1000 / 10, 0, &getSubscriptions()),
|
|
// main prediction loop, 100 hz
|
|
_sub_sensor(ORB_ID(sensor_combined), 1000 / 100, 0, &getSubscriptions()),
|
|
// status updates 2 hz
|
|
_sub_param_update(ORB_ID(parameter_update), 1000 / 2, 0, &getSubscriptions()),
|
|
_sub_manual(ORB_ID(manual_control_setpoint), 1000 / 2, 0, &getSubscriptions()),
|
|
_sub_home(ORB_ID(home_position), 1000 / 2, 0, &getSubscriptions()),
|
|
// gps 10 hz
|
|
_sub_gps(ORB_ID(vehicle_gps_position), 1000 / 10, 0, &getSubscriptions()),
|
|
// vision 5 hz
|
|
_sub_vision_pos(ORB_ID(vision_position_estimate), 1000 / 5, 0, &getSubscriptions()),
|
|
// all distance sensors, 10 hz
|
|
_sub_mocap(ORB_ID(att_pos_mocap), 1000 / 10, 0, &getSubscriptions()),
|
|
_sub_dist0(ORB_ID(distance_sensor), 1000 / 10, 0, &getSubscriptions()),
|
|
_sub_dist1(ORB_ID(distance_sensor), 1000 / 10, 1, &getSubscriptions()),
|
|
_sub_dist2(ORB_ID(distance_sensor), 1000 / 10, 2, &getSubscriptions()),
|
|
_sub_dist3(ORB_ID(distance_sensor), 1000 / 10, 3, &getSubscriptions()),
|
|
_dist_subs(),
|
|
_sub_lidar(NULL),
|
|
_sub_sonar(NULL),
|
|
|
|
// publications
|
|
_pub_lpos(ORB_ID(vehicle_local_position), -1, &getPublications()),
|
|
_pub_gpos(ORB_ID(vehicle_global_position), -1, &getPublications()),
|
|
_pub_est_status(ORB_ID(estimator_status), -1, &getPublications()),
|
|
|
|
// map projection
|
|
_map_ref(),
|
|
|
|
// block parameters
|
|
_xy_pub_thresh(this, "XY_PUB"),
|
|
_z_pub_thresh(this, "Z_PUB"),
|
|
_sonar_z_stddev(this, "SNR_Z"),
|
|
_sonar_z_offset(this, "SNR_OFF_Z"),
|
|
_lidar_z_stddev(this, "LDR_Z"),
|
|
_lidar_z_offset(this, "LDR_OFF_Z"),
|
|
_accel_xy_stddev(this, "ACC_XY"),
|
|
_accel_z_stddev(this, "ACC_Z"),
|
|
_baro_stddev(this, "BAR_Z"),
|
|
_gps_on(this, "GPS_ON"),
|
|
_gps_delay(this, "GPS_DELAY"),
|
|
_gps_xy_stddev(this, "GPS_XY"),
|
|
_gps_z_stddev(this, "GPS_Z"),
|
|
_gps_vxy_stddev(this, "GPS_VXY"),
|
|
_gps_vz_stddev(this, "GPS_VZ"),
|
|
_gps_eph_max(this, "EPH_MAX"),
|
|
_gps_epv_max(this, "EPV_MAX"),
|
|
_vision_xy_stddev(this, "VIS_XY"),
|
|
_vision_z_stddev(this, "VIS_Z"),
|
|
_vision_on(this, "VIS_ON"),
|
|
_mocap_p_stddev(this, "VIC_P"),
|
|
_flow_z_offset(this, "FLW_OFF_Z"),
|
|
_flow_xy_stddev(this, "FLW_XY"),
|
|
//_flow_board_x_offs(NULL, "SENS_FLW_XOFF"),
|
|
//_flow_board_y_offs(NULL, "SENS_FLW_YOFF"),
|
|
_flow_min_q(this, "FLW_QMIN"),
|
|
_pn_p_noise_density(this, "PN_P"),
|
|
_pn_v_noise_density(this, "PN_V"),
|
|
_pn_b_noise_density(this, "PN_B"),
|
|
_pn_t_noise_density(this, "PN_T"),
|
|
|
|
// init home
|
|
_init_home_lat(this, "LAT"),
|
|
_init_home_lon(this, "LON"),
|
|
|
|
// flow gyro
|
|
_flow_gyro_x_high_pass(this, "FGYRO_HP"),
|
|
_flow_gyro_y_high_pass(this, "FGYRO_HP"),
|
|
|
|
// stats
|
|
_baroStats(this, ""),
|
|
_sonarStats(this, ""),
|
|
_lidarStats(this, ""),
|
|
_flowQStats(this, ""),
|
|
_visionStats(this, ""),
|
|
_mocapStats(this, ""),
|
|
_gpsStats(this, ""),
|
|
|
|
// low pass
|
|
_xLowPass(this, "X_LP"),
|
|
// use same lp constant for agl
|
|
_aglLowPass(this, "X_LP"),
|
|
|
|
// delay
|
|
_xDelay(this, ""),
|
|
_tDelay(this, ""),
|
|
|
|
// misc
|
|
_polls(),
|
|
_timeStamp(hrt_absolute_time()),
|
|
_time_last_hist(0),
|
|
_time_last_xy(0),
|
|
_time_last_z(0),
|
|
_time_last_tz(0),
|
|
_time_last_flow(0),
|
|
_time_last_baro(0),
|
|
_time_last_gps(0),
|
|
_time_last_lidar(0),
|
|
_time_last_sonar(0),
|
|
_time_init_sonar(0),
|
|
_time_last_vision_p(0),
|
|
_time_last_mocap(0),
|
|
|
|
// initialization flags
|
|
_receivedGps(false),
|
|
_baroInitialized(false),
|
|
_gpsInitialized(false),
|
|
_lidarInitialized(false),
|
|
_sonarInitialized(false),
|
|
_flowInitialized(false),
|
|
_visionInitialized(false),
|
|
_mocapInitialized(false),
|
|
|
|
// reference altitudes
|
|
_altHome(0),
|
|
_altHomeInitialized(false),
|
|
_baroAltHome(0),
|
|
_gpsAltHome(0),
|
|
_visionHome(),
|
|
_mocapHome(),
|
|
|
|
// flow integration
|
|
_flowX(0),
|
|
_flowY(0),
|
|
_flowMeanQual(0),
|
|
|
|
// status
|
|
_validXY(false),
|
|
_validZ(false),
|
|
_validTZ(false),
|
|
_xyTimeout(true),
|
|
_zTimeout(true),
|
|
_tzTimeout(true),
|
|
_lastArmedState(false),
|
|
|
|
// faults
|
|
_baroFault(FAULT_NONE),
|
|
_gpsFault(FAULT_NONE),
|
|
_lidarFault(FAULT_NONE),
|
|
_flowFault(FAULT_NONE),
|
|
_sonarFault(FAULT_NONE),
|
|
_visionFault(FAULT_NONE),
|
|
_mocapFault(FAULT_NONE),
|
|
|
|
// loop performance
|
|
_loop_perf(),
|
|
_interval_perf(),
|
|
_err_perf(),
|
|
|
|
// kf matrices
|
|
_x(), _u(), _P()
|
|
{
|
|
// assign distance subs to array
|
|
_dist_subs[0] = &_sub_dist0;
|
|
_dist_subs[1] = &_sub_dist1;
|
|
_dist_subs[2] = &_sub_dist2;
|
|
_dist_subs[3] = &_sub_dist3;
|
|
|
|
// setup event triggering based on new flow messages to integrate
|
|
_polls[POLL_FLOW].fd = _sub_flow.getHandle();
|
|
_polls[POLL_FLOW].events = POLLIN;
|
|
|
|
_polls[POLL_PARAM].fd = _sub_param_update.getHandle();
|
|
_polls[POLL_PARAM].events = POLLIN;
|
|
|
|
_polls[POLL_SENSORS].fd = _sub_sensor.getHandle();
|
|
_polls[POLL_SENSORS].events = POLLIN;
|
|
|
|
// initialize A, B, P, x, u
|
|
_x.setZero();
|
|
_u.setZero();
|
|
_flowX = 0;
|
|
_flowY = 0;
|
|
initSS();
|
|
|
|
// perf counters
|
|
_loop_perf = perf_alloc(PC_ELAPSED,
|
|
"local_position_estimator_runtime");
|
|
//_interval_perf = perf_alloc(PC_INTERVAL,
|
|
//"local_position_estimator_interval");
|
|
_err_perf = perf_alloc(PC_COUNT, "local_position_estimator_err");
|
|
|
|
// map
|
|
_map_ref.init_done = false;
|
|
|
|
// intialize parameter dependent matrices
|
|
updateParams();
|
|
}
|
|
|
|
BlockLocalPositionEstimator::~BlockLocalPositionEstimator()
|
|
{
|
|
}
|
|
|
|
Vector<float, BlockLocalPositionEstimator::n_x> BlockLocalPositionEstimator::dynamics(
|
|
float t,
|
|
const Vector<float, BlockLocalPositionEstimator::n_x> &x,
|
|
const Vector<float, BlockLocalPositionEstimator::n_u> &u)
|
|
{
|
|
return _A * x + _B * u;
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::update()
|
|
{
|
|
|
|
// wait for a sensor update, check for exit condition every 100 ms
|
|
int ret = px4_poll(_polls, 3, 100);
|
|
|
|
if (ret < 0) {
|
|
/* poll error, count it in perf */
|
|
perf_count(_err_perf);
|
|
return;
|
|
}
|
|
|
|
uint64_t newTimeStamp = hrt_absolute_time();
|
|
float dt = (newTimeStamp - _timeStamp) / 1.0e6f;
|
|
_timeStamp = newTimeStamp;
|
|
|
|
// set dt for all child blocks
|
|
setDt(dt);
|
|
|
|
// auto-detect connected rangefinders while not armed
|
|
bool armedState = _sub_armed.get().armed;
|
|
|
|
if (!armedState && (_sub_lidar == NULL || _sub_sonar == NULL)) {
|
|
detectDistanceSensors();
|
|
}
|
|
|
|
// reset pos, vel, and terrain on arming
|
|
if (!_lastArmedState && armedState) {
|
|
|
|
// we just armed, we are at home position on the ground
|
|
_x(X_x) = 0;
|
|
_x(X_y) = 0;
|
|
|
|
// the pressure altitude of home may have drifted, so we don't
|
|
// reset z to zero
|
|
|
|
// reset flow integral
|
|
_flowX = 0;
|
|
_flowY = 0;
|
|
|
|
// we aren't moving, all velocities are zero
|
|
_x(X_vx) = 0;
|
|
_x(X_vy) = 0;
|
|
_x(X_vz) = 0;
|
|
|
|
// assume we are on the ground, so terrain alt is local alt
|
|
_x(X_tz) = _x(X_z);
|
|
|
|
// reset lowpass filter as well
|
|
_xLowPass.setState(_x);
|
|
_aglLowPass.setState(0);
|
|
}
|
|
|
|
_lastArmedState = armedState;
|
|
|
|
// see which updates are available
|
|
bool flowUpdated = _sub_flow.updated();
|
|
bool paramsUpdated = _sub_param_update.updated();
|
|
bool baroUpdated = _sub_sensor.updated();
|
|
bool gpsUpdated = _gps_on.get() && _sub_gps.updated();
|
|
bool homeUpdated = _sub_home.updated();
|
|
bool visionUpdated = _vision_on.get() && _sub_vision_pos.updated();
|
|
bool mocapUpdated = _sub_mocap.updated();
|
|
bool lidarUpdated = (_sub_lidar != NULL) && _sub_lidar->updated();
|
|
bool sonarUpdated = (_sub_sonar != NULL) && _sub_sonar->updated();
|
|
|
|
// get new data
|
|
updateSubscriptions();
|
|
|
|
// update parameters
|
|
if (paramsUpdated) {
|
|
updateParams();
|
|
updateSSParams();
|
|
}
|
|
|
|
// update home position projection
|
|
if (homeUpdated) {
|
|
updateHome();
|
|
}
|
|
|
|
// is xy valid?
|
|
bool xy_stddev_ok = sqrtf(math::max(_P(X_x, X_x), _P(X_y, X_y))) < _xy_pub_thresh.get();
|
|
|
|
if (_validXY) {
|
|
// if valid and gps has timed out, set to not valid
|
|
if (!xy_stddev_ok && !_gpsInitialized) {
|
|
_validXY = false;
|
|
}
|
|
|
|
} else {
|
|
if (xy_stddev_ok) {
|
|
_validXY = true;
|
|
}
|
|
}
|
|
|
|
// is z valid?
|
|
bool z_stddev_ok = sqrtf(_P(X_z, X_z)) < _z_pub_thresh.get();
|
|
|
|
if (_validZ) {
|
|
// if valid and baro has timed out, set to not valid
|
|
if (!z_stddev_ok && !_baroInitialized) {
|
|
_validZ = false;
|
|
}
|
|
|
|
} else {
|
|
if (z_stddev_ok) {
|
|
_validZ = true;
|
|
}
|
|
}
|
|
|
|
// is terrain valid?
|
|
bool tz_stddev_ok = sqrtf(_P(X_tz, X_tz)) < _z_pub_thresh.get();
|
|
|
|
if (_validTZ) {
|
|
if (!tz_stddev_ok) {
|
|
_validTZ = false;
|
|
}
|
|
|
|
} else {
|
|
if (tz_stddev_ok) {
|
|
_validTZ = true;
|
|
}
|
|
}
|
|
|
|
// timeouts
|
|
if (_validXY) {
|
|
_time_last_xy = _timeStamp;
|
|
}
|
|
|
|
if (_validZ) {
|
|
_time_last_z = _timeStamp;
|
|
}
|
|
|
|
if (_validTZ) {
|
|
_time_last_tz = _timeStamp;
|
|
}
|
|
|
|
// check timeouts
|
|
checkTimeouts();
|
|
|
|
// if we have no lat, lon initialize projection at 0,0
|
|
if (_validXY && !_map_ref.init_done) {
|
|
map_projection_init(&_map_ref,
|
|
_init_home_lat.get(),
|
|
_init_home_lon.get());
|
|
}
|
|
|
|
// reinitialize x if necessary
|
|
bool reinit_x = false;
|
|
|
|
for (int i = 0; i < n_x; i++) {
|
|
// should we do a reinit
|
|
// of sensors here?
|
|
// don't want it to take too long
|
|
if (!PX4_ISFINITE(_x(i))) {
|
|
reinit_x = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (reinit_x) {
|
|
for (int i = 0; i < n_x; i++) {
|
|
_x(i) = 0;
|
|
}
|
|
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] reinit x");
|
|
}
|
|
|
|
// reinitialize P if necessary
|
|
bool reinit_P = false;
|
|
|
|
for (int i = 0; i < n_x; i++) {
|
|
for (int j = 0; j < n_x; j++) {
|
|
if (!PX4_ISFINITE(_P(i, j))) {
|
|
reinit_P = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (reinit_P) { break; }
|
|
}
|
|
|
|
if (reinit_P) {
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] reinit P");
|
|
initP();
|
|
}
|
|
|
|
// do prediction
|
|
predict();
|
|
|
|
// sensor corrections/ initializations
|
|
if (gpsUpdated) {
|
|
if (!_gpsInitialized) {
|
|
gpsInit();
|
|
|
|
} else {
|
|
gpsCorrect();
|
|
}
|
|
}
|
|
|
|
if (baroUpdated) {
|
|
if (!_baroInitialized) {
|
|
baroInit();
|
|
|
|
} else {
|
|
baroCorrect();
|
|
}
|
|
}
|
|
|
|
if (lidarUpdated) {
|
|
if (!_lidarInitialized) {
|
|
lidarInit();
|
|
|
|
} else {
|
|
lidarCorrect();
|
|
}
|
|
}
|
|
|
|
if (sonarUpdated) {
|
|
if (!_sonarInitialized) {
|
|
sonarInit();
|
|
|
|
} else {
|
|
sonarCorrect();
|
|
}
|
|
}
|
|
|
|
if (flowUpdated) {
|
|
if (!_flowInitialized) {
|
|
flowInit();
|
|
|
|
} else {
|
|
perf_begin(_loop_perf);// TODO
|
|
flowCorrect();
|
|
//perf_count(_interval_perf);
|
|
perf_end(_loop_perf);
|
|
}
|
|
}
|
|
|
|
if (visionUpdated) {
|
|
if (!_visionInitialized) {
|
|
visionInit();
|
|
|
|
} else {
|
|
visionCorrect();
|
|
}
|
|
}
|
|
|
|
if (mocapUpdated) {
|
|
if (!_mocapInitialized) {
|
|
mocapInit();
|
|
|
|
} else {
|
|
mocapCorrect();
|
|
}
|
|
}
|
|
|
|
if (_altHomeInitialized) {
|
|
// update all publications if possible
|
|
publishLocalPos();
|
|
publishEstimatorStatus();
|
|
|
|
if (_validXY) {
|
|
publishGlobalPos();
|
|
}
|
|
}
|
|
|
|
// propagate delayed state, no matter what
|
|
// if state is frozen, delayed state still
|
|
// needs to be propagated with frozen state
|
|
float dt_hist = 1.0e-6f * (_timeStamp - _time_last_hist);
|
|
|
|
if (_time_last_hist == 0 ||
|
|
(dt_hist > HIST_STEP)) {
|
|
_tDelay.update(Scalar<uint64_t>(_timeStamp));
|
|
_xDelay.update(_x);
|
|
_time_last_hist = _timeStamp;
|
|
}
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::checkTimeouts()
|
|
{
|
|
if (_timeStamp - _time_last_xy > EST_SRC_TIMEOUT) {
|
|
if (!_xyTimeout) {
|
|
_xyTimeout = true;
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] xy timeout ");
|
|
}
|
|
|
|
} else if (_xyTimeout) {
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] xy resume ");
|
|
_xyTimeout = false;
|
|
}
|
|
|
|
if (_timeStamp - _time_last_z > EST_SRC_TIMEOUT) {
|
|
if (!_zTimeout) {
|
|
_zTimeout = true;
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] z timeout ");
|
|
}
|
|
|
|
} else if (_zTimeout) {
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] z resume ");
|
|
_zTimeout = false;
|
|
}
|
|
|
|
if (_timeStamp - _time_last_tz > EST_SRC_TIMEOUT) {
|
|
if (!_tzTimeout) {
|
|
_tzTimeout = true;
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] tz timeout ");
|
|
}
|
|
|
|
} else if (_tzTimeout) {
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] tz resume ");
|
|
_tzTimeout = false;
|
|
}
|
|
|
|
lidarCheckTimeout();
|
|
sonarCheckTimeout();
|
|
baroCheckTimeout();
|
|
gpsCheckTimeout();
|
|
flowCheckTimeout();
|
|
visionCheckTimeout();
|
|
mocapCheckTimeout();
|
|
}
|
|
|
|
float BlockLocalPositionEstimator::agl()
|
|
{
|
|
return _x(X_tz) - _x(X_z);
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::correctionLogic(Vector<float, n_x> &dx)
|
|
{
|
|
// don't correct bias when rotating rapidly
|
|
float ang_speed = sqrtf(
|
|
_sub_att.get().rollspeed * _sub_att.get().rollspeed +
|
|
_sub_att.get().pitchspeed * _sub_att.get().pitchspeed +
|
|
_sub_att.get().yawspeed * _sub_att.get().yawspeed);
|
|
|
|
if (ang_speed > 1) {
|
|
dx(X_bx) = 0;
|
|
dx(X_by) = 0;
|
|
dx(X_bz) = 0;
|
|
}
|
|
|
|
if (!_validXY) {
|
|
dx(X_x) = 0;
|
|
dx(X_y) = 0;
|
|
dx(X_vx) = 0;
|
|
dx(X_vy) = 0;
|
|
dx(X_bx) = 0;
|
|
dx(X_by) = 0;
|
|
}
|
|
|
|
if (!_validZ) {
|
|
dx(X_z) = 0;
|
|
dx(X_vz) = 0;
|
|
dx(X_bz) = 0;
|
|
}
|
|
|
|
if (!_validTZ) {
|
|
dx(X_tz) = 0;
|
|
}
|
|
|
|
// saturate bias
|
|
float bx = dx(X_bx) + _x(X_bx);
|
|
float by = dx(X_by) + _x(X_by);
|
|
float bz = dx(X_bz) + _x(X_bz);
|
|
|
|
if (std::abs(bx) > BIAS_MAX) { bx = BIAS_MAX * bx / std::abs(bx); }
|
|
|
|
if (std::abs(by) > BIAS_MAX) { by = BIAS_MAX * by / std::abs(by); }
|
|
|
|
if (std::abs(bz) > BIAS_MAX) { bz = BIAS_MAX * bz / std::abs(bz); }
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::detectDistanceSensors()
|
|
{
|
|
for (int i = 0; i < N_DIST_SUBS; i++) {
|
|
uORB::Subscription<distance_sensor_s> *s = _dist_subs[i];
|
|
|
|
if (s == _sub_lidar || s == _sub_sonar) { continue; }
|
|
|
|
if (s->updated()) {
|
|
s->update();
|
|
|
|
if (s->get().timestamp == 0) { continue; }
|
|
|
|
if (s->get().type == \
|
|
distance_sensor_s::MAV_DISTANCE_SENSOR_LASER &&
|
|
_sub_lidar == NULL) {
|
|
_sub_lidar = s;
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] Lidar detected with ID %i", i);
|
|
|
|
} else if (s->get().type == \
|
|
distance_sensor_s::MAV_DISTANCE_SENSOR_ULTRASOUND &&
|
|
_sub_sonar == NULL) {
|
|
_sub_sonar = s;
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] Sonar detected with ID %i", i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::updateHome()
|
|
{
|
|
double lat = _sub_home.get().lat;
|
|
double lon = _sub_home.get().lon;
|
|
float alt = _sub_home.get().alt;
|
|
|
|
// updating home causes absolute measurements
|
|
// like gps and baro to be off, need to allow it
|
|
// to reset by resetting covariance
|
|
initP();
|
|
mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] home "
|
|
"lat %6.2f lon %6.2f alt %5.1f m",
|
|
lat, lon, double(alt));
|
|
map_projection_init(&_map_ref, lat, lon);
|
|
float delta_alt = alt - _altHome;
|
|
_altHomeInitialized = true;
|
|
_altHome = alt;
|
|
_gpsAltHome += delta_alt;
|
|
_baroAltHome += delta_alt;
|
|
_visionHome(2) += delta_alt;
|
|
_mocapHome(2) += delta_alt;
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::publishLocalPos()
|
|
{
|
|
const Vector<float, n_x> &xLP = _xLowPass.getState();
|
|
|
|
// publish local position
|
|
if (PX4_ISFINITE(_x(X_x)) && PX4_ISFINITE(_x(X_y)) && PX4_ISFINITE(_x(X_z)) &&
|
|
PX4_ISFINITE(_x(X_vx)) && PX4_ISFINITE(_x(X_vy))
|
|
&& PX4_ISFINITE(_x(X_vz))) {
|
|
_pub_lpos.get().timestamp = _timeStamp;
|
|
_pub_lpos.get().xy_valid = _validXY;
|
|
_pub_lpos.get().z_valid = _validZ;
|
|
_pub_lpos.get().v_xy_valid = _validXY;
|
|
_pub_lpos.get().v_z_valid = _validZ;
|
|
_pub_lpos.get().x = xLP(X_x); // north
|
|
_pub_lpos.get().y = xLP(X_y); // east
|
|
_pub_lpos.get().z = xLP(X_z); // down
|
|
_pub_lpos.get().vx = xLP(X_vx); // north
|
|
_pub_lpos.get().vy = xLP(X_vy); // east
|
|
_pub_lpos.get().vz = xLP(X_vz); // down
|
|
_pub_lpos.get().yaw = _sub_att.get().yaw;
|
|
_pub_lpos.get().xy_global = _sub_home.get().timestamp != 0; // need home for reference
|
|
_pub_lpos.get().z_global = _baroInitialized;
|
|
_pub_lpos.get().ref_timestamp = _sub_home.get().timestamp;
|
|
_pub_lpos.get().ref_lat = _map_ref.lat_rad * 180 / M_PI;
|
|
_pub_lpos.get().ref_lon = _map_ref.lon_rad * 180 / M_PI;
|
|
_pub_lpos.get().ref_alt = _sub_home.get().alt;
|
|
_pub_lpos.get().dist_bottom = _aglLowPass.getState();
|
|
_pub_lpos.get().dist_bottom_rate = - xLP(X_vz);
|
|
_pub_lpos.get().surface_bottom_timestamp = _timeStamp;
|
|
_pub_lpos.get().dist_bottom_valid = _validTZ && _validZ;
|
|
_pub_lpos.get().eph = sqrtf(_P(X_x, X_x) + _P(X_y, X_y));
|
|
_pub_lpos.get().epv = sqrtf(_P(X_z, X_z));
|
|
_pub_lpos.update();
|
|
}
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::publishEstimatorStatus()
|
|
{
|
|
_pub_est_status.get().timestamp = _timeStamp;
|
|
|
|
for (int i = 0; i < n_x; i++) {
|
|
_pub_est_status.get().states[i] = _x(i);
|
|
_pub_est_status.get().covariances[i] = _P(i, i);
|
|
}
|
|
|
|
_pub_est_status.get().n_states = n_x;
|
|
_pub_est_status.get().nan_flags = 0;
|
|
_pub_est_status.get().health_flags =
|
|
((_baroFault > FAULT_NONE) << SENSOR_BARO)
|
|
+ ((_gpsFault > FAULT_NONE) << SENSOR_GPS)
|
|
+ ((_lidarFault > FAULT_NONE) << SENSOR_LIDAR)
|
|
+ ((_flowFault > FAULT_NONE) << SENSOR_FLOW)
|
|
+ ((_sonarFault > FAULT_NONE) << SENSOR_SONAR)
|
|
+ ((_visionFault > FAULT_NONE) << SENSOR_VISION)
|
|
+ ((_mocapFault > FAULT_NONE) << SENSOR_MOCAP);
|
|
_pub_est_status.get().timeout_flags =
|
|
(_baroInitialized << SENSOR_BARO)
|
|
+ (_gpsInitialized << SENSOR_GPS)
|
|
+ (_flowInitialized << SENSOR_FLOW)
|
|
+ (_lidarInitialized << SENSOR_LIDAR)
|
|
+ (_sonarInitialized << SENSOR_SONAR)
|
|
+ (_visionInitialized << SENSOR_VISION)
|
|
+ (_mocapInitialized << SENSOR_MOCAP);
|
|
_pub_est_status.update();
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::publishGlobalPos()
|
|
{
|
|
// publish global position
|
|
double lat = 0;
|
|
double lon = 0;
|
|
const Vector<float, n_x> &xLP = _xLowPass.getState();
|
|
map_projection_reproject(&_map_ref, xLP(X_x), xLP(X_y), &lat, &lon);
|
|
float alt = -xLP(X_z) + _altHome;
|
|
|
|
if (PX4_ISFINITE(lat) && PX4_ISFINITE(lon) && PX4_ISFINITE(alt) &&
|
|
PX4_ISFINITE(xLP(X_vx)) && PX4_ISFINITE(xLP(X_vy)) &&
|
|
PX4_ISFINITE(xLP(X_vz))) {
|
|
_pub_gpos.get().timestamp = _timeStamp;
|
|
_pub_gpos.get().time_utc_usec = _sub_gps.get().time_utc_usec;
|
|
_pub_gpos.get().lat = lat;
|
|
_pub_gpos.get().lon = lon;
|
|
_pub_gpos.get().alt = alt;
|
|
_pub_gpos.get().vel_n = xLP(X_vx);
|
|
_pub_gpos.get().vel_e = xLP(X_vy);
|
|
_pub_gpos.get().vel_d = xLP(X_vz);
|
|
_pub_gpos.get().yaw = _sub_att.get().yaw;
|
|
_pub_gpos.get().eph = sqrtf(_P(X_x, X_x) + _P(X_y, X_y));
|
|
_pub_gpos.get().epv = sqrtf(_P(X_z, X_z));
|
|
_pub_gpos.get().terrain_alt = _altHome - xLP(X_tz);
|
|
_pub_gpos.get().terrain_alt_valid = _validTZ;
|
|
_pub_gpos.get().dead_reckoning = !_validXY && !_xyTimeout;
|
|
_pub_gpos.get().pressure_alt = _sub_sensor.get().baro_alt_meter[0];
|
|
_pub_gpos.update();
|
|
}
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::initP()
|
|
{
|
|
_P.setZero();
|
|
_P(X_x, X_x) = 2 * EST_STDDEV_XY_VALID; // initialize to twice valid condition
|
|
_P(X_y, X_y) = 2 * EST_STDDEV_XY_VALID;
|
|
_P(X_z, X_z) = 2 * EST_STDDEV_Z_VALID;
|
|
_P(X_vx, X_vx) = 1;
|
|
_P(X_vy, X_vy) = 1;
|
|
_P(X_vz, X_vz) = 1;
|
|
_P(X_bx, X_bx) = 1e-6;
|
|
_P(X_by, X_by) = 1e-6;
|
|
_P(X_bz, X_bz) = 1e-6;
|
|
_P(X_tz, X_tz) = 2 * EST_STDDEV_TZ_VALID;
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::initSS()
|
|
{
|
|
initP();
|
|
|
|
// dynamics matrix
|
|
_A.setZero();
|
|
// derivative of position is velocity
|
|
_A(X_x, X_vx) = 1;
|
|
_A(X_y, X_vy) = 1;
|
|
_A(X_z, X_vz) = 1;
|
|
|
|
// input matrix
|
|
_B.setZero();
|
|
_B(X_vx, U_ax) = 1;
|
|
_B(X_vy, U_ay) = 1;
|
|
_B(X_vz, U_az) = 1;
|
|
|
|
// update components that depend on current state
|
|
updateSSStates();
|
|
updateSSParams();
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::updateSSStates()
|
|
{
|
|
// derivative of velocity is accelerometer acceleration
|
|
// (in input matrix) - bias (in body frame)
|
|
Matrix3f R_att(_sub_att.get().R);
|
|
_A(X_vx, X_bx) = -R_att(0, 0);
|
|
_A(X_vx, X_by) = -R_att(0, 1);
|
|
_A(X_vx, X_bz) = -R_att(0, 2);
|
|
|
|
_A(X_vy, X_bx) = -R_att(1, 0);
|
|
_A(X_vy, X_by) = -R_att(1, 1);
|
|
_A(X_vy, X_bz) = -R_att(1, 2);
|
|
|
|
_A(X_vz, X_bx) = -R_att(2, 0);
|
|
_A(X_vz, X_by) = -R_att(2, 1);
|
|
_A(X_vz, X_bz) = -R_att(2, 2);
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::updateSSParams()
|
|
{
|
|
// input noise covariance matrix
|
|
_R.setZero();
|
|
_R(U_ax, U_ax) = _accel_xy_stddev.get() * _accel_xy_stddev.get();
|
|
_R(U_ay, U_ay) = _accel_xy_stddev.get() * _accel_xy_stddev.get();
|
|
_R(U_az, U_az) = _accel_z_stddev.get() * _accel_z_stddev.get();
|
|
|
|
// process noise power matrix
|
|
_Q.setZero();
|
|
float pn_p_sq = _pn_p_noise_density.get() * _pn_p_noise_density.get();
|
|
float pn_v_sq = _pn_v_noise_density.get() * _pn_v_noise_density.get();
|
|
_Q(X_x, X_x) = pn_p_sq;
|
|
_Q(X_y, X_y) = pn_p_sq;
|
|
_Q(X_z, X_z) = pn_p_sq;
|
|
_Q(X_vx, X_vx) = pn_v_sq;
|
|
_Q(X_vy, X_vy) = pn_v_sq;
|
|
_Q(X_vz, X_vz) = pn_v_sq;
|
|
|
|
// technically, the noise is in the body frame,
|
|
// but the components are all the same, so
|
|
// ignoring for now
|
|
float pn_b_sq = _pn_b_noise_density.get() * _pn_b_noise_density.get();
|
|
_Q(X_bx, X_bx) = pn_b_sq;
|
|
_Q(X_by, X_by) = pn_b_sq;
|
|
_Q(X_bz, X_bz) = pn_b_sq;
|
|
|
|
// terrain random walk noise
|
|
float pn_t_sq = _pn_t_noise_density.get() * _pn_t_noise_density.get();
|
|
_Q(X_tz, X_tz) = pn_t_sq;
|
|
}
|
|
|
|
void BlockLocalPositionEstimator::predict()
|
|
{
|
|
// if can't update anything, don't propagate
|
|
// state or covariance
|
|
if (!_validXY && !_validZ) { return; }
|
|
|
|
if (integrate && _sub_att.get().R_valid) {
|
|
Matrix3f R_att(_sub_att.get().R);
|
|
Vector3f a;
|
|
float accel_dt = _sub_sensor.get().accelerometer_integral_dt[0] / 1.e6f;
|
|
a(0) = _sub_sensor.get().accelerometer_integral_m_s[0] / accel_dt;
|
|
a(1) = _sub_sensor.get().accelerometer_integral_m_s[1] / accel_dt;
|
|
a(2) = _sub_sensor.get().accelerometer_integral_m_s[2] / accel_dt;
|
|
_u = R_att * a;
|
|
_u(U_az) += 9.81f; // add g
|
|
|
|
} else {
|
|
_u = Vector3f(0, 0, 0);
|
|
}
|
|
|
|
// update state space based on new states
|
|
updateSSStates();
|
|
|
|
// continuous time kalman filter prediction
|
|
// integrate runge kutta 4th order
|
|
// TODO move rk4 algorithm to matrixlib
|
|
// https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
|
|
float h = getDt();
|
|
Vector<float, n_x> k1, k2, k3, k4;
|
|
k1 = dynamics(0, _x, _u);
|
|
k2 = dynamics(h / 2, _x + k1 * h / 2, _u);
|
|
k3 = dynamics(h / 2, _x + k2 * h / 2, _u);
|
|
k4 = dynamics(h, _x + k3 * h, _u);
|
|
Vector<float, n_x> dx = (k1 + k2 * 2 + k3 * 2 + k4) * (h / 6);
|
|
|
|
// propagate
|
|
correctionLogic(dx);
|
|
_x += dx;
|
|
_P += (_A * _P + _P * _A.transpose() +
|
|
_B * _R * _B.transpose() +
|
|
_Q) * getDt();
|
|
_xLowPass.update(_x);
|
|
_aglLowPass.update(agl());
|
|
}
|