Main MRPT website > C++ reference for MRPT 1.9.9
test.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include <mrpt/random.h>
11 #include <mrpt/graphslam/levmarq.h>
12 #include <mrpt/gui.h>
16 #include <mrpt/img/TColor.h>
18 #include <iostream>
19 
20 using namespace mrpt;
21 using namespace mrpt::graphs;
22 using namespace mrpt::graphslam;
23 using namespace mrpt::poses;
24 using namespace mrpt::math;
25 using namespace mrpt::gui;
26 using namespace mrpt::opengl;
27 using namespace mrpt::random;
28 using namespace mrpt::img;
29 using namespace std;
30 using namespace mrpt::system;
31 
32 // Level of noise in nodes initial positions:
33 const double STD_NOISE_NODE_XYZ = 0.5;
34 const double STD_NOISE_NODE_ANG = DEG2RAD(5);
35 
36 // Level of noise in edges:
37 const double STD_NOISE_EDGE_XYZ = 0.001;
38 const double STD_NOISE_EDGE_ANG = DEG2RAD(0.01);
39 
40 const double STD4EDGES_COV_MATRIX = 10;
41 const double ERROR_IN_INCOMPATIBLE_EDGE = 0.3; // ratio [0,1]
42 
43 /**
44  * Generic struct template
45  * Auxiliary class to add a new edge to the graph. The edge is annotated with
46  * the relative position of the two nodes
47  */
48 template <class GRAPH, bool EDGES_ARE_PDF = GRAPH::edge_t::is_PDF_val>
49 struct EdgeAdders;
50 
51 /**
52  * Specific templates based on the above EdgeAdders template
53  * Non-PDF version:
54  */
55 template <class GRAPH>
56 struct EdgeAdders<GRAPH, false>
57 {
58  static const int DIM = GRAPH::edge_t::type_value::static_size;
60 
61  static void addEdge(
62  TNodeID from, TNodeID to,
63  const typename GRAPH::global_poses_t& real_poses, GRAPH& graph,
64  const cov_t& COV_MAT)
65  {
66  /**
67  * No covariance argument here (although it is passed in the function
68  * declaration above)
69  * See also :
70  * https://groups.google.com/d/msg/mrpt-users/Sr9LSydArgY/vRNM5V_uA-oJ
71  * for a more detailed explanation on how it is treated
72  */
73  typename GRAPH::edge_t RelativePose(
74  real_poses.find(to)->second - real_poses.find(from)->second);
75  graph.insertEdge(from, to, RelativePose);
76  }
77 };
78 // PDF version:
79 template <class GRAPH>
80 struct EdgeAdders<GRAPH, true>
81 {
82  static const int DIM = GRAPH::edge_t::type_value::static_size;
84 
85  static void addEdge(
86  TNodeID from, TNodeID to,
87  const typename GRAPH::global_poses_t& real_poses, GRAPH& graph,
88  const cov_t& COV_MAT)
89  {
90  typename GRAPH::edge_t RelativePose(
91  real_poses.find(to)->second - real_poses.find(from)->second,
92  COV_MAT);
93  graph.insertEdge(from, to, RelativePose);
94  }
95 };
96 
97 // Container to handle the propagation of the square root error of the problem
98 vector<double> log_sq_err_evolution;
99 
100 // This example lives inside this template class, which can be instanced for
101 // different kind of graphs (see main()):
102 template <class my_graph_t>
104 {
105  template <class GRAPH_T>
106  static void my_levmarq_feedback(
107  const GRAPH_T& graph, const size_t iter, const size_t max_iter,
108  const double cur_sq_error)
109  {
110  log_sq_err_evolution.push_back(std::log(cur_sq_error));
111  if ((iter % 100) == 0)
112  cout << "Progress: " << iter << " / " << max_iter
113  << ", total sq err = " << cur_sq_error << endl;
114  }
115 
116  // ------------------------------------------------------
117  // GraphSLAMDemo
118  // ------------------------------------------------------
119  void run(bool add_extra_tightening_edge)
120  {
121  // The graph: nodes + edges:
122  my_graph_t graph;
123 
124  // The global poses of the graph nodes (without covariance):
125  typename my_graph_t::global_poses_t real_node_poses;
126 
127  /**
128  * Initialize the PRNG from the given random seed.
129  * Method used to initially randomise the generator
130  */
132 
133  // ----------------------------
134  // Create a random graph:
135  // ----------------------------
136  const size_t N_VERTEX = 50;
137  const double DIST_THRES = 7;
138  const double NODES_XY_MAX = 20;
139 
140  /**
141  * First add all the nodes (so that, when I add edges, I can refer to
142  * them
143  */
144  for (TNodeID j = 0; j < N_VERTEX; j++)
145  {
146  // Use evenly distributed nodes:
147  static double ang = 2 * M_PI / N_VERTEX;
148  const double R = NODES_XY_MAX + 2 * (j % 2 ? 1 : -1);
149  CPose2D p(R * cos(ang * j), R * sin(ang * j), ang);
150 
151  // Save real pose:
152  real_node_poses[j] = p;
153 
154  // Copy the nodes to the graph, and add some noise:
155  graph.nodes[j] = p;
156  }
157 
158  /**
159  * Add some edges
160  * Also initialize the information matrix used for EACH constraint. For
161  * simplicity the same information matrix is used for each one of the
162  * edges. This information matrix is RELATIVE to each constraint/edge
163  * (not in global ref. frame) see also:
164  * https://groups.google.com/d/msg/mrpt-users/Sr9LSydArgY/wYFeU2BXr4kJ
165  */
166  typedef EdgeAdders<my_graph_t> edge_adder_t;
167  typename edge_adder_t::cov_t inf_matrix;
168  inf_matrix.unit(
169  edge_adder_t::cov_t::RowsAtCompileTime,
171 
172  /**
173  * add the edges using the node ids added to the graph before
174  */
175  for (TNodeID i = 0; i < N_VERTEX; i++)
176  {
177  for (TNodeID j = i + 1; j < N_VERTEX; j++)
178  {
179  if (real_node_poses[i].distanceTo(real_node_poses[j]) <
180  DIST_THRES)
182  i, j, real_node_poses, graph, inf_matrix);
183  }
184  }
185 
186  // Add an additional edge to deform the graph?
187  if (add_extra_tightening_edge)
188  {
189  // inf_matrix.unit(square(1.0/(STD4EDGES_COV_MATRIX)));
191  0, N_VERTEX / 2, real_node_poses, graph, inf_matrix);
192 
193  // Tweak this last node to make it incompatible with the rest:
194  // It must exist, don't check errors...
195  typename my_graph_t::edge_t& ed =
196  graph.edges.find(make_pair(TNodeID(0), TNodeID(N_VERTEX / 2)))
197  ->second;
198  ed.getPoseMean().x(
199  (1 - ERROR_IN_INCOMPATIBLE_EDGE) * ed.getPoseMean().x());
200  }
201 
202  // The root node (the origin of coordinates):
203  graph.root = TNodeID(0);
204 
205  // This is the ground truth graph (make a copy for later use):
206  const my_graph_t graph_GT = graph;
207 
208  cout << "graph nodes: " << graph_GT.nodeCount() << endl;
209  cout << "graph edges: " << graph_GT.edgeCount() << endl;
210 
211  // Add noise to edges & nodes:
212  for (typename my_graph_t::edges_map_t::iterator itEdge =
213  graph.edges.begin();
214  itEdge != graph.edges.end(); ++itEdge)
215  {
216  const typename my_graph_t::edge_t::type_value delta_noise(
217  CPose3D(
218  getRandomGenerator().drawGaussian1D(0, STD_NOISE_EDGE_XYZ),
219  getRandomGenerator().drawGaussian1D(0, STD_NOISE_EDGE_XYZ),
220  getRandomGenerator().drawGaussian1D(0, STD_NOISE_EDGE_XYZ),
221  getRandomGenerator().drawGaussian1D(0, STD_NOISE_EDGE_ANG),
222  getRandomGenerator().drawGaussian1D(0, STD_NOISE_EDGE_ANG),
223  getRandomGenerator().drawGaussian1D(
224  0, STD_NOISE_EDGE_ANG)));
225  itEdge->second.getPoseMean() +=
226  typename my_graph_t::edge_t::type_value(delta_noise);
227  }
228 
229  for (typename my_graph_t::global_poses_t::iterator itNode =
230  graph.nodes.begin();
231  itNode != graph.nodes.end(); ++itNode)
232  if (itNode->first != graph.root)
233  itNode->second.getPoseMean() +=
234  typename my_graph_t::edge_t::type_value(
235  CPose3D(
236  getRandomGenerator().drawGaussian1D(
237  0, STD_NOISE_NODE_XYZ),
238  getRandomGenerator().drawGaussian1D(
239  0, STD_NOISE_NODE_XYZ),
240  getRandomGenerator().drawGaussian1D(
241  0, STD_NOISE_NODE_XYZ),
242  getRandomGenerator().drawGaussian1D(
243  0, STD_NOISE_NODE_ANG),
244  getRandomGenerator().drawGaussian1D(
245  0, STD_NOISE_NODE_ANG),
246  getRandomGenerator().drawGaussian1D(
247  0, STD_NOISE_NODE_ANG)));
248 
249  // This is the initial input graph (make a copy for later use):
250  const my_graph_t graph_initial = graph;
251  // graph_GT.saveToTextFile("test_GT.graph");
252  // graph_initial.saveToTextFile("test.graph");
253 
254  // ----------------------------
255  // Run graph slam:
256  // ----------------------------
258  // params["verbose"] = 1;
259  params["profiler"] = 1;
260  params["max_iterations"] = 500;
261  params["scale_hessian"] = 0.1; // If <1, will "exagerate" the scale of
262  // the gradient and, normally, will
263  // converge much faster.
264  params["tau"] = 1e-3;
265 
266  // e2: Lev-marq algorithm iteration stopping criterion #2: |delta_incr|
267  // < e2*(x_norm+e2)
268  // params["e1"] = 1e-6;
269  // params["e2"] = 1e-6;
270 
272  log_sq_err_evolution.clear();
273 
274  cout << "Global graph RMS error / edge = "
275  << std::sqrt(graph.getGlobalSquareError(false) / graph.edgeCount())
276  << endl;
277  cout << "Global graph RMS error / edge = "
278  << std::sqrt(graph.getGlobalSquareError(true) / graph.edgeCount())
279  << " (ignoring information matrices)." << endl;
280 
281  // Do the optimization
283  graph, levmarq_info,
284  nullptr, // List of nodes to optimize. nullptr -> all but the root
285  // node.
286  params, &my_levmarq_feedback<my_graph_t>);
287 
288  cout << "Global graph RMS error / edge = "
289  << std::sqrt(graph.getGlobalSquareError(false) / graph.edgeCount())
290  << endl;
291  cout << "Global graph RMS error / edge = "
292  << std::sqrt(graph.getGlobalSquareError(true) / graph.edgeCount())
293  << " (ignoring information matrices)." << endl;
294 
295  // ----------------------------
296  // Display results:
297  // ----------------------------
298  CDisplayWindow3D win("graph-slam demo results");
299  CDisplayWindow3D win2("graph-slam demo initial state");
300 
301  // The final optimized graph:
302  TParametersDouble graph_render_params1;
303  graph_render_params1["show_edges"] = 1;
304  graph_render_params1["edge_width"] = 1;
305  graph_render_params1["nodes_corner_scale"] = 1;
306  CSetOfObjects::Ptr gl_graph1 =
308  graph, graph_render_params1);
309 
310  // The initial noisy graph:
311  TParametersDouble graph_render_params2;
312  graph_render_params2["show_ground_grid"] = 0;
313  graph_render_params2["show_edges"] = 0;
314  graph_render_params2["show_node_corners"] = 0;
315  graph_render_params2["nodes_point_size"] = 7;
316 
317  CSetOfObjects::Ptr gl_graph2 =
319  graph_initial, graph_render_params2);
320 
321  graph_render_params2["nodes_point_size"] = 5;
322  CSetOfObjects::Ptr gl_graph5 =
324  graph, graph_render_params2);
325 
326  // The ground truth graph:
327  TParametersDouble graph_render_params3;
328  graph_render_params3["show_ground_grid"] = 0;
329  graph_render_params3["show_ID_labels"] = 1;
330  graph_render_params3["show_edges"] = 1;
331  graph_render_params3["edge_width"] = 3;
332  graph_render_params3["nodes_corner_scale"] = 2;
333  CSetOfObjects::Ptr gl_graph3 =
335  graph_GT, graph_render_params3);
336  CSetOfObjects::Ptr gl_graph4 =
338  graph_initial, graph_render_params3);
339 
340  win.addTextMessage(
341  5, 5, "Ground truth: Big corners & thick edges", TColorf(0, 0, 0),
342  1000 /* arbitrary, unique text label ID */,
344  win.addTextMessage(
345  5, 5 + 15, "Initial graph: Gray thick points.", TColorf(0, 0, 0),
346  1001 /* arbitrary, unique text label ID */,
348  win.addTextMessage(
349  5, 5 + 30, "Final graph: Small corners & thin edges",
350  TColorf(0, 0, 0), 1002 /* arbitrary, unique text label ID */,
352 
353  win2.addTextMessage(
354  5, 5, "Ground truth: Big corners & thick edges", TColorf(0, 0, 0),
355  1000 /* arbitrary, unique text label ID */,
357  win2.addTextMessage(
358  5, 5 + 15, "Initial graph: Small corners & thin edges",
359  TColorf(0, 0, 0), 1001 /* arbitrary, unique text label ID */,
361 
362  {
363  COpenGLScene::Ptr& scene = win.get3DSceneAndLock();
364  scene->insert(gl_graph1);
365  scene->insert(gl_graph3);
366  scene->insert(gl_graph2);
367  scene->insert(gl_graph5);
368  win.unlockAccess3DScene();
369  win.repaint();
370  }
371 
372  {
373  COpenGLScene::Ptr& scene = win2.get3DSceneAndLock();
374  scene->insert(gl_graph3);
375  scene->insert(gl_graph4);
376  win2.unlockAccess3DScene();
377  win2.repaint();
378  }
379 
380  // Show progress of error:
381  CDisplayWindowPlots win_err("Evolution of log(sq. error)");
382  win_err.plot(log_sq_err_evolution, "-b");
383  win_err.axis_fit();
384 
385  // wait end:
386  cout << "Close any window to end...\n";
387  while (win.isOpen() && win2.isOpen() && win_err.isOpen() &&
389  {
390  std::this_thread::sleep_for(10ms);
391  }
392  }
393 };
394 
395 int main()
396 {
397  try
398  {
399  // typedef CNetworkOfPoses<CPose2D,map_traits_map_as_vector> my_graph_t;
400 
401  cout << "Select the type of graph to optimize:\n"
402  "1. CNetworkOfPoses2D \n"
403  "2. CNetworkOfPoses2DInf \n"
404  "3. CNetworkOfPoses3D \n"
405  "4. CNetworkOfPoses3DInf \n";
406 
407  cout << ">> ";
408 
409  int i = 0;
410  {
411  string l;
412  std::getline(cin, l);
413  l = mrpt::system::trim(l);
414  i = atoi(&l[0]);
415  }
416 
417  bool add_extra_tightening_edge;
418  cout << "Add an extra, incompatible tightening edge? [y/N] ";
419  {
420  string l;
421  std::getline(cin, l);
422  l = mrpt::system::trim(l);
423  add_extra_tightening_edge = l[0] == 'Y' || l[0] == 'y';
424  }
425 
426  switch (i)
427  {
428  case 1:
429  {
431  demo.run(add_extra_tightening_edge);
432  }
433  break;
434  case 2:
435  {
437  demo.run(add_extra_tightening_edge);
438  }
439  break;
440  case 3:
441  {
443  demo.run(add_extra_tightening_edge);
444  }
445  break;
446  case 4:
447  {
449  demo.run(add_extra_tightening_edge);
450  }
451  break;
452  };
453 
454  std::this_thread::sleep_for(20ms);
455  return 0;
456  }
457  catch (exception& e)
458  {
459  cout << "MRPT exception caught: " << e.what() << endl;
460  return -1;
461  }
462  catch (...)
463  {
464  printf("Another exception!!");
465  return -1;
466  }
467 }
mrpt::system::os::kbhit
bool kbhit() noexcept
An OS-independent version of kbhit, which returns true if a key has been pushed.
Definition: os.cpp:390
graph_tools.h
addEdge
void addEdge(TNodeID from, TNodeID to, const mrpt::aligned_std_map< TNodeID, CPose2D > &real_poses, CNetworkOfPoses2D &graph_links)
Definition: vision_stereo_rectify/test.cpp:36
log_sq_err_evolution
vector< double > log_sq_err_evolution
Definition: vision_stereo_rectify/test.cpp:98
static_size
@ static_size
Definition: eigen_plugins.h:19
CSetOfObjects.h
mrpt::graphs::TNodeID
uint64_t TNodeID
A generic numeric type for unique IDs of nodes or entities.
Definition: TNodeID.h:17
mrpt::graphslam::TResultInfoSpaLevMarq
Output information for mrpt::graphslam::optimize_graph_spa_levmarq()
Definition: graphslam/include/mrpt/graphslam/types.h:67
mrpt
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Definition: CKalmanFilterCapable.h:30
R
const float R
Definition: CKinematicChain.cpp:138
p
GLfloat GLfloat p
Definition: glext.h:6305
mrpt::square
T square(const T x)
Inline function for the square of a number.
Definition: core/include/mrpt/core/bits_math.h:18
mrpt::poses
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
Definition: CHierarchicalMapMHPartition.h:25
mrpt::opengl::MRPT_GLUT_BITMAP_HELVETICA_12
@ MRPT_GLUT_BITMAP_HELVETICA_12
Definition: opengl_fonts.h:31
TParameters.h
random.h
mrpt::random::CRandomGenerator::randomize
void randomize(const uint32_t seed)
Initialize the PRNG from the given random seed.
Definition: RandomGenerator.cpp:32
main
int main()
Definition: vision_stereo_rectify/test.cpp:78
mrpt::graphslam
SLAM methods related to graphs of pose constraints.
Definition: TUserOptionsChecker.h:33
M_PI
#define M_PI
Definition: core/include/mrpt/core/bits_math.h:38
STD_NOISE_NODE_XYZ
const double STD_NOISE_NODE_XYZ
Definition: vision_stereo_rectify/test.cpp:33
mrpt::graphslam::optimize_graph_spa_levmarq
void optimize_graph_spa_levmarq(GRAPH_T &graph, TResultInfoSpaLevMarq &out_info, const std::set< mrpt::graphs::TNodeID > *in_nodes_to_optimize=nullptr, const mrpt::system::TParametersDouble &extra_params=mrpt::system::TParametersDouble(), typename graphslam_traits< GRAPH_T >::TFunctorFeedback functor_feedback=typename graphslam_traits< GRAPH_T >::TFunctorFeedback())
Optimize a graph of pose constraints using the Sparse Pose Adjustment (SPA) sparse representation and...
Definition: levmarq.h:80
mrpt::img
Definition: CCanvas.h:17
COpenGLScene.h
win
mrpt::gui::CDisplayWindow3D::Ptr win
Definition: vision_stereo_rectify/test.cpp:31
EdgeAdders
Generic struct template Auxiliary class to add a new edge to the graph.
Definition: vision_stereo_rectify/test.cpp:49
mrpt::system::TParameters< double >
mrpt::poses::CPose2D
A class used to store a 2D pose, including the 2D coordinate point and a heading (phi) angle.
Definition: CPose2D.h:40
mrpt::poses::CPose3D
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:88
mrpt::opengl::graph_tools::graph_visualize
CSetOfObjects::Ptr graph_visualize(const GRAPH_T &g, const mrpt::system::TParametersDouble &extra_params=mrpt::system::TParametersDouble())
Returns an opengl objects representation of an arbitrary graph, as a network of 3D pose frames.
Definition: graph_tools_impl.h:28
STD4EDGES_COV_MATRIX
const double STD4EDGES_COV_MATRIX
Definition: vision_stereo_rectify/test.cpp:40
mrpt::img::TColorf
A RGB color - floats in the range [0,1].
Definition: TColor.h:79
mrpt::graphs
Abstract graph and tree data structures, plus generic graph algorithms.
Definition: CAStarAlgorithm.h:18
STD_NOISE_EDGE_ANG
const double STD_NOISE_EDGE_ANG
Definition: vision_stereo_rectify/test.cpp:38
levmarq.h
mrpt::opengl::CSetOfObjects::Ptr
std::shared_ptr< CSetOfObjects > Ptr
Definition: CSetOfObjects.h:30
mrpt::gui
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:16
mrpt::math::CMatrixFixedNumeric< double, DIM, DIM >
mrpt::gui::CDisplayWindowPlots
Create a GUI window and display plots with MATLAB-like interfaces and commands.
Definition: CDisplayWindowPlots.h:31
ExampleDemoGraphSLAM
Definition: vision_stereo_rectify/test.cpp:103
mrpt::random::getRandomGenerator
CRandomGenerator & getRandomGenerator()
A static instance of a CRandomGenerator class, for use in single-thread applications.
Definition: RandomGenerator.cpp:19
mrpt::math
This base provides a set of functions for maths stuff.
Definition: math/include/mrpt/math/bits_math.h:13
gui.h
mrpt::system::trim
std::string trim(const std::string &str)
Removes leading and trailing spaces.
Definition: string_utils.cpp:270
mrpt::opengl::COpenGLScene::Ptr
std::shared_ptr< COpenGLScene > Ptr
Definition: COpenGLScene.h:61
STD_NOISE_NODE_ANG
const double STD_NOISE_NODE_ANG
Definition: vision_stereo_rectify/test.cpp:34
mrpt::random
A namespace of pseudo-random numbers generators of diferent distributions.
Definition: random_shuffle.h:17
iterator
Scalar * iterator
Definition: eigen_plugins.h:26
ExampleDemoGraphSLAM::run
void run(bool add_extra_tightening_edge)
Definition: vision_stereo_rectify/test.cpp:119
mrpt::opengl
The namespace for 3D scene representation and rendering.
Definition: CGlCanvasBase.h:15
ERROR_IN_INCOMPATIBLE_EDGE
const double ERROR_IN_INCOMPATIBLE_EDGE
Definition: vision_stereo_rectify/test.cpp:41
mrpt::gui::CDisplayWindow3D
A graphical user interface (GUI) for efficiently rendering 3D scenes in real-time.
Definition: CDisplayWindow3D.h:117
TColor.h
STD_NOISE_EDGE_XYZ
const double STD_NOISE_EDGE_XYZ
Definition: vision_stereo_rectify/test.cpp:37
mrpt::system
This namespace provides a OS-independent interface to many useful functions: filenames manipulation,...
Definition: math_frwds.h:25
params
GLenum const GLfloat * params
Definition: glext.h:3534
mrpt::DEG2RAD
double DEG2RAD(const double x)
Degrees to radians.
Definition: core/include/mrpt/core/bits_math.h:42



Page generated by Doxygen 1.8.17 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at miƩ 12 jul 2023 10:03:34 CEST