use tracktor::filters::lmb::{LabeledBirthModel, LmbFilter, LmbTrack, extract_lmb_estimates};
use tracktor::prelude::*;
struct SimpleBirthModel {
birth_locations: Vec<(f64, StateVector<f64, 4>, StateCovariance<f64, 4>)>,
}
impl SimpleBirthModel {
fn new() -> Self {
let cov = StateCovariance::from_matrix(nalgebra::matrix![
100.0, 0.0, 0.0, 0.0;
0.0, 100.0, 0.0, 0.0;
0.0, 0.0, 25.0, 0.0;
0.0, 0.0, 0.0, 25.0
]);
Self {
birth_locations: vec![
(0.03, StateVector::from_array([10.0, 10.0, 0.0, 0.0]), cov),
(0.03, StateVector::from_array([190.0, 190.0, 0.0, 0.0]), cov),
],
}
}
}
impl LabeledBirthModel<f64, 4> for SimpleBirthModel {
fn birth_tracks(&self, label_gen: &mut LabelGenerator) -> Vec<BernoulliTrack<f64, 4>> {
self.birth_locations
.iter()
.map(|(existence, mean, cov)| {
let label = label_gen.next_label();
let state = GaussianState::new(1.0, *mean, *cov);
BernoulliTrack::new(label, *existence, state)
})
.collect()
}
fn expected_birth_count(&self) -> f64 {
self.birth_locations.iter().map(|(e, _, _)| e).sum()
}
}
fn main() {
println!("LMB Filter: Labeled Multi-Target Tracking");
println!("==========================================\n");
let transition = ConstantVelocity2D::new(1.0, 0.99);
let observation = PositionSensor2D::new(5.0, 0.9);
let clutter = UniformClutter2D::new(5.0, (0.0, 200.0), (0.0, 200.0));
let birth = SimpleBirthModel::new();
let filter: LmbFilter<f64, _, _, _, _, 4, 2> =
LmbFilter::new(transition, observation, clutter, birth);
let initial_tracks = vec![
LmbTrack::new(
Label::new(0, 0),
0.95,
GaussianState::new(
1.0,
StateVector::from_array([50.0, 50.0, 2.0, 1.0]),
StateCovariance::from_matrix(nalgebra::matrix![
10.0, 0.0, 0.0, 0.0;
0.0, 10.0, 0.0, 0.0;
0.0, 0.0, 5.0, 0.0;
0.0, 0.0, 0.0, 5.0
]),
),
),
LmbTrack::new(
Label::new(0, 1),
0.95,
GaussianState::new(
1.0,
StateVector::from_array([100.0, 100.0, -1.0, 2.0]),
StateCovariance::from_matrix(nalgebra::matrix![
10.0, 0.0, 0.0, 0.0;
0.0, 10.0, 0.0, 0.0;
0.0, 0.0, 5.0, 0.0;
0.0, 0.0, 0.0, 5.0
]),
),
),
];
let mut state = filter.initial_state_from(initial_tracks);
println!(
"Initial: {} tracks, {:.2} expected targets\n",
state.num_tracks(),
state.expected_target_count()
);
let measurements_per_step = [
vec![[52.0, 51.0], [99.0, 102.0], [150.0, 30.0]], vec![[54.0, 52.0], [45.0, 180.0]], vec![[56.0, 53.0], [97.0, 106.0]], vec![[58.0, 54.0], [95.0, 110.0], [20.0, 20.0]], vec![[60.0, 55.0], [93.0, 114.0]], ];
let dt = 1.0;
for (t, meas_data) in measurements_per_step.iter().enumerate() {
let measurements: Vec<Measurement<f64, 2>> = meas_data
.iter()
.map(|m| Measurement::from_array(*m))
.collect();
println!("Time {}: {} measurements", t, measurements.len());
let (updated, stats) = filter.step(state, &measurements, dt);
state = updated;
state.tracks.prune_by_existence(0.01);
println!(
" Tracks: {}, Expected: {:.2}",
state.num_tracks(),
state.expected_target_count()
);
if stats.singular_covariance_count > 0 {
println!(
" Warning: {} singular covariances",
stats.singular_covariance_count
);
}
let estimates = extract_lmb_estimates(&state);
for est in &estimates {
println!(
" Track {:?}: pos=({:.1}, {:.1}), r={:.3}",
est.label,
est.state.index(0),
est.state.index(1),
est.existence
);
}
println!();
}
}