PX4 Firmware
PX4 Autopilot Software http://px4.io
BlockLocalPositionEstimator.cpp
Go to the documentation of this file.
3 #include <fcntl.h>
4 #include <systemlib/err.h>
5 #include <matrix/math.hpp>
6 #include <cstdlib>
7 
9 
10 // required standard deviation of estimate for estimator to publish data
11 static const uint32_t EST_STDDEV_XY_VALID = 2.0; // 2.0 m
12 static const uint32_t EST_STDDEV_Z_VALID = 2.0; // 2.0 m
13 static const uint32_t EST_STDDEV_TZ_VALID = 2.0; // 2.0 m
14 
15 static const float P_MAX = 1.0e6f; // max allowed value in state covariance
16 static const float LAND_RATE = 10.0f; // rate of land detector correction
17 
18 static const char *msg_label = "[lpe] "; // rate of land detector correction
19 
21  ModuleParams(nullptr),
22  WorkItem(MODULE_NAME, px4::wq_configurations::att_pos_ctrl),
23 
24  // this block has no parent, and has name LPE
25  SuperBlock(nullptr, "LPE"),
26 
27  // map projection
28  _map_ref(),
29 
30  // flow gyro
31  _flow_gyro_x_high_pass(this, "FGYRO_HP"),
32  _flow_gyro_y_high_pass(this, "FGYRO_HP"),
33 
34  // stats
35  _baroStats(this, ""),
36  _sonarStats(this, ""),
37  _lidarStats(this, ""),
38  _flowQStats(this, ""),
39  _visionStats(this, ""),
40  _mocapStats(this, ""),
41  _gpsStats(this, ""),
42 
43  // low pass
44  _xLowPass(this, "X_LP"),
45 
46  // use same lp constant for agl
47  _aglLowPass(this, "X_LP"),
48 
49  // delay
50  _xDelay(this, ""),
51  _tDelay(this, ""),
52 
53  // misc
54  _timeStamp(hrt_absolute_time()),
55  _time_origin(0),
56  _timeStampLastBaro(hrt_absolute_time()),
57  _time_last_hist(0),
58  _time_last_flow(0),
59  _time_last_baro(0),
60  _time_last_gps(0),
61  _time_last_lidar(0),
62  _time_last_sonar(0),
63  _time_init_sonar(0),
64  _time_last_vision_p(0),
65  _time_last_mocap(0),
66  _time_last_land(0),
67  _time_last_target(0),
68 
69  // reference altitudes
70  _altOrigin(0),
71  _altOriginInitialized(false),
72  _altOriginGlobal(false),
73  _baroAltOrigin(0),
74  _gpsAltOrigin(0),
75 
76  // status
77  _receivedGps(false),
78  _lastArmedState(false),
79 
80  // masks
81  _sensorTimeout(UINT16_MAX),
82  _sensorFault(0),
83  _estimatorInitialized(0),
84 
85  // sensor update flags
86  _flowUpdated(false),
87  _gpsUpdated(false),
88  _visionUpdated(false),
89  _mocapUpdated(false),
90  _lidarUpdated(false),
91  _sonarUpdated(false),
92  _landUpdated(false),
93  _baroUpdated(false),
94 
95  // sensor validation flags
96  _vision_xy_valid(false),
97  _vision_z_valid(false),
98  _mocap_xy_valid(false),
99  _mocap_z_valid(false),
100 
101  // sensor std deviations
102  _vision_eph(0.0),
103  _vision_epv(0.0),
104  _mocap_eph(0.0),
105  _mocap_epv(0.0),
106 
107  // local to global coversion related variables
108  _is_global_cov_init(false),
109  _global_ref_timestamp(0.0),
110  _ref_lat(0.0),
111  _ref_lon(0.0),
112  _ref_alt(0.0)
113 {
114  _sensors_sub.set_interval_ms(10); // main prediction loop, 100 hz
115 
116  // assign distance subs to array
117  _dist_subs[0] = &_sub_dist0;
118  _dist_subs[1] = &_sub_dist1;
119  _dist_subs[2] = &_sub_dist2;
120  _dist_subs[3] = &_sub_dist3;
121 
122  // initialize A, B, P, x, u
123  _x.setZero();
124  _u.setZero();
125  initSS();
126 
127  // map
128  _map_ref.init_done = false;
129 
130  // print fusion settings to console
131  PX4_INFO("fuse gps: %d, flow: %d, vis_pos: %d, "
132  "landing_target: %d, land: %d, pub_agl_z: %d, flow_gyro: %d, "
133  "baro: %d\n",
134  (_param_lpe_fusion.get() & FUSE_GPS) != 0,
135  (_param_lpe_fusion.get() & FUSE_FLOW) != 0,
136  (_param_lpe_fusion.get() & FUSE_VIS_POS) != 0,
137  (_param_lpe_fusion.get() & FUSE_LAND_TARGET) != 0,
138  (_param_lpe_fusion.get() & FUSE_LAND) != 0,
139  (_param_lpe_fusion.get() & FUSE_PUB_AGL_Z) != 0,
140  (_param_lpe_fusion.get() & FUSE_FLOW_GYRO_COMP) != 0,
141  (_param_lpe_fusion.get() & FUSE_BARO) != 0);
142 }
143 
144 bool
146 {
148  PX4_ERR("sensor combined callback registration failed!");
149  return false;
150  }
151 
152  return true;
153 }
154 
155 
157  float t,
160 {
161  return m_A * x + m_B * u;
162 }
163 
165 {
166  if (should_exit()) {
168  exit_and_cleanup();
169  return;
170  }
171 
172  sensor_combined_s imu;
173 
174  if (!_sensors_sub.update(&imu)) {
175  return;
176  }
177 
178  uint64_t newTimeStamp = hrt_absolute_time();
179  float dt = (newTimeStamp - _timeStamp) / 1.0e6f;
180  _timeStamp = newTimeStamp;
181 
182  // set dt for all child blocks
183  setDt(dt);
184 
185  // auto-detect connected rangefinders while not armed
186  _sub_armed.update();
187  bool armedState = _sub_armed.get().armed;
188 
189  if (!armedState && (_sub_lidar == nullptr || _sub_sonar == nullptr)) {
190  // detect distance sensors
191  for (size_t i = 0; i < N_DIST_SUBS; i++) {
192  auto *s = _dist_subs[i];
193 
194  if (s == _sub_lidar || s == _sub_sonar) { continue; }
195 
196  if (s->update()) {
197 
198  if (s->get().timestamp == 0) { continue; }
199 
200  if (s->get().type == distance_sensor_s::MAV_DISTANCE_SENSOR_LASER &&
201  s->get().orientation == distance_sensor_s::ROTATION_DOWNWARD_FACING &&
202  _sub_lidar == nullptr) {
203  _sub_lidar = s;
204  mavlink_and_console_log_info(&mavlink_log_pub, "%sDownward-facing Lidar detected with ID %zu", msg_label, i);
205 
206  } else if (s->get().type == distance_sensor_s::MAV_DISTANCE_SENSOR_ULTRASOUND &&
207  s->get().orientation == distance_sensor_s::ROTATION_DOWNWARD_FACING &&
208  _sub_sonar == nullptr) {
209  _sub_sonar = s;
210  mavlink_and_console_log_info(&mavlink_log_pub, "%sDownward-facing Sonar detected with ID %zu", msg_label, i);
211  }
212  }
213  }
214  }
215 
216  // reset pos, vel, and terrain on arming
217 
218  // XXX this will be re-enabled for indoor use cases using a
219  // selection param, but is really not helping outdoors
220  // right now.
221 
222  // if (!_lastArmedState && armedState) {
223 
224  // // we just armed, we are at origin on the ground
225  // _x(X_x) = 0;
226  // _x(X_y) = 0;
227  // // reset Z or not? _x(X_z) = 0;
228 
229  // // we aren't moving, all velocities are zero
230  // _x(X_vx) = 0;
231  // _x(X_vy) = 0;
232  // _x(X_vz) = 0;
233 
234  // // assume we are on the ground, so terrain alt is local alt
235  // _x(X_tz) = _x(X_z);
236 
237  // // reset lowpass filter as well
238  // _xLowPass.setState(_x);
239  // _aglLowPass.setState(0);
240  // }
241 
242  _lastArmedState = armedState;
243 
244  // see which updates are available
245  bool paramsUpdated = _sub_param_update.update();
246  _baroUpdated = false;
247 
248  if ((_param_lpe_fusion.get() & FUSE_BARO) && _sub_airdata.update()) {
250  _baroUpdated = true;
252  }
253  }
254 
255  _flowUpdated = (_param_lpe_fusion.get() & FUSE_FLOW) && _sub_flow.update();
256  _gpsUpdated = (_param_lpe_fusion.get() & FUSE_GPS) && _sub_gps.update();
257  _visionUpdated = (_param_lpe_fusion.get() & FUSE_VIS_POS) && _sub_visual_odom.update();
259  _lidarUpdated = (_sub_lidar != nullptr) && _sub_lidar->update();
260  _sonarUpdated = (_sub_sonar != nullptr) && _sub_sonar->update();
261  _landUpdated = landed() && ((_timeStamp - _time_last_land) > 1.0e6f / LAND_RATE);// throttle rate
262  bool targetPositionUpdated = _sub_landing_target_pose.update();
263 
264  // get new data
265  _sub_att.update();
267 
268  // update parameters
269  if (paramsUpdated) {
270  SuperBlock::updateParams();
271  ModuleParams::updateParams();
272  updateSSParams();
273  }
274 
275  // is xy valid?
276  bool vxy_stddev_ok = false;
277 
278  if (math::max(m_P(X_vx, X_vx), m_P(X_vy, X_vy)) < _param_lpe_vxy_pub.get() * _param_lpe_vxy_pub.get()) {
279  vxy_stddev_ok = true;
280  }
281 
283  // if valid and gps has timed out, set to not valid
284  if (!vxy_stddev_ok && (_sensorTimeout & SENSOR_GPS)) {
285  _estimatorInitialized &= ~EST_XY;
286  }
287 
288  } else {
289  if (vxy_stddev_ok) {
290  if (!(_sensorTimeout & SENSOR_GPS)
296  ) {
298  }
299  }
300  }
301 
302  // is z valid?
303  bool z_stddev_ok = sqrtf(m_P(X_z, X_z)) < _param_lpe_z_pub.get();
304 
306  // if valid and baro has timed out, set to not valid
307  if (!z_stddev_ok && (_sensorTimeout & SENSOR_BARO)) {
308  _estimatorInitialized &= ~EST_Z;
309  }
310 
311  } else {
312  if (z_stddev_ok) {
314  }
315  }
316 
317  // is terrain valid?
318  bool tz_stddev_ok = sqrtf(m_P(X_tz, X_tz)) < _param_lpe_z_pub.get();
319 
321  if (!tz_stddev_ok) {
322  _estimatorInitialized &= ~EST_TZ;
323  }
324 
325  } else {
326  if (tz_stddev_ok) {
328  }
329  }
330 
331  // check timeouts
332  checkTimeouts();
333 
334  // if we have no lat, lon initialize projection to LPE_LAT, LPE_LON parameters
335  if (!_map_ref.init_done && (_estimatorInitialized & EST_XY) && _param_lpe_fake_origin.get()) {
337  (double)_param_lpe_lat.get(),
338  (double)_param_lpe_lon.get());
339 
340  // set timestamp when origin was set to current time
342 
343  mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] global origin init (parameter) : lat %6.2f lon %6.2f alt %5.1f m",
344  double(_param_lpe_lat.get()), double(_param_lpe_lon.get()), double(_altOrigin));
345  }
346 
347  // reinitialize x if necessary
348  bool reinit_x = false;
349 
350  for (size_t i = 0; i < n_x; i++) {
351  // should we do a reinit
352  // of sensors here?
353  // don't want it to take too long
354  if (!PX4_ISFINITE(_x(i))) {
355  reinit_x = true;
356  mavlink_and_console_log_info(&mavlink_log_pub, "%sreinit x, x(%zu) not finite", msg_label, i);
357  break;
358  }
359  }
360 
361  if (reinit_x) {
362  for (size_t i = 0; i < n_x; i++) {
363  _x(i) = 0;
364  }
365 
367  }
368 
369  // force P symmetry and reinitialize P if necessary
370  bool reinit_P = false;
371 
372  for (size_t i = 0; i < n_x; i++) {
373  for (size_t j = 0; j <= i; j++) {
374  if (!PX4_ISFINITE(m_P(i, j))) {
376  "%sreinit P (%zu, %zu) not finite", msg_label, i, j);
377  reinit_P = true;
378  }
379 
380  if (i == j) {
381  // make sure diagonal elements are positive
382  if (m_P(i, i) <= 0) {
384  "%sreinit P (%zu, %zu) negative", msg_label, i, j);
385  reinit_P = true;
386  }
387 
388  } else {
389  // copy elememnt from upper triangle to force
390  // symmetry
391  m_P(j, i) = m_P(i, j);
392  }
393 
394  if (reinit_P) { break; }
395  }
396 
397  if (reinit_P) { break; }
398  }
399 
400  if (reinit_P) {
401  initP();
402  }
403 
404  // do prediction
405  predict(imu);
406 
407  // sensor corrections/ initializations
408  if (_gpsUpdated) {
409  if (_sensorTimeout & SENSOR_GPS) {
410  gpsInit();
411 
412  } else {
413  gpsCorrect();
414  }
415  }
416 
417  if (_baroUpdated) {
418  if (_sensorTimeout & SENSOR_BARO) {
419  baroInit();
420 
421  } else {
422  baroCorrect();
423  }
424  }
425 
426  if (_lidarUpdated) {
428  lidarInit();
429 
430  } else {
431  lidarCorrect();
432  }
433  }
434 
435  if (_sonarUpdated) {
437  sonarInit();
438 
439  } else {
440  sonarCorrect();
441  }
442  }
443 
444  if (_flowUpdated) {
445  if (_sensorTimeout & SENSOR_FLOW) {
446  flowInit();
447 
448  } else {
449  flowCorrect();
450  }
451  }
452 
453  if (_visionUpdated) {
455  visionInit();
456 
457  } else {
458  visionCorrect();
459  }
460  }
461 
462  if (_mocapUpdated) {
464  mocapInit();
465 
466  } else {
467  mocapCorrect();
468  }
469  }
470 
471  if (_landUpdated) {
472  if (_sensorTimeout & SENSOR_LAND) {
473  landInit();
474 
475  } else {
476  landCorrect();
477  }
478  }
479 
480  if (targetPositionUpdated) {
483 
484  } else {
486  }
487  }
488 
489  if (_altOriginInitialized) {
490  // update all publications if possible
491  publishLocalPos();
492  publishOdom();
495  _pub_innov.update();
496 
497  if ((_estimatorInitialized & EST_XY) && (_map_ref.init_done || _param_lpe_fake_origin.get())) {
499  }
500  }
501 
502  // propagate delayed state, no matter what
503  // if state is frozen, delayed state still
504  // needs to be propagated with frozen state
505  float dt_hist = 1.0e-6f * (_timeStamp - _time_last_hist);
506 
507  if (_time_last_hist == 0 ||
508  (dt_hist > HIST_STEP)) {
510  _xDelay.update(_x);
512  }
513 }
514 
516 {
518  gpsCheckTimeout();
526 }
527 
529 {
530  if (!(_param_lpe_fusion.get() & FUSE_LAND)) {
531  return false;
532  }
533 
534  _sub_land.update();
535 
536  bool disarmed_not_falling = (!_sub_armed.get().armed) && (!_sub_land.get().freefall);
537 
538  return _sub_land.get().landed || disarmed_not_falling;
539 }
540 
542 {
543  const Vector<float, n_x> &xLP = _xLowPass.getState();
544 
545  // lie about eph/epv to allow visual odometry only navigation when velocity est. good
546  float evh = sqrtf(m_P(X_vx, X_vx) + m_P(X_vy, X_vy));
547  float evv = sqrtf(m_P(X_vz, X_vz));
548  float eph = sqrtf(m_P(X_x, X_x) + m_P(X_y, X_y));
549  float epv = sqrtf(m_P(X_z, X_z));
550 
551  float eph_thresh = 3.0f;
552  float epv_thresh = 3.0f;
553 
554  if (evh < _param_lpe_vxy_pub.get()) {
555  if (eph > eph_thresh) {
556  eph = eph_thresh;
557  }
558 
559  if (epv > epv_thresh) {
560  epv = epv_thresh;
561  }
562  }
563 
564  // publish local position
565  if (PX4_ISFINITE(_x(X_x)) && PX4_ISFINITE(_x(X_y)) && PX4_ISFINITE(_x(X_z)) &&
566  PX4_ISFINITE(_x(X_vx)) && PX4_ISFINITE(_x(X_vy))
567  && PX4_ISFINITE(_x(X_vz))) {
569 
574 
575  _pub_lpos.get().x = xLP(X_x); // north
576  _pub_lpos.get().y = xLP(X_y); // east
577 
578  if (_param_lpe_fusion.get() & FUSE_PUB_AGL_Z) {
579  _pub_lpos.get().z = -_aglLowPass.getState(); // agl
580 
581  } else {
582  _pub_lpos.get().z = xLP(X_z); // down
583  }
584 
586 
587  _pub_lpos.get().vx = xLP(X_vx); // north
588  _pub_lpos.get().vy = xLP(X_vy); // east
589  _pub_lpos.get().vz = xLP(X_vz); // down
590 
591  // this estimator does not provide a separate vertical position time derivative estimate, so use the vertical velocity
592  _pub_lpos.get().z_deriv = xLP(X_vz);
593 
594  _pub_lpos.get().ax = _u(U_ax); // north
595  _pub_lpos.get().ay = _u(U_ay); // east
596  _pub_lpos.get().az = _u(U_az); // down
597 
606  // we estimate agl even when we don't have terrain info
607  // if you are in terrain following mode this is important
608  // so that if terrain estimation fails there isn't a
609  // sudden altitude jump
611  _pub_lpos.get().eph = eph;
612  _pub_lpos.get().epv = epv;
613  _pub_lpos.get().evh = evh;
614  _pub_lpos.get().evv = evv;
615  _pub_lpos.get().vxy_max = INFINITY;
616  _pub_lpos.get().vz_max = INFINITY;
617  _pub_lpos.get().hagl_min = INFINITY;
618  _pub_lpos.get().hagl_max = INFINITY;
619  _pub_lpos.update();
620  }
621 }
622 
624 {
625  const Vector<float, n_x> &xLP = _xLowPass.getState();
626 
627  // publish vehicle odometry
628  if (PX4_ISFINITE(_x(X_x)) && PX4_ISFINITE(_x(X_y)) && PX4_ISFINITE(_x(X_z)) &&
629  PX4_ISFINITE(_x(X_vx)) && PX4_ISFINITE(_x(X_vy))
630  && PX4_ISFINITE(_x(X_vz))) {
632  _pub_odom.get().local_frame = _pub_odom.get().LOCAL_FRAME_NED;
633 
634  // position
635  _pub_odom.get().x = xLP(X_x); // north
636  _pub_odom.get().y = xLP(X_y); // east
637 
638  if (_param_lpe_fusion.get() & FUSE_PUB_AGL_Z) {
639  _pub_odom.get().z = -_aglLowPass.getState(); // agl
640 
641  } else {
642  _pub_odom.get().z = xLP(X_z); // down
643  }
644 
645  // orientation
647  q.copyTo(_pub_odom.get().q);
648 
649  // linear velocity
650  _pub_odom.get().vx = xLP(X_vx); // vel north
651  _pub_odom.get().vy = xLP(X_vy); // vel east
652  _pub_odom.get().vz = xLP(X_vz); // vel down
653 
654  // angular velocity
655  _pub_odom.get().rollspeed = _sub_angular_velocity.get().xyz[0]; // roll rate
656  _pub_odom.get().pitchspeed = _sub_angular_velocity.get().xyz[1]; // pitch rate
657  _pub_odom.get().yawspeed = _sub_angular_velocity.get().xyz[2]; // yaw rate
658 
659  // get the covariance matrix size
660  const size_t POS_URT_SIZE = sizeof(_pub_odom.get().pose_covariance) / sizeof(_pub_odom.get().pose_covariance[0]);
661  const size_t VEL_URT_SIZE = sizeof(_pub_odom.get().velocity_covariance) / sizeof(
663 
664  // initially set pose covariances to 0
665  for (size_t i = 0; i < POS_URT_SIZE; i++) {
666  _pub_odom.get().pose_covariance[i] = 0.0;
667  }
668 
669  // set the position variances
670  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_X_VARIANCE] = m_P(X_vx, X_vx);
671  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_Y_VARIANCE] = m_P(X_vy, X_vy);
672  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_Z_VARIANCE] = m_P(X_vz, X_vz);
673 
674  // unknown orientation covariances
675  // TODO: add orientation covariance to vehicle_attitude
676  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_ROLL_VARIANCE] = NAN;
677  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_PITCH_VARIANCE] = NAN;
678  _pub_odom.get().pose_covariance[_pub_odom.get().COVARIANCE_MATRIX_YAW_VARIANCE] = NAN;
679 
680  // initially set velocity covariances to 0
681  for (size_t i = 0; i < VEL_URT_SIZE; i++) {
682  _pub_odom.get().velocity_covariance[i] = 0.0;
683  }
684 
685  // set the linear velocity variances
686  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_VX_VARIANCE] = m_P(X_vx, X_vx);
687  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_VY_VARIANCE] = m_P(X_vy, X_vy);
688  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_VZ_VARIANCE] = m_P(X_vz, X_vz);
689 
690  // unknown angular velocity covariances
691  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_ROLLRATE_VARIANCE] = NAN;
692  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_PITCHRATE_VARIANCE] = NAN;
693  _pub_odom.get().velocity_covariance[_pub_odom.get().COVARIANCE_MATRIX_YAWRATE_VARIANCE] = NAN;
694 
695  _pub_odom.update();
696  }
697 }
698 
700 {
702 
703  for (size_t i = 0; i < n_x; i++) {
704  _pub_est_status.get().states[i] = _x(i);
705  }
706 
707  // matching EKF2 covariances indexing
708  // quaternion - not determined, as it is a position estimator
709  _pub_est_status.get().covariances[0] = NAN;
710  _pub_est_status.get().covariances[1] = NAN;
711  _pub_est_status.get().covariances[2] = NAN;
712  _pub_est_status.get().covariances[3] = NAN;
713  // linear velocity
717  // position
721  // gyro bias - not determined
722  _pub_est_status.get().covariances[10] = NAN;
723  _pub_est_status.get().covariances[11] = NAN;
724  _pub_est_status.get().covariances[12] = NAN;
725  // accel bias
729 
730  // mag - not determined
731  for (size_t i = 16; i <= 21; i++) {
732  _pub_est_status.get().covariances[i] = NAN;
733  }
734 
735  // replacing the hor wind cov with terrain altitude covariance
737  _pub_est_status.get().covariances[23] = NAN;
738 
744 
746 }
747 
749 {
750  // publish global position
751  double lat = 0;
752  double lon = 0;
753  const Vector<float, n_x> &xLP = _xLowPass.getState();
754  map_projection_reproject(&_map_ref, xLP(X_x), xLP(X_y), &lat, &lon);
755  float alt = -xLP(X_z) + _altOrigin;
756 
757  // lie about eph/epv to allow visual odometry only navigation when velocity est. good
758  float evh = sqrtf(m_P(X_vx, X_vx) + m_P(X_vy, X_vy));
759  float eph = sqrtf(m_P(X_x, X_x) + m_P(X_y, X_y));
760  float epv = sqrtf(m_P(X_z, X_z));
761 
762  float eph_thresh = 3.0f;
763  float epv_thresh = 3.0f;
764 
765  if (evh < _param_lpe_vxy_pub.get()) {
766  if (eph > eph_thresh) {
767  eph = eph_thresh;
768  }
769 
770  if (epv > epv_thresh) {
771  epv = epv_thresh;
772  }
773  }
774 
775  if (PX4_ISFINITE(lat) && PX4_ISFINITE(lon) && PX4_ISFINITE(alt) &&
776  PX4_ISFINITE(xLP(X_vx)) && PX4_ISFINITE(xLP(X_vy)) &&
777  PX4_ISFINITE(xLP(X_vz))) {
779  _pub_gpos.get().lat = lat;
780  _pub_gpos.get().lon = lon;
781  _pub_gpos.get().alt = alt;
782  _pub_gpos.get().vel_n = xLP(X_vx);
783  _pub_gpos.get().vel_e = xLP(X_vy);
784  _pub_gpos.get().vel_d = xLP(X_vz);
786  _pub_gpos.get().eph = eph;
787  _pub_gpos.get().epv = epv;
791  _pub_gpos.update();
792  }
793 }
794 
796 {
797  m_P.setZero();
798  // initialize to twice valid condition
800  m_P(X_y, X_y) = 2 * EST_STDDEV_XY_VALID * EST_STDDEV_XY_VALID;
802  m_P(X_vx, X_vx) = 2 * _param_lpe_vxy_pub.get() * _param_lpe_vxy_pub.get();
803  m_P(X_vy, X_vy) = 2 * _param_lpe_vxy_pub.get() * _param_lpe_vxy_pub.get();
804  // use vxy thresh for vz init as well
805  m_P(X_vz, X_vz) = 2 * _param_lpe_vxy_pub.get() * _param_lpe_vxy_pub.get();
806  // initialize bias uncertainty to small values to keep them stable
807  m_P(X_bx, X_bx) = 1e-6;
808  m_P(X_by, X_by) = 1e-6;
809  m_P(X_bz, X_bz) = 1e-6;
811 }
812 
814 {
815  initP();
816 
817  // dynamics matrix
818  m_A.setZero();
819  // derivative of position is velocity
820  m_A(X_x, X_vx) = 1;
821  m_A(X_y, X_vy) = 1;
822  m_A(X_z, X_vz) = 1;
823 
824  // input matrix
825  m_B.setZero();
826  m_B(X_vx, U_ax) = 1;
827  m_B(X_vy, U_ay) = 1;
828  m_B(X_vz, U_az) = 1;
829 
830  // update components that depend on current state
831  updateSSStates();
832  updateSSParams();
833 }
834 
836 {
837  // derivative of velocity is accelerometer acceleration
838  // (in input matrix) - bias (in body frame)
839  m_A(X_vx, X_bx) = -_R_att(0, 0);
840  m_A(X_vx, X_by) = -_R_att(0, 1);
841  m_A(X_vx, X_bz) = -_R_att(0, 2);
842 
843  m_A(X_vy, X_bx) = -_R_att(1, 0);
844  m_A(X_vy, X_by) = -_R_att(1, 1);
845  m_A(X_vy, X_bz) = -_R_att(1, 2);
846 
847  m_A(X_vz, X_bx) = -_R_att(2, 0);
848  m_A(X_vz, X_by) = -_R_att(2, 1);
849  m_A(X_vz, X_bz) = -_R_att(2, 2);
850 }
851 
853 {
854  // input noise covariance matrix
855  m_R.setZero();
856  m_R(U_ax, U_ax) = _param_lpe_acc_xy.get() * _param_lpe_acc_xy.get();
857  m_R(U_ay, U_ay) = _param_lpe_acc_xy.get() * _param_lpe_acc_xy.get();
858  m_R(U_az, U_az) = _param_lpe_acc_z.get() * _param_lpe_acc_z.get();
859 
860  // process noise power matrix
861  m_Q.setZero();
862  float pn_p_sq = _param_lpe_pn_p.get() * _param_lpe_pn_p.get();
863  float pn_v_sq = _param_lpe_pn_v.get() * _param_lpe_pn_v.get();
864  m_Q(X_x, X_x) = pn_p_sq;
865  m_Q(X_y, X_y) = pn_p_sq;
866  m_Q(X_z, X_z) = pn_p_sq;
867  m_Q(X_vx, X_vx) = pn_v_sq;
868  m_Q(X_vy, X_vy) = pn_v_sq;
869  m_Q(X_vz, X_vz) = pn_v_sq;
870 
871  // technically, the noise is in the body frame,
872  // but the components are all the same, so
873  // ignoring for now
874  float pn_b_sq = _param_lpe_pn_b.get() * _param_lpe_pn_b.get();
875  m_Q(X_bx, X_bx) = pn_b_sq;
876  m_Q(X_by, X_by) = pn_b_sq;
877  m_Q(X_bz, X_bz) = pn_b_sq;
878 
879  // terrain random walk noise ((m/s)/sqrt(hz)), scales with velocity
880  float pn_t_noise_density =
881  _param_lpe_pn_t.get() +
882  (_param_lpe_t_max_grade.get() / 100.0f) * sqrtf(_x(X_vx) * _x(X_vx) + _x(X_vy) * _x(X_vy));
883  m_Q(X_tz, X_tz) = pn_t_noise_density * pn_t_noise_density;
884 }
885 
887 {
888  // get acceleration
891  // note, bias is removed in dynamics function
892  _u = _R_att * a;
893  _u(U_az) += CONSTANTS_ONE_G; // add g
894 
895  // update state space based on new states
896  updateSSStates();
897 
898  // continuous time kalman filter prediction
899  // integrate runge kutta 4th order
900  // TODO move rk4 algorithm to matrixlib
901  // https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
902  float h = getDt();
903  Vector<float, n_x> k1, k2, k3, k4;
904  k1 = dynamics(0, _x, _u);
905  k2 = dynamics(h / 2, _x + k1 * h / 2, _u);
906  k3 = dynamics(h / 2, _x + k2 * h / 2, _u);
907  k4 = dynamics(h, _x + k3 * h, _u);
908  Vector<float, n_x> dx = (k1 + k2 * 2 + k3 * 2 + k4) * (h / 6);
909 
910  // don't integrate position if no valid xy data
911  if (!(_estimatorInitialized & EST_XY)) {
912  dx(X_x) = 0;
913  dx(X_vx) = 0;
914  dx(X_y) = 0;
915  dx(X_vy) = 0;
916  }
917 
918  // don't integrate z if no valid z data
919  if (!(_estimatorInitialized & EST_Z)) {
920  dx(X_z) = 0;
921  }
922 
923  // don't integrate tz if no valid tz data
924  if (!(_estimatorInitialized & EST_TZ)) {
925  dx(X_tz) = 0;
926  }
927 
928  // saturate bias
929  float bx = dx(X_bx) + _x(X_bx);
930  float by = dx(X_by) + _x(X_by);
931  float bz = dx(X_bz) + _x(X_bz);
932 
933  if (std::abs(bx) > BIAS_MAX) {
934  bx = BIAS_MAX * bx / std::abs(bx);
935  dx(X_bx) = bx - _x(X_bx);
936  }
937 
938  if (std::abs(by) > BIAS_MAX) {
939  by = BIAS_MAX * by / std::abs(by);
940  dx(X_by) = by - _x(X_by);
941  }
942 
943  if (std::abs(bz) > BIAS_MAX) {
944  bz = BIAS_MAX * bz / std::abs(bz);
945  dx(X_bz) = bz - _x(X_bz);
946  }
947 
948  // propagate
949  _x += dx;
951  m_B * m_R * m_B.transpose() + m_Q) * getDt();
952 
953  // covariance propagation logic
954  for (size_t i = 0; i < n_x; i++) {
955  if (m_P(i, i) > P_MAX) {
956  // if diagonal element greater than max, stop propagating
957  dP(i, i) = 0;
958 
959  for (size_t j = 0; j < n_x; j++) {
960  dP(i, j) = 0;
961  dP(j, i) = 0;
962  }
963  }
964  }
965 
966  m_P += dP;
969 }
970 
971 int BlockLocalPositionEstimator::getDelayPeriods(float delay, uint8_t *periods)
972 {
973  float t_delay = 0;
974  uint8_t i_hist = 0;
975 
976  for (i_hist = 1; i_hist < HIST_LEN; i_hist++) {
977  t_delay = 1.0e-6f * (_timeStamp - _tDelay.get(i_hist)(0, 0));
978 
979  if (t_delay > delay) {
980  break;
981  }
982  }
983 
984  *periods = i_hist;
985 
986  if (t_delay > DELAY_MAX) {
987  mavlink_and_console_log_info(&mavlink_log_pub, "%sdelayed data old: %8.4f", msg_label, double(t_delay));
988  return -1;
989  }
990 
991  return OK;
992 }
993 
994 int
996 {
997  return print_usage("unknown command");
998 }
999 
1000 int
1002 {
1004 
1005  if (instance) {
1006  _object.store(instance);
1007  _task_id = task_id_is_work_queue;
1008 
1009  if (instance->init()) {
1010  return PX4_OK;
1011  }
1012 
1013  } else {
1014  PX4_ERR("alloc failed");
1015  }
1016 
1017  delete instance;
1018  _object.store(nullptr);
1019  _task_id = -1;
1020 
1021  return PX4_ERROR;
1022 }
1023 
1024 int
1026 {
1027  if (reason) {
1028  PX4_WARN("%s\n", reason);
1029  }
1030 
1031  PRINT_MODULE_DESCRIPTION(
1032  R"DESCR_STR(
1033 ### Description
1034 Attitude and position estimator using an Extended Kalman Filter.
1035 
1036 )DESCR_STR");
1037 
1038  PRINT_MODULE_USAGE_NAME("local_position_estimator", "estimator");
1039  PRINT_MODULE_USAGE_COMMAND("start");
1040  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
1041 
1042  return 0;
1043 }
1044 
1045 extern "C" __EXPORT int local_position_estimator_main(int argc, char *argv[])
1046 {
1047  return BlockLocalPositionEstimator::main(argc, argv);
1048 }
uORB::SubscriptionData< distance_sensor_s > * _sub_sonar
static const float P_MAX
bool update(void *dst)
Copy the struct if updated.
orb_advert_t mavlink_log_pub
int getDelayPeriods(float delay, uint8_t *periods)
float getDt()
Definition: Block.hpp:78
uORB::SubscriptionData< optical_flow_s > _sub_flow
int main(int argc, char **argv)
Definition: main.cpp:3
static const size_t HIST_LEN
matrix::Vector< Type, M > update(const matrix::Matrix< Type, M, 1 > &input)
uORB::SubscriptionData< distance_sensor_s > * _sub_lidar
uORB::SubscriptionData< vehicle_angular_velocity_s > _sub_angular_velocity
Definition: I2C.hpp:51
uORB::SubscriptionCallbackWorkItem _sensors_sub
static const float LAND_RATE
matrix::Vector< Type, M > getState()
float accelerometer_m_s2[3]
Vector< float, n_x > dynamics(float t, const Vector< float, n_x > &x, const Vector< float, n_u > &u)
LidarLite * instance
Definition: ll40ls.cpp:65
void copyTo(Type dst[M *N]) const
Definition: Matrix.hpp:115
Quaternion class.
Definition: Dcm.hpp:24
static const size_t N_DIST_SUBS
Matrix< Type, N, M > transpose() const
Definition: Matrix.hpp:353
static constexpr float CONSTANTS_ONE_G
Definition: geo.h:51
uORB::SubscriptionData< vehicle_attitude_s > _sub_att
float velocity_covariance[21]
void set_interval_ms(uint32_t interval)
Set the interval in milliseconds.
uORB::PublicationData< estimator_status_s > _pub_est_status
struct map_projection_reference_s _map_ref
uORB::PublicationData< vehicle_odometry_s > _pub_odom
float update(float input)
static const uint32_t EST_STDDEV_XY_VALID
uORB::SubscriptionData< distance_sensor_s > _sub_dist0
static const float DELAY_MAX
static int print_usage(const char *reason=nullptr)
uORB::SubscriptionData< vehicle_gps_position_s > _sub_gps
int map_projection_reproject(const struct map_projection_reference_s *ref, float x, float y, double *lat, double *lon)
Transforms a point in the local azimuthal equidistant plane to the geographic coordinate system using...
Definition: geo.cpp:166
matrix::Matrix< Type, M, N > update(const matrix::Matrix< Type, M, N > &u)
Definition: BlockDelay.hpp:70
static int task_spawn(int argc, char *argv[])
static const float HIST_STEP
void setDt(float dt) override
Definition: Block.cpp:100
void predict(const sensor_combined_s &imu)
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
uORB::PublicationData< vehicle_global_position_s > _pub_gpos
Euler< float > Eulerf
Definition: Euler.hpp:156
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
uORB::SubscriptionData< distance_sensor_s > * _dist_subs[N_DIST_SUBS]
static const float BIAS_MAX
uORB::PublicationData< vehicle_local_position_s > _pub_lpos
uORB::SubscriptionData< vehicle_air_data_s > _sub_airdata
static const uint32_t EST_STDDEV_Z_VALID
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
const T & get() const
__EXPORT int local_position_estimator_main(int argc, char *argv[])
uORB::SubscriptionData< vehicle_odometry_s > _sub_visual_odom
constexpr _Tp max(_Tp a, _Tp b)
Definition: Limits.hpp:60
BlockLowPassVector< float, n_x > _xLowPass
static int custom_command(int argc, char *argv[])
Quaternion< float > Quatf
Definition: Quaternion.hpp:544
float dt
Definition: px4io.c:73
#define M_PI
Definition: gps_helper.cpp:38
uORB::SubscriptionData< actuator_armed_s > _sub_armed
static const uint32_t EST_STDDEV_TZ_VALID
matrix::Matrix< Type, M, N > get(size_t delay)
Definition: BlockDelay.hpp:99
Definition: bst.cpp:62
BlockDelay< uint64_t, 1, 1, HIST_LEN > _tDelay
uORB::SubscriptionData< distance_sensor_s > _sub_dist1
uORB::SubscriptionData< landing_target_pose_s > _sub_landing_target_pose
#define OK
Definition: uavcan_main.cpp:71
BlockDelay< float, n_x, 1, HIST_LEN > _xDelay
void setZero()
Definition: Matrix.hpp:416
Dual< Scalar, N > abs(const Dual< Scalar, N > &a)
Definition: Dual.hpp:196
uORB::SubscriptionData< distance_sensor_s > _sub_dist2
int map_projection_init(struct map_projection_reference_s *ref, double lat_0, double lon_0)
Initializes the map transformation given by the argument and sets the timestamp to now...
Definition: geo.cpp:105
uORB::SubscriptionData< vehicle_odometry_s > _sub_mocap_odom
uORB::SubscriptionData< distance_sensor_s > _sub_dist3
uORB::PublicationData< ekf2_innovations_s > _pub_innov
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uORB::SubscriptionData< parameter_update_s > _sub_param_update
uORB::SubscriptionData< vehicle_land_detected_s > _sub_land
static const char * msg_label