@@ -194,6 +194,7 @@ void FrameToFrameLoopClosure::initialize(const mrpt::containers::yaml& c)
194194 YAML_LOAD_OPT (params_, max_lc_candidates, size_t );
195195 YAML_LOAD_OPT (params_, min_frames_between_lc, size_t );
196196 YAML_LOAD_OPT (params_, max_lc_optimization_rounds, size_t );
197+ YAML_LOAD_OPT (params_, lc_optimize_every_n, size_t );
197198
198199 if (params_.min_frames_between_lc == 0 )
199200 {
@@ -357,21 +358,25 @@ void FrameToFrameLoopClosure::process(mrpt::maps::CSimpleMap& sm) // NOLINT
357358
358359 MRPT_LOG_INFO_STREAM (" Found " << candidates.size () << " loop closure candidates" );
359360
360- // Sort candidates by frame locality to maximize point cloud cache hits:
361+ // Sort candidates by ascending topological gap (frame index separation)
362+ // so that inner (smaller) loops are closed first, improving the graph
363+ // before attempting larger loops:
361364 std::sort (
362365 candidates.begin (), candidates.end (),
363366 [](const LoopCandidate& a, const LoopCandidate& b)
364367 {
365- const auto minA = std::min (a. frame_i , a. frame_j ) ;
366- const auto minB = std::min (b. frame_i , b. frame_j ) ;
367- if (minA != minB )
368+ const auto gapA = a. frame_j - a. frame_i ;
369+ const auto gapB = b. frame_j - b. frame_i ;
370+ if (gapA != gapB )
368371 {
369- return minA < minB ;
372+ return gapA < gapB ;
370373 }
371- return std::max (a.frame_i , a.frame_j ) < std::max (b.frame_i , b.frame_j );
374+ // Tie-break: earlier frame first (cache locality)
375+ return std::min (a.frame_i , a.frame_j ) < std::min (b.frame_i , b.frame_j );
372376 });
373377
374- const auto frameGroup = static_cast <double >(params_.min_frames_between_lc );
378+ const auto frameGroup = static_cast <double >(params_.min_frames_between_lc );
379+ size_t acceptedSinceLastOpt = 0 ;
375380
376381 for (const auto & lc : candidates)
377382 {
@@ -397,6 +402,19 @@ void FrameToFrameLoopClosure::process(mrpt::maps::CSimpleMap& sm) // NOLINT
397402 {
398403 anyGraphChange = true ;
399404 accepted_lcs++;
405+ acceptedSinceLastOpt++;
406+
407+ // Intermediate optimization: re-optimize after every N accepted LCs
408+ // so that later (larger-gap) candidates benefit from corrected poses.
409+ if (params_.lc_optimize_every_n > 0 &&
410+ acceptedSinceLastOpt >= params_.lc_optimize_every_n )
411+ {
412+ MRPT_LOG_INFO_STREAM (
413+ " Intermediate optimization after " << acceptedSinceLastOpt
414+ << " accepted LCs" );
415+ optimize_graph ();
416+ acceptedSinceLastOpt = 0 ;
417+ }
400418 }
401419 }
402420
@@ -405,8 +423,9 @@ void FrameToFrameLoopClosure::process(mrpt::maps::CSimpleMap& sm) // NOLINT
405423 break ; // No new candidates
406424 }
407425
408- if (anyGraphChange)
426+ if (anyGraphChange && acceptedSinceLastOpt > 0 )
409427 {
428+ // Final optimization for remaining accepted LCs in this round
410429 const double largestDelta = optimize_graph ();
411430
412431 if (params_.save_3d_scene_files && params_.save_3d_scene_files_per_iteration )
@@ -1130,21 +1149,28 @@ double FrameToFrameLoopClosure::optimize_graph()
11301149
11311150 // Log GNC weights for LC edges (for diagnostics)
11321151 const auto & gncWeights = gnc.getWeights ();
1133- size_t numLcOutliers = 0 ;
1152+ size_t numLcOutliers = 0 ;
1153+ size_t numLcInliers = 0 ;
11341154 for (size_t k = 0 ; k < static_cast <size_t >(gncWeights.size ()); k++)
11351155 {
11361156 // Check if this factor is NOT a known inlier (i.e., it's an LC edge)
11371157 const bool isKnownInlier = std::binary_search (
11381158 state_.knownInlierFactorIndices .begin (), state_.knownInlierFactorIndices .end (), k);
1139- if (!isKnownInlier && gncWeights[k] < 0.5 )
1159+ if (!isKnownInlier)
11401160 {
1141- numLcOutliers++;
1161+ if (gncWeights[k] < 0.5 )
1162+ {
1163+ numLcOutliers++;
1164+ }
1165+ else
1166+ {
1167+ numLcInliers++;
1168+ }
11421169 }
11431170 }
1144- if (numLcOutliers > 0 )
1145- {
1146- MRPT_LOG_INFO_STREAM (" GNC rejected " << numLcOutliers << " LC edge(s) as outliers" );
1147- }
1171+ MRPT_LOG_INFO_STREAM (
1172+ " GNC result: " << numLcInliers << " LC inlier(s), " << numLcOutliers
1173+ << " LC outlier(s) rejected" );
11481174
11491175 // Compute largest pose change
11501176 double largestDelta = 0.0 ;
@@ -1318,11 +1344,10 @@ void FrameToFrameLoopClosure::save_3d_scene_files(const std::string& suffix) con
13181344 {
13191345 auto lines = mrpt::opengl::CSetOfLines::Create ();
13201346 lines->setLineWidth (params_.scene_path_line_width );
1321- lines->setColor_u8 (
1322- mrpt::img::TColorf (
1323- params_.scene_path_color_r , params_.scene_path_color_g , params_.scene_path_color_b ,
1324- params_.scene_path_color_a * 255 )
1325- .asTColor ());
1347+ lines->setColor_u8 (mrpt::img::TColorf (
1348+ params_.scene_path_color_r , params_.scene_path_color_g ,
1349+ params_.scene_path_color_b , params_.scene_path_color_a * 255 )
1350+ .asTColor ());
13261351
13271352 for (size_t i = 1 ; i < sm.size (); i++)
13281353 {
@@ -1348,11 +1373,10 @@ void FrameToFrameLoopClosure::save_3d_scene_files(const std::string& suffix) con
13481373 {
13491374 auto pts = mrpt::opengl::CPointCloud::Create ();
13501375 pts->setPointSize (params_.scene_keyframe_point_size );
1351- pts->setColor_u8 (
1352- mrpt::img::TColorf (
1353- params_.scene_path_color_r , params_.scene_path_color_g , params_.scene_path_color_b ,
1354- params_.scene_path_color_a )
1355- .asTColor ());
1376+ pts->setColor_u8 (mrpt::img::TColorf (
1377+ params_.scene_path_color_r , params_.scene_path_color_g ,
1378+ params_.scene_path_color_b , params_.scene_path_color_a )
1379+ .asTColor ());
13561380
13571381 for (size_t i = 0 ; i < sm.size (); i++)
13581382 {
@@ -1377,11 +1401,10 @@ void FrameToFrameLoopClosure::save_3d_scene_files(const std::string& suffix) con
13771401 {
13781402 auto lines = mrpt::opengl::CSetOfLines::Create ();
13791403 lines->setLineWidth (params_.scene_lc_line_width );
1380- lines->setColor_u8 (
1381- mrpt::img::TColorf (
1382- params_.scene_lc_color_r , params_.scene_lc_color_g , params_.scene_lc_color_b ,
1383- params_.scene_lc_color_a )
1384- .asTColor ());
1404+ lines->setColor_u8 (mrpt::img::TColorf (
1405+ params_.scene_lc_color_r , params_.scene_lc_color_g ,
1406+ params_.scene_lc_color_b , params_.scene_lc_color_a )
1407+ .asTColor ());
13851408
13861409 for (const auto & [fi, fj] : accepted_lc_edges_)
13871410 {
0 commit comments