mp2p_icp/src/ICP.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  * A repertory of multi primitive-to-primitive (MP2P) ICP algorithms in C++
3  * Copyright (C) 2018-2021 Jose Luis Blanco, University of Almeria
4  * See LICENSE for license information.
5  * ------------------------------------------------------------------------- */
13 #include <mp2p_icp/ICP.h>
14 #include <mp2p_icp/covariance.h>
15 #include <mrpt/core/exceptions.h>
16 #include <mrpt/poses/Lie/SE.h>
17 #include <mrpt/tfest/se3.h>
18 
19 #include <regex>
20 
21 IMPLEMENTS_MRPT_OBJECT(ICP, mrpt::rtti::CObject, mp2p_icp)
22 
23 using namespace mp2p_icp;
24 
26  const metric_map_t& pcLocal, const metric_map_t& pcGlobal,
27  const mrpt::math::TPose3D& initialGuessLocalWrtGlobal, const Parameters& p,
28  Results& result, const mrpt::optional_ref<LogRecord>& outputDebugInfo)
29 {
30  using namespace std::string_literals;
31 
32  MRPT_START
33 
34  // ----------------------------
35  // Initial sanity checks
36  // ----------------------------
37  ASSERT_(!matchers_.empty());
38  ASSERT_(!solvers_.empty());
39  ASSERT_(!quality_evaluators_.empty());
40 
41  ASSERT_(!pcGlobal.empty());
42  ASSERT_(!pcLocal.empty());
43 
44  // ----------------------------
45  // Preparation
46  // ----------------------------
47  // Reset output:
48  result = Results();
49 
50  // Prepare output debug records:
51  std::optional<LogRecord> currentLog;
52 
53  const bool generateDebugRecord =
54  outputDebugInfo.has_value() || p.generateDebugFiles;
55 
56  if (generateDebugRecord)
57  {
58  currentLog.emplace();
59  currentLog->pcGlobal = pcGlobal.get_shared_from_this_or_clone();
60  currentLog->pcLocal = pcLocal.get_shared_from_this_or_clone();
61  currentLog->initialGuessLocalWrtGlobal = initialGuessLocalWrtGlobal;
62  currentLog->icpParameters = p;
63  }
64 
65  // ------------------------------------------------------
66  // Main ICP loop
67  // ------------------------------------------------------
68  ICP_State state(pcGlobal, pcLocal);
69  if (currentLog) state.log = &currentLog.value();
70 
72  mrpt::poses::CPose3D(initialGuessLocalWrtGlobal);
73  auto prev_solution = state.currentSolution.optimalPose;
74 
75  for (result.nIterations = 0; result.nIterations < p.maxIterations;
76  result.nIterations++)
77  {
78  state.currentIteration = result.nIterations;
79 
80  // Matchings
81  // ---------------------------------------
82  MatchContext mc;
83  mc.icpIteration = state.currentIteration;
84 
86  matchers_, state.pcGlobal, state.pcLocal,
87  state.currentSolution.optimalPose, mc);
88 
89  if (state.currentPairings.empty())
90  {
92  break;
93  }
94 
95  // Optimal relative pose:
96  // ---------------------------------------
97  SolverContext sc;
98  sc.icpIteration = state.currentIteration;
100 
101  // Compute the optimal pose:
102  const bool solvedOk = run_solvers(
103  solvers_, state.currentPairings, state.currentSolution, sc);
104 
105  if (!solvedOk)
106  {
108  break;
109  }
110 
111  // Updated solution is already in "state.currentSolution".
112 
113  // Termination criterion: small delta:
114  const auto deltaSol = state.currentSolution.optimalPose - prev_solution;
115  const mrpt::math::CVectorFixed<double, 6> dSol =
116  mrpt::poses::Lie::SE<3>::log(deltaSol);
117  const double delta_xyz = dSol.blockCopy<3, 1>(0, 0).norm();
118  const double delta_rot = dSol.blockCopy<3, 1>(3, 0).norm();
119 
121  {
122  printf(
123  "[ICP] Iter=%3u Delta_xyz=%9.02e, Delta_rot=%6.03f deg, "
124  "(xyzypr)=%s pairs=%s\n",
125  static_cast<unsigned int>(state.currentIteration),
126  std::abs(delta_xyz), mrpt::RAD2DEG(std::abs(delta_rot)),
127  state.currentSolution.optimalPose.asString().c_str(),
128  state.currentPairings.contents_summary().c_str());
129  }
130 
131  if (std::abs(delta_xyz) < p.minAbsStep_trans &&
132  std::abs(delta_rot) < p.minAbsStep_rot)
133  {
135  break;
136  }
137 
138  prev_solution = state.currentSolution.optimalPose;
139  }
140 
141  // ----------------------------
142  // Fill in "result"
143  // ----------------------------
144  if (result.nIterations >= p.maxIterations)
146 
147  // Quality:
148  result.quality = evaluate_quality(
149  quality_evaluators_, pcGlobal, pcLocal,
151 
152  // Store output:
153  result.optimal_tf.mean = state.currentSolution.optimalPose;
155  result.finalPairings = std::move(state.currentPairings);
156 
157  // Covariance:
159 
160  result.optimal_tf.cov = mp2p_icp::covariance(
161  result.finalPairings, result.optimal_tf.mean, covParams);
162 
163  // ----------------------------
164  // Log records
165  // ----------------------------
166  // Store results into log struct:
167  if (currentLog) currentLog->icpResult = result;
168 
169  // Save log to disk:
170  if (currentLog.has_value()) save_log_file(*currentLog, p);
171 
172  // return log info:
173  if (currentLog && outputDebugInfo.has_value())
174  outputDebugInfo.value().get() = std::move(currentLog.value());
175 
176  MRPT_END
177 }
178 
179 void ICP::save_log_file(const LogRecord& log, const Parameters& p)
180 {
181  using namespace std::string_literals;
182 
183  if (!p.generateDebugFiles) return;
184 
185  // global log file record counter:
186  static unsigned int logFileCounter = 0;
187  static std::mutex counterMtx;
188  unsigned int RECORD_UNIQUE_ID;
189  {
190  counterMtx.lock();
191  RECORD_UNIQUE_ID = logFileCounter++;
192  counterMtx.unlock();
193  }
194 
196 
197  {
198  const std::string expr = "\\$UNIQUE_ID";
199  const auto value = mrpt::format("%05u", RECORD_UNIQUE_ID);
200  filename = std::regex_replace(filename, std::regex(expr), value);
201  }
202 
203  {
204  const std::string expr = "\\$GLOBAL_ID";
205  const auto value = mrpt::format(
206  "%05u", static_cast<unsigned int>(
207  (log.pcGlobal && log.pcGlobal->id.has_value())
208  ? log.pcGlobal->id.value()
209  : 0));
210  filename = std::regex_replace(filename, std::regex(expr), value);
211  }
212 
213  {
214  const std::string expr = "\\$GLOBAL_LABEL";
215  const auto value = (log.pcGlobal && log.pcGlobal->label.has_value())
216  ? log.pcGlobal->label.value()
217  : ""s;
218  filename = std::regex_replace(filename, std::regex(expr), value);
219  }
220  {
221  const std::string expr = "\\$LOCAL_ID";
222  const auto value = mrpt::format(
223  "%05u", static_cast<unsigned int>(
224  (log.pcLocal && log.pcLocal->id.has_value())
225  ? log.pcLocal->id.value()
226  : 0));
227  filename = std::regex_replace(filename, std::regex(expr), value);
228  }
229 
230  {
231  const std::string expr = "\\$LOCAL_LABEL";
232  const auto value = (log.pcLocal && log.pcLocal->label.has_value())
233  ? log.pcLocal->label.value()
234  : ""s;
235  filename = std::regex_replace(filename, std::regex(expr), value);
236  }
237 
238  const bool saveOk = log.save_to_file(filename);
239  if (!saveOk)
240  {
241  std::cerr << "[ERROR] Could not save icp log file to '" << filename
242  << "'" << std::endl;
243  }
244 }
245 
247  const solver_list_t& solvers, const Pairings& pairings,
248  OptimalTF_Result& out, const SolverContext& sc)
249 {
250  for (const auto& solver : solvers)
251  {
252  ASSERT_(solver);
253  if (solver->optimal_pose(pairings, out, sc)) return true;
254  }
255  return false;
256 }
257 
258 void ICP::initialize_solvers(const mrpt::containers::yaml& params)
259 {
260  initialize_solvers(params, solvers_);
261 }
262 
264  const mrpt::containers::yaml& params, ICP::solver_list_t& lst)
265 {
266  lst.clear();
267 
268  ASSERT_(params.isSequence());
269  for (const auto& entry : params.asSequence())
270  {
271  const auto& e = entry.asMap();
272  // disabled?
273  if (e.count("enabled") && e.at("enabled").as<bool>() == false) continue;
274 
275  const auto sClass = e.at("class").as<std::string>();
276  auto o = mrpt::rtti::classFactory(sClass);
277  ASSERT_(o);
278 
279  auto m = std::dynamic_pointer_cast<Solver>(o);
280  ASSERTMSG_(
281  m, mrpt::format(
282  "`%s` class seems not to be derived from Solver",
283  sClass.c_str()));
284 
285  m->initialize(e.at("params"));
286  lst.push_back(m);
287  }
288 }
289 
290 void ICP::initialize_matchers(const mrpt::containers::yaml& params)
291 {
293 }
294 
296  const mrpt::containers::yaml& params, matcher_list_t& lst)
297 {
298  lst.clear();
299 
300  ASSERT_(params.isSequence());
301  for (const auto& entry : params.asSequence())
302  {
303  const auto& e = entry.asMap();
304  // disabled?
305  if (e.count("enabled") && e.at("enabled").as<bool>() == false) continue;
306 
307  const auto sClass = e.at("class").as<std::string>();
308  auto o = mrpt::rtti::classFactory(sClass);
309  ASSERT_(o);
310 
311  auto m = std::dynamic_pointer_cast<Matcher>(o);
312  ASSERTMSG_(
313  m, mrpt::format(
314  "`%s` class seems not to be derived from Matcher",
315  sClass.c_str()));
316 
317  m->initialize(e.at("params"));
318  lst.push_back(m);
319  }
320 }
321 
323  const mrpt::containers::yaml& params, ICP::quality_eval_list_t& lst)
324 {
325  lst.clear();
326 
327  ASSERT_(params.isSequence());
328  const auto numEntries = params.asSequence().size();
329 
330  for (const auto& entry : params.asSequence())
331  {
332  const auto& e = entry.asMap();
333  // disabled?
334  if (e.count("enabled") && e.at("enabled").as<bool>() == false) continue;
335 
336  const auto sClass = e.at("class").as<std::string>();
337  auto o = mrpt::rtti::classFactory(sClass);
338  ASSERT_(o);
339 
340  auto m = std::dynamic_pointer_cast<QualityEvaluator>(o);
341  ASSERTMSG_(
342  m, mrpt::format(
343  "`%s` class seems not to be derived from QualityEvaluator",
344  sClass.c_str()));
345 
346  m->initialize(e.at("params"));
347 
348  double weight = 1.0;
349  if (numEntries > 0 && e.count("weight") > 0)
350  weight = e.at("weight").as<double>();
351  lst.emplace_back(m, weight);
352  }
353 }
354 
355 void ICP::initialize_quality_evaluators(const mrpt::containers::yaml& params)
356 {
358 }
359 
361  const quality_eval_list_t& evaluators, const metric_map_t& pcGlobal,
362  const metric_map_t& pcLocal, const mrpt::poses::CPose3D& localPose,
363  const Pairings& finalPairings)
364 {
365  ASSERT_(!evaluators.empty());
366 
367  double sumW = .0, sumEvals = .0;
368  for (const auto& e : evaluators)
369  {
370  const double w = e.relativeWeight;
371  ASSERT_GT_(w, 0);
372  const double eval =
373  e.obj->evaluate(pcGlobal, pcLocal, localPose, finalPairings);
374  sumEvals += w * eval;
375  sumW += w;
376  }
377  ASSERT_(sumW > 0);
378 
379  return sumEvals / sumW;
380 }
mp2p_icp::ICP::ICP_State::log
LogRecord * log
Definition: ICP.h:216
mp2p_icp::Results::terminationReason
IterTermReason terminationReason
Definition: Results.h:33
mp2p_icp::OptimalTF_Result::optimalPose
mrpt::poses::CPose3D optimalPose
Definition: OptimalTF_Result.h:26
mp2p_icp::ICP::quality_eval_list_t
std::vector< QualityEvaluatorEntry > quality_eval_list_t
Definition: ICP.h:146
mp2p_icp::Pairings::contents_summary
virtual std::string contents_summary() const
Definition: Pairings.cpp:152
mp2p_icp::IterTermReason::Stalled
@ Stalled
ICP.h
Generic ICP algorithm container.
mp2p_icp
Definition: covariance.h:17
mp2p_icp::metric_map_t::empty
virtual bool empty() const
Definition: metricmap.cpp:245
mp2p_icp::ICP::evaluate_quality
static double evaluate_quality(const quality_eval_list_t &evaluators, const metric_map_t &pcGlobal, const metric_map_t &pcLocal, const mrpt::poses::CPose3D &localPose, const Pairings &finalPairings)
Definition: mp2p_icp/src/ICP.cpp:360
mp2p_icp::Parameters::minAbsStep_trans
double minAbsStep_trans
Definition: Parameters.h:38
s
XmlRpcServer s
mp2p_icp::CovarianceParameters
Definition: covariance.h:19
mp2p_icp::Pairings
Definition: Pairings.h:94
mp2p_icp::Pairings::empty
virtual bool empty() const
Definition: Pairings.h:118
mp2p_icp::ICP::quality_evaluators_
quality_eval_list_t quality_evaluators_
Definition: ICP.h:198
mp2p_icp::OptimalTF_Result::optimalScale
double optimalScale
Definition: OptimalTF_Result.h:27
mp2p_icp::LogRecord
Definition: LogRecord.h:30
mp2p_icp::SolverContext
Definition: Solver.h:30
mp2p_icp::Results::optimal_tf
mrpt::poses::CPose3DPDFGaussian optimal_tf
Definition: Results.h:25
mp2p_icp::ICP::ICP_State::currentSolution
OptimalTF_Result currentSolution
Definition: ICP.h:214
mp2p_icp::OptimalTF_Result
Definition: OptimalTF_Result.h:24
mp2p_icp::Parameters::generateDebugFiles
bool generateDebugFiles
Definition: Parameters.h:57
mp2p_icp::ICP::solvers_
solver_list_t solvers_
Definition: ICP.h:196
mp2p_icp::Results::finalPairings
Pairings finalPairings
Definition: Results.h:41
mp2p_icp::ICP::initialize_solvers
void initialize_solvers(const mrpt::containers::yaml &params)
Definition: mp2p_icp/src/ICP.cpp:258
mp2p_icp::SolverContext::guessRelativePose
std::optional< mrpt::poses::CPose3D > guessRelativePose
Definition: Solver.h:35
mp2p_icp::Parameters::debugFileNameFormat
std::string debugFileNameFormat
Definition: Parameters.h:60
mp2p_icp::ICP::ICP_State::pcGlobal
const metric_map_t & pcGlobal
Definition: ICP.h:210
testing::internal::string
::std::string string
Definition: gtest.h:1979
mp2p_icp::ICP::solver_list_t
std::vector< mp2p_icp::Solver::Ptr > solver_list_t
Definition: ICP.h:68
mp2p_icp::ICP::align
virtual void align(const metric_map_t &pcLocal, const metric_map_t &pcGlobal, const mrpt::math::TPose3D &initialGuessLocalWrtGlobal, const Parameters &p, Results &result, const mrpt::optional_ref< LogRecord > &outputDebugInfo=std::nullopt)
Definition: mp2p_icp/src/ICP.cpp:25
mp2p_icp::Results::nIterations
size_t nIterations
Definition: Results.h:31
mp2p_icp::ICP
Definition: ICP.h:51
mp2p_icp::IterTermReason::MaxIterations
@ MaxIterations
mp2p_icp::LogRecord::save_to_file
bool save_to_file(const std::string &fileName) const
Definition: LogRecord.cpp:74
mp2p_icp::Parameters::maxIterations
uint32_t maxIterations
Definition: Parameters.h:33
mp2p_icp::covariance
mrpt::math::CMatrixDouble66 covariance(const Pairings &finalPairings, const mrpt::poses::CPose3D &finalAlignSolution, const CovarianceParameters &p)
Definition: covariance.cpp:22
mp2p_icp::Results::optimalScale
double optimalScale
Definition: Results.h:28
mp2p_icp::SolverContext::icpIteration
std::optional< uint32_t > icpIteration
Definition: Solver.h:34
kitti-batch-convert.out
string out
Definition: kitti-batch-convert.py:7
mp2p_icp::ICP::solvers
const solver_list_t & solvers() const
Definition: ICP.h:94
mp2p_icp::ICP::initialize_matchers
void initialize_matchers(const mrpt::containers::yaml &params)
Definition: mp2p_icp/src/ICP.cpp:290
mp2p_icp::MatchContext
Definition: Matcher.h:26
mp2p_icp::ICP::ICP_State
Definition: ICP.h:203
mp2p_icp::Parameters::minAbsStep_rot
double minAbsStep_rot
Definition: Parameters.h:43
covariance.h
Covariance estimation methods for ICP results.
mp2p_icp::LogRecord::pcGlobal
metric_map_t::ConstPtr pcGlobal
Definition: LogRecord.h:42
mp2p_icp::IterTermReason::SolverError
@ SolverError
mp2p_icp::IterTermReason::NoPairings
@ NoPairings
mp2p_icp::run_matchers
Pairings run_matchers(const matcher_list_t &matchers, const metric_map_t &pcGlobal, const metric_map_t &pcLocal, const mrpt::poses::CPose3D &local_wrt_global, const MatchContext &mc, const mrpt::optional_ref< MatchState > &userProvidedMS=std::nullopt)
Definition: mp2p_icp/src/Matcher.cpp:37
mp2p_icp::ICP::ICP_State::currentPairings
Pairings currentPairings
Definition: ICP.h:213
mp2p_icp::metric_map_t
Generic container of pointcloud(s), extracted features and other maps.
Definition: metricmap.h:47
mp2p_icp::Parameters::debugPrintIterationProgress
bool debugPrintIterationProgress
Definition: Parameters.h:64
mp2p_icp::LogRecord::pcLocal
metric_map_t::ConstPtr pcLocal
Definition: LogRecord.h:42
IMPLEMENTS_MRPT_OBJECT
IMPLEMENTS_MRPT_OBJECT(metric_map_t, mrpt::serialization::CSerializable, mp2p_icp) using namespace mp2p_icp
mp2p_icp::Results
Definition: Results.h:21
mp2p_icp::ICP::run_solvers
static bool run_solvers(const solver_list_t &solvers, const Pairings &pairings, OptimalTF_Result &out, const SolverContext &sc={})
Definition: mp2p_icp/src/ICP.cpp:246
mp2p_icp::metric_map_t::get_shared_from_this_or_clone
Ptr get_shared_from_this_or_clone()
Definition: metricmap.cpp:456
mp2p_icp::Parameters
Definition: Parameters.h:25
mp2p_icp::ICP::ICP_State::pcLocal
const metric_map_t & pcLocal
Definition: ICP.h:211
mp2p_icp::ICP::initialize_quality_evaluators
void initialize_quality_evaluators(const mrpt::containers::yaml &params)
Definition: mp2p_icp/src/ICP.cpp:355
mp2p_icp::matcher_list_t
std::vector< mp2p_icp::Matcher::Ptr > matcher_list_t
Definition: Matcher.h:98
mp2p_icp::ICP::ICP_State::currentIteration
uint32_t currentIteration
Definition: ICP.h:215
kitti-batch-convert.filename
filename
Definition: kitti-batch-convert.py:6
mp2p_icp::ICP::save_log_file
static void save_log_file(const LogRecord &log, const Parameters &p)
Definition: mp2p_icp/src/ICP.cpp:179
mp2p_icp::ICP::matchers_
matcher_list_t matchers_
Definition: ICP.h:197
mp2p_icp::Results::quality
double quality
Definition: Results.h:38
mp2p_icp::MatchContext::icpIteration
uint32_t icpIteration
The ICP iteration number we are in:
Definition: Matcher.h:31


mrpt_local_obstacles
Author(s): Jose-Luis Blanco-Claraco
autogenerated on Mon Aug 14 2023 02:09:03