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,
31 const std::optional<mrpt::poses::CPose3DPDFGaussianInf>& prior,
32 const mrpt::optional_ref<LogRecord>& outputDebugInfo)
34 using namespace std::string_literals;
38 mrpt::system::CTimeLoggerEntry tle(
profiler_,
"align");
47 ASSERT_(!pcGlobal.
empty());
48 ASSERT_(!pcLocal.
empty());
53 mrpt::system::CTimeLoggerEntry tle1(
profiler_,
"align.1_prepare");
58 std::optional<LogRecord> currentLog;
60 const bool generateDebugRecord =
63 if (generateDebugRecord)
68 currentLog->initialGuessLocalWrtGlobal = initialGuessLocalWrtGlobal;
69 currentLog->icpParameters = p;
78 std::set<ParameterSource*> activeParamSouces;
89 activeParamSouces.insert(ps);
91 auto lambdaRealizeParamSources = [&]()
93 for (
auto& ps : activeParamSouces) ps->realize();
99 mrpt::system::CTimeLoggerEntry tle2(
profiler_,
"align.2_create_state");
102 if (currentLog) state.
log = ¤tLog.value();
106 const auto initGuess = mrpt::poses::CPose3D(initialGuessLocalWrtGlobal);
111 std::optional<mrpt::poses::CPose3D> prev2_solution;
112 std::optional<mrpt::poses::CPose3D> lastCorrection;
119 mrpt::system::CTimeLoggerEntry tle3(
profiler_,
"align.3_iter");
125 for (
auto& obj :
matchers_) lambdaAddOwnParams(*obj);
126 for (
auto& obj :
solvers_) lambdaAddOwnParams(*obj);
128 lambdaRealizeParamSources();
135 mrpt::system::CTimeLoggerEntry tle4(
profiler_,
"align.3.1_matchers");
149 "[ICP] Iter=%3u No pairings!\n",
157 mrpt::system::CTimeLoggerEntry tle5(
profiler_,
"align.3.2_solvers");
177 "[ICP] Iter=%3u Solver returned false\n",
184 mrpt::system::CTimeLoggerEntry tle6(
188 auto lambdaCalcIncrs = [](
const mrpt::poses::CPose3D& deltaSol)
189 -> std::tuple<double, double>
191 const mrpt::math::CVectorFixed<double, 6> dSol =
192 mrpt::poses::Lie::SE<3>::log(deltaSol);
193 const double delta_xyz = dSol.blockCopy<3, 1>(0, 0).norm();
194 const double delta_rot = dSol.blockCopy<3, 1>(3, 0).norm();
195 return {delta_xyz, delta_rot};
202 lastCorrection = deltaSol;
204 auto [delta_xyz, delta_rot] = lambdaCalcIncrs(deltaSol);
206 if (prev2_solution.has_value())
208 auto [delta_xyz2, delta_rot2] = lambdaCalcIncrs(
211 mrpt::keep_min(delta_xyz, delta_xyz2);
212 mrpt::keep_min(delta_rot, delta_rot2);
218 "[ICP] Iter=%3u Δt=%9.02e, ΔR=%6.03f deg, "
219 "(xyzypr)=%s pairs=%s\n",
221 std::abs(delta_xyz), mrpt::RAD2DEG(std::abs(delta_rot)),
236 if (!currentLog->iterationsDetails.has_value())
237 currentLog->iterationsDetails.emplace();
253 "[ICP] Iter=%3u Solver stalled.\n",
264 const double minQuality = itQ->second;
267 lambdaRealizeParamSources();
273 if (quality < minQuality)
280 "[ICP] Iter=%3u quality checkpoint did not pass: %f < "
283 quality, minQuality);
310 prev2_solution = prev_solution;
321 mrpt::system::CTimeLoggerEntry tle7(
profiler_,
"align.4_quality");
324 lambdaRealizeParamSources();
346 mrpt::system::CTimeLoggerEntry tle8(
profiler_,
"align.5_save_log");
351 currentLog->icpResult = result;
356 currentLog->dynamicVariables =
matchers()
360 ->getVariableValues();
363 currentLog->icpResult = result;
368 auto pc = mp2p_icp::metric_map_t::Create();
369 *pc = *currentLog->pcLocal;
371 currentLog->pcLocal = pc;
375 auto pc = mp2p_icp::metric_map_t::Create();
376 *pc = *currentLog->pcGlobal;
378 currentLog->pcGlobal = pc;
384 if (outputDebugInfo.has_value())
385 outputDebugInfo.value().get() = std::move(currentLog.value());
393 using namespace std::string_literals;
398 static unsigned int logFileCounter = 0;
399 static std::mutex counterMtx;
400 unsigned int RECORD_UNIQUE_ID;
402 auto lck = mrpt::lockHelper(counterMtx);
404 RECORD_UNIQUE_ID = logFileCounter++;
415 const auto value = mrpt::format(
"%05u", RECORD_UNIQUE_ID);
421 const auto value = mrpt::format(
422 "%05u",
static_cast<unsigned int>(
438 const auto value = mrpt::format(
439 "%05u",
static_cast<unsigned int>(
448 const auto value = (log.
pcLocal && log.
pcLocal->label.has_value())
455 const auto baseDir = mrpt::system::extractFileDirectory(
filename);
456 if (!mrpt::system::directoryExists(baseDir))
458 const bool ok = mrpt::system::createDirectory(baseDir);
461 std::cerr <<
"[ICP::save_log_file] Could not create directory to "
462 "save icp log file: '"
463 << baseDir <<
"'" << std::endl;
468 <<
"[ICP::save_log_file] Created output directory for logs: '"
469 << baseDir <<
"'" << std::endl;
477 std::cerr <<
"[ICP::save_log_file] Could not save icp log file to '"
486 for (
const auto& solver :
solvers)
489 if (solver->optimal_pose(pairings,
out, sc))
return true;
504 ASSERT_(params.isSequence());
505 for (
const auto& entry : params.asSequence())
507 const auto& e = entry.asMap();
509 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
511 const auto sClass = e.at(
"class").as<
std::string>();
512 auto o = mrpt::rtti::classFactory(sClass);
515 auto m = std::dynamic_pointer_cast<Solver>(o);
518 "`%s` class seems not to be derived from Solver",
521 m->initialize(e.at(
"params"));
536 ASSERT_(params.isSequence());
537 for (
const auto& entry : params.asSequence())
539 const auto& e = entry.asMap();
541 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
543 const auto sClass = e.at(
"class").as<
std::string>();
544 auto o = mrpt::rtti::classFactory(sClass);
547 auto m = std::dynamic_pointer_cast<Matcher>(o);
550 "`%s` class seems not to be derived from Matcher",
553 m->initialize(e.at(
"params"));
563 ASSERT_(params.isSequence());
564 const auto numEntries = params.asSequence().size();
566 for (
const auto& entry : params.asSequence())
568 const auto& e = entry.asMap();
570 if (e.count(
"enabled") && e.at(
"enabled").as<
bool>() ==
false)
continue;
572 const auto sClass = e.at(
"class").as<
std::string>();
573 auto o = mrpt::rtti::classFactory(sClass);
576 auto m = std::dynamic_pointer_cast<QualityEvaluator>(o);
579 "`%s` class seems not to be derived from QualityEvaluator",
582 m->initialize(e.at(
"params"));
585 if (numEntries > 0 && e.count(
"weight") > 0)
586 weight = e.at(
"weight").as<
double>();
587 lst.emplace_back(m, weight);
598 const metric_map_t& pcLocal,
const mrpt::poses::CPose3D& localPose,
601 ASSERT_(!evaluators.empty());
603 double sumW = .0, sumEvals = .0;
604 for (
const auto& e : evaluators)
606 const double w = e.relativeWeight;
608 const auto evalResult =
609 e.obj->evaluate(pcGlobal, pcLocal, localPose, finalPairings);
611 if (evalResult.hard_discard)
return 0;
613 sumEvals += w * evalResult.quality;
618 return sumEvals / sumW;