Skip to content

Commit a213b35

Browse files
authored
feat: Add CTRV process model with Unscented Kalman filter (#2, #13)
A CTRV process model was added into the equations of Unscented Kalman filter. Partially written unit tests for new code. Some unit tests are to be written and marked with "TODO" comment. Resolves #2.
1 parent d2a38db commit a213b35

31 files changed

+841
-227
lines changed

include/definitions.hpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace ser94mor
2727
namespace sensor_fusion
2828
{
2929

30-
constexpr const double kEpsilon{1e-11};
30+
constexpr const double_t kEpsilon{1e-11};
3131

3232
enum class EntityType
3333
{
@@ -75,27 +75,28 @@ namespace ser94mor
7575
}
7676
}
7777

78-
using IndividualNoiseProcessesCovarianceMatrix = Eigen::Matrix<double, 2, 2>;
79-
80-
#define PROCESS_MODEL_DEFINITIONS(state_vector_dims, control_vector_dims, is_linear) \
78+
#define PROCESS_MODEL_DEFINITIONS(state_vector_dims, control_vector_dims, process_noise_vector_dims, is_linear) \
8179
const int kStateVectorDims{state_vector_dims}; \
8280
const int kControlVectorDims{control_vector_dims}; \
81+
const int kProcessNoiseVectorDims{process_noise_vector_dims}; \
8382
const bool kIsLinear{is_linear}; \
84-
using StateVector = Eigen::Matrix<double, kStateVectorDims, 1>; \
85-
using StateCovarianceMatrix = Eigen::Matrix<double, kStateVectorDims, kStateVectorDims>; \
86-
using StateTransitionMatrix = Eigen::Matrix<double, kStateVectorDims, kStateVectorDims>; \
87-
using ControlVector = Eigen::Matrix<double, kControlVectorDims, 1>; \
88-
using ControlTransitionMatrix = Eigen::Matrix<double, kStateVectorDims, kControlVectorDims>; \
89-
using ProcessCovarianceMatrix = Eigen::Matrix<double, kStateVectorDims, kStateVectorDims>
83+
using StateVector = Eigen::Matrix<double_t, kStateVectorDims, 1>; \
84+
using StateCovarianceMatrix = Eigen::Matrix<double_t, kStateVectorDims, kStateVectorDims>; \
85+
using StateTransitionMatrix = Eigen::Matrix<double_t, kStateVectorDims, kStateVectorDims>; \
86+
using ControlVector = Eigen::Matrix<double_t, kControlVectorDims, 1>; \
87+
using ControlTransitionMatrix = Eigen::Matrix<double_t, kStateVectorDims, kControlVectorDims>; \
88+
using ProcessCovarianceMatrix = Eigen::Matrix<double_t, kStateVectorDims, kStateVectorDims>; \
89+
using ProcessNoiseVector = Eigen::Matrix<double_t, kProcessNoiseVectorDims, 1>; \
90+
using ProcessNoiseCovarianceMatrix = Eigen::Matrix<double_t, kProcessNoiseVectorDims, kProcessNoiseVectorDims>
9091

9192
namespace CV
9293
{
93-
PROCESS_MODEL_DEFINITIONS(4, 4, true);
94+
PROCESS_MODEL_DEFINITIONS(4, 4, 2, true);
9495
}
9596

9697
namespace CTRV
9798
{
98-
PROCESS_MODEL_DEFINITIONS(5, 5, false);
99+
PROCESS_MODEL_DEFINITIONS(5, 5, 2, false);
99100
}
100101

101102

@@ -106,8 +107,8 @@ namespace ser94mor
106107
#define MEASUREMENT_MODEL_DEFINITIONS(measurement_vector_dims, is_linear) \
107108
const int kMeasurementVectorDims{measurement_vector_dims}; \
108109
const bool kIsLinear{is_linear}; \
109-
using MeasurementVector = Eigen::Matrix<double, kMeasurementVectorDims, 1>; \
110-
using MeasurementCovarianceMatrix = Eigen::Matrix<double, kMeasurementVectorDims, kMeasurementVectorDims>
110+
using MeasurementVector = Eigen::Matrix<double_t, kMeasurementVectorDims, 1>; \
111+
using MeasurementCovarianceMatrix = Eigen::Matrix<double_t, kMeasurementVectorDims, kMeasurementVectorDims>
111112

112113
namespace Lidar
113114
{

include/filters.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717

1818
#include "../src/filters/KalmanFilter.hpp"
1919
#include "../src/filters/ExtendedKalmanFilter.hpp"
20+
#include "../src/filters/UnscentedKalmanFilter.hpp"

src/beliefs/Belief.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace ser94mor
5050
* Belief's timestamp.
5151
* @return timestamp
5252
*/
53-
double t() const
53+
double_t t() const
5454
{
5555
return timestamp_;
5656
}
@@ -74,7 +74,7 @@ namespace ser94mor
7474
return state_covariance_matrix_;
7575
}
7676

77-
Belief(double timestamp, const StateVector& state_vector,
77+
Belief(double_t timestamp, const StateVector& state_vector,
7878
const StateCovarianceMatrix& state_covariance_matrix)
7979
: timestamp_{timestamp}, state_vector_{state_vector}, state_covariance_matrix_{state_covariance_matrix}
8080
{
@@ -120,7 +120,7 @@ namespace ser94mor
120120
}
121121

122122
private:
123-
double timestamp_;
123+
double_t timestamp_;
124124
StateVector state_vector_;
125125
StateCovarianceMatrix state_covariance_matrix_;
126126
};

src/filters/ExtendedKalmanFilter.hpp

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,61 +21,56 @@
2121

2222
#include "KalmanFilter.hpp"
2323

24-
#include <type_traits>
25-
2624

2725
namespace ser94mor
2826
{
2927
namespace sensor_fusion
3028
{
3129

3230
/**
33-
* A template class holding Extended Kalman Filter equations.
31+
* A template class holding extended Kalman filter equations.
3432
* The naming of vectors and matrices are taken from the
3533
* "Thrun, S., Burgard, W. and Fox, D., 2005. Probabilistic robotics. MIT press."
3634
*
3735
* @tparam ProcessModel a class of the process model to use
3836
* @tparam MeasurementModel a class of measurement model to use; notice that here it is not a template class
3937
*/
4038
template<class ProcessModel, class MeasurementModel>
41-
class ExtendedKalmanFilter : public KalmanFilter<ProcessModel, MeasurementModel>
39+
class ExtendedKalmanFilter : public KalmanFilterBase<ProcessModel, MeasurementModel, ExtendedKalmanFilter>
4240
{
4341
private:
4442
using Belief = typename ProcessModel::Belief_type;
4543
using ControlVector = typename ProcessModel::ControlVector_type;
4644
using Measurement = typename MeasurementModel::Measurement_type;
4745
public:
48-
using KalmanFilter<ProcessModel, MeasurementModel>::Predict;
49-
using KalmanFilter<ProcessModel, MeasurementModel>::Update;
46+
using KalmanFilterBase<ProcessModel, MeasurementModel, ExtendedKalmanFilter>::Predict;
47+
using KalmanFilterBase<ProcessModel, MeasurementModel, ExtendedKalmanFilter>::Update;
5048

5149
/**
5250
* Prediction step of the Extended Kalman filter. Predicts the object's state in dt time in the future
5351
* in accordance with NON-LINEAR process model and input control vector.
5452
*
5553
* Notice that for linear process models the compiler will choose the corresponding method from the
56-
* base class (KalmanFilter) which works with linear process models.
54+
* base class (KalmanFilterBase) which works with linear process models.
5755
*
58-
* @param belief_posterior a current belief of the object's state
56+
* @param bel a current belief of the object's state
5957
* @param ut a control vector
6058
* @param dt time interval between the previous and current measurements
6159
* @param process_model an instance of the process model
6260
*
6361
* @return a prior belief, that is, after prediction but before incorporating the measurement
6462
*/
65-
template<bool EnableBool = true>
66-
static Belief Predict(const Belief& belief_posterior,
67-
const ControlVector& ut,
68-
double dt,
69-
const std::enable_if_t<not ProcessModel::IsLinear() && EnableBool, ProcessModel>&
70-
process_model)
63+
template<bool enable = true>
64+
static auto
65+
Predict(const Belief& bel, const ControlVector& ut, double_t dt, const ProcessModel& process_model)
66+
-> std::enable_if_t<not ProcessModel::IsLinear() and enable, Belief>
7167
{
72-
auto mu{belief_posterior.mu()};
68+
auto mu{bel.mu()};
7369
auto Gt{process_model.G(dt, mu)};
7470
return {
75-
/* timestamp */ belief_posterior.t() + dt,
71+
/* timestamp */ bel.t() + dt,
7672
/* state vector */ process_model.g(dt, ut, mu),
77-
/* state covariance matrix */ Gt * belief_posterior.Sigma() * Gt.transpose()
78-
+ process_model.R(dt, mu),
73+
/* state covariance matrix */ Gt * bel.Sigma() * Gt.transpose() + process_model.R(dt, mu),
7974
};
8075
}
8176

@@ -86,23 +81,22 @@ namespace ser94mor
8681
* Notice that for linear measurement models the compiler will choose the corresponding method from the
8782
* base class (KalmanFilter) which works with linear measurement models.
8883
*
89-
* @param belief_prior a belief after the prediction Extended Kalman filter step
84+
* @param bel a belief after the prediction Extended Kalman filter step
9085
* @param measurement a measurement from the sensor
9186
* @param measurement_model an instance of the measurement model
9287
*
9388
* @return a posterior belief, that is, after the incorporation of the measurement
9489
*/
95-
template<bool EnableBool = true>
96-
static Belief Update(const Belief& belief_prior,
97-
const Measurement& measurement,
98-
const std::enable_if_t<not MeasurementModel::IsLinear() && EnableBool,
99-
MeasurementModel>& measurement_model)
90+
template<bool enable = true>
91+
static auto
92+
Update(const Belief& bel, const Measurement& measurement, const MeasurementModel& measurement_model)
93+
-> std::enable_if_t<not MeasurementModel::IsLinear() and enable, Belief>
10094
{
101-
auto mu{belief_prior.mu()};
102-
auto Sigma{belief_prior.Sigma()};
95+
auto mu{bel.mu()};
96+
auto Sigma{bel.Sigma()};
10397
auto Ht{measurement_model.H(mu)};
10498
auto Kt{Sigma * Ht.transpose() * (Ht * Sigma * Ht.transpose() + measurement_model.Q()).inverse()};
105-
auto I{Eigen::Matrix<double, ProcessModel::StateDims(), ProcessModel::StateDims()>::Identity()};
99+
auto I{Eigen::Matrix<double_t, ProcessModel::StateDims(), ProcessModel::StateDims()>::Identity()};
106100

107101
return {
108102
/* timestamp */ measurement.t(),

src/filters/KalmanFilter.hpp

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,74 +30,114 @@ namespace ser94mor
3030
{
3131

3232
/**
33-
* A template class holding Kalman Filter equations.
33+
* A base template class holding Kalman filter equations.
3434
* The naming of vectors and matrices are taken from the
3535
* "Thrun, S., Burgard, W. and Fox, D., 2005. Probabilistic robotics. MIT press."
3636
*
3737
* @tparam ProcessModel a class of the process model to use
3838
* @tparam MeasurementModel a class of measurement model to use; notice that here it is not a template class
39+
* @tparam DerivedKalmanFilter a concrete sub-template-class of the KalmanFilterBase;
40+
* it is needed to invoke methods from that class
3941
*/
40-
template<class ProcessModel, class MeasurementModel>
41-
class KalmanFilter
42+
template<class ProcessModel, class MeasurementModel, template<class, class> class DerivedKalmanFilter>
43+
class KalmanFilterBase
4244
{
4345
protected:
4446
using Belief = typename ProcessModel::Belief_type;
4547
using ControlVector = typename ProcessModel::ControlVector_type;
4648
using Measurement = typename MeasurementModel::Measurement_type;
47-
4849
public:
50+
4951
/**
5052
* Prediction step of the Kalman filter. Predicts the object's state in dt time in the future in accordance with
5153
* LINEAR process model and input control vector.
5254
*
53-
* @param belief_posterior a current belief of the object's state
55+
* @param bel a current belief of the object's state
5456
* @param ut a control vector
5557
* @param dt time interval between the previous and current measurements
5658
* @param process_model an instance of the process model
5759
*
5860
* @return a prior belief, that is, after prediction but before incorporating the measurement
5961
*/
60-
template <bool EnableBool = true>
61-
static Belief Predict(const Belief& belief_posterior,
62-
const ControlVector& ut,
63-
double dt,
64-
const std::enable_if_t<ProcessModel::IsLinear() && EnableBool, ProcessModel>& process_model)
62+
template <bool enable = true>
63+
static auto
64+
Predict(const Belief& bel, const ControlVector& ut, double_t dt, const ProcessModel& process_model)
65+
-> std::enable_if_t<ProcessModel::IsLinear() and enable, Belief>
6566
{
6667
auto At{process_model.A(dt)};
6768
return {
68-
/* timestamp */ belief_posterior.t() + dt,
69-
/* state vector */ At * belief_posterior.mu() + process_model.B() * ut,
70-
/* state covariance matrix */ At * belief_posterior.Sigma() * At.transpose() + process_model.R(dt),
69+
/* timestamp */ bel.t() + dt,
70+
/* state vector */ At * bel.mu() + process_model.B() * ut,
71+
/* state covariance matrix */ At * bel.Sigma() * At.transpose() + process_model.R(dt),
7172
};
7273
}
7374

7475
/**
7576
* Update step of the Kalman filter. Incorporates the sensor measurement into the given prior belief.
7677
* Works only with linear measurement models.
7778
*
78-
* @param belief_prior a belief after the prediction Kalman filter step
79+
* @param bel a belief after the prediction Kalman filter step
7980
* @param measurement a measurement from the sensor
8081
* @param measurement_model an instance of the measurement model
8182
*
8283
* @return a posterior belief, that is, after the incorporation of the measurement
8384
*/
84-
template <bool EnableBool = true>
85-
static Belief Update(const Belief& belief_prior, const Measurement& measurement,
86-
const std::enable_if_t<MeasurementModel::IsLinear() && EnableBool, MeasurementModel>&
87-
measurement_model)
85+
template <bool enable = true>
86+
static auto
87+
Update(const Belief& bel, const Measurement& measurement, const MeasurementModel& measurement_model)
88+
-> std::enable_if_t<MeasurementModel::IsLinear() and enable, Belief>
8889
{
8990
auto Ct{measurement_model.C()};
90-
auto mu{belief_prior.mu()};
91-
auto Sigma{belief_prior.Sigma()};
91+
auto mu{bel.mu()};
92+
auto Sigma{bel.Sigma()};
9293
auto Kt{Sigma * Ct.transpose() * (Ct * Sigma * Ct.transpose() + measurement_model.Q()).inverse()};
93-
auto I{Eigen::Matrix<double, ProcessModel::StateDims(), ProcessModel::StateDims()>::Identity()};
94+
auto I{Eigen::Matrix<double_t, ProcessModel::StateDims(), ProcessModel::StateDims()>::Identity()};
9495

9596
return {
9697
/* timestamp */ measurement.t(),
9798
/* state vector */ mu + Kt * (measurement.z() - Ct * mu),
9899
/* state covariance matrix */ (I - Kt * Ct) * Sigma,
99100
};
100101
}
102+
103+
/**
104+
* Combined Predict and Update steps of the derived Kalman filter.
105+
* Predicts the object's state in dt time in the future in accordance with the process model
106+
* and input control vector and then incorporates the sensor measurement into the belief.
107+
*
108+
* Notice that process model and mesurement model can be either linear and non-linear.
109+
*
110+
* @param bel a current belief of the object's state
111+
* @param ut a control vector
112+
* @param measurement a measurement from the sensor
113+
* @param process_model an instance of the process model
114+
* @param measurement_model an instance of the measurement model
115+
* @return a posterior belief, that is, after the prediction and incorporation of the measurement
116+
*/
117+
static Belief
118+
PredictUpdate(const Belief& bel, const ControlVector& ut, const Measurement& measurement,
119+
const ProcessModel& process_model, const MeasurementModel& measurement_model)
120+
{
121+
auto dt = measurement.t() - bel.t();
122+
123+
auto belief_prior{DerivedKalmanFilter<ProcessModel, MeasurementModel>::Predict(bel, ut, dt, process_model)};
124+
125+
return DerivedKalmanFilter<ProcessModel, MeasurementModel>::
126+
Update(belief_prior, measurement, measurement_model);
127+
}
128+
};
129+
130+
131+
/**
132+
* A concrete class representing Kalman filter. All the equations are the same as in its base class.
133+
*
134+
* @tparam ProcessModel a class of the process model to use
135+
* @tparam MeasurementModel a class of measurement model to use; notice that here it is not a template class
136+
*/
137+
template <class ProcessModel, class MeasurementModel>
138+
class KalmanFilter : public KalmanFilterBase<ProcessModel, MeasurementModel, KalmanFilter>
139+
{
140+
101141
};
102142

103143
}

0 commit comments

Comments
 (0)