Main MRPT website > C++ reference for MRPT 1.9.9
CKalmanFilterCapable_impl.h
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 #ifndef CKalmanFilterCapable_IMPL_H
10 #define CKalmanFilterCapable_IMPL_H
11 
12 #ifndef CKalmanFilterCapable_H
13 #error Include this file only from 'CKalmanFilterCapable.h'
14 #endif
15 
16 namespace mrpt
17 {
18 namespace bayes
19 {
20 // The main entry point in the Kalman Filter class:
21 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
22  typename KFTYPE>
23 void CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE,
24  KFTYPE>::runOneKalmanIteration()
25 {
26  using namespace std;
28 
29  m_timLogger.enable(KF_options.enable_profiler);
30  m_timLogger.enter("KF:complete_step");
31 
32  ASSERT_(size_t(m_xkk.size()) == m_pkk.getColCount())
33  ASSERT_(size_t(m_xkk.size()) >= VEH_SIZE)
34 
35  // =============================================================
36  // 1. CREATE ACTION MATRIX u FROM ODOMETRY
37  // =============================================================
38  KFArray_ACT u;
39 
40  m_timLogger.enter("KF:1.OnGetAction");
41  OnGetAction(u);
42  m_timLogger.leave("KF:1.OnGetAction");
43 
44  // Sanity check:
45  if (FEAT_SIZE)
46  {
47  ASSERTDEB_(
48  (((m_xkk.size() - VEH_SIZE) / FEAT_SIZE) * FEAT_SIZE) ==
49  (m_xkk.size() - VEH_SIZE))
50  }
51 
52  // =============================================================
53  // 2. PREDICTION OF NEW POSE xv_{k+1|k}
54  // =============================================================
55  m_timLogger.enter("KF:2.prediction stage");
56 
57  const size_t N_map = getNumberOfLandmarksInTheMap();
58 
59  KFArray_VEH xv(&m_xkk[0]); // Vehicle pose
60 
61  bool skipPrediction = false; // Whether to skip the prediction step (in
62  // SLAM this is desired for the first
63  // iteration...)
64 
65  // Update mean: xv will have the updated pose until we update it in the
66  // filterState later.
67  // This is to maintain a copy of the last robot pose in the state vector,
68  // required for the Jacobian computation.
69  OnTransitionModel(u, xv, skipPrediction);
70 
71  if (!skipPrediction)
72  {
73  // =============================================================
74  // 3. PREDICTION OF COVARIANCE P_{k+1|k}
75  // =============================================================
76  // First, we compute de Jacobian fv_by_xv (derivative of f_vehicle wrt
77  // x_vehicle):
78  KFMatrix_VxV dfv_dxv;
79 
80  // Try closed-form Jacobian first:
81  m_user_didnt_implement_jacobian = false; // Set to true by the default
82  // method if not reimplemented
83  // in base class.
84  if (KF_options.use_analytic_transition_jacobian)
85  OnTransitionJacobian(dfv_dxv);
86 
87  if (m_user_didnt_implement_jacobian ||
88  !KF_options.use_analytic_transition_jacobian ||
89  KF_options.debug_verify_analytic_jacobians)
90  { // Numeric approximation:
91  KFArray_VEH xkk_vehicle(
92  &m_xkk[0]); // A copy of the vehicle part of the state vector.
93  KFArray_VEH xkk_veh_increments;
94  OnTransitionJacobianNumericGetIncrements(xkk_veh_increments);
95 
97  xkk_vehicle,
98  std::function<void(
99  const KFArray_VEH& x,
100  const std::pair<KFCLASS*, KFArray_ACT>& dat,
101  KFArray_VEH& out_x)>(&KF_aux_estimate_trans_jacobian),
102  xkk_veh_increments, std::pair<KFCLASS*, KFArray_ACT>(this, u),
103  dfv_dxv);
104 
105  if (KF_options.debug_verify_analytic_jacobians)
106  {
108  OnTransitionJacobian(dfv_dxv_gt);
109  if ((dfv_dxv - dfv_dxv_gt).array().abs().sum() >
110  KF_options.debug_verify_analytic_jacobians_threshold)
111  {
112  std::cerr << "[KalmanFilter] ERROR: User analytical "
113  "transition Jacobians are wrong: \n"
114  << " Real dfv_dxv: \n"
115  << dfv_dxv << "\n Analytical dfv_dxv:\n"
116  << dfv_dxv_gt << "Diff:\n"
117  << (dfv_dxv - dfv_dxv_gt) << "\n";
119  "ERROR: User analytical transition Jacobians are wrong "
120  "(More details dumped to cerr)")
121  }
122  }
123  }
124 
125  // Q is the process noise covariance matrix, is associated to the robot
126  // movement and is necesary to calculate the prediction P(k+1|k)
127  KFMatrix_VxV Q;
128  OnTransitionNoise(Q);
129 
130  // ====================================
131  // 3.1: Pxx submatrix
132  // ====================================
133  // Replace old covariance:
134  Eigen::Block<typename KFMatrix::Base, VEH_SIZE, VEH_SIZE>(m_pkk, 0, 0) =
135  Q +
136  dfv_dxv * Eigen::Block<typename KFMatrix::Base, VEH_SIZE, VEH_SIZE>(
137  m_pkk, 0, 0) *
138  dfv_dxv.transpose();
139 
140  // ====================================
141  // 3.2: All Pxy_i
142  // ====================================
143  // Now, update the cov. of landmarks, if any:
144  KFMatrix_VxF aux;
145  for (size_t i = 0; i < N_map; i++)
146  {
147  aux = dfv_dxv *
148  Eigen::Block<typename KFMatrix::Base, VEH_SIZE, FEAT_SIZE>(
149  m_pkk, 0, VEH_SIZE + i * FEAT_SIZE);
150 
151  Eigen::Block<typename KFMatrix::Base, VEH_SIZE, FEAT_SIZE>(
152  m_pkk, 0, VEH_SIZE + i * FEAT_SIZE) = aux;
153  Eigen::Block<typename KFMatrix::Base, FEAT_SIZE, VEH_SIZE>(
154  m_pkk, VEH_SIZE + i * FEAT_SIZE, 0) = aux.transpose();
155  }
156 
157  // =============================================================
158  // 4. NOW WE CAN OVERWRITE THE NEW STATE VECTOR
159  // =============================================================
160  for (size_t i = 0; i < VEH_SIZE; i++) m_xkk[i] = xv[i];
161 
162  // Normalize, if neccesary.
163  OnNormalizeStateVector();
164 
165  } // end if (!skipPrediction)
166 
167  const double tim_pred = m_timLogger.leave("KF:2.prediction stage");
168 
169  // =============================================================
170  // 5. PREDICTION OF OBSERVATIONS AND COMPUTE JACOBIANS
171  // =============================================================
172  m_timLogger.enter("KF:3.predict all obs");
173 
174  KFMatrix_OxO R; // Sensor uncertainty (covariance matrix): R
175  OnGetObservationNoise(R);
176 
177  // Predict the observations for all the map LMs, so the user
178  // can decide if their covariances (more costly) must be computed as well:
179  all_predictions.resize(N_map);
180  OnObservationModel(
181  mrpt::math::sequenceStdVec<size_t, 1>(0, N_map), all_predictions);
182 
183  const double tim_pred_obs = m_timLogger.leave("KF:3.predict all obs");
184 
185  m_timLogger.enter("KF:4.decide pred obs");
186 
187  // Decide if some of the covariances shouldn't be predicted.
188  predictLMidxs.clear();
189  if (FEAT_SIZE == 0)
190  // In non-SLAM problems, just do one prediction, for the entire system
191  // state:
192  predictLMidxs.assign(1, 0);
193  else
194  // On normal SLAM problems:
195  OnPreComputingPredictions(all_predictions, predictLMidxs);
196 
197  m_timLogger.leave("KF:4.decide pred obs");
198 
199  // =============================================================
200  // 6. COMPUTE INNOVATION MATRIX "S"
201  // =============================================================
202  // Do the prediction of the observation covariances:
203  // Compute S: S = H P ~H + R
204  //
205  // Build a big dh_dx Jacobian composed of the small block Jacobians.
206  // , but: it's actually a subset of the full Jacobian, since the
207  // non-predicted
208  // features do not appear.
209  //
210  // dh_dx: O*M x V+M*F
211  // S: O*M x O*M
212  // M = |predictLMidxs|
213  // O=size of each observation.
214  // F=size of features in the map
215  //
216  // Updated: Now we only keep the non-zero blocks of that Jacobian,
217  // in the vectors Hxs[] and Hys[].
218  //
219 
220  m_timLogger.enter("KF:5.build Jacobians");
221 
222  size_t N_pred =
223  FEAT_SIZE == 0
224  ? 1 /* In non-SLAM problems, there'll be only 1 fixed observation */
225  : predictLMidxs.size();
226 
227  vector_int data_association; // -1: New map feature.>=0: Indexes in the
228  // state vector
229 
230  // The next loop will only do more than one iteration if the heuristic in
231  // OnPreComputingPredictions() fails,
232  // which will be detected by the addition of extra landmarks to predict
233  // into "missing_predictions_to_add"
234  std::vector<size_t> missing_predictions_to_add;
235 
236  Hxs.resize(N_pred); // Lists of partial Jacobians
237  Hys.resize(N_pred);
238 
239  size_t first_new_pred = 0; // This will be >0 only if we perform multiple
240  // loops due to failures in the prediction
241  // heuristic.
242 
243  do
244  {
245  if (!missing_predictions_to_add.empty())
246  {
247  const size_t nNew = missing_predictions_to_add.size();
249  "[KF] *Performance Warning*: "
250  << nNew << " LMs were not correctly predicted by "
251  "OnPreComputingPredictions().");
252 
253  ASSERTDEB_(FEAT_SIZE != 0)
254  for (size_t j = 0; j < nNew; j++)
255  predictLMidxs.push_back(missing_predictions_to_add[j]);
256 
257  N_pred = predictLMidxs.size();
258  missing_predictions_to_add.clear();
259  }
260 
261  Hxs.resize(N_pred); // Append new entries, if needed.
262  Hys.resize(N_pred);
263 
264  for (size_t i = first_new_pred; i < N_pred; ++i)
265  {
266  const size_t lm_idx = FEAT_SIZE == 0 ? 0 : predictLMidxs[i];
267  KFMatrix_OxV& Hx = Hxs[i];
268  KFMatrix_OxF& Hy = Hys[i];
269 
270  // Try the analitic Jacobian first:
271  m_user_didnt_implement_jacobian =
272  false; // Set to true by the default method if not
273  // reimplemented in base class.
274  if (KF_options.use_analytic_observation_jacobian)
275  OnObservationJacobians(lm_idx, Hx, Hy);
276 
277  if (m_user_didnt_implement_jacobian ||
278  !KF_options.use_analytic_observation_jacobian ||
279  KF_options.debug_verify_analytic_jacobians)
280  { // Numeric approximation:
281  const size_t lm_idx_in_statevector =
282  VEH_SIZE + lm_idx * FEAT_SIZE;
283 
284  const KFArray_VEH x_vehicle(&m_xkk[0]);
285  const KFArray_FEAT x_feat(&m_xkk[lm_idx_in_statevector]);
286 
287  KFArray_VEH xkk_veh_increments;
288  KFArray_FEAT feat_increments;
289  OnObservationJacobiansNumericGetIncrements(
290  xkk_veh_increments, feat_increments);
291 
293  x_vehicle,
294  std::function<void(
295  const KFArray_VEH& x,
296  const std::pair<KFCLASS*, size_t>& dat,
297  KFArray_OBS& out_x)>(&KF_aux_estimate_obs_Hx_jacobian),
298  xkk_veh_increments,
299  std::pair<KFCLASS*, size_t>(this, lm_idx), Hx);
300  // The state vector was temporarily modified by
301  // KF_aux_estimate_*, restore it:
302  ::memcpy(&m_xkk[0], &x_vehicle[0], sizeof(m_xkk[0]) * VEH_SIZE);
303 
305  x_feat,
306  std::function<void(
307  const KFArray_FEAT& x,
308  const std::pair<KFCLASS*, size_t>& dat,
309  KFArray_OBS& out_x)>(&KF_aux_estimate_obs_Hy_jacobian),
310  feat_increments, std::pair<KFCLASS*, size_t>(this, lm_idx),
311  Hy);
312  // The state vector was temporarily modified by
313  // KF_aux_estimate_*, restore it:
314  ::memcpy(
315  &m_xkk[lm_idx_in_statevector], &x_feat[0],
316  sizeof(m_xkk[0]) * FEAT_SIZE);
317 
318  if (KF_options.debug_verify_analytic_jacobians)
319  {
322  OnObservationJacobians(lm_idx, Hx_gt, Hy_gt);
323  if ((Hx - Hx_gt).array().abs().sum() >
324  KF_options.debug_verify_analytic_jacobians_threshold)
325  {
326  std::cerr << "[KalmanFilter] ERROR: User analytical "
327  "observation Hx Jacobians are wrong: \n"
328  << " Real Hx: \n"
329  << Hx << "\n Analytical Hx:\n"
330  << Hx_gt << "Diff:\n"
331  << Hx - Hx_gt << "\n";
333  "ERROR: User analytical observation Hx Jacobians "
334  "are wrong (More details dumped to cerr)")
335  }
336  if ((Hy - Hy_gt).array().abs().sum() >
337  KF_options.debug_verify_analytic_jacobians_threshold)
338  {
339  std::cerr << "[KalmanFilter] ERROR: User analytical "
340  "observation Hy Jacobians are wrong: \n"
341  << " Real Hy: \n"
342  << Hy << "\n Analytical Hx:\n"
343  << Hy_gt << "Diff:\n"
344  << Hy - Hy_gt << "\n";
346  "ERROR: User analytical observation Hy Jacobians "
347  "are wrong (More details dumped to cerr)")
348  }
349  }
350  }
351  }
352  m_timLogger.leave("KF:5.build Jacobians");
353 
354  m_timLogger.enter("KF:6.build S");
355 
356  // Compute S: S = H P ~H + R (R will be added below)
357  // exploiting the sparsity of H:
358  // Each block in S is:
359  // Sij =
360  // ------------------------------------------
361  S.setSize(N_pred * OBS_SIZE, N_pred * OBS_SIZE);
362 
363  if (FEAT_SIZE > 0)
364  { // SLAM-like problem:
365  const Eigen::Block<const typename KFMatrix::Base, VEH_SIZE,
366  VEH_SIZE>
367  Px(m_pkk, 0, 0); // Covariance of the vehicle pose
368 
369  for (size_t i = 0; i < N_pred; ++i)
370  {
371  const size_t lm_idx_i = predictLMidxs[i];
372  const Eigen::Block<const typename KFMatrix::Base, FEAT_SIZE,
373  VEH_SIZE>
374  Pxyi_t(
375  m_pkk, VEH_SIZE + lm_idx_i * FEAT_SIZE, 0); // Pxyi^t
376 
377  // Only do j>=i (upper triangle), since S is symmetric:
378  for (size_t j = i; j < N_pred; ++j)
379  {
380  const size_t lm_idx_j = predictLMidxs[j];
381  // Sij block:
382  Eigen::Block<typename KFMatrix::Base, OBS_SIZE, OBS_SIZE>
383  Sij(S, OBS_SIZE * i, OBS_SIZE * j);
384 
385  const Eigen::Block<const typename KFMatrix::Base, VEH_SIZE,
386  FEAT_SIZE>
387  Pxyj(m_pkk, 0, VEH_SIZE + lm_idx_j * FEAT_SIZE);
388  const Eigen::Block<const typename KFMatrix::Base, FEAT_SIZE,
389  FEAT_SIZE>
390  Pyiyj(
391  m_pkk, VEH_SIZE + lm_idx_i * FEAT_SIZE,
392  VEH_SIZE + lm_idx_j * FEAT_SIZE);
393 
394  Sij = Hxs[i] * Px * Hxs[j].transpose() +
395  Hys[i] * Pxyi_t * Hxs[j].transpose() +
396  Hxs[i] * Pxyj * Hys[j].transpose() +
397  Hys[i] * Pyiyj * Hys[j].transpose();
398 
399  // Copy transposed to the symmetric lower-triangular part:
400  if (i != j)
401  Eigen::Block<typename KFMatrix::Base, OBS_SIZE,
402  OBS_SIZE>(S, OBS_SIZE * j, OBS_SIZE * i) =
403  Sij.transpose();
404  }
405 
406  // Sum the "R" term to the diagonal blocks:
407  const size_t obs_idx_off = i * OBS_SIZE;
408  Eigen::Block<typename KFMatrix::Base, OBS_SIZE, OBS_SIZE>(
409  S, obs_idx_off, obs_idx_off) += R;
410  }
411  }
412  else
413  { // Not SLAM-like problem: simply S=H*Pkk*H^t + R
414  ASSERTDEB_(N_pred == 1)
415  ASSERTDEB_(S.getColCount() == OBS_SIZE)
416 
417  S = Hxs[0] * m_pkk * Hxs[0].transpose() + R;
418  }
419 
420  m_timLogger.leave("KF:6.build S");
421 
422  Z.clear(); // Each entry is one observation:
423 
424  m_timLogger.enter("KF:7.get obs & DA");
425 
426  // Get observations and do data-association:
427  OnGetObservationsAndDataAssociation(
428  Z, data_association, // Out
429  all_predictions, S, predictLMidxs, R // In
430  );
431  ASSERTDEB_(
432  data_association.size() == Z.size() ||
433  (data_association.empty() && FEAT_SIZE == 0));
434 
435  // Check if an observation hasn't been predicted in
436  // OnPreComputingPredictions() but has been actually
437  // observed. This may imply an error in the heuristic of
438  // OnPreComputingPredictions(), and forces us
439  // to rebuild the matrices
440  missing_predictions_to_add.clear();
441  if (FEAT_SIZE != 0)
442  {
443  for (size_t i = 0; i < data_association.size(); ++i)
444  {
445  if (data_association[i] < 0) continue;
446  const size_t assoc_idx_in_map =
447  static_cast<size_t>(data_association[i]);
448  const size_t assoc_idx_in_pred = mrpt::utils::find_in_vector(
449  assoc_idx_in_map, predictLMidxs);
450  if (assoc_idx_in_pred == std::string::npos)
451  missing_predictions_to_add.push_back(assoc_idx_in_map);
452  }
453  }
454 
455  first_new_pred = N_pred; // If we do another loop, start at the begin
456  // of new predictions
457 
458  } while (!missing_predictions_to_add.empty());
459 
460  const double tim_obs_DA = m_timLogger.leave("KF:7.get obs & DA");
461 
462  // =============================================================
463  // 7. UPDATE USING THE KALMAN GAIN
464  // =============================================================
465  // Update, only if there are observations!
466  if (!Z.empty())
467  {
468  m_timLogger.enter("KF:8.update stage");
469 
470  switch (KF_options.method)
471  {
472  // -----------------------
473  // FULL KF- METHOD
474  // -----------------------
475  case kfEKFNaive:
476  case kfIKFFull:
477  {
478  // Build the whole Jacobian dh_dx matrix
479  // ---------------------------------------------
480  // Keep only those whose DA is not -1
481  vector_int mapIndicesForKFUpdate(data_association.size());
482  mapIndicesForKFUpdate.resize(
484  mapIndicesForKFUpdate.begin(),
485  std::remove_copy_if(
486  data_association.begin(), data_association.end(),
487  mapIndicesForKFUpdate.begin(),
488  bind1st(equal_to<int>(), -1))));
489 
490  const size_t N_upd =
491  (FEAT_SIZE == 0) ? 1 : // Non-SLAM problems: Just one
492  // observation for the entire
493  // system.
494  mapIndicesForKFUpdate
495  .size(); // SLAM: # of observed known landmarks
496 
497  // Just one, or several update iterations??
498  const size_t nKF_iterations = (KF_options.method == kfEKFNaive)
499  ? 1
500  : KF_options.IKF_iterations;
501 
502  const KFVector xkk_0 = m_xkk;
503 
504  // For each IKF iteration (or 1 for EKF)
505  if (N_upd > 0) // Do not update if we have no observations!
506  {
507  for (size_t IKF_iteration = 0;
508  IKF_iteration < nKF_iterations; IKF_iteration++)
509  {
510  // Compute ytilde = OBS - PREDICTION
511  KFVector ytilde(OBS_SIZE * N_upd);
512  size_t ytilde_idx = 0;
513 
514  // TODO: Use a Matrix view of "dh_dx_full" instead of
515  // creating a copy into "dh_dx_full_obs"
516  dh_dx_full_obs.zeros(
517  N_upd * OBS_SIZE,
518  VEH_SIZE + FEAT_SIZE * N_map); // Init to zeros.
519  KFMatrix S_observed; // The KF "S" matrix: A
520  // re-ordered, subset, version of
521  // the prediction S:
522 
523  if (FEAT_SIZE != 0)
524  { // SLAM problems:
525  vector_size_t S_idxs;
526  S_idxs.reserve(OBS_SIZE * N_upd);
527 
528  // const size_t row_len = VEH_SIZE + FEAT_SIZE *
529  // N_map;
530 
531  for (size_t i = 0; i < data_association.size(); ++i)
532  {
533  if (data_association[i] < 0) continue;
534 
535  const size_t assoc_idx_in_map =
536  static_cast<size_t>(data_association[i]);
537  const size_t assoc_idx_in_pred =
539  assoc_idx_in_map, predictLMidxs);
540  ASSERTMSG_(
541  assoc_idx_in_pred != string::npos,
542  "OnPreComputingPredictions() didn't "
543  "recommend the prediction of a landmark "
544  "which has been actually observed!")
545  // TODO: In these cases, extend the prediction
546  // right now instead of launching an
547  // exception... or is this a bad idea??
548 
549  // Build the subset dh_dx_full_obs:
550  // dh_dx_full_obs.block(S_idxs.size()
551  //,0, OBS_SIZE, row_len)
552  // =
553  // dh_dx_full.block
554  //(assoc_idx_in_pred*OBS_SIZE,0, OBS_SIZE,
555  // row_len);
556 
557  Eigen::Block<typename KFMatrix::Base, OBS_SIZE,
558  VEH_SIZE>(
559  dh_dx_full_obs, S_idxs.size(), 0) =
560  Hxs[assoc_idx_in_pred];
561  Eigen::Block<typename KFMatrix::Base, OBS_SIZE,
562  FEAT_SIZE>(
563  dh_dx_full_obs, S_idxs.size(),
564  VEH_SIZE + assoc_idx_in_map * FEAT_SIZE) =
565  Hys[assoc_idx_in_pred];
566 
567  // S_idxs.size() is used as counter for
568  // "dh_dx_full_obs".
569  for (size_t k = 0; k < OBS_SIZE; k++)
570  S_idxs.push_back(
571  assoc_idx_in_pred * OBS_SIZE + k);
572 
573  // ytilde_i = Z[i] - all_predictions[i]
574  KFArray_OBS ytilde_i = Z[i];
575  OnSubstractObservationVectors(
576  ytilde_i,
577  all_predictions
578  [predictLMidxs[assoc_idx_in_pred]]);
579  for (size_t k = 0; k < OBS_SIZE; k++)
580  ytilde[ytilde_idx++] = ytilde_i[k];
581  }
582  // Extract the subset that is involved in this
583  // observation:
584  S.extractSubmatrixSymmetrical(S_idxs, S_observed);
585  }
586  else
587  { // Non-SLAM problems:
588  ASSERT_(
589  Z.size() == 1 && all_predictions.size() == 1)
590  ASSERT_(
591  dh_dx_full_obs.getRowCount() == OBS_SIZE &&
592  dh_dx_full_obs.getColCount() == VEH_SIZE)
593  ASSERT_(Hxs.size() == 1)
594 
595  dh_dx_full_obs = Hxs[0]; // Was: dh_dx_full
596  KFArray_OBS ytilde_i = Z[0];
597  OnSubstractObservationVectors(
598  ytilde_i, all_predictions[0]);
599  for (size_t k = 0; k < OBS_SIZE; k++)
600  ytilde[ytilde_idx++] = ytilde_i[k];
601  // Extract the subset that is involved in this
602  // observation:
603  S_observed = S;
604  }
605 
606  // Compute the full K matrix:
607  // ------------------------------
608  m_timLogger.enter("KF:8.update stage:1.FULLKF:build K");
609 
610  K.setSize(
611  m_pkk.getRowCount(), S_observed.getColCount());
612 
613  // K = m_pkk * (~dh_dx) * S.inv() );
614  K.multiply_ABt(m_pkk, dh_dx_full_obs);
615 
616  // Inverse of S_observed -> S_1
617  S_observed.inv(S_1);
618  K *= S_1;
619 
620  m_timLogger.leave("KF:8.update stage:1.FULLKF:build K");
621 
622  // Use the full K matrix to update the mean:
623  if (nKF_iterations == 1)
624  {
625  m_timLogger.enter(
626  "KF:8.update stage:2.FULLKF:update xkk");
627  m_xkk += K * ytilde;
628  m_timLogger.leave(
629  "KF:8.update stage:2.FULLKF:update xkk");
630  }
631  else
632  {
633  m_timLogger.enter(
634  "KF:8.update stage:2.FULLKF:iter.update xkk");
635 
636  KFVector HAx_column;
637  dh_dx_full_obs.multiply_Ab(
638  m_xkk - xkk_0, HAx_column);
639 
640  m_xkk = xkk_0;
641  K.multiply_Ab(
642  (ytilde - HAx_column), m_xkk,
643  true /* Accumulate in output */
644  );
645 
646  m_timLogger.leave(
647  "KF:8.update stage:2.FULLKF:iter.update xkk");
648  }
649 
650  // Update the covariance just at the end
651  // of iterations if we are in IKF, always in normal
652  // EKF.
653  if (IKF_iteration == (nKF_iterations - 1))
654  {
655  m_timLogger.enter(
656  "KF:8.update stage:3.FULLKF:update Pkk");
657 
658  // Use the full K matrix to update the covariance:
659  // m_pkk = (I - K*dh_dx ) * m_pkk;
660  // TODO: "Optimize this: sparsity!"
661 
662  // K * dh_dx_full_obs
663  aux_K_dh_dx.multiply(K, dh_dx_full_obs);
664 
665  // aux_K_dh_dx <-- I-aux_K_dh_dx
666  const size_t stat_len = aux_K_dh_dx.getColCount();
667  for (size_t r = 0; r < stat_len; r++)
668  {
669  for (size_t c = 0; c < stat_len; c++)
670  {
671  if (r == c)
672  aux_K_dh_dx.get_unsafe(r, c) =
673  -aux_K_dh_dx.get_unsafe(r, c) +
674  kftype(1);
675  else
676  aux_K_dh_dx.get_unsafe(r, c) =
677  -aux_K_dh_dx.get_unsafe(r, c);
678  }
679  }
680 
681  m_pkk.multiply_result_is_symmetric(
682  aux_K_dh_dx, m_pkk);
683 
684  m_timLogger.leave(
685  "KF:8.update stage:3.FULLKF:update Pkk");
686  }
687  } // end for each IKF iteration
688  }
689  }
690  break;
691 
692  // --------------------------------------------------------------------
693  // - EKF 'a la' Davison: One observation element at once
694  // --------------------------------------------------------------------
695  case kfEKFAlaDavison:
696  {
697  // For each observed landmark/whole system state:
698  for (size_t obsIdx = 0; obsIdx < Z.size(); obsIdx++)
699  {
700  // Known & mapped landmark?
701  bool doit;
702  size_t idxInTheFilter = 0;
703 
704  if (data_association.empty())
705  {
706  doit = true;
707  }
708  else
709  {
710  doit = data_association[obsIdx] >= 0;
711  if (doit) idxInTheFilter = data_association[obsIdx];
712  }
713 
714  if (doit)
715  {
716  m_timLogger.enter(
717  "KF:8.update stage:1.ScalarAtOnce.prepare");
718 
719  // Already mapped: OK
720  const size_t idx_off =
721  VEH_SIZE +
722  idxInTheFilter *
723  FEAT_SIZE; // The offset in m_xkk & Pkk.
724 
725  // Compute just the part of the Jacobian that we need
726  // using the current updated m_xkk:
727  vector_KFArray_OBS pred_obs;
728  OnObservationModel(
729  vector_size_t(1, idxInTheFilter), pred_obs);
730  ASSERTDEB_(pred_obs.size() == 1);
731 
732  // ytilde = observation - prediction
733  KFArray_OBS ytilde = Z[obsIdx];
734  OnSubstractObservationVectors(ytilde, pred_obs[0]);
735 
736  // Jacobians:
737  // dh_dx: already is (N_pred*OBS_SIZE) x (VEH_SIZE +
738  // FEAT_SIZE * N_pred )
739  // with N_pred = |predictLMidxs|
740 
741  const size_t i_idx_in_preds =
743  idxInTheFilter, predictLMidxs);
744  ASSERTMSG_(
745  i_idx_in_preds != string::npos,
746  "OnPreComputingPredictions() didn't recommend the "
747  "prediction of a landmark which has been actually "
748  "observed!")
749 
750  const KFMatrix_OxV& Hx = Hxs[i_idx_in_preds];
751  const KFMatrix_OxF& Hy = Hys[i_idx_in_preds];
752 
753  m_timLogger.leave(
754  "KF:8.update stage:1.ScalarAtOnce.prepare");
755 
756  // For each component of the observation:
757  for (size_t j = 0; j < OBS_SIZE; j++)
758  {
759  m_timLogger.enter(
760  "KF:8.update stage:2.ScalarAtOnce.update");
761 
762 // Compute the scalar S_i for each component j of the observation:
763 // Sij = dhij_dxv Pxx dhij_dxv^t + 2 * dhij_dyi Pyix dhij_dxv + dhij_dyi Pyiyi
764 // dhij_dyi^t + R
765 // ^^
766 // Hx:(O*LxSv)
767 // \----------------------/ \--------------------------/
768 // \------------------------/ \-/
769 // TERM 1 TERM 2 TERM 3 R
770 //
771 // O: Observation size (3)
772 // L: # landmarks
773 // Sv: Vehicle state size (6)
774 //
775 
776 #if defined(_DEBUG)
777  {
778  // This algorithm is applicable only if the
779  // scalar component of the sensor noise are
780  // INDEPENDENT:
781  for (size_t a = 0; a < OBS_SIZE; a++)
782  for (size_t b = 0; b < OBS_SIZE; b++)
783  if (a != b)
784  if (R(a, b) != 0)
786  "This KF algorithm assumes "
787  "independent noise "
788  "components in the "
789  "observation (matrix R). "
790  "Select another KF "
791  "algorithm.")
792  }
793 #endif
794  // R:
795  KFTYPE Sij = R.get_unsafe(j, j);
796 
797  // TERM 1:
798  for (size_t k = 0; k < VEH_SIZE; k++)
799  {
800  KFTYPE accum = 0;
801  for (size_t q = 0; q < VEH_SIZE; q++)
802  accum += Hx.get_unsafe(j, q) *
803  m_pkk.get_unsafe(q, k);
804  Sij += Hx.get_unsafe(j, k) * accum;
805  }
806 
807  // TERM 2:
808  KFTYPE term2 = 0;
809  for (size_t k = 0; k < VEH_SIZE; k++)
810  {
811  KFTYPE accum = 0;
812  for (size_t q = 0; q < FEAT_SIZE; q++)
813  accum += Hy.get_unsafe(j, q) *
814  m_pkk.get_unsafe(idx_off + q, k);
815  term2 += Hx.get_unsafe(j, k) * accum;
816  }
817  Sij += 2 * term2;
818 
819  // TERM 3:
820  for (size_t k = 0; k < FEAT_SIZE; k++)
821  {
822  KFTYPE accum = 0;
823  for (size_t q = 0; q < FEAT_SIZE; q++)
824  accum += Hy.get_unsafe(j, q) *
825  m_pkk.get_unsafe(
826  idx_off + q, idx_off + k);
827  Sij += Hy.get_unsafe(j, k) * accum;
828  }
829 
830  // Compute the Kalman gain "Kij" for this
831  // observation element:
832  // --> K = m_pkk * (~dh_dx) * S.inv() );
833  size_t N = m_pkk.getColCount();
834  vector<KFTYPE> Kij(N);
835 
836  for (size_t k = 0; k < N; k++)
837  {
838  KFTYPE K_tmp = 0;
839 
840  // dhi_dxv term
841  size_t q;
842  for (q = 0; q < VEH_SIZE; q++)
843  K_tmp += m_pkk.get_unsafe(k, q) *
844  Hx.get_unsafe(j, q);
845 
846  // dhi_dyi term
847  for (q = 0; q < FEAT_SIZE; q++)
848  K_tmp += m_pkk.get_unsafe(k, idx_off + q) *
849  Hy.get_unsafe(j, q);
850 
851  Kij[k] = K_tmp / Sij;
852  } // end for k
853 
854  // Update the state vector m_xkk:
855  // x' = x + Kij * ytilde(ij)
856  for (size_t k = 0; k < N; k++)
857  m_xkk[k] += Kij[k] * ytilde[j];
858 
859  // Update the covariance Pkk:
860  // P' = P - Kij * Sij * Kij^t
861  {
862  for (size_t k = 0; k < N; k++)
863  {
864  for (size_t q = k; q < N;
865  q++) // Half matrix
866  {
867  m_pkk(k, q) -= Sij * Kij[k] * Kij[q];
868  // It is symmetric
869  m_pkk(q, k) = m_pkk(k, q);
870  }
871 
872 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
873  if (m_pkk(k, k) < 0)
874  {
875  m_pkk.saveToTextFile("Pkk_err.txt");
877  Kij, "Kij.txt");
878  ASSERT_(m_pkk(k, k) > 0)
879  }
880 #endif
881  }
882  }
883 
884  m_timLogger.leave(
885  "KF:8.update stage:2.ScalarAtOnce.update");
886  } // end for j
887  } // end if mapped
888  } // end for each observed LM.
889  }
890  break;
891 
892  // --------------------------------------------------------------------
893  // - IKF method, processing each observation scalar secuentially:
894  // --------------------------------------------------------------------
895  case kfIKF: // TODO !!
896  {
897  THROW_EXCEPTION("IKF scalar by scalar not implemented yet.");
898 #if 0
899  KFMatrix h,Hx,Hy;
900 
901  // Just one, or several update iterations??
902  size_t nKF_iterations = KF_options.IKF_iterations;
903 
904  // To avoid wasting time, if we are doing 1 iteration only, do not reserve memory for this matrix:
905  KFMatrix *saved_Pkk=nullptr;
906  if (nKF_iterations>1)
907  {
908  // Create a copy of Pkk for later restoring it at the beginning of each iteration:
909  saved_Pkk = new KFMatrix( m_pkk );
910  }
911 
912  KFVector xkk_0 = m_xkk; // First state vector at the beginning of IKF
913  KFVector xkk_next_iter = m_xkk; // for IKF only
914 
915  // For each IKF iteration (or 1 for EKF)
916  for (size_t IKF_iteration=0;IKF_iteration<nKF_iterations;IKF_iteration++)
917  {
918  // Restore initial covariance matrix.
919  // The updated mean is stored in m_xkk and is improved with each estimation,
920  // and so do the Jacobians which use that improving mean.
921  if (IKF_iteration>0)
922  {
923  m_pkk = *saved_Pkk;
924  xkk_next_iter = xkk_0;
925  }
926 
927  // For each observed landmark/whole system state:
928  for (size_t obsIdx=0;obsIdx<Z.getRowCount();obsIdx++)
929  {
930  // Known & mapped landmark?
931  bool doit;
932  size_t idxInTheFilter=0;
933 
934  if (data_association.empty())
935  {
936  doit = true;
937  }
938  else
939  {
940  doit = data_association[obsIdx] >= 0;
941  if (doit)
942  idxInTheFilter = data_association[obsIdx];
943  }
944 
945  if ( doit )
946  {
947  // Already mapped: OK
948  const size_t idx_off = VEH_SIZE + idxInTheFilter *FEAT_SIZE; // The offset in m_xkk & Pkk.
949  const size_t R_row_offset = obsIdx*OBS_SIZE; // Offset row in R
950 
951  // Compute just the part of the Jacobian that we need using the current updated m_xkk:
952  KFVector ytilde; // Diff. observation - prediction
953  OnObservationModelAndJacobians(
954  Z,
955  data_association,
956  false, // Only a partial Jacobian
957  (int)obsIdx, // Which row from Z
958  ytilde,
959  Hx,
960  Hy );
961 
962  ASSERTDEB_(ytilde.size() == OBS_SIZE )
963  ASSERTDEB_(Hx.getRowCount() == OBS_SIZE )
964  ASSERTDEB_(Hx.getColCount() == VEH_SIZE )
965 
966  if (FEAT_SIZE>0)
967  {
968  ASSERTDEB_(Hy.getRowCount() == OBS_SIZE )
969  ASSERTDEB_(Hy.getColCount() == FEAT_SIZE )
970  }
971 
972  // Compute the OxO matrix S_i for each observation:
973  // Si = TERM1 + TERM2 + TERM2^t + TERM3 + R
974  //
975  // TERM1: dhi_dxv Pxx dhi_dxv^t
976  // ^^
977  // Hx:(OxV)
978  //
979  // TERM2: dhi_dyi Pyix dhi_dxv
980  // ^^
981  // Hy:(OxF)
982  //
983  // TERM3: dhi_dyi Pyiyi dhi_dyi^t
984  //
985  // i: obsIdx
986  // O: Observation size
987  // F: Feature size
988  // V: Vehicle state size
989  //
990 
991  // R:
992  KFMatrix Si(OBS_SIZE,OBS_SIZE);
993  R.extractMatrix(R_row_offset,0, Si);
994 
995  size_t k;
996  KFMatrix term(OBS_SIZE,OBS_SIZE);
997 
998  // TERM1: dhi_dxv Pxx dhi_dxv^t
999  Hx.multiply_HCHt(
1000  m_pkk, // The cov. matrix
1001  Si, // The output
1002  true, // Yes, submatrix of m_pkk only
1003  0, // Offset in m_pkk
1004  true // Yes, accum results in output.
1005  );
1006 
1007  // TERM2: dhi_dyi Pyix dhi_dxv
1008  // + its transpose:
1009  KFMatrix Pyix( FEAT_SIZE, VEH_SIZE );
1010  m_pkk.extractMatrix(idx_off,0, Pyix); // Extract cross cov. to Pyix
1011 
1012  term.multiply_ABCt( Hy, Pyix, Hx ); //term = Hy * Pyix * ~Hx;
1013  Si.add_AAt( term ); // Si += A + ~A
1014 
1015  // TERM3: dhi_dyi Pyiyi dhi_dyi^t
1016  Hy.multiply_HCHt(
1017  m_pkk, // The cov. matrix
1018  Si, // The output
1019  true, // Yes, submatrix of m_pkk only
1020  idx_off, // Offset in m_pkk
1021  true // Yes, accum results in output.
1022  );
1023 
1024  // Compute the inverse of Si:
1025  KFMatrix Si_1(OBS_SIZE,OBS_SIZE);
1026 
1027  // Compute the Kalman gain "Ki" for this i'th observation:
1028  // --> Ki = m_pkk * (~dh_dx) * S.inv();
1029  size_t N = m_pkk.getColCount();
1030 
1031  KFMatrix Ki( N, OBS_SIZE );
1032 
1033  for (k=0;k<N;k++) // for each row of K
1034  {
1035  size_t q;
1036 
1037  for (size_t c=0;c<OBS_SIZE;c++) // for each column of K
1038  {
1039  KFTYPE K_tmp = 0;
1040 
1041  // dhi_dxv term
1042  for (q=0;q<VEH_SIZE;q++)
1043  K_tmp+= m_pkk.get_unsafe(k,q) * Hx.get_unsafe(c,q);
1044 
1045  // dhi_dyi term
1046  for (q=0;q<FEAT_SIZE;q++)
1047  K_tmp+= m_pkk.get_unsafe(k,idx_off+q) * Hy.get_unsafe(c,q);
1048 
1049  Ki.set_unsafe(k,c, K_tmp);
1050  } // end for c
1051  } // end for k
1052 
1053  Ki.multiply(Ki, Si.inv() ); // K = (...) * S^-1
1054 
1055 
1056  // Update the state vector m_xkk:
1057  if (nKF_iterations==1)
1058  {
1059  // EKF:
1060  // x' = x + Kij * ytilde(ij)
1061  for (k=0;k<N;k++)
1062  for (size_t q=0;q<OBS_SIZE;q++)
1063  m_xkk[k] += Ki.get_unsafe(k,q) * ytilde[q];
1064  }
1065  else
1066  {
1067  // IKF:
1068  Eigen::Matrix<KFTYPE,Eigen::Dynamic,1> HAx(OBS_SIZE);
1069  size_t o,q;
1070  // HAx = H*(x0-xi)
1071  for (o=0;o<OBS_SIZE;o++)
1072  {
1073  KFTYPE tmp = 0;
1074  for (q=0;q<VEH_SIZE;q++)
1075  tmp += Hx.get_unsafe(o,q) * (xkk_0[q] - m_xkk[q]);
1076 
1077  for (q=0;q<FEAT_SIZE;q++)
1078  tmp += Hy.get_unsafe(o,q) * (xkk_0[idx_off+q] - m_xkk[idx_off+q]);
1079 
1080  HAx[o] = tmp;
1081  }
1082 
1083  // Compute only once (ytilde[j] - HAx)
1084  for (o=0;o<OBS_SIZE;o++)
1085  HAx[o] = ytilde[o] - HAx[o];
1086 
1087  // x' = x_0 + Kij * ( ytilde(ij) - H*(x0-xi)) -> xi: i=this iteration
1088  // xkk_next_iter is initialized to xkk_0 at each IKF iteration.
1089  for (k=0;k<N;k++)
1090  {
1091  KFTYPE tmp = xkk_next_iter[k];
1092  //m_xkk[k] = xkk_0[k] + Kij[k] * (ytilde[j] - HAx );
1093  for (o=0;o<OBS_SIZE;o++)
1094  tmp += Ki.get_unsafe(k,o) * HAx[o];
1095 
1096  xkk_next_iter[k] = tmp;
1097  }
1098  }
1099 
1100  // Update the covariance Pkk:
1101  // P' = P - Kij * Sij * Kij^t
1102  //if (IKF_iteration==(nKF_iterations-1)) // Just for the last IKF iteration
1103  {
1104  // m_pkk -= Ki * Si * ~Ki;
1105  Ki.multiplyByMatrixAndByTransposeNonSymmetric(
1106  Si,
1107  m_pkk, // Output
1108  true, // Accumulate
1109  true); // Substract instead of sum.
1110 
1111  m_pkk.force_symmetry();
1112 
1113  /* for (k=0;k<N;k++)
1114  {
1115  for (size_t q=k;q<N;q++) // Half matrix
1116  {
1117  m_pkk(k,q) -= Sij * Kij[k] * Kij[q];
1118  // It is symmetric
1119  m_pkk(q,k) = m_pkk(k,q);
1120  }
1121 
1122  #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1123  if (m_pkk(k,k)<0)
1124  {
1125  m_pkk.saveToTextFile("Pkk_err.txt");
1126  mrpt::system::vectorToTextFile(Kij,"Kij.txt");
1127  ASSERT_(m_pkk(k,k)>0)
1128  }
1129  #endif
1130  }
1131  */
1132  }
1133 
1134  } // end if doit
1135 
1136  } // end for each observed LM.
1137 
1138  // Now, save the new iterated mean:
1139  if (nKF_iterations>1)
1140  {
1141 #if 0
1142  cout << "IKF iter: " << IKF_iteration << " -> " << xkk_next_iter << endl;
1143 #endif
1144  m_xkk = xkk_next_iter;
1145  }
1146 
1147  } // end for each IKF_iteration
1148 
1149  // Copy of pkk not required anymore:
1150  if (saved_Pkk) delete saved_Pkk;
1151 
1152 #endif
1153  }
1154  break;
1155 
1156  default:
1157  THROW_EXCEPTION("Invalid value of options.KF_method");
1158  } // end switch method
1159  }
1160 
1161  const double tim_update = m_timLogger.leave("KF:8.update stage");
1162 
1163  m_timLogger.enter("KF:9.OnNormalizeStateVector");
1164  OnNormalizeStateVector();
1165  m_timLogger.leave("KF:9.OnNormalizeStateVector");
1166 
1167  // =============================================================
1168  // 8. INTRODUCE NEW LANDMARKS
1169  // =============================================================
1170  if (!data_association.empty())
1171  {
1172  m_timLogger.enter("KF:A.add new landmarks");
1173  detail::addNewLandmarks(*this, Z, data_association, R);
1174  m_timLogger.leave("KF:A.add new landmarks");
1175  } // end if data_association!=empty
1176 
1177  // Post iteration user code:
1178  m_timLogger.enter("KF:B.OnPostIteration");
1179  OnPostIteration();
1180  m_timLogger.leave("KF:B.OnPostIteration");
1181 
1182  m_timLogger.leave("KF:complete_step");
1183 
1185  mrpt::format(
1186  "[KF] %u LMs | Pr: %.2fms | Pr.Obs: %.2fms | Obs.DA: %.2fms | Upd: "
1187  "%.2fms\n",
1188  static_cast<unsigned int>(getNumberOfLandmarksInTheMap()),
1189  1e3 * tim_pred, 1e3 * tim_pred_obs, 1e3 * tim_obs_DA,
1190  1e3 * tim_update));
1191  MRPT_END
1192 } // End of "runOneKalmanIteration"
1193 
1194 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1195  typename KFTYPE>
1198  const KFArray_VEH& x, const std::pair<KFCLASS*, KFArray_ACT>& dat,
1199  KFArray_VEH& out_x)
1200 {
1201  bool dumm;
1202  out_x = x;
1203  dat.first->OnTransitionModel(dat.second, out_x, dumm);
1204 }
1205 
1206 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1207  typename KFTYPE>
1210  const KFArray_VEH& x, const std::pair<KFCLASS*, size_t>& dat,
1211  KFArray_OBS& out_x)
1212 {
1213  vector_size_t idxs_to_predict(1, dat.second);
1214  vector_KFArray_OBS prediction;
1215  // Overwrite (temporarily!) the affected part of the state vector:
1216  ::memcpy(&dat.first->m_xkk[0], &x[0], sizeof(x[0]) * VEH_SIZE);
1217  dat.first->OnObservationModel(idxs_to_predict, prediction);
1218  ASSERTDEB_(prediction.size() == 1);
1219  out_x = prediction[0];
1220 }
1221 
1222 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1223  typename KFTYPE>
1226  const KFArray_FEAT& x, const std::pair<KFCLASS*, size_t>& dat,
1227  KFArray_OBS& out_x)
1228 {
1229  vector_size_t idxs_to_predict(1, dat.second);
1230  vector_KFArray_OBS prediction;
1231  const size_t lm_idx_in_statevector = VEH_SIZE + FEAT_SIZE * dat.second;
1232  // Overwrite (temporarily!) the affected part of the state vector:
1233  ::memcpy(
1234  &dat.first->m_xkk[lm_idx_in_statevector], &x[0],
1235  sizeof(x[0]) * FEAT_SIZE);
1236  dat.first->OnObservationModel(idxs_to_predict, prediction);
1237  ASSERTDEB_(prediction.size() == 1);
1238  out_x = prediction[0];
1239 }
1240 
1241 namespace detail
1242 {
1243 // generic version for SLAM. There is a speciation below for NON-SLAM problems.
1244 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1245  typename KFTYPE>
1248  const typename CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE,
1249  KFTYPE>::vector_KFArray_OBS& Z,
1250  const vector_int& data_association,
1251  const typename CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE,
1252  KFTYPE>::KFMatrix_OxO& R)
1253 {
1254  typedef CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE,
1255  KFTYPE>
1256  KF;
1257 
1258  for (size_t idxObs = 0; idxObs < Z.size(); idxObs++)
1259  {
1260  // Is already in the map?
1261  if (data_association[idxObs] < 0) // Not in the map yet!
1262  {
1263  obj.getProfiler().enter("KF:9.create new LMs");
1264  // Add it:
1265 
1266  // Append to map of IDs <-> position in the state vector:
1267  ASSERTDEB_(FEAT_SIZE > 0)
1268  ASSERTDEB_(
1269  0 == ((obj.internal_getXkk().size() - VEH_SIZE) %
1270  FEAT_SIZE)) // Sanity test
1271 
1272  const size_t newIndexInMap =
1273  (obj.internal_getXkk().size() - VEH_SIZE) / FEAT_SIZE;
1274 
1275  // Inverse sensor model:
1276  typename KF::KFArray_FEAT yn;
1277  typename KF::KFMatrix_FxV dyn_dxv;
1278  typename KF::KFMatrix_FxO dyn_dhn;
1279  typename KF::KFMatrix_FxF dyn_dhn_R_dyn_dhnT;
1280  bool use_dyn_dhn_jacobian = true;
1281 
1282  // Compute the inv. sensor model and its Jacobians:
1283  obj.OnInverseObservationModel(
1284  Z[idxObs], yn, dyn_dxv, dyn_dhn, dyn_dhn_R_dyn_dhnT,
1285  use_dyn_dhn_jacobian);
1286 
1287  // And let the application do any special handling of adding a new
1288  // feature to the map:
1289  obj.OnNewLandmarkAddedToMap(idxObs, newIndexInMap);
1290 
1291  ASSERTDEB_(yn.size() == FEAT_SIZE)
1292 
1293  // Append to m_xkk:
1294  size_t q;
1295  size_t idx = obj.internal_getXkk().size();
1296  obj.internal_getXkk().conservativeResize(
1297  obj.internal_getXkk().size() + FEAT_SIZE);
1298 
1299  for (q = 0; q < FEAT_SIZE; q++)
1300  obj.internal_getXkk()[idx + q] = yn[q];
1301 
1302  // --------------------
1303  // Append to Pkk:
1304  // --------------------
1305  ASSERTDEB_(
1306  obj.internal_getPkk().getColCount() == idx &&
1307  obj.internal_getPkk().getRowCount() == idx);
1308 
1309  obj.internal_getPkk().setSize(idx + FEAT_SIZE, idx + FEAT_SIZE);
1310 
1311  // Fill the Pxyn term:
1312  // --------------------
1313  typename KF::KFMatrix_VxV Pxx;
1314  obj.internal_getPkk().extractMatrix(0, 0, Pxx);
1315  typename KF::KFMatrix_FxV Pxyn; // Pxyn = dyn_dxv * Pxx
1316  Pxyn.multiply(dyn_dxv, Pxx);
1317 
1318  obj.internal_getPkk().insertMatrix(idx, 0, Pxyn);
1319  obj.internal_getPkk().insertMatrixTranspose(0, idx, Pxyn);
1320 
1321  // Fill the Pyiyn terms:
1322  // --------------------
1323  const size_t nLMs =
1324  (idx - VEH_SIZE) / FEAT_SIZE; // Number of previous landmarks:
1325  for (q = 0; q < nLMs; q++)
1326  {
1327  typename KF::KFMatrix_VxF P_x_yq(
1329  obj.internal_getPkk().extractMatrix(
1330  0, VEH_SIZE + q * FEAT_SIZE, P_x_yq);
1331 
1332  typename KF::KFMatrix_FxF P_cross(
1334  P_cross.multiply(dyn_dxv, P_x_yq);
1335 
1336  obj.internal_getPkk().insertMatrix(
1337  idx, VEH_SIZE + q * FEAT_SIZE, P_cross);
1338  obj.internal_getPkk().insertMatrixTranspose(
1339  VEH_SIZE + q * FEAT_SIZE, idx, P_cross);
1340  } // end each previous LM(q)
1341 
1342  // Fill the Pynyn term:
1343  // P_yn_yn = (dyn_dxv * Pxx * ~dyn_dxv) + (dyn_dhn * R *
1344  // ~dyn_dhn);
1345  // --------------------
1346  typename KF::KFMatrix_FxF P_yn_yn(mrpt::math::UNINITIALIZED_MATRIX);
1347  dyn_dxv.multiply_HCHt(Pxx, P_yn_yn);
1348  if (use_dyn_dhn_jacobian)
1349  dyn_dhn.multiply_HCHt(
1350  R, P_yn_yn, true); // Accumulate in P_yn_yn
1351  else
1352  P_yn_yn += dyn_dhn_R_dyn_dhnT;
1353 
1354  obj.internal_getPkk().insertMatrix(idx, idx, P_yn_yn);
1355 
1356  obj.getProfiler().leave("KF:9.create new LMs");
1357  }
1358  }
1359 }
1360 
1361 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
1363  CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, 0 /* FEAT_SIZE=0 */, ACT_SIZE,
1364  KFTYPE>& obj,
1365  const typename CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, 0 /* FEAT_SIZE=0 */,
1366  ACT_SIZE, KFTYPE>::vector_KFArray_OBS&
1367  Z,
1368  const vector_int& data_association,
1369  const typename CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, 0 /* FEAT_SIZE=0 */,
1370  ACT_SIZE, KFTYPE>::KFMatrix_OxO& R)
1371 {
1372  // Do nothing: this is NOT a SLAM problem.
1373 }
1374 
1375 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1376  typename KFTYPE>
1379  obj)
1380 {
1381  return (obj.getStateVectorLength() - VEH_SIZE) / FEAT_SIZE;
1382 }
1383 // Specialization for FEAT_SIZE=0
1384 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
1386  const CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, 0 /*FEAT_SIZE*/, ACT_SIZE,
1387  KFTYPE>& obj)
1388 {
1389  return 0;
1390 }
1391 
1392 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE,
1393  typename KFTYPE>
1394 inline bool isMapEmpty(
1396  obj)
1397 {
1398  return (obj.getStateVectorLength() == VEH_SIZE);
1399 }
1400 // Specialization for FEAT_SIZE=0
1401 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
1402 inline bool isMapEmpty(
1403  const CKalmanFilterCapable<VEH_SIZE, OBS_SIZE, 0 /*FEAT_SIZE*/, ACT_SIZE,
1404  KFTYPE>& obj)
1405 {
1406  return true;
1407 }
1408 } // end namespace "detail"
1409 } // ns
1410 } // ns
1411 
1412 #endif
mrpt::utils::CTimeLogger & getProfiler()
Eigen::Matrix< double, Eigen::Dynamic, 1 > KFVector
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::AutoAlign|Eigen::RowMajor > Base
#define THROW_EXCEPTION(msg)
GLdouble GLdouble GLdouble GLdouble q
Definition: glext.h:3721
static void KF_aux_estimate_obs_Hx_jacobian(const KFArray_VEH &x, const std::pair< KFCLASS *, size_t > &dat, KFArray_OBS &out_x)
CArrayNumeric is an array for numeric types supporting several mathematical operations (actually...
Definition: CArrayNumeric.h:26
STL namespace.
GLsizei GLsizei GLuint * obj
Definition: glext.h:4070
void estimateJacobian(const VECTORLIKE &x, const std::function< void(const VECTORLIKE &x, const USERPARAM &y, VECTORLIKE3 &out)> &functor, const VECTORLIKE2 &increments, const USERPARAM &userParam, MATRIXLIKE &out_Jacobian)
Estimate the Jacobian of a multi-dimensional function around a point "x", using finite differences of...
Definition: num_jacobian.h:31
A numeric matrix of compile-time fixed size.
#define MRPT_END
const GLubyte * c
Definition: glext.h:6313
mrpt::aligned_containers< KFArray_OBS >::vector_t vector_KFArray_OBS
CONTAINER::Scalar sum(const CONTAINER &v)
Computes the sum of all the elements.
Virtual base for Kalman Filter (EKF,IEKF,UKF) implementations.
size_t getNumberOfLandmarksInMap(const CKalmanFilterCapable< VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE, KFTYPE > &obj)
GLubyte GLubyte b
Definition: glext.h:6279
static void KF_aux_estimate_trans_jacobian(const KFArray_VEH &x, const std::pair< KFCLASS *, KFArray_ACT > &dat, KFArray_VEH &out_x)
Auxiliary functions for Jacobian numeric estimation.
#define MRPT_LOG_WARN_STREAM(__CONTENTS)
#define MRPT_LOG_DEBUG(_STRING)
void addNewLandmarks(CKalmanFilterCapable< VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE, KFTYPE > &obj, const typename CKalmanFilterCapable< VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE, KFTYPE >::vector_KFArray_OBS &Z, const vector_int &data_association, const typename CKalmanFilterCapable< VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE, KFTYPE >::KFMatrix_OxO &R)
double kftype
The numeric type used in the Kalman Filter (default=double)
GLsizei GLboolean transpose
Definition: glext.h:4133
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
#define ASSERTDEB_(f)
Defines an assertion mechanism - only when compiled in debug.
GLdouble GLdouble GLdouble r
Definition: glext.h:3705
const float R
bool isMapEmpty(const CKalmanFilterCapable< VEH_SIZE, OBS_SIZE, FEAT_SIZE, ACT_SIZE, KFTYPE > &obj)
#define ASSERT_(f)
std::vector< size_t > vector_size_t
Definition: types_simple.h:26
std::vector< int32_t > vector_int
Definition: types_simple.h:24
static void KF_aux_estimate_obs_Hy_jacobian(const KFArray_FEAT &x, const std::pair< KFCLASS *, size_t > &dat, KFArray_OBS &out_x)
GLsizeiptr size
Definition: glext.h:3923
GLenum GLint x
Definition: glext.h:3538
bool vectorToTextFile(const std::vector< float > &vec, const std::string &fileName, bool append=false, bool byRows=false)
A useful function for debugging, which saves a std::vector into a text file (compat.
size_t find_in_vector(const T &value, const CONTAINER &vect)
Returns the index of the value "T" in the container "vect" (std::vector,std::deque,etc), or string::npos if not found.
void enter(const char *func_name)
Start of a named section.
Definition: CTimeLogger.h:117
#define ASSERTMSG_(f, __ERROR_MSG)
GLubyte GLubyte GLubyte a
Definition: glext.h:6279
double distance(const TPoint2D &p1, const TPoint2D &p2)
Gets the distance between two points in a 2D space.
Definition: geometry.cpp:1885
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
Definition: os.cpp:355



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019