diff --git a/include/laser/processor/processor_loop_closure_icp.h b/include/laser/processor/processor_loop_closure_icp.h index 8d95796f4c6795b9bbdae8e1b0b7ff88c6c12261..e6e92bdad1ec9d70aa5394b6e0162276da02a9ad 100644 --- a/include/laser/processor/processor_loop_closure_icp.h +++ b/include/laser/processor/processor_loop_closure_icp.h @@ -25,6 +25,7 @@ /************************** * WOLF includes * **************************/ +#include "laser/internal/config.h" #include "core/common/wolf.h" #include "core/processor/processor_loop_closure.h" #include "core/factor/factor_relative_pose_2d_with_extrinsics.h" diff --git a/src/processor/processor_loop_closure_icp.cpp b/src/processor/processor_loop_closure_icp.cpp index a609404ec3ce49042b23b7f668a4a5b7473e06e4..d962983064fd578e3687503e7428fa71d1f3ff4e 100644 --- a/src/processor/processor_loop_closure_icp.cpp +++ b/src/processor/processor_loop_closure_icp.cpp @@ -134,19 +134,7 @@ MatchLoopClosurePtr ProcessorLoopClosureIcp::matchScans(CaptureLaser2dPtr cap_re Rotation2Dd R(T_s_ref_s_tar.rotation()); transform_guess(2) = R.angle(); - // WOLF_DEBUG("LOOP CLOSURE: Aligning key frames: ", _keyframe_tar->id(), " and ", frm_ref->id(), "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - // WOLF_DEBUG("Sensor own scan params\n"); - // sen_tar->getScanParams().print(); - // WOLF_DEBUG("Sensor other scan params\n"); - // sensor_ref->getScanParams().print(); - // WOLF_DEBUG("Icp params\n"); - // WOLF_DEBUG("\n max_correspondence_dist: ", this->icp_params_.max_correspondence_dist, - // "\n use_point_to_line_distance: ", this->icp_params_.use_point_to_line_distance, - // "\n max_iterations: ", this->icp_params_.max_iterations, - // "\n outliers_adaptive_mult: ", this->icp_params_.outliers_adaptive_mult, - // "\n outliers_adaptive_order: ", this->icp_params_.outliers_adaptive_order, - // "\n outliers_maxPerc: ", this->icp_params_.outliers_maxPerc, "\n use corr tricks: ", this->icp_params_.use_corr_tricks); - WOLF_DEBUG("DBG Attempting to close loop with ", frm_tar->id(), " and ", frm_ref->id()); + WOLF_DEBUG("Attempting to close loop between frames ", frm_tar->id(), " and ", frm_ref->id()); try { icpOutput icp_out = icp_tools_ptr_->align(cap_tar->getScan(), @@ -161,13 +149,15 @@ MatchLoopClosurePtr ProcessorLoopClosureIcp::matchScans(CaptureLaser2dPtr cap_re double mean_error = icp_out.error / icp_out.nvalid; WOLF_DEBUG("DBG ------------------------------"); - WOLF_DEBUG("DBG valid? ", icp_out.valid, - " m_er ", mean_error, " ", points_coeff_used * 100, "% own_id: ", frm_tar->id(), - " other_id: ", frm_ref->id()); - WOLF_DEBUG("DBG own_POSE: ", frm_tar->getState().vector("PO").transpose(), + WOLF_DEBUG_COND(not icp_out.valid, "ICP not valid"); + WOLF_DEBUG_COND(icp_out.valid, + "ICP valid ", + " m_er ", mean_error, " ", points_coeff_used * 100, "% own_id: ", frm_tar->id(), + " other_id: ", frm_ref->id()); + WOLF_DEBUG("own_POSE: ", frm_tar->getState().vector("PO").transpose(), " other_POSE: ", frm_ref->getState().vector("PO").transpose(), - " Icp_guess: ", transform_guess.transpose(), - " Icp_trf: ", icp_out.res_transf.transpose()); + " icp_guess: ", transform_guess.transpose(), + " icp_trf: ", icp_out.res_transf.transpose()); // WOLF_DEBUG("Covariance \n", icp_out.res_covar); // Valid output @@ -175,7 +165,7 @@ MatchLoopClosurePtr ProcessorLoopClosureIcp::matchScans(CaptureLaser2dPtr cap_re mean_error < params_loop_closure_icp_->max_error_threshold and points_coeff_used * 100 > params_loop_closure_icp_->min_points_percent) { - WOLF_DEBUG("DBG MATCH CONFIRMED ", frm_tar->id(), " and ", frm_ref->id()); + WOLF_DEBUG("MATCH CONFIRMED ", frm_tar->id(), " and ", frm_ref->id()); auto match = std::make_shared<MatchLoopClosureIcp>(); match->capture_reference = cap_ref; @@ -188,7 +178,7 @@ MatchLoopClosurePtr ProcessorLoopClosureIcp::matchScans(CaptureLaser2dPtr cap_re } else { - WOLF_DEBUG("DBG DISCARDED ", frm_tar->id(), " and ", frm_ref->id()); + WOLF_DEBUG("MATCH DISCARDED ", frm_tar->id(), " and ", frm_ref->id()); return nullptr; } @@ -228,6 +218,11 @@ FrameBasePtrList ProcessorLoopClosureIcp::generateSearchList(CaptureBasePtr _cap int N = getProblem()->getTrajectory()->getFrameMap().size() - 1 - params_loop_closure_icp_->recent_frames_ignored; + WOLF_DEBUG("ProcessorLoopClosureIcp::generateSearchList N = ", N); + + if (N <= 0) + return FrameBasePtrList(); + auto map_begin = getProblem()->getTrajectory()->getFrameMap().begin(); //std::vector<int> idxs; @@ -283,7 +278,7 @@ FrameBasePtrList ProcessorLoopClosureIcp::generateSearchList(CaptureBasePtr _cap params_loop_closure_icp_->candidate_generation == "RANDOM" or params_loop_closure_icp_->candidate_generation == "Random") { - int checked = 1; + int checked = 0; while (checked < N and frame_list.size() < params_loop_closure_icp_->max_attempts) { @@ -318,6 +313,16 @@ FrameBasePtrList ProcessorLoopClosureIcp::generateSearchList(CaptureBasePtr _cap throw std::runtime_error("ParamsProcessorLoopClosureIcp::candidate_generation only accepts 'tree' or 'random'"); } + WOLF_DEBUG("ProcessorLoopClosureIcp::generateSearchList: in mode ", + params_loop_closure_icp_->candidate_generation, + " resulting list:"); + #ifdef _WOLF_DEBUG + std::cout << "\t"; + for (auto frm : frame_list) + std::cout << frm->id() << ", "; + std::cout << std::endl; + #endif + return frame_list; } diff --git a/test/gtest_processor_loop_closure_icp.cpp b/test/gtest_processor_loop_closure_icp.cpp index 12f33d6c9568a12d52233393fc21d7ded7c6063b..9c60e1481de880f30994f609325fbddcbb792504 100644 --- a/test/gtest_processor_loop_closure_icp.cpp +++ b/test/gtest_processor_loop_closure_icp.cpp @@ -26,6 +26,7 @@ #include "laser/processor/processor_loop_closure_icp.h" #include <stdlib.h> +#include <random> using namespace wolf; using namespace Eigen; @@ -56,35 +57,35 @@ class ProcessorLoopClosureIcp_Test : public testing::Test params->max_attempts = 50; params->candidate_generation = "random"; - params->icp_params.use_point_to_line_distance = 1; + params->icp_params.use_point_to_line_distance = true; params->icp_params.max_correspondence_dist = 1e9; params->icp_params.max_iterations = 1e3; - params->icp_params.use_corr_tricks = 1; - params->icp_params.outliers_maxPerc = 5; - params->icp_params.outliers_adaptive_order = 6; - params->icp_params.outliers_adaptive_mult = 7; - params->icp_params.do_compute_covariance = 1; + params->icp_params.use_corr_tricks = true; + params->icp_params.outliers_maxPerc = 0.8; + params->icp_params.outliers_adaptive_order = 0.7; + params->icp_params.outliers_adaptive_mult = 2; + params->icp_params.do_compute_covariance = true; params->icp_params.cov_factor = 1; - params->icp_params.max_angular_correction_deg = 1e9; - params->icp_params.max_linear_correction = 1e9; - params->icp_params.epsilon_xy = 0.1; - params->icp_params.epsilon_theta = 0.1; + params->icp_params.max_angular_correction_deg = 1.5; + params->icp_params.max_linear_correction = 10; + params->icp_params.epsilon_xy = 0.01; + params->icp_params.epsilon_theta = 0.01; params->icp_params.sigma = 0.1; - params->icp_params.restart = 0; + params->icp_params.restart = false; params->icp_params.restart_threshold_mean_error = 0; params->icp_params.restart_dt = 0; params->icp_params.restart_dtheta = 0; - params->icp_params.clustering_threshold = 0; - params->icp_params.orientation_neighbourhood = 0; - params->icp_params.do_alpha_test = 0; + params->icp_params.clustering_threshold = 0.2; + params->icp_params.orientation_neighbourhood = 5; + params->icp_params.do_alpha_test = false; params->icp_params.do_alpha_test_thresholdDeg = 0; - params->icp_params.do_visibility_test = 0; - params->icp_params.outliers_remove_doubles = 0; - params->icp_params.debug_verify_tricks = 0; + params->icp_params.do_visibility_test = false; + params->icp_params.outliers_remove_doubles = false; + params->icp_params.debug_verify_tricks = false; params->icp_params.min_reading = 0; params->icp_params.max_reading = 100; - params->icp_params.use_ml_weights = 0; - params->icp_params.use_sigma_weights = 0; + params->icp_params.use_ml_weights = false; + params->icp_params.use_sigma_weights = false; processor = std::static_pointer_cast<ProcessorLoopClosureIcp>(problem->installProcessor("ProcessorLoopClosureIcp", "prc icp", @@ -101,6 +102,9 @@ class ProcessorLoopClosureIcp_Test : public testing::Test // shape: 0-2 half circles, 1-square, 2-star-like int shape = frame_idx % 3; + WOLF_DEBUG_COND(shape == 0, "Generating ranges: HALF CIRCLES"); + WOLF_DEBUG_COND(shape == 1, "Generating ranges: SQUARE"); + WOLF_DEBUG_COND(shape == 2, "Generating ranges: STAR-LIKE"); std::vector<float> ranges(n_ranges); for (auto i = 0; i < n_ranges; i++) @@ -114,15 +118,18 @@ class ProcessorLoopClosureIcp_Test : public testing::Test ranges[i] = 2.0 * sqrt(std::pow(cos(i * lidar->getScanParams().angle_step_),2) + std::pow(sin(i * lidar->getScanParams().angle_step_),2)); - // STAR-LIKE (2m to 10m) + // STAR-LIKE (3m to 10m) else if (shape == 2) - ranges[i] = 2.0 + (i % (n_ranges / 6)) * 8.0 / (n_ranges / 6); + ranges[i] = 3.0 + (i % (n_ranges / 6)) * 8.0 / (n_ranges / 6); } // rotate randomly - srand (time(NULL)); // initialize random seed - int init = rand() % n_ranges; // half starting randomly between 0 and n_ranges-1 + std::random_device rd; // obtain a random number from hardware + std::mt19937 gen(rd()); // seed the generator + std::uniform_int_distribution<> distr(0, 5); // define the range of random + int init = distr(gen); // generate random number std::rotate(ranges.begin(),ranges.begin()+init,ranges.end()); + WOLF_DEBUG("Rotated ranges in ", init, " beams"); return ranges; } @@ -138,6 +145,48 @@ class ProcessorLoopClosureIcp_Test : public testing::Test // new capture return CaptureBase::emplace<CaptureLaser2d>(frame, frame->getTimeStamp(), lidar, _ranges); } + + void test() + { + TimeStamp t(0.0); + + auto i_loop = 3; + auto N = 6; + while (i_loop < processor->getParams()->recent_frames_ignored) + { + i_loop += 3; + N += 3; + } + + for (int i = 0; i < N; i++) + { + auto frm = emplaceFrame(t, Vector3d::Zero()); + frm->perturb(); + auto cap = emplaceCaptureLaser2d(frm, generateRanges(i)); + + processor->keyFrameCallback(frm); + + WOLF_INFO_COND(i == i_loop, "Loop should have been detected:"); + problem->print(4,1,1,1); + + if (i == i_loop) + { + ASSERT_EQ(cap->getFeatureList().size(), 1); + ASSERT_EQ(cap->getFeatureList().front()->getFactorList().size(), 1); + EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getType(), "FactorRelativePose2dWithExtrinsics"); + ASSERT_FALSE(cap->getFeatureList().front()->getFactorList().front()->getFrameOther() == nullptr); + EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getFrameOther()->getTimeStamp(), TimeStamp(i%3)); + + i_loop += 1 + processor->getParams()->frames_ignored_after_loop; + } + else + { + EXPECT_TRUE(cap->getFeatureList().empty()); + EXPECT_TRUE(frm->getConstrainedByList().empty()); + } + t += 1; + } + } }; @@ -146,95 +195,41 @@ TEST_F(ProcessorLoopClosureIcp_Test, setup) ASSERT_TRUE(problem->check()); } -TEST_F(ProcessorLoopClosureIcp_Test, loop_closure) +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_random) { - TimeStamp t(0.0); - - for (int i = 0; i < 6; i++) - { - auto frm = emplaceFrame(t, Vector3d::Zero()); - auto cap = emplaceCaptureLaser2d(frm, generateRanges(i)); - - processor->keyFrameCallback(frm); - if (i > 2) - { - EXPECT_EQ(cap->getFeatureList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getType(), "FactorRelativePose2dWithExtrinsics"); - EXPECT_FALSE(cap->getFeatureList().front()->getFactorList().front()->getFrameOther() == nullptr); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getFrameOther()->getTimeStamp(), TimeStamp(i%3)); - problem->print(4,1,1,1); - } - else - { - EXPECT_TRUE(cap->getFeatureList().empty()); - EXPECT_TRUE(frm->getConstrainedByList().empty()); - problem->print(4,1,1,1); - } - t += 1; - } + test(); } -TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_ignore_previous) +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_ignore_previous_random) { - processor->getParams()->recent_frames_ignored = 3; - - TimeStamp t(0.0); - - for (int i = 0; i < 6; i++) - { - auto frm = emplaceFrame(t, Vector3d::Zero()); - auto cap = emplaceCaptureLaser2d(frm, generateRanges(i)); - - processor->keyFrameCallback(frm); - if (i > 5) - { - EXPECT_EQ(cap->getFeatureList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getType(), "FactorRelativePose2dWithExtrinsics"); - EXPECT_FALSE(cap->getFeatureList().front()->getFactorList().front()->getFrameOther() == nullptr); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getFrameOther()->getTimeStamp(), TimeStamp(i%3)); - problem->print(4,1,1,1); - } - else - { - EXPECT_TRUE(cap->getFeatureList().empty()); - EXPECT_TRUE(frm->getConstrainedByList().empty()); - problem->print(4,1,1,1); - } - t += 1; - } + processor->getParams()->recent_frames_ignored = 4; + test(); } -TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_frames_ignored_after_loop) +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_frames_ignored_after_loop_random) { processor->getParams()->frames_ignored_after_loop = 2; + test(); +} - TimeStamp t(0.0); +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_tree) +{ + processor->getParams()->candidate_generation = "tree"; + test(); +} - for (int i = 0; i < 6; i++) - { - auto frm = emplaceFrame(t, Vector3d::Zero()); - auto cap = emplaceCaptureLaser2d(frm, generateRanges(i)); +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_ignore_previous_tree) +{ + processor->getParams()->candidate_generation = "tree"; + processor->getParams()->recent_frames_ignored = 4; + test(); +} - processor->keyFrameCallback(frm); - if (i == 3) - { - EXPECT_EQ(cap->getFeatureList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().size(), 1); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getType(), "FactorRelativePose2dWithExtrinsics"); - EXPECT_FALSE(cap->getFeatureList().front()->getFactorList().front()->getFrameOther() == nullptr); - EXPECT_EQ(cap->getFeatureList().front()->getFactorList().front()->getFrameOther()->getTimeStamp(), TimeStamp(i%3)); - problem->print(4,1,1,1); - } - else - { - EXPECT_TRUE(cap->getFeatureList().empty()); - EXPECT_TRUE(frm->getConstrainedByList().empty()); - problem->print(4,1,1,1); - } - t += 1; - } +TEST_F(ProcessorLoopClosureIcp_Test, loop_closure_frames_ignored_after_loop_tree) +{ + processor->getParams()->candidate_generation = "tree"; + processor->getParams()->frames_ignored_after_loop = 2; + test(); } int main(int argc, char **argv)