Bayes Filters Library
sigma_point.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016-2019 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This software may be modified and distributed under the terms of the
5  * BSD 3-Clause license. See the accompanying LICENSE file for details.
6  */
7 
10 #include <BayesFilters/utils.h>
11 
12 #include <Eigen/SVD>
13 
14 using namespace bfl;
15 using namespace bfl::directional_statistics;
16 using namespace bfl::sigma_point;
17 using namespace bfl::utils;
18 using namespace Eigen;
19 
20 
22 (
23  std::size_t dof,
24  const double alpha,
25  const double beta,
26  const double kappa
27 ) :
28  mean((2 * dof) + 1),
29  covariance((2 * dof) + 1)
30 {
31  unscented_weights(dof, alpha, beta, kappa, mean, covariance, c);
32 }
33 
34 
36 (
37  const VectorDescription& vector_description,
38  const double alpha,
39  const double beta,
40  const double kappa
41 )
42 {
43  /* Degree of freedom associated to input space. */
44  std::size_t dof = vector_description.dof_size();
45 
46  mean.resize((2 * dof) + 1);
47  covariance.resize((2 * dof) + 1);
48 
49  unscented_weights(dof, alpha, beta, kappa, mean, covariance, c);
50 }
51 
52 
54 (
55  const std::size_t n,
56  const double alpha,
57  const double beta,
58  const double kappa,
59  Ref<VectorXd> weight_mean,
60  Ref<VectorXd> weight_covariance,
61  double& c
62 )
63 {
64  double lambda = std::pow(alpha, 2.0) * (n + kappa) - n;
65 
66  for (int j = 0; j < ((2 * n) + 1); ++j)
67  {
68  if (j == 0)
69  {
70  weight_mean(j) = lambda / (n + lambda);
71  weight_covariance(j) = lambda / (n + lambda) + (1 - std::pow(alpha, 2.0) + beta);
72  }
73  else
74  {
75  weight_mean(j) = 1 / (2 * (n + lambda));
76  weight_covariance(j) = weight_mean(j);
77  }
78  }
79 
80  c = n + lambda;
81 }
82 
83 
84 MatrixXd bfl::sigma_point::sigma_point(const GaussianMixture& state, const double c)
85 {
86  MatrixXd sigma_points(state.dim, ((state.dim_covariance * 2) + 1) * state.components);
87 
88  for (std::size_t i = 0; i < state.components; i++)
89  {
90  JacobiSVD<MatrixXd> svd = state.covariance(i).jacobiSvd(ComputeThinU);
91 
92  MatrixXd A = svd.matrixU() * svd.singularValues().cwiseSqrt().asDiagonal();
93 
94  Ref<MatrixXd> sp = sigma_points.middleCols(((state.dim_covariance * 2) + 1) * i, ((state.dim_covariance * 2) + 1));
95 
96  MatrixXd perturbations(state.dim_covariance, (state.dim_covariance * 2) + 1);
97  perturbations << VectorXd::Zero(state.dim_covariance), std::sqrt(c) * A, -std::sqrt(c) * A;
98 
99  if (state.dim_linear > 0)
100  sp.topRows(state.dim_linear) = perturbations.topRows(state.dim_linear).colwise() + state.mean(i).topRows(state.dim_linear);
101 
102  if (state.dim_circular > 0)
103  {
104  if (state.use_quaternion)
105  for (std::size_t j = 0; j < state.dim_circular; j++)
106  {
107  /* Enforce first sigma point to be the mean quaternion. */
108  sp.middleRows(state.dim_linear + j * 4, 4).col(0) = state.mean(i).middleRows(state.dim_linear + j * 4, 4);
109 
110  /* Evaluate the remaining sigma points as perturbation of the mean quaternion. */
111  sp.middleRows(state.dim_linear + j * 4, 4).rightCols(2 * state.dim_covariance) = sum_quaternion_rotation_vector(state.mean(i).middleRows(state.dim_linear + j * 4, 4), perturbations.middleRows(state.dim_linear + j * 3, 3).rightCols(2 * state.dim_covariance));
112  }
113  else
114  sp.middleRows(state.dim_linear, state.dim_circular) = directional_add(perturbations.middleRows(state.dim_linear, state.dim_circular), state.mean(i).middleRows(state.dim_linear, state.dim_circular));
115  }
116 
117  if (state.dim_noise > 0)
118  sp.bottomRows(state.dim_noise) = perturbations.bottomRows(state.dim_noise).colwise() + state.mean(i).bottomRows(state.dim_noise);
119  }
120 
121  return sigma_points;
122 }
123 
124 
125 std::tuple<bool, GaussianMixture, MatrixXd> bfl::sigma_point::unscented_transform
126 (
127  const GaussianMixture& input,
128  const UTWeight& weight,
129  FunctionEvaluation function
130 )
131 {
132  /* Sample sigma points. */
133  MatrixXd input_sigma_points = sigma_point::sigma_point(input, weight.c);
134 
135  /* Propagate sigma points */
136  bool valid_fun_data;
137  Data fun_data;
138  VectorDescription output_description;
139  std::tie(valid_fun_data, fun_data, output_description) = function(input_sigma_points);
140 
141  /* Stop here if function evaluation failed. */
142  if (!valid_fun_data)
143  return std::make_tuple(false, GaussianMixture(), MatrixXd(0, 0));
144 
145  /* Unscented transforms are available only for vector-valued functions. Hence, casting Data to MatrixXd. */
146  MatrixXd prop_sigma_points = bfl::any::any_cast<MatrixXd&&>(std::move(fun_data));
147 
148  /* Initialize transformed gaussian. */
149  GaussianMixture output(input.components, output_description.linear_components(), output_description.circular_components(), output_description.circular_type == VectorDescription::CircularType::Quaternion);
150 
151  /* Initialize cross covariance matrix (noise components in the input are not considered). */
152  MatrixXd cross_covariance(input.dim_covariance - input.dim_noise, output.dim_covariance * output.components);
153 
154  /* Process all the components of the mixture. */
155  std::size_t base = ((input.dim_covariance * 2) + 1);
156  for (std::size_t i = 0; i < input.components; i++)
157  {
158  const Ref<MatrixXd> input_sigma_points_i = input_sigma_points.middleCols(base * i, base);
159  const Ref<MatrixXd> prop_sigma_points_i = prop_sigma_points.middleCols(base * i, base);
160 
161  /* Evaluate the mean. */
162  output.mean(i).topRows(output.dim_linear).noalias() = prop_sigma_points_i.topRows(output.dim_linear) * weight.mean;
163 
164  if (output.dim_circular > 0)
165  {
166  if (output.use_quaternion)
167  for (std::size_t j = 0; j < output.dim_circular; j++)
168  output.mean(i).middleRows(output.dim_linear + j * 4, 4) = mean_quaternion(weight.mean, prop_sigma_points_i.middleRows(output.dim_linear + j * 4, 4));
169  else
170  output.mean(i).bottomRows(output.dim_circular) = directional_mean(prop_sigma_points_i.bottomRows(output.dim_circular), weight.mean);
171  }
172 
173  /* Evaluate the covariance. */
174  MatrixXd offsets_from_mean(output.dim_covariance, input_sigma_points_i.cols());
175 
176  offsets_from_mean.topRows(output.dim_linear) = prop_sigma_points_i.topRows(output.dim_linear).colwise() - output.mean(i).topRows(output.dim_linear);
177  if (output.dim_circular > 0)
178  {
179  if (output.use_quaternion)
180  for (std::size_t j = 0; j < output.dim_circular; j++)
181  offsets_from_mean.middleRows(output.dim_linear + j * 3, 3) = diff_quaternion(prop_sigma_points_i.middleRows(output.dim_linear + j * 4, 4), output.mean(i).middleRows(output.dim_linear + j * 4, 4));
182  else
183  offsets_from_mean.bottomRows(output.dim_circular) = directional_sub(prop_sigma_points_i.bottomRows(output.dim_circular), output.mean(i).bottomRows(output.dim_circular));
184  }
185  output.covariance(i).noalias() = offsets_from_mean * weight.covariance.asDiagonal() * offsets_from_mean.transpose();
186 
187  /* Evaluate the input-output cross covariance matrix (noise components in the input are not considered). */
188  Ref<MatrixXd> cross_covariance_i = cross_covariance.middleCols(output.dim_covariance * i, output.dim_covariance);
189  MatrixXd input_offsets_from_mean(input.dim_covariance - input.dim_noise, input_sigma_points_i.cols());
190  input_offsets_from_mean.topRows(input.dim_linear) = input_sigma_points_i.topRows(input.dim_linear).colwise() - input.mean(i).topRows(input.dim_linear);
191  if (input.dim_circular > 0)
192  {
193  if (input.use_quaternion)
194  for (std::size_t j = 0; j < input.dim_circular; j++)
195  input_offsets_from_mean.middleRows(input.dim_linear + j * 3, 3) = diff_quaternion(input_sigma_points_i.middleRows(input.dim_linear + j * 4, 4), input.mean(i).middleRows(input.dim_linear + j * 4, 4));
196  else
197  input_offsets_from_mean.bottomRows(input.dim_circular) = directional_sub(input_sigma_points_i.middleRows(input.dim_linear, input.dim_circular), input.mean(i).middleRows(input.dim_linear, input.dim_circular));
198  }
199  cross_covariance_i.noalias() = input_offsets_from_mean * weight.covariance.asDiagonal() * offsets_from_mean.transpose();
200  }
201 
202  return std::make_tuple(true, output, cross_covariance);
203 }
204 
205 
206 std::pair<GaussianMixture, MatrixXd> bfl::sigma_point::unscented_transform
207 (
208  const GaussianMixture& state,
209  const UTWeight& weight,
210  StateModel& state_model
211 )
212 {
213  FunctionEvaluation f = [&state_model](const Ref<const MatrixXd>& state)
214  {
215  MatrixXd tmp(state_model.getStateDescription().total_size(), state.cols());
216 
217  state_model.motion(state, tmp);
218 
219  return std::make_tuple(true, std::move(tmp), state_model.getStateDescription());
220  };
221  MatrixXd cross_covariance;
222  GaussianMixture output;
223  std::tie(std::ignore, output, cross_covariance) = unscented_transform(state, weight, f);
224 
225  return std::make_pair(output, cross_covariance);
226 }
227 
228 
229 std::pair<GaussianMixture, MatrixXd> bfl::sigma_point::unscented_transform
230 (
231  const GaussianMixture& state,
232  const UTWeight& weight,
233  AdditiveStateModel& state_model
234 )
235 {
236  FunctionEvaluation f = [&state_model](const Ref<const MatrixXd>& state)
237  {
238  MatrixXd tmp(state.rows(), state.cols());
239 
240  state_model.propagate(state, tmp);
241 
242  return std::make_tuple(true, std::move(tmp), state_model.getStateDescription());
243  };
244 
245  MatrixXd cross_covariance;
246  GaussianMixture output;
247  std::tie(std::ignore, output, cross_covariance) = unscented_transform(state, weight, f);
248 
249  /* In the additive case the covariance matrix is augmented with the noise
250  covariance matrix. */
251  for(std::size_t i = 0; i < state.components; i++)
252  output.covariance(i) += state_model.getNoiseCovarianceMatrix();
253 
254  return std::make_pair(output, cross_covariance);
255 }
256 
257 
258 std::tuple<bool, GaussianMixture, MatrixXd> bfl::sigma_point::unscented_transform
259 (
260  const GaussianMixture& state,
261  const UTWeight& weight,
262  MeasurementModel& meas_model
263 )
264 {
265  FunctionEvaluation f = [&meas_model](const Ref<const MatrixXd>& state)
266  {
267  bool valid_prediction;
268  bfl::Data prediction;
269 
270  std::tie(valid_prediction, prediction) = meas_model.predictedMeasure(state);
271 
272  return std::make_tuple(valid_prediction, std::move(prediction), meas_model.getMeasurementDescription());
273  };
274 
275  bool valid;
276  MatrixXd cross_covariance;
277  GaussianMixture output;
278  std::tie(valid, output, cross_covariance) = unscented_transform(state, weight, f);
279 
280  return std::make_tuple(valid, output, cross_covariance);
281 }
282 
283 
284 std::tuple<bool, GaussianMixture, MatrixXd> bfl::sigma_point::unscented_transform
285 (
286  const GaussianMixture& state,
287  const UTWeight& weight,
288  AdditiveMeasurementModel& meas_model
289 )
290 {
291  FunctionEvaluation f = [&meas_model](const Ref<const MatrixXd>& state)
292  {
293  bool valid_prediction;
294  bfl::Data prediction;
295 
296  std::tie(valid_prediction, prediction) = meas_model.predictedMeasure(state);
297 
298  return std::make_tuple(valid_prediction, std::move(prediction), meas_model.getMeasurementDescription());
299  };
300 
301  bool valid;
302  MatrixXd cross_covariance;
303  GaussianMixture output;
304  std::tie(valid, output, cross_covariance) = unscented_transform(state, weight, f);
305 
306  /* In the additive case the covariance matrix is augmented with the noise
307  covariance matrix. */
308  MatrixXd noise_cov;
309  std::tie(std::ignore, noise_cov) = meas_model.getNoiseCovarianceMatrix();
310  for (std::size_t i = 0; i < state.components; i++)
311  output.covariance(i) += noise_cov;
312 
313  return std::make_tuple(valid, output, cross_covariance);
314 }
sigma_point.h
bfl::utils::mean_quaternion
Eigen::Matrix< DerivedScalar, 4, 1 > mean_quaternion(const Eigen::MatrixBase< Derived > &weight, const Eigen::MatrixBase< DerivedArg2 > &quaternion)
Evaluate the weighted mean of a set of unit quaternions given a set of weights .
Definition: utils.h:271
bfl::VectorDescription::dof_size
std::size_t dof_size() const
Definition: VectorDescription.cpp:72
bfl::directional_statistics::directional_mean
Eigen::VectorXd directional_mean(const Eigen::Ref< const Eigen::MatrixXd > &a, const Eigen::Ref< const Eigen::VectorXd > &w)
bfl::MeasurementModel::getNoiseCovarianceMatrix
virtual std::pair< bool, Eigen::MatrixXd > getNoiseCovarianceMatrix() const
Definition: MeasurementModel.cpp:16
bfl::directional_statistics::directional_add
Eigen::MatrixXd directional_add(const Eigen::Ref< const Eigen::MatrixXd > &a, const Eigen::Ref< const Eigen::VectorXd > &b)
bfl::GaussianMixture::dim_circular
std::size_t dim_circular
Definition: GaussianMixture.h:77
bfl::VectorDescription::linear_components
std::size_t linear_components() const
Definition: VectorDescription.cpp:27
bfl::MeasurementModel::predictedMeasure
virtual std::pair< bool, Data > predictedMeasure(const Eigen::Ref< const Eigen::MatrixXd > &cur_states) const =0
bfl::GaussianMixture::dim_covariance
std::size_t dim_covariance
Definition: GaussianMixture.h:81
bfl::sigma_point::UTWeight::c
double c
c = sqrt(n + lambda) with lambda a ut parameter.
Definition: sigma_point.h:46
bfl::directional_statistics::directional_sub
Eigen::MatrixXd directional_sub(const Eigen::Ref< const Eigen::MatrixXd > &a, const Eigen::Ref< const Eigen::VectorXd > &b)
bfl
Port of boost::any for C++11 compilers.
Definition: AdditiveMeasurementModel.h:13
bfl::GaussianMixture::components
std::size_t components
Definition: GaussianMixture.h:67
bfl::GaussianMixture::dim_linear
std::size_t dim_linear
Definition: GaussianMixture.h:75
bfl::sigma_point::sigma_point
Eigen::MatrixXd sigma_point(const GaussianMixture &state, const double c)
Definition: sigma_point.cpp:84
bfl::utils::diff_quaternion
Eigen::Matrix< DerivedScalar, 3, Eigen::Dynamic > diff_quaternion(const Eigen::MatrixBase< Derived > &quaternion_left, const Eigen::MatrixBase< DerivedArg2 > &quaternion_right)
Evaluate the colwise difference between a set of unit quaternions and a unit quaternion .
Definition: utils.h:227
bfl::sigma_point::unscented_transform
std::tuple< bool, GaussianMixture, Eigen::MatrixXd > unscented_transform(const GaussianMixture &input, const UTWeight &weight, FunctionEvaluation function)
Definition: sigma_point.cpp:126
bfl::MeasurementModel::getMeasurementDescription
virtual VectorDescription getMeasurementDescription() const
Definition: MeasurementModel.cpp:37
bfl::GaussianMixture::mean
Eigen::Ref< Eigen::MatrixXd > mean()
Definition: GaussianMixture.cpp:94
bfl::utils::sum_quaternion_rotation_vector
Eigen::Matrix< DerivedScalar, 4, Eigen::Dynamic > sum_quaternion_rotation_vector(const Eigen::MatrixBase< Derived > &quaternion, const Eigen::MatrixBase< DerivedArg2 > &rotation_vector)
Evaluate the colwise sum between a unit quaternion and a set of rotation vectors in the tangent spa...
Definition: utils.h:184
bfl::sigma_point::UTWeight
Definition: sigma_point.h:37
utils.h
bfl::directional_statistics
Definition: directional_statistics.h:16
bfl::VectorDescription::CircularType::Quaternion
@ Quaternion
bfl::StateProcess::motion
virtual void motion(const Eigen::Ref< const Eigen::MatrixXd > &cur_states, Eigen::Ref< Eigen::MatrixXd > mot_states)=0
bfl::GaussianMixture::dim_noise
std::size_t dim_noise
Definition: GaussianMixture.h:79
bfl::GaussianMixture::covariance
Eigen::Ref< Eigen::MatrixXd > covariance()
Definition: GaussianMixture.cpp:130
bfl::sigma_point::UTWeight::UTWeight
UTWeight(std::size_t dof, const double alpha, const double beta, const double kappa)
Constructs the weights from number of degrees of freedom of the input space and UT parameters alpha,...
Definition: sigma_point.cpp:22
bfl::AdditiveMeasurementModel
This class represent an additive measurement model f(x) + w, where x is a state vector and w is rando...
Definition: AdditiveMeasurementModel.h:21
bfl::GaussianMixture::dim
std::size_t dim
Definition: GaussianMixture.h:73
bfl::StateProcess::getStateDescription
virtual VectorDescription getStateDescription()=0
Returns the vector description of the output of the state equation.
bfl::GaussianMixture::use_quaternion
bool use_quaternion
Definition: GaussianMixture.h:69
bfl::sigma_point
Definition: sigma_point.h:27
bfl::StateModel
Definition: StateModel.h:22
bfl::VectorDescription::circular_components
std::size_t circular_components() const
Definition: VectorDescription.cpp:33
bfl::StateModel::getNoiseCovarianceMatrix
virtual Eigen::MatrixXd getNoiseCovarianceMatrix()
Definition: StateModel.cpp:72
bfl::StateProcess::propagate
virtual void propagate(const Eigen::Ref< const Eigen::MatrixXd > &cur_states, Eigen::Ref< Eigen::MatrixXd > prop_states)=0
bfl::MeasurementModel
This class represent a generic measurement model f(x, w), where x is a state vector and w is random n...
Definition: MeasurementModel.h:29
bfl::utils
Definition: utils.h:20
bfl::sigma_point::unscented_weights
void unscented_weights(const std::size_t n, const double alpha, const double beta, const double kappa, Eigen::Ref< Eigen::VectorXd > weight_mean, Eigen::Ref< Eigen::VectorXd > weight_covariance, double &c)
bfl::AdditiveStateModel
Definition: AdditiveStateModel.h:19
bfl::sigma_point::UTWeight::covariance
Eigen::VectorXd covariance
Definition: sigma_point.h:41
bfl::sigma_point::UTWeight::mean
Eigen::VectorXd mean
Definition: sigma_point.h:39
bfl::VectorDescription::total_size
std::size_t total_size() const
Definition: VectorDescription.cpp:66
bfl::VectorDescription::circular_type
CircularType circular_type
Definition: VectorDescription.h:49
bfl::sigma_point::FunctionEvaluation
std::function< std::tuple< bool, bfl::Data, bfl::VectorDescription >(const Eigen::Ref< const Eigen::MatrixXd > &)> FunctionEvaluation
A FunctionEvaluation return.
Definition: sigma_point.h:35
bfl::any::any
The class any describes a type-safe container for single values of any type.
Definition: any.h:77
bfl::GaussianMixture
Definition: GaussianMixture.h:20
bfl::VectorDescription
Definition: VectorDescription.h:18
directional_statistics.h