test-mp2p_icp_algos.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  * A Modular Optimization framework for Localization and mApping (MOLA)
3  * Copyright (C) 2018-2021 Jose Luis Blanco, University of Almeria
4  * See LICENSE for license information.
5  * ------------------------------------------------------------------------- */
6 
18 #include <mp2p_icp/Solver_Horn.h>
19 #include <mp2p_icp/Solver_OLAE.h>
20 #include <mp2p_icp/load_xyz_file.h>
21 #include <mrpt/core/exceptions.h>
22 #include <mrpt/core/get_env.h>
23 #include <mrpt/maps/CSimplePointsMap.h>
24 #include <mrpt/poses/CPose3D.h>
25 #include <mrpt/poses/CPose3DQuat.h>
26 #include <mrpt/poses/Lie/SE.h>
27 #include <mrpt/poses/Lie/SO.h>
28 #include <mrpt/random.h>
29 #include <mrpt/system/CTimeLogger.h>
30 #include <mrpt/system/filesystem.h>
31 #include <mrpt/version.h>
32 
33 #include <Eigen/Dense>
34 #include <iostream>
35 
36 static int NUM_REPS = mrpt::get_env<int>("NUM_REPS", 3);
37 static bool DO_SAVE_STAT_FILES =
38  mrpt::get_env<bool>("DO_SAVE_STAT_FILES", false);
39 static bool DO_PRINT_ALL = mrpt::get_env<bool>("DO_PRINT_ALL", false);
40 
41 const std::string datasetDir = MP2P_DATASET_DIR;
42 
43 static void test_icp(
44  const std::string& inFile, const std::string& icpClassName,
45  const std::string& solverName, const std::string& matcherName,
46  int pointDecimation)
47 {
48  using namespace mrpt::poses::Lie;
49 
50  const auto fileFullPath = datasetDir + inFile;
51 
52  mrpt::maps::CSimplePointsMap::Ptr pts =
53  mp2p_icp::load_xyz_file(fileFullPath);
54 
55  if (pointDecimation > 1)
56  {
57  auto decimPts = mrpt::maps::CSimplePointsMap::Create();
58  for (size_t i = 0; i < pts->size(); i += pointDecimation)
59  {
60  float x, y, z;
61  pts->getPoint(i, x, y, z);
62  decimPts->insertPoint(x, y, z);
63  }
64  pts = decimPts;
65  }
66 
67  std::cout << "Running " << icpClassName << "|" << solverName << "|"
68  << matcherName << " test on: " << inFile << " with "
69  << pts->size() << " points\n";
70 
71  double outliers_ratio = 0;
72  bool use_robust = true;
73 
74  const std::string tstName = mrpt::format(
75  "test_icp_Model=%s_Algo=%s_%s_%s_outliers=%06.03f_robust=%i",
76  inFile.c_str(), icpClassName.c_str(), solverName.c_str(),
77  matcherName.c_str(), outliers_ratio, use_robust ? 1 : 0);
78 
79  const auto bbox = pts->boundingBox();
80  const auto bbox_size = bbox.max - bbox.min;
81 
82  const double max_dim = mrpt::max3(bbox_size.x, bbox_size.y, bbox_size.z);
83 
84  const double f = 0.15;
85 
86  mrpt::system::CTicTac timer;
87 
88  // Collect stats: columns are (see write TXT to file code at the bottom)
89  mrpt::math::CMatrixDouble stats(NUM_REPS, 2 + 2 + 1);
90 
91  for (int rep = 0; rep < NUM_REPS; rep++)
92  {
93  // Create transformed (and corrupted?) point cloud:
94  auto& rnd = mrpt::random::getRandomGenerator();
95  const double Dx = rnd.drawUniform(-f * bbox_size.x, f * bbox_size.x);
96  const double Dy = rnd.drawUniform(-f * bbox_size.y, f * bbox_size.y);
97  const double Dz = rnd.drawUniform(-f * bbox_size.z, f * bbox_size.z);
98  const double yaw = mrpt::DEG2RAD(rnd.drawUniform(-10.0, 10.0));
99  const double pitch = mrpt::DEG2RAD(rnd.drawUniform(-10.0, 10.0));
100  const double roll = mrpt::DEG2RAD(rnd.drawUniform(-10.0, 10.0));
101 
102  const auto gt_pose = mrpt::poses::CPose3D(Dx, Dy, Dz, yaw, pitch, roll);
103 
104  stats(rep, 0) = SO<3>::log(gt_pose.getRotationMatrix()).norm();
105  stats(rep, 1) = gt_pose.norm();
106 
107  auto pts_reg = mrpt::maps::CSimplePointsMap::Create();
108  pts_reg->changeCoordinatesReference(*pts, -gt_pose);
109 
110  // Point clouds: reference and modified:
111  mp2p_icp::metric_map_t pc_ref, pc_mod;
112  pc_ref.layers["raw"] = pts;
113  pc_mod.layers["raw"] = pts_reg;
114 
115  const auto init_guess = mrpt::math::TPose3D::Identity();
116 
117  mp2p_icp::ICP::Ptr icp = std::dynamic_pointer_cast<mp2p_icp::ICP>(
118  mrpt::rtti::classFactory(icpClassName));
119 
120  if (!icp)
121  THROW_EXCEPTION_FMT(
122  "Could not create object of type `%s`, is it registered?",
123  icpClassName.c_str());
124 
125  // Initialize solvers:
126  if (!solverName.empty())
127  {
128  mp2p_icp::Solver::Ptr solver =
129  std::dynamic_pointer_cast<mp2p_icp::Solver>(
130  mrpt::rtti::classFactory(solverName));
131 
132  if (!solver)
133  THROW_EXCEPTION_FMT(
134  "Could not create Solver of type `%s`, is it registered?",
135  solverName.c_str());
136 
137  icp->solvers().clear();
138  icp->solvers().push_back(solver);
139  }
140 
141  // Initialize matchers:
142  if (!matcherName.empty())
143  {
144  mp2p_icp::Matcher::Ptr matcher =
145  std::dynamic_pointer_cast<mp2p_icp::Matcher>(
146  mrpt::rtti::classFactory(matcherName));
147 
148  if (!matcher)
149  THROW_EXCEPTION_FMT(
150  "Could not create Matcher of type `%s`, is it registered?",
151  matcherName.c_str());
152 
153  icp->matchers().push_back(matcher);
154  }
155 
156  // Special parameters:
157  if (!icp->matchers().empty())
158  {
159  if (auto m = std::dynamic_pointer_cast<
161  icp->matchers().at(0));
162  m)
163  {
164  mrpt::containers::yaml ps;
165  ps["threshold"] = 0.40 * max_dim;
166  m->initialize(ps);
167  }
168 
169  if (auto m =
170  std::dynamic_pointer_cast<mp2p_icp::Matcher_Point2Plane>(
171  icp->matchers().at(0));
172  m)
173  {
174  mrpt::containers::yaml ps;
175  ps["distanceThreshold"] = 0.05 * max_dim;
176  ps["searchRadius"] = 0.20 * max_dim;
177  ps["planeEigenThreshold"] = 10.0;
178  ps["minimumPlanePoints"] = 5;
179  ps["knn"] = 5;
180 
181  m->initialize(ps);
182  }
183  }
184 
185  // ICP test itself:
186  mp2p_icp::Parameters icp_params;
187  mp2p_icp::Results icp_results;
188 
189  icp_params.maxIterations = 100;
190 
191  timer.Tic();
192 
193  icp->align(pc_mod, pc_ref, init_guess, icp_params, icp_results);
194 
195  const double dt = timer.Tac();
196 
197  const auto pos_error = gt_pose - icp_results.optimal_tf.mean;
198  const auto err_log_n = SO<3>::log(pos_error.getRotationMatrix()).norm();
199  const auto err_xyz = pos_error.norm();
200  const auto err_se3 = SE<3>::log(pos_error).norm();
201 
202  stats(rep, 2 + 0) = err_log_n;
203  stats(rep, 2 + 1) = err_xyz;
204  stats(rep, 2 + 2) = dt;
205 
206  if (DO_PRINT_ALL)
207  {
208  std::cout << "GT pose : " << gt_pose.asString() << "\n";
209  std::cout << "ICP pose : "
210  << icp_results.optimal_tf.mean.asString() << "\n";
211  std::cout << "Error SE(3) : " << err_se3 << "\n";
212  std::cout << "ICP pose stddev: "
213  << icp_results.optimal_tf.cov.asEigen()
214  .diagonal()
215  .array()
216  .sqrt()
217  .matrix()
218  .transpose()
219  << "\n";
220  std::cout << "ICP quality : " << icp_results.quality << "\n";
221  std::cout << "ICP iterations : " << icp_results.nIterations << "\n";
222  }
223  ASSERT_LT_(err_se3, 0.1);
224 
225  } // for reps
226 
227  if (DO_SAVE_STAT_FILES)
228  stats.saveToTextFile(
229  mrpt::system::fileNameStripInvalidChars(tstName) +
230  std::string(".txt"),
231  mrpt::math::MATRIX_FORMAT_ENG, true,
232  "% Columns: norm GT_rot, norm_GT_XYZ, norm(SO3_error) "
233  "norm(XYZ_error) icp_time\n\n");
234 }
235 
236 int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
237 {
238  try
239  {
240  auto& rnd = mrpt::random::getRandomGenerator();
241  rnd.randomize(1234); // for reproducible tests
242 
243  const std::vector<const char*> lst_files{
244  {"bunny_decim.xyz.gz", "happy_buddha_decim.xyz.gz"}};
245 
246  using lst_algos_t = std::vector<std::tuple<
247  const char*, const char*, const char*, int /*decimation*/>>;
248  // clang-format off
249  lst_algos_t lst_algos = {
250  {"mp2p_icp::ICP", "mp2p_icp::Solver_Horn", "mp2p_icp::Matcher_Points_DistanceThreshold", 10},
251  {"mp2p_icp::ICP", "mp2p_icp::Solver_Horn", "mp2p_icp::Matcher_Points_InlierRatio", 10},
252  {"mp2p_icp::ICP", "mp2p_icp::Solver_Horn", "mp2p_icp::Matcher_Point2Plane", 10},
253 
254  {"mp2p_icp::ICP", "mp2p_icp::Solver_OLAE", "mp2p_icp::Matcher_Points_DistanceThreshold", 10},
255  {"mp2p_icp::ICP", "mp2p_icp::Solver_OLAE", "mp2p_icp::Matcher_Points_InlierRatio", 10},
256  {"mp2p_icp::ICP", "mp2p_icp::Solver_OLAE", "mp2p_icp::Matcher_Point2Plane", 10},
257 
258  {"mp2p_icp::ICP", "mp2p_icp::Solver_GaussNewton", "mp2p_icp::Matcher_Points_DistanceThreshold", 10},
259  {"mp2p_icp::ICP", "mp2p_icp::Solver_GaussNewton", "mp2p_icp::Matcher_Points_InlierRatio", 10},
260  {"mp2p_icp::ICP", "mp2p_icp::Solver_GaussNewton", "mp2p_icp::Matcher_Point2Plane", 10},
261  };
262  // clang-format on
263 
264  // Optional methods:
265 #if 0 // Disabled for now: we now need to initialize from a YAML file:
267  lst_algos.push_back({"mp2p_icp::ICP_LibPointmatcher", "", "", 1});
268 #endif
269 
270  for (const auto& algo : lst_algos)
271  for (const auto& fil : lst_files)
272  test_icp(
273  fil, std::get<0>(algo), std::get<1>(algo),
274  std::get<2>(algo), std::get<3>(algo));
275  }
276  catch (std::exception& e)
277  {
278  std::cerr << mrpt::exception_to_str(e) << "\n";
279  return 1;
280  }
281 }
const std::string datasetDir
std::map< layer_name_t, mrpt::maps::CMetricMap::Ptr > layers
Definition: metricmap.h:74
ICP registration for points and planes.
static bool DO_SAVE_STAT_FILES
size_t nIterations
Definition: Results.h:31
Generic container of pointcloud(s), extracted features and other maps.
Definition: metricmap.h:47
x
Definition: test.py:4
::std::string string
Definition: gtest.h:1979
uint32_t maxIterations
Definition: Parameters.h:33
mrpt::poses::CPose3DPDFGaussian optimal_tf
Definition: Results.h:25
mrpt::maps::CSimplePointsMap::Ptr load_xyz_file(const std::string &fil)
ICP registration for points and planes.
static int NUM_REPS
Unit tests common utilities.
int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
boost::timer timer
Definition: Timer.h:80
static void test_icp(const std::string &inFile, const std::string &icpClassName, const std::string &solverName, const std::string &matcherName, int pointDecimation)
ICP solver for pointclouds split in different "layers".
static bool DO_PRINT_ALL
Pointcloud matcher: fixed distance thresholds.
Pointcloud matcher: point to plane-fit of nearby points.
double quality
Definition: Results.h:38
ICP wrapper on libpointmatcher.
virtual void initialize(const mrpt::containers::yaml &params)


mrpt_local_obstacles
Author(s): Jose-Luis Blanco-Claraco
autogenerated on Thu Jun 1 2023 03:06:43