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



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