Skip to content

Commit f3383e8

Browse files
TristanWolframEirikKolas
authored andcommitted
added docstring and minor changes
1 parent a8b2136 commit f3383e8

File tree

4 files changed

+247
-102
lines changed

4 files changed

+247
-102
lines changed

vortex-filtering/include/vortex_filtering/filters/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ The perceived measurements. This parameter consists of an Eigen vector. It shoul
273273
This will be a short description of what is happening inside the step function. For a more clear and specific explanation of the steps of the PDAF please look into the recommended textbooks.
274274
* The step function will first use the dynamic model to calculate the next state (only considering the given model). Then the sensor model is used to convert the predicted state into the measurement space. That means to get the value, we would measure with our sensor if the perceived state is like the predicted state (of the dynamic model). Concerning our rocket example: We predict the drive temperature with the dynamic model and use the sensor model to convert the drive temperature to the temperature we would measure from the outside.
275275
Both of those steps are done in one line of code by using the EKF explained earlier.
276-
* The second step is the gating of the measurements. This is to exclude measurements that are too far away. SO, we can assume that they are definitely clutter, and we won't use them. The **mahalanobis_threshold** parameter is used to define the threshold used for gating. This value will scale the covariance of the predicted state. All measurements inside this will be considered. Additionally,**min_gate_threshold** and **max_gate_threshold** are used here. Since we scale the over-time-changing covariance, we implemented a min and max threshold. If the scaled covariance is too large or too small, we still take measurements in a fixed area into account.
276+
* The second step is the gating of the measurements. This is to exclude measurements that are too far away. So, we can assume that they are definitely clutter, and we won't use them. The **mahalanobis_threshold** parameter is used to define the threshold used for gating. This value will scale the covariance of the predicted state. All measurements inside this will be considered. Additionally, **min_gate_threshold** and **max_gate_threshold** are used here. Since we scale the over-time-changing covariance, we implemented a min and max threshold. If the scaled covariance is too large or too small, we still take measurements in a fixed area into account.
277277
* The next step is the update states step. All measurements inside the gate will be compared to the predicted state estimate (by the dynamic model). This results in a Gaussian distribution for all of these measurements.
278278
* In the last step, the weighted average of estimated Gaussian distributions will be calculated. This weighted average will be the final output of the PDAF and is considered to be the current state estimate. Therefore, it will be the previous state estimate in the next iteration.
279279

vortex-filtering/include/vortex_filtering/filters/ipda.hpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
/**
2+
* @file ipda.hpp
3+
* @author Tristan Wolfram
4+
* @brief File for the IPDA filter
5+
* @version 1.0
6+
* @date 2024-05-07
7+
*
8+
* @copyright Copyright (c) 2024
9+
*
10+
*/
111
#pragma once
212
#include <vector>
313
#include <Eigen/Dense>
@@ -7,6 +17,11 @@
717

818
namespace vortex::filter
919
{
20+
/**
21+
* @brief The IPDA filter class
22+
* @tparam DynModT The dynamic model type
23+
* @tparam SensModT The sensor model type
24+
*/
1025
template <concepts::DynamicModelLTVWithDefinedSizes DynModT, concepts::SensorModelLTVWithDefinedSizes SensModT>
1126
class IPDA
1227
{
@@ -25,7 +40,7 @@ class IPDA
2540
using Gauss_x = typename T::Gauss_x;
2641
using Vec_z = typename T::Vec_z;
2742
using GaussMix = vortex::prob::GaussianMixture<N_DIM_x>;
28-
using PDAF = vortex::filter::PDAF<vortex::models::ConstantVelocity, vortex::models::IdentitySensorModel<4, 2>>;
43+
using PDAF = vortex::filter::PDAF<DynModT, SensModT>;
2944

3045
IPDA() = delete;
3146

@@ -60,6 +75,19 @@ class IPDA
6075
return (l_k * predicted_existence_probability) / (1 - (1 - l_k) * predicted_existence_probability);
6176
}
6277

78+
/**
79+
* @brief The IPDAF step function. Gets following parameters and calculates the next state with the probablity of
80+
* existence.
81+
* @param dyn_model The dynamic model.
82+
* @param sen_model The sensor model.
83+
* @param timestep The timestep.
84+
* @param x_est The estimated state.
85+
* @param z_meas The percieved measurements.
86+
* @param survive_est The estimated survival probability (current state).
87+
* @param config configuration data - see Config struct of PDAF.
88+
* @return A tuple containing the final state, the existence probability, the inside (of the gate) measurements, the
89+
* outside (of the gate) measurements, the predicted state, the predicted measurements, and the updated state.
90+
*/
6391
static std::tuple<Gauss_x, double, std::vector<Vec_z>, std::vector<Vec_z>, Gauss_x, Gauss_z, std::vector<Gauss_x>>
6492
step(const DynModT& dyn_model, const SensModT& sen_model, double timestep, const Gauss_x& x_est,
6593
const std::vector<Vec_z>& z_meas, double survive_est, const IPDA::Config& config)
Lines changed: 108 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
/**
2+
* @file ipda.hpp
3+
* @author Tristan Wolfram
4+
* @brief File for the PDAF filter
5+
* @version 1.0
6+
* @date 2024-05-07
7+
*
8+
* @copyright Copyright (c) 2024
9+
*
10+
*/
111
#pragma once
212

313
#include <Eigen/Dense>
@@ -7,9 +17,16 @@
717
#include <vector>
818
#include <vortex_filtering/vortex_filtering.hpp>
919

10-
namespace vortex::filter {
11-
12-
template <concepts::DynamicModelLTVWithDefinedSizes DynModT, concepts::SensorModelLTVWithDefinedSizes SensModT> class PDAF {
20+
namespace vortex::filter
21+
{
22+
/**
23+
* @brief The PDAF filter class
24+
* @tparam DynModT The dynamic model
25+
* @tparam SensModT The sensor model
26+
*/
27+
template <concepts::DynamicModelLTVWithDefinedSizes DynModT, concepts::SensorModelLTVWithDefinedSizes SensModT>
28+
class PDAF
29+
{
1330
public:
1431
static constexpr int N_DIM_x = DynModT::N_DIM_x;
1532
static constexpr int N_DIM_z = SensModT::N_DIM_z;
@@ -19,85 +36,140 @@ template <concepts::DynamicModelLTVWithDefinedSizes DynModT, concepts::SensorMod
1936

2037
using T = Types_xzuvw<N_DIM_x, N_DIM_z, N_DIM_u, N_DIM_v, N_DIM_w>;
2138

22-
using EKF = vortex::filter::EKF_t<N_DIM_x, N_DIM_z, N_DIM_u, N_DIM_v, N_DIM_w>;
23-
using Gauss_z = typename T::Gauss_z;
24-
using Gauss_x = typename T::Gauss_x;
25-
using Vec_z = typename T::Vec_z;
39+
using EKF = vortex::filter::EKF_t<N_DIM_x, N_DIM_z, N_DIM_u, N_DIM_v, N_DIM_w>;
40+
using Gauss_z = typename T::Gauss_z;
41+
using Gauss_x = typename T::Gauss_x;
42+
using Vec_z = typename T::Vec_z;
2643
using MeasurementsZd = std::vector<Vec_z>;
27-
using StatesXd = std::vector<Gauss_x>;
28-
using GaussMixZd = vortex::prob::GaussianMixture<N_DIM_x>;
44+
using StatesXd = std::vector<Gauss_x>;
45+
using GaussMixXd = vortex::prob::GaussianMixture<N_DIM_x>;
2946

30-
struct Config {
47+
struct Config
48+
{
3149
double mahalanobis_threshold = 1.0;
32-
double min_gate_threshold = 0.0;
33-
double max_gate_threshold = HUGE_VAL;
34-
double prob_of_detection = 1.0;
35-
double clutter_intensity = 1.0;
50+
double min_gate_threshold = 0.0;
51+
double max_gate_threshold = HUGE_VAL;
52+
double prob_of_detection = 1.0;
53+
double clutter_intensity = 1.0;
3654
};
3755

3856
PDAF() = delete;
3957

58+
/**
59+
* @brief The step function for the PDAF filter
60+
* @param dyn_model The dynamic model
61+
* @param sen_model The sensor model
62+
* @param timestep The timestep
63+
* @param x_est The estimated state (current state)
64+
* @param z_meas The measurements
65+
* @param config The configuration
66+
* @return A tuple containing the (new) estimated state, the measurements that were inside the gate, the measurements
67+
* that were outside the gate, the predicted state (from dynamic model), the predicted measurement (from sensor
68+
* model), and the updated states (comparisson of prediced state and actual measurements)
69+
*/
4070
static std::tuple<Gauss_x, MeasurementsZd, MeasurementsZd, Gauss_x, Gauss_z, StatesXd>
41-
step(const DynModT &dyn_model, const SensModT &sen_model, double timestep, const Gauss_x &x_est, const MeasurementsZd &z_meas, const Config &config)
71+
step(const DynModT& dyn_model, const SensModT& sen_model, double timestep, const Gauss_x& x_est,
72+
const MeasurementsZd& z_meas, const Config& config)
4273
{
43-
auto [x_pred, z_pred] = EKF::predict(dyn_model, sen_model, timestep, x_est);
44-
auto [inside, outside] = apply_gate(z_meas, z_pred, config.mahalanobis_threshold, config.min_gate_threshold, config.max_gate_threshold);
74+
auto [x_pred, z_pred] = EKF::predict(dyn_model, sen_model, timestep, x_est);
75+
auto [inside, outside] =
76+
apply_gate(z_meas, z_pred, config.mahalanobis_threshold, config.min_gate_threshold, config.max_gate_threshold);
4577

4678
StatesXd x_updated;
47-
for (const auto &measurement : inside) {
79+
for (const auto& measurement : inside)
80+
{
4881
x_updated.push_back(EKF::update(sen_model, x_pred, z_pred, measurement));
4982
}
5083

51-
Gauss_x x_final = get_weighted_average(inside, x_updated, z_pred, x_pred, config.prob_of_detection, config.clutter_intensity);
52-
return {x_final, inside, outside, x_pred, z_pred, x_updated};
84+
Gauss_x x_final =
85+
get_weighted_average(inside, x_updated, z_pred, x_pred, config.prob_of_detection, config.clutter_intensity);
86+
return { x_final, inside, outside, x_pred, z_pred, x_updated };
5387
}
5488

55-
static std::tuple<MeasurementsZd, MeasurementsZd> apply_gate(const MeasurementsZd &z_meas, const Gauss_z &z_pred, double mahalanobis_threshold,
56-
double min_gate_threshold = 0.0, double max_gate_threshold = HUGE_VAL)
89+
/**
90+
* @brief Applies the gate to the measurements
91+
* @param z_meas The measurements
92+
* @param z_pred The predicted measurement
93+
* @param mahalanobis_threshold The threshold for the Mahalanobis distance
94+
* @param min_gate_threshold The minimum threshold for the gate
95+
* @param max_gate_threshold The maximum threshold for the gate
96+
* @return A tuple containing the measurements that were inside the gate and the measurements that were outside the
97+
* gate
98+
*/
99+
static std::tuple<MeasurementsZd, MeasurementsZd> apply_gate(const MeasurementsZd& z_meas, const Gauss_z& z_pred,
100+
double mahalanobis_threshold,
101+
double min_gate_threshold = 0.0,
102+
double max_gate_threshold = HUGE_VAL)
57103
{
58104
MeasurementsZd inside_meas;
59105
MeasurementsZd outside_meas;
60106

61-
for (const auto &measurement : z_meas) {
107+
for (const auto& measurement : z_meas)
108+
{
62109
double mahalanobis_distance = z_pred.mahalanobis_distance(measurement);
63-
double regular_distance = (z_pred.mean() - measurement).norm();
64-
if ((mahalanobis_distance <= mahalanobis_threshold || regular_distance <= min_gate_threshold) && regular_distance <= max_gate_threshold) {
110+
double regular_distance = (z_pred.mean() - measurement).norm();
111+
if ((mahalanobis_distance <= mahalanobis_threshold || regular_distance <= min_gate_threshold) &&
112+
regular_distance <= max_gate_threshold)
113+
{
65114
inside_meas.push_back(measurement);
66115
}
67-
else {
116+
else
117+
{
68118
outside_meas.push_back(measurement);
69119
}
70120
}
71121

72-
return {inside_meas, outside_meas};
122+
return { inside_meas, outside_meas };
73123
}
74124

75-
// Getting weighted average of the predicted states
76-
static Gauss_x get_weighted_average(const MeasurementsZd &z_meas, const StatesXd &updated_states, const Gauss_z &z_pred, const Gauss_x &x_pred,
77-
double prob_of_detection, double clutter_intensity)
125+
/**
126+
* @brief Calculates the weighted average of the measurements
127+
* @param z_meas The measurements
128+
* @param updated_states The updated states
129+
* @param z_pred The predicted measurement
130+
* @param x_pred The predicted state
131+
* @param prob_of_detection The probability of detection
132+
* @param clutter_intensity The clutter intensity
133+
* @return The weighted average of the measurements
134+
*/
135+
static Gauss_x get_weighted_average(const MeasurementsZd& z_meas, const StatesXd& updated_states,
136+
const Gauss_z& z_pred, const Gauss_x& x_pred, double prob_of_detection,
137+
double clutter_intensity)
78138
{
79139
StatesXd states;
80140
states.push_back(x_pred);
81141
states.insert(states.end(), updated_states.begin(), updated_states.end());
82142

83143
Eigen::VectorXd weights = get_weights(z_meas, z_pred, prob_of_detection, clutter_intensity);
84144

85-
GaussMixZd gaussian_mixture(weights, states);
145+
GaussMixXd gaussian_mixture(weights, states);
86146

87147
return gaussian_mixture.reduce();
88148
}
89149

90-
// Getting association probabilities according to textbook p. 123 "Corollary 7.3.3"
91-
static Eigen::VectorXd get_weights(const MeasurementsZd &z_meas, const Gauss_z &z_pred, double prob_of_detection, double clutter_intensity)
150+
/**
151+
* @brief Gets the weights for the measurements -> see association probabilities in textbook p. 123
152+
* "Corollary 7.3.3
153+
* @param z_meas The measurements
154+
* @param z_pred The predicted measurement
155+
* @param prob_of_detection The probability of detection
156+
* @param clutter_intensity The clutter intensity
157+
* @return The weights for the measurements -> same order as in z_meas
158+
* The first element is the weight for no association
159+
* length of weights = z_meas.size() + 1
160+
*/
161+
static Eigen::VectorXd get_weights(const MeasurementsZd& z_meas, const Gauss_z& z_pred, double prob_of_detection,
162+
double clutter_intensity)
92163
{
93164
Eigen::VectorXd weights(z_meas.size() + 1);
94165

95166
// in case no measurement assosiates with the target
96167
double no_association = clutter_intensity * (1 - prob_of_detection);
97-
weights(0) = no_association;
168+
weights(0) = no_association;
98169

99170
// measurements associating with the target
100-
for (size_t k = 1; k < z_meas.size() + 1; k++) {
171+
for (size_t k = 1; k < z_meas.size() + 1; k++)
172+
{
101173
weights(k) = (prob_of_detection * z_pred.pdf(z_meas.at(k - 1)));
102174
}
103175

@@ -108,4 +180,4 @@ template <concepts::DynamicModelLTVWithDefinedSizes DynModT, concepts::SensorMod
108180
}
109181
};
110182

111-
} // namespace vortex::filter
183+
} // namespace vortex::filter

0 commit comments

Comments
 (0)