Skip to content

Commit a7f6728

Browse files
authored
Merge pull request #50 from jbrodovsky:jbrodovsky/issue39
Fix ukf propagate test bug
2 parents bf6c881 + c8748ea commit a7f6728

3 files changed

Lines changed: 65 additions & 86 deletions

File tree

core/src/filter.rs

Lines changed: 65 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,6 @@ impl SigmaPoint {
149149
/// sigma_point.forward(&imu_data, dt, None);
150150
/// ```
151151
pub fn forward(&mut self, imu_data: &IMUData, dt: f64, noise: Option<Vec<f64>>) {
152-
// Subtract out the imu biases
153-
154152
// Propagate the strapdown state using the strapdown equations
155153
self.nav_state.forward(imu_data, dt);
156154
let noise_vect = match noise {
@@ -272,6 +270,27 @@ impl SigmaPoint {
272270
};
273271
SigmaPoint::new(nav_state, other_states, weight)
274272
}
273+
/// Get a debugging string representation of the sigma point.
274+
pub fn get_string(&self) -> String {
275+
let mut state_str = String::new();
276+
state_str.push_str(&format!(
277+
"SigmaPoint: [lat: {:.4}, lon: {:.4}, alt: {:.2}, vel_n: {:.2}, vel_e: {:.2}, vel_d: {:.2}, roll: {:.2}, pitch: {:.2}, yaw: {:.2}]",
278+
self.nav_state.latitude.to_degrees(),
279+
self.nav_state.longitude.to_degrees(),
280+
self.nav_state.altitude,
281+
self.nav_state.velocity_north,
282+
self.nav_state.velocity_east,
283+
self.nav_state.velocity_down,
284+
self.nav_state.attitude.euler_angles().0.to_degrees(),
285+
self.nav_state.attitude.euler_angles().1.to_degrees(),
286+
self.nav_state.attitude.euler_angles().2.to_degrees()
287+
));
288+
if !self.other_states.is_empty() {
289+
state_str.push_str(&format!(", other_states: {:?}", self.other_states));
290+
}
291+
state_str.push_str(&format!(", weight: {}", self.weight));
292+
state_str
293+
}
275294
}
276295

277296
/// Basic strapdown state parameters for the UKF and particle filter initialization.
@@ -443,41 +462,28 @@ impl UKF {
443462
/// * none
444463
pub fn predict(&mut self, imu_data: &IMUData, dt: f64) {
445464
// Propagate the strapdown state using the strapdown equations
446-
//let mut sigma_points = self.sigma_points_as_matrix();
447-
// for i in 0..sigma_points.ncols() {
448-
// let mut sigma_point =
449-
// SigmaPoint::from_vector(sigma_points.column(i).into(), None, false);
450-
// sigma_point.forward(imu_data, dt, None);
451-
// sigma_points
452-
// .column_mut(i)
453-
// .copy_from(&sigma_point.to_vector(false));
454-
// }
455465
let mut sigma_points = self.get_sigma_points();
456-
let mut sigma_points_matrix =
457-
DMatrix::<f64>::zeros(self.state_size, 2 * self.state_size + 1);
458-
for (i, sigma_point) in sigma_points.iter_mut().enumerate() {
466+
for sigma_point in &mut sigma_points {
467+
println!("{}", sigma_point.get_string());
459468
sigma_point.forward(imu_data, dt, None);
460-
sigma_points_matrix
461-
.column_mut(i)
462-
.copy_from(&sigma_point.to_vector(false));
463469
}
464-
let mu_bar = &sigma_points_matrix * &self.weights_mean;
465-
let mut cov_bar = DMatrix::<f64>::zeros(self.state_size, self.state_size);
466-
// Update the covariance through a naive loop
467-
// for (i, column) in sigma_points.column_iter().enumerate() {
468-
// let diff = column - &mu_bar;
469-
// cov_bar += self.weights_cov[i] * (&diff * &diff.transpose());
470-
// }
471-
//let mut diff = DMatrix::<f64>::zeros(self.state_size, 2 * self.state_size + 1);
472-
for i in 0..(2 * self.state_size + 1) {
473-
//diff.column_mut(i).copy_from(&(&sigma_points.column(i) - &mu_bar));
474-
let diff = &sigma_points_matrix.column(i) - &mu_bar;
475-
cov_bar += self.weights_cov[i] * (&diff * diff.transpose());
470+
// Update the mean state as mu_bar
471+
let mut mu_bar = DVector::<f64>::zeros(self.state_size);
472+
for (i, sigma_point) in sigma_points.iter().enumerate() {
473+
mu_bar += self.weights_mean[i] * sigma_point.to_vector(false);
476474
}
477-
//let cov_bar = (&diff * &diff.transpose()) * &self.weights_cov;
475+
// Update the covariance as P_bar
476+
let mut p_bar = DMatrix::<f64>::zeros(self.state_size, self.state_size);
477+
for (i, sigma_point) in sigma_points.iter().enumerate() {
478+
let diff = sigma_point.to_vector(false) - &mu_bar;
479+
p_bar += self.weights_cov[i] * &diff * &diff.transpose();
480+
}
481+
// Add process noise to the covariance
482+
p_bar += &self.process_noise;
483+
// Update the mean state and covariance
478484
self.mean_state = mu_bar;
479-
self.covariance = &cov_bar + &self.process_noise;
480-
}
485+
self.covariance = p_bar;
486+
}
481487
/// Perform the Kalman measurement update step.
482488
///
483489
/// This method updates the state and covariance based on the measurement and measurement
@@ -901,6 +907,8 @@ impl GPS for UKF {
901907
/// Tests
902908
#[cfg(test)]
903909
mod tests {
910+
use std::os::unix::process;
911+
904912
use crate::earth;
905913

906914
use super::*;
@@ -1081,66 +1089,53 @@ mod tests {
10811089
}
10821090
#[test]
10831091
fn ukf_propagate() {
1084-
let position = [0.0, 0.0, 0.0];
1085-
let velocity = [0.0, 0.0, 0.0];
1086-
let attitude = [0.0, 0.0, 0.0];
1087-
let imu_biases = vec![0.0; 6];
1088-
//let measurement_bias = vec![1.0, 1.0, 1.0];
1089-
let n = 9 + imu_biases.len(); // + measurement_bias.len();
1090-
let covariance_diagonal = vec![1e-9; n];
1091-
let process_noise_diagonal = vec![1e-6; n];
1092-
let alpha = 1e-3;
1093-
let beta = 2.0;
1094-
let kappa = 0.0;
1092+
let n = 15;
1093+
let process_noise_diagonal = vec![1e-3; n];
10951094
let ukf_params = StrapdownParams {
1096-
latitude: position[0],
1097-
longitude: position[1],
1098-
altitude: position[2],
1099-
northward_velocity: velocity[0],
1100-
eastward_velocity: velocity[1],
1101-
downward_velocity: velocity[2],
1102-
roll: attitude[0],
1103-
pitch: attitude[1],
1104-
yaw: attitude[2],
1095+
latitude: 0.0,
1096+
longitude: 0.0,
1097+
altitude: 0.0,
1098+
northward_velocity: 0.0,
1099+
eastward_velocity: 0.0,
1100+
downward_velocity: 0.0,
1101+
roll: 0.0,
1102+
pitch: 0.0,
1103+
yaw: 0.0,
11051104
in_degrees: false,
11061105
};
11071106
let mut ukf = UKF::new(
11081107
ukf_params,
1109-
imu_biases.clone(),
1108+
vec![0.0; 6],
11101109
None, //Some(measurement_bias.clone()),
1111-
covariance_diagonal,
1110+
vec![0.0; n], // Absolute certainty use for testing the process
11121111
DMatrix::from_diagonal(&DVector::from_vec(process_noise_diagonal)),
1113-
alpha,
1114-
beta,
1115-
kappa,
1112+
1e-3,
1113+
2.0,
1114+
0.0,
11161115
);
11171116
let dt = 1.0;
11181117
let imu_data = IMUData::new_from_vector(
11191118
Vector3::new(0.0, 0.0, earth::gravity(&0.0, &0.0)),
11201119
Vector3::new(0.0, 0.0, 0.0), // No rotation
11211120
);
1122-
println!("mean_state: {}", ukf.mean_state);
1123-
println!("covariance: {}", ukf.covariance);
1124-
println!("sigmas: {:.2e}", ukf.sigma_points_as_matrix());
11251121
ukf.predict(&imu_data, dt);
11261122
assert!(
11271123
ukf.mean_state.len()
1128-
== position.len() + velocity.len() + attitude.len() + imu_biases.len() //+ measurement_bias.len()
1124+
== 15 //+ measurement_bias.len()
11291125
);
1130-
println!("mean_state: {:.4e}", ukf.mean_state);
1131-
println!("covariance: {:.2e}", ukf.covariance);
1126+
let measurement = DVector::from_vec(vec![0.0; 3]);
11321127
ukf.update(
1133-
&DVector::from_vec(position.to_vec()),
1128+
&measurement,
11341129
&ukf.position_measurement_model(true),
11351130
&ukf.position_measurement_noise(true),
11361131
);
11371132
// Check that the state has not changed
1138-
assert_approx_eq!(ukf.mean_state[0], position[0], 1e-3);
1139-
assert_approx_eq!(ukf.mean_state[1], position[1], 1e-3);
1140-
assert_approx_eq!(ukf.mean_state[2], position[2], 0.1);
1141-
assert_approx_eq!(ukf.mean_state[3], velocity[0], 0.1);
1142-
assert_approx_eq!(ukf.mean_state[4], velocity[1], 0.1);
1143-
assert_approx_eq!(ukf.mean_state[5], velocity[2], 0.1);
1133+
assert_approx_eq!(ukf.mean_state[0], 0.0, 1e-3);
1134+
assert_approx_eq!(ukf.mean_state[1], 0.0, 1e-3);
1135+
assert_approx_eq!(ukf.mean_state[2], 0.0, 0.1);
1136+
assert_approx_eq!(ukf.mean_state[3], 0.0, 0.1);
1137+
assert_approx_eq!(ukf.mean_state[4], 0.0, 0.1);
1138+
assert_approx_eq!(ukf.mean_state[5], 0.0, 0.1);
11441139
}
11451140
#[test]
11461141
fn ukf_debug() {

core/src/sim.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -561,17 +561,6 @@ impl NavigationResult {
561561
///
562562
/// # Returns
563563
/// * `io::Result<()>` - Ok if successful, Err otherwise
564-
///
565-
/// # Example
566-
/// ```
567-
/// use strapdown::sim::NavigationResult;
568-
/// use std::path::Path;
569-
/// let records = vec![NavigationResult::new(
570-
/// "2023-01-01 00:00:00+00:00", &0.0, &0.0, &0.0, &0.0, &0.0, &0.0, &0.0, &0.0, &0.0, None )];
571-
/// NavigationResult::to_csv(&records, "navigation_results.csv").expect("Failed to write CSV");
572-
/// // doctest cleanup
573-
/// std::fs::remove_file("navigation_results.csv").unwrap();
574-
/// ```
575564
pub fn to_csv<P: AsRef<Path>>(records: &[Self], path: P) -> io::Result<()> {
576565
let mut writer = csv::Writer::from_path(path)?;
577566
for record in records {

core/src/strapdown.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -692,11 +692,6 @@ where
692692
}
693693
wrapped
694694
}
695-
696-
/// tester function for building bindings
697-
pub fn add(a: f64, b: f64) -> f64 {
698-
a + b
699-
}
700695
/// Wrap latitude to the range -90 to 90 degrees
701696
///
702697
/// This function is generic and can be used with any type that implements the necessary traits.

0 commit comments

Comments
 (0)