MRPT  1.9.9
CLoopCloserERD_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-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 #ifndef CLOOPCLOSERERD_IMPL_H
11 #define CLOOPCLOSERERD_IMPL_H
12 #include <mrpt/math/utils.h>
14 #include <mrpt/opengl/CEllipsoid.h>
15 #include <mrpt/opengl/CSphere.h>
16 #include <mrpt/math/data_utils.h>
17 
19 {
20 // Ctors, Dtors
21 // //////////////////////////////////
22 template <class GRAPH_T>
24  : m_visualize_curr_node_covariance(false),
25  m_curr_node_covariance_color(160, 160, 160, /*alpha = */ 255),
26  m_partitions_full_update(false),
27  m_is_first_time_node_reg(true),
28  m_dijkstra_node_count_thresh(3)
29 {
30  this->initializeLoggers("CLoopCloserERD");
31  m_edge_types_to_nums["ICP2D"] = 0;
32  m_edge_types_to_nums["LC"] = 0;
33  MRPT_LOG_DEBUG_STREAM("Initialized class object");
34 }
35 
36 template <class GRAPH_T>
38 {
39  using namespace mrpt::graphslam;
40 
41  // release memory of m_node_optimal_paths map.
42  MRPT_LOG_DEBUG_STREAM("Releasing memory of m_node_optimal_paths map...");
43  for (auto it = m_node_optimal_paths.begin();
44  it != m_node_optimal_paths.end(); ++it)
45  {
46  delete it->second;
47  }
48 }
49 
50 // Methods implementations
51 // //////////////////////////////////
52 
53 template <class GRAPH_T>
56  mrpt::obs::CSensoryFrame::Ptr observations,
57  mrpt::obs::CObservation::Ptr observation)
58 {
59  MRPT_START;
60  MRPT_UNUSED_PARAM(action);
61  this->m_time_logger.enter("updateState");
62  using namespace std;
63  using namespace mrpt;
64  using namespace mrpt::obs;
65  using namespace mrpt::obs::utils;
66  using namespace mrpt::opengl;
67  using namespace mrpt::poses;
68  using namespace mrpt::math;
69 
70  // Track the last recorded laser scan
71  {
73  getObservation<CObservation2DRangeScan>(observations, observation);
74  if (scan)
75  {
76  m_last_laser_scan2D = scan;
77  }
78  }
79 
80  if (!m_first_laser_scan)
81  {
82  m_first_laser_scan = m_last_laser_scan2D;
83  }
84 
85  // check possible prior node registration
86  size_t num_registered =
87  absDiff(this->m_last_total_num_nodes, this->m_graph->nodeCount());
88  bool registered_new_node = num_registered > 0;
89 
90  if (registered_new_node)
91  {
92  MRPT_LOG_DEBUG_STREAM("New node has been registered in the graph!");
93  registered_new_node = true;
94 
95  // either single node registration, or double node registration for the
96  // first time only, unless we override this check.
97  if (!this->m_override_registered_nodes_check)
98  {
99  if (!((num_registered == 1) ^
100  (num_registered == 2 && m_is_first_time_node_reg)))
101  {
103  "Invalid number of registered nodes since last call to "
104  "updateStates| "
105  "Found \""
106  << num_registered << "\" new nodes.");
107  THROW_EXCEPTION("Invalid number of registered nodes.");
108  }
109  }
110 
111  // first time call:
112  // NRD should have registered *2* nodes; one for the root node and one
113  // for
114  // the first added constraint. Add the first measurement taken to the
115  // root
116  // and the second as usual
117  if (m_is_first_time_node_reg)
118  {
120  "Assigning first laserScan to the graph root node.");
121  this->m_nodes_to_laser_scans2D[this->m_graph->root] =
122  m_first_laser_scan;
123  m_is_first_time_node_reg = false;
124  }
125 
126  // register the new node-laserScan pair
127  this->m_nodes_to_laser_scans2D[this->m_graph->nodeCount() - 1] =
128  m_last_laser_scan2D;
129 
130  if (m_laser_params.use_scan_matching)
131  {
132  // scan match with previous X nodes
133  this->addScanMatchingEdges(this->m_graph->nodeCount() - 1);
134  }
135 
136  // update partitioning scheme with the latest pose/measurement
137  m_partitions_full_update =
138  ((this->m_graph->nodeCount() %
139  m_lc_params.full_partition_per_nodes) == 0 ||
140  this->m_just_inserted_lc)
141  ? true
142  : false;
143  this->updateMapPartitions(
144  m_partitions_full_update,
145  /* is_first_time_node_reg = */ num_registered == 2);
146 
147  // check for loop closures
148  partitions_t partitions_for_LC;
149  this->checkPartitionsForLC(&partitions_for_LC);
150  this->evaluatePartitionsForLC(partitions_for_LC);
151 
152  if (m_visualize_curr_node_covariance)
153  {
154  this->execDijkstraProjection();
155  }
156 
157  this->m_last_total_num_nodes = this->m_graph->nodeCount();
158  }
159 
160  this->m_time_logger.leave("updateState");
161  return true;
162  MRPT_END;
163 }
164 
165 template <class GRAPH_T>
167  const mrpt::graphs::TNodeID& curr_nodeID,
168  std::set<mrpt::graphs::TNodeID>* nodes_set)
169 {
170  ASSERTDEB_(nodes_set);
171 
172  // deal with the case that less than `prev_nodes_for_ICP` nodes have been
173  // registered
174  int fetched_nodeIDs = 0;
175  for (int nodeID_i = static_cast<int>(curr_nodeID) - 1;
176  ((fetched_nodeIDs <= this->m_laser_params.prev_nodes_for_ICP) &&
177  (nodeID_i >= 0)); // <----- I *have* to use int (instead of unsigned)
178  // if I use this condition
179  --nodeID_i)
180  {
181  nodes_set->insert(nodeID_i);
182  fetched_nodeIDs++;
183  }
184 } // end of fetchNodeIDsForScanMatching
185 
186 template <class GRAPH_T>
188  const mrpt::graphs::TNodeID& curr_nodeID)
189 {
190  MRPT_START;
191  using namespace std;
192  using namespace mrpt;
193  using namespace mrpt::obs;
194  using namespace mrpt::math;
195  using mrpt::graphs::TNodeID;
196 
197  // get a list of nodes to check ICP against
198  MRPT_LOG_DEBUG_STREAM("Adding ICP Constraints for nodeID: " << curr_nodeID);
199 
200  std::set<TNodeID> nodes_set;
201  this->fetchNodeIDsForScanMatching(curr_nodeID, &nodes_set);
202 
203  // try adding ICP constraints with each node in the previous set
204  for (std::set<TNodeID>::const_iterator node_it = nodes_set.begin();
205  node_it != nodes_set.end(); ++node_it)
206  {
207  constraint_t rel_edge;
209 
211  "Fetching laser scan for nodes: " << *node_it << "==> "
212  << curr_nodeID);
213 
214  bool found_edge =
215  this->getICPEdge(*node_it, curr_nodeID, &rel_edge, &icp_info);
216  if (!found_edge) continue;
217 
218  // keep track of the recorded goodness values
219  // TODO - rethink on these condition.
220  if (!isnan(icp_info.goodness) ||
221  !approximatelyEqual(static_cast<double>(icp_info.goodness), 0.0))
222  {
223  m_laser_params.goodness_threshold_win.addNewMeasurement(
224  icp_info.goodness);
225  }
226  double goodness_thresh =
227  m_laser_params.goodness_threshold_win.getMedian() *
228  m_consec_icp_constraint_factor;
229  bool accept_goodness = icp_info.goodness > goodness_thresh;
231  "Curr. Goodness: " << icp_info.goodness
232  << "|\t Threshold: " << goodness_thresh << " => "
233  << (accept_goodness ? "ACCEPT" : "REJECT"));
234 
235  // make sure that the suggested edge makes sense with regards to current
236  // graph config - check against the current position difference
237  bool accept_mahal_distance = this->mahalanobisDistanceOdometryToICPEdge(
238  *node_it, curr_nodeID, rel_edge);
239 
240  // criterion for registering a new node
241  if (accept_goodness && accept_mahal_distance)
242  {
243  this->registerNewEdge(*node_it, curr_nodeID, rel_edge);
244  }
245  }
246 
247  MRPT_END;
248 } // end of addScanMatchingEdges
249 template <class GRAPH_T>
251  const mrpt::graphs::TNodeID& from, const mrpt::graphs::TNodeID& to,
252  constraint_t* rel_edge, mrpt::slam::CICP::TReturnInfo* icp_info,
253  const TGetICPEdgeAdParams* ad_params /*=NULL*/)
254 {
255  MRPT_START;
256  ASSERTDEB_(rel_edge);
257  this->m_time_logger.enter("getICPEdge");
258 
259  using namespace mrpt::obs;
260  using namespace std;
261  using namespace mrpt::graphslam::detail;
262 
263  // fetch the relevant laser scans and poses of the nodeIDs
264  // If given in the additional params struct, use those values instead of
265  // searching in the class std::map(s)
266  CObservation2DRangeScan::Ptr from_scan, to_scan;
267  global_pose_t from_pose;
268  global_pose_t to_pose;
269 
270  MRPT_LOG_DEBUG_STREAM("****In getICPEdge method: ");
271  if (ad_params)
272  {
274  "TGetICPEdgeAdParams:\n"
275  << ad_params->getAsString() << endl);
276  }
277 
278  // from-node parameters
279  const node_props_t* from_params =
280  ad_params ? &ad_params->from_params : NULL;
281  bool from_success = this->getPropsOfNodeID(
282  from, &from_pose, from_scan, from_params); // TODO
283  // to-node parameters
284  const node_props_t* to_params = ad_params ? &ad_params->to_params : NULL;
285  bool to_success = this->getPropsOfNodeID(to, &to_pose, to_scan, to_params);
286 
287  if (!from_success || !to_success)
288  {
290  "Either node #"
291  << from << " or node #" << to
292  << " doesn't contain a valid LaserScan. Ignoring this...");
293  return false;
294  }
295 
296  // make use of initial node position difference for the ICP edge
297  // from_node pose
298  pose_t initial_estim;
299  if (ad_params)
300  {
301  initial_estim = ad_params->init_estim;
302  }
303  else
304  {
305  initial_estim = to_pose - from_pose;
306  }
307 
309  "from_pose: " << from_pose << "| to_pose: " << to_pose
310  << "| init_estim: " << initial_estim);
311 
312  range_ops_t::getICPEdge(
313  *from_scan, *to_scan, rel_edge, &initial_estim, icp_info);
314  MRPT_LOG_DEBUG_STREAM("*************");
315 
316  this->m_time_logger.leave("getICPEdge");
317  return true;
318  MRPT_END;
319 } // end of getICPEdge
320 
321 template <class GRAPH_T>
323  const mrpt::graphs::TNodeID& nodeID,
324  const std::map<mrpt::graphs::TNodeID, node_props_t>& group_params,
325  node_props_t* node_props)
326 {
327  ASSERTDEB_(node_props);
328 
329  // MRPT_LOG_DEBUG_STREAM(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
330  // MRPT_LOG_DEBUG_STREAM("Running fillNodePropsFromGroupParams for nodeID
331  // \""
332  //<< nodeID << "\"");
333 
334  // Make sure that the given nodeID exists in the group_params
336  search = group_params.find(nodeID);
337  bool res = false;
338  if (search == group_params.end())
339  {
340  // MRPT_LOG_DEBUG_STREAM("Operation failed.");
341  }
342  else
343  {
344  *node_props = search->second;
345  res = true;
346  // MRPT_LOG_DEBUG_STREAM("Properties filled: " <<
347  // node_props->getAsString());
348  }
349 
350  // MRPT_LOG_DEBUG_STREAM("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
351  return res;
352 }
353 
354 template <class GRAPH_T>
356  const mrpt::graphs::TNodeID& nodeID, global_pose_t* pose,
358  const node_props_t* node_props /*=NULL*/) const
359 {
360  // make sure output instances are valid
361  ASSERTDEB_(pose);
362 
363  bool filled_pose = false;
364  bool filled_scan = false;
365 
366  if (node_props)
367  {
368  // Pose
369  if (node_props->pose != global_pose_t())
370  {
371  *pose = node_props->pose;
372  filled_pose = true;
373  }
374  // LaserScan
375  if (node_props->scan)
376  {
377  scan = node_props->scan;
378  filled_scan = true;
379  }
380  }
381 
382  // TODO - What if the node_props->pose is indeed 0?
384  !(filled_pose ^ filled_scan),
385  format(
386  "Either BOTH or NONE of the filled_pose, filled_scan should be set."
387  "NodeID: [%lu]",
388  static_cast<unsigned long>(nodeID)));
389 
390  //
391  // fill with class data if not yet available
392  //
393  if (!filled_pose)
394  {
395  // fill with class data if not yet available
397  this->m_graph->nodes.find(nodeID);
398  if (search != this->m_graph->nodes.end())
399  {
400  *pose = search->second;
401  filled_pose = true;
402  }
403  else
404  {
405  MRPT_LOG_WARN_STREAM("pose not found for nodeID: " << nodeID);
406  }
407  }
408  if (!filled_scan)
409  {
410  typename nodes_to_scans2D_t::const_iterator search =
411  this->m_nodes_to_laser_scans2D.find(nodeID);
412  if (search != this->m_nodes_to_laser_scans2D.end())
413  {
414  scan = search->second;
415  filled_scan = true;
416  }
417  }
418 
419  return filled_pose && filled_scan;
420 }
421 
422 template <class GRAPH_T>
424  partitions_t* partitions_for_LC)
425 {
426  MRPT_START;
427  this->m_time_logger.enter("LoopClosureEvaluation");
428 
429  using namespace std;
430  using namespace mrpt;
431 
432  ASSERTDEB_(partitions_for_LC);
433  partitions_for_LC->clear();
434 
435  // keep track of the previous nodes list of every partition. If this is not
436  // changed - do not mark it as potential for loop closure
437  map<int, std::vector<uint32_t>>::iterator finder;
438  // reset the previous list if full partitioning was issued
439  if (m_partitions_full_update)
440  {
441  m_partitionID_to_prev_nodes_list.clear();
442  }
443 
444  int partitionID = 0;
445  // for every partition...
446  for (partitions_t::const_iterator partitions_it = m_curr_partitions.begin();
447  partitions_it != m_curr_partitions.end();
448  ++partitions_it, ++partitionID)
449  {
450  // check whether the last registered node is in the currently traversed
451  // partition - if not, ignore it.
452  if (m_lc_params.LC_check_curr_partition_only)
453  {
454  bool curr_node_in_curr_partition =
455  ((find(
456  partitions_it->begin(), partitions_it->end(),
457  this->m_graph->nodeCount() - 1)) != partitions_it->end());
458  if (!curr_node_in_curr_partition)
459  {
460  continue;
461  }
462  }
463 
464  // keep track of the previous nodes list
465  finder = m_partitionID_to_prev_nodes_list.find(partitionID);
466  if (finder == m_partitionID_to_prev_nodes_list.end())
467  {
468  // nodes list is not registered yet
469  m_partitionID_to_prev_nodes_list.insert(
470  make_pair(partitionID, *partitions_it));
471  }
472  else
473  {
474  if (*partitions_it == finder->second)
475  {
476  // MRPT_LOG_DEBUG_STREAM("Partition " << partitionID
477  //<< " remained unchanged. ");
478  continue; // same list as before.. no need to check this...
479  }
480  else
481  { // list was changed - update the previous nodes list
482  // MRPT_LOG_DEBUG_STREAM("Partition " << partitionID << "
483  // CHANGED. ");
484  finder->second = *partitions_it;
485  }
486  }
487 
488  // investigate each partition
489  int curr_node_index = 1;
490  size_t prev_nodeID = *(partitions_it->begin());
492  it = partitions_it->begin() + 1;
493  it != partitions_it->end(); ++it, ++curr_node_index)
494  {
495  size_t curr_nodeID = *it;
496 
497  // are there consecutive nodes with large difference inside this
498  // partition? Are these nodes enough to consider LC?
499  if ((curr_nodeID - prev_nodeID) > m_lc_params.LC_min_nodeid_diff)
500  {
501  // there is at least one divergent node..
502 
503  int num_after_nodes = partitions_it->size() - curr_node_index;
504  int num_before_nodes = partitions_it->size() - num_after_nodes;
505  if (num_after_nodes >= m_lc_params.LC_min_remote_nodes &&
506  num_before_nodes >= m_lc_params.LC_min_remote_nodes)
507  { // at least X LC nodes
509  "Found potential loop closures:"
510  << endl
511  << "\tPartitionID: " << partitionID << endl
512  << "\tPartition: "
514  *partitions_it)
515  .c_str()
516  << endl
517  << "\t" << prev_nodeID << " ==> " << curr_nodeID << endl
518  << "\tNumber of LC nodes: " << num_after_nodes);
519  partitions_for_LC->push_back(*partitions_it);
520  break; // no need to check the rest of the nodes in this
521  // partition
522  }
523  }
524 
525  // update the previous node
526  prev_nodeID = curr_nodeID;
527  }
529  "Successfully checked partition: " << partitionID);
530  }
531 
532  this->m_time_logger.leave("LoopClosureEvaluation");
533  MRPT_END;
534 }
535 
536 template <class GRAPH_T>
538  const partitions_t& partitions)
539 {
540  MRPT_START;
541  using namespace mrpt;
542  using namespace mrpt::graphslam::detail;
543  using namespace mrpt::math;
544  using namespace std;
545  this->m_time_logger.enter("LoopClosureEvaluation");
546 
547  if (partitions.size() == 0) return;
548 
550  "Evaluating partitions for loop closures...\n%s\n",
551  this->header_sep.c_str());
552 
553  // for each partition to be evaulated...
554  for (partitions_t::const_iterator p_it = partitions.begin();
555  p_it != partitions.end(); ++p_it)
556  {
557  std::vector<uint32_t> partition(*p_it);
558 
559  // split the partition to groups
560  std::vector<uint32_t> groupA, groupB;
561  this->splitPartitionToGroups(
562  partition, &groupA, &groupB,
563  /*max_nodes_in_group=*/5);
564 
565  // generate hypotheses pool
566  hypotsp_t hypots_pool;
567  this->generateHypotsPool(groupA, groupB, &hypots_pool);
568 
569  // compute the pair-wise consistency matrix
570  CMatrixDouble consist_matrix(hypots_pool.size(), hypots_pool.size());
571  this->generatePWConsistenciesMatrix(
572  groupA, groupB, hypots_pool, &consist_matrix);
573 
574  // evaluate resulting matrix - fill valid hypotheses
575  hypotsp_t valid_hypots;
576  this->evalPWConsistenciesMatrix(
577  consist_matrix, hypots_pool, &valid_hypots);
578 
579  // registering the indicated/valid hypotheses
580  if (valid_hypots.size())
581  {
582  MRPT_LOG_WARN_STREAM("Registering Hypotheses...");
583  for (typename hypotsp_t::iterator it = valid_hypots.begin();
584  it != valid_hypots.end(); ++it)
585  {
586  this->registerHypothesis(**it);
587  }
588  }
589  // delete all hypotheses - generated in the heap...
590  MRPT_LOG_DEBUG_STREAM("Deleting the generated hypotheses pool...");
591  for (typename hypotsp_t::iterator it = hypots_pool.begin();
592  it != hypots_pool.end(); ++it)
593  {
594  delete *it;
595  }
596  } // for each partition
597 
598  MRPT_LOG_DEBUG_STREAM("\n" << this->header_sep);
599  this->m_time_logger.leave("LoopClosureEvaluation");
600 
601  MRPT_END;
602 }
603 
604 template <class GRAPH_T>
606  const mrpt::math::CMatrixDouble& consist_matrix,
607  const hypotsp_t& hypots_pool, hypotsp_t* valid_hypots)
608 {
609  MRPT_START;
610  using namespace std;
611  using namespace mrpt::math;
612 
613  ASSERTDEB_(valid_hypots);
614  valid_hypots->clear();
615 
616  // evaluate the pair-wise consistency matrix
617  // compute dominant eigenvector
619  bool valid_lambda_ratio = this->computeDominantEigenVector(
620  consist_matrix, &u,
621  /*use_power_method=*/false);
622  if (!valid_lambda_ratio) return;
623 
624  // cout << "Dominant eigenvector: " << u.transpose() << endl;
625 
626  // discretize the indicator vector - maximize the dot product of
627  // w_unit .* u
628  ASSERTDEB_(u.size());
629  dynamic_vector<double> w(u.size(), 0); // discretized indicator vector
630  double dot_product = 0;
631  for (int i = 0; i != w.size(); ++i)
632  {
633  // stream for debugging reasons
634  stringstream ss;
635 
636  // make the necessary change and see if the dot product increases
637  w(i) = 1;
638  double potential_dot_product =
639  ((w.transpose() * u) / w.squaredNorm()).value();
640  ss << mrpt::format(
641  "current: %f | potential_dot_product: %f", dot_product,
642  potential_dot_product);
643  if (potential_dot_product > dot_product)
644  {
645  ss << " ==> ACCEPT";
646  dot_product = potential_dot_product;
647  }
648  else
649  {
650  ss << " ==> REJECT";
651  w(i) = 0; // revert the change
652  }
653  ss << endl;
654  // MRPT_LOG_DEBUG_STREAM(ss.str());
655  }
656  cout << "Outcome of discretization: " << w.transpose() << endl;
657  // mrpt::system::pause();
658 
659  // Current hypothesis is to be registered.
660  if (!w.isZero())
661  {
662  for (int wi = 0; wi != w.size(); ++wi)
663  {
664  if (w(wi) == 1)
665  {
666  // search through the potential hypotheses, find the one with
667  // the
668  // correct ID and register it.
669  valid_hypots->push_back(this->findHypotByID(hypots_pool, wi));
670  }
671  }
672  }
673  // mrpt::system::pause();
674 
675  MRPT_END;
676 }
677 
678 template <class GRAPH_T>
680  std::vector<uint32_t>& partition, std::vector<uint32_t>* groupA,
681  std::vector<uint32_t>* groupB, int max_nodes_in_group /*=5*/)
682 {
683  MRPT_START;
684 
685  using namespace mrpt;
686  using namespace mrpt::math;
687  using mrpt::graphs::TNodeID;
688 
689  // assertions
690  ASSERTDEBMSG_(groupA, "Pointer to groupA is not valid");
691  ASSERTDEBMSG_(groupB, "Pointer to groupB is not valid");
693  max_nodes_in_group == -1 || max_nodes_in_group > 0,
694  format(
695  "Value %d not permitted for max_nodes_in_group"
696  "Either use a positive integer, "
697  "or -1 for non-restrictive partition size",
698  max_nodes_in_group));
699 
700  // find a large difference between successive nodeIDs - that's where the cut
701  // is going to be
702  TNodeID prev_nodeID = 0;
703  size_t index_to_split = 1;
704  for (std::vector<uint32_t>::const_iterator it = partition.begin() + 1;
705  it != partition.end(); ++it, ++index_to_split)
706  {
707  TNodeID curr_nodeID = *it;
708 
709  if ((curr_nodeID - prev_nodeID) > m_lc_params.LC_min_nodeid_diff)
710  {
711  break;
712  }
713  // update the last nodeID
714  prev_nodeID = curr_nodeID;
715  }
716  ASSERTDEB_(partition.size() > index_to_split);
717 
718  // Fill the groups
719  *groupA = std::vector<uint32_t>(
720  partition.begin(), partition.begin() + index_to_split);
721  *groupB = std::vector<uint32_t>(
722  partition.begin() + index_to_split, partition.end());
723  // limit the number of nodes in each group
724  if (max_nodes_in_group != -1)
725  {
726  // groupA
727  if (groupA->size() > static_cast<size_t>(max_nodes_in_group))
728  {
729  *groupA = std::vector<uint32_t>(
730  groupA->begin(), groupA->begin() + max_nodes_in_group);
731  }
732  // groupB
733  if (groupB->size() > static_cast<size_t>(max_nodes_in_group))
734  {
735  *groupB = std::vector<uint32_t>(
736  groupB->end() - max_nodes_in_group, groupB->end());
737  }
738  }
739 
740  // mrpt::system::pause();
741  MRPT_END;
742 }
743 
744 template <class GRAPH_T>
746  const std::vector<uint32_t>& groupA, const std::vector<uint32_t>& groupB,
747  hypotsp_t* generated_hypots,
748  const TGenerateHypotsPoolAdParams* ad_params /*=NULL*/)
749 {
750  MRPT_START;
751  using namespace mrpt;
752  using namespace mrpt::containers;
753 
755  generated_hypots,
756  "generateHypotsPool: Given hypotsp_t pointer is invalid.");
757  generated_hypots->clear();
758 
759  MRPT_LOG_DEBUG_STREAM("Generating hypotheses for groups: " << endl);
761  "- groupA:\t" << getSTLContainerAsString(groupA)
762  << " - size: " << groupA.size() << endl);
764  "- groupB:\t" << getSTLContainerAsString(groupB)
765  << " - size: " << groupB.size() << endl);
766 
767  // verify that the number of laserScans is the same as the number of poses
768  // if
769  // the TGenerateHyptsPoolAdParams is used
770  // formulate into function and pass std::vector<uint32_t> and
771  // ad_params->group
772  // TODO
773  if (ad_params)
774  {
776  ad_params->groupA_params;
777  if (params.size())
778  {
779  size_t nodes_count = groupA.size();
780 
781  // map should have same size
783  nodes_count == params.size(),
784  format(
785  "Size mismatch between nodeIDs in group [%lu]"
786  " and corresponding properties map [%lu]",
787  nodes_count, params.size()));
788  }
789  }
790 
791  // use a hypothesis ID with which the consistency matrix will then be
792  // formed
793  int hypot_counter = 0;
794  int invalid_hypots = 0; // just for keeping track of them.
795  {
796  // iterate over all the nodes in both groups
798  b_it = groupB.begin();
799  b_it != groupB.end(); ++b_it)
800  {
802  a_it = groupA.begin();
803  a_it != groupA.end(); ++a_it)
804  {
805  // by default hypotheses will direct bi => ai; If the hypothesis
806  // is
807  // traversed the opposite way, take the opposite of the
808  // constraint
809  hypot_t* hypot = new hypot_t;
810  hypot->from = *b_it;
811  hypot->to = *a_it;
812  hypot->id = hypot_counter++;
813 
814  // [from] *b_it ====[edge]===> [to] *a_it
815 
816  // Fetch and set the pose and LaserScan of from, to nodeIDs
817  //
818  // even if icp_ad_params NULL, it will be handled appropriately
819  // by the
820  // getICPEdge fun.
821  // bool from_success, to_success;
822  TGetICPEdgeAdParams* icp_ad_params = NULL;
823  if (ad_params)
824  {
825  icp_ad_params = new TGetICPEdgeAdParams;
826  // from_success = fillNodePropsFromGroupParams(
827  fillNodePropsFromGroupParams(
828  *b_it, ad_params->groupB_params,
829  &icp_ad_params->from_params);
830  // to_success = fillNodePropsFromGroupParams(
831  fillNodePropsFromGroupParams(
832  *a_it, ad_params->groupA_params,
833  &icp_ad_params->to_params);
834 
835  // MRPT_LOG_DEBUG_STREAM(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
836  // MRPT_LOG_DEBUG_STREAM("From nodeID (other): " << *b_it);
837  // MRPT_LOG_DEBUG_STREAM("From params (other): "
838  //<< icp_ad_params->from_params.getAsString());
839  // MRPT_LOG_DEBUG_STREAM("from_success: " << (from_success?
840  // "TRUE" : "FALSE"));
841  // MRPT_LOG_DEBUG_STREAM("**********");
842  // MRPT_LOG_DEBUG_STREAM("To nodeID (own) : " << *a_it);
843  // MRPT_LOG_DEBUG_STREAM("To params (own) : "
844  //<< icp_ad_params->to_params.getAsString());
845  // MRPT_LOG_DEBUG_STREAM("to_success: " << (to_success?
846  // "TRUE" : "FALSE"));
847  // MRPT_LOG_DEBUG_STREAM("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
848  }
849 
850  // fetch the ICP constraint bi => ai
852  constraint_t edge;
853  bool found_edge = this->getICPEdge(
854  *b_it, *a_it, &edge, &icp_info, icp_ad_params);
855 
856  hypot->setEdge(edge);
857  hypot->goodness =
858  icp_info.goodness; // goodness related to the edge
859 
860  // Check if invalid
861  //
862  // Goodness Threshold
863  double goodness_thresh =
864  m_laser_params.goodness_threshold_win.getMedian() *
865  m_lc_icp_constraint_factor;
866  bool accept_goodness = icp_info.goodness > goodness_thresh;
868  "generateHypotsPool:\nCurr. Goodness: "
869  << icp_info.goodness << "|\t Threshold: " << goodness_thresh
870  << " => " << (accept_goodness ? "ACCEPT" : "REJECT")
871  << endl);
872 
873  if (!found_edge || !accept_goodness)
874  {
875  hypot->is_valid = false;
876  invalid_hypots++;
877  }
878  generated_hypots->push_back(hypot);
879  MRPT_LOG_DEBUG_STREAM(hypot->getAsString());
880 
881  // delete pointer to getICPEdge additional parameters if they
882  // were
883  // initialized
884  delete icp_ad_params;
885  }
886  }
888  "Generated pool of hypotheses...\tsize = "
889  << generated_hypots->size()
890  << "\tinvalid hypotheses: " << invalid_hypots);
891  }
892  // mrpt::system::pause();
893 
894  MRPT_END;
895 } // end of generateHypotsPool
896 
897 template <class GRAPH_T>
899  const mrpt::math::CMatrixDouble& consist_matrix,
900  mrpt::math::dynamic_vector<double>* eigvec, bool use_power_method /*=true*/)
901 {
902  MRPT_START;
903  using namespace mrpt;
904  using namespace mrpt::math;
905  using namespace std;
906  ASSERTDEB_(eigvec);
907 
908  this->m_time_logger.enter("DominantEigenvectorComputation");
909 
910  double lambda1, lambda2; // eigenvalues to use
911  bool is_valid_lambda_ratio = false;
912 
913  if (use_power_method)
914  {
916  "\nPower method for computing the first two "
917  "eigenvectors/eigenvalues hasn't been implemented yet\n");
918  }
919  else
920  { // call to eigenVectors method
921  CMatrixDouble eigvecs, eigvals;
922  consist_matrix.eigenVectors(eigvecs, eigvals);
923 
924  // assert that the eivenvectors, eigenvalues, consistency matrix are of
925  // the
926  // same size
928  eigvecs.size() == eigvals.size() &&
929  consist_matrix.size() == eigvals.size(),
930  mrpt::format(
931  "Size of eigvecs \"%lu\","
932  "eigvalues \"%lu\","
933  "consist_matrix \"%lu\" don't match",
934  static_cast<unsigned long>(eigvecs.size()),
935  static_cast<unsigned long>(eigvals.size()),
936  static_cast<unsigned long>(consist_matrix.size())));
937 
938  eigvecs.extractCol(eigvecs.cols() - 1, *eigvec);
939  lambda1 = eigvals(eigvals.rows() - 1, eigvals.cols() - 1);
940  lambda2 = eigvals(eigvals.rows() - 2, eigvals.cols() - 2);
941  }
942 
943  // I don't care about the sign of the eigenvector element
944  for (int i = 0; i != eigvec->size(); ++i)
945  {
946  (*eigvec)(i) = abs((*eigvec)(i));
947  }
948 
949  // check the ratio of the two eigenvalues - reject hypotheses set if ratio
950  // smaller than threshold
951  if (approximatelyEqual(0.0, lambda2, /**limit = */ 0.00001))
952  {
954  "Bad lambda2 value: "
955  << lambda2 << " => Skipping current evaluation." << endl);
956  return false;
957  }
958  double curr_lambda_ratio = lambda1 / lambda2;
960  "lambda1 = " << lambda1 << " | lambda2 = " << lambda2
961  << "| ratio = " << curr_lambda_ratio << endl);
962 
963  is_valid_lambda_ratio =
964  (curr_lambda_ratio > m_lc_params.LC_eigenvalues_ratio_thresh);
965 
966  this->m_time_logger.leave("DominantEigenvectorComputation");
967  return is_valid_lambda_ratio;
968 
969  MRPT_END;
970 } // end of computeDominantEigenVector
971 
972 template <class GRAPH_T>
974  const std::vector<uint32_t>& groupA, const std::vector<uint32_t>& groupB,
975  const hypotsp_t& hypots_pool, mrpt::math::CMatrixDouble* consist_matrix,
976  const paths_t* groupA_opt_paths /*=NULL*/,
977  const paths_t* groupB_opt_paths /*=NULL*/)
978 {
979  MRPT_START;
980 
981  using namespace mrpt;
982  using namespace mrpt::math;
983  using namespace mrpt::containers;
984  using namespace std;
985  using mrpt::graphs::TNodeID;
986 
988  consist_matrix, "Invalid pointer to the Consistency matrix is given");
990  static_cast<unsigned long>(consist_matrix->rows()) ==
991  hypots_pool.size() &&
992  static_cast<unsigned long>(consist_matrix->rows()) ==
993  hypots_pool.size(),
994  "Consistency matrix dimensions aren't equal to the hypotheses pool "
995  "size");
996 
998  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
999  << endl
1000  << "In generatePWConsistencyMatrix:\n"
1001  << "\tgroupA: " << getSTLContainerAsString(groupA) << endl
1002  << "\tgroupB: " << getSTLContainerAsString(groupB) << endl
1003  << "\tHypots pool Size: " << hypots_pool.size());
1004 
1005  // b1
1006  for (std::vector<uint32_t>::const_iterator b1_it = groupB.begin();
1007  b1_it != groupB.end(); ++b1_it)
1008  {
1009  TNodeID b1 = *b1_it;
1010 
1011  // b2
1012  for (std::vector<uint32_t>::const_iterator b2_it = b1_it + 1;
1013  b2_it != groupB.end(); ++b2_it)
1014  {
1015  TNodeID b2 = *b2_it;
1016 
1017  // a1
1018  for (std::vector<uint32_t>::const_iterator a1_it = groupA.begin();
1019  a1_it != groupA.end(); ++a1_it)
1020  {
1021  TNodeID a1 = *a1_it;
1022  hypot_t* hypot_b2_a1 =
1023  this->findHypotByEnds(hypots_pool, b2, a1);
1024  // MRPT_LOG_DEBUG_STREAM("hypot_b2_a1: " <<
1025  // hypot_b2_a1->getAsString());
1026 
1027  // a2
1028  for (std::vector<uint32_t>::const_iterator a2_it = a1_it + 1;
1029  a2_it != groupA.end(); ++a2_it)
1030  {
1031  TNodeID a2 = *a2_it;
1032  hypot_t* hypot_b1_a2 =
1033  this->findHypotByEnds(hypots_pool, b1, a2);
1034  // MRPT_LOG_DEBUG_STREAM("hypot_b1_a2: " <<
1035  // hypot_b1_a2->getAsString());
1036 
1037  double consistency;
1038 
1039  // compute consistency element
1040  if (hypot_b2_a1->is_valid && hypot_b1_a2->is_valid)
1041  {
1042  // extract vector of hypotheses that connect the given
1043  // nodes,
1044  // instead of passing the whole hypothesis pool.
1045  hypotsp_t extracted_hypots;
1046  extracted_hypots.push_back(hypot_b2_a1);
1047  extracted_hypots.push_back(hypot_b1_a2);
1048 
1049  paths_t* curr_opt_paths = NULL;
1050  if (groupA_opt_paths || groupB_opt_paths)
1051  { // fill curr_opt_paths
1052  curr_opt_paths = new paths_t();
1053  }
1054 
1055  // decide on additional optimal paths
1056  if (curr_opt_paths)
1057  {
1058  // groupA
1059  if (groupA_opt_paths)
1060  { // a1 -> a2 optimal path
1061  const path_t* p = this->findPathByEnds(
1062  *groupA_opt_paths, a1, a2,
1063  /*throw_exc=*/true);
1064  curr_opt_paths->push_back(*p);
1065  }
1066  else
1067  { // empty
1068  curr_opt_paths->push_back(path_t());
1069  }
1070 
1071  if (groupB_opt_paths)
1072  { // b1 -> b2 optimal path
1073  const path_t* p = this->findPathByEnds(
1074  *groupB_opt_paths, b1, b2,
1075  /*throw_exc=*/true);
1076  curr_opt_paths->push_back(*p);
1077  }
1078  else
1079  { // empty
1080  curr_opt_paths->push_back(path_t());
1081  }
1082  }
1083 
1084  consistency = this->generatePWConsistencyElement(
1085  a1, a2, b1, b2, extracted_hypots, curr_opt_paths);
1086 
1087  delete curr_opt_paths;
1088  }
1089  else
1090  { // null those that don't look good
1091  consistency = 0;
1092  }
1093 
1094  // MRPT_LOG_DEBUG_STREAM(
1095  //"Adding hypothesis consistency for nodeIDs: "
1096  //<< "[b1] " << b1 << ", [b2] -> " << b2
1097  //<< ", [a1] -> " << a1 << ", [a2] -> " << a2
1098  //<< " ==> " << consistency << endl);
1099 
1100  // fill the PW consistency matrix corresponding element -
1101  // symmetrical
1102  int id1 = hypot_b2_a1->id;
1103  int id2 = hypot_b1_a2->id;
1104 
1105  (*consist_matrix)(id1, id2) = consistency;
1106  (*consist_matrix)(id2, id1) = consistency;
1107 
1108  // MRPT_LOG_DEBUG_STREAM("id1 = " << id1 << "\t|"
1109  //<< "id2 = " << id2 << "\t|"
1110  //<< "consistency = " << consistency);
1111  }
1112  }
1113  }
1114  }
1115 
1116  // MRPT_LOG_WARN_STREAM("Consistency matrix:" << endl
1117  //<< this->header_sep << endl
1118  //<< *consist_matrix << endl);
1119 
1120  MRPT_END;
1121 } // end of generatePWConsistenciesMatrix
1122 
1123 template <class GRAPH_T>
1127  const hypotsp_t& hypots, const paths_t* opt_paths /*=NULL*/)
1128 {
1129  MRPT_START;
1130  using namespace std;
1131  using namespace mrpt;
1132  using namespace mrpt::math;
1133  using namespace mrpt::graphslam;
1134  using namespace mrpt::graphslam::detail;
1135 
1136  // MRPT_LOG_DEBUG_STREAM("In generatePWConsistencyElement.\n"
1137  //<< "\t[" << a1 << ", " << a2 << "]\n"
1138  //<< "\t[" << b1 << ", " << b2 << "]");
1139  // MRPT_LOG_DEBUG_STREAM("a1->a2 optimal path:"
1140  //<< (opt_paths && !opt_paths->begin()->isEmpty()?
1141  // opt_paths->begin()->getAsString() :
1142  //"NONE"));
1143  // MRPT_LOG_DEBUG_STREAM("b1->b2 optimal path: "
1144  //<< (opt_paths && !opt_paths->rbegin()->isEmpty()?
1145  // opt_paths->rbegin()->getAsString() :
1146  //"NONE"));
1147 
1148  // standard size assertions
1149  ASSERTDEB_(hypots.size() == 2);
1150  if (opt_paths)
1151  {
1152  ASSERTDEB_(opt_paths->size() == 2);
1153  }
1154 
1155  //
1156  // get the Dijkstra links
1157  //
1158  // a1 ==> a2
1159  const path_t* path_a1_a2;
1160  if (!opt_paths || opt_paths->begin()->isEmpty())
1161  {
1163  "Running dijkstra [a1] " << a1 << " => [a2] " << a2);
1164  execDijkstraProjection(/*starting_node=*/a1, /*ending_node=*/a2);
1165  path_a1_a2 = this->queryOptimalPath(a2);
1166  }
1167  else
1168  { // fetch the path from the opt_paths arg
1169  // TODO dubious practice
1170  path_a1_a2 = &(*opt_paths->begin());
1171  }
1172  ASSERTDEB_(path_a1_a2);
1173  path_a1_a2->assertIsBetweenNodeIDs(/*start=*/a1, /*end*/ a2);
1174 
1175  // b1 ==> b2
1176  const path_t* path_b1_b2;
1177  if (!opt_paths || opt_paths->rend()->isEmpty())
1178  {
1180  "Running djkstra [b1] " << b1 << " => [b2] " << b2);
1181  execDijkstraProjection(/*starting_node=*/b1, /*ending_node=*/b2);
1182  path_b1_b2 = this->queryOptimalPath(b2);
1183  }
1184  else
1185  { // fetch the path from the opt_paths arg
1186  path_b1_b2 = &(*opt_paths->rbegin());
1187  }
1188  ASSERTDEB_(path_b1_b2);
1189  path_b1_b2->assertIsBetweenNodeIDs(/*start=*/b1, /*end*/ b2);
1190  // get the edges of the hypotheses
1191  // by default hypotheses are stored bi => ai
1192  //
1193  // Backwards edge: a2=>b1
1194  hypot_t* hypot_b1_a2 = this->findHypotByEnds(hypots, b1, a2);
1195  // forward edge b2=>a1
1196  hypot_t* hypot_b2_a1 = this->findHypotByEnds(hypots, b2, a1);
1197 
1198  // Composition of Poses
1199  // Order : a1 ==> a2 ==> b1 ==> b2 ==> a1
1200  constraint_t res_transform(path_a1_a2->curr_pose_pdf);
1201  res_transform += hypot_b1_a2->getInverseEdge();
1202  res_transform += path_b1_b2->curr_pose_pdf;
1203  res_transform += hypot_b2_a1->getEdge();
1204 
1206  "\n-----------Resulting Transformation----------- Hypots: #"
1207  << hypot_b1_a2->id << ", #" << hypot_b2_a1->id << endl
1208  << "a1 --> a2 => b1 --> b2 => a1: " << a1 << " --> " << a2 << " => "
1209  << b1 << " --> " << b2 << " => " << a1 << endl
1210  << res_transform << endl
1211  << endl
1212  << "DIJKSTRA: " << a1 << " --> " << a2 << ": "
1213  << path_a1_a2->curr_pose_pdf << endl
1214  << "DIJKSTRA: " << b1 << " --> " << b2 << ": "
1215  << path_b1_b2->curr_pose_pdf << endl
1216  << "hypot_b1_a2(inv):\n"
1217  << hypot_b1_a2->getInverseEdge() << endl
1218  << "hypot_b2_a1:\n"
1219  << hypot_b2_a1->getEdge() << endl);
1220 
1221  // get the vector of the corresponding transformation - [x, y, phi] form
1223  res_transform.getMeanVal().getAsVector(T);
1224 
1225  // information matrix
1226  CMatrixDouble33 cov_mat;
1227  res_transform.getCovariance(cov_mat);
1228 
1229  // there has to be an error with the initial Olson formula - p.15.
1230  // There must be a minus in the exponent and the covariance matrix instead
1231  // of
1232  // the information matrix.
1233  double exponent = (-T.transpose() * cov_mat * T).value();
1234  double consistency_elem = exp(exponent);
1235 
1236  // cout << "T = " << endl << T << endl;
1237  // cout << "exponent = " << exponent << endl;
1238  // cout << "consistency_elem = " << consistency_elem << endl;
1239  // mrpt::system::pause();
1240 
1241  return consistency_elem;
1242  MRPT_END;
1243 } // end of generatePWConsistencyElement
1244 
1245 template <class GRAPH_T>
1248  const paths_t& vec_paths, const mrpt::graphs::TNodeID& src,
1249  const mrpt::graphs::TNodeID& dst, bool throw_exc /*=true*/)
1250 {
1251  using namespace mrpt;
1252 
1253  ASSERTDEB_(vec_paths.size());
1254  const path_t* res = NULL;
1255 
1256  for (typename paths_t::const_iterator cit = vec_paths.begin();
1257  cit != vec_paths.end(); ++cit)
1258  {
1259  if (cit->getSource() == src && cit->getDestination() == dst)
1260  {
1261  res = &(*cit);
1262  break;
1263  }
1264  }
1265  if (throw_exc && !res)
1266  {
1268  format(
1269  "Path for %lu => %lu is not found. Exiting...\n",
1270  static_cast<unsigned long>(src),
1271  static_cast<unsigned long>(dst)));
1272  }
1273 
1274  return res;
1275 }
1276 
1277 template <class GRAPH_T>
1280  const hypotsp_t& vec_hypots, const mrpt::graphs::TNodeID& from,
1281  const mrpt::graphs::TNodeID& to, bool throw_exc /*=true*/)
1282 {
1283  using namespace mrpt::graphslam::detail;
1284  using namespace std;
1285 
1286  for (typename hypotsp_t::const_iterator v_cit = vec_hypots.begin();
1287  v_cit != vec_hypots.end(); v_cit++)
1288  {
1289  if ((*v_cit)->hasEnds(from, to))
1290  {
1291  // cout << "findHypotByEnds: Found hypot " << from
1292  //<< " => " << to << " : " << (*v_cit)->getAsString() << endl;
1293  return *v_cit;
1294  }
1295  }
1296 
1297  // not found.
1298  if (throw_exc)
1299  {
1301  }
1302  else
1303  {
1304  return NULL;
1305  }
1306 } // end of findHypotByEnds
1307 
1308 template <class GRAPH_T>
1311  const hypotsp_t& vec_hypots, const size_t& id, bool throw_exc /*=true*/)
1312 {
1313  using namespace mrpt::graphslam::detail;
1314 
1315  for (typename hypotsp_t::const_iterator v_cit = vec_hypots.begin();
1316  v_cit != vec_hypots.end(); v_cit++)
1317  {
1318  if ((*v_cit)->id == id)
1319  {
1320  return *v_cit;
1321  }
1322  }
1323 
1324  // not found.
1325  if (throw_exc)
1326  {
1328  }
1329  else
1330  {
1331  return NULL;
1332  }
1333 }
1334 
1335 template <class GRAPH_T>
1337  mrpt::graphs::TNodeID starting_node /*=0*/,
1338  mrpt::graphs::TNodeID ending_node /*=INVALID_NODEID*/)
1339 {
1340  MRPT_START;
1341  using namespace std;
1342  using namespace mrpt;
1343  using namespace mrpt::math;
1344  using mrpt::graphs::TNodeID;
1345 
1346  // for the full algorithm see
1347  // - Recognizing places using spectrally clustered local matches - E.Olson,
1348  // p.6
1349 
1350  this->m_time_logger.enter("Dijkstra Projection");
1351  const std::string dijkstra_end =
1352  "----------- Done with Dijkstra Projection... ----------";
1353 
1354  // ending_node is either INVALID_NODEID or one of the already registered
1355  // nodeIDs
1356  ASSERTDEB_(
1357  ending_node == INVALID_NODEID ||
1358  (ending_node >= 0 && ending_node < this->m_graph->nodeCount()));
1359  ASSERTDEBMSG_(
1360  starting_node != ending_node, "Starting and Ending nodes coincede");
1361 
1362  // debugging message
1363  stringstream ss_debug("");
1364  ss_debug << "Executing Dijkstra Projection: " << starting_node << " => ";
1365  if (ending_node == INVALID_NODEID)
1366  {
1367  ss_debug << "..." << endl;
1368  }
1369  else
1370  {
1371  ss_debug << ending_node << endl;
1372  }
1373 
1374  if (this->m_graph->nodeCount() < m_dijkstra_node_count_thresh)
1375  {
1376  return;
1377  }
1378 
1379  // keep track of the nodes that I have visited
1380  std::vector<bool> visited_nodes(this->m_graph->nodeCount(), false);
1381  m_node_optimal_paths.clear();
1382 
1383  // get the neighbors of each node
1384  std::map<TNodeID, std::set<TNodeID>> neighbors_of;
1385  this->m_graph->getAdjacencyMatrix(neighbors_of);
1386 
1387  // initialize a pool of TUncertaintyPaths - draw the minimum-uncertainty
1388  // path during
1389  // execution
1390  std::set<path_t*> pool_of_paths;
1391  // get the edge to each one of the neighboring nodes of the starting node
1392  std::set<TNodeID> starting_node_neighbors(neighbors_of.at(starting_node));
1394  starting_node_neighbors.begin();
1395  n_it != starting_node_neighbors.end(); ++n_it)
1396  {
1397  path_t* path_between_neighbors = new path_t();
1398  this->getMinUncertaintyPath(
1399  starting_node, *n_it, path_between_neighbors);
1400 
1401  pool_of_paths.insert(path_between_neighbors);
1402  }
1403  // just visited the first node
1404  visited_nodes.at(starting_node) = true;
1405 
1406  //// TODO Remove these - >>>>>>>>>>>>>>>>>>>>
1407  //// printing the pool for verification
1408  // cout << "Pool of Paths: " << endl;
1409  // for (typename std::set<path_t*>::const_iterator it =
1410  // pool_of_paths.begin();
1411  // it != pool_of_paths.end(); ++it) {
1412  // printSTLContainer((*it)->nodes_traversed);
1413  //}
1414  // cout << "------ Done with the starting node ... ------" << endl;
1415  // int iters = 0;
1416  //// TODO Remove these - <<<<<<<<<<<<<<<<<<<<< vvvUNCOMMENT BELOW AS WELLvvv
1417 
1418  while (true)
1419  {
1420  // if there is at least one false, exit loop
1421  for (std::vector<bool>::const_iterator it = visited_nodes.begin();
1422  it != visited_nodes.end(); ++it)
1423  {
1424  if (!*it)
1425  {
1426  break;
1427  }
1428  }
1429 
1430  // if an ending nodeID has been specified, end the method when the path
1431  // to
1432  // it is found.
1433  if (ending_node != INVALID_NODEID)
1434  {
1435  if (visited_nodes.at(ending_node))
1436  {
1437  // MRPT_LOG_DEBUG_STREAM(dijkstra_end);
1438  this->m_time_logger.leave("Dijkstra Projection");
1439  return;
1440  }
1441  }
1442 
1443  path_t* optimal_path = this->popMinUncertaintyPath(&pool_of_paths);
1444  TNodeID dest = optimal_path->getDestination();
1445 
1446  //// TODO Remove these - >>>>>>>>>>>>>>>>>>>> ^^^UNCOMMENT ABOVE AS
1447  /// WELL^^^
1448  // cout << iters << " " << std::string(40, '>') << endl;
1449  // cout << "current path Destination: " << dest << endl;
1450  //// printing the pool for verification
1451  // cout << "Pool of Paths: " << endl;
1452  // for (typename std::set<path_t*>::const_iterator
1453  // it = pool_of_paths.begin();
1454  // it != pool_of_paths.end(); ++it) {
1455  // printSTLContainer((*it)->nodes_traversed);
1456  //}
1457  // cout << "Nodes visited: " << endl;
1458  // std::vector<int> tmp_vec;
1459  // for (int i = 0; i != visited_nodes.size(); ++i) {
1460  // tmp_vec.push_back(i);
1461  //}
1462  // printSTLContainer(tmp_vec); cout << endl; // indices of numbers
1463  // printSTLContainer(visited_nodes); // actual flags
1464  // cout << std::string(40, '<') << " " << iters++ << endl;
1465  // mrpt::system::pause();
1466  //// TODO Remove these - <<<<<<<<<<<<<<<<<<<<<
1467 
1468  if (!visited_nodes.at(dest))
1469  {
1470  m_node_optimal_paths[dest] = optimal_path;
1471  visited_nodes.at(dest) = true;
1472 
1473  // for all the edges leaving this node .. compose the transforms
1474  // with the
1475  // current pool of paths.
1476  this->addToPaths(
1477  &pool_of_paths, *optimal_path, neighbors_of.at(dest));
1478  }
1479  }
1480 
1481  // MRPT_LOG_DEBUG_STREAM(dijkstra_end);
1482  this->m_time_logger.leave("Dijkstra Projection");
1483  MRPT_END;
1484 }
1485 
1486 template <class GRAPH_T>
1488  std::set<path_t*>* pool_of_paths, const path_t& current_path,
1489  const std::set<mrpt::graphs::TNodeID>& neighbors) const
1490 {
1491  MRPT_START;
1492  using namespace std;
1493  using namespace mrpt::graphslam;
1494  using mrpt::graphs::TNodeID;
1495 
1496  TNodeID node_to_append_from = current_path.getDestination();
1497 
1498  // compose transforms for every neighbor of node_to_append_from *except*
1499  // for the link connecting node_to_append_from and the second to last node
1500  // in
1501  // the current_path
1502  TNodeID second_to_last_node = current_path.nodes_traversed.rbegin()[1];
1503  for (std::set<TNodeID>::const_iterator neigh_it = neighbors.begin();
1504  neigh_it != neighbors.end(); ++neigh_it)
1505  {
1506  if (*neigh_it == second_to_last_node) continue;
1507 
1508  // get the path between node_to_append_from, *node_it
1509  path_t path_between_nodes;
1510  this->getMinUncertaintyPath(
1511  node_to_append_from, *neigh_it, &path_between_nodes);
1512 
1513  // format the path to append
1514  path_t* path_to_append = new path_t();
1515  *path_to_append = current_path;
1516  *path_to_append += path_between_nodes;
1517 
1518  pool_of_paths->insert(path_to_append);
1519  }
1520 
1521  MRPT_END;
1522 }
1523 
1524 template <class GRAPH_T>
1526  GRAPH_T>::queryOptimalPath(const mrpt::graphs::TNodeID node) const
1527 {
1528  using namespace std;
1529 
1530  path_t* path = NULL;
1532  search = m_node_optimal_paths.find(node);
1533  if (search != m_node_optimal_paths.end())
1534  {
1535  path = search->second;
1536  }
1537 
1538  // MRPT_LOG_DEBUG_STREAM("Queried optimal path for nodeID: " << node
1539  //<< " ==> Path: " << (path? "Found" : "NOT Found"));
1540  return path;
1541 }
1542 
1543 template <class GRAPH_T>
1545  const mrpt::graphs::TNodeID from, const mrpt::graphs::TNodeID to,
1546  path_t* path_between_nodes) const
1547 {
1548  MRPT_START;
1549  using namespace mrpt::math;
1550  using namespace std;
1551 
1552  ASSERTDEBMSG_(
1553  this->m_graph->edgeExists(from, to) ||
1554  this->m_graph->edgeExists(to, from),
1555  mrpt::format(
1556  "\nEdge between the provided nodeIDs"
1557  "(%lu <-> %lu) does not exist\n",
1558  from, to));
1559  ASSERTDEB_(path_between_nodes);
1560 
1561  // cout << "getMinUncertaintyPath: " << from << " => " << to << endl;
1562 
1563  // don't add to the path_between_nodes, just fill it in afterwards
1564  path_between_nodes->clear();
1565 
1566  // iterate over all the edges, ignore the ones that are all 0s - find the
1567  // one that is with the lowest uncertainty
1568  double curr_determinant = 0;
1569  // forward edges from -> to
1570  std::pair<edges_citerator, edges_citerator> fwd_edges_pair =
1571  this->m_graph->getEdges(from, to);
1572 
1573  // cout << "Forward edges: " << endl;
1574  // for (edges_citerator e_it = fwd_edges_pair.first; e_it !=
1575  // fwd_edges_pair.second;
1576  //++e_it) {
1577  // cout << e_it->second << endl;
1578  //}
1579 
1580  for (edges_citerator edges_it = fwd_edges_pair.first;
1581  edges_it != fwd_edges_pair.second; ++edges_it)
1582  {
1583  // operate on a temporary object instead of the real edge - otherwise
1584  // function is non-const
1585  constraint_t curr_edge;
1586  curr_edge.copyFrom(edges_it->second);
1587 
1588  // is it all 0s?
1589  CMatrixDouble33 inf_mat;
1590  curr_edge.getInformationMatrix(inf_mat);
1591 
1592  if (inf_mat == CMatrixDouble33() || std::isnan(inf_mat(0, 0)))
1593  {
1594  inf_mat.unit();
1595  curr_edge.cov_inv = inf_mat;
1596  }
1597 
1598  path_t curr_path(from); // set the starting node
1599  curr_path.addToPath(to, curr_edge);
1600 
1601  // update the resulting path_between_nodes if its determinant is smaller
1602  // than the determinant of the current path_between_nodes
1603  if (curr_determinant < curr_path.getDeterminant())
1604  {
1605  curr_determinant = curr_path.getDeterminant();
1606  *path_between_nodes = curr_path;
1607  }
1608  }
1609  // backwards edges to -> from
1610  std::pair<edges_citerator, edges_citerator> bwd_edges_pair =
1611  this->m_graph->getEdges(to, from);
1612 
1613  // cout << "Backwards edges: " << endl;
1614  // for (edges_citerator e_it = bwd_edges_pair.first; e_it !=
1615  // bwd_edges_pair.second;
1616  //++e_it) {
1617  // cout << e_it->second << endl;
1618  //}
1619 
1620  for (edges_citerator edges_it = bwd_edges_pair.first;
1621  edges_it != bwd_edges_pair.second; ++edges_it)
1622  {
1623  // operate on a temporary object instead of the real edge - otherwise
1624  // function is non-const
1625  constraint_t curr_edge;
1626  (edges_it->second).inverse(curr_edge);
1627 
1628  // is it all 0s?
1629  CMatrixDouble33 inf_mat;
1630  curr_edge.getInformationMatrix(inf_mat);
1631 
1632  if (inf_mat == CMatrixDouble33() || std::isnan(inf_mat(0, 0)))
1633  {
1634  inf_mat.unit();
1635  curr_edge.cov_inv = inf_mat;
1636  }
1637 
1638  path_t curr_path(from); // set the starting node
1639  curr_path.addToPath(to, curr_edge);
1640 
1641  // update the resulting path_between_nodes if its determinant is smaller
1642  // than the determinant of the current path_between_nodes
1643  if (curr_determinant < curr_path.getDeterminant())
1644  {
1645  curr_determinant = curr_path.getDeterminant();
1646  *path_between_nodes = curr_path;
1647  }
1648  }
1649 
1650  MRPT_END;
1651 }
1652 
1653 template <class GRAPH_T>
1655  typename std::set<path_t*>* pool_of_paths) const
1656 {
1657  MRPT_START;
1658  using namespace std;
1659 
1660  // cout << "Determinants: ";
1661  path_t* optimal_path = NULL;
1662  double curr_determinant = 0;
1663  for (typename std::set<path_t*>::const_iterator it = pool_of_paths->begin();
1664  it != pool_of_paths->end(); ++it)
1665  {
1666  // cout << (*it)->getDeterminant() << ", ";
1667 
1668  // keep the largest determinant - we are in INFORMATION form.
1669  if (curr_determinant < (*it)->getDeterminant())
1670  {
1671  curr_determinant = (*it)->getDeterminant();
1672  optimal_path = *it;
1673  }
1674  }
1675 
1676  ASSERTDEB_(optimal_path);
1677  pool_of_paths->erase(optimal_path); // erase it from the pool
1678 
1679  return optimal_path;
1680  MRPT_END;
1681 }
1682 
1683 template <class GRAPH_T>
1685  const mrpt::graphs::TNodeID& from, const mrpt::graphs::TNodeID& to,
1686  const constraint_t& rel_edge)
1687 {
1688  MRPT_START;
1689 
1690  using namespace std;
1691  using namespace mrpt::math;
1692 
1693  // mean difference
1694  pose_t initial_estim =
1695  this->m_graph->nodes.at(to) - this->m_graph->nodes.at(from);
1696  dynamic_vector<double> mean_diff;
1697  (rel_edge.getMeanVal() - initial_estim).getAsVector(mean_diff);
1698 
1699  // covariance matrix
1700  CMatrixDouble33 cov_mat;
1701  rel_edge.getCovariance(cov_mat);
1702 
1703  // mahalanobis distance computation
1704  double mahal_distance =
1705  mrpt::math::mahalanobisDistance2(mean_diff, cov_mat);
1706  bool mahal_distance_null = std::isnan(mahal_distance);
1707  if (!mahal_distance_null)
1708  {
1709  m_laser_params.mahal_distance_ICP_odom_win.addNewMeasurement(
1710  mahal_distance);
1711  }
1712 
1713  // double threshold = m_laser_params.mahal_distance_ICP_odom_win.getMean() +
1714  // 2*m_laser_params.mahal_distance_ICP_odom_win.getStdDev();
1715  double threshold =
1716  m_laser_params.mahal_distance_ICP_odom_win.getMedian() * 4;
1717  bool accept_edge =
1718  (threshold >= mahal_distance && !mahal_distance_null) ? true : false;
1719 
1720  // cout << "Suggested Edge: " << rel_edge.getMeanVal() << "|\tInitial
1721  // Estim.: " << initial_estim
1722  //<< "|\tMahalanobis Dist: " << mahal_distance << "|\tThresh.: " <<
1723  // threshold
1724  //<< " => " << (accept_edge? "ACCEPT": "REJECT") << endl;
1725 
1726  return accept_edge;
1727  MRPT_END;
1728 }
1729 
1730 template <class GRAPH_T>
1732 {
1733  // MRPT_LOG_DEBUG_STREAM("Registering hypothesis: " <<
1734  // hypot.getAsString([>oneline=<] true));
1735  this->registerNewEdge(hypot.from, hypot.to, hypot.getEdge());
1736 }
1737 
1738 template <class GRAPH_T>
1740  const mrpt::graphs::TNodeID& from, const mrpt::graphs::TNodeID& to,
1741  const constraint_t& rel_edge)
1742 {
1743  MRPT_START;
1744  using namespace mrpt::math;
1745  using namespace std;
1746  parent_t::registerNewEdge(from, to, rel_edge);
1747 
1748  // keep track of the registered edges...
1749  m_edge_types_to_nums["ICP2D"]++;
1750 
1751  // keep track of the registered edges...
1752  if (absDiff(to, from) > m_lc_params.LC_min_nodeid_diff)
1753  {
1754  m_edge_types_to_nums["LC"]++;
1755  this->m_just_inserted_lc = true;
1756  MRPT_LOG_INFO("\tLoop Closure edge!");
1757  }
1758  else
1759  {
1760  this->m_just_inserted_lc = false;
1761  }
1762 
1763  // actuall registration
1764  this->m_graph->insertEdge(from, to, rel_edge);
1765 
1766  MRPT_END;
1767 }
1768 
1769 template <class GRAPH_T>
1771  mrpt::graphslam::CWindowManager* win_manager)
1772 {
1773  // call parent_t class method
1774  parent_t::setWindowManagerPtr(win_manager);
1775 
1776  // may still be null..
1777  if (this->m_win_manager)
1778  {
1779  if (this->m_win_observer)
1780  {
1781  this->m_win_observer->registerKeystroke(
1782  m_laser_params.keystroke_laser_scans,
1783  "Toggle LaserScans Visualization");
1784  this->m_win_observer->registerKeystroke(
1785  m_lc_params.keystroke_map_partitions,
1786  "Toggle Map Partitions Visualization");
1787  }
1788 
1790  "Fetched the window manager, window observer successfully.");
1791  }
1792 }
1793 template <class GRAPH_T>
1795  const std::map<std::string, bool>& events_occurred)
1796 {
1797  MRPT_START;
1798  parent_t::notifyOfWindowEvents(events_occurred);
1799 
1800  // laser scans
1801  if (events_occurred.at(m_laser_params.keystroke_laser_scans))
1802  {
1803  this->toggleLaserScansVisualization();
1804  }
1805  // map partitions
1806  if (events_occurred.at(m_lc_params.keystroke_map_partitions))
1807  {
1808  this->toggleMapPartitionsVisualization();
1809  }
1810 
1811  MRPT_END;
1812 }
1813 
1814 template <class GRAPH_T>
1816 {
1817  using namespace mrpt;
1818  using namespace mrpt::gui;
1819  using namespace mrpt::math;
1820  using namespace mrpt::opengl;
1821 
1822  // textmessage - display the number of partitions
1823  this->m_win_manager->assignTextMessageParameters(
1824  /* offset_y* = */ &m_lc_params.offset_y_map_partitions,
1825  /* text_index* = */ &m_lc_params.text_index_map_partitions);
1826 
1827  // just add an empty CSetOfObjects in the scene - going to populate it later
1828  CSetOfObjects::Ptr map_partitions_obj =
1829  mrpt::make_aligned_shared<CSetOfObjects>();
1830  map_partitions_obj->setName("map_partitions");
1831 
1832  COpenGLScene::Ptr& scene = this->m_win->get3DSceneAndLock();
1833  scene->insert(map_partitions_obj);
1834  this->m_win->unlockAccess3DScene();
1835  this->m_win->forceRepaint();
1836 }
1837 
1838 template <class GRAPH_T>
1840 {
1841  using namespace mrpt;
1842  using namespace mrpt::gui;
1843  using namespace mrpt::math;
1844  using namespace mrpt::opengl;
1845  using namespace mrpt::poses;
1846 
1847  // textmessage
1848  std::stringstream title;
1849  title << "# Partitions: " << m_curr_partitions.size();
1850  this->m_win_manager->addTextMessage(
1851  5, -m_lc_params.offset_y_map_partitions, title.str(),
1852  mrpt::img::TColorf(m_lc_params.balloon_std_color),
1853  /* unique_index = */ m_lc_params.text_index_map_partitions);
1854 
1855  // update the partitioning visualization
1856  COpenGLScene::Ptr& scene = this->m_win->get3DSceneAndLock();
1857 
1858  // fetch the partitions CSetOfObjects
1859  CSetOfObjects::Ptr map_partitions_obj;
1860  {
1861  CRenderizable::Ptr obj = scene->getByName("map_partitions");
1862  // do not check for null ptr - must be properly created in the init*
1863  // method
1864  map_partitions_obj = std::dynamic_pointer_cast<CSetOfObjects>(obj);
1865  }
1866 
1867  int partitionID = 0;
1868  bool partition_contains_last_node = false;
1869  bool found_last_node =
1870  false; // last node must exist in one partition at all cost TODO
1872  "Searching for the partition of the last nodeID: "
1873  << (this->m_graph->nodeCount() - 1));
1874 
1875  for (partitions_t::const_iterator p_it = m_curr_partitions.begin();
1876  p_it != m_curr_partitions.end(); ++p_it, ++partitionID)
1877  {
1878  // MRPT_LOG_DEBUG_STREAM("Working on Partition #" << partitionID);
1879  std::vector<uint32_t> nodes_list = *p_it;
1880 
1881  // finding the partition in which the last node is in
1882  if (std::find(
1883  nodes_list.begin(), nodes_list.end(),
1884  this->m_graph->nodeCount() - 1) != nodes_list.end())
1885  {
1886  partition_contains_last_node = true;
1887 
1888  found_last_node = true;
1889  }
1890  else
1891  {
1892  partition_contains_last_node = false;
1893  }
1894 
1895  // fetch the current partition object if it exists - create otherwise
1896  std::string partition_obj_name =
1897  mrpt::format("partition_%d", partitionID);
1898  std::string balloon_obj_name = mrpt::format("#%d", partitionID);
1899 
1901  map_partitions_obj->getByName(partition_obj_name);
1902  CSetOfObjects::Ptr curr_partition_obj;
1903  if (obj)
1904  {
1905  // MRPT_LOG_DEBUG_STREAM(
1906  //"\tFetching CSetOfObjects partition object for partition #" <<
1907  // partitionID);
1908  curr_partition_obj = std::dynamic_pointer_cast<CSetOfObjects>(obj);
1909  if (m_lc_params.LC_check_curr_partition_only)
1910  { // make all but the last partition invisible
1911  curr_partition_obj->setVisibility(partition_contains_last_node);
1912  }
1913  }
1914  else
1915  {
1917  "\tCreating a new CSetOfObjects partition object for partition "
1918  "#"
1919  << partitionID);
1920  curr_partition_obj = mrpt::make_aligned_shared<CSetOfObjects>();
1921  curr_partition_obj->setName(partition_obj_name);
1922  if (m_lc_params.LC_check_curr_partition_only)
1923  {
1924  // make all but the last partition invisible
1925  curr_partition_obj->setVisibility(partition_contains_last_node);
1926  }
1927 
1928  // MRPT_LOG_DEBUG_STREAM("\t\tCreating a new CSphere balloon
1929  // object");
1930  CSphere::Ptr balloon_obj = mrpt::make_aligned_shared<CSphere>();
1931  balloon_obj->setName(balloon_obj_name);
1932  balloon_obj->setRadius(m_lc_params.balloon_radius);
1933  balloon_obj->setColor_u8(m_lc_params.balloon_std_color);
1934  balloon_obj->enableShowName();
1935 
1936  curr_partition_obj->insert(balloon_obj);
1937 
1938  // set of lines connecting the graph nodes to the balloon
1939  // MRPT_LOG_DEBUG_STREAM(
1940  //"\t\tCreating set of lines that will connect to the Balloon");
1941  CSetOfLines::Ptr connecting_lines_obj =
1942  mrpt::make_aligned_shared<CSetOfLines>();
1943  connecting_lines_obj->setName("connecting_lines");
1944  connecting_lines_obj->setColor_u8(
1945  m_lc_params.connecting_lines_color);
1946  connecting_lines_obj->setLineWidth(0.1f);
1947 
1948  curr_partition_obj->insert(connecting_lines_obj);
1949 
1950  // add the created CSetOfObjects to the total CSetOfObjects
1951  // responsible
1952  // for the map partitioning
1953  map_partitions_obj->insert(curr_partition_obj);
1954  // MRPT_LOG_DEBUG_STREAM("\tInserted new CSetOfObjects
1955  // successfully");
1956  }
1957  // up to now the CSetOfObjects exists and the balloon inside it as
1958  // well..
1959 
1960  std::pair<double, double> centroid_coords;
1961  this->computeCentroidOfNodesVector(nodes_list, &centroid_coords);
1962 
1963  TPoint3D balloon_location(
1964  centroid_coords.first, centroid_coords.second,
1965  m_lc_params.balloon_elevation);
1966 
1967  // MRPT_LOG_DEBUG_STREAM("\tUpdating the balloon position");
1968  // set the balloon properties
1969  CSphere::Ptr balloon_obj;
1970  {
1971  // place the partitions baloon at the centroid elevated by a fixed Z
1972  // value
1974  curr_partition_obj->getByName(balloon_obj_name);
1975  balloon_obj = std::dynamic_pointer_cast<CSphere>(obj);
1976  balloon_obj->setLocation(balloon_location);
1977  if (partition_contains_last_node)
1978  balloon_obj->setColor_u8(m_lc_params.balloon_curr_color);
1979  else
1980  balloon_obj->setColor_u8(m_lc_params.balloon_std_color);
1981  }
1982 
1983  // MRPT_LOG_DEBUG_STREAM("\tUpdating the lines connecting nodes to
1984  // balloon");
1985  // set the lines connecting the nodes of the partition to the partition
1986  // balloon - set it from scratch all the times since the node positions
1987  // tend to change according to the dijkstra position estimation
1988  CSetOfLines::Ptr connecting_lines_obj;
1989  {
1990  // place the partitions baloon at the centroid elevated by a fixed Z
1991  // value
1993  curr_partition_obj->getByName("connecting_lines");
1994  connecting_lines_obj = std::dynamic_pointer_cast<CSetOfLines>(obj);
1995 
1996  connecting_lines_obj->clear();
1997 
1998  for (std::vector<uint32_t>::const_iterator it = nodes_list.begin();
1999  it != nodes_list.end(); ++it)
2000  {
2001  CPose3D curr_pose(this->m_graph->nodes.at(*it));
2002  TPoint3D curr_node_location(curr_pose.asTPose());
2003 
2004  TSegment3D connecting_line(
2005  curr_node_location, balloon_location);
2006  connecting_lines_obj->appendLine(connecting_line);
2007  }
2008  }
2009  // MRPT_LOG_DEBUG_STREAM("Done working on partition #" << partitionID);
2010  }
2011 
2012  if (!found_last_node)
2013  {
2014  /// @todo Have some sorts of a string_view instead
2015  THROW_EXCEPTION("Last inserted nodeID was not found in any partition.");
2016  }
2017 
2018  // remove outdated partitions
2019  // these occur when more partitions existed during the previous
2020  // visualization
2021  // update, thus the partitions with higher ID than the maximum partitionID
2022  // would otherwise remain in the visual as zombie partitions
2023  size_t prev_size = m_last_partitions.size();
2024  size_t curr_size = m_curr_partitions.size();
2025  if (curr_size < prev_size)
2026  {
2027  MRPT_LOG_DEBUG_STREAM("Removing outdated partitions in visual");
2028  for (size_t partitionID = curr_size; partitionID != prev_size;
2029  ++partitionID)
2030  {
2031  MRPT_LOG_DEBUG_STREAM("\tRemoving partition " << partitionID);
2032  std::string partition_obj_name = mrpt::format(
2033  "partition_%lu", static_cast<unsigned long>(partitionID));
2034 
2036  map_partitions_obj->getByName(partition_obj_name);
2037  if (!obj)
2038  {
2039  THROW_EXCEPTION_FMT("Partition: %s was not found",
2040  partition_obj_name.c_str())
2041  }
2042  map_partitions_obj->removeObject(obj);
2043  }
2044  }
2045  MRPT_LOG_DEBUG_STREAM("Done working on the partitions visualization.");
2046 
2047  this->m_win->unlockAccess3DScene();
2048  this->m_win->forceRepaint();
2049 } // end of updateMapPartitionsVisualization
2050 
2051 template <class GRAPH_T>
2053 {
2054  MRPT_START;
2055  ASSERTDEBMSG_(this->m_win, "No CDisplayWindow3D* was provided");
2056  ASSERTDEBMSG_(this->m_win_manager, "No CWindowManager* was provided");
2057  using namespace mrpt::opengl;
2058 
2059  MRPT_LOG_INFO("Toggling map partitions visualization...");
2060  mrpt::opengl::COpenGLScene::Ptr scene = this->m_win->get3DSceneAndLock();
2061 
2062  if (m_lc_params.visualize_map_partitions)
2063  {
2065  scene->getByName("map_partitions");
2066  obj->setVisibility(!obj->isVisible());
2067  }
2068  else
2069  {
2070  this->dumpVisibilityErrorMsg("visualize_map_partitions");
2071  }
2072 
2073  this->m_win->unlockAccess3DScene();
2074  this->m_win->forceRepaint();
2075 
2076  MRPT_END;
2077 } // end of toggleMapPartitionsVisualization
2078 
2079 template <class GRAPH_T>
2081  const std::vector<uint32_t>& nodes_list,
2082  std::pair<double, double>* centroid_coords) const
2083 {
2084  MRPT_START;
2085 
2086  // get the poses and find the centroid so that we can place the baloon over
2087  // and at their center
2088  double centroid_x = 0;
2089  double centroid_y = 0;
2090  for (std::vector<uint32_t>::const_iterator node_it = nodes_list.begin();
2091  node_it != nodes_list.end(); ++node_it)
2092  {
2093  pose_t curr_node_pos = this->m_graph->nodes.at(*node_it);
2094  centroid_x += curr_node_pos.x();
2095  centroid_y += curr_node_pos.y();
2096  }
2097 
2098  // normalize by the size - assign to the given pair
2099  centroid_coords->first =
2100  centroid_x / static_cast<double>(nodes_list.size());
2101  centroid_coords->second =
2102  centroid_y / static_cast<double>(nodes_list.size());
2103 
2104  MRPT_END;
2105 } // end of computeCentroidOfNodesVector
2106 
2107 template <class GRAPH_T>
2109 {
2110  MRPT_START;
2111 
2112  // laser scan visualization
2113  if (m_laser_params.visualize_laser_scans)
2114  {
2116  this->m_win->get3DSceneAndLock();
2117 
2118  mrpt::opengl::CPlanarLaserScan::Ptr laser_scan_viz =
2119  mrpt::make_aligned_shared<mrpt::opengl::CPlanarLaserScan>();
2120  laser_scan_viz->enablePoints(true);
2121  laser_scan_viz->enableLine(true);
2122  laser_scan_viz->enableSurface(true);
2123  laser_scan_viz->setSurfaceColor(
2124  m_laser_params.laser_scans_color.R,
2125  m_laser_params.laser_scans_color.G,
2126  m_laser_params.laser_scans_color.B,
2127  m_laser_params.laser_scans_color.A);
2128 
2129  laser_scan_viz->setName("laser_scan_viz");
2130 
2131  scene->insert(laser_scan_viz);
2132  this->m_win->unlockAccess3DScene();
2133  this->m_win->forceRepaint();
2134  }
2135 
2136  MRPT_END;
2137 }
2138 
2139 template <class GRAPH_T>
2141 {
2142  MRPT_START;
2143 
2144  // update laser scan visual
2145  if (m_laser_params.visualize_laser_scans && m_last_laser_scan2D)
2146  {
2148  this->m_win->get3DSceneAndLock();
2149 
2151  scene->getByName("laser_scan_viz");
2152  mrpt::opengl::CPlanarLaserScan::Ptr laser_scan_viz =
2153  std::dynamic_pointer_cast<mrpt::opengl::CPlanarLaserScan>(obj);
2154  laser_scan_viz->setScan(*m_last_laser_scan2D);
2155 
2156  // set the pose of the laser scan
2157  const auto search =
2158  this->m_graph->nodes.find(this->m_graph->nodeCount() - 1);
2159  if (search != this->m_graph->nodes.end())
2160  {
2161  laser_scan_viz->setPose(search->second);
2162  // put the laser scan underneath the graph, so that you can still
2163  // visualize the loop closures with the nodes ahead
2164  laser_scan_viz->setPose(
2166  laser_scan_viz->getPoseX(), laser_scan_viz->getPoseY(),
2167  -0.15, mrpt::DEG2RAD(laser_scan_viz->getPoseYaw()),
2168  mrpt::DEG2RAD(laser_scan_viz->getPosePitch()),
2169  mrpt::DEG2RAD(laser_scan_viz->getPoseRoll())));
2170  }
2171 
2172  this->m_win->unlockAccess3DScene();
2173  this->m_win->forceRepaint();
2174  }
2175 
2176  MRPT_END;
2177 }
2178 
2179 template <class GRAPH_T>
2181 {
2182  MRPT_START;
2183  ASSERTDEBMSG_(this->m_win, "No CDisplayWindow3D* was provided");
2184  ASSERTDEBMSG_(this->m_win_manager, "No CWindowManager* was provided");
2185 
2186  MRPT_LOG_INFO("Toggling LaserScans visualization...");
2187 
2188  mrpt::opengl::COpenGLScene::Ptr scene = this->m_win->get3DSceneAndLock();
2189 
2190  if (m_laser_params.visualize_laser_scans)
2191  {
2193  scene->getByName("laser_scan_viz");
2194  obj->setVisibility(!obj->isVisible());
2195  }
2196  else
2197  {
2198  this->dumpVisibilityErrorMsg("visualize_laser_scans");
2199  }
2200 
2201  this->m_win->unlockAccess3DScene();
2202  this->m_win->forceRepaint();
2203 
2204  MRPT_END;
2205 }
2206 
2207 template <class GRAPH_T>
2209  std::map<std::string, int>* edge_types_to_num) const
2210 {
2211  MRPT_START;
2212  *edge_types_to_num = m_edge_types_to_nums;
2213  MRPT_END;
2214 }
2215 
2216 template <class GRAPH_T>
2218 {
2219  MRPT_START;
2220  parent_t::initializeVisuals();
2221  // MRPT_LOG_DEBUG_STREAM("Initializing visuals");
2222  this->m_time_logger.enter("Visuals");
2223 
2224  ASSERTDEBMSG_(
2225  m_laser_params.has_read_config,
2226  "Configuration parameters aren't loaded yet");
2227  if (m_laser_params.visualize_laser_scans)
2228  {
2229  this->initLaserScansVisualization();
2230  }
2231  if (m_lc_params.visualize_map_partitions)
2232  {
2233  this->initMapPartitionsVisualization();
2234  }
2235 
2236  if (m_visualize_curr_node_covariance)
2237  {
2238  this->initCurrCovarianceVisualization();
2239  }
2240 
2241  this->m_time_logger.leave("Visuals");
2242  MRPT_END;
2243 }
2244 template <class GRAPH_T>
2246 {
2247  MRPT_START;
2248  parent_t::updateVisuals();
2249  // MRPT_LOG_DEBUG_STREAM("Updating visuals");
2250  this->m_time_logger.enter("Visuals");
2251 
2252  if (m_laser_params.visualize_laser_scans)
2253  {
2254  this->updateLaserScansVisualization();
2255  }
2256  if (m_lc_params.visualize_map_partitions)
2257  {
2258  this->updateMapPartitionsVisualization();
2259  }
2260  if (m_visualize_curr_node_covariance)
2261  {
2262  this->updateCurrCovarianceVisualization();
2263  }
2264 
2265  this->m_time_logger.leave("Visuals");
2266  MRPT_END;
2267 }
2268 
2269 template <class GRAPH_T>
2271 {
2272  MRPT_START;
2273  using namespace std;
2274  using namespace mrpt::opengl;
2275 
2276  // text message for covariance ellipsis
2277  this->m_win_manager->assignTextMessageParameters(
2278  /* offset_y* = */ &m_offset_y_curr_node_covariance,
2279  /* text_index* = */ &m_text_index_curr_node_covariance);
2280 
2281  std::string title("Position uncertainty");
2282  this->m_win_manager->addTextMessage(
2283  5, -m_offset_y_curr_node_covariance, title,
2284  mrpt::img::TColorf(m_curr_node_covariance_color),
2285  /* unique_index = */ m_text_index_curr_node_covariance);
2286 
2287  // covariance ellipsis
2288  CEllipsoid::Ptr cov_ellipsis_obj = mrpt::make_aligned_shared<CEllipsoid>();
2289  cov_ellipsis_obj->setName("cov_ellipsis_obj");
2290  cov_ellipsis_obj->setColor_u8(m_curr_node_covariance_color);
2291  cov_ellipsis_obj->setLocation(0, 0, 0);
2292  // cov_ellipsis_obj->setQuantiles(2.0);
2293 
2294  mrpt::opengl::COpenGLScene::Ptr scene = this->m_win->get3DSceneAndLock();
2295  scene->insert(cov_ellipsis_obj);
2296  this->m_win->unlockAccess3DScene();
2297  this->m_win->forceRepaint();
2298 
2299  MRPT_END;
2300 }
2301 
2302 template <class GRAPH_T>
2304 {
2305  MRPT_START;
2306  using namespace std;
2307  using namespace mrpt::math;
2308  using namespace mrpt::opengl;
2309  using namespace mrpt::gui;
2310 
2311  // get the optimal path to the current node
2312  mrpt::graphs::TNodeID curr_node = this->m_graph->nodeCount() - 1;
2313  path_t* path = queryOptimalPath(curr_node);
2314  if (!path) return;
2315 
2316  CMatrixDouble33 mat;
2317  path->curr_pose_pdf.getCovariance(mat);
2318  pose_t curr_position = this->m_graph->nodes.at(curr_node);
2319 
2321  "In updateCurrCovarianceVisualization\n"
2322  "Covariance matrix:\n"
2323  << mat << "\n"
2324  "determinant : "
2325  << mat.det());
2326 
2327  mrpt::opengl::COpenGLScene::Ptr scene = this->m_win->get3DSceneAndLock();
2328  CRenderizable::Ptr obj = scene->getByName("cov_ellipsis_obj");
2329  CEllipsoid::Ptr cov_ellipsis_obj =
2330  std::dynamic_pointer_cast<CEllipsoid>(obj);
2331 
2332  // set the pose and corresponding covariance matrix of the ellipsis
2333  cov_ellipsis_obj->setLocation(curr_position.x(), curr_position.y(), 0);
2334  // pose_t loc = path->curr_pose_pdf.getMeanVal();
2335  // cov_ellipsis_obj->setLocation(loc.x(), loc.y(), 0);
2336  cov_ellipsis_obj->setCovMatrix(mat, 2);
2337 
2338  this->m_win->unlockAccess3DScene();
2339  this->m_win->forceRepaint();
2340 
2341  MRPT_END;
2342 }
2343 
2344 template <class GRAPH_T>
2346  std::string viz_flag, int sleep_time /* = 500 milliseconds */)
2347 {
2348  MRPT_START;
2349 
2350  this->logFmt(
2352  "Cannot toggle visibility of specified object.\n "
2353  "Make sure that the corresponding visualization flag ( %s "
2354  ") is set to true in the .ini file.\n",
2355  viz_flag.c_str());
2356  std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
2357 
2358  MRPT_END;
2359 }
2360 
2361 template <class GRAPH_T>
2363 {
2364  MRPT_START;
2365  parent_t::loadParams(source_fname);
2366 
2367  m_partitioner.options.loadFromConfigFileName(
2368  source_fname, "EdgeRegistrationDeciderParameters");
2369  m_laser_params.loadFromConfigFileName(
2370  source_fname, "EdgeRegistrationDeciderParameters");
2371  m_lc_params.loadFromConfigFileName(
2372  source_fname, "EdgeRegistrationDeciderParameters");
2373 
2374  mrpt::config::CConfigFile source(source_fname);
2375 
2376  m_consec_icp_constraint_factor = source.read_double(
2377  "EdgeRegistrationDeciderParameters", "consec_icp_constraint_factor",
2378  0.90, false);
2379  m_lc_icp_constraint_factor = source.read_double(
2380  "EdgeRegistrationDeciderParameters", "lc_icp_constraint_factor", 0.70,
2381  false);
2382 
2383  // set the logging level if given by the user
2384  int min_verbosity_level = source.read_int(
2385  "EdgeRegistrationDeciderParameters", "class_verbosity", 1, false);
2386 
2387  this->setMinLoggingLevel(mrpt::system::VerbosityLevel(min_verbosity_level));
2388  MRPT_END;
2389 }
2390 template <class GRAPH_T>
2392 {
2393  MRPT_START;
2394  using namespace std;
2395 
2396  cout << "------------------[Pair-wise Consistency of ICP Edges - "
2397  "Registration Procedure Summary]------------------"
2398  << endl;
2399 
2400  parent_t::printParams();
2401  m_partitioner.options.dumpToConsole();
2402  m_laser_params.dumpToConsole();
2403  m_lc_params.dumpToConsole();
2404 
2405  cout << "Scan-matching ICP Constraint factor: "
2406  << m_consec_icp_constraint_factor << endl;
2407  cout << "Loop-closure ICP Constraint factor: "
2408  << m_lc_icp_constraint_factor << endl;
2409 
2410  MRPT_LOG_DEBUG_STREAM("Printed the relevant parameters");
2411  MRPT_END;
2412 }
2413 
2414 template <class GRAPH_T>
2416  std::string* report_str) const
2417 {
2418  MRPT_START;
2419 
2420  // Report on graph
2421  std::stringstream class_props_ss;
2422  class_props_ss << "Pair-wise Consistency of ICP Edges - Registration "
2423  "Procedure Summary: "
2424  << std::endl;
2425  class_props_ss << this->header_sep << std::endl;
2426 
2427  // time and output logging
2428  const std::string time_res = this->m_time_logger.getStatsAsText();
2429  const std::string output_res = this->getLogAsString();
2430 
2431  // merge the individual reports
2432  report_str->clear();
2433  parent_t::getDescriptiveReport(report_str);
2434 
2435  *report_str += class_props_ss.str();
2436  *report_str += this->report_sep;
2437 
2438  *report_str += time_res;
2439  *report_str += this->report_sep;
2440 
2441  *report_str += output_res;
2442  *report_str += this->report_sep;
2443 
2444  MRPT_END;
2445 } // end of getDescriptiveReport
2446 
2447 template <class GRAPH_T>
2449  partitions_t& partitions_out) const
2450 {
2451  partitions_out = this->getCurrentPartitions();
2452 }
2453 
2454 template <class GRAPH_T>
2455 const std::vector<std::vector<uint32_t>>&
2457 {
2458  return m_curr_partitions;
2459 }
2460 
2461 template <class GRAPH_T>
2463  bool full_update /* = false */, bool is_first_time_node_reg /* = false */)
2464 {
2465  MRPT_START;
2466  using namespace mrpt::math;
2467  using namespace std;
2468  this->m_time_logger.enter("updateMapPartitions");
2469 
2470  // Initialize the nodeIDs => LaserScans map
2471  nodes_to_scans2D_t nodes_to_scans;
2472  if (full_update)
2473  {
2474  MRPT_LOG_INFO(
2475  "updateMapPartitions: Full partitioning of map was issued");
2476  // clear the existing partitions and recompute the partitioned map for
2477  // all the nodes
2478  m_partitioner.clear();
2479  nodes_to_scans = this->m_nodes_to_laser_scans2D;
2480  }
2481  else
2482  {
2483  // if registering measurement for root node as well...
2484  if (is_first_time_node_reg)
2485  {
2486  nodes_to_scans.insert(
2487  make_pair(
2488  this->m_graph->root,
2489  this->m_nodes_to_laser_scans2D.at(this->m_graph->root)));
2490  }
2491 
2492  // just use the last node-laser scan pair
2493  nodes_to_scans.insert(
2494  make_pair(
2495  this->m_graph->nodeCount() - 1,
2496  this->m_nodes_to_laser_scans2D.at(
2497  this->m_graph->nodeCount() - 1)));
2498  }
2499 
2500  // TODO - Should always exist.
2501  // for each one of the above nodes - add its position and correspoding
2502  // laserScan to the partitioner object
2503  for (auto it = nodes_to_scans.begin(); it != nodes_to_scans.end(); ++it)
2504  {
2505  if (!it->second)
2506  { // if laserScan invalid go to next...
2508  "nodeID \"" << it->first << "\" has invalid laserScan");
2509  continue;
2510  }
2511 
2512  // find pose of node, if it exists...
2513  // TODO - investigate this case. Why should this be happening?
2514  const auto& search = this->m_graph->nodes.find(it->first);
2515  if (search == this->m_graph->nodes.end())
2516  {
2517  MRPT_LOG_WARN_STREAM("Couldn't find pose for nodeID " << it->first);
2518  continue;
2519  }
2520 
2521  // pose
2522  const auto curr_constraint = constraint_t(search->second);
2523  const auto pose3d(mrpt::poses::CPose3DPDF::createFrom2D(curr_constraint));
2524 
2525  // laser scan
2527  sf.insert(it->second);
2528 
2529  m_partitioner.addMapFrame(sf, *pose3d);
2530  }
2531 
2532  // update the last partitions list
2533  size_t curr_size = m_curr_partitions.size();
2534  m_last_partitions.resize(curr_size);
2535  for (size_t i = 0; i < curr_size; i++)
2536  {
2537  m_last_partitions[i] = m_curr_partitions[i];
2538  }
2539  // update current partitions list
2540  m_partitioner.updatePartitions(m_curr_partitions);
2541 
2542  MRPT_LOG_DEBUG_STREAM("Updated map partitions successfully.");
2543  this->m_time_logger.leave("updateMapPartitions");
2544  MRPT_END;
2545 } // end of updateMapPartitions
2546 
2547 // TLaserParams
2548 // //////////////////////////////////
2549 
2550 template <class GRAPH_T>
2552  : laser_scans_color(0, 20, 255),
2553  keystroke_laser_scans("l"),
2554  has_read_config(false)
2555 {
2557  200); // use the last X mahalanobis distance values
2558  goodness_threshold_win.resizeWindow(200); // use the last X ICP values
2559 }
2560 
2561 template <class GRAPH_T>
2563 {
2564 }
2565 
2566 template <class GRAPH_T>
2568  std::ostream& out) const
2569 {
2570  MRPT_START;
2571 
2572  out << "Use scan-matching constraints = "
2573  << (use_scan_matching ? "TRUE" : "FALSE") << std::endl;
2574  out << "Num. of previous nodes to check ICP against = "
2575  << prev_nodes_for_ICP << std::endl;
2576  out << "Visualize laser scans = "
2577  << (visualize_laser_scans ? "TRUE" : "FALSE") << std::endl;
2578 
2579  MRPT_END;
2580 }
2581 template <class GRAPH_T>
2583  const mrpt::config::CConfigFileBase& source, const std::string& section)
2584 {
2585  MRPT_START;
2586 
2587  use_scan_matching =
2588  source.read_bool(section, "use_scan_matching", true, false);
2589  prev_nodes_for_ICP = source.read_int( // how many nodes to check ICP against
2590  section,
2591  "prev_nodes_for_ICP",
2592  10, false);
2593  visualize_laser_scans = source.read_bool(
2594  "VisualizationParameters", "visualize_laser_scans", true, false);
2595 
2596  has_read_config = true;
2597  MRPT_END;
2598 }
2599 // TLoopClosureParams
2600 // //////////////////////////////////
2601 
2602 template <class GRAPH_T>
2604  : keystroke_map_partitions("b"),
2605  balloon_elevation(3),
2606  balloon_radius(0.5),
2607  balloon_std_color(153, 0, 153),
2608  balloon_curr_color(62, 0, 80),
2609  connecting_lines_color(balloon_std_color),
2610  has_read_config(false)
2611 {
2612 }
2613 
2614 template <class GRAPH_T>
2616 {
2617 }
2618 
2619 template <class GRAPH_T>
2621  std::ostream& out) const
2622 {
2623  MRPT_START;
2624  using namespace std;
2625 
2626  stringstream ss;
2627  ss << "Min. node difference for loop closure = "
2628  << LC_min_nodeid_diff << endl;
2629  ss << "Remote NodeIDs to consider the potential loop closure = "
2630  << LC_min_remote_nodes << endl;
2631  ss << "Min EigenValues ratio for accepting a hypotheses set = "
2632  << LC_eigenvalues_ratio_thresh << endl;
2633  ss << "Check only current node's partition for loop closures = "
2634  << (LC_check_curr_partition_only ? "TRUE" : "FALSE") << endl;
2635  ss << "New registered nodes required for full partitioning = "
2636  << full_partition_per_nodes << endl;
2637  ss << "Visualize map partitions = "
2638  << (visualize_map_partitions ? "TRUE" : "FALSE") << endl;
2639 
2640  out << mrpt::format("%s", ss.str().c_str());
2641 
2642  MRPT_END;
2643 }
2644 template <class GRAPH_T>
2646  const mrpt::config::CConfigFileBase& source, const std::string& section)
2647 {
2648  MRPT_START;
2649  LC_min_nodeid_diff = source.read_int(
2650  "GeneralConfiguration", "LC_min_nodeid_diff", 30, false);
2651  LC_min_remote_nodes =
2652  source.read_int(section, "LC_min_remote_nodes", 3, false);
2653  LC_eigenvalues_ratio_thresh =
2654  source.read_double(section, "LC_eigenvalues_ratio_thresh", 2, false);
2655  LC_check_curr_partition_only =
2656  source.read_bool(section, "LC_check_curr_partition_only", true, false);
2657  full_partition_per_nodes =
2658  source.read_int(section, "full_partition_per_nodes", 50, false);
2659  visualize_map_partitions = source.read_bool(
2660  "VisualizationParameters", "visualize_map_partitions", true, false);
2661 
2662  has_read_config = true;
2663  MRPT_END;
2664 }
2665 } // end of namespaces
2666 
2667 #endif /* end of include guard: CLOOPCLOSERERD_IMPL_H */
2668 
2669 
#define INVALID_NODEID
Definition: TNodeID.h:19
This class allows loading and storing values and vectors of different types from a configuration text...
This class allows loading and storing values and vectors of different types from "....
Class acts as a container for storing pointers to mrpt::gui::CDisplayWindow3D, mrpt::graphslam::CWind...
Edge Registration Decider scheme specialized in Loop Closing.
void evaluatePartitionsForLC(const partitions_t &partitions)
Evaluate the given partitions for loop closures.
void generateHypotsPool(const std::vector< uint32_t > &groupA, const std::vector< uint32_t > &groupB, hypotsp_t *generated_hypots, const TGenerateHypotsPoolAdParams *ad_params=NULL)
Generate the hypothesis pool for all the inter-group constraints between two groups of nodes.
bool computeDominantEigenVector(const mrpt::math::CMatrixDouble &consist_matrix, mrpt::math::dynamic_vector< double > *eigvec, bool use_power_method=false)
const partitions_t & getCurrentPartitions() const
void registerNewEdge(const mrpt::graphs::TNodeID &from, const mrpt::graphs::TNodeID &to, const constraint_t &rel_edge)
void splitPartitionToGroups(std::vector< uint32_t > &partition, std::vector< uint32_t > *groupA, std::vector< uint32_t > *groupB, int max_nodes_in_group=5)
Split an existing partition to Groups.
bool getPropsOfNodeID(const mrpt::graphs::TNodeID &nodeID, global_pose_t *pose, mrpt::obs::CObservation2DRangeScan::Ptr &scan, const node_props_t *node_props=NULL) const
Fill the pose and LaserScan for the given nodeID.
void getMinUncertaintyPath(const mrpt::graphs::TNodeID from, const mrpt::graphs::TNodeID to, path_t *path) const
Given two nodeIDs compute and return the path connecting them.
typename GRAPH_T::global_pose_t global_pose_t
virtual bool updateState(mrpt::obs::CActionCollection::Ptr action, mrpt::obs::CSensoryFrame::Ptr observations, mrpt::obs::CObservation::Ptr observation)
virtual void addScanMatchingEdges(const mrpt::graphs::TNodeID &curr_nodeID)
Addd ICP constraints from X previous nodeIDs up to the given nodeID.
static const path_t * findPathByEnds(const paths_t &vec_paths, const mrpt::graphs::TNodeID &src, const mrpt::graphs::TNodeID &dst, bool throw_exc=true)
Given a vector of TUncertaintyPath objects, find the one that has the given source and destination no...
bool fillNodePropsFromGroupParams(const mrpt::graphs::TNodeID &nodeID, const std::map< mrpt::graphs::TNodeID, node_props_t > &group_params, node_props_t *node_props)
Fill the TNodeProps instance using the parameters from the map.
mrpt::graphslam::TUncertaintyPath< GRAPH_T > * popMinUncertaintyPath(std::set< path_t * > *pool_of_paths) const
Find the minimum uncertainty path from te given pool of TUncertaintyPath instances.
std::map< std::string, int > m_edge_types_to_nums
Keep track of the registered edge types.
typename parent_t::nodes_to_scans2D_t nodes_to_scans2D_t
void loadParams(const std::string &source_fname)
void setWindowManagerPtr(mrpt::graphslam::CWindowManager *win_manager)
static hypot_t * findHypotByEnds(const hypotsp_t &vec_hypots, const mrpt::graphs::TNodeID &from, const mrpt::graphs::TNodeID &to, bool throw_exc=true)
Given a vector of THypothesis objects, find the one that has the given start and end nodes.
void registerHypothesis(const hypot_t &h)
Wrapper around the registerNewEdge method which accepts a THypothesis object instead.
std::vector< std::vector< uint32_t > > partitions_t
Typedef for referring to a list of partitions.
bool mahalanobisDistanceOdometryToICPEdge(const mrpt::graphs::TNodeID &from, const mrpt::graphs::TNodeID &to, const constraint_t &rel_edge)
brief Compare the suggested ICP edge against the initial node difference.
void updateMapPartitionsVisualization()
Update the map partitions visualization.
static hypot_t * findHypotByID(const hypotsp_t &vec_hypots, const size_t &id, bool throw_exc=true)
Given a vector of THypothesis objects, find the one that has the given ID.
void getEdgesStats(std::map< std::string, int > *edge_types_to_num) const
void execDijkstraProjection(mrpt::graphs::TNodeID starting_node=0, mrpt::graphs::TNodeID ending_node=INVALID_NODEID)
compute the minimum uncertainty of each node position with regards to the graph root.
void computeCentroidOfNodesVector(const std::vector< uint32_t > &nodes_list, std::pair< double, double > *centroid_coords) const
Compute the Centroid of a group of a vector of node positions.
typename mrpt::graphs::detail::THypothesis< GRAPH_T > hypot_t
typename GRAPH_T::edges_map_t::const_iterator edges_citerator
void dumpVisibilityErrorMsg(std::string viz_flag, int sleep_time=500)
void updateMapPartitions(bool full_update=false, bool is_first_time_node_reg=false)
Split the currently registered graph nodes into partitions.
void toggleLaserScansVisualization()
togle the LaserScans visualization on and off
void getDescriptiveReport(std::string *report_str) const
typename GRAPH_T::constraint_t::type_value pose_t
type of underlying poses (2D/3D).
void generatePWConsistenciesMatrix(const std::vector< uint32_t > &groupA, const std::vector< uint32_t > &groupB, const hypotsp_t &hypots_pool, mrpt::math::CMatrixDouble *consist_matrix, const paths_t *groupA_opt_paths=NULL, const paths_t *groupB_opt_paths=NULL)
Compute the pair-wise consistencies Matrix.
void notifyOfWindowEvents(const std::map< std::string, bool > &events_occurred)
virtual void fetchNodeIDsForScanMatching(const mrpt::graphs::TNodeID &curr_nodeID, std::set< mrpt::graphs::TNodeID > *nodes_set)
Fetch a list of nodes with regards prior to the given nodeID for which to try and add scan matching e...
void checkPartitionsForLC(partitions_t *partitions_for_LC)
Check the registered so far partitions for potential loop closures.
void initMapPartitionsVisualization()
Initialize the visualization of the map partition objects.
double generatePWConsistencyElement(const mrpt::graphs::TNodeID &a1, const mrpt::graphs::TNodeID &a2, const mrpt::graphs::TNodeID &b1, const mrpt::graphs::TNodeID &b2, const hypotsp_t &hypots, const paths_t *opt_paths=NULL)
Return the pair-wise consistency between the observations of the given nodes.
void addToPaths(std::set< path_t * > *pool_of_paths, const path_t &curr_path, const std::set< mrpt::graphs::TNodeID > &neibors) const
Append the paths starting from the current node.
virtual bool getICPEdge(const mrpt::graphs::TNodeID &from, const mrpt::graphs::TNodeID &to, constraint_t *rel_edge, mrpt::slam::CICP::TReturnInfo *icp_info=NULL, const TGetICPEdgeAdParams *ad_params=NULL)
Get the ICP Edge between the provided nodes.
void evalPWConsistenciesMatrix(const mrpt::math::CMatrixDouble &consist_matrix, const hypotsp_t &hypots_pool, hypotsp_t *valid_hypots)
Evalute the consistencies matrix, fill the valid hypotheses.
typename GRAPH_T::constraint_t constraint_t
Handy typedefs.
void toggleMapPartitionsVisualization()
Toggle the map partitions visualization objects.
Column vector, like Eigen::MatrixX*, but automatically initialized to zeros since construction.
Definition: types_math.h:68
std::shared_ptr< CActionCollection > Ptr
std::shared_ptr< CObservation2DRangeScan > Ptr
std::shared_ptr< CObservation > Ptr
Definition: CObservation.h:45
Declares a class for storing a "sensory frame", a set of "observations" taken by the robot approximat...
Definition: CSensoryFrame.h:53
void insert(const CObservation::Ptr &obs)
Inserts a new observation to the list: The pointer to the objects is copied, thus DO NOT delete the p...
std::shared_ptr< CSensoryFrame > Ptr
Definition: CSensoryFrame.h:54
std::shared_ptr< CEllipsoid > Ptr
Definition: CEllipsoid.h:47
std::shared_ptr< COpenGLScene > Ptr
Definition: COpenGLScene.h:61
std::shared_ptr< CPlanarLaserScan > Ptr
std::shared_ptr< CRenderizable > Ptr
Definition: CRenderizable.h:43
std::shared_ptr< CSetOfLines > Ptr
Definition: CSetOfLines.h:33
std::shared_ptr< CSetOfObjects > Ptr
Definition: CSetOfObjects.h:28
std::shared_ptr< CSphere > Ptr
Definition: CSphere.h:31
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:87
mrpt::math::TPose3D asTPose() const
Definition: CPose3D.cpp:1043
static CPose3DPDF * createFrom2D(const CPosePDF &o)
This is a static transformation method from 2D poses to 3D PDFs, preserving the representation type (...
Definition: CPose3DPDF.cpp:34
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
Scalar * iterator
Definition: eigen_plugins.h:26
const Scalar * const_iterator
Definition: eigen_plugins.h:27
#define MRPT_START
Definition: exceptions.h:262
#define ASSERTDEB_(f)
Defines an assertion mechanism - only when compiled in debug.
Definition: exceptions.h:205
#define MRPT_END
Definition: exceptions.h:266
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
#define ASSERTDEBMSG_(f, __ERROR_MSG)
Definition: exceptions.h:208
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: exceptions.h:43
GLsizei GLsizei GLuint * obj
Definition: glext.h:4070
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:4178
GLuint res
Definition: glext.h:7268
GLuint src
Definition: glext.h:7278
GLuint dst
Definition: glext.h:7135
GLfloat GLfloat p
Definition: glext.h:6305
GLenum const GLfloat * params
Definition: glext.h:3534
GLenum GLsizei GLenum format
Definition: glext.h:3531
GLsizei const GLfloat * value
Definition: glext.h:4117
GLsizei const GLchar ** string
Definition: glext.h:4101
GLsizei GLsizei GLchar * source
Definition: glext.h:4082
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
MAT::Scalar mahalanobisDistance2(const VECTORLIKE1 &X, const VECTORLIKE2 &MU, const MAT &COV)
Computes the squared mahalanobis distance of a vector X given the mean MU and the covariance inverse ...
Definition: data_utils.h:34
std::string getSTLContainerAsString(const T &t)
Return a STL container in std::string form.
const_iterator find(const KEY &key) const
Definition: ts_hash_map.h:217
uint64_t TNodeID
A generic numeric type for unique IDs of nodes or entities.
Definition: TNodeID.h:16
Internal auxiliary classes.
Definition: levmarq_impl.h:18
SLAM methods related to graphs of pose constraints.
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:15
This base provides a set of functions for maths stuff.
bool approximatelyEqual(T1 a, T1 b, T2 epsilon)
Compare 2 floats and determine whether they are equal.
CMatrixFixedNumeric< double, 3, 3 > CMatrixDouble33
Definition: eigen_frwds.h:57
T absDiff(const T &lhs, const T &rhs)
Absolute difference between two numbers.
This namespace contains representation of robot actions and observations.
The namespace for 3D scene representation and rendering.
Definition: CGlCanvasBase.h:16
Eigen::Matrix< dataType, 4, 4 > inverse(Eigen::Matrix< dataType, 4, 4 > &pose)
Definition: Miscellaneous.h:80
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
VerbosityLevel
Enumeration of available verbosity levels.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
double DEG2RAD(const double x)
Degrees to radians.
An edge hypothesis between two nodeIDs.
Definition: THypothesis.h:34
void resizeWindow(size_t new_size)
Resize the window.
Holds the data of an information path.
void addToPath(const mrpt::graphs::TNodeID &node, const constraint_t &edge)
add a new link in the current path.
void assertIsBetweenNodeIDs(const mrpt::graphs::TNodeID &from, const mrpt::graphs::TNodeID &to) const
Assert that the current path is between the given nodeIDs.
const mrpt::graphs::TNodeID & getDestination() const
Return the Destination node of this path.
std::vector< mrpt::graphs::TNodeID > nodes_traversed
Nodes that the Path comprises of.
constraint_t curr_pose_pdf
Current path position + corresponding covariance.
Struct for passing additional parameters to the generateHypotsPool call.
std::map< mrpt::graphs::TNodeID, node_props_t > group_t
Struct for passing additional parameters to the getICPEdge call.
void loadFromConfigFile(const mrpt::config::CConfigFileBase &source, const std::string &section)
This method load the options from a ".ini"-like file or memory-stored string list.
void dumpToTextStream(std::ostream &out) const
This method should clearly display all the contents of the structure in textual form,...
TSlidingWindow goodness_threshold_win
Keep track of ICP Goodness values for ICP between nearby nodes and adapt the Goodness threshold based...
TSlidingWindow mahal_distance_ICP_odom_win
Keep track of the mahalanobis distance between the initial pose difference and the suggested new edge...
void loadFromConfigFile(const mrpt::config::CConfigFileBase &source, const std::string &section)
This method load the options from a ".ini"-like file or memory-stored string list.
void dumpToTextStream(std::ostream &out) const
This method should clearly display all the contents of the structure in textual form,...
mrpt::obs::CObservation2DRangeScan::Ptr scan
Definition: TNodeProps.h:21
GRAPH_T::global_pose_t pose
Definition: TNodeProps.h:20
A RGB color - floats in the range [0,1].
Definition: TColor.h:78
Lightweight 3D point.
3D segment, consisting of two points.
The ICP algorithm return information.
Definition: CICP.h:191
float goodness
A goodness measure for the alignment, it is a [0,1] range indicator of percentage of correspondences.
Definition: CICP.h:197
#define MRPT_LOG_ERROR_STREAM(__CONTENTS)
#define MRPT_LOG_DEBUG_FMT(_FMT_STRING,...)
Use: MRPT_LOG_DEBUG_FMT("i=%u", i);
#define MRPT_LOG_DEBUG_STREAM(__CONTENTS)
Use: MRPT_LOG_DEBUG_STREAM("Var=" << value << " foo=" << foo_var);
#define MRPT_LOG_DEBUG(_STRING)
Use: MRPT_LOG_DEBUG("message");
#define MRPT_LOG_INFO(_STRING)
#define MRPT_LOG_WARN_STREAM(__CONTENTS)



Page generated by Doxygen 1.9.1 for MRPT 1.9.9 Git: 814d80880 Fri Aug 24 01:51:28 2018 +0200 at mar 26 may 2026 12:30:59 CEST