CGraphSlamEngine_MR_impl.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ | | | | Copyright (c)
4  2005-2016, Individual contributors, see AUTHORS file | | See:
5  http://www.mrpt.org/Authors - All rights reserved. | |
6  Released under BSD License. See details in http://www.mrpt.org/License |
7  +---------------------------------------------------------------------------+
8  */
9 
10 #pragma once
11 
12 #include <chrono>
13 #include <mrpt/version.h>
14 #include <thread>
15 
16 #if MRPT_VERSION >= 0x199
17 #include <mrpt/math/CMatrixFixed.h>
18 namespace mrpt::math {
19 // backwards compatible
20 template <typename T, std::size_t ROWS, std::size_t COLS>
21 using CMatrixFixedNumeric = CMatrixFixed<T, ROWS, COLS>;
22 } // namespace mrpt::math
23 #endif
24 
25 namespace mrpt {
26 namespace graphslam {
27 
28 template <class GRAPH_T>
30  ros::NodeHandle *nh, const std::string &config_file,
31  const std::string &rawlog_fname /* ="" */,
32  const std::string &fname_GT /* ="" */,
33  mrpt::graphslam::CWindowManager *win_manager /* = NULL */,
35  *node_reg /* = NULL */,
37  *edge_reg /* = NULL */,
39  *optimizer /* = NULL */)
40  : parent_t::CGraphSlamEngine_ROS(nh, config_file, rawlog_fname, fname_GT,
41  win_manager, node_reg, edge_reg,
42  optimizer),
43  m_conn_manager(dynamic_cast<mrpt::utils::COutputLogger *>(this), nh),
44  m_nh(nh), m_graph_nodes_last_size(0), m_registered_multiple_nodes(false),
45  cm_graph_async_spinner(/* threads_num: */ 1, &this->custom_service_queue),
46  m_sec_alignment_params("AlignmentParameters"),
47  m_sec_mr_slam_params("MultiRobotParameters"), m_opts(*this) {
48  this->initClass();
49 }
50 
53  "In Destructor: Deleting CGraphSlamEngine_MR instance...");
55 
56  for (typename neighbors_t::iterator neighbors_it = m_neighbors.begin();
57  neighbors_it != m_neighbors.end(); ++neighbors_it) {
58  delete *neighbors_it;
59  }
60 }
61 
62 template <class GRAPH_T>
64  MRPT_START;
65  using namespace mrpt::graphslam::detail;
66 
67  bool ret_val = false;
68  for (const auto &neighbor : m_neighbors) {
69  if (!isEssentiallyZero(
70  neighbor
71  ->tf_self_to_neighbor_first_integrated_pose) && // inter-graph
72  // TF found
73  neighbor->hasNewData() &&
74  neighbor->hasNewNodesBatch(m_opts.nodes_integration_batch_size)) {
75  bool loc_ret_val = this->addNodeBatchFromNeighbor(neighbor);
76  if (loc_ret_val) {
77  ret_val = true;
78  }
79  }
80  }
81 
82  return ret_val;
83  MRPT_END;
84 } // end of addNodeBatchesFromAllNeighbors
85 
86 template <class GRAPH_T>
88  TNeighborAgentProps *neighbor) {
89  MRPT_START;
90  ASSERT_(neighbor);
91  using namespace mrpt::utils;
92  using namespace mrpt::poses;
93  using namespace mrpt::math;
94  using namespace std;
95 
96  std::vector<TNodeID> nodeIDs;
97  std::map<TNodeID, node_props_t> nodes_params;
98  neighbor->getCachedNodes(&nodeIDs, &nodes_params, /*only unused=*/true);
99 
100  //
101  // get the condensed measurements graph of the new nodeIDs
102  //
103  mrpt_msgs::GetCMGraph cm_graph_srv;
104  // mrpt_msgs::GetCMGraphRequest::_nodeIDs_type& cm_graph_nodes =
105  // cm_graph_srv.request.nodeIDs;
106  for (const auto n : nodeIDs) {
107  cm_graph_srv.request.nodeIDs.push_back(n);
108  }
109 
110  MRPT_LOG_DEBUG_STREAM("Asking for the graph.");
111  bool res = neighbor->cm_graph_srvclient.call(cm_graph_srv); // blocking
112  if (!res) {
113  MRPT_LOG_ERROR_STREAM("Service call for CM_Graph failed.");
114  return false; // skip this if failed to fetch other's graph
115  }
116  MRPT_LOG_DEBUG_STREAM("Fetched graph successfully.");
117  MRPT_LOG_DEBUG_STREAM(cm_graph_srv.response.cm_graph);
118 
119  GRAPH_T other_graph;
120  mrpt_bridge::convert(cm_graph_srv.response.cm_graph, other_graph);
121 
122  //
123  // merge batch of nodes in own graph
124  //
126  "Merging new batch from \""
127  << neighbor->getAgentNs() << "\"..." << endl
128  << "Batch: " << getSTLContainerAsString(cm_graph_srv.request.nodeIDs));
129  hypots_t graph_conns;
130  // build a hypothesis connecting the new batch with the last integrated
131  // pose of the neighbor
132  {
133  pair<TNodeID, node_props_t> node_props_to_connect = *nodes_params.begin();
134 
135  hypot_t graph_conn;
136  constraint_t c;
137  c.mean = node_props_to_connect.second.pose -
138  neighbor->last_integrated_pair_neighbor_frame.second;
139 #if MRPT_VERSION >= 0x199
140  c.cov_inv.setIdentity();
141 #else
142  c.cov_inv.unit();
143 #endif
144  graph_conn.setEdge(c);
145 
146  graph_conn.from = INVALID_NODEID;
147  // get the nodeID of the last integrated neighbor node after remapping
148  // in own graph numbering
149  for (const auto n : this->m_graph.nodes) {
150  if (n.second.agent_ID_str == neighbor->getAgentNs() &&
151  n.second.nodeID_loc ==
152  neighbor->last_integrated_pair_neighbor_frame.first) {
153  graph_conn.from = n.first;
154  break;
155  }
156  }
157  ASSERT_(graph_conn.from != INVALID_NODEID);
158  graph_conn.to = node_props_to_connect.first; // other
159 
161  "Hypothesis for adding the batch of nodes: " << graph_conn);
162  graph_conns.push_back(graph_conn);
163  }
164 
165  std::map<TNodeID, TNodeID> old_to_new_mappings;
166  this->m_graph.mergeGraph(other_graph, graph_conns,
167  /*hypots_from_other_to_self=*/false,
168  &old_to_new_mappings);
169 
170  //
171  // Mark Nodes/LaserScans as integrated
172  //
173  MRPT_LOG_WARN_STREAM("Marking used nodes as integrated - Integrating LSs");
174  nodes_to_scans2D_t new_nodeIDs_to_scans_pairs;
175  for (typename std::vector<TNodeID>::const_iterator n_cit = nodeIDs.begin();
176  n_cit != nodeIDs.end(); ++n_cit) {
177 
178  // Mark the neighbor's used nodes as integrated
179  neighbor->nodeID_to_is_integrated.at(*n_cit) = true;
180 
181  // Integrate LaserScans at the newly integrated nodeIDs in own
182  // nodes_to_laser_scans2D map
183  new_nodeIDs_to_scans_pairs.insert(make_pair(old_to_new_mappings.at(*n_cit),
184  nodes_params.at(*n_cit).scan));
185  MRPT_LOG_INFO_STREAM("Adding nodeID-LS of nodeID: "
186  << "\t[old:] " << *n_cit << endl
187  << "| [new:] " << old_to_new_mappings.at(*n_cit));
188  }
189 
190  this->m_nodes_to_laser_scans2D.insert(new_nodeIDs_to_scans_pairs.begin(),
191  new_nodeIDs_to_scans_pairs.end());
192  edge_reg_mr_t *edge_reg_mr = dynamic_cast<edge_reg_mr_t *>(this->m_edge_reg);
193  edge_reg_mr->addBatchOfNodeIDsAndScans(new_nodeIDs_to_scans_pairs);
194 
196  neighbor->resetFlags();
197 
198  // keep track of the last integrated nodeID
199  {
200  TNodeID last_node = *nodeIDs.rbegin();
202  make_pair(last_node, nodes_params.at(last_node).pose);
203  }
204 
205  return true;
206  MRPT_END;
207 } // end of addNodeBatchFromNeighbor
208 
209 template <class GRAPH_T>
211  MRPT_START;
212  using namespace mrpt::utils;
213  using namespace mrpt::math;
214 
215  if (this->m_graph.nodeCount() < m_opts.inter_group_node_count_thresh) {
216  return false;
217  }
218 
219  // cache the gridmap so that it is computed only once during all the
220  // findMatches* calls
221  this->computeMap();
222 
223  bool ret_val = false; // at *any* node registration, change to true
224 
225  // iterate over all neighbors - run map-matching proc.
226  for (typename neighbors_t::iterator neighbors_it = m_neighbors.begin();
227  neighbors_it != m_neighbors.end(); ++neighbors_it) {
228 
229  // run matching proc with neighbor only if I haven't found an initial
230  // inter-graph TF
231  if (!m_neighbor_to_found_initial_tf.at(*neighbors_it)) {
232 
233  bool loc_ret_val = findTFWithNeighbor(*neighbors_it);
234  if (loc_ret_val) {
236  "Updating own cached map after successful node registration...");
237  // update own map if new nodes have been added.
238  this->computeMap();
239 
240  // mark current neighbor tf as found.
241  m_neighbor_to_found_initial_tf.at(*neighbors_it) = true;
242 
243  ret_val = true;
244  }
245  }
246  }
247 
248  return ret_val;
249  MRPT_END;
250 } // end of findTFsWithAllNeighbors
251 
252 template <class GRAPH_T>
254  TNeighborAgentProps *neighbor) {
255  MRPT_START;
256  using namespace mrpt::slam;
257  using namespace mrpt::math;
258  using namespace mrpt::poses;
259  using namespace mrpt::utils;
260  using namespace mrpt::maps;
261  using namespace mrpt::graphslam::detail;
262 
263  bool ret_val = false;
264 
265  std::vector<TNodeID> neighbor_nodes;
266  std::map<TNodeID, node_props_t> nodes_params;
267  neighbor->getCachedNodes(&neighbor_nodes, &nodes_params,
268  /*only unused=*/false);
269  if (neighbor_nodes.size() < m_opts.inter_group_node_count_thresh ||
270  !neighbor->hasNewData()) {
271  return false;
272  }
273 
274  //
275  // run alignment procedure
276  //
277  COccupancyGridMap2D::Ptr neighbor_gridmap = neighbor->getGridMap();
278  CGridMapAligner gridmap_aligner;
279  gridmap_aligner.options = m_alignment_options;
280 
282  float run_time = 0;
283  // TODO - make use of agents' rendez-vous in the initial estimation
284  CPosePDFGaussian init_estim;
285  init_estim.mean = neighbor->tf_self_to_neighbor_first_integrated_pose;
286  this->logFmt(LVL_INFO, "Trying to align the maps, initial estimation: %s",
287  init_estim.mean.asString().c_str());
288 
289  // Save them for debugging reasons
290  // neighbor_gridmap->saveMetricMapRepresentationToFile(this->getLoggerName() +
291  // "_other");
292  // this->m_gridmap_cached->saveMetricMapRepresentationToFile(this->getLoggerName()
293  // + "_self");
294 
295  const CPosePDF::Ptr pdf_tmp = gridmap_aligner.AlignPDF(
296  this->m_gridmap_cached.get(), neighbor_gridmap.get(), init_estim,
297  &run_time, &results);
298  this->logFmt(LVL_INFO, "Elapsed Time: %f", run_time);
299  CPosePDFSOG::Ptr pdf_out = CPosePDFSOG::Create();
300  pdf_out->copyFrom(*pdf_tmp);
301 
302  CPose2D pose_out;
303  CMatrixDouble33 cov_out;
304  pdf_out->getMostLikelyCovarianceAndMean(cov_out, pose_out);
305 
306  this->logFmt(LVL_INFO, "%s\n",
307  getGridMapAlignmentResultsAsString(*pdf_tmp, results).c_str());
308 
309  // dismiss this?
310  if (results.goodness > 0.999 || isEssentiallyZero(pose_out)) {
311  return false;
312  }
313 
314  //
315  //
316  // Map-merging operation is successful. Integrate graph into own.
318 
319  //
320  // ask for condensed measurements graph
321  //
322  mrpt_msgs::GetCMGraph cm_graph_srv;
323  typedef mrpt_msgs::GetCMGraphRequest::_nodeIDs_type nodeID_type;
324  nodeID_type &matched_nodeIDs = cm_graph_srv.request.nodeIDs;
325 
326  // which nodes to ask the condensed graph for
327  // I assume that no nodes of the other graph have been integrated yet.
328  for (typename std::vector<TNodeID>::const_iterator n_cit =
329  neighbor_nodes.begin();
330  n_cit != neighbor_nodes.end(); ++n_cit) {
331  matched_nodeIDs.push_back(*n_cit); // all used nodes
332  }
333 
334  MRPT_LOG_DEBUG_STREAM("Asking for the graph.");
335  bool res = neighbor->cm_graph_srvclient.call(cm_graph_srv); // blocking
336  if (!res) {
337  MRPT_LOG_ERROR_STREAM("Service call for CM_Graph failed.");
338  return false; // skip this if failed to fetch other's graph
339  }
340  MRPT_LOG_DEBUG_STREAM("Fetched graph successfully.");
341  MRPT_LOG_DEBUG_STREAM(cm_graph_srv.response.cm_graph);
342 
343  GRAPH_T other_graph;
344  mrpt_bridge::convert(cm_graph_srv.response.cm_graph, other_graph);
345 
346  //
347  // merge graphs
348  //
349  MRPT_LOG_WARN_STREAM("Merging graph of \"" << neighbor->getAgentNs()
350  << "\"...");
351  hypots_t graph_conns;
352  // build the only hypothesis connecting graph with neighbor subgraph
353  // own origin -> first valid nodeID of neighbor
354  {
355  hypot_t graph_conn;
356  constraint_t c;
357  c.mean = pose_out;
358  // assign the inverse of the found covariance to c
359 #if MRPT_VERSION >= 0x199
360  cov_out = c.cov_inv.inverse();
361 #else
362  cov_out.inv(c.cov_inv);
363 #endif
364  graph_conn.setEdge(c);
365  // TODO - change this.
366  graph_conn.from = 0; // self
367  graph_conn.to = *neighbor_nodes.begin(); // other
368 
369  graph_conn.goodness = results.goodness;
370  graph_conns.push_back(graph_conn);
371  }
372 
373  std::map<TNodeID, TNodeID> old_to_new_mappings;
374  this->m_graph.mergeGraph(other_graph, graph_conns,
375  /*hypots_from_other_to_self=*/false,
376  &old_to_new_mappings);
377 
378  //
379  // Mark Nodes/LaserScans as integrated
380  //
381  MRPT_LOG_WARN_STREAM("Marking used nodes as integrated - Integrating LSs");
382  nodes_to_scans2D_t new_nodeIDs_to_scans_pairs;
383  for (typename std::vector<TNodeID>::const_iterator n_cit =
384  neighbor_nodes.begin();
385  n_cit != neighbor_nodes.end(); ++n_cit) {
386 
387  // Mark the neighbor's used nodes as integrated
388  neighbor->nodeID_to_is_integrated.at(*n_cit) = true;
389 
390  // Integrate LaserScans at the newly integrated nodeIDs in own
391  // nodes_to_laser_scans2D map
392  new_nodeIDs_to_scans_pairs.insert(make_pair(old_to_new_mappings.at(*n_cit),
393  nodes_params.at(*n_cit).scan));
394  MRPT_LOG_INFO_STREAM("Adding nodeID-LS of nodeID: "
395  << "\t[old:] " << *n_cit << endl
396  << "| [new:] " << old_to_new_mappings.at(*n_cit));
397  }
398 
399  this->m_nodes_to_laser_scans2D.insert(new_nodeIDs_to_scans_pairs.begin(),
400  new_nodeIDs_to_scans_pairs.end());
401  edge_reg_mr_t *edge_reg_mr = dynamic_cast<edge_reg_mr_t *>(this->m_edge_reg);
402  edge_reg_mr->addBatchOfNodeIDsAndScans(new_nodeIDs_to_scans_pairs);
403 
404  // Call for a Full graph visualization update and Dijkstra update -
405  // CGraphSlamOptimizer
406  // MRPT_LOG_WARN_STREAM("Optimizing graph..." << endl);
407  // this->m_optimizer->optimizeGraph();
408  MRPT_LOG_WARN_STREAM("Executing Dijkstra..." << endl);
410 
411  neighbor->resetFlags();
412 
413  // update the initial tf estimation
414  neighbor->tf_self_to_neighbor_first_integrated_pose = pose_out;
415 
416  // keep track of the last integrated nodeID
417  {
418  TNodeID last_node = *neighbor_nodes.rbegin();
420  make_pair(last_node, nodes_params.at(last_node).pose);
421  }
422 
423  MRPT_LOG_WARN_STREAM("Nodes of neighbor ["
424  << neighbor->getAgentNs()
425  << "] have been integrated successfully");
426 
427  ret_val = true;
428 
429  return ret_val;
430  MRPT_END;
431 } // end if findTFWithNeighbor
432 
433 template <class GRAPH_T>
435  mrpt::obs::CActionCollection::Ptr &action,
436  mrpt::obs::CSensoryFrame::Ptr &observations,
437  mrpt::obs::CObservation::Ptr &observation, size_t &rawlog_entry) {
438  MRPT_START;
439  using namespace mrpt::graphslam::deciders;
440  using namespace mrpt;
441 
442  // call parent method
443  bool continue_exec = parent_t::_execGraphSlamStep(action, observations,
444  observation, rawlog_entry);
445 
446  // find matches between own nodes and those of the neighbors
447  bool did_register_from_map_merge;
449  did_register_from_map_merge = this->findTFsWithAllNeighbors();
450  } else { // inter-graph TF is available - add new nodes
452  "Conservative inter-graph TF computation is not yet implemented.");
453  }
454 
455  bool did_register_from_batches = this->addNodeBatchesFromAllNeighbors();
457  (did_register_from_map_merge || did_register_from_batches);
458 
460  if (this->m_enable_visuals) {
461  this->updateAllVisuals();
462  }
463  }
464 
465  return continue_exec;
466  MRPT_END;
467 } // end of _execGraphSlamStep
468 
469 template <class GRAPH_T> void CGraphSlamEngine_MR<GRAPH_T>::initClass() {
470  using namespace mrpt::graphslam;
471  using namespace mrpt::utils;
472  using namespace std;
473 
474  // initialization of topic namespace names
475  // TODO - put these into seperate method
476  m_mr_ns = "mr_info";
477 
478  // initialization of topic names / services
479  m_list_neighbors_topic = m_mr_ns + "/" + "neighbors";
480  m_last_regd_id_scan_topic = m_mr_ns + "/" + "last_nodeID_laser_scan";
481  m_last_regd_nodes_topic = m_mr_ns + "/" + "last_regd_nodes";
482  m_cm_graph_service = m_mr_ns + "/" + "get_cm_graph";
483 
484  this->m_class_name = "CGraphSlamEngine_MR_" + m_conn_manager.getTrimmedNs();
485  this->setLoggerName(this->m_class_name);
486  this->setupComm();
487 
488  // Make sure that master_discovery and master_sync are up before trying to
489  // connect to other agents
490  std::vector<string> nodes_up;
491  ros::master::getNodes(nodes_up);
492  // If both master_dicovery and master_sync are running, then the
493  // /master_sync/get_sync_info service should be available
495  "Waiting for master_discovery, master_sync nodes to come up....");
497  "/master_sync/get_sync_info"); // block until it is
498  MRPT_LOG_INFO_STREAM("master_discovery, master_sync are available.");
499 
500  this->m_node_reg->setClassName(this->m_node_reg->getClassName() + "_" +
502  this->m_edge_reg->setClassName(this->m_edge_reg->getClassName() + "_" +
504  this->m_optimizer->setClassName(this->m_optimizer->getClassName() + "_" +
506 
507  // in case of Multi-robot specific deciders/optimizers (they
508  // inherit from the CDeciderOrOptimizer_ROS interface) set the
509  // CConnectionManager*
510  // NOTE: It's not certain, even though we are running multi-robot graphSLAM
511  // that all these classes do inherit from the
512  // CRegistrationDeciderOrOptimizer_MR base class. They might be more generic
513  // and not care about the multi-robot nature of the algorithm (e.g.
514  // optimization scheme)
515  { // NRD
518  this->m_node_reg);
519 
520  if (dec_opt_mr) {
522  }
523  }
524  { // ERD
527  this->m_edge_reg);
528  ASSERT_(dec_opt_mr);
530  dec_opt_mr->setCGraphSlamEnginePtr(this);
531  }
532  { // GSO
535  this->m_optimizer);
536  if (dec_opt_mr) {
538  }
539  }
540  // display messages with the names of the deciders, optimizer and agent string
541  // ID
542  if (this->m_enable_visuals) {
543  // NRD
545  /* offset_y* = */ &m_offset_y_nrd,
546  /* text_index* = */ &m_text_index_nrd);
549  mrpt::format("NRD: %s", this->m_node_reg->getClassName().c_str()),
550  TColorf(0, 0, 0),
551  /* unique_index = */ m_text_index_nrd);
552 
553  // ERD
555  /* offset_y* = */ &m_offset_y_erd,
556  /* text_index* = */ &m_text_index_erd);
559  mrpt::format("ERD: %s", this->m_edge_reg->getClassName().c_str()),
560  TColorf(0, 0, 0),
561  /* unique_index = */ m_text_index_erd);
562 
563  // GSO
565  /* offset_y* = */ &m_offset_y_gso,
566  /* text_index* = */ &m_text_index_gso);
569  mrpt::format("GSO: %s", this->m_optimizer->getClassName().c_str()),
570  TColorf(0, 0, 0),
571  /* unique_index = */ m_text_index_gso);
572 
573  // Agent Namespace
575  /* offset_y* = */ &m_offset_y_namespace,
576  /* text_index* = */ &m_text_index_namespace);
579  mrpt::format("Agent ID: %s", m_conn_manager.getTrimmedNs().c_str()),
580  TColorf(0, 0, 0),
581  /* unique_index = */ m_text_index_namespace);
582  }
583  this->readParams();
584 
586 
587  // start the spinner for asynchronously servicing cm_graph requests
589 } // end of initClass
590 
591 template <class GRAPH_T>
594  const TNodeID nodeID) const {
595  MRPT_START;
596  using namespace mrpt::graphs::detail;
597 
598  // if this is my own node ID return the corresponding CPose2D, otherwise
599  // return the pose connecting own graph with the neighbor's graph
600 
601  const TMRSlamNodeAnnotations *node_annots =
602  dynamic_cast<const TMRSlamNodeAnnotations *>(
603  &this->m_graph.nodes.at(nodeID));
604 
605  // Is the current nodeID registered by a neighboring agent or is it my own?
606  TNeighborAgentProps *neighbor = NULL;
607  this->getNeighborByAgentID(node_annots->agent_ID_str, neighbor);
608 
609  CPose3D laser_pose;
610  if (!neighbor) { // own node
611  laser_pose = parent_t::getLSPoseForGridMapVisualization(nodeID);
612  } else { // not by me - return TF to neighbor graph
613  laser_pose = CPose3D(neighbor->tf_self_to_neighbor_first_integrated_pose);
614  }
615  return laser_pose;
616 
617  MRPT_END;
618 }
619 
620 template <class GRAPH_T>
622  const std::string &agent_ID_str, TNeighborAgentProps *&neighbor) const {
623  MRPT_START;
624 
625  bool ret = false;
626  for (auto neighbors_it = m_neighbors.begin();
627  neighbors_it != m_neighbors.end(); ++neighbors_it) {
628 
629  if ((*neighbors_it)->getAgentNs() == agent_ID_str) {
630  ret = true;
631  neighbor = *neighbors_it;
632  break;
633  }
634  }
635 
636  return ret;
637  MRPT_END;
638 }
639 
640 template <class GRAPH_T>
642  MRPT_START;
643  using namespace mrpt_bridge;
644  using namespace mrpt_msgs;
645  using namespace std;
646  using namespace mrpt::math;
647  using ::operator==;
648 
649  // call the parent class
651 
652  // update list of neighbors that the current agent can communicate with.
653  mrpt_msgs::GraphSlamAgents nearby_slam_agents;
654  m_conn_manager.getNearbySlamAgents(&nearby_slam_agents,
655  /*ignore_self = */ true);
656  m_list_neighbors_pub.publish(nearby_slam_agents);
657 
658  // Initialize TNeighborAgentProps
659  // for each *new* GraphSlamAgent we should add a TNeighborAgentProps instance,
660  // and initialize its subscribers so that we fetch every new LaserScan and
661  // list of modified nodes it publishes
662  {
663  for (GraphSlamAgents::_list_type::const_iterator it =
664  nearby_slam_agents.list.begin();
665  it != nearby_slam_agents.list.end(); ++it) {
666 
667  const GraphSlamAgent &gsa = *it;
668 
669  // Is the current GraphSlamAgent already registered?
670  const auto search = [gsa](const TNeighborAgentProps *neighbor) {
671  return (neighbor->agent == gsa);
672  };
673  typename neighbors_t::const_iterator neighbor_it =
674  find_if(m_neighbors.begin(), m_neighbors.end(), search);
675 
676  if (neighbor_it == m_neighbors.end()) { // current gsa not found, add it
677 
678  MRPT_LOG_DEBUG_STREAM("Initializing NeighborAgentProps instance for "
679  << gsa.name.data);
680  m_neighbors.push_back(new TNeighborAgentProps(*this, gsa));
681  TNeighborAgentProps *latest_neighbor = m_neighbors.back();
684  make_pair(latest_neighbor, false));
685  latest_neighbor->setupComm();
686  }
687  }
688  }
689 
690  this->pubUpdatedNodesList();
691  this->pubLastRegdIDScan();
692 
693  MRPT_END;
694 } // end of usePublishersBroadcasters
695 
696 template <class GRAPH_T>
698  MRPT_START;
699  using namespace mrpt_msgs;
700 
701  // update the last X NodeIDs + positions; Do it only when a new node is
702  // inserted. notified of this change anyway after a new node addition
703  if (this->m_graph_nodes_last_size == this->m_graph.nodes.size()) {
704  return false;
705  }
706  MRPT_LOG_DEBUG_STREAM("Updating list of node poses");
707 
708  // send at most m_num_last_rgd_nodes
709  typename GRAPH_T::global_poses_t poses_to_send;
710  int poses_counter = 0;
711  // fill the NodeIDWithPose_vec msg
712  NodeIDWithPose_vec ros_nodes;
713 
714  // send up to num_last_regd_nodes Nodes - start from end.
715  for (typename GRAPH_T::global_poses_t::const_reverse_iterator cit =
716  this->m_graph.nodes.rbegin();
717  (poses_counter <= m_opts.num_last_regd_nodes &&
718  cit != this->m_graph.nodes.rend());
719  ++cit) {
720 
721  // Do not resend nodes of others, registered in own graph.
722  if (cit->second.agent_ID_str != m_conn_manager.getTrimmedNs()) {
723  continue; // skip this.
724  }
725 
726  NodeIDWithPose curr_node_w_pose;
727  // send basics - NodeID, Pose
728  curr_node_w_pose.nodeID = cit->first;
729  mrpt_bridge::convert(cit->second, curr_node_w_pose.pose);
730 
731  // send mr-fields
732  curr_node_w_pose.str_ID.data = cit->second.agent_ID_str;
733  curr_node_w_pose.nodeID_loc = cit->second.nodeID_loc;
734 
735  ros_nodes.vec.push_back(curr_node_w_pose);
736 
737  poses_counter++;
738  }
739 
740  m_last_regd_nodes_pub.publish(ros_nodes);
741 
742  // update the last known size
743  m_graph_nodes_last_size = this->m_graph.nodeCount();
744 
745  bool res = false;
746  if (poses_counter) {
747  res = true;
748  }
749 
750  return res;
751  MRPT_END;
752 } // end of pubUpdatedNodesList
753 
754 template <class GRAPH_T>
756  MRPT_START;
757  using namespace mrpt_msgs;
758  using namespace mrpt_bridge;
759 
760  // Update the last registered scan + associated nodeID
761  // Last registered scan always corresponds to the *last* element of the of the
762  // published NodeIDWithPose_vec that is published above.
763  //
764  // - Check if map is empty
765  // - Have I already published the last laser scan?
766  if (!this->m_nodes_to_laser_scans2D.empty() &&
768  this->m_nodes_to_laser_scans2D.size()) {
769  // last registered scan
770  MRPT_NodeIDWithLaserScan mrpt_last_regd_id_scan =
771  *(this->m_nodes_to_laser_scans2D.rbegin());
772 
773  // if this is a mr-registered nodeID+LS, skip it.
774  if (!this->isOwnNodeID(mrpt_last_regd_id_scan.first)) {
775  return false;
776  }
777 
778  // MRPT_LOG_DEBUG_STREAM
779  //<< "Publishing LaserScan of nodeID \""
780  //<< mrpt_last_regd_id_scan.first << "\"";
781  ASSERT_(mrpt_last_regd_id_scan.second);
782 
783  // convert to ROS msg
784  mrpt_msgs::NodeIDWithLaserScan ros_last_regd_id_scan;
785  convert(*(mrpt_last_regd_id_scan.second), ros_last_regd_id_scan.scan);
786  ros_last_regd_id_scan.nodeID = mrpt_last_regd_id_scan.first;
787 
788  m_last_regd_id_scan_pub.publish(ros_last_regd_id_scan);
789 
790  // update the last known size.
792 
793  return true;
794  } else {
795  return false;
796  }
797 
798  MRPT_END;
799 } // end of pubLastRegdIDScan
800 
801 template <class GRAPH_T>
803  const TNodeID nodeID, const global_pose_t *pose_out /*=NULL*/) const {
804  MRPT_START;
805  using namespace mrpt::graphs::detail;
806 
807  // make sure give node is in the graph
808  typename GRAPH_T::global_poses_t::const_iterator global_pose_search =
809  this->m_graph.nodes.find(nodeID);
810  ASSERTMSG_(global_pose_search != this->m_graph.nodes.end(),
811  mrpt::format("isOwnNodeID called for nodeID \"%lu\" which is not "
812  "found in the graph",
813  static_cast<unsigned long>(nodeID)));
814 
815  // fill pointer to global_pose_t
816  if (pose_out) {
817  pose_out = &global_pose_search->second;
818  }
819 
820  bool res = false;
821  const TMRSlamNodeAnnotations *node_annots =
822  dynamic_cast<const TMRSlamNodeAnnotations *>(&global_pose_search->second);
823  if ((node_annots &&
824  node_annots->agent_ID_str == m_conn_manager.getTrimmedNs()) ||
825  (!node_annots)) {
826  res = true;
827  }
828 
829  return res;
830 
831  MRPT_END;
832 } // end of isOwnNodeID
833 
834 template <class GRAPH_T> void CGraphSlamEngine_MR<GRAPH_T>::readParams() {
835  using namespace mrpt::utils;
836  using namespace mrpt::slam;
837 
838  this->readROSParameters();
839 
840  // GridMap Alignment
843 
844  // Thu Jun 8 17:02:17 EEST 2017, Nikos Koukis
845  // other option ends up in segfault.
846  m_alignment_options.methodSelection = CGridMapAligner::amModifiedRANSAC;
847 
849 } // end of readParams
850 
851 template <class GRAPH_T>
853  // call parent method first in case the parent wants to ask for any ROS
854  // parameters
856 }
857 
858 template <class GRAPH_T>
863 }
864 
865 template <class GRAPH_T> void CGraphSlamEngine_MR<GRAPH_T>::setupSubs() {}
866 
867 template <class GRAPH_T> void CGraphSlamEngine_MR<GRAPH_T>::setupPubs() {
868  using namespace mrpt_msgs;
869  using namespace sensor_msgs;
870 
871  m_list_neighbors_pub = m_nh->advertise<GraphSlamAgents>(
873  /*latch = */ true);
874 
875  // last registered laser scan - by default this corresponds to the last
876  // nodeID of the vector of last registered nodes.
877  m_last_regd_id_scan_pub = m_nh->advertise<NodeIDWithLaserScan>(
879  /*latch = */ true);
880 
881  // last X nodeIDs + positions
882  m_last_regd_nodes_pub = m_nh->advertise<NodeIDWithPose_vec>(
884  /*latch = */ true);
885 }
886 
887 template <class GRAPH_T> void CGraphSlamEngine_MR<GRAPH_T>::setupSrvs() {
888  // cm_graph service requests are handled by the custom custom_service_queue
889  // CallbackQueue
892 
895 
896  m_nh->setCallbackQueue(global_queue);
897 }
898 
899 template <class GRAPH_T>
901  mrpt_msgs::GetCMGraph::Request &req, mrpt_msgs::GetCMGraph::Response &res) {
902  using namespace std;
903  using namespace mrpt::utils;
904  using namespace mrpt::math;
905 
906  set<TNodeID> nodes_set(req.nodeIDs.begin(), req.nodeIDs.end());
907  MRPT_LOG_INFO_STREAM("Called the GetCMGraph service for nodeIDs: "
908  << getSTLContainerAsString(nodes_set));
909 
910  if (nodes_set.size() < 2) {
911  return false;
912  }
913 
914  // fill the given Response with the ROS NetworkOfPoses
915  GRAPH_T mrpt_subgraph;
916  this->m_graph.extractSubGraph(nodes_set, &mrpt_subgraph,
917  /*root_node = */ INVALID_NODEID,
918  /*auto_expand_set=*/false);
919  mrpt_bridge::convert(mrpt_subgraph, res.cm_graph);
920  return true;
921 } // end of getCMGraph
922 
923 template <class GRAPH_T>
925  const TNodeID nodeID, mrpt::opengl::CSetOfObjects::Ptr &viz_object) {
926  using namespace mrpt::utils;
927  MRPT_START;
928 
929  // who registered this node?
930  TNeighborAgentProps *neighbor = NULL;
931  std::string &agent_ID_str = this->m_graph.nodes.at(nodeID).agent_ID_str;
932  bool is_not_own = getNeighborByAgentID(agent_ID_str, neighbor);
933 
934  TColor obj_color;
935  if (!is_not_own) { // I registered this.
936  // use the standard maps color
937  obj_color = this->m_optimized_map_color;
938  } else {
939  ASSERT_(neighbor);
940  obj_color = neighbor->color;
941  }
942  viz_object->setColor_u8(obj_color);
943 
944  MRPT_END;
945 } // end of setObjectPropsFromNodeID
946 
947 template <class GRAPH_T>
949  bool registered /*=false*/, std::string class_name /*="Class"*/) {
950  MRPT_START;
951  using namespace mrpt::utils;
952 
954  MRPT_LOG_ERROR_STREAM("m_registered_multiple_nodes = TRUE!");
956  this->m_nodeID_max = this->m_graph.nodeCount() - 1;
957  } else {
958  parent_t::monitorNodeRegistration(registered, class_name);
959  }
960 
961  MRPT_END;
962 } // end of monitorNodeRegistration
963 
964 template <class GRAPH_T>
966  typename GRAPH_T::global_poses_t *graph_poses) const {
967  MRPT_START;
968  ASSERT_(graph_poses);
969 
970  for (const auto &n : this->m_graph.nodes) {
971  if (n.second.agent_ID_str == m_conn_manager.getTrimmedNs()) {
972  graph_poses->insert(n);
973  }
974  }
975 
976  MRPT_END;
977 } // end of getRobotEstimatedTrajectory
978 
979 template <class GRAPH_T>
981  std::set<TNodeID> *nodes_set) const {
982  ASSERT_(nodes_set);
983  nodes_set->clear();
984 
985  for (const auto &n : this->m_graph.nodes) {
986  if (n.second.agent_ID_str == m_conn_manager.getTrimmedNs()) {
987  nodes_set->insert(n.first);
988  }
989  }
990 } // end of getAllOwnNodes
991 
992 template <class GRAPH_T>
994  std::set<TNodeID> *nodes_set) const {
995  ASSERT_(nodes_set);
996  this->getAllOwnNodes(nodes_set);
997 } // end of getNodeIDsOfEstimatedTrajectory
998 
1000 
1001 template <class GRAPH_T>
1003  CGraphSlamEngine_MR<GRAPH_T> &engine_in,
1004  const mrpt_msgs::GraphSlamAgent &agent_in)
1005  : engine(engine_in), agent(agent_in), has_setup_comm(false) {
1006  using namespace mrpt::utils;
1007 
1008  nh = engine.m_nh;
1009  m_queue_size = engine.m_queue_size;
1010  this->resetFlags();
1011 
1012  // fill the full paths of the topics to subscribe
1013  // ASSUMPTION: agents namespaces start at the root "/"
1014  this->last_regd_nodes_topic =
1015  "/" + agent.topic_namespace.data + "/" + engine.m_last_regd_nodes_topic;
1016  this->last_regd_id_scan_topic =
1017  "/" + agent.topic_namespace.data + "/" + engine.m_last_regd_id_scan_topic;
1018  this->cm_graph_service =
1019  "/" + agent.topic_namespace.data + "/" + engine.m_cm_graph_service;
1020 
1021  engine.logFmt(LVL_DEBUG,
1022  "In constructor of TNeighborAgentProps for topic namespace: %s",
1023  agent.topic_namespace.data.c_str());
1024 
1025  // initialize the occupancy map based on the engine's gridmap properties
1026  gridmap_cached = mrpt::maps::COccupancyGridMap2D::Create();
1027  COccupancyGridMap2D::Ptr eng_gridmap = engine.m_gridmap_cached;
1028  gridmap_cached->setSize(eng_gridmap->getXMin(), eng_gridmap->getXMax(),
1029  eng_gridmap->getYMin(), eng_gridmap->getYMax(),
1030  eng_gridmap->getResolution());
1031 
1032 } // TNeighborAgentProps::TNeighborAgentProps
1033 
1034 template <class GRAPH_T>
1036 
1037 template <class GRAPH_T>
1039  using namespace mrpt::utils;
1040 
1041  this->setupSubs();
1042  this->setupSrvs();
1043 
1044  engine.logFmt(LVL_DEBUG,
1045  "TNeighborAgentProps: Successfully set up subscribers/services "
1046  "to agent topics for namespace [%s].",
1047  agent.topic_namespace.data.c_str());
1048 
1049  has_setup_comm = true;
1050 }
1051 
1052 template <class GRAPH_T>
1054 
1056  nh->serviceClient<mrpt_msgs::GetCMGraph>(cm_graph_service);
1057 }
1058 
1059 template <class GRAPH_T>
1061  using namespace mrpt_msgs;
1062  using namespace mrpt::utils;
1063 
1064  last_regd_nodes_sub = nh->subscribe<NodeIDWithPose_vec>(
1067  last_regd_id_scan_sub = nh->subscribe<NodeIDWithLaserScan>(
1070 }
1071 
1072 template <class GRAPH_T>
1074  const mrpt_msgs::NodeIDWithPose_vec::ConstPtr &nodes) {
1075  MRPT_START;
1076  using namespace mrpt_msgs;
1077  using namespace mrpt_bridge;
1078  using namespace std;
1079  using namespace mrpt::utils;
1080 
1081  typedef typename GRAPH_T::constraint_t::type_value pose_t;
1082  engine.logFmt(LVL_DEBUG, "In fetchUpdatedNodesList method.");
1083 
1084  for (NodeIDWithPose_vec::_vec_type::const_iterator n_it = nodes->vec.begin();
1085  n_it != nodes->vec.end(); ++n_it) {
1086 
1087  // insert in the set if not already there.
1088  TNodeID nodeID = static_cast<TNodeID>(n_it->nodeID);
1089  std::pair<set<TNodeID>::iterator, bool> res = nodeIDs_set.insert(nodeID);
1090  // if I just inserted this node mark it as not used (in own graph)
1091  if (res.second) { // insertion took place.
1092  engine.logFmt(LVL_INFO, "Just fetched a new nodeID: %lu",
1093  static_cast<unsigned long>(nodeID));
1094  nodeID_to_is_integrated.insert(make_pair(n_it->nodeID, false));
1095  }
1096 
1097  // update the poses
1098  pose_t curr_pose;
1099  // note: use "operator[]" instead of "insert" so that if the key already
1100  // exists, the corresponding value is changed rather than ignored.
1101  poses[static_cast<TNodeID>(n_it->nodeID)] = convert(n_it->pose, curr_pose);
1102  }
1103  has_new_nodes = true;
1104 
1106  // TODO When using 3 graphSLAM agents GDB Shows that it crashes on
1107  // engine.logFmt lines. Fix this.
1108  //
1109  // engine.logFmt(LVL_DEBUG, // THIS CRASHES - GDB WHERE
1110  //"NodeIDs for topic namespace: %s -> [%s]",
1111  // agent.topic_namespace.data.c_str(),
1112  // mrpt::math::getSTLContainerAsString(vector<TNodeID>(
1113  // nodeIDs_set.begin(), nodeIDs_set.end())).c_str());
1114  // print poses just for verification
1115  // engine.logFmt(LVL_DEBUG, "Poses for topic namespace: %s",
1116  // agent.topic_namespace.data.c_str());
1117  // for (typename GRAPH_T::global_poses_t::const_iterator
1118  // p_it = poses.begin();
1119  // p_it != poses.end();
1120  //++p_it) {
1121  // std::string p_str; p_it->second.asString(p_str);
1122  // engine.logFmt(LVL_DEBUG, "nodeID: %lu | pose: %s",
1123  // static_cast<unsigned long>(p_it->first),
1124  // p_str.c_str());
1125  //}
1126  // TODO - These also seem to crash sometimes
1127  // cout << "Agent information: " << agent << endl;
1128  // cout << "Nodes: " <<
1129  // mrpt::math::getSTLContainerAsString(vector<TNodeID>(nodeIDs_set.begin(),
1130  // nodeIDs_set.end()));
1131  // print nodeIDs just for verification
1132 
1133  MRPT_END;
1134 } // end of fetchUpdatedNodesList
1135 
1136 template <class GRAPH_T>
1138  const mrpt_msgs::NodeIDWithLaserScan::ConstPtr &last_regd_id_scan) {
1139  MRPT_START;
1140  using namespace std;
1141  using namespace mrpt::utils;
1142  ASSERT_(last_regd_id_scan);
1143  engine.logFmt(LVL_DEBUG, "In fetchLastRegdIDScan method.");
1144 
1145  // Pose may not be available due to timing in publishing of the corresponding
1146  // topics. Just keep it in ROS form and then convert them to MRPT form when
1147  // needed.
1148  ros_scans.push_back(*last_regd_id_scan);
1149  has_new_scans = true;
1150 
1151  MRPT_END;
1152 } // end of fetchLastRegdIDScan
1153 
1154 template <class GRAPH_T>
1156  std::vector<TNodeID> *nodeIDs /*=NULL*/,
1157  std::map<TNodeID, node_props_t> *nodes_params /*=NULL*/,
1158  bool only_unused /*=true*/) const {
1159  MRPT_START;
1160  using namespace mrpt::obs;
1161  using namespace mrpt::utils;
1162  using namespace mrpt::math;
1163  using namespace std;
1164  using namespace mrpt::poses;
1165 
1166  // at least one of the two args should be given.
1167  ASSERT_(nodeIDs || nodes_params);
1168 
1169  // traverse all nodes
1170  for (std::set<TNodeID>::const_iterator n_it = nodeIDs_set.begin();
1171  n_it != nodeIDs_set.end(); ++n_it) {
1172 
1173  // Should I return only the unused ones?
1174  // If so, if the current nodeID is integrated, skip it.
1175  if (only_unused && nodeID_to_is_integrated.at(*n_it)) {
1176  continue;
1177  }
1178 
1179  // add the node properties
1180  if (nodes_params) {
1181  std::pair<TNodeID, node_props_t> params; // current pair to be inserted.
1182  const pose_t *p = &poses.at(*n_it);
1183 
1184  params.first = *n_it;
1185  params.second.pose = *p;
1186  CObservation2DRangeScan::Ptr mrpt_scan =
1187  CObservation2DRangeScan::Create();
1188  const sensor_msgs::LaserScan *ros_laser_scan =
1189  this->getLaserScanByNodeID(*n_it);
1190 
1191  // if LaserScan not found, skip nodeID altogether.
1192  //
1193  // Its natural to have more poses than LaserScans since on every node
1194  // registration I send a modified list of X node positions and just one
1195  // LaserScan
1196  if (!ros_laser_scan) {
1197  continue;
1198  }
1199 
1200  mrpt_bridge::convert(*ros_laser_scan, CPose3D(*p), *mrpt_scan);
1201  params.second.scan = mrpt_scan;
1202 
1203  // insert the pair
1204  nodes_params->insert(params);
1205 
1206  // debugging message
1207  // std::stringstream ss;
1208  // ss << "\ntID: " << nodes_params->rbegin()->first
1209  //<< " ==> Props: " << nodes_params->rbegin()->second;
1210  // engine.logFmt(LVL_DEBUG, "%s", ss.str().c_str());
1211  }
1212 
1213  // add the nodeID
1214  if (nodeIDs) {
1215  // do not return nodeIDs that don't have valid laserScans
1216  // this is also checked at the prior if
1217  if (!nodes_params && !this->getLaserScanByNodeID(*n_it)) {
1218  continue;
1219  }
1220 
1221  nodeIDs->push_back(*n_it);
1222  }
1223  }
1224 
1225  MRPT_END;
1226 } // end of TNeighborAgentProps::getCachedNodes
1227 
1228 template <class GRAPH_T>
1230  has_new_nodes = false;
1231  has_new_scans = false;
1232 }
1233 
1234 template <class GRAPH_T>
1236  int new_batch_size) {
1237  MRPT_START;
1238 
1239  auto is_not_integrated = [this](const std::pair<TNodeID, bool> pair) {
1240  return (pair.second == false && getLaserScanByNodeID(pair.first));
1241  };
1242  int not_integrated_num =
1243  count_if(nodeID_to_is_integrated.begin(), nodeID_to_is_integrated.end(),
1244  is_not_integrated);
1245 
1246  return (not_integrated_num >= new_batch_size);
1247 
1248  MRPT_END;
1249 }
1250 
1251 template <class GRAPH_T>
1252 const sensor_msgs::LaserScan *
1254  const TNodeID nodeID) const {
1255  MRPT_START;
1256 
1257  // assert that the current nodeID exists in the nodeIDs_set
1258  ASSERT_(nodeIDs_set.find(nodeID) != nodeIDs_set.end());
1259 
1260  for (std::vector<mrpt_msgs::NodeIDWithLaserScan>::const_iterator it =
1261  ros_scans.begin();
1262  it != ros_scans.end(); ++it) {
1263  if (it->nodeID == nodeID) {
1264  return &it->scan;
1265  }
1266  }
1267 
1268  // if out here, LaserScan doesn't exist.
1269  return 0;
1270  MRPT_END;
1271 } // end of TNeighborAgentProps::getLaserScanByNodeID
1272 
1273 template <class GRAPH_T>
1275  const std::set<TNodeID> &nodeIDs, paths_t *opt_paths) const {
1276  MRPT_START;
1277  using namespace std;
1278  using namespace mrpt::utils;
1279  typedef std::set<TNodeID>::const_iterator set_cit;
1280 
1281  // make sure that all given nodes belong in the nodeIDs_set.
1282  ASSERTMSG_(includes(nodeIDs_set.begin(), nodeIDs_set.end(), nodeIDs.begin(),
1283  nodeIDs.end()),
1284  "nodeIDs is not a strict subset of the current neighbor's nodes");
1285  ASSERT_(opt_paths);
1286 
1287  for (set_cit cit = nodeIDs.begin(); cit != std::prev(nodeIDs.end()); ++cit) {
1288 
1289  for (set_cit cit2 = std::next(cit); cit2 != nodeIDs.end(); ++cit2) {
1290 
1291  constraint_t c = constraint_t(poses.at(*cit2) - poses.at(*cit));
1292  c.cov_inv =
1293  mrpt::math::CMatrixFixedNumeric<double, constraint_t::state_length,
1294  constraint_t::state_length>();
1295  c.cov_inv.unit();
1296  c.cov_inv *= 500;
1297 
1298  opt_paths->push_back(path_t(
1299  /*start=*/*cit,
1300  /*end=*/*cit2,
1301  /*constraint=*/c));
1302 
1303  engine.logFmt(
1304  LVL_DEBUG, "Filling optimal path for nodes: %lu => %lu: %s",
1305  static_cast<unsigned long>(*cit), static_cast<unsigned long>(*cit2),
1306  opt_paths->back().curr_pose_pdf.getMeanVal().asString().c_str());
1307  }
1308  }
1309  MRPT_END;
1310 } // end of TNeighborAgentProps::fillOptPaths
1311 
1312 template <class GRAPH_T>
1314  MRPT_START;
1315  using namespace mrpt::poses;
1316  using namespace mrpt::utils;
1317  using namespace mrpt;
1318  using namespace mrpt::math;
1319  using namespace std;
1320 
1321  gridmap_cached->clear();
1322 
1323  std::vector<TNodeID> nodeIDs;
1324  std::map<TNodeID, node_props_t> nodes_params;
1325  // get list of nodes, laser scans
1326  this->getCachedNodes(&nodeIDs, &nodes_params, false);
1327 
1328  // iterate over poses, scans. Add them to the gridmap.
1329  for (typename map<TNodeID, node_props_t>::const_iterator it =
1330  nodes_params.begin();
1331  it != nodes_params.end(); ++it) {
1332  const CPose3D curr_pose_3d; // do not add the actual pose!
1333  auto obs = it->second.scan.get();
1334 #if MRPT_VERSION >= 0x199
1335  gridmap_cached->insertObservation(*obs, &curr_pose_3d);
1336 #else
1337  gridmap_cached->insertObservation(obs, &curr_pose_3d);
1338 #endif
1339  }
1340 
1341  MRPT_END;
1342 } // end of TNeighborAgentProps::computeGridMap
1343 
1344 template <class GRAPH_T>
1345 const mrpt::maps::COccupancyGridMap2D::Ptr &
1347  MRPT_START;
1348  if (hasNewData()) {
1349  this->computeGridMap();
1350  }
1351 
1352  return gridmap_cached;
1353  MRPT_END;
1354 } // end of TNeighborAgentProps::getGridMap
1355 
1356 template <class GRAPH_T>
1358  mrpt::maps::COccupancyGridMap2D::Ptr &map) const {
1359  MRPT_START;
1360  ASSERT_(map);
1361 
1362  if (hasNewData()) {
1363  this->computeGridMap();
1364  }
1365 
1366  map->copyMapContentFrom(*gridmap_cached);
1367  MRPT_END;
1368 } // end of TNeighborAgentProps::getGridMap
1369 
1370 template <class GRAPH_T>
1372  return has_new_nodes && has_new_scans;
1373 } // end of hasNewData
1374 
1375 template <class GRAPH_T>
1377  : conservative_find_initial_tfs_to_neighbors(false),
1378  nodes_integration_batch_size(4), num_last_regd_nodes(10),
1379  inter_group_node_count_thresh(40),
1380  inter_group_node_count_thresh_minadv(25), engine(engine_in) {
1381 } // end of TOptions::TOptions
1382 
1383 template <class GRAPH_T>
1385 } // end of TOptions::~TOptions
1386 
1387 template <class GRAPH_T>
1389  const CConfigFileBase &source, const std::string &section) {
1390 
1391  MRPT_LOAD_CONFIG_VAR(nodes_integration_batch_size, int, source, section);
1392  MRPT_LOAD_CONFIG_VAR(num_last_regd_nodes, int, source, section);
1393 
1395  section);
1397  "Conservative behavior is not yet implemented.");
1398 
1399  MRPT_LOAD_CONFIG_VAR(inter_group_node_count_thresh, int, source, section);
1400  // warn user if they choose smaller threshold.
1402  engine.logFmt(LVL_ERROR,
1403  "inter_group_node_count_thresh [%d]"
1404  "is set lower than the advised minimum [%d]",
1407 
1408  std::this_thread::sleep_for(std::chrono::milliseconds(2000));
1409  }
1410 
1411 } // end of TOptions::loadFromConfigFile
1412 
1413 template <class GRAPH_T>
1415 #if MRPT_VERSION >= 0x199
1416  std::ostream &out) const {
1417 #else
1418  mrpt::utils::CStream &out) const {
1419 #endif
1420 
1426 
1427 } // end of TOptions::dumpToTextStream
1428 
1429 } // namespace graphslam
1430 } // namespace mrpt
virtual mrpt::poses::CPose3D getLSPoseForGridMapVisualization(const mrpt::utils::TNodeID nodeID) const
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void dumpToTextStream(mrpt::utils::CStream &out) const
bool pubUpdatedNodesList()
Update the last registered NodeIDs and corresponding positions of the current agent.
void computeGridMap() const
Using the fetched LaserScans and nodeID positions, compute the occupancy gridmap of the given neighbo...
mrpt::maps::COccupancyGridMap2DPtr m_gridmap_cached
void getCachedNodes(std::vector< TNodeID > *nodeIDs=NULL, std::map< TNodeID, node_props_t > *nodes_params=NULL, bool only_unused=true) const
Return cached list of nodeIDs (with their corresponding poses, LaserScans)
GRAPH_T::constraint_t::type_value pose_t
mrpt::graphslam::deciders::CNodeRegistrationDecider< GRAPH_T > * m_node_reg
std::string m_list_neighbors_topic
Name of topic at which we publish information about the agents that we can currently communicate with...
ros::AsyncSpinner cm_graph_async_spinner
AsyncSpinner that is used to query the CM-Graph service in case a new request arrives.
void publish(const boost::shared_ptr< M > &message) const
void loadFromConfigFileName(const std::string &config_file, const std::string &section)
void addTextMessage(const double x, const double y, const std::string &text, const mrpt::utils::TColorf &color=mrpt::utils::TColorf(1.0, 1.0, 1.0), const size_t unique_index=0)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
const GLfloat * c
bool hasNewNodesBatch(int new_batch_size)
Decide whether there are enough new nodes + scans that have not been integrated in the graph yet...
GRAPH_T::constraint_t constraint_t
#define THROW_EXCEPTION(msg)
ros::Publisher m_last_regd_id_scan_pub
Publisher of the laserScan + the corresponding (last) registered node.
bool m_registered_multiple_nodes
Indicates whether multiple nodes were just registered. Used for checking correct node registration in...
#define LOADABLEOPTS_DUMP_VAR(variableName, variableType)
#define INVALID_NODEID
mrpt::utils::TColor getNextTColor()
std::string getSTLContainerAsString(const T &t)
bool addNodeBatchesFromAllNeighbors()
Traverse all neighbors for which I know the inter-graph transformation, run addNodeBatchFromNeighbor...
bool call(MReq &req, MRes &res)
std::set< TNodeID > nodeIDs_set
NodeIDs that I have received from this graphSLAM agent.
#define MRPT_LOG_WARN_STREAM(__CONTENTS)
ros::NodeHandle * nh
NodeHandle passed by the calling CGraphSlamEngine_MR class.
std::vector< mrpt_msgs::NodeIDWithLaserScan > ros_scans
ROS LaserScans that I have received from this graphSLAM agent.
mrpt::graphslam::deciders::CEdgeRegistrationDecider< GRAPH_T > * m_edge_reg
#define MRPT_LOG_INFO_STREAM(__CONTENTS)
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
std::pair< TNodeID, mrpt::obs::CObservation2DRangeScan::Ptr > MRPT_NodeIDWithLaserScan
TColor color
Unique color of current TNeighborAgentProps instance.
void getNodeIDsOfEstimatedTrajectory(std::set< TNodeID > *nodes_set) const
bool findTFsWithAllNeighbors()
Using map-merging find a potentuial transofrmation between own graph map to another agent&#39;s map and u...
#define MRPT_LOG_DEBUG_STREAM(__CONTENTS)
mrpt::graphslam::TUncertaintyPath< GRAPH_T > path_t
std::map< TNeighborAgentProps *, bool > m_neighbor_to_found_initial_tf
Map from neighbor instance to transformation from own to neighbor&#39;s graph.
bool getNeighborByAgentID(const std::string &agent_ID_str, TNeighborAgentProps *&neighbor) const
Fill the TNeighborAgentProps instance based on the given agent_ID_str string that is provided...
mrpt::poses::CPosePDFPtr AlignPDF(const mrpt::maps::CMetricMap *m1, const mrpt::maps::CMetricMap *m2, const mrpt::poses::CPosePDFGaussian &initialEstimationPDF, float *runningTime=NULL, void *info=NULL)
std::string getGridMapAlignmentResultsAsString(const mrpt::poses::CPosePDF &pdf, const mrpt::slam::CGridMapAligner::TReturnInfo &ret_info)
Definition: common.cpp:26
TNeighborAgentProps(CGraphSlamEngine_MR< GRAPH_T > &engine_in, const mrpt_msgs::GraphSlamAgent &agent_in)
Constructor.
void fetchLastRegdIDScan(const mrpt_msgs::NodeIDWithLaserScan::ConstPtr &last_regd_id_scan)
Fill the LaserScan of the last registered nodeID.
GLsizei n
#define MRPT_LOAD_CONFIG_VAR(variableName, variableType, configFileObject, sectionNameStr)
#define MRPT_END
void setupSubs()
Setup the necessary subscribers for fetching nodes, laserScans for the current neighbor.
size_t m_graph_nodes_last_size
Last known size of the m_nodes map.
mrpt::poses::CPose2D tf_self_to_neighbor_first_integrated_pose
CPose2D connecting own graph origin to neighbor first received and integrated node. If this pose is 0 then it is not yet computed.
const mrpt::maps::COccupancyGridMap2D::Ptr & getGridMap() const
obs
std::map< TNodeID, bool > nodeID_to_is_integrated
Have I already integrated this node in my graph?
bool _execGraphSlamStep(mrpt::obs::CActionCollection::Ptr &action, mrpt::obs::CSensoryFrame::Ptr &observations, mrpt::obs::CObservation::Ptr &observation, size_t &rawlog_entry)
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
virtual void monitorNodeRegistration(bool registered=false, std::string class_name="Class")
mrpt::slam::CGridMapAligner::TConfigParams m_alignment_options
Parameters used during the alignment operation.
void fillOptPaths(const std::set< TNodeID > &nodeIDs, paths_t *opt_paths) const
Fill the optimal paths for each combination of the given nodeIDs.
void getRobotEstimatedTrajectory(typename GRAPH_T::global_poses_t *graph_poses) const
size_t inter_group_node_count_thresh
Lowest number of nodes that should exist in a group of nodes before evaluating it. These nodes are fetched by the other running graphSLAM agents.
virtual void setCConnectionManagerPtr(mrpt::graphslam::detail::CConnectionManager *conn_manager)
uint64_t TNodeID
mrpt::graphslam::detail::CConnectionManager m_conn_manager
CConnectionManager instance.
void setCallbackQueue(CallbackQueueInterface *queue)
void setTColor(const TColor &color_in)
Set the color related to the current neighbor agent.
GLfloat GLfloat p
bool isEssentiallyZero(const mrpt::poses::CPose2D &p)
Definition: common.cpp:50
mrpt::graphslam::CWindowManager * m_win_manager
bool convert(const mrpt_msgs::ObservationRangeBeacon &_msg, const mrpt::poses::CPose3D &_pose, mrpt::obs::CObservationBeaconRanges &_obj)
std::vector< CMatrixD > results
ros::NodeHandle * m_nh
NodeHandle pointer - inherited by the parent. Redefined here for convenience.
void setEdge(const constraint_t &edge)
bool pubLastRegdIDScan()
Update the last registered NodeID and LaserScan of the current agent.
void fetchUpdatedNodesList(const mrpt_msgs::NodeIDWithPose_vec::ConstPtr &nodes)
Update nodeIDs + corresponding estimated poses.
int num_last_regd_nodes
Max number of last registered NodeIDs + corresponding positions to publish.
virtual bool _execGraphSlamStep(mrpt::obs::CActionCollection::Ptr &action, mrpt::obs::CSensoryFrame::Ptr &observations, mrpt::obs::CObservation::Ptr &observation, size_t &rawlog_entry)
void usePublishersBroadcasters()
Provide feedback about the SLAM operation.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
GLuint res
void loadFromConfigFile(const CConfigFileBase &source, const std::string &section)
Interface for implementing deciders/optimizers related to the Condensed Measurements multi-robot grap...
mrpt::graphslam::CGraphSlamEngine_MR::TOptions m_opts
ros::CallbackQueue custom_service_queue
Custom Callback queue for processing requests for the services outside the standard CallbackQueue...
virtual void usePublishersBroadcasters()
Provide feedback about the SLAM operation.
#define MRPT_START
mrpt::utils::TNodeID m_nodeID_max
mrpt::poses::CPose3D getLSPoseForGridMapVisualization(const TNodeID nodeID) const
void monitorNodeRegistration(bool registered=false, std::string class_name="Class")
Overriden method that takes in account registration of multiple nodes of other running graphSLAM agen...
bool findTFWithNeighbor(TNeighborAgentProps *neighbor)
Method is used only when I haven&#39;t found any transformation between mine and the current neighbor&#39;s g...
virtual void setClassName(const std::string &name)
void convert(const ros::Time &src, mrpt::system::TTimeStamp &des)
mrpt::utils::TColor m_optimized_map_color
bool conservative_find_initial_tfs_to_neighbors
Be conservative when it comes to deciding the initial transformation of own graph with regards to gra...
std::string m_mr_ns
Condensed Measurements topic namespace.
const std::string & getTrimmedNs() const
Get the agent ROS namespace.
int nodes_integration_batch_size
After an inter-graph transformation is found between own graph and a neighbor&#39;s map, newly fetched neighbor&#39;s nodes are added in batches.
mrpt::maps::COccupancyGridMap2D::Ptr gridmap_cached
Pointer to the gridmap object of the neighbor.
ros::Publisher m_last_regd_nodes_pub
Publisher of the last registered nodeIDs and positions.
nodes_to_scans2D_t m_nodes_to_laser_scans2D
GRAPH_T::global_pose_t global_pose_t
bool getCMGraph(mrpt_msgs::GetCMGraph::Request &req, mrpt_msgs::GetCMGraph::Response &res)
Compute and fill the Condensed Measurements Graph.
void setObjectPropsFromNodeID(const TNodeID nodeID, mrpt::opengl::CSetOfObjects::Ptr &viz_object)
mrpt::slam::CGridMapAligner::TConfigParams options
void setupSrvs()
Setup necessary services for neighbor.
void getNearbySlamAgents(mrpt_msgs::GraphSlamAgents *agents_vec, bool ignore_self=true)
Fill the given vector with the SLAM Agents that the current manager can see and communicate with...
mrpt::graphslam::optimizers::CGraphSlamOptimizer< GRAPH_T > * m_optimizer
#define ASSERT_(f)
GLfloat * params
virtual void addBatchOfNodeIDsAndScans(const std::map< TNodeID, mrpt::obs::CObservation2DRangeScan::Ptr > &nodeIDs_to_scans2D)
neighbors_t m_neighbors
GraphSlamAgent instance pointer to TNeighborAgentProps.
size_t inter_group_node_count_thresh_minadv
Minimum advised limit of inter_group_node_count_thresh.
double m_offset_y_nrd
Display the Deciders/Optimizers with which we are running as well as the namespace of the current age...
void setupComm()
Wrapper for calling setupSubs, setupSrvs.
GRAPH_T::global_poses_t poses
Poses that I have received from this graphSLAM agent.
#define MRPT_LOG_ERROR_STREAM(__CONTENTS)
std::string m_cm_graph_service
Name of the server which is to be called when other agent wants to have a subgraph of certain nodes r...
std::map< mrpt::utils::TNodeID, mrpt::obs::CObservation2DRangeScanPtr > nodes_to_scans2D_t
bool isOwnNodeID(const TNodeID nodeID, const global_pose_t *pose_out=NULL) const
Return true if current CGraphSlamEngine_MR object initially registered this nodeID, false otherwise.
ROSCPP_DECL bool getNodes(V_string &nodes)
CallbackQueueInterface * getCallbackQueue() const
std::pair< TNodeID, mrpt::poses::CPose2D > last_integrated_pair_neighbor_frame
Last nodeID/CPose2D of neighbor that was integrated - Pose is taken with regards to neighbor&#39;s frame ...
mrpt::graphslam::CGraphSlamEngine derived class for executing multi-robot graphSLAM ...
bool hasNewData() const
Return True if there are new data (node positions and corresponding LaserScans available) ...
std::string m_last_regd_id_scan_topic
Name of the topic that the last registered laser scan (+ corresponding nodeID) is published at...
ROSCPP_DECL bool search(const std::string &ns, const std::string &key, std::string &result)
#define ASSERTMSG_(f, __ERROR_MSG)
size_t m_nodes_to_laser_scans2D_last_size
Last known size of the m_nodes_to_laser_scans2D map.
Struct responsible for holding properties (nodeIDs, node positions, LaserScans) that have been regist...
void readROSParameters()
Read configuration parameters from the ROS parameter server.
bool addNodeBatchFromNeighbor(TNeighborAgentProps *neighbor)
neighbors for which I know the inter-graph transformation, add new batches of fetches nodeIDs and sca...
std::string m_last_regd_nodes_topic
Name of the topic that the last X registered nodes + positions are going to be published at...
GLsizei GLsizei GLchar * source
void assignTextMessageParameters(double *offset_y, int *text_index)
void setupComm()
Wrapper method around the protected setup* class methods.
ROSCPP_DECL bool waitForService(const std::string &service_name, int32_t timeout)
void asString(std::string &s) const
void getAllOwnNodes(std::set< TNodeID > *nodes_set) const
Fill given set with the nodeIDs that were initially registered by the current graphSLAM engine (and n...
const sensor_msgs::LaserScan * getLaserScanByNodeID(const TNodeID nodeID) const


mrpt_graphslam_2d
Author(s): Nikos Koukis
autogenerated on Sat May 2 2020 03:44:17