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

Time error projected kalman filter #1036

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

oleflb
Copy link
Contributor

@oleflb oleflb commented May 29, 2024

Why? What?

Mitigates the effects of inaccurate measurement times.
The innovation covariance is adjusted by the derivative of the state, causing the filter to be more uncertain in the direction of state change.
Also fixes the covariance plotting in twix, which had an incorrect orientation before.

Fixes #

ToDo / Known Issues

  • Not sure how to deal with the current code duplication?

If this is a WIP describe which problems are to be fixed.

Ideas for Next Iterations (Not This PR)

If there are some improvements that could be done in a next iteration, describe them here.

How to Test

  • Use the Ball Filter overlay in twix
  • When the ball rolls towards the robot, the covariance should be elongated along the velocity direction of the ball

@schmidma
Copy link
Member

Is there any literature to that?

@oleflb
Copy link
Contributor Author

oleflb commented Jun 1, 2024

Is there any literature to that?

Didn't find literature, just blog post that I liked

@oleflb oleflb added the RC24 label Jun 1, 2024
@oleflb oleflb mentioned this pull request Jun 1, 2024
@oleflb oleflb enabled auto-merge June 1, 2024 16:08
@@ -322,6 +322,7 @@
"process_noise": [0.005, 0.005, 0.2, 0.2],
"measurement_noise_moving": [0.5, 2.0],
"measurement_noise_resting": [300.0, 500.0],
"measurement_time_noise": 40.0,
Copy link
Member

Choose a reason for hiding this comment

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

How did you tune this?

Comment on lines +6 to +22
pub trait TimeErrorProjectedKalmanFilter<const STATE_DIMENSION: usize> {
fn predict<const CONTROL_DIMENSION: usize>(
&mut self,
state_prediction: SMatrix<f32, STATE_DIMENSION, STATE_DIMENSION>,
control_input_model: SMatrix<f32, STATE_DIMENSION, CONTROL_DIMENSION>,
control: SVector<f32, CONTROL_DIMENSION>,
process_noise: SMatrix<f32, STATE_DIMENSION, STATE_DIMENSION>,
);
fn update<const MEASUREMENT_DIMENSION: usize>(
&mut self,
measurement_prediction: SMatrix<f32, MEASUREMENT_DIMENSION, STATE_DIMENSION>,
measurement: SVector<f32, MEASUREMENT_DIMENSION>,
measurement_noise: SMatrix<f32, MEASUREMENT_DIMENSION, MEASUREMENT_DIMENSION>,
state_derivative: SVector<f32, STATE_DIMENSION>,
measurement_time_noise: f32,
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
pub trait TimeErrorProjectedKalmanFilter<const STATE_DIMENSION: usize> {
fn predict<const CONTROL_DIMENSION: usize>(
&mut self,
state_prediction: SMatrix<f32, STATE_DIMENSION, STATE_DIMENSION>,
control_input_model: SMatrix<f32, STATE_DIMENSION, CONTROL_DIMENSION>,
control: SVector<f32, CONTROL_DIMENSION>,
process_noise: SMatrix<f32, STATE_DIMENSION, STATE_DIMENSION>,
);
fn update<const MEASUREMENT_DIMENSION: usize>(
&mut self,
measurement_prediction: SMatrix<f32, MEASUREMENT_DIMENSION, STATE_DIMENSION>,
measurement: SVector<f32, MEASUREMENT_DIMENSION>,
measurement_noise: SMatrix<f32, MEASUREMENT_DIMENSION, MEASUREMENT_DIMENSION>,
state_derivative: SVector<f32, STATE_DIMENSION>,
measurement_time_noise: f32,
);
}
pub trait TimeErrorProjectedKalmanFilter<const STATE_DIMENSION: usize> {
fn update_with_timing_noise<const MEASUREMENT_DIMENSION: usize>(
&mut self,
measurement_prediction: SMatrix<f32, MEASUREMENT_DIMENSION, STATE_DIMENSION>,
measurement: SVector<f32, MEASUREMENT_DIMENSION>,
measurement_noise: SMatrix<f32, MEASUREMENT_DIMENSION, MEASUREMENT_DIMENSION>,
state_derivative: SVector<f32, STATE_DIMENSION>,
measurement_time_noise: f32,
);
}

use types::multivariate_normal_distribution::MultivariateNormalDistribution;

use crate::kalman_filter::KalmanFilter;

Copy link
Member

Choose a reason for hiding this comment

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

Please link the blogpost somewhere here

Comment on lines +51 to +64
let residual = measurement - measurement_prediction * self.mean;
let time_error_adjusted_covariance = self.covariance
+ measurement_noise_variance * state_derivative * state_derivative.transpose();
let residual_covariance = measurement_prediction
* time_error_adjusted_covariance
* measurement_prediction.transpose()
+ measurement_noise;
let kalman_gain = self.covariance
* measurement_prediction.transpose()
* residual_covariance
.try_inverse()
.expect("Residual covariance matrix is not invertible");
self.mean += kalman_gain * residual;
self.covariance -= kalman_gain * measurement_prediction * self.covariance;
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure whether we want to split up the Kalman update into smaller steps, to reuse the functionality between the normal and the time-invariant version #codeDuplication

Comment on lines +340 to +347
let resting_state_derivative = nalgebra::vector![
hypothesis.resting_state.mean.z,
hypothesis.resting_state.mean.w,
(configuration.velocity_decay_factor - 1.) * hypothesis.resting_state.mean.z
/ cycle_time.as_secs_f32(),
(configuration.velocity_decay_factor - 1.) * hypothesis.resting_state.mean.w
/ cycle_time.as_secs_f32(),
];
Copy link
Member

Choose a reason for hiding this comment

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

Why is this repeated, if it is the same derivative as above?

Comment on lines +298 to +299
KalmanFilter::predict(
&mut hypothesis.moving_state,
Copy link
Member

Choose a reason for hiding this comment

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

If you'd rename the trait function as commented below, you can revert this explicit syntax back to the self.xxx

@@ -97,6 +104,11 @@ impl BallFilter {
context: &CycleContext,
) -> Vec<Hypothesis> {
for (detection_time, balls) in measurements {
let cycle_time = detection_time
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let cycle_time = detection_time
let last_cycle_duration = detection_time

? What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Request for Review
Development

Successfully merging this pull request may close these issues.

3 participants