MRPT  1.9.9
graph_slam_levmarq_unittest.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 
11 
12 #include <gtest/gtest.h>
14 #include <mrpt/system/filesystem.h>
15 
16 // Defined in tests/test_main.cpp
17 namespace mrpt
18 {
20 }
21 
22 using namespace mrpt;
23 using namespace mrpt::random;
24 using namespace mrpt::poses;
25 using namespace mrpt::graphs;
26 using namespace mrpt::math;
27 using namespace std;
28 
29 // Define in/out files for testing:
30 using in_out_filenames = std::set<std::tuple<std::string, std::string>>;
31 const std::map<std::string, in_out_filenames> inout_graph_files{
32  {"GraphTester2D",
33  {{"graphslam_SE2_in.graph", "graphslam_SE2_out_good.graph"},
34  {"graphslam_SE2_in2.graph", "graphslam_SE2_out_good2.graph"},
35  {"graphslam_SE2_in3.graph", "graphslam_SE2_out_good3.graph"}}},
36  {"GraphTester2DInf",
37  {{"graphslam_SE2_in.graph", "graphslam_SE2_out_good.graph"},
38  {"graphslam_SE2pdf_in.graph", "graphslam_SE2pdf_out_good.graph"}}}};
39 
40 template <class my_graph_t>
41 class GraphTester : public GraphSlamLevMarqTest<my_graph_t>,
42  public ::testing::Test
43 {
44  protected:
45  virtual void SetUp() {}
46  virtual void TearDown() {}
47  void test_ring_path(const char* className)
48  {
49  // This is the initial input graph (make a copy for later use):
50  my_graph_t graph;
52 
53  const my_graph_t graph_initial = graph;
54 
55  // ----------------------------
56  // Run graph slam:
57  // ----------------------------
59  // params["verbose"] = 1;
60  params["max_iterations"] = 100;
61 
63 
65  graph, levmarq_info, nullptr, params);
66 
67  const double err_init = graph_initial.chi2();
68  const double err_end = graph.chi2();
69  std::cout << "err_init: " << err_init << std::endl;
70  std::cout << "err_end: " << err_end << std::endl;
71  // graph_initial.saveToTextFile(
72  // string("in_") + string(className) + string(".graph"));
73 
74  // Do some basic checks on the results:
75  EXPECT_GE(levmarq_info.num_iters, 2U);
76  EXPECT_LE(levmarq_info.final_total_sq_error, 5e-2);
77  EXPECT_LT(err_end, err_init);
78 
79  } // end test_ring_path
80 
82  const my_graph_t& g1, const my_graph_t& g2,
83  const double eps_node_pos = 1e-3, const double eps_edges = 1e-3)
84  {
85  EXPECT_EQ(g1.edges.size(), g2.edges.size());
86  EXPECT_EQ(g1.nodes.size(), g2.nodes.size());
87  EXPECT_EQ(g1.root, g2.root);
88 
89  if (g1.edges.size() != g2.edges.size() ||
90  g1.nodes.size() != g2.nodes.size())
91  return;
92 
93  // Check that the edge values are OK:
94  {
95  typename my_graph_t::const_iterator it1, it2;
96  for (it1 = g1.edges.begin(), it2 = g2.edges.begin();
97  it1 != g1.edges.end(); ++it1, ++it2)
98  {
99  EXPECT_EQ(it1->first, it2->first);
100  EXPECT_NEAR(
101  0,
102  (it1->second.getPoseMean().getAsVectorVal() -
103  it2->second.getPoseMean().getAsVectorVal())
104  .array()
105  .abs()
106  .maxCoeff(),
107  eps_edges);
108  }
109  }
110 
111  // Check nodes:
112  {
113  auto itn1 = g1.nodes.cbegin(), itn2 = g2.nodes.cbegin();
114  for (; itn1 != g1.nodes.cend(); ++itn1, ++itn2)
115  {
116  EXPECT_EQ(itn1->first, itn2->first);
117  EXPECT_NEAR(
118  0,
119  (itn1->second.getAsVectorVal() -
120  itn2->second.getAsVectorVal())
121  .array()
122  .abs()
123  .maxCoeff(),
124  eps_node_pos)
125  << "Poses of keyframe #" << itn1->first
126  << " do not match:" << std::endl
127  << "- Expected: " << itn2->second << std::endl
128  << "- Got : " << itn1->second << std::endl;
129  }
130  }
131  }
132 
134  {
135  my_graph_t graph;
137  // text write:
138  std::stringstream ss;
139  graph.writeAsText(ss);
140  // read:
141  my_graph_t read_graph;
142  ss.seekg(0); // rewind
143  read_graph.readAsText(ss);
144 
145  compare_two_graphs(graph, read_graph);
146  }
147 
149  {
150  my_graph_t graph;
152  // binary write:
154  auto arch = mrpt::serialization::archiveFrom(mem);
155  arch << graph;
156  // read:
157  my_graph_t read_graph;
158  mem.Seek(0);
159  arch >> read_graph;
160 
161  compare_two_graphs(graph, read_graph);
162  }
163 
165  {
166  auto files_it = inout_graph_files.find(type);
167  if (files_it == inout_graph_files.end())
168  return; // No tests for this type
169 
170  const string prefix = MRPT_GLOBAL_UNITTEST_SRC_DIR + string("/tests/");
171  for (const auto& tst : files_it->second)
172  {
173  std::cout << "Testing graph type `" << type << "`, in_file=`"
174  << std::get<0>(tst) << "`" << std::endl;
175 
176  const string in_f = prefix + std::get<0>(tst);
177  ASSERT_FILE_EXISTS_(in_f);
178  const string good_f = prefix + std::get<1>(tst);
179  ASSERT_FILE_EXISTS_(good_f);
180 
181  my_graph_t graph, graph_good;
182  graph.loadFromTextFile(in_f);
183  graph_good.loadFromTextFile(good_f);
184  ASSERT_(graph.nodeCount() > 1);
185  ASSERT_EQ(graph.nodeCount(), graph_good.nodeCount());
186  ASSERT_EQ(graph.edgeCount(), graph_good.edgeCount());
187 
188  // Optimize:
189  const my_graph_t graph_initial = graph;
191  params["max_iterations"] = 100;
192 
194 
196  graph, levmarq_info, nullptr, params);
197 
198  /* DEBUG */
199  const double err_init = graph_initial.chi2();
200  const double err_end = graph.chi2();
201  const double err_good = graph_good.chi2();
202  std::cout << "err_init: " << err_init << std::endl;
203  std::cout << "err_end: " << err_end << std::endl;
204  std::cout << "err_good: " << err_good << std::endl;
205 
206  // Do some basic checks on the results:
207  EXPECT_GE(levmarq_info.num_iters, 2U);
208  EXPECT_LE(levmarq_info.final_total_sq_error, 0.2);
209  EXPECT_LT(err_end, err_init);
210 
211  // Compare to good solution:
212  compare_two_graphs(graph, graph_good);
213  }
214  }
215 };
216 
221 
222 #define GRAPHS_TESTS(_TYPE) \
223  TEST_F(_TYPE, OptimizeSampleRingPath) \
224  { \
225  for (int seed = 1; seed <= 3; seed++) \
226  { \
227  getRandomGenerator().randomize(seed); \
228  test_ring_path(#_TYPE); \
229  } \
230  } \
231  TEST_F(_TYPE, BinarySerialization) \
232  { \
233  getRandomGenerator().randomize(123); \
234  test_graph_bin_serialization(); \
235  } \
236  TEST_F(_TYPE, WriteReadTextFile) \
237  { \
238  getRandomGenerator().randomize(123); \
239  test_graph_text_serialization(); \
240  } \
241  TEST_F(_TYPE, OptimizeCompareKnownSolution) \
242  { \
243  test_optimize_compare_known_solution(#_TYPE); \
244  }
245 
246 MRPT_TODO("Re-enable tests after https://github.com/MRPT/mrpt/issues/770");
247 
249 //GRAPHS_TESTS(GraphTester3D)
251 //GRAPHS_TESTS(GraphTester3DInf)
A namespace of pseudo-random numbers generators of diferent distributions.
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:76
#define GRAPHS_TESTS(_TYPE)
Abstract graph and tree data structures, plus generic graph algorithms.
std::set< std::tuple< std::string, std::string > > in_out_filenames
size_t num_iters
The number of LM iterations executed.
void test_ring_path(const char *className)
STL namespace.
CArchiveStreamBase< STREAM > archiveFrom(STREAM &s)
Helper function to create a templatized wrapper CArchive object for a: MRPT&#39;s CStream, std::istream, std::ostream, std::stringstream
Definition: CArchive.h:555
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
This base provides a set of functions for maths stuff.
const std::map< std::string, in_out_filenames > inout_graph_files
MRPT_TODO("Re-enable tests after https://github.com/MRPT/mrpt/issues/770")
This CStream derived class allow using a memory buffer as a CStream.
uint64_t Seek(int64_t Offset, CStream::TSeekOrigin Origin=sFromBeginning) override
Introduces a pure virtual method for moving to a specified position in the streamed resource...
GLsizei const GLchar ** string
Definition: glext.h:4101
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
std::string MRPT_GLOBAL_UNITTEST_SRC_DIR
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Output information for mrpt::graphslam::optimize_graph_spa_levmarq()
static void create_ring_path(my_graph_t &graph, size_t N_VERTEX=50, double DIST_THRES=7, double NODES_XY_MAX=20)
void compare_two_graphs(const my_graph_t &g1, const my_graph_t &g2, const double eps_node_pos=1e-3, const double eps_edges=1e-3)
#define ASSERT_FILE_EXISTS_(FIL)
Definition: filesystem.h:21
GLenum const GLfloat * params
Definition: glext.h:3534
const Scalar * const_iterator
Definition: eigen_plugins.h:27
GLuint GLuint GLsizei GLenum type
Definition: glext.h:3528
void test_optimize_compare_known_solution(const char *type)
double final_total_sq_error
The sum of all the squared errors for every constraint involved in the problem.



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