From 9a7bc1369b7d68547b4a88451b67b2961ea5a7ef Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Fri, 2 Feb 2024 11:29:38 +0000 Subject: [PATCH 1/9] initial draft of the MHT example --- docs/examples/mht_example.py | 289 +++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 docs/examples/mht_example.py diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py new file mode 100644 index 000000000..ff7bc77bc --- /dev/null +++ b/docs/examples/mht_example.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +======================================================== +General Multi Hypotheses tracking implementation example +======================================================== +""" + +# %% +# Multi hypotheses tracking (MHT) algorithm is considered one of the best algorithm +# for visual tracking, which consists in creating a tree of potential tracks for +# each target candidate (in a multi-target scenario) and prune such hypotheses +# in the data association phase. It is particularly efficient in maintain trajectories of +# multiple objects and handling uncertainties and ambiguities of tracks (e.g. presence of +# clutter). +# MHT, by definition, has several algorithms that fall under this definition, which +# we can list as Global Nearest Neighbour (GNN), Joint Probabilistic Data association +# (JPDA), Multi-frame assignment (MFA [#]_, see example here), Multi Bernoulli filter +# and Probabilistic multi hypotheses tracking (PMHT). +# In this example we employ the multi-frame assignment data associator for showing how +# to use the various components present in Stone Soup. +# +# This example follows this structure: +# 1. Create ground truth and detections; +# 2. Instantiate the tracking components and tracker; +# 3. Run the tracker and visualise the results. +# + +# %% +# General imports +# ^^^^^^^^^^^^^^^ +import numpy as np +from datetime import datetime, timedelta +from itertools import tee +from copy import deepcopy + +# %% +# Stone soup imports +# ^^^^^^^^^^^^^^^^^^ +from stonesoup.types.array import StateVector, CovarianceMatrix +from stonesoup.types.state import GaussianState +from stonesoup.models.transition.linear import CombinedLinearGaussianTransitionModel, ConstantVelocity +from stonesoup.models.measurement.nonlinear import CartesianToBearingRange +from stonesoup.simulator.simple import MultiTargetGroundTruthSimulator, SimpleDetectionSimulator + +# Simulation parameters +np.random.seed(1908) # fix a random seed +start_time = datetime.now().replace(microsecond=0) +simulation_steps = 50 +birth_rate = 0.2 # 20% +death_probability = 0.05 # 5% +timestep_size = timedelta(seconds=2) +prob_detection = 0.99 +initial_state_mean = StateVector([[10], [0], [10], [0]]) +initial_covariance = CovarianceMatrix(np.diag([5, 1, 5, 1])) + +# clutter will be generated uniformly in this are around the target +clutter_area = np.array([[-1, 1], [-1, 1]])*50 +clutter_rate = 0.5 +surveillance_area = ((clutter_area[0][1] - clutter_area[0][0])* + (clutter_area[1][1] - clutter_area[1][0])) +clutter_spatial_density = clutter_rate/surveillance_area + +# %% +# 1. Create ground truth and detections; +# -------------------------------------- +# We have prepared all the general parameters for the simulation, +# including the clutter spatial density, target birth rate and +# death probability. We can, now, instantiate the transition model +# of the targets and the measurement model. In this example we employ +# :class:`~.CartesianToBearingRange` non-linear measurement model. +# Then we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` +# and use a :class:`~.SimpleDetectionSimulator` +# to obtain the target ground truth tracks, detections and clutter. +# + +# Create an initial state +initial_state = GaussianState(state_vector=initial_state_mean, + covar=initial_covariance, + timestamp=start_time) + +# Instantiate the transition model +transition_model = CombinedLinearGaussianTransitionModel([ConstantVelocity(0.05), + ConstantVelocity(0.05)]) + +# Define a measuremnet model +measurement_model = CartesianToBearingRange(ndim_state=4, + mapping=(0, 2), + noise_covar=np.diag([np.radians(0.5), 3])) + +# Instantiate the multi-target simulator +ground_truth_simulator = MultiTargetGroundTruthSimulator( + transition_model=transition_model, + initial_state=initial_state, + timestep=timestep_size, + number_steps=simulation_steps, + birth_rate=birth_rate, + death_probability=death_probability) + +# Create a detector +detection_sim = SimpleDetectionSimulator( + groundtruth=ground_truth_simulator, + measurement_model=measurement_model, + detection_probability=prob_detection, + meas_range=clutter_area, + clutter_rate=clutter_rate) + + +# Instantiate a set for detections/clutter and ground truths +detections = set() +ground_truth = set() + +# Duplicate the detection simulator +plot, trackx = tee(detection_sim, 2) + +# Iterate in the detection simulator to generate the measurements +for time, dets in plot: + detections |= dets + ground_truth |= ground_truth_simulator.groundtruth_paths + +# Visualise the detections and tracks +from stonesoup.plotter import Plotterly + +plotter = Plotterly() +plotter.plot_ground_truths(ground_truth, [0, 2]) +plotter.plot_measurements(detections, [0, 2]) + +plotter.fig + +# %% +# 2. Instantiate the tracking components and tracker; +# --------------------------------------------------- +# We need to prepare the tracker and its components. In this +# example we consider a Unscented kalman filter since we are +# dealing with non-linear measurements. We consider a +# :class:`~.UnscentedKalmanPredictor` and :class:`~.UnscentedKalmanUpdater`. +# As said previously, we consider a Multi-frame assignment data associator +# which wraps a :class:`~.PDAHypothesiser` probability hypothesiser into a +# :class:`~.MFAHypothesiser` to work with the :class:`~.MFADataAssociator`. +# To instantiate the tracks we can use :class:`~.GaussianMixtureInitiator` which +# job is to start the tracks with GaussianMixture models. +# + +from stonesoup.predictor.kalman import UnscentedKalmanPredictor +from stonesoup.updater.kalman import UnscentedKalmanUpdater + +predictor = UnscentedKalmanPredictor(transition_model) +updater = UnscentedKalmanUpdater(measurement_model) + +from stonesoup.dataassociator.mfa import MFADataAssociator +from stonesoup.hypothesiser.mfa import MFAHypothesiser +from stonesoup.hypothesiser.probability import PDAHypothesiser + +hypothesiser = PDAHypothesiser(predictor, + updater, + clutter_spatial_density, + prob_gate=0.9999, + prob_detect=prob_detection) + +hypothesiser = MFAHypothesiser(hypothesiser) +data_associator = MFADataAssociator(hypothesiser, + slide_window=3) + +from stonesoup.deleter.time import UpdateTimeDeleter +deleter = UpdateTimeDeleter(timedelta(seconds=4), + delete_last_pred=False) + +from stonesoup.initiator.simple import MultiMeasurementInitiator +from stonesoup.hypothesiser.distance import DistanceHypothesiser +from stonesoup.dataassociator.neighbour import GNNWith2DAssignment +from stonesoup.measures import Mahalanobis +from stonesoup.types.state import TaggedWeightedGaussianState +from stonesoup.types.track import Track +from stonesoup.types.mixture import GaussianMixture +from stonesoup.types.numeric import Probability + +from stonesoup.hypothesiser.gaussianmixture import GaussianMixtureHypothesiser +from stonesoup.initiator.simple import GaussianMixtureInitiator, SimpleMeasurementInitiator + +# base_hypothesiser = DistanceHypothesiser( +# predictor=predictor, +# updater=updater, +# measure=Mahalanobis(), +# missed_distance=10, +# include_all=False +# ) +# +# hypothesiser_init = GaussianMixtureHypothesiser( +# base_hypothesiser +# ) + +# GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, +# initial_state.covar, +# timestamp=initial_state.timestamp, +# weight=Probability(2), +# tag=TaggedWeightedGaussianState.BIRTH,), +# ]), + + +initiator_p = MultiMeasurementInitiator( + GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, + initial_state.covar, + weight=Probability(1), + tag=[])]), + measurement_model=None, + deleter=deleter, + data_associator=GNNWith2DAssignment( + DistanceHypothesiser(predictor, updater, Mahalanobis(), missed_distance=5)), + updater=updater, + min_points=3 +) + +s_prior_state = GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, + initial_state.covar, + weight=Probability(0.1), tag=[])]) +initiator = GaussianMixtureInitiator( + SimpleMeasurementInitiator( + prior_state=s_prior_state, + measurement_model=measurement_model + ) +) + +# from stonesoup.initiator.simple import SimpleMeasurementInitiator +# birth_component = GaussianMixture([TaggedWeightedGaussianState( +# state_vector=initial_state.state_vector, +# covar=initial_state.covar, +# weight=0.5, +# tag=TaggedWeightedGaussianState.BIRTH, +# timestamp=start_time +# )]) + +# initiator = SimpleMeasurementInitiator(birth_component, +# measurement_model=measurement_model) + + +from stonesoup.tracker.simple import MultiTargetTracker, MultiTargetMixtureTracker + +tracker = MultiTargetTracker( # Runs the tracker + initiator=initiator, #GaussianMixtureInitiator(initiator_p), + deleter=deleter, + detector=detection_sim, + data_associator=data_associator, + updater=updater) +from stonesoup.types.update import GaussianMixtureUpdate +prior1 = GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, + initial_state.covar*1.5, + timestamp=initial_state.timestamp, + weight=Probability(0.5), + tag=[])]) +prior2 = deepcopy(prior1) + +tracks = {Track([prior1]), Track([prior2])} #, Track([prior1]), Track([prior1])} +#for (time, current_tracks) in tracker: +# print(time) +# tracks.update(current_tracks) + +for time, detection in trackx: #enumerate(detections): # loop over that + association = data_associator.associate(tracks, detection, time) + + for track, hypotheses in association.items(): + components = [] + for hypothesis in hypotheses: + if not hypothesis: + components.append(hypothesis.prediction) + else: + update = updater.update(hypothesis) + components.append(update) + track.append(GaussianMixtureUpdate(components=components, + hypothesis=hypotheses)) + + tracks.update(track) + +plotter.plot_tracks(tracks, [0, 2], track_label="EKF", line=dict(color="orange")) +plotter.fig.show() + +# %% +# Conclusion +# ---------- +# In this example we have presented how to set up a Multi-hypotheses tracking +# (MHT) simulation, by employing the existing components present in Stone Soup +# and perform the tracking in a cluttered multi-target scenario. + +# References +# ---------- +# .. [#] Xia, Y., Granström, K., Svensson, L., García-Fernández, Á.F., and Williams, J.L., +# 2019. Multiscan Implementation of the Trajectory Poisson Multi-Bernoulli Mixture Filter. +# J. Adv. Information Fusion, 14(2), pp. 213–235. + From e11bc7d3a9d34bccf36a7b14e8b5c088c1eae66f Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Wed, 7 Feb 2024 17:03:28 +0000 Subject: [PATCH 2/9] refined the example of MHT with MFA components and standard stone soup functions --- docs/examples/mht_example.py | 204 ++++++++++++++--------------------- 1 file changed, 81 insertions(+), 123 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index ff7bc77bc..be90c5026 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -16,15 +16,16 @@ # clutter). # MHT, by definition, has several algorithms that fall under this definition, which # we can list as Global Nearest Neighbour (GNN), Joint Probabilistic Data association -# (JPDA), Multi-frame assignment (MFA [#]_, see example here), Multi Bernoulli filter -# and Probabilistic multi hypotheses tracking (PMHT). -# In this example we employ the multi-frame assignment data associator for showing how -# to use the various components present in Stone Soup. +# (JPDA), Multi-frame assignment +# (MFA [#]_, see other example `here `__), +# Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). +# In this example we employ the multi-frame assignment data associator and +# hypothesiser for showing how to use components present in Stone Soup. # # This example follows this structure: -# 1. Create ground truth and detections; -# 2. Instantiate the tracking components and tracker; -# 3. Run the tracker and visualise the results. +# 1. Create ground truth and detections; +# 2. Instantiate the tracking components and tracker; +# 3. Run the tracker and visualise the results. # # %% @@ -33,14 +34,14 @@ import numpy as np from datetime import datetime, timedelta from itertools import tee -from copy import deepcopy # %% # Stone soup imports # ^^^^^^^^^^^^^^^^^^ from stonesoup.types.array import StateVector, CovarianceMatrix from stonesoup.types.state import GaussianState -from stonesoup.models.transition.linear import CombinedLinearGaussianTransitionModel, ConstantVelocity +from stonesoup.models.transition.linear import CombinedLinearGaussianTransitionModel, \ + ConstantVelocity from stonesoup.models.measurement.nonlinear import CartesianToBearingRange from stonesoup.simulator.simple import MultiTargetGroundTruthSimulator, SimpleDetectionSimulator @@ -48,16 +49,14 @@ np.random.seed(1908) # fix a random seed start_time = datetime.now().replace(microsecond=0) simulation_steps = 50 -birth_rate = 0.2 # 20% -death_probability = 0.05 # 5% timestep_size = timedelta(seconds=2) prob_detection = 0.99 initial_state_mean = StateVector([[10], [0], [10], [0]]) -initial_covariance = CovarianceMatrix(np.diag([5, 1, 5, 1])) +initial_covariance = CovarianceMatrix(np.diag([30, 1, 40, 1])) # clutter will be generated uniformly in this are around the target -clutter_area = np.array([[-1, 1], [-1, 1]])*50 -clutter_rate = 0.5 +clutter_area = np.array([[-1, 1], [-1, 1]])*150 +clutter_rate = 10 surveillance_area = ((clutter_area[0][1] - clutter_area[0][0])* (clutter_area[1][1] - clutter_area[1][0])) clutter_spatial_density = clutter_rate/surveillance_area @@ -66,8 +65,10 @@ # 1. Create ground truth and detections; # -------------------------------------- # We have prepared all the general parameters for the simulation, -# including the clutter spatial density, target birth rate and -# death probability. We can, now, instantiate the transition model +# including the clutter spatial density, in this example we set +# the birth rate and the death probability as zero, using only the knowledge of the +# prior states to generate the tracks so the number of targets is fixed (3 in this case). +# We can, now, instantiate the transition model # of the targets and the measurement model. In this example we employ # :class:`~.CartesianToBearingRange` non-linear measurement model. # Then we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` @@ -81,13 +82,13 @@ timestamp=start_time) # Instantiate the transition model -transition_model = CombinedLinearGaussianTransitionModel([ConstantVelocity(0.05), - ConstantVelocity(0.05)]) +transition_model = CombinedLinearGaussianTransitionModel([ConstantVelocity(0.005), + ConstantVelocity(0.005)]) # Define a measuremnet model measurement_model = CartesianToBearingRange(ndim_state=4, mapping=(0, 2), - noise_covar=np.diag([np.radians(0.5), 3])) + noise_covar=np.diag([np.radians(1), 5])) # Instantiate the multi-target simulator ground_truth_simulator = MultiTargetGroundTruthSimulator( @@ -95,8 +96,9 @@ initial_state=initial_state, timestep=timestep_size, number_steps=simulation_steps, - birth_rate=birth_rate, - death_probability=death_probability) + birth_rate=0, # no other targets more than the specified ones + death_probability=0, # all targets will remain during the simulation + preexisting_states=[[10, 1, 10, 1], [-10, -1, -10, -1], [-10, -1, 10, 1]]) # Create a detector detection_sim = SimpleDetectionSimulator( @@ -106,48 +108,57 @@ meas_range=clutter_area, clutter_rate=clutter_rate) - # Instantiate a set for detections/clutter and ground truths detections = set() ground_truth = set() +timestamps = [] # Duplicate the detection simulator -plot, trackx = tee(detection_sim, 2) +plot, tracking = tee(detection_sim, 2) # Iterate in the detection simulator to generate the measurements for time, dets in plot: detections |= dets ground_truth |= ground_truth_simulator.groundtruth_paths + timestamps.append(time) # Visualise the detections and tracks -from stonesoup.plotter import Plotterly +from stonesoup.plotter import AnimatedPlotterly -plotter = Plotterly() +plotter = AnimatedPlotterly(timestamps) plotter.plot_ground_truths(ground_truth, [0, 2]) plotter.plot_measurements(detections, [0, 2]) - plotter.fig # %% # 2. Instantiate the tracking components and tracker; # --------------------------------------------------- # We need to prepare the tracker and its components. In this -# example we consider a Unscented kalman filter since we are +# example we consider an unscented kalman filter since we are # dealing with non-linear measurements. We consider a # :class:`~.UnscentedKalmanPredictor` and :class:`~.UnscentedKalmanUpdater`. # As said previously, we consider a Multi-frame assignment data associator # which wraps a :class:`~.PDAHypothesiser` probability hypothesiser into a # :class:`~.MFAHypothesiser` to work with the :class:`~.MFADataAssociator`. -# To instantiate the tracks we can use :class:`~.GaussianMixtureInitiator` which -# job is to start the tracks with GaussianMixture models. -# - +# To instantiate the tracks we could use :class:`~.GaussianMixtureInitiator` which +# wraps a Gaussian Initiator (such as :class:`~.MultiMeasurementInitiator`) and +# create GaussianMixture states, however it is not straightforward to adjust it +# from its original implementation (meant for GM-PHD application). +# Therefore, to create the track priors we consider a :class:`~.GaussianMixture` state over a +# :class:`~.TaggedWeightedGaussianState`, which can be used in MFA, and set it +# on the pre-existing states passed to the multi-groundtruth simulator. +# As it is now, there is not a tracker wrapper (as +# :class:`~.MultiTargetMixtureTracker`) that can be applied directly when dealing with MFA, +# so we need to specify the tracking loop explicitly. + +# load the components from stonesoup.predictor.kalman import UnscentedKalmanPredictor from stonesoup.updater.kalman import UnscentedKalmanUpdater predictor = UnscentedKalmanPredictor(transition_model) updater = UnscentedKalmanUpdater(measurement_model) +# Data associator and hypothesiser from stonesoup.dataassociator.mfa import MFADataAssociator from stonesoup.hypothesiser.mfa import MFAHypothesiser from stonesoup.hypothesiser.probability import PDAHypothesiser @@ -162,100 +173,47 @@ data_associator = MFADataAssociator(hypothesiser, slide_window=3) -from stonesoup.deleter.time import UpdateTimeDeleter -deleter = UpdateTimeDeleter(timedelta(seconds=4), - delete_last_pred=False) - -from stonesoup.initiator.simple import MultiMeasurementInitiator -from stonesoup.hypothesiser.distance import DistanceHypothesiser -from stonesoup.dataassociator.neighbour import GNNWith2DAssignment -from stonesoup.measures import Mahalanobis +# Prepare the priors from stonesoup.types.state import TaggedWeightedGaussianState from stonesoup.types.track import Track from stonesoup.types.mixture import GaussianMixture from stonesoup.types.numeric import Probability +from stonesoup.types.update import GaussianMixtureUpdate -from stonesoup.hypothesiser.gaussianmixture import GaussianMixtureHypothesiser -from stonesoup.initiator.simple import GaussianMixtureInitiator, SimpleMeasurementInitiator +prior1 = GaussianMixture([TaggedWeightedGaussianState( + StateVector([10, 1, 10, 1]), + np.diag([10, 1, 10, 1]), + timestamp=initial_state.timestamp, + weight=Probability(1), + tag=[])]) + +prior2 = GaussianMixture([TaggedWeightedGaussianState( + StateVector([-10, -1, -10, -1]), + np.diag([10, 1, 10, 1]), + timestamp=initial_state.timestamp, + weight=Probability(1), + tag=[])]) + +prior3 = GaussianMixture([TaggedWeightedGaussianState( + StateVector([-10, -1, 10, 1]), + np.diag([10, 1, 10, 1]), + timestamp=initial_state.timestamp, + weight=Probability(1), + tag=[])]) + +# instantiate the tracks +tracks = {Track([prior1]), + Track([prior2]), + Track([prior3])} -# base_hypothesiser = DistanceHypothesiser( -# predictor=predictor, -# updater=updater, -# measure=Mahalanobis(), -# missed_distance=10, -# include_all=False -# ) -# -# hypothesiser_init = GaussianMixtureHypothesiser( -# base_hypothesiser -# ) - -# GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, -# initial_state.covar, -# timestamp=initial_state.timestamp, -# weight=Probability(2), -# tag=TaggedWeightedGaussianState.BIRTH,), -# ]), - - -initiator_p = MultiMeasurementInitiator( - GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, - initial_state.covar, - weight=Probability(1), - tag=[])]), - measurement_model=None, - deleter=deleter, - data_associator=GNNWith2DAssignment( - DistanceHypothesiser(predictor, updater, Mahalanobis(), missed_distance=5)), - updater=updater, - min_points=3 -) - -s_prior_state = GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, - initial_state.covar, - weight=Probability(0.1), tag=[])]) -initiator = GaussianMixtureInitiator( - SimpleMeasurementInitiator( - prior_state=s_prior_state, - measurement_model=measurement_model - ) -) - -# from stonesoup.initiator.simple import SimpleMeasurementInitiator -# birth_component = GaussianMixture([TaggedWeightedGaussianState( -# state_vector=initial_state.state_vector, -# covar=initial_state.covar, -# weight=0.5, -# tag=TaggedWeightedGaussianState.BIRTH, -# timestamp=start_time -# )]) - -# initiator = SimpleMeasurementInitiator(birth_component, -# measurement_model=measurement_model) - - -from stonesoup.tracker.simple import MultiTargetTracker, MultiTargetMixtureTracker - -tracker = MultiTargetTracker( # Runs the tracker - initiator=initiator, #GaussianMixtureInitiator(initiator_p), - deleter=deleter, - detector=detection_sim, - data_associator=data_associator, - updater=updater) -from stonesoup.types.update import GaussianMixtureUpdate -prior1 = GaussianMixture([TaggedWeightedGaussianState(initial_state.state_vector, - initial_state.covar*1.5, - timestamp=initial_state.timestamp, - weight=Probability(0.5), - tag=[])]) -prior2 = deepcopy(prior1) - -tracks = {Track([prior1]), Track([prior2])} #, Track([prior1]), Track([prior1])} -#for (time, current_tracks) in tracker: -# print(time) -# tracks.update(current_tracks) - -for time, detection in trackx: #enumerate(detections): # loop over that +# %% +# 3. Run the tracker and visualise the results; +# --------------------------------------------- +# We are ready to loop over the detections in the +# simulation and obtain the final tracks. + + +for time, detection in tracking: association = data_associator.associate(tracks, detection, time) for track, hypotheses in association.items(): @@ -269,17 +227,17 @@ track.append(GaussianMixtureUpdate(components=components, hypothesis=hypotheses)) - tracks.update(track) + tracks.add(track) -plotter.plot_tracks(tracks, [0, 2], track_label="EKF", line=dict(color="orange")) -plotter.fig.show() +plotter.plot_tracks(tracks, [0, 2], track_label="Tracks", line=dict(color="Green")) +plotter.fig # %% # Conclusion # ---------- # In this example we have presented how to set up a Multi-hypotheses tracking # (MHT) simulation, by employing the existing components present in Stone Soup -# and perform the tracking in a cluttered multi-target scenario. +# and perform the tracking in a heavy cluttered multi-target scenario. # References # ---------- From 3afbd2da17685f2960f40cb65908d3238fe1a588 Mon Sep 17 00:00:00 2001 From: James Wright <69153443+jswright-dstl@users.noreply.github.com> Date: Wed, 7 Feb 2024 23:13:46 +0000 Subject: [PATCH 3/9] Fix reference block to text rather than code --- docs/examples/mht_example.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index be90c5026..7152573bb 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -239,6 +239,7 @@ # (MHT) simulation, by employing the existing components present in Stone Soup # and perform the tracking in a heavy cluttered multi-target scenario. +# %% # References # ---------- # .. [#] Xia, Y., Granström, K., Svensson, L., García-Fernández, Á.F., and Williams, J.L., From 8d12a120afac6f52c0f0a2a5d57a5951a07176c7 Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Tue, 13 Feb 2024 11:44:32 +0000 Subject: [PATCH 4/9] text upgrads, fix on the third track drift, better explanations and more references on the existing tutorials and examples --- docs/examples/mht_example.py | 50 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index 7152573bb..ecaa0b063 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -8,19 +8,24 @@ """ # %% -# Multi hypotheses tracking (MHT) algorithm is considered one of the best algorithm -# for visual tracking, which consists in creating a tree of potential tracks for -# each target candidate (in a multi-target scenario) and prune such hypotheses -# in the data association phase. It is particularly efficient in maintain trajectories of +# The multi hypotheses tracking (MHT) algorithm is considered one of the best tracking algorithms, +# consisting of creating a tree of potential tracks for +# each target candidate (in a multi-target scenario) and pruning such hypotheses +# in the data association phase. It is particularly efficient in maintaining trajectories of # multiple objects and handling uncertainties and ambiguities of tracks (e.g. presence of # clutter). +# # MHT, by definition, has several algorithms that fall under this definition, which -# we can list as Global Nearest Neighbour (GNN), Joint Probabilistic Data association -# (JPDA), Multi-frame assignment +# include Global Nearest Neighbour +# (GNN, tutorial `here '__), +# Joint Probabilistic Data association +# (JPDA, tutorial `here '__), +# Multi-frame assignment # (MFA [#]_, see other example `here `__), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). +# Some of these algorithms are already implemented the Stone Soup. # In this example we employ the multi-frame assignment data associator and -# hypothesiser for showing how to use components present in Stone Soup. +# hypothesiser using their Stone Soup implementation. # # This example follows this structure: # 1. Create ground truth and detections; @@ -36,7 +41,7 @@ from itertools import tee # %% -# Stone soup imports +# Stone Soup imports # ^^^^^^^^^^^^^^^^^^ from stonesoup.types.array import StateVector, CovarianceMatrix from stonesoup.types.state import GaussianState @@ -54,9 +59,9 @@ initial_state_mean = StateVector([[10], [0], [10], [0]]) initial_covariance = CovarianceMatrix(np.diag([30, 1, 40, 1])) -# clutter will be generated uniformly in this are around the target +# clutter will be generated uniformly in this area around the targets clutter_area = np.array([[-1, 1], [-1, 1]])*150 -clutter_rate = 10 +clutter_rate = 9 surveillance_area = ((clutter_area[0][1] - clutter_area[0][0])* (clutter_area[1][1] - clutter_area[1][0])) clutter_spatial_density = clutter_rate/surveillance_area @@ -65,7 +70,7 @@ # 1. Create ground truth and detections; # -------------------------------------- # We have prepared all the general parameters for the simulation, -# including the clutter spatial density, in this example we set +# including the clutter spatial density. In this example we set # the birth rate and the death probability as zero, using only the knowledge of the # prior states to generate the tracks so the number of targets is fixed (3 in this case). # We can, now, instantiate the transition model @@ -85,7 +90,7 @@ transition_model = CombinedLinearGaussianTransitionModel([ConstantVelocity(0.005), ConstantVelocity(0.005)]) -# Define a measuremnet model +# Define the measurement model measurement_model = CartesianToBearingRange(ndim_state=4, mapping=(0, 2), noise_covar=np.diag([np.radians(1), 5])) @@ -137,21 +142,22 @@ # example we consider an unscented kalman filter since we are # dealing with non-linear measurements. We consider a # :class:`~.UnscentedKalmanPredictor` and :class:`~.UnscentedKalmanUpdater`. -# As said previously, we consider a Multi-frame assignment data associator +# As said previously, we consider a multi-frame assignment data associator # which wraps a :class:`~.PDAHypothesiser` probability hypothesiser into a # :class:`~.MFAHypothesiser` to work with the :class:`~.MFADataAssociator`. # To instantiate the tracks we could use :class:`~.GaussianMixtureInitiator` which -# wraps a Gaussian Initiator (such as :class:`~.MultiMeasurementInitiator`) and -# create GaussianMixture states, however it is not straightforward to adjust it -# from its original implementation (meant for GM-PHD application). -# Therefore, to create the track priors we consider a :class:`~.GaussianMixture` state over a -# :class:`~.TaggedWeightedGaussianState`, which can be used in MFA, and set it -# on the pre-existing states passed to the multi-groundtruth simulator. -# As it is now, there is not a tracker wrapper (as +# uses a Gaussian Initiator (such as :class:`~.MultiMeasurementInitiator`) to +# create GaussianMixture prior states, however, its current implementation +# is intended for a GM-PHD filter making it troublesome to adapt for our needs. +# Therefore, we consider :class:`~.TaggedWeightedGaussianState` to create the track priors, +# which can be handled by MFA components, using the pre-existing states fed to the +# multi-groundtruth simulator. +# Such prior states are, then, wrapped in :class:`~.GaussianMixture` states. +# As it is now, there is not a tracker wrapper (as for the # :class:`~.MultiTargetMixtureTracker`) that can be applied directly when dealing with MFA, # so we need to specify the tracking loop explicitly. -# load the components +# load tracker the components from stonesoup.predictor.kalman import UnscentedKalmanPredictor from stonesoup.updater.kalman import UnscentedKalmanUpdater @@ -235,7 +241,7 @@ # %% # Conclusion # ---------- -# In this example we have presented how to set up a Multi-hypotheses tracking +# In this example we have presented how to set up a multi-hypotheses tracking # (MHT) simulation, by employing the existing components present in Stone Soup # and perform the tracking in a heavy cluttered multi-target scenario. From 9db204fb9b9d62e6887b964b21c1977aa4ba7f12 Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Tue, 13 Feb 2024 12:26:01 +0000 Subject: [PATCH 5/9] fix typo on link references --- docs/examples/mht_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index ecaa0b063..9d81540b0 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -17,9 +17,9 @@ # # MHT, by definition, has several algorithms that fall under this definition, which # include Global Nearest Neighbour -# (GNN, tutorial `here '__), +# (GNN, tutorial `here `__), # Joint Probabilistic Data association -# (JPDA, tutorial `here '__), +# (JPDA, tutorial `here `__), # Multi-frame assignment # (MFA [#]_, see other example `here `__), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). From 582a94f2b6eae85646ded8d2769bb7e90baa4963 Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Fri, 16 Feb 2024 09:58:09 +0000 Subject: [PATCH 6/9] changed references to other examples, small text improvements --- docs/examples/mht_example.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index 9d81540b0..37b4cfccd 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -16,12 +16,9 @@ # clutter). # # MHT, by definition, has several algorithms that fall under this definition, which -# include Global Nearest Neighbour -# (GNN, tutorial `here `__), -# Joint Probabilistic Data association -# (JPDA, tutorial `here `__), -# Multi-frame assignment -# (MFA [#]_, see other example `here `__), +# include Global Nearest Neighbour (GNN, :ref:`auto_tutorials/06_DataAssociation-MultiTargetTutorial:tutorial here`), +# Joint Probabilistic Data association (JPDA, :ref:`auto_tutorials/08_JPDATutorial:tutorial here`), +# Multi-frame assignment (MFA [#]_, see other :ref:`auto_examples/MFA_example:example here`), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). # Some of these algorithms are already implemented the Stone Soup. # In this example we employ the multi-frame assignment data associator and @@ -62,8 +59,8 @@ # clutter will be generated uniformly in this area around the targets clutter_area = np.array([[-1, 1], [-1, 1]])*150 clutter_rate = 9 -surveillance_area = ((clutter_area[0][1] - clutter_area[0][0])* - (clutter_area[1][1] - clutter_area[1][0])) +surveillance_area = ((clutter_area[0, 1] - clutter_area[0, 0])* + (clutter_area[1, 1] - clutter_area[1, 0])) clutter_spatial_density = clutter_rate/surveillance_area # %% @@ -73,8 +70,8 @@ # including the clutter spatial density. In this example we set # the birth rate and the death probability as zero, using only the knowledge of the # prior states to generate the tracks so the number of targets is fixed (3 in this case). -# We can, now, instantiate the transition model -# of the targets and the measurement model. In this example we employ +# +# We can instantiate the transition model of the targets and the measurement model. In this example we employ # :class:`~.CartesianToBearingRange` non-linear measurement model. # Then we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` # and use a :class:`~.SimpleDetectionSimulator` @@ -138,13 +135,14 @@ # %% # 2. Instantiate the tracking components and tracker; # --------------------------------------------------- -# We need to prepare the tracker and its components. In this -# example we consider an unscented kalman filter since we are -# dealing with non-linear measurements. We consider a -# :class:`~.UnscentedKalmanPredictor` and :class:`~.UnscentedKalmanUpdater`. +# We need to prepare the tracker and its components. In this example we consider an +# Unscented Kalman filter since we are dealing with non-linear measurements. +# We consider a :class:`~.UnscentedKalmanPredictor` and :class:`~.UnscentedKalmanUpdater`. +# # As said previously, we consider a multi-frame assignment data associator # which wraps a :class:`~.PDAHypothesiser` probability hypothesiser into a # :class:`~.MFAHypothesiser` to work with the :class:`~.MFADataAssociator`. +# # To instantiate the tracks we could use :class:`~.GaussianMixtureInitiator` which # uses a Gaussian Initiator (such as :class:`~.MultiMeasurementInitiator`) to # create GaussianMixture prior states, however, its current implementation @@ -153,6 +151,7 @@ # which can be handled by MFA components, using the pre-existing states fed to the # multi-groundtruth simulator. # Such prior states are, then, wrapped in :class:`~.GaussianMixture` states. +# # As it is now, there is not a tracker wrapper (as for the # :class:`~.MultiTargetMixtureTracker`) that can be applied directly when dealing with MFA, # so we need to specify the tracking loop explicitly. @@ -251,4 +250,3 @@ # .. [#] Xia, Y., Granström, K., Svensson, L., García-Fernández, Á.F., and Williams, J.L., # 2019. Multiscan Implementation of the Trajectory Poisson Multi-Bernoulli Mixture Filter. # J. Adv. Information Fusion, 14(2), pp. 213–235. - From 8e73c03b61e7e252e80a54c3abbdd5d4f919bc34 Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Fri, 16 Feb 2024 14:50:22 +0000 Subject: [PATCH 7/9] switched to doc instead of refs to create the hyper-links --- docs/examples/mht_example.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index 37b4cfccd..7b646cab6 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -16,9 +16,9 @@ # clutter). # # MHT, by definition, has several algorithms that fall under this definition, which -# include Global Nearest Neighbour (GNN, :ref:`auto_tutorials/06_DataAssociation-MultiTargetTutorial:tutorial here`), -# Joint Probabilistic Data association (JPDA, :ref:`auto_tutorials/08_JPDATutorial:tutorial here`), -# Multi-frame assignment (MFA [#]_, see other :ref:`auto_examples/MFA_example:example here`), +# include Global Nearest Neighbour (GNN, :doc:`check here `), +# Joint Probabilistic Data association (JPDA, :doc:`tutorial here `), +# Multi-frame assignment (MFA [#]_, see other :doc:`example here `), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). # Some of these algorithms are already implemented the Stone Soup. # In this example we employ the multi-frame assignment data associator and From 124b083b640c68a2104851b2d825763bf1e620a3 Mon Sep 17 00:00:00 2001 From: Alberto Acuto Date: Wed, 21 Feb 2024 15:23:37 +0000 Subject: [PATCH 8/9] documentation links fix --- docs/examples/mht_example.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/examples/mht_example.py b/docs/examples/mht_example.py index 7b646cab6..74da785a7 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/mht_example.py @@ -16,11 +16,12 @@ # clutter). # # MHT, by definition, has several algorithms that fall under this definition, which -# include Global Nearest Neighbour (GNN, :doc:`check here `), -# Joint Probabilistic Data association (JPDA, :doc:`tutorial here `), +# include Global Nearest Neighbour (GNN, :doc:`check here <../tutorials/06_DataAssociation-MultiTargetTutorial>`), +# Joint Probabilistic Data association (JPDA, :doc:`tutorial here <../tutorials/08_JPDATutorial>`), # Multi-frame assignment (MFA [#]_, see other :doc:`example here `), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). # Some of these algorithms are already implemented the Stone Soup. +# # In this example we employ the multi-frame assignment data associator and # hypothesiser using their Stone Soup implementation. # @@ -73,7 +74,7 @@ # # We can instantiate the transition model of the targets and the measurement model. In this example we employ # :class:`~.CartesianToBearingRange` non-linear measurement model. -# Then we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` +# Then, we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` # and use a :class:`~.SimpleDetectionSimulator` # to obtain the target ground truth tracks, detections and clutter. # From 55861420dd756dd911fe6169756afa83fbbdaf8c Mon Sep 17 00:00:00 2001 From: James Wright Date: Mon, 13 May 2024 09:47:43 +0100 Subject: [PATCH 9/9] Move MHT Example to new data association examples Fix links to other tutorials --- .../{ => dataassociation}/mht_example.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename docs/examples/{ => dataassociation}/mht_example.py (94%) diff --git a/docs/examples/mht_example.py b/docs/examples/dataassociation/mht_example.py similarity index 94% rename from docs/examples/mht_example.py rename to docs/examples/dataassociation/mht_example.py index 74da785a7..0b056bdb9 100644 --- a/docs/examples/mht_example.py +++ b/docs/examples/dataassociation/mht_example.py @@ -16,9 +16,11 @@ # clutter). # # MHT, by definition, has several algorithms that fall under this definition, which -# include Global Nearest Neighbour (GNN, :doc:`check here <../tutorials/06_DataAssociation-MultiTargetTutorial>`), -# Joint Probabilistic Data association (JPDA, :doc:`tutorial here <../tutorials/08_JPDATutorial>`), -# Multi-frame assignment (MFA [#]_, see other :doc:`example here `), +# include Global Nearest Neighbour (GNN, +# :doc:`tutorial <../../auto_tutorials/06_DataAssociation-MultiTargetTutorial>`), +# Joint Probabilistic Data association (JPDA, +# :doc:`tutorial <../../auto_tutorials/08_JPDATutorial>`), +# Multi-frame assignment (MFA [#]_, :doc:`example `), # Multi Bernoulli filter and Probabilistic multi hypotheses tracking (PMHT). # Some of these algorithms are already implemented the Stone Soup. # @@ -26,6 +28,7 @@ # hypothesiser using their Stone Soup implementation. # # This example follows this structure: +# # 1. Create ground truth and detections; # 2. Instantiate the tracking components and tracker; # 3. Run the tracker and visualise the results. @@ -60,7 +63,7 @@ # clutter will be generated uniformly in this area around the targets clutter_area = np.array([[-1, 1], [-1, 1]])*150 clutter_rate = 9 -surveillance_area = ((clutter_area[0, 1] - clutter_area[0, 0])* +surveillance_area = ((clutter_area[0, 1] - clutter_area[0, 0]) * (clutter_area[1, 1] - clutter_area[1, 0])) clutter_spatial_density = clutter_rate/surveillance_area @@ -72,11 +75,11 @@ # the birth rate and the death probability as zero, using only the knowledge of the # prior states to generate the tracks so the number of targets is fixed (3 in this case). # -# We can instantiate the transition model of the targets and the measurement model. In this example we employ -# :class:`~.CartesianToBearingRange` non-linear measurement model. +# We can instantiate the transition model of the targets and the measurement model. +# In this example we employ a :class:`~.CartesianToBearingRange` non-linear measurement model. # Then, we pass all these details to a :class:`~.MultiTargetGroundTruthSimulator` # and use a :class:`~.SimpleDetectionSimulator` -# to obtain the target ground truth tracks, detections and clutter. +# to obtain the target ground truth, detections and clutter. # # Create an initial state