15 #include <mrpt/core/exceptions.h>
16 #include <mrpt/core/lock_helper.h>
17 #include <mrpt/poses/Lie/SE.h>
18 #include <mrpt/system/filesystem.h>
19 #include <mrpt/tfest/se3.h>
29 const mrpt::math::TPose3D& initialGuessLocalWrtGlobal,
const Parameters& p,
Results& result,
30 const std::optional<mrpt::poses::CPose3DPDFGaussianInf>& prior,
31 const mrpt::optional_ref<LogRecord>& outputDebugInfo)
33 using namespace std::string_literals;
37 mrpt::system::CTimeLoggerEntry tle(
profiler_,
"align");
46 ASSERT_(!pcGlobal.
empty());
47 ASSERT_(!pcLocal.
empty());
52 mrpt::system::CTimeLoggerEntry tle1(
profiler_,
"align.1_prepare");
57 std::optional<LogRecord> currentLog;
61 if (generateDebugRecord)
66 currentLog->initialGuessLocalWrtGlobal = initialGuessLocalWrtGlobal;
67 currentLog->icpParameters = p;
76 std::set<ParameterSource*> activeParamSouces;
87 activeParamSouces.insert(ps);
89 auto lambdaRealizeParamSources = [&]()
91 for (
auto& ps : activeParamSouces) ps->realize();
97 mrpt::system::CTimeLoggerEntry tle2(
profiler_,
"align.2_create_state");
100 if (currentLog) state.
log = ¤tLog.value();
104 const auto initGuess = mrpt::poses::CPose3D(initialGuessLocalWrtGlobal);
109 std::optional<mrpt::poses::CPose3D> prev2_solution;
110 std::optional<mrpt::poses::CPose3D> lastCorrection;
116 mrpt::system::CTimeLoggerEntry tle3(
profiler_,
"align.3_iter");
122 for (
auto& obj :
matchers_) lambdaAddOwnParams(*obj);
123 for (
auto& obj :
solvers_) lambdaAddOwnParams(*obj);
125 lambdaRealizeParamSources();
132 mrpt::system::CTimeLoggerEntry tle4(
profiler_,
"align.3.1_matchers");
145 "[ICP] Iter=%3u No pairings!\n",
153 mrpt::system::CTimeLoggerEntry tle5(
profiler_,
"align.3.2_solvers");
161 const bool solvedOk =
172 "[ICP] Iter=%3u Solver returned false\n",
179 mrpt::system::CTimeLoggerEntry tle6(
profiler_,
"align.3.3_end_criterions");
182 auto lambdaCalcIncrs =
183 [](
const mrpt::poses::CPose3D& deltaSol) -> std::tuple<double, double>
185 const mrpt::math::CVectorFixed<double, 6> dSol = mrpt::poses::Lie::SE<3>::log(deltaSol);
186 const double delta_xyz = dSol.blockCopy<3, 1>(0, 0).norm();
187 const double delta_rot = dSol.blockCopy<3, 1>(3, 0).norm();
188 return {delta_xyz, delta_rot};
195 lastCorrection = deltaSol;
197 auto [delta_xyz, delta_rot] = lambdaCalcIncrs(deltaSol);
199 if (prev2_solution.has_value())
201 auto [delta_xyz2, delta_rot2] =
204 mrpt::keep_min(delta_xyz, delta_xyz2);
205 mrpt::keep_min(delta_rot, delta_rot2);
211 "[ICP] Iter=%3u Δt=%9.02e, ΔR=%6.03f deg, "
212 "(xyzypr)=%s pairs=%s\n",
214 mrpt::RAD2DEG(std::abs(delta_rot)),
227 if (!currentLog->iterationsDetails.has_value()) currentLog->iterationsDetails.emplace();
242 "[ICP] Iter=%3u Solver stalled.\n",
253 const double minQuality = itQ->second;
256 lambdaRealizeParamSources();
262 if (quality < minQuality)
268 "[ICP] Iter=%3u quality checkpoint did not pass: %f < "
297 prev2_solution = prev_solution;
308 mrpt::system::CTimeLoggerEntry tle7(
profiler_,
"align.4_quality");
311 lambdaRealizeParamSources();
333 mrpt::system::CTimeLoggerEntry tle8(
profiler_,
"align.5_save_log");
338 currentLog->icpResult = result;
343 currentLog->dynamicVariables =
344 matchers().begin()->get()->attachedSource()->getVariableValues();
347 currentLog->icpResult = result;
352 auto pc = mp2p_icp::metric_map_t::Create();
353 *pc = *currentLog->pcLocal;
355 currentLog->pcLocal = pc;
359 auto pc = mp2p_icp::metric_map_t::Create();
360 *pc = *currentLog->pcGlobal;
362 currentLog->pcGlobal = pc;
368 if (outputDebugInfo.has_value())
369 outputDebugInfo.value().get() = std::move(currentLog.value());
377 using namespace std::string_literals;
382 static unsigned int logFileCounter = 0;
383 static std::mutex counterMtx;
384 unsigned int RECORD_UNIQUE_ID;
386 auto lck = mrpt::lockHelper(counterMtx);
388 RECORD_UNIQUE_ID = logFileCounter++;
398 const auto value = mrpt::format(
"%05u", RECORD_UNIQUE_ID);
404 const auto value = mrpt::format(
406 static_cast<unsigned int>(
419 const auto value = mrpt::format(
421 static_cast<unsigned int>(
434 const auto baseDir = mrpt::system::extractFileDirectory(
filename);
435 if (!mrpt::system::directoryExists(baseDir))
437 const bool ok = mrpt::system::createDirectory(baseDir);
440 std::cerr <<
"[ICP::save_log_file] Could not create directory to "
441 "save icp log file: '"
442 << baseDir <<
"'" << std::endl;
446 std::cerr <<
"[ICP::save_log_file] Created output directory for logs: '" << baseDir
455 std::cerr <<
"[ICP::save_log_file] Could not save icp log file to '" <<
filename <<
"'"
464 for (
const auto& solver :
solvers)
467 if (solver->optimal_pose(pairings,
out, sc))
return true;
481 ASSERT_(params.isSequence());
482 for (
const auto& entry : params.asSequence())
484 const auto& e = entry.asMap();
486 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
488 const auto sClass = e.at(
"class").as<
std::string>();
489 auto o = mrpt::rtti::classFactory(sClass);
492 auto m = std::dynamic_pointer_cast<Solver>(o);
494 m, mrpt::format(
"`%s` class seems not to be derived from Solver", sClass.c_str()));
496 m->initialize(e.at(
"params"));
510 ASSERT_(params.isSequence());
511 for (
const auto& entry : params.asSequence())
513 const auto& e = entry.asMap();
515 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
517 const auto sClass = e.at(
"class").as<
std::string>();
518 auto o = mrpt::rtti::classFactory(sClass);
521 auto m = std::dynamic_pointer_cast<Matcher>(o);
523 m, mrpt::format(
"`%s` class seems not to be derived from Matcher", sClass.c_str()));
525 m->initialize(e.at(
"params"));
535 ASSERT_(params.isSequence());
536 const auto numEntries = params.asSequence().size();
538 for (
const auto& entry : params.asSequence())
540 const auto& e = entry.asMap();
542 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
544 const auto sClass = e.at(
"class").as<
std::string>();
545 auto o = mrpt::rtti::classFactory(sClass);
548 auto m = std::dynamic_pointer_cast<QualityEvaluator>(o);
551 "`%s` class seems not to be derived from QualityEvaluator", sClass.c_str()));
553 m->initialize(e.at(
"params"));
556 if (numEntries > 0 && e.count(
"weight") > 0) weight = e.at(
"weight").as<
double>();
557 lst.emplace_back(m, weight);
568 const metric_map_t& pcLocal,
const mrpt::poses::CPose3D& localPose,
571 ASSERT_(!evaluators.empty());
573 double sumW = .0, sumEvals = .0;
574 for (
const auto& e : evaluators)
576 const double w = e.relativeWeight;
578 const auto evalResult = e.obj->evaluate(pcGlobal, pcLocal, localPose, finalPairings);
580 if (evalResult.hard_discard)
return 0;
582 sumEvals += w * evalResult.quality;
587 return sumEvals / sumW;