MRPT  1.9.9
CMultiMetricMapPDF_RBPF.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, 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 
10 #include "slam-precomp.h" // Precompiled headers
11 
12 #include <mrpt/random.h>
13 #include <mrpt/math/utils.h>
14 #include <mrpt/system/CTicTac.h>
15 #include <mrpt/io/CFileStream.h>
16 
26 
28 
29 using namespace mrpt;
30 using namespace mrpt::bayes;
31 using namespace mrpt::math;
32 using namespace mrpt::slam;
33 using namespace mrpt::obs;
34 using namespace mrpt::maps;
35 using namespace mrpt::poses;
36 using namespace mrpt::random;
37 using namespace std;
38 
39 namespace mrpt::slam
40 {
41 /** Fills out a "TPoseBin2D" variable, given a path hypotesis and (if not set to
42  * nullptr) a new pose appended at the end, using the KLD params in "options".
43  */
44 template <>
46  detail::TPoseBin2D& outBin, const TKLDParams& opts,
47  const mrpt::maps::CRBPFParticleData* currentParticleValue,
48  const TPose3D* newPoseToBeInserted)
49 {
50  // 2D pose approx: Use the latest pose only:
51  if (newPoseToBeInserted)
52  {
53  outBin.x = round(newPoseToBeInserted->x / opts.KLD_binSize_XY);
54  outBin.y = round(newPoseToBeInserted->y / opts.KLD_binSize_XY);
55  outBin.phi = round(newPoseToBeInserted->yaw / opts.KLD_binSize_PHI);
56  }
57  else
58  {
59  ASSERT_(
60  currentParticleValue && !currentParticleValue->robotPath.empty());
61  const TPose3D& p = *currentParticleValue->robotPath.rbegin();
62  outBin.x = round(p.x / opts.KLD_binSize_XY);
63  outBin.y = round(p.y / opts.KLD_binSize_XY);
64  outBin.phi = round(p.yaw / opts.KLD_binSize_PHI);
65  }
66 }
67 
68 /** Fills out a "TPathBin2D" variable, given a path hypotesis and (if not set to
69  * nullptr) a new pose appended at the end, using the KLD params in "options".
70  */
71 template <>
73  detail::TPathBin2D& outBin, const TKLDParams& opts,
74  const mrpt::maps::CRBPFParticleData* currentParticleValue,
75  const TPose3D* newPoseToBeInserted)
76 {
77  const size_t lenBinPath = (currentParticleValue != nullptr)
78  ? currentParticleValue->robotPath.size()
79  : 0;
80 
81  // Set the output bin dimensionality:
82  outBin.bins.resize(lenBinPath + (newPoseToBeInserted != nullptr ? 1 : 0));
83 
84  // Is a path provided??
85  if (currentParticleValue != nullptr)
86  for (size_t i = 0; i < lenBinPath; ++i) // Fill the bin data:
87  {
88  outBin.bins[i].x = round(
89  currentParticleValue->robotPath[i].x / opts.KLD_binSize_XY);
90  outBin.bins[i].y = round(
91  currentParticleValue->robotPath[i].y / opts.KLD_binSize_XY);
92  outBin.bins[i].phi = round(
93  currentParticleValue->robotPath[i].yaw / opts.KLD_binSize_PHI);
94  }
95 
96  // Is a newPose provided??
97  if (newPoseToBeInserted != nullptr)
98  {
99  // And append the last pose: the new one:
100  outBin.bins[lenBinPath].x =
101  round(newPoseToBeInserted->x / opts.KLD_binSize_XY);
102  outBin.bins[lenBinPath].y =
103  round(newPoseToBeInserted->y / opts.KLD_binSize_XY);
104  outBin.bins[lenBinPath].phi =
105  round(newPoseToBeInserted->yaw / opts.KLD_binSize_PHI);
106  }
107 }
108 }
109 
110 // Include this AFTER specializations:
112 
113 /** Auxiliary for optimal sampling in RO-SLAM */
115 {
117  : sensorLocationOnRobot(),
118  sensedDistance(0),
119  beaconID(INVALID_BEACON_ID),
120  nGaussiansInMap(0)
121  {
122  }
123 
127  size_t
128  nGaussiansInMap; // Number of Gaussian modes in the map representation
129 
130  /** Auxiliary for optimal sampling in RO-SLAM */
131  static bool cmp_Asc(const TAuxRangeMeasInfo& a, const TAuxRangeMeasInfo& b)
132  {
133  return a.nGaussiansInMap < b.nGaussiansInMap;
134  }
135 };
136 
137 /*----------------------------------------------------------------------------------
138  prediction_and_update_pfAuxiliaryPFOptimal
139 
140  See paper reference in "PF_SLAM_implementation_pfAuxiliaryPFOptimal"
141  ----------------------------------------------------------------------------------*/
142 void CMultiMetricMapPDF::prediction_and_update_pfAuxiliaryPFOptimal(
143  const mrpt::obs::CActionCollection* actions,
144  const mrpt::obs::CSensoryFrame* sf,
146 {
147  MRPT_START
148 
149  PF_SLAM_implementation_pfAuxiliaryPFOptimal<mrpt::slam::detail::TPoseBin2D>(
150  actions, sf, PF_options, options.KLD_params);
151 
152  MRPT_END
153 }
154 
155 /*----------------------------------------------------------------------------------
156  PF_SLAM_implementation_pfAuxiliaryPFStandard
157  ----------------------------------------------------------------------------------*/
158 void CMultiMetricMapPDF::prediction_and_update_pfAuxiliaryPFStandard(
159  const mrpt::obs::CActionCollection* actions,
160  const mrpt::obs::CSensoryFrame* sf,
162 {
163  MRPT_START
164 
165  PF_SLAM_implementation_pfAuxiliaryPFStandard<
167  actions, sf, PF_options, options.KLD_params);
168 
169  MRPT_END
170 }
171 
172 /*----------------------------------------------------------------------------------
173  prediction_and_update_pfOptimalProposal
174 
175 For grid-maps:
176 ==============
177  Approximation by Grissetti et al: Use scan matching to approximate
178  the observation model by a Gaussian:
179  See: "Improved Grid-based SLAM with Rao-Blackwellized PF by Adaptive Proposals
180  and Selective Resampling" (G. Grisetti, C. Stachniss, W. Burgard)
181 
182 For beacon maps:
183 ===============
184  (JLBC: Method under development)
185 
186  ----------------------------------------------------------------------------------*/
187 void CMultiMetricMapPDF::prediction_and_update_pfOptimalProposal(
188  const mrpt::obs::CActionCollection* actions,
189  const mrpt::obs::CSensoryFrame* sf,
191 {
192  MRPT_START
193 
194  // ----------------------------------------------------------------------
195  // PREDICTION STAGE
196  // ----------------------------------------------------------------------
197  CVectorDouble rndSamples;
198  size_t M = m_particles.size();
199  bool updateStageAlreadyDone = false;
200  CPose3D initialPose, incrPose, finalPose;
201 
202  // ICP used if "pfOptimalProposal_mapSelection" = 0 or 1
203  CICP icp(
204  options.icp_params); // Set our ICP params instead of default ones.
205  CICP::TReturnInfo icpInfo;
206 
208 
209  ASSERT_(sf != nullptr);
210  // Find a robot movement estimation:
211  CPose3D motionModelMeanIncr; // The mean motion increment:
212  CPoseRandomSampler robotActionSampler;
213  {
214  CActionRobotMovement2D::Ptr robotMovement2D =
215  actions->getBestMovementEstimation();
216 
217  // If there is no 2D action, look for a 3D action:
218  if (robotMovement2D)
219  {
220  robotActionSampler.setPosePDF(
221  robotMovement2D->poseChange.get_ptr());
222  motionModelMeanIncr =
223  mrpt::poses::CPose3D(robotMovement2D->poseChange->getMeanVal());
224  }
225  else
226  {
227  CActionRobotMovement3D::Ptr robotMovement3D =
229  if (robotMovement3D)
230  {
231  robotActionSampler.setPosePDF(robotMovement3D->poseChange);
232  robotMovement3D->poseChange.getMean(motionModelMeanIncr);
233  }
234  else
235  {
236  motionModelMeanIncr.setFromValues(0, 0, 0);
237  }
238  }
239  }
240 
241  // Average map will need to be updated after this:
242  averageMapIsUpdated = false;
243 
244  // --------------------------------------------------------------------------------------
245  // Prediction:
246  //
247  // Compute a new mean and covariance by sampling around the mean of the
248  // input "action"
249  // --------------------------------------------------------------------------------------
250  printf(" 1) Prediction...");
251  M = m_particles.size();
252 
253  // To be computed as an average from all m_particles:
254  size_t particleWithHighestW = 0;
255  for (size_t i = 0; i < M; i++)
256  if (getW(i) > getW(particleWithHighestW)) particleWithHighestW = i;
257 
258  // The paths MUST already contain the starting location for each particle:
259  ASSERT_(!m_particles[0].d->robotPath.empty());
260  // Build the local map of points for ICP:
261  CSimplePointsMap localMapPoints;
262  CLandmarksMap localMapLandmarks;
263  bool built_map_points = false;
264  bool built_map_lms = false;
265 
266  // Update particle poses:
267  size_t i;
268  for (i = 0, partIt = m_particles.begin(); partIt != m_particles.end();
269  partIt++, i++)
270  {
271  double extra_log_lik = 0; // Used for the optimal_PF with ICP
272 
273  // Set initial robot pose estimation for this particle:
274  const CPose3D ith_last_pose = CPose3D(
275  *partIt->d->robotPath.rbegin()); // The last robot pose in the path
276 
277  CPose3D initialPoseEstimation = ith_last_pose + motionModelMeanIncr;
278 
279  // Use ICP with the map associated to particle?
280  if (options.pfOptimalProposal_mapSelection == 0 ||
281  options.pfOptimalProposal_mapSelection == 1 ||
282  options.pfOptimalProposal_mapSelection == 3)
283  {
284  CPosePDFGaussian icpEstimation;
285 
286  // Configure the matchings that will take place in the ICP process:
287  if (partIt->d->mapTillNow.m_pointsMaps.size())
288  {
289  ASSERT_(partIt->d->mapTillNow.m_pointsMaps.size() == 1);
290  // partIt->d->mapTillNow.m_pointsMaps[0]->insertionOptions.matchStaticPointsOnly
291  // = false;
292  }
293 
294  CMetricMap* map_to_align_to = nullptr;
295 
296  if (options.pfOptimalProposal_mapSelection == 0) // Grid map
297  {
298  ASSERT_(!partIt->d->mapTillNow.m_gridMaps.empty());
299 
300  // Build local map of points.
301  if (!built_map_points)
302  {
303  built_map_points = true;
304 
305  localMapPoints.insertionOptions.minDistBetweenLaserPoints =
306  0.02f; // 3.0f *
307  // m_particles[0].d->mapTillNow.m_gridMaps[0]->getResolution();;
308  localMapPoints.insertionOptions.isPlanarMap = true;
309  sf->insertObservationsInto(&localMapPoints);
310  }
311 
312  map_to_align_to = partIt->d->mapTillNow.m_gridMaps[0].get();
313  }
314  else if (options.pfOptimalProposal_mapSelection == 3) // Map of
315  // points
316  {
317  ASSERT_(!partIt->d->mapTillNow.m_pointsMaps.empty());
318 
319  // Build local map of points.
320  if (!built_map_points)
321  {
322  built_map_points = true;
323 
324  localMapPoints.insertionOptions.minDistBetweenLaserPoints =
325  0.02f; // 3.0f *
326  // m_particles[0].d->mapTillNow.m_gridMaps[0]->getResolution();;
327  localMapPoints.insertionOptions.isPlanarMap = true;
328  sf->insertObservationsInto(&localMapPoints);
329  }
330 
331  map_to_align_to = partIt->d->mapTillNow.m_pointsMaps[0].get();
332  }
333  else
334  {
335  ASSERT_(partIt->d->mapTillNow.m_landmarksMap);
336 
337  // Build local map of LMs.
338  if (!built_map_lms)
339  {
340  built_map_lms = true;
341  sf->insertObservationsInto(&localMapLandmarks);
342  }
343 
344  map_to_align_to = partIt->d->mapTillNow.m_landmarksMap.get();
345  }
346 
347  ASSERT_(map_to_align_to != nullptr);
348 
349  // Use ICP to align to each particle's map:
350  {
351  CPosePDF::Ptr alignEst = icp.Align(
352  map_to_align_to, &localMapPoints,
353  CPose2D(initialPoseEstimation), nullptr, &icpInfo);
354  icpEstimation.copyFrom(*alignEst);
355  }
356 
357  if (i == particleWithHighestW)
358  {
359  newInfoIndex = 1 - icpInfo.goodness; // newStaticPointsRatio;
360  // //* icpInfo.goodness;
361  }
362 
363  // Set the gaussian pose:
364  CPose3DPDFGaussian finalEstimatedPoseGauss(icpEstimation);
365 
366  // printf("[rbpf-slam] gridICP[%u]: %.02f%%\n", i,
367  // 100*icpInfo.goodness);
368  if (icpInfo.goodness < options.ICPGlobalAlign_MinQuality &&
369  SFs.size())
370  {
371  printf(
372  "[rbpf-slam] Warning: gridICP[%u]: %.02f%% -> Using "
373  "odometry instead!\n",
374  (unsigned int)i, 100 * icpInfo.goodness);
375  icpEstimation.mean = CPose2D(initialPoseEstimation);
376  }
377 
378 // As a way to take into account the odometry / "prior", use
379 // a correcting factor in the likelihood from the mismatch
380 // prior<->icp_estimate:
381 // const double prior_dist_lin =
382 // initialPoseEstimation.distanceTo(icpEstimation.mean);
383 // const double prior_dist_ang = std::abs( mrpt::math::wrapToPi(
384 // initialPoseEstimation.yaw()-icpEstimation.mean.phi() ) );
385 //// if (prior_dist_lin>0.10 || prior_dist_ang>DEG2RAD(3))
386 //// printf(" >>>>>>>>>> %f
387 ///%f\n",prior_dist_lin,RAD2DEG(prior_dist_ang));
388 // extra_log_lik = -(prior_dist_lin/0.20) -
389 //(prior_dist_ang/DEG2RAD(20));
390 
391 // printf("gICP: %.02f%%,
392 // Iters=%u\n",icpInfo.goodness,icpInfo.nIterations);
393 
394 #if 0 // Use hacked ICP covariance:
395  CPose3D Ap = finalEstimatedPoseGauss.mean - ith_last_pose;
396  const double Ap_dist = Ap.norm();
397 
398  finalEstimatedPoseGauss.cov.zeros();
399  finalEstimatedPoseGauss.cov(0,0) = square( fabs(Ap_dist)*0.01 );
400  finalEstimatedPoseGauss.cov(1,1) = square( fabs(Ap_dist)*0.01 );
401  finalEstimatedPoseGauss.cov(2,2) = square( fabs(Ap.yaw())*0.02 );
402 #else
403  // Use real ICP covariance (with a minimum level):
404  keep_max(finalEstimatedPoseGauss.cov(0, 0), square(0.002));
405  keep_max(finalEstimatedPoseGauss.cov(1, 1), square(0.002));
406  keep_max(finalEstimatedPoseGauss.cov(2, 2), square(DEG2RAD(0.1)));
407 
408 #endif
409 
410  // Generate gaussian-distributed 2D-pose increments according to
411  // "finalEstimatedPoseGauss":
412  // -------------------------------------------------------------------------------------------
413  finalPose =
414  finalEstimatedPoseGauss.mean; // Add to the new robot pose:
416  rndSamples, finalEstimatedPoseGauss.cov);
417  // Add noise:
418  finalPose.setFromValues(
419  finalPose.x() + rndSamples[0], finalPose.y() + rndSamples[1],
420  finalPose.z(), finalPose.yaw() + rndSamples[2],
421  finalPose.pitch(), finalPose.roll());
422  }
423  else if (options.pfOptimalProposal_mapSelection == 2)
424  {
425  // --------------------------------------------------------
426  // Perform optimal sampling with the beacon map:
427  // Described in paper...
428  // --------------------------------------------------------
429  /** \todo Add paper ref!
430  */
431  ASSERT_(partIt->d->mapTillNow.m_beaconMap);
432  CBeaconMap::Ptr beacMap = partIt->d->mapTillNow.m_beaconMap;
433 
434  updateStageAlreadyDone =
435  true; // We'll also update the weight of the particle here
436 
437  // =====================================================================
438  // SUMMARY:
439  // For each beacon measurement in the SF, stored in
440  // "lstObservedRanges",
441  // compute the SOG of the observation model, and multiply all of
442  // them
443  // (fuse) in "fusedObsModels". The result will hopefully be a very
444  // small
445  // PDF where to draw a sample from for the new robot pose.
446  // =====================================================================
447  bool methodSOGorGrid = false; // TRUE=SOG
448  CPoint3D newDrawnPosition;
449  float firstEstimateRobotHeading = std::numeric_limits<float>::max();
450 
451  // The parameters to discard too far gaussians:
452  CPoint3D centerPositionPrior(ith_last_pose);
453  float centerPositionPriorRadius = 2.0f;
454 
455  if (!robotActionSampler.isPrepared())
456  {
457  firstEstimateRobotHeading = ith_last_pose.yaw();
458  // If the map is empty: There is no solution!:
459  // THROW_EXCEPTION("There is no odometry & the initial beacon
460  // map is empty: RO-SLAM has no solution -> ABORTED!!!");
461 
462  if (!beacMap->size())
463  {
464  // First iteration only...
465  cerr << "[RO-SLAM] Optimal filtering without map & "
466  "odometry...this message should appear only the "
467  "first iteration!!"
468  << endl;
469  }
470  else
471  {
472  // To make RO-SLAM to have a solution, arbitrarily fix one
473  // of the beacons so
474  // unbiguity dissapears.
475  if (beacMap->get(0).m_typePDF == CBeacon::pdfSOG)
476  {
477  cerr << "[RO-SLAM] Optimal filtering without map & "
478  "odometry->FIXING ONE BEACON!"
479  << endl;
480  ASSERT_(beacMap->get(0).m_locationSOG.size() > 0);
481  CPoint3D fixedBeacon(
482  beacMap->get(0).m_locationSOG[0].val.mean);
483 
484  // Pass to gaussian without uncertainty:
485  beacMap->get(0).m_typePDF = CBeacon::pdfGauss;
486  beacMap->get(0).m_locationSOG.clear();
487  beacMap->get(0).m_locationGauss.mean = fixedBeacon;
488  beacMap->get(0).m_locationGauss.cov.unit(3, 1e-6);
489  }
490  }
491  } // end if there is no odometry
492 
493  // 1. Make the list of beacon IDs-ranges:
494  // -------------------------------------------
495  deque<TAuxRangeMeasInfo> lstObservedRanges;
496 
497  for (CSensoryFrame::const_iterator itObs = sf->begin();
498  itObs != sf->end(); ++itObs)
499  {
500  if (IS_CLASS((*itObs), CObservationBeaconRanges))
501  {
502  const CObservationBeaconRanges* obs =
503  static_cast<const CObservationBeaconRanges*>(
504  itObs->get());
505  deque<CObservationBeaconRanges::TMeasurement>::
506  const_iterator itRanges;
507  for (itRanges = obs->sensedData.begin();
508  itRanges != obs->sensedData.end(); itRanges++)
509  {
510  ASSERT_(itRanges->beaconID != INVALID_BEACON_ID);
511  // only add those in the map:
512  for (CBeaconMap::iterator itBeacs = beacMap->begin();
513  itBeacs != beacMap->end(); ++itBeacs)
514  {
515  if ((itBeacs)->m_ID == itRanges->beaconID)
516  {
517  TAuxRangeMeasInfo newMeas;
518  newMeas.beaconID = itRanges->beaconID;
519  newMeas.sensedDistance =
520  itRanges->sensedDistance;
521  newMeas.sensorLocationOnRobot =
522  itRanges->sensorLocationOnRobot;
523 
524  ASSERT_(
525  (itBeacs)->m_typePDF == CBeacon::pdfGauss ||
526  (itBeacs)->m_typePDF == CBeacon::pdfSOG);
527  newMeas.nGaussiansInMap =
528  (itBeacs)->m_typePDF == CBeacon::pdfSOG
529  ? (itBeacs)->m_locationSOG.size()
530  : 1 /*pdfGauss*/;
531 
532  lstObservedRanges.push_back(newMeas);
533  break; // Next observation
534  }
535  }
536  }
537  }
538  }
539 
540  // ASSERT_( lstObservedRanges.size()>=2 );
541 
542  // Sort ascending ranges: Smallest ranges first -> the problem is
543  // easiest!
544  sort(
545  lstObservedRanges.begin(), lstObservedRanges.end(),
547 
548  if (methodSOGorGrid)
549  {
550  CPointPDFSOG fusedObsModels; //<- p(z_t|x_t) for all the range
551  // measurements (fused)
552  fusedObsModels.clear();
553 
554  // 0. OPTIONAL: Create a "prior" as a first mode in
555  // "fusedObsModels"
556  // using odometry. If there is no odometry, we
557  // *absolutely* need
558  // at least one well-localized beacon at the beginning, or
559  // the symmetry cannot be broken!
560  // -----------------------------------------------------------------------------------------------
561  if (robotActionSampler.isPrepared())
562  {
564  newMode.log_w = 0;
565 
566  CPose3D auxPose =
567  ith_last_pose +
568  motionModelMeanIncr; // CPose3D(robotMovement->poseChange->getEstimatedPose()));
569  firstEstimateRobotHeading = auxPose.yaw();
570 
571  newMode.val.mean = CPoint3D(auxPose);
572 
573  // Uncertainty in z is null:
574  // CMatrix poseCOV =
575  // robotMovement->poseChange->getEstimatedCovariance();
576  CMatrixD poseCOV;
577  robotActionSampler.getOriginalPDFCov2D(poseCOV);
578 
579  poseCOV.setSize(2, 2);
580  poseCOV.setSize(3, 3);
581  newMode.val.cov = poseCOV;
582  fusedObsModels.push_back(newMode); // Add it:
583  }
584 
585  // 2. Generate the optimal proposal by fusing obs models
586  // -------------------------------------------------------------
587  for (CBeaconMap::iterator itBeacs = beacMap->begin();
588  itBeacs != beacMap->end(); ++itBeacs)
589  {
590  // for each observed beacon (by its ID), generate
591  // observation model:
593  lstObservedRanges.begin();
594  itObs != lstObservedRanges.end(); ++itObs)
595  {
596  if ((itBeacs)->m_ID == itObs->beaconID)
597  {
598  // Match:
599  float sensedRange = itObs->sensedDistance;
600 
601  CPointPDFSOG newObsModel;
602  (itBeacs)->generateObservationModelDistribution(
603  sensedRange, newObsModel,
604  beacMap.get(), // The beacon map, for options
605  itObs->sensorLocationOnRobot, // Sensor
606  // location on
607  // robot
608  centerPositionPrior, centerPositionPriorRadius);
609 
610  if (!fusedObsModels.size())
611  {
612  // This is the first one: Just copy the obs.
613  // model here:
614  fusedObsModels = newObsModel;
615  }
616  else
617  {
618  // Fuse with existing:
619  CPointPDFSOG tempFused(0);
620  tempFused.bayesianFusion(
621  fusedObsModels, newObsModel,
622  3 // minMahalanobisDistToDrop
623  );
624  fusedObsModels = tempFused;
625  }
626 
627  // Remove modes with negligible weights:
628  // -----------------------------------------------------------
629 
630  {
631  cout << "#modes bef: " << fusedObsModels.size()
632  << " ESS: " << fusedObsModels.ESS()
633  << endl;
634  double max_w = -1e100;
635  // int idx;
636 
638 
639  for (it = fusedObsModels.begin();
640  it != fusedObsModels.end(); it++)
641  max_w =
642  max(max_w, (it)->log_w); // keep the
643  // maximum
644  // mode weight
645 
646  for (it = fusedObsModels.begin();
647  it != fusedObsModels.end();)
648  {
649  if (max_w - (it)->log_w >
650  20) // Remove the mode:
651  it = fusedObsModels.erase(it);
652  else
653  it++;
654  }
655 
656  cout
657  << "#modes after: " << fusedObsModels.size()
658  << endl;
659  }
660 
661  // Shall we simplify the PDF?
662  // -----------------------------------------------------------
663  CMatrixDouble currentCov;
664  fusedObsModels.getCovariance(currentCov);
665  ASSERT_(
666  currentCov(0, 0) > 0 && currentCov(1, 1) > 0);
667  if (sqrt(currentCov(0, 0)) < 0.10f &&
668  sqrt(currentCov(1, 1)) < 0.10f &&
669  sqrt(currentCov(2, 2)) < 0.10f)
670  {
671  // Approximate by a single gaussian!
672  CPoint3D currentMean;
673  fusedObsModels.getMean(currentMean);
674  fusedObsModels[0].log_w = 0;
675  fusedObsModels[0].val.mean = currentMean;
676  fusedObsModels[0].val.cov = currentCov;
677 
678  fusedObsModels.resize(1);
679  }
680 
681  { /*
682  CMatrixD evalGrid;
683  fusedObsModels.evaluatePDFInArea(-3,3,-3,3,0.1,0,evalGrid,
684  true);
685  evalGrid *= 1.0/evalGrid.maxCoeff();
686  CImage imgF(evalGrid, true);
687  static int autoCount=0;
688  imgF.saveToFile(format("debug_%04i.png",autoCount++));*/
689  }
690  }
691  } // end for itObs (in lstObservedRanges)
692 
693  } // end for itBeacs
694 
695  /** /
696  COpenGLScene scene;
697  opengl::CSetOfObjects *obj = new opengl::CSetOfObjects();
698  fusedObsModels.getAs3DObject( *obj );
699  scene.insert( obj );
700  CFileStream("debug.3Dscene",fomWrite) << scene;
701  cout << "fusedObsModels # of modes: " <<
702  fusedObsModels.m_modes.size() << endl;
703  printf("ESS: %f\n",fusedObsModels.ESS() );
704  cout << fusedObsModels.getEstimatedCovariance() << endl;
705  mrpt::system::pause(); / **/
706 
707  if (beacMap->size())
708  fusedObsModels.drawSingleSample(newDrawnPosition);
709  }
710  else
711  {
712  // =============================
713  // GRID METHOD
714  // =============================
715 
716  float grid_min_x = ith_last_pose.x() - 0.5f;
717  float grid_max_x = ith_last_pose.x() + 0.5f;
718  float grid_min_y = ith_last_pose.y() - 0.5f;
719  float grid_max_y = ith_last_pose.y() + 0.5f;
720  float grid_resXY = 0.02f;
721 
722  bool repeatGridCalculation = false;
723 
724  do
725  {
726  CPosePDFGrid* pdfGrid = new CPosePDFGrid(
727  grid_min_x, grid_max_x, grid_min_y, grid_max_y,
728  grid_resXY, DEG2RAD(180), 0, 0);
729 
730  pdfGrid->uniformDistribution();
731 
732  // Fuse all the observation models in the grid:
733  // -----------------------------------------------------
734  for (CBeaconMap::iterator itBeacs = beacMap->begin();
735  itBeacs != beacMap->end(); ++itBeacs)
736  {
737  // for each observed beacon (by its ID), generate
738  // observation model:
740  lstObservedRanges.begin();
741  itObs != lstObservedRanges.end(); ++itObs)
742  {
743  if ((itBeacs)->m_ID == itObs->beaconID)
744  {
745  // Match:
746  float sensedRange = itObs->sensedDistance;
747 
748  /** /
749  CPointPDFSOG
750  newObsModel;
751  (itBeacs)->generateObservationModelDistribution(
752  sensedRange,
753  newObsModel,
754  beacMap,
755  // The beacon map, for options
756  itObs->sensorLocationOnRobot,//
757  Sensor location on robot
758  centerPositionPrior,
759  centerPositionPriorRadius
760  );
761  / **/
762  for (size_t idxX = 0;
763  idxX < pdfGrid->getSizeX(); idxX++)
764  {
765  float grid_x = pdfGrid->idx2x(idxX);
766  for (size_t idxY = 0;
767  idxY < pdfGrid->getSizeY(); idxY++)
768  {
769  double grid_y = pdfGrid->idx2y(idxY);
770 
771  // Evaluate obs model:
772  double* cell =
773  pdfGrid->getByIndex(idxX, idxY, 0);
774 
775  //
776  double lik =
777  1; // newObsModel.evaluatePDF(CPoint3D(grid_x,grid_y,0),true);
778  switch ((itBeacs)->m_typePDF)
779  {
780  case CBeacon::pdfSOG:
781  {
782  CPointPDFSOG* sog =
783  &(itBeacs)->m_locationSOG;
784  double sensorSTD2 = square(
785  beacMap->likelihoodOptions
786  .rangeStd);
787 
789  for (it = sog->begin();
790  it != sog->end(); it++)
791  {
792  lik *= exp(
793  -0.5 *
794  square(
795  sensedRange -
796  (it)->val.mean.distance2DTo(
797  grid_x +
798  itObs
799  ->sensorLocationOnRobot
800  .x(),
801  grid_y +
802  itObs
803  ->sensorLocationOnRobot
804  .y())) /
805  sensorSTD2);
806  }
807  }
808  break;
809  default:
810  break; // THROW_EXCEPTION("NO");
811  }
812 
813  (*cell) *= lik;
814 
815  } // for idxY
816  } // for idxX
817  pdfGrid->normalize();
818  } // end if match
819  } // end for itObs
820  } // end for beacons in map
821 
822  // Draw the single pose from the grid:
823  if (beacMap->size())
824  {
825  // Take the most likely cell:
826  float maxW = -1e10f;
827  float maxX = 0, maxY = 0;
828  for (size_t idxX = 0; idxX < pdfGrid->getSizeX();
829  idxX++)
830  {
831  // float grid_x = pdfGrid->idx2x(idxX);
832  for (size_t idxY = 0; idxY < pdfGrid->getSizeY();
833  idxY++)
834  {
835  // float grid_y = pdfGrid->idx2y(idxY);
836 
837  // Evaluate obs model:
838  float c = *pdfGrid->getByIndex(idxX, idxY, 0);
839  if (c > maxW)
840  {
841  maxW = c;
842  maxX = pdfGrid->idx2x(idxX);
843  maxY = pdfGrid->idx2y(idxY);
844  }
845  }
846  }
847  newDrawnPosition.x(maxX);
848  newDrawnPosition.y(maxY);
849 
850 #if 0
851  {
852  //cout << "Grid: " << pdfGaussApprox << endl;
853  //pdfGrid->saveToTextFile("debug.txt");
854  CMatrixDouble outMat;
855  pdfGrid->getAsMatrix(0, outMat );
856  outMat *= 1.0f/outMat.maxCoeff();
857  CImage imgF(outMat, true);
858  static int autocount=0;
859  imgF.saveToFile(format("debug_grid_%f_%05i.png",grid_resXY,autocount++));
860  printf("grid res: %f MAX: %f,%f\n",grid_resXY,maxX,maxY);
861  //mrpt::system::pause();
862  }
863 #endif
864 
865  if (grid_resXY > 0.01f)
866  {
867  grid_min_x = maxX - 0.03f;
868  grid_max_x = maxX + 0.03f;
869  grid_min_y = maxY - 0.03f;
870  grid_max_y = maxY + 0.03f;
871  grid_resXY = 0.002f;
872  repeatGridCalculation = true;
873  }
874  else
875  repeatGridCalculation = false;
876 
877  /*
878  // Approximate by a Gaussian:
879  CPosePDFGaussian pdfGaussApprox(
880  pdfGrid->getEstimatedPose(),
881  pdfGrid->getEstimatedCovariance() );
882  CPose2D newDrawnPose;
883  //pdfGrid->drawSingleSample( newDrawnPose );
884  pdfGaussApprox.drawSingleSample( newDrawnPose );
885  newDrawnPosition = newDrawnPose;
886  */
887  }
888  delete pdfGrid;
889  pdfGrid = nullptr;
890 
891  } while (repeatGridCalculation);
892 
893  // newDrawnPosition has the pose:
894  }
895 
896  // 3. Get the new "finalPose" of this particle as a random sample
897  // from the
898  // optimal proposal:
899  // ---------------------------------------------------------------------------
900  if (!beacMap->size())
901  {
902  // If we are in the first iteration (no map yet!) just stay
903  // still:
904  finalPose = ith_last_pose;
905  }
906  else
907  {
908  cout << "Drawn: " << newDrawnPosition << endl;
909  // cout << "Final cov was:\n" <<
910  // fusedObsModels.getEstimatedCovariance() << endl << endl;
911 
912  // Make sure it was initialized
913  ASSERT_(
914  firstEstimateRobotHeading !=
915  std::numeric_limits<float>::max());
916 
917  finalPose.setFromValues(
918  newDrawnPosition.x(), newDrawnPosition.y(),
919  newDrawnPosition.z(), firstEstimateRobotHeading, 0, 0);
920  }
921  /** \todo If there are 2+ sensors on the robot, compute phi?
922  */
923 
924  // 4. Update the weight:
925  // In optimal sampling this is the expectation of the
926  // observation likelihood: This is the observation likelihood
927  // averaged
928  // over the whole optimal proposal.
929  // ---------------------------------------------------------------------------
930  double weightUpdate = 0;
931  partIt->log_w += PF_options.powFactor * weightUpdate;
932  }
933  else
934  {
935  // By default:
936  // Generate gaussian-distributed 2D-pose increments according to
937  // mean-cov:
938  if (!robotActionSampler.isPrepared())
940  "Action list does not contain any CActionRobotMovement2D "
941  "or CActionRobotMovement3D object!");
942 
943  robotActionSampler.drawSample(incrPose);
944 
945  finalPose = ith_last_pose + incrPose;
946  }
947 
948  // Insert as the new pose in the path:
949  partIt->d->robotPath.push_back(finalPose.asTPose());
950 
951  // ----------------------------------------------------------------------
952  // UPDATE STAGE
953  // ----------------------------------------------------------------------
954  if (!updateStageAlreadyDone)
955  {
956  partIt->log_w += PF_options.powFactor *
957  (PF_SLAM_computeObservationLikelihoodForParticle(
958  PF_options, i, *sf, finalPose) +
959  extra_log_lik);
960  } // if update not already done...
961 
962  } // end of for each particle "i" & "partIt"
963 
964  printf("Ok\n");
965 
966  MRPT_END
967 }
968 
969 /*---------------------------------------------------------------
970  prediction_and_update_pfStandardProposal
971  ---------------------------------------------------------------*/
972 void CMultiMetricMapPDF::prediction_and_update_pfStandardProposal(
973  const mrpt::obs::CActionCollection* actions,
974  const mrpt::obs::CSensoryFrame* sf,
976 {
977  MRPT_START
978 
979  PF_SLAM_implementation_pfStandardProposal<mrpt::slam::detail::TPoseBin2D>(
980  actions, sf, PF_options, options.KLD_params);
981 
982  // Average map will need to be updated after this:
983  averageMapIsUpdated = false;
984 
985  MRPT_END
986 }
987 
988 // Specialization for my kind of particles:
989 void CMultiMetricMapPDF::
990  PF_SLAM_implementation_custom_update_particle_with_new_pose(
991  CRBPFParticleData* particleData, const TPose3D& newPose) const
992 {
993  particleData->robotPath.push_back(newPose);
994 }
995 
996 // Specialization for RBPF maps:
997 bool CMultiMetricMapPDF::PF_SLAM_implementation_doWeHaveValidObservations(
998  const CMultiMetricMapPDF::CParticleList& particles,
999  const CSensoryFrame* sf) const
1000 {
1001  if (sf == nullptr) return false;
1002  ASSERT_(!particles.empty());
1003  return particles.begin()
1004  ->d.get()
1005  ->mapTillNow.canComputeObservationsLikelihood(*sf);
1006 }
1007 
1008 /** Do not move the particles until the map is populated. */
1009 bool CMultiMetricMapPDF::PF_SLAM_implementation_skipRobotMovement() const
1010 {
1011  return 0 == getNumberOfObservationsInSimplemap();
1012 }
1013 
1014 /*---------------------------------------------------------------
1015  Evaluate the observation likelihood for one
1016  particle at a given location
1017  ---------------------------------------------------------------*/
1018 double CMultiMetricMapPDF::PF_SLAM_computeObservationLikelihoodForParticle(
1019  const CParticleFilter::TParticleFilterOptions& PF_options,
1020  const size_t particleIndexForMap, const CSensoryFrame& observation,
1021  const CPose3D& x) const
1022 {
1023  MRPT_UNUSED_PARAM(PF_options);
1024  CMultiMetricMap* map = const_cast<CMultiMetricMap*>(
1025  &m_particles[particleIndexForMap].d->mapTillNow);
1026  double ret = 0;
1027  for (CSensoryFrame::const_iterator it = observation.begin();
1028  it != observation.end(); ++it)
1029  ret += map->computeObservationLikelihood((CObservation*)it->get(), x);
1030  return ret;
1031 }
A namespace of pseudo-random numbers generators of diferent distributions.
mrpt::math::TPose3D asTPose() const
Definition: CPose3D.cpp:1043
double x() const
Common members of all points & poses classes.
Definition: CPoseOrPoint.h:140
void copyFrom(const CPosePDF &o) override
Copy operator, translating if necesary (for example, between particles and gaussian representations) ...
Scalar * iterator
Definition: eigen_plugins.h:26
#define MRPT_START
Definition: exceptions.h:262
CPose2D mean
The mean value.
CPose3D mean
The mean value.
This class is a "CSerializable" wrapper for "CMatrixTemplateNumeric<double>".
Definition: CMatrixD.h:22
static bool cmp_Asc(const TAuxRangeMeasInfo &a, const TAuxRangeMeasInfo &b)
Auxiliary for optimal sampling in RO-SLAM.
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
void getMean(CPoint3D &mean_point) const override
Returns an estimate of the point, (the mean, or mathematical expectation of the PDF) ...
mrpt::poses::CPosePDF::Ptr Align(const mrpt::maps::CMetricMap *m1, const mrpt::maps::CMetricMap *m2, const mrpt::poses::CPose2D &grossEst, float *runningTime=nullptr, void *info=nullptr)
The method for aligning a pair of metric maps, aligning only 2D + orientation.
CPoint3D mean
The mean value.
Several implementations of ICP (Iterative closest point) algorithms for aligning two point maps or a ...
Definition: CICP.h:65
double DEG2RAD(const double x)
Degrees to radians.
The namespace for Bayesian filtering algorithm: different particle filters and Kalman filter algorith...
void resize(const size_t N)
Resize the number of SOG modes.
Declares a class that represents a Probability Density function (PDF) of a 3D point ...
Definition: CPointPDFSOG.h:34
The struct for each mode:
Definition: CPointPDFSOG.h:41
double computeObservationLikelihood(const mrpt::obs::CObservation *obs, const mrpt::poses::CPose3D &takenFrom)
Computes the log-likelihood of a given observation given an arbitrary robot 3D pose.
Definition: CMetricMap.cpp:189
void uniformDistribution()
Assigns the same value to all the cells in the grid, so the sum 1.
This file contains the implementations of the template members declared in mrpt::slam::PF_implementat...
double x
X,Y,Z, coords.
Column vector, like Eigen::MatrixX*, but automatically initialized to zeros since construction...
Definition: eigen_frwds.h:44
void clear()
Clear all the gaussian modes.
double yaw() const
Get the YAW angle (in radians)
Definition: CPose3D.h:526
Declares a class derived from "CObservation" that represents one (or more) range measurements to labe...
Option set for KLD algorithm.
Definition: TKLDParams.h:18
void drawSingleSample(CPoint3D &outSample) const override
Draw a sample from the pdf.
A cloud of points in 2D or 3D, which can be built from a sequence of laser scans. ...
STL namespace.
void getCovariance(mrpt::math::CMatrixDouble &cov) const
Returns the estimate of the covariance matrix (STATE_LEN x STATE_LEN covariance matrix) ...
double yaw
Yaw coordinate (rotation angle over Z axis).
bool isPrepared() const
Return true if samples can be generated, which only requires a previous call to setPosePDF.
void KLF_loadBinFromParticle(detail::TPathBin2D &outBin, const TKLDParams &opts, const mrpt::maps::CRBPFParticleData *currentParticleValue, const TPose3D *newPoseToBeInserted)
Fills out a "TPathBin2D" variable, given a path hypotesis and (if not set to nullptr) a new pose appe...
Declares a class for storing a collection of robot actions.
double idx2y(size_t y) const
Returns coordinates from "indexes":
void getAsMatrix(const double &phi, MATRIXLIKE &outMat)
Returns the whole grid as a matrix, for a given constant "phi" and where each row contains values for...
const T * getByIndex(size_t x, size_t y, size_t phi) const
Reads the contents of a cell.
T square(const T x)
Inline function for the square of a number.
const_iterator begin() const
Returns a constant iterator to the first observation: this is an example of usage: ...
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
Represents a probabilistic 3D (6D) movement.
float goodness
A goodness measure for the alignment, it is a [0,1] range indicator of percentage of correspondences...
Definition: CICP.h:197
mrpt::img::CImage CImage
Definition: utils/CImage.h:5
This base provides a set of functions for maths stuff.
Auxiliary structure.
void keep_max(T &var, const K test_val)
If the second argument is above the first one, set the first argument to this higher value...
A class for storing a map of 3D probabilistic landmarks.
Definition: CLandmarksMap.h:75
Declares a class that represents a Probability Density function (PDF) of a 2D pose ...
void setPosePDF(const CPosePDF *pdf)
This method must be called to select the PDF from which to draw samples.
CActionRobotMovement2D::Ptr getBestMovementEstimation() const
Returns the best pose increment estimator in the collection, based on the determinant of its pose cha...
double norm() const
Returns the euclidean norm of vector: .
Definition: CPoseOrPoint.h:253
__int64 int64_t
Definition: rptypes.h:49
void push_back(const TGaussianMode &m)
Inserts a copy of the given mode into the SOG.
Definition: CPointPDFSOG.h:100
void getOriginalPDFCov2D(mrpt::math::CMatrixDouble33 &cov3x3) const
Retrieves the 3x3 covariance of the original PDF in .
const GLubyte * c
Definition: glext.h:6313
mrpt::math::CMatrixDouble33 cov
The 3x3 covariance matrix.
std::deque< TMeasurement > sensedData
The list of observed ranges.
std::deque< CObservation::Ptr >::const_iterator const_iterator
You can use CSensoryFrame::begin to get a iterator to the first element.
This namespace contains representation of robot actions and observations.
int val
Definition: mrpt_jpeglib.h:955
Declares a class for storing a "sensory frame", a set of "observations" taken by the robot approximat...
Definition: CSensoryFrame.h:52
GLubyte GLubyte b
Definition: glext.h:6279
double KLD_binSize_XY
Parameters for the KLD adaptive sample size algorithm (see Dieter Fox&#39;s papers), which is used only i...
Definition: TKLDParams.h:32
A class used to store a 3D point.
Definition: CPoint3D.h:31
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
Auxiliary class used in mrpt::maps::CMultiMetricMapPDF.
iterator erase(iterator i)
Definition: CPointPDFSOG.h:105
Auxiliary structure used in KLD-sampling in particle filters.
mrpt::math::CMatrixDouble66 cov
The 6x6 covariance matrix.
#define INVALID_BEACON_ID
Used for CObservationBeaconRange, CBeacon, etc.
Definition: CObservation.h:24
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
void drawGaussianMultivariate(std::vector< T > &out_result, const MATRIX &cov, const std::vector< T > *mean=nullptr)
Generate multidimensional random samples according to a given covariance matrix.
Declares a virtual base class for all metric maps storage classes.
Definition: CMetricMap.h:54
A class used to store a 2D pose, including the 2D coordinate point and a heading (phi) angle...
Definition: CPose2D.h:38
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:86
Declares a class that represents any robot&#39;s observation.
Definition: CObservation.h:43
double powFactor
An optional step to "smooth" dramatic changes in the observation model to affect the variance of the ...
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
double ESS() const
Computes the "Effective sample size" (typical measure for Particle Filters), applied to the weights o...
#define IS_CLASS(ptrObj, class_name)
Evaluates to true if the given pointer to an object (derived from mrpt::rtti::CObject) is of the give...
Definition: CObject.h:102
The configuration of a particle filter.
size_t size() const
Return the number of Gaussian modes.
Definition: CPointPDFSOG.h:109
#define MRPT_END
Definition: exceptions.h:266
std::deque< mrpt::math::TPose3D > robotPath
void setFromValues(const double x0, const double y0, const double z0, const double yaw=0, const double pitch=0, const double roll=0)
Set the pose from a 3D position (meters) and yaw/pitch/roll angles (radians) - This method recomputes...
Definition: CPose3D.cpp:239
The ICP algorithm return information.
Definition: CICP.h:190
Lightweight 3D pose (three spatial coordinates, plus three angular coordinates).
Declares a class that represents a Probability Density function (PDF) of a 3D pose ...
std::vector< TPoseBin2D > bins
double idx2x(size_t x) const
Returns coordinates from "indexes":
T::Ptr getActionByClass(const size_t &ith=0) const
Access to the i&#39;th action of a given class, or a nullptr smart pointer if there is no action of that ...
Auxiliary for optimal sampling in RO-SLAM.
CPose2D & drawSample(CPose2D &p) const
Generate a new sample from the selected PDF.
GLenum GLint x
Definition: glext.h:3538
const_iterator end() const
Returns a constant iterator to the end of the list of observations: this is an example of usage: ...
This class stores any customizable set of metric maps.
An efficient generator of random samples drawn from a given 2D (CPosePDF) or 3D (CPose3DPDF) pose pro...
Declares a class that represents a Probability Distribution function (PDF) of a 2D pose (x...
Definition: CPosePDFGrid.h:25
CRandomGenerator & getRandomGenerator()
A static instance of a CRandomGenerator class, for use in single-thread applications.
bool insertObservationsInto(mrpt::maps::CMetricMap *theMap, const mrpt::poses::CPose3D *robotPose=nullptr) const
Insert all the observations in this SF into a metric map or any kind (see mrpt::maps::CMetricMap).
GLubyte GLubyte GLubyte a
Definition: glext.h:6279
GLfloat GLfloat p
Definition: glext.h:6305
void normalize()
Normalizes the PDF, such as all cells sum the unity.
std::deque< CBeacon >::iterator iterator
Definition: CBeaconMap.h:50
void bayesianFusion(const CPointPDF &p1, const CPointPDF &p2, const double minMahalanobisDistToDrop=0) override
Bayesian fusion of two point distributions (product of two distributions->new distribution), then save the result in this object (WARNING: See implementing classes to see classes that can and cannot be mixtured!)
std::deque< TGaussianMode >::iterator iterator
Definition: CPointPDFSOG.h:53
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
int round(const T value)
Returns the closer integer (int) to x.
Definition: round.h:23



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020