Main MRPT website > C++ reference for MRPT 1.5.7
CNetworkOfPoses_impl.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 #ifndef CONSTRAINED_POSE_NETWORK_IMPL_H
10 #define CONSTRAINED_POSE_NETWORK_IMPL_H
11 
12 #include <mrpt/graphs/dijkstra.h>
16 #include <mrpt/math/wrap2pi.h>
17 #include <mrpt/math/ops_matrices.h> // multiply_*()
20 #include <mrpt/poses/CPose2D.h>
21 #include <mrpt/poses/CPose3D.h>
22 #include <mrpt/poses/CPose3DQuat.h>
28 
29 namespace mrpt
30 {
31  namespace graphs
32  {
33  namespace detail
34  {
35  using namespace std;
36  using namespace mrpt;
37  using namespace mrpt::utils;
38  using namespace mrpt::poses;
39  using namespace mrpt::graphs;
40 
41  template <class POSE_PDF> struct TPosePDFHelper
42  {
43  static inline void copyFrom2D(POSE_PDF &p, const CPosePDFGaussianInf &pdf) { p.copyFrom(pdf); }
44  static inline void copyFrom3D(POSE_PDF &p, const CPose3DPDFGaussianInf &pdf) { p.copyFrom(pdf); }
45  };
46  template <> struct TPosePDFHelper<CPose2D>
47  {
48  static inline void copyFrom2D(CPose2D &p, const CPosePDFGaussianInf &pdf) { p = pdf.mean; }
49  static inline void copyFrom3D(CPose2D &p, const CPose3DPDFGaussianInf &pdf) { p = CPose2D(pdf.mean); }
50  };
51  template <> struct TPosePDFHelper<CPose3D>
52  {
53  static inline void copyFrom2D(CPose3D &p, const CPosePDFGaussianInf &pdf) { p = CPose3D(pdf.mean); }
54  static inline void copyFrom3D(CPose3D &p, const CPose3DPDFGaussianInf &pdf) { p = pdf.mean; }
55  };
56 
57  /// a helper struct with static template functions \sa CNetworkOfPoses
58  template <class graph_t>
59  struct graph_ops
60  {
61  static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose2D &p, std::ostream &f)
62  {
63  // VERTEX2 id x y phi
64  f << "VERTEX_SE2 " << id << " " << p.x() << " " << p.y() << " " << p.phi();
65  }
66  static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose3D &p, std::ostream &f)
67  {
68  // VERTEX3 id x y z roll pitch yaw
69  // **CAUTION** In the TORO graph format angles are in the RPY order vs. MRPT's YPR.
70  f << "VERTEX3 " << id << " " << p.x() << " " << p.y() << " " << p.z()<< " " << p.roll()<< " " << p.pitch()<< " " << p.yaw();
71  }
72 
73 
74  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const CPosePDFGaussianInf & edge, std::ostream &f)
75  {
76  // G2O: EDGE_SE2 from_id to_id Ax Ay Aphi inf_xx inf_xy inf_yy inf_pp inf_xp inf_yp
77  f << "EDGE_SE2 " << edgeIDs.first << " " << edgeIDs.second << " " <<
78  edge.mean.x()<<" "<<edge.mean.y()<<" "<<edge.mean.phi()<<" "<<
79  edge.cov_inv(0,0)<<" "<<edge.cov_inv(0,1)<<" "<<edge.cov_inv(0,2)<<" "<<
80  edge.cov_inv(1,1)<<" "<<edge.cov_inv(1,2)<<" "<<edge.cov_inv(2,2) << std::endl;
81  }
82  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const CPose3DPDFGaussianInf & edge, std::ostream &f)
83  {
84  // EDGE3 from_id to_id Ax Ay Az Aroll Apitch Ayaw inf_11 inf_12 .. inf_16 inf_22 .. inf_66
85  // **CAUTION** In the TORO graph format angles are in the RPY order vs. MRPT's YPR.
86  f << "EDGE3 " << edgeIDs.first << " " << edgeIDs.second << " " <<
87  edge.mean.x()<<" "<<edge.mean.y()<<" "<<edge.mean.z()<<" "<<
88  edge.mean.roll()<<" "<<edge.mean.pitch()<<" "<<edge.mean.yaw()<<" "<<
89  edge.cov_inv(0,0)<<" "<<edge.cov_inv(0,1)<<" "<<edge.cov_inv(0,2)<<" "<<edge.cov_inv(0,5)<<" "<<edge.cov_inv(0,4)<<" "<<edge.cov_inv(0,3)<<" "<<
90  edge.cov_inv(1,1)<<" "<<edge.cov_inv(1,2)<<" "<<edge.cov_inv(1,5)<<" "<<edge.cov_inv(1,4)<<" "<<edge.cov_inv(1,3)<<" "<<
91  edge.cov_inv(2,2)<<" "<<edge.cov_inv(2,5)<<" "<<edge.cov_inv(2,4)<<" "<<edge.cov_inv(2,3)<<" "<<
92  edge.cov_inv(5,5)<<" "<<edge.cov_inv(5,4)<<" "<<edge.cov_inv(5,3)<<" "<<
93  edge.cov_inv(4,4)<<" "<<edge.cov_inv(4,3)<<" "<<
94  edge.cov_inv(3,3) << std::endl;
95  }
96  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const CPosePDFGaussian & edge, std::ostream &f)
97  {
99  p.copyFrom(edge);
100  write_EDGE_line(edgeIDs,p,f);
101  }
102  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const CPose3DPDFGaussian & edge, std::ostream &f)
103  {
105  p.copyFrom(edge);
106  write_EDGE_line(edgeIDs,p,f);
107  }
108  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const mrpt::poses::CPose2D & edge, std::ostream &f)
109  {
111  p.mean = edge;
112  p.cov_inv.unit(3,1.0);
113  write_EDGE_line(edgeIDs,p,f);
114  }
115  static void write_EDGE_line(const TPairNodeIDs &edgeIDs,const mrpt::poses::CPose3D & edge, std::ostream &f)
116  {
118  p.mean = edge;
119  p.cov_inv.unit(6,1.0);
120  write_EDGE_line(edgeIDs,p,f);
121  }
122 
123 
125  const graph_t* g, std::ostream& f)
126  {
127  // 1st: Nodes
128  for (const auto& n : g->nodes)
129  {
130  write_VERTEX_line(n.first, n.second, f);
131  // Node annotations:
132  const auto sAnnot = n.second.retAnnotsAsString();
133  if (!sAnnot.empty()) f << " | " << sAnnot;
134  f << endl;
135  // Root?
136  if (n.first == g->root) f << "FIX " << n.first << endl;
137  }
138 
139  // 2nd: Edges:
140  for (const auto& e : *g)
141  if (e.first.first != e.first.second) // ignore EQUIV edges
142  write_EDGE_line(e.first, e.second, f);
143 
144  }
145 
146  // =================================================================
147  // save_graph_of_poses_to_text_file
148  // =================================================================
150  const graph_t* g, const std::string& fil)
151  {
152  std::ofstream f;
153  f.open(fil.c_str());
154  if (!f.is_open())
156  "Error opening file '%s' for writing", fil.c_str());
157  save_graph_of_poses_to_ostream(g, f);
158  }
159 
160  // =================================================================
161  // save_graph_of_poses_to_binary_file
162  // =================================================================
164  {
165  // Store class name:
166  const std::string sClassName = TTypeName<graph_t>::get();
167  out << sClassName;
168 
169  // Store serialization version & object data:
170  const uint32_t version = 0;
171  out << version;
172  out << g->nodes << g->edges << g->root;
173  }
174 
175  // =================================================================
176  // read_graph_of_poses_from_binary_file
177  // =================================================================
179  {
180  // Compare class name:
181  const std::string sClassName = TTypeName<graph_t>::get();
182  std::string sStoredClassName;
183  in >> sStoredClassName;
184  ASSERT_EQUAL_(sStoredClassName,sClassName)
185 
186  // Check serialization version:
187  uint32_t stored_version;
188  in >> stored_version;
189 
190  g->clear();
191  switch (stored_version)
192  {
193  case 0:
194  in >> g->nodes >> g->edges >> g->root;
195  break;
196  default: MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(stored_version)
197  }
198 
199  }
200 
201  // =================================================================
202  // load_graph_of_poses_from_text_file
203  // =================================================================
205  graph_t* g, std::istream& f,
206  const std::string& fil = std::string("(none)"))
207  {
208  using mrpt::system::strCmpI;
209  using namespace mrpt::math;
210 
211  using CPOSE = typename graph_t::constraint_t;
212 
213  set<string> alreadyWarnedUnknowns; // for unknown line types, show a
214  // warning to cerr just once.
215 
216  // First, empty the graph:
217  g->clear();
218 
219  // Determine if it's a 2D or 3D graph, just to raise an error if loading
220  // a 3D graph in a 2D one, since
221  // it would be an unintentional loss of information:
222  const bool graph_is_3D = CPOSE::is_3D();
223 
225 
226  // -------------------------------------------
227  // 1st PASS: Read EQUIV entries only
228  // since processing them AFTER loading the data
229  // is much much slower.
230  // -------------------------------------------
231  map<TNodeID, TNodeID> lstEquivs; // List of EQUIV entries: NODEID ->
232  // NEWNODEID. NEWNODEID will be always
233  // the lowest ID number.
234 
235  // Read & process lines each at once until EOF:
236  istringstream s;
237  while (filParser.getNextLine(s))
238  {
239  const unsigned int lineNum = filParser.getCurrentLineNumber();
240  const string lin = s.str();
241 
242  string key;
243  if (!(s >> key) || key.empty())
245  "Line %u: Can't read string for entry type in: '%s'",
246  lineNum, lin.c_str()));
247 
248  if (mrpt::system::strCmpI(key, "EQUIV"))
249  {
250  // Process these ones at the end, for now store in a list:
251  TNodeID id1, id2;
252  if (!(s >> id1 >> id2))
254  "Line %u: Can't read id1 & id2 in EQUIV line: '%s'",
255  lineNum, lin.c_str()));
256  lstEquivs[std::max(id1, id2)] = std::min(id1, id2);
257  }
258  } // end 1st pass
259 
260  // -------------------------------------------
261  // 2nd PASS: Read all other entries
262  // -------------------------------------------
263  filParser.rewind();
264 
265  // Read & process lines each at once until EOF:
266  while (filParser.getNextLine(s))
267  {
268  const unsigned int lineNum = filParser.getCurrentLineNumber();
269  const string lin = s.str();
270 
271  // Recognized strings:
272  // VERTEX2 id x y phi
273  // =(VERTEX_SE2)
274  // EDGE2 from_id to_id Ax Ay Aphi inf_xx inf_xy inf_yy inf_pp
275  // inf_xp inf_yp
276  // =(EDGE or EDGE_SE2 or ODOMETRY)
277  // VERTEX3 id x y z roll pitch yaw
278  // VERTEX_SE3:QUAT id x y z qx qy qz qw
279  // EDGE3 from_id to_id Ax Ay Az Aroll Apitch Ayaw inf_11 inf_12 ..
280  // inf_16 inf_22 .. inf_66
281  // EDGE_SE3:QUAT from_id to_id Ax Ay Az qx qy qz qw inf_11 inf_12
282  // .. inf_16 inf_22 .. inf_66
283  // EQUIV id1 id2
284  string key;
285  if (!(s >> key) || key.empty())
287  "Line %u: Can't read string for entry type in: '%s'",
288  lineNum, lin.c_str()));
289 
290  if (strCmpI(key, "VERTEX2") || strCmpI(key, "VERTEX") ||
291  strCmpI(key, "VERTEX_SE2"))
292  {
293  TNodeID id;
294  TPose2D p2D;
295  if (!(s >> id >> p2D.x >> p2D.y >> p2D.phi))
297  "Line %u: Error parsing VERTEX2 line: '%s'", lineNum,
298  lin.c_str()));
299 
300  // Make sure the node is new:
301  if (g->nodes.find(id) != g->nodes.end())
303  "Line %u: Error, duplicated verted ID %u in line: "
304  "'%s'",
305  lineNum, static_cast<unsigned int>(id), lin.c_str()));
306 
307  // EQUIV? Replace ID by new one.
308  {
310  lstEquivs.find(id);
311  if (itEq != lstEquivs.end()) id = itEq->second;
312  }
313 
314  // Add to map: ID -> absolute pose:
315  if (g->nodes.find(id) == g->nodes.end())
316  {
317  using pose_t = typename CNetworkOfPoses<
318  CPOSE>::constraint_t::type_value;
319  pose_t& newNode = g->nodes[id];
320  newNode = pose_t(CPose2D(
321  p2D)); // Convert to mrpt::poses::CPose3D if needed
322  }
323  }
324  else if (strCmpI(key, "VERTEX3"))
325  {
326  if (!graph_is_3D)
328  "Line %u: Try to load VERTEX3 into a 2D graph: "
329  "'%s'",
330  lineNum, lin.c_str()));
331 
332  // VERTEX3 id x y z roll pitch yaw
333  TNodeID id;
334  TPose3D p3D;
335  // **CAUTION** In the TORO graph format angles are in the RPY
336  // order vs. MRPT's YPR.
337  if (!(s >> id >> p3D.x >> p3D.y >> p3D.z >> p3D.roll >>
338  p3D.pitch >> p3D.yaw))
340  "Line %u: Error parsing VERTEX3 line: '%s'", lineNum,
341  lin.c_str()));
342 
343  // Make sure the node is new:
344  if (g->nodes.find(id) != g->nodes.end())
346  "Line %u: Error, duplicated verted ID %u in line: "
347  "'%s'",
348  lineNum, static_cast<unsigned int>(id), lin.c_str()));
349 
350  // EQUIV? Replace ID by new one.
351  {
353  lstEquivs.find(id);
354  if (itEq != lstEquivs.end()) id = itEq->second;
355  }
356 
357  // Add to map: ID -> absolute pose:
358  if (g->nodes.find(id) == g->nodes.end())
359  {
360  g->nodes[id] = typename CNetworkOfPoses<CPOSE>::
362  p3D)); // Auto converted to CPose2D if needed
363  }
364  }
365  else if (strCmpI(key, "VERTEX_SE3:QUAT"))
366  {
367  if (!graph_is_3D)
369  "Line %u: Try to load VERTEX_SE3:QUAT into a 2D "
370  "graph: '%s'",
371  lineNum, lin.c_str()));
372 
373  // VERTEX_SE3:QUAT id x y z qx qy qz qw
374  TNodeID id;
375  TPose3DQuat p3D;
376  if (!(s >> id >> p3D.x >> p3D.y >> p3D.z >> p3D.qx >> p3D.qy >>
377  p3D.qz >> p3D.qr))
379  "Line %u: Error parsing VERTEX_SE3:QUAT line: '%s'",
380  lineNum, lin.c_str()));
381 
382  // Make sure the node is new:
383  if (g->nodes.find(id) != g->nodes.end())
385  "Line %u: Error, duplicated verted ID %u in line: "
386  "'%s'",
387  lineNum, static_cast<unsigned int>(id), lin.c_str()));
388 
389  // EQUIV? Replace ID by new one.
390  {
392  lstEquivs.find(id);
393  if (itEq != lstEquivs.end()) id = itEq->second;
394  }
395 
396  // Add to map: ID -> absolute pose:
397  if (g->nodes.find(id) == g->nodes.end())
398  {
399  g->nodes[id] = typename CNetworkOfPoses<CPOSE>::
401  CPose3D(CPose3DQuat(p3D))); // Auto converted to
402  // CPose2D if needed
403  }
404  }
405  else if (
406  strCmpI(key, "EDGE2") || strCmpI(key, "EDGE") ||
407  strCmpI(key, "ODOMETRY") || strCmpI(key, "EDGE_SE2"))
408  {
409  // EDGE_SE2 from_id to_id Ax Ay Aphi inf_xx i_xy i_xp i_yy i_yp
410  // i_pp Read values are:
411  // [ s00 s01 s02 ]
412  // [ - s11 s12 ]
413  // [ - - s22 ]
414  //
415  TNodeID to_id, from_id;
416  if (!(s >> from_id >> to_id))
418  "Line %u: Error parsing EDGE2 line: '%s'", lineNum,
419  lin.c_str()));
420 
421  // EQUIV? Replace ID by new one.
422  {
424  lstEquivs.find(to_id);
425  if (itEq != lstEquivs.end()) to_id = itEq->second;
426  }
427  {
429  lstEquivs.find(from_id);
430  if (itEq != lstEquivs.end()) from_id = itEq->second;
431  }
432 
433  if (from_id != to_id) // Don't load self-edges! (probably come
434  // from an EQUIV)
435  {
436  TPose2D Ap_mean;
437  mrpt::math::CMatrixDouble33 Ap_cov_inv;
438  if (!(s >> Ap_mean.x >> Ap_mean.y >> Ap_mean.phi >>
439  Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
440  Ap_cov_inv(0, 2) >> Ap_cov_inv(1, 1) >>
441  Ap_cov_inv(1, 2) >> Ap_cov_inv(2, 2)))
443  "Line %u: Error parsing EDGE2 line: '%s'", lineNum,
444  lin.c_str()));
445 
446  // Complete low triangular part of inf matrix:
447  Ap_cov_inv(1, 0) = Ap_cov_inv(0, 1);
448  Ap_cov_inv(2, 0) = Ap_cov_inv(0, 2);
449  Ap_cov_inv(2, 1) = Ap_cov_inv(1, 2);
450 
451  // Convert to 2D cov, 3D cov or 3D inv_cov as needed:
452  typename CNetworkOfPoses<CPOSE>::edge_t newEdge;
454  newEdge,
455  CPosePDFGaussianInf(CPose2D(Ap_mean), Ap_cov_inv));
456  g->insertEdge(from_id, to_id, newEdge);
457  }
458  }
459  else if (strCmpI(key, "EDGE3"))
460  {
461  if (!graph_is_3D)
463  "Line %u: Try to load EDGE3 into a 2D graph: '%s'",
464  lineNum, lin.c_str()));
465 
466  // EDGE3 from_id to_id Ax Ay Az Aroll Apitch Ayaw inf_11 inf_12
467  // .. inf_16 inf_22 .. inf_66
468  TNodeID to_id, from_id;
469  if (!(s >> from_id >> to_id))
471  "Line %u: Error parsing EDGE3 line: '%s'", lineNum,
472  lin.c_str()));
473 
474  // EQUIV? Replace ID by new one.
475  {
477  lstEquivs.find(to_id);
478  if (itEq != lstEquivs.end()) to_id = itEq->second;
479  }
480  {
482  lstEquivs.find(from_id);
483  if (itEq != lstEquivs.end()) from_id = itEq->second;
484  }
485 
486  if (from_id != to_id) // Don't load self-edges! (probably come
487  // from an EQUIV)
488  {
489  TPose3D Ap_mean;
490  mrpt::math::CMatrixDouble66 Ap_cov_inv;
491  // **CAUTION** In the TORO graph format angles are in the
492  // RPY order vs. MRPT's YPR.
493  if (!(s >> Ap_mean.x >> Ap_mean.y >> Ap_mean.z >>
494  Ap_mean.roll >> Ap_mean.pitch >> Ap_mean.yaw))
496  "Line %u: Error parsing EDGE3 line: '%s'", lineNum,
497  lin.c_str()));
498 
499  // **CAUTION** Indices are shuffled to the change YAW(3) <->
500  // ROLL(5) in the order of the data.
501  if (!(s >> Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
502  Ap_cov_inv(0, 2) >> Ap_cov_inv(0, 5) >>
503  Ap_cov_inv(0, 4) >> Ap_cov_inv(0, 3) >>
504  Ap_cov_inv(1, 1) >> Ap_cov_inv(1, 2) >>
505  Ap_cov_inv(1, 5) >> Ap_cov_inv(1, 4) >>
506  Ap_cov_inv(1, 3) >> Ap_cov_inv(2, 2) >>
507  Ap_cov_inv(2, 5) >> Ap_cov_inv(2, 4) >>
508  Ap_cov_inv(2, 3) >> Ap_cov_inv(5, 5) >>
509  Ap_cov_inv(5, 4) >> Ap_cov_inv(5, 3) >>
510  Ap_cov_inv(4, 4) >> Ap_cov_inv(4, 3) >>
511  Ap_cov_inv(3, 3)))
512  {
513  // Cov may be omitted in the file:
514  Ap_cov_inv.unit(6, 1.0);
515 
516  if (alreadyWarnedUnknowns.find("MISSING_3D") ==
517  alreadyWarnedUnknowns.end())
518  {
519  alreadyWarnedUnknowns.insert("MISSING_3D");
520  cerr << "[CNetworkOfPoses::loadFromTextFile] "
521  << fil << ":" << lineNum
522  << ": Warning: Information matrix missing, "
523  "assuming unity.\n";
524  }
525  }
526  else
527  {
528  // Complete low triangular part of inf matrix:
529  for (size_t r = 1; r < 6; r++)
530  for (size_t c = 0; c < r; c++)
531  Ap_cov_inv(r, c) = Ap_cov_inv(c, r);
532  }
533 
534  // Convert as needed:
535  typename CNetworkOfPoses<CPOSE>::edge_t newEdge;
537  newEdge,
538  CPose3DPDFGaussianInf(CPose3D(Ap_mean), Ap_cov_inv));
539  g->insertEdge(from_id, to_id, newEdge);
540  }
541  }
542  else if (strCmpI(key, "EDGE_SE3:QUAT"))
543  {
544  if (!graph_is_3D)
546  "Line %u: Try to load EDGE3 into a 2D graph: '%s'",
547  lineNum, lin.c_str()));
548 
549  // EDGE_SE3:QUAT from_id to_id Ax Ay Az qx qy qz qw inf_11
550  // inf_12 .. inf_16 inf_22 .. inf_66
551  // EDGE3 from_id to_id Ax Ay Az Aroll Apitch Ayaw inf_11 inf_12
552  // .. inf_16 inf_22 .. inf_66
553  TNodeID to_id, from_id;
554  if (!(s >> from_id >> to_id))
556  "Line %u: Error parsing EDGE_SE3:QUAT line: '%s'",
557  lineNum, lin.c_str()));
558 
559  // EQUIV? Replace ID by new one.
560  {
562  lstEquivs.find(to_id);
563  if (itEq != lstEquivs.end()) to_id = itEq->second;
564  }
565  {
567  lstEquivs.find(from_id);
568  if (itEq != lstEquivs.end()) from_id = itEq->second;
569  }
570 
571  if (from_id != to_id) // Don't load self-edges! (probably come
572  // from an EQUIV)
573  {
574  TPose3DQuat Ap_mean;
575  mrpt::math::CMatrixDouble66 Ap_cov_inv;
576  if (!(s >> Ap_mean.x >> Ap_mean.y >> Ap_mean.z >>
577  Ap_mean.qx >> Ap_mean.qy >> Ap_mean.qz >> Ap_mean.qr))
579  "Line %u: Error parsing EDGE_SE3:QUAT line: "
580  "'%s'",
581  lineNum, lin.c_str()));
582 
583  // **CAUTION** Indices are shuffled to the change YAW(3) <->
584  // ROLL(5) in the order of the data.
585  if (!(s >> Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
586  Ap_cov_inv(0, 2) >> Ap_cov_inv(0, 5) >>
587  Ap_cov_inv(0, 4) >> Ap_cov_inv(0, 3) >>
588  Ap_cov_inv(1, 1) >> Ap_cov_inv(1, 2) >>
589  Ap_cov_inv(1, 5) >> Ap_cov_inv(1, 4) >>
590  Ap_cov_inv(1, 3) >> Ap_cov_inv(2, 2) >>
591  Ap_cov_inv(2, 5) >> Ap_cov_inv(2, 4) >>
592  Ap_cov_inv(2, 3) >> Ap_cov_inv(5, 5) >>
593  Ap_cov_inv(5, 4) >> Ap_cov_inv(5, 3) >>
594  Ap_cov_inv(4, 4) >> Ap_cov_inv(4, 3) >>
595  Ap_cov_inv(3, 3)))
596  {
597  // Cov may be omitted in the file:
598  Ap_cov_inv.unit(6, 1.0);
599 
600  if (alreadyWarnedUnknowns.find("MISSING_3D") ==
601  alreadyWarnedUnknowns.end())
602  {
603  alreadyWarnedUnknowns.insert("MISSING_3D");
604  cerr << "[CNetworkOfPoses::loadFromTextFile] "
605  << fil << ":" << lineNum
606  << ": Warning: Information matrix missing, "
607  "assuming unity.\n";
608  }
609  }
610  else
611  {
612  // Complete low triangular part of inf matrix:
613  for (size_t r = 1; r < 6; r++)
614  for (size_t c = 0; c < r; c++)
615  Ap_cov_inv(r, c) = Ap_cov_inv(c, r);
616  }
617 
618  // Convert as needed:
619  typename CNetworkOfPoses<CPOSE>::edge_t newEdge;
621  newEdge,
623  CPose3D(CPose3DQuat(Ap_mean)), Ap_cov_inv));
624  g->insertEdge(from_id, to_id, newEdge);
625  }
626  }
627  else if (strCmpI(key, "EQUIV"))
628  {
629  // Already read in the 1st pass.
630  }
631  else if (strCmpI(key, "FIX"))
632  {
633  TNodeID id;
634  if (!(s >> id))
636  "Line %u: Can't read id in FIX line: '%s'",
637  lineNum, lin.c_str()));
638  g->root = id;
639  }
640  else
641  { // Unknown entry: Warn the user just once:
642  if (alreadyWarnedUnknowns.find(key) ==
643  alreadyWarnedUnknowns.end())
644  {
645  alreadyWarnedUnknowns.insert(key);
646  cerr << "[CNetworkOfPoses::loadFromTextFile] " << fil << ":"
647  << lineNum << ": Warning: unknown entry type: " << key
648  << endl;
649  }
650  }
651  } // end while
652 
653  } // end load_graph
654 
656  graph_t* g, const std::string& fil)
657  {
658  std::ifstream f(fil);
659  if (!f.is_open())
661  "Error opening file '%s' for reading", fil.c_str());
662  load_graph_of_poses_from_text_stream(g, f, fil);
663  }
664 
665  // --------------------------------------------------------------------------------
666  // Implements: collapseDuplicatedEdges
667  //
668  // Look for duplicated edges (even in opposite directions) between all pairs of nodes and fuse them.
669  // Upon return, only one edge remains between each pair of nodes with the mean
670  // & covariance (or information matrix) corresponding to the Bayesian fusion of all the Gaussians.
671  // --------------------------------------------------------------------------------
672  static size_t graph_of_poses_collapse_dup_edges(graph_t *g)
673  {
674  MRPT_START
675  typedef typename graph_t::edges_map_t::iterator TEdgeIterator;
676 
677  // Data structure: (id1,id2) -> all edges between them
678  // (with id1 < id2)
679  typedef map<pair<TNodeID,TNodeID>, vector<TEdgeIterator> > TListAllEdges; // For god's sake... when will ALL compilers support auto!! :'-(
680  TListAllEdges lstAllEdges;
681 
682  // Clasify all edges to identify duplicated ones:
683  for (TEdgeIterator itEd=g->edges.begin();itEd!=g->edges.end();++itEd)
684  {
685  // Build a pair <id1,id2> with id1 < id2:
686  const pair<TNodeID,TNodeID> arc_id = make_pair(std::min(itEd->first.first,itEd->first.second),std::max(itEd->first.first,itEd->first.second));
687  // get (or create the first time) the list of edges between them:
688  vector<TEdgeIterator> &lstEdges = lstAllEdges[arc_id];
689  // And add this one:
690  lstEdges.push_back(itEd);
691  }
692 
693  // Now, remove all but the first edge:
694  size_t nRemoved = 0;
695  for (typename TListAllEdges::const_iterator it=lstAllEdges.begin();it!=lstAllEdges.end();++it)
696  {
697  const size_t N = it->second.size();
698  for (size_t i=1;i<N;i++) // i=0 is NOT removed
699  g->edges.erase(it->second[i]);
700 
701  if (N>=2) nRemoved+=N-1;
702  }
703 
704  return nRemoved;
705  MRPT_END
706  } // end of graph_of_poses_collapse_dup_edges
707 
708  // --------------------------------------------------------------------------------
709  // Implements: dijkstra_nodes_estimate
710  //
711  // Compute a simple estimation of the global coordinates of each node just from the information in all edges, sorted in a Dijkstra tree based on the current "root" node.
712  // Note that "global" coordinates are with respect to the node with the ID specified in \a root.
713  // --------------------------------------------------------------------------------
714  static void graph_of_poses_dijkstra_init(graph_t *g)
715  {
716  MRPT_START;
717  using namespace std;
718 
719  // Do Dijkstra shortest path from "root" to all other nodes:
721  typedef typename graph_t::constraint_t constraint_t;
722 
723  // initialize corresponding dijkstra object from root.
724  dijkstra_t dijkstra(*g, g->root);
725 
726  // Get the tree representation of the graph and traverse it
727  // from its root toward the leafs:
728  typename dijkstra_t::tree_graph_t treeView;
729  dijkstra.getTreeGraph(treeView);
730 
731  // This visitor class performs the real job of
732  struct VisitorComputePoses : public dijkstra_t::tree_graph_t::Visitor
733  {
734  graph_t * m_g; // The original graph
735 
736  VisitorComputePoses(graph_t *g) : m_g(g) { }
737  virtual void OnVisitNode(const TNodeID parent_id, const typename dijkstra_t::tree_graph_t::Visitor::tree_t::TEdgeInfo &edge_to_child, const size_t depth_level) MRPT_OVERRIDE
738  {
739  MRPT_UNUSED_PARAM(depth_level);
740  const TNodeID child_id = edge_to_child.id;
741 
742  // Compute the pose of "child_id" as parent_pose (+) edge_delta_pose,
743  // taking into account that that edge may be in reverse order
744  // and then have to invert the delta_pose:
745  if ((!edge_to_child.reverse && !m_g->edges_store_inverse_poses) ||
746  (edge_to_child.reverse && m_g->edges_store_inverse_poses)
747  )
748  { // pose_child = p_parent (+) p_delta
749  m_g->nodes[child_id].composeFrom(m_g->nodes[parent_id], edge_to_child.data->getPoseMean());
750  }
751  else
752  { // pose_child = p_parent (+) [(-)p_delta]
753  m_g->nodes[child_id].composeFrom(m_g->nodes[parent_id], - edge_to_child.data->getPoseMean());
754  }
755  }
756  };
757 
758  // Remove all global poses except for the root node, which is the origin:
759  //
760  // Keep track of the NODE_ANNOTATIONS for each node and put it after
761  // the global pose computation
762  bool empty_node_annots = g->nodes.begin()->second.is_node_annots_empty;
763  map<const TNodeID, TNodeAnnotations*> nodeID_to_annots;
764  if (!empty_node_annots) {
766  poses_cit = g->nodes.begin();
767  poses_cit != g->nodes.end();
768  ++poses_cit) {
769 
770  nodeID_to_annots.insert(
771  make_pair(
772  poses_cit->first, poses_cit->second.getCopyOfAnnots()));
773  }
774  }
775 
776  g->nodes.clear();
777  g->nodes[g->root] = typename constraint_t::type_value(); // Typ: CPose2D() or CPose3D()
778 
779  // Run the visit thru all nodes in the tree:
780  VisitorComputePoses myVisitor(g);
781  treeView.visitBreadthFirst(treeView.root, myVisitor);
782 
783  // Fill the NODE_ANNOTATIONS part again
784  if (!empty_node_annots) {
786  poses_cit = g->nodes.begin();
787  poses_cit != g->nodes.end();
788  ++poses_cit) {
789 
790  TNodeAnnotations* node_annots = nodeID_to_annots.at(poses_cit->first);
791  bool res = poses_cit->second.setAnnots(*node_annots);
792 
793  // free dynamically allocated mem
794  delete node_annots;
795  node_annots = NULL;
796 
797  // make sure setting annotations was successful
799  "Setting annotations for nodeID \"%lu\" was unsuccessful",
800  static_cast<unsigned long>(poses_cit->first)));
801  }
802  }
803 
804  MRPT_END
805  } // end of graph_of_poses_dijkstra_init
806 
807 
808  // Auxiliary funcs:
809  template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPosePDFGaussianInf &p) {
810  math::wrapToPiInPlace(err[2]);
811  return mrpt::math::multiply_HCHt_scalar(err,p.cov_inv); // err^t*cov_inv*err
812  }
813  template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose3DPDFGaussianInf &p) {
814  math::wrapToPiInPlace(err[3]);
815  math::wrapToPiInPlace(err[4]);
816  math::wrapToPiInPlace(err[5]);
817  return mrpt::math::multiply_HCHt_scalar(err,p.cov_inv); // err^t*cov_inv*err
818  }
819  template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPosePDFGaussian &p) {
820  math::wrapToPiInPlace(err[2]);
822  p.cov.inv(COV_INV);
823  return mrpt::math::multiply_HCHt_scalar(err,COV_INV); // err^t*cov_inv*err
824  }
825  template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose3DPDFGaussian &p) {
826  math::wrapToPiInPlace(err[3]);
827  math::wrapToPiInPlace(err[4]);
828  math::wrapToPiInPlace(err[5]);
830  p.cov.inv(COV_INV);
831  return mrpt::math::multiply_HCHt_scalar(err,COV_INV); // err^t*cov_inv*err
832  }
833  // These two are for simulating maha2 distances for non-PDF types: fallback to squared-norm:
834  template <class VEC> static inline double auxMaha2Dist(VEC &err,const mrpt::poses::CPose2D &p) {
835  math::wrapToPiInPlace(err[2]);
836  return square(err[0])+square(err[1])+square(err[2]);
837  }
838  template <class VEC> static inline double auxMaha2Dist(VEC &err,const mrpt::poses::CPose3D &p) {
839  math::wrapToPiInPlace(err[3]);
840  math::wrapToPiInPlace(err[4]);
841  math::wrapToPiInPlace(err[5]);
842  return square(err[0])+square(err[1])+square(err[2])+square(err[3])+square(err[4])+square(err[5]);
843  }
844 
845 
846  static inline double auxEuclid2Dist(const mrpt::poses::CPose2D &p1,const mrpt::poses::CPose2D &p2) {
847  return
848  square(p1.x()-p2.x())+
849  square(p1.y()-p2.y())+
850  square(mrpt::math::wrapToPi(p1.phi()-p2.phi()));
851  }
852  static inline double auxEuclid2Dist(const mrpt::poses::CPose3D &p1,const mrpt::poses::CPose3D &p2) {
853  return
854  square(p1.x()-p2.x())+
855  square(p1.y()-p2.y())+
856  square(p1.z()-p2.z())+
857  square(mrpt::math::wrapToPi(p1.yaw()-p2.yaw()))+
859  square(mrpt::math::wrapToPi(p1.roll()-p2.roll()));
860  }
861 
862  // --------------------------------------------------------------------------------
863  // Implements: detail::graph_edge_sqerror
864  //
865  // Compute the square error of a single edge, in comparison to the nodes global poses.
866  // --------------------------------------------------------------------------------
867  static double graph_edge_sqerror(
868  const graph_t *g,
870  bool ignoreCovariances)
871  {
872  MRPT_START
873 
874  // Get node IDs:
875  const TNodeID from_id = itEdge->first.first;
876  const TNodeID to_id = itEdge->first.second;
877 
878  // And their global poses as stored in "nodes"
879  typename graph_t::global_poses_t::const_iterator itPoseFrom = g->nodes.find(from_id);
880  typename graph_t::global_poses_t::const_iterator itPoseTo = g->nodes.find(to_id);
881  ASSERTMSG_(itPoseFrom!=g->nodes.end(), format("Node %u doesn't have a global pose in 'nodes'.", static_cast<unsigned int>(from_id)))
882  ASSERTMSG_(itPoseTo!=g->nodes.end(), format("Node %u doesn't have a global pose in 'nodes'.", static_cast<unsigned int>(to_id)))
883 
884  // The global poses:
885  typedef typename graph_t::constraint_t constraint_t;
886 
887  const typename constraint_t::type_value &from_mean = itPoseFrom->second;
888  const typename constraint_t::type_value &to_mean = itPoseTo->second;
889 
890  // The delta_pose as stored in the edge:
891  const constraint_t &edge_delta_pose = itEdge->second;
892  const typename constraint_t::type_value &edge_delta_pose_mean = edge_delta_pose.getPoseMean();
893 
894  if (ignoreCovariances)
895  { // Square Euclidean distance: Just use the mean values, ignore covs.
896  // from_plus_delta = from_mean (+) edge_delta_pose_mean
897  typename constraint_t::type_value from_plus_delta(UNINITIALIZED_POSE);
898  from_plus_delta.composeFrom(from_mean, edge_delta_pose_mean);
899 
900  // (auxMaha2Dist will also take into account the 2PI wrapping)
901  return auxEuclid2Dist(from_plus_delta,to_mean);
902  }
903  else
904  {
905  // Square Mahalanobis distance
906  // from_plus_delta = from_mean (+) edge_delta_pose (as a Gaussian)
907  constraint_t from_plus_delta = edge_delta_pose;
908  from_plus_delta.changeCoordinatesReference(from_mean);
909 
910  // "from_plus_delta" is now a 3D or 6D Gaussian, to be compared to "to_mean":
911  // We want to compute the squared Mahalanobis distance:
912  // err^t * INV_COV * err
913  //
915  for (size_t i=0;i<constraint_t::type_value::static_size;i++)
916  err[i] = from_plus_delta.getPoseMean()[i] - to_mean[i];
917 
918  // (auxMaha2Dist will also take into account the 2PI wrapping)
919  return auxMaha2Dist(err,from_plus_delta);
920  }
921  MRPT_END
922  }
923 
924  }; // end of graph_ops<graph_t>
925 
926  }// end NS
927  }// end NS
928 } // end NS
929 
930 #endif
The Dijkstra algorithm for finding the shortest path between a given source node in a (weighted) dire...
Definition: dijkstra.h:104
A directed graph with the argument of the template specifying the type of the annotations in the edge...
A directed graph of pose constraints, with edges being the relative poses between pairs of nodes iden...
global_poses_t nodes
The nodes (vertices) of the graph, with their estimated "global" (with respect to root) position,...
A partial specialization of CArrayNumeric for double numbers.
Definition: CArrayNumeric.h:75
A class used to store a 2D pose, including the 2D coordinate point and a heading (phi) angle.
Definition: CPose2D.h:37
const double & phi() const
Get the phi angle of the 2D pose (in radians)
Definition: CPose2D.h:84
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:73
double pitch() const
Get the PITCH angle (in radians)
Definition: CPose3D.h:392
double roll() const
Get the ROLL angle (in radians)
Definition: CPose3D.h:393
double yaw() const
Get the YAW angle (in radians)
Definition: CPose3D.h:391
Declares a class that represents a Probability Density function (PDF) of a 3D pose .
Declares a class that represents a Probability Density function (PDF) of a 3D pose as a Gaussian des...
mrpt::math::CMatrixDouble66 cov_inv
The inverse of the 6x6 covariance matrix.
A class used to store a 3D pose as a translation (x,y,z) and a quaternion (qr,qx,qy,...
Definition: CPose3DQuat.h:42
double x() const
Common members of all points & poses classes.
Definition: CPoseOrPoint.h:113
Declares a class that represents a Probability Density function (PDF) of a 2D pose .
A Probability Density function (PDF) of a 2D pose as a Gaussian with a mean and the inverse of the c...
mrpt::math::CMatrixDouble33 cov_inv
The inverse of the 3x3 covariance matrix (the "information" matrix)
This base class is used to provide a unified interface to files,memory buffers,..Please see the deriv...
Definition: CStream.h:39
A class for parsing text files, returning each non-empty and non-comment line, along its line number.
size_t getCurrentLineNumber() const
Return the line number of the last line returned with getNextLine.
void rewind()
Reset the read pointer to the beginning of the file.
bool getNextLine(std::string &out_str)
Reads from the file and return the next (non-comment) line, as a std::string.
@ static_size
Definition: eigen_plugins.h:17
Scalar * iterator
Definition: eigen_plugins.h:23
const Scalar * const_iterator
Definition: eigen_plugins.h:24
GLenum GLsizei n
Definition: glext.h:4618
GLuint res
Definition: glext.h:6298
const GLubyte * c
Definition: glext.h:5590
GLuint id
Definition: glext.h:3770
GLuint in
Definition: glext.h:6301
GLubyte g
Definition: glext.h:5575
GLfloat GLfloat p
Definition: glext.h:5587
GLdouble GLdouble GLdouble r
Definition: glext.h:3618
GLdouble s
Definition: glext.h:3602
GLenum GLsizei GLenum format
Definition: glext.h:3513
GLsizei const GLchar ** string
Definition: glext.h:3919
void wrapToPiInPlace(T &a)
Modifies the given angle to translate it into the ]-pi,pi] range.
Definition: wrap2pi.h:61
T wrapToPi(T a)
Modifies the given angle to translate it into the ]-pi,pi] range.
Definition: wrap2pi.h:51
uint64_t TNodeID
The type for node IDs in graphs of different types.
Definition: types_simple.h:45
std::pair< TNodeID, TNodeID > TPairNodeIDs
A pair of node IDs.
Definition: types_simple.h:46
bool BASE_IMPEXP strCmpI(const std::string &s1, const std::string &s2)
Return true if the two strings are equal (case insensitive)
This file implements matrix/vector text and binary serialization.
int version
Definition: mrpt_jpeglib.h:898
#define ASSERT_EQUAL_(__A, __B)
Definition: mrpt_macros.h:281
#define MRPT_START
Definition: mrpt_macros.h:366
#define MRPT_OVERRIDE
C++11 "override" for virtuals:
Definition: mrpt_macros.h:58
#define MRPT_END
Definition: mrpt_macros.h:370
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
Definition: mrpt_macros.h:217
#define THROW_EXCEPTION(msg)
Definition: mrpt_macros.h:154
#define MRPT_UNUSED_PARAM(a)
Can be used to avoid "not used parameters" warnings from the compiler.
Definition: mrpt_macros.h:307
#define ASSERTMSG_(f, __ERROR_MSG)
Definition: mrpt_macros.h:277
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: mrpt_macros.h:163
Abstract graph and tree data structures, plus generic graph algorithms.
This base provides a set of functions for maths stuff.
Definition: CArrayNumeric.h:20
@ UNINITIALIZED_MATRIX
Definition: math_frwds.h:74
MAT_C::Scalar multiply_HCHt_scalar(const VECTOR_H &H, const MAT_C &C)
r (a scalar) = H * C * H^t (with a vector H and a symmetric matrix C)
Definition: ops_matrices.h:62
T square(const T x)
Inline function for the square of a number.
Definition: bits.h:52
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
Definition: CPoint.h:18
@ UNINITIALIZED_POSE
Definition: CPoseOrPoint.h:35
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values,...
Definition: zip.h:16
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:21
This file implements miscelaneous matrix and matrix/vector operations, and internal functions in mrpt...
#define min(a, b)
unsigned __int32 uint32_t
Definition: rptypes.h:49
Abstract class from which NodeAnnotations related classes can be implemented.
bool setAnnots(const self_t &other)
Set the properties of the current TNodeAnnotations object.
static void copyFrom3D(CPose2D &p, const CPose3DPDFGaussianInf &pdf)
static void copyFrom2D(CPose2D &p, const CPosePDFGaussianInf &pdf)
static void copyFrom3D(CPose3D &p, const CPose3DPDFGaussianInf &pdf)
static void copyFrom2D(CPose3D &p, const CPosePDFGaussianInf &pdf)
static void copyFrom2D(POSE_PDF &p, const CPosePDFGaussianInf &pdf)
static void copyFrom3D(POSE_PDF &p, const CPose3DPDFGaussianInf &pdf)
a helper struct with static template functions
static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose3D &p, std::ostream &f)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPosePDFGaussian &edge, std::ostream &f)
static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose2D &p, std::ostream &f)
static void graph_of_poses_dijkstra_init(graph_t *g)
static void read_graph_of_poses_from_binary_file(graph_t *g, mrpt::utils::CStream &in)
static void load_graph_of_poses_from_text_stream(graph_t *g, std::istream &f, const std::string &fil=std::string("(none)"))
static double auxMaha2Dist(VEC &err, const CPosePDFGaussianInf &p)
static double auxEuclid2Dist(const mrpt::poses::CPose2D &p1, const mrpt::poses::CPose2D &p2)
static size_t graph_of_poses_collapse_dup_edges(graph_t *g)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPosePDFGaussianInf &edge, std::ostream &f)
static double auxMaha2Dist(VEC &err, const CPose3DPDFGaussian &p)
static double auxMaha2Dist(VEC &err, const CPose3DPDFGaussianInf &p)
static double auxMaha2Dist(VEC &err, const CPosePDFGaussian &p)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPose3DPDFGaussian &edge, std::ostream &f)
static void save_graph_of_poses_to_ostream(const graph_t *g, std::ostream &f)
static void load_graph_of_poses_from_text_file(graph_t *g, const std::string &fil)
static void save_graph_of_poses_to_binary_file(const graph_t *g, mrpt::utils::CStream &out)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const mrpt::poses::CPose2D &edge, std::ostream &f)
static double auxEuclid2Dist(const mrpt::poses::CPose3D &p1, const mrpt::poses::CPose3D &p2)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPose3DPDFGaussianInf &edge, std::ostream &f)
static double auxMaha2Dist(VEC &err, const mrpt::poses::CPose3D &p)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const mrpt::poses::CPose3D &edge, std::ostream &f)
static double auxMaha2Dist(VEC &err, const mrpt::poses::CPose2D &p)
static double graph_edge_sqerror(const graph_t *g, const typename mrpt::graphs::CDirectedGraph< typename graph_t::constraint_t >::edges_map_t::const_iterator &itEdge, bool ignoreCovariances)
static void save_graph_of_poses_to_text_file(const graph_t *g, const std::string &fil)
Lightweight 2D pose.
double phi
Orientation (rads)
double y
X,Y coordinates.
Lightweight 3D pose (three spatial coordinates, plus three angular coordinates).
double z
X,Y,Z, coords.
double roll
Roll coordinate (rotation angle over X coordinate).
double pitch
Pitch coordinate (rotation angle over Y axis).
double yaw
Yaw coordinate (rotation angle over Z axis).
Lightweight 3D pose (three spatial coordinates, plus a quaternion ).
double z
Translation in x,y,z.
double qz
Unit quaternion part, qr,qx,qy,qz.
static std::string get()
Definition: TTypeName.h:49



Page generated by Doxygen 1.9.1 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at mar 26 may 2026 13:12:03 CEST