-
Notifications
You must be signed in to change notification settings - Fork 13.6k
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
New filter for distance sensor validation #24106
base: main
Are you sure you want to change the base?
Changes from all commits
7b1424f
1c60dd3
5e50f22
9c9e844
71dec7e
9369de0
8be67f2
9f42b6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -36,56 +36,119 @@ | |||||
*/ | ||||||
|
||||||
#include <aid_sources/range_finder/range_finder_consistency_check.hpp> | ||||||
#include "ekf_derivation/generated/range_validation_filter.h" | ||||||
|
||||||
void RangeFinderConsistencyCheck::update(float dist_bottom, float dist_bottom_var, float vz, float vz_var, | ||||||
bool horizontal_motion, uint64_t time_us) | ||||||
using namespace matrix; | ||||||
|
||||||
void RangeFinderConsistencyCheck::init(const float &z, const float &z_var, const float &dist_bottom, | ||||||
const float &dist_bottom_var) | ||||||
{ | ||||||
if (horizontal_motion) { | ||||||
_time_last_horizontal_motion = time_us; | ||||||
} | ||||||
float p[4] = {z_var, 0.f, 0.f, z_var + dist_bottom_var}; | ||||||
_P = Matrix<float, RangeFilter::size, RangeFilter::size>(p); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
sym::RangeValidationFilter(&_H); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you set the output to be None in symforce it will create a function that returns H instead of having to pass it via a pointer (
|
||||||
_x(RangeFilter::z.idx) = z; | ||||||
_x(RangeFilter::terrain.idx) = z - dist_bottom; | ||||||
_initialized = true; | ||||||
_state = _test_ratio_lpf.getState() < 1.f ? KinematicState::UNKNOWN : KinematicState::INCONSISTENT; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you need to have that in the init function? That's anyway only called when there was no sensor data for a long time, right? Then you could just set it to |
||||||
_test_ratio_lpf.reset(2.f); | ||||||
_t_since_first_sample = 0.f; | ||||||
_test_ratio_lpf.setAlpha(0.2f); | ||||||
} | ||||||
|
||||||
void RangeFinderConsistencyCheck::update(const float &z, const float &z_var, const float &vz, const float &vz_var, | ||||||
const float &dist_bottom, const float &dist_bottom_var, const uint64_t &time_us) | ||||||
{ | ||||||
const float dt = static_cast<float>(time_us - _time_last_update_us) * 1e-6f; | ||||||
|
||||||
if ((_time_last_update_us == 0) | ||||||
|| (dt < 0.001f) || (dt > 0.5f)) { | ||||||
if (_time_last_update_us == 0 || dt > 1.f) { | ||||||
_time_last_update_us = time_us; | ||||||
_dist_bottom_prev = dist_bottom; | ||||||
init(z, z_var, dist_bottom, dist_bottom_var); | ||||||
return; | ||||||
} | ||||||
|
||||||
const float vel_bottom = (dist_bottom - _dist_bottom_prev) / dt; | ||||||
_innov = -vel_bottom - vz; // vel_bottom is +up while vz is +down | ||||||
_time_last_update_us = time_us; | ||||||
|
||||||
// Variance of the time derivative of a random variable: var(dz/dt) = 2*var(z) / dt^2 | ||||||
const float var = 2.f * dist_bottom_var / (dt * dt); | ||||||
_innov_var = var + vz_var; | ||||||
_x(RangeFilter::z.idx) -= dt * vz; | ||||||
_P(0, 0) += dt * dt * vz_var + 0.001f; | ||||||
_P(1, 1) += terrain_process_noise; | ||||||
|
||||||
const float normalized_innov_sq = (_innov * _innov) / _innov_var; | ||||||
_test_ratio = normalized_innov_sq / (_gate * _gate); | ||||||
_signed_test_ratio_lpf.setParameters(dt, _signed_test_ratio_tau); | ||||||
const float signed_test_ratio = matrix::sign(_innov) * _test_ratio; | ||||||
_signed_test_ratio_lpf.update(signed_test_ratio); | ||||||
const Vector2f measurements(z, dist_bottom); | ||||||
|
||||||
updateConsistency(vz, time_us); | ||||||
Vector2f Kv{1.f, 0.f}; | ||||||
Vector2f test_ratio{0.f, 0.f}; | ||||||
Vector2f R{z_var, dist_bottom_var}; | ||||||
Vector2f y; | ||||||
|
||||||
_time_last_update_us = time_us; | ||||||
_dist_bottom_prev = dist_bottom; | ||||||
} | ||||||
for (int i = 0; i < 2 ; i++) { | ||||||
y = measurements - _H * _x; | ||||||
Vector2f H = _H.row(i); | ||||||
_innov_var = (H.transpose() * _P * H + R(i))(0, 0); | ||||||
Kv(RangeFilter::terrain.idx) = _P(RangeFilter::terrain.idx, i) / _innov_var; | ||||||
|
||||||
void RangeFinderConsistencyCheck::updateConsistency(float vz, uint64_t time_us) | ||||||
{ | ||||||
if (fabsf(_signed_test_ratio_lpf.getState()) >= 1.f) { | ||||||
if ((time_us - _time_last_horizontal_motion) > _signed_test_ratio_tau) { | ||||||
_is_kinematically_consistent = false; | ||||||
_time_last_inconsistent_us = time_us; | ||||||
Vector2f PH = _P.row(i); | ||||||
|
||||||
for (int u = 0; u < RangeFilter::size; u++) { | ||||||
for (int v = 0; v < RangeFilter::size; v++) { | ||||||
_P(u, v) -= Kv(u) * PH(v); | ||||||
} | ||||||
} | ||||||
|
||||||
} else { | ||||||
if ((fabsf(vz) > _min_vz_for_valid_consistency) | ||||||
&& (_test_ratio < 1.f) | ||||||
&& ((time_us - _time_last_inconsistent_us) > _consistency_hyst_time_us) | ||||||
) { | ||||||
_is_kinematically_consistent = true; | ||||||
PH = _P.col(i); | ||||||
|
||||||
for (int u = 0; u < RangeFilter::size; u++) { | ||||||
for (int v = 0; v <= u; v++) { | ||||||
_P(u, v) = _P(u, v) - PH(u) * Kv(v) + Kv(u) * R(i) * Kv(v); | ||||||
_P(v, u) = _P(u, v); | ||||||
} | ||||||
} | ||||||
|
||||||
test_ratio(i) = fminf(sq(y(i)) / sq(_gate), 4.f); | ||||||
|
||||||
if (i == (int)RangeFilter::z.idx && test_ratio(1) > 1.f) { | ||||||
Kv(1) = 0.f; | ||||||
} | ||||||
|
||||||
_x = _x + Kv * y; | ||||||
} | ||||||
|
||||||
_innov = y(RangeFilter::terrain.idx); | ||||||
_test_ratio_lpf.update(sign(_innov) * test_ratio(1)); | ||||||
|
||||||
// start the consistency check after 1s | ||||||
if (_t_since_first_sample + dt > 1.0f) { | ||||||
_t_since_first_sample = 2.0f; | ||||||
|
||||||
if (abs(_test_ratio_lpf.getState()) < 1.f) { | ||||||
const bool vertical_motion = sq(vz) > fmaxf(vz_var, 0.1f); | ||||||
|
||||||
if (!horizontal_motion && vertical_motion) { | ||||||
_state = KinematicState::CONSISTENT; | ||||||
|
||||||
} else { | ||||||
_state = KinematicState::UNKNOWN; | ||||||
} | ||||||
|
||||||
} else { | ||||||
_t_since_first_sample = 0.f; | ||||||
_state = KinematicState::INCONSISTENT; | ||||||
} | ||||||
|
||||||
} else { | ||||||
_t_since_first_sample += dt; | ||||||
} | ||||||
} | ||||||
|
||||||
void RangeFinderConsistencyCheck::run(const float &z, const float &vz, | ||||||
const matrix::SquareMatrix<float, estimator::State::size> &P, | ||||||
const float &dist_bottom, const float &dist_bottom_var, uint64_t time_us) | ||||||
{ | ||||||
const float z_var = P(estimator::State::pos.idx + 2, estimator::State::pos.idx + 2); | ||||||
const float vz_var = P(estimator::State::vel.idx + 2, estimator::State::vel.idx + 2); | ||||||
|
||||||
if (!_initialized || current_posD_reset_count != _last_posD_reset_count) { | ||||||
_last_posD_reset_count = current_posD_reset_count; | ||||||
init(z, z_var, dist_bottom, dist_bottom_var); | ||||||
} | ||||||
|
||||||
update(z, z_var, vz, vz_var, dist_bottom, dist_bottom_var, time_us); | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need to initialize the covariance elements as height and terrain states are correlated:
https://colab.research.google.com/drive/11gIyOub-rub4uL7mZHH1gGICL3fTTEyH?usp=sharing