diff --git a/include/laser/processor/processor_loop_closure_icp.h b/include/laser/processor/processor_loop_closure_icp.h index db6c1c261d42ac79b1cafa24a9a09ce3ea1bbfa0..1142c38c997b40a674c04ffece7cc254a58f1d0d 100644 --- a/include/laser/processor/processor_loop_closure_icp.h +++ b/include/laser/processor/processor_loop_closure_icp.h @@ -5,7 +5,7 @@ * WOLF includes * **************************/ #include "core/common/wolf.h" -#include <core/processor/processor_base.h> +#include <core/processor/processor_loop_closure.h> #include <laser/capture/capture_laser_2d.h> #include <core/factor/factor_relative_pose_2d_with_extrinsics.h> @@ -22,10 +22,10 @@ namespace wolf WOLF_PTR_TYPEDEFS(ProcessorLoopClosureIcp); WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorLoopClosureIcp); -struct ParamsProcessorLoopClosureIcp : public ParamsProcessorBase +struct ParamsProcessorLoopClosureIcp : public ParamsProcessorLoopClosure { - int recent_key_frames_ignored; - int key_frames_to_wait; + int recent_frames_ignored; + int frames_to_wait; double max_error_threshold; double min_points_percent; @@ -34,14 +34,14 @@ struct ParamsProcessorLoopClosureIcp : public ParamsProcessorBase ParamsProcessorLoopClosureIcp() = default; ParamsProcessorLoopClosureIcp(std::string _unique_name, const ParamsServer& _server): - ParamsProcessorBase(_unique_name, _server) + ParamsProcessorLoopClosure(_unique_name, _server) { - recent_key_frames_ignored = _server.getParam<int> (prefix + _unique_name + "/recent_key_frames_ignored"); - key_frames_to_wait = _server.getParam<int> (prefix + _unique_name + "/key_frames_to_wait"); + recent_frames_ignored = _server.getParam<int> (prefix + _unique_name + "/recent_frames_ignored"); + frames_to_wait = _server.getParam<int> (prefix + _unique_name + "/frames_to_wait"); max_error_threshold = _server.getParam<double> (prefix + _unique_name + "/max_error_threshold"); min_points_percent = _server.getParam<double> (prefix + _unique_name + "/min_points_percent"); - icp_params.use_point_to_line_distance = _server.getParam<double> (prefix + _unique_name + "/cov_factor"); + icp_params.cov_factor = _server.getParam<double> (prefix + _unique_name + "/cov_factor"); icp_params.use_point_to_line_distance = _server.getParam<int> (prefix + _unique_name + "/use_point_to_line_distance"); icp_params.max_correspondence_dist = _server.getParam<double> (prefix + _unique_name + "/max_correspondence_dist"); @@ -79,22 +79,21 @@ struct ParamsProcessorLoopClosureIcp : public ParamsProcessorBase } std::string print() const override { - return "\n" + ParamsProcessorBase::print() - + "recent_key_frames_ignored: " + std::to_string(recent_key_frames_ignored) + "\n" - + "key_frames_to_wait: " + std::to_string(key_frames_to_wait) + "\n" - + "max_error_threshold: " + std::to_string(max_error_threshold) + "\n" - + "min_points_percent: " + std::to_string(min_points_percent) + "\n"; + return "\n" + ParamsProcessorLoopClosure::print() + + "recent_frames_ignored: " + std::to_string(recent_frames_ignored) + "\n" + + "frames_to_wait: " + std::to_string(frames_to_wait) + "\n" + + "max_error_threshold: " + std::to_string(max_error_threshold) + "\n" + + "min_points_percent: " + std::to_string(min_points_percent) + "\n"; } }; - struct CapturesAligned{ - CaptureBasePtr capture_own; - CaptureBasePtr capture_other; - laserscanutils::icpOutput align_result; - - }; +WOLF_STRUCT_PTR_TYPEDEFS(MatchLoopClosureIcp); +struct MatchLoopClosureIcp : public MatchLoopClosure +{ + laserscanutils::icpOutput align_result_; +}; -class ProcessorLoopClosureIcp : public ProcessorBase +class ProcessorLoopClosureIcp : public ProcessorLoopClosure { protected: // Useful sensor stuff @@ -103,7 +102,7 @@ class ProcessorLoopClosureIcp : public ProcessorBase //Closeloop parameters ParamsProcessorLoopClosureIcpPtr params_loop_closure_icp_; - int key_frames_skipped_; + int frames_skipped_; // ICP algorithm std::shared_ptr<laserscanutils::ICP> icp_tools_ptr_; @@ -115,32 +114,29 @@ class ProcessorLoopClosureIcp : public ProcessorBase void configure(SensorBasePtr _sensor) override; protected: - void processCapture(CaptureBasePtr) override; - - void processKeyFrame(FrameBasePtr _keyframe_ptr, - const double& _time_tolerance) override; - - bool triggerInCapture(CaptureBasePtr) const override; - - bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr, - const double& _time_tolerance) const override; - - bool storeKeyFrame(FrameBasePtr) override; - - bool storeCapture(CaptureBasePtr) override; - - bool voteForKeyFrame() const override; - FrameBasePtrList selectCandidates(FrameBasePtr _keyframe_ptr, - const double &_time_tolerance); + /** \brief Returns if findLoopClosures() has to be called for the given capture + * Since there is no enough information in the capture to decide if a loop can be closed, try always. + */ + bool voteFindLoopClosures(CaptureBasePtr cap) override; - std::map<double, CapturesAligned> evaluateCandidates(FrameBasePtr _keyframe_own, - FrameBasePtrList& _keyframe_candidates); + /** \brief detects and emplaces all features of the given capture + * Since the loop closures of this processor are not based on any features, it is empty + */ + void emplaceFeatures(CaptureBasePtr cap) override{}; - CapturesAligned bestCandidate(std::map<double, CapturesAligned>& _capture_candidates); + /** \brief Find captures that correspond to loop closures with the given capture + */ + std::map<double, MatchLoopClosurePtr> findLoopClosures(CaptureBasePtr _capture) override; - FactorBasePtr emplaceFeatureAndFactor(CapturesAligned& _captures_aligned); + /** \brief validates a loop closure + * Loops are validated just after calling ICP + */ + bool validateLoopClosure(MatchLoopClosurePtr) override{ return true;}; + /** \brief emplaces the factor(s) corresponding to a Loop Closure between two captures + */ + void emplaceFactors(MatchLoopClosurePtr) override; }; } diff --git a/src/processor/processor_loop_closure_icp.cpp b/src/processor/processor_loop_closure_icp.cpp index cd3e809f405ee5b40ee252d21a9420d33c06b42b..ad47fe887654a6b6cf50aef494bfbfd2d804ebd4 100644 --- a/src/processor/processor_loop_closure_icp.cpp +++ b/src/processor/processor_loop_closure_icp.cpp @@ -2,17 +2,15 @@ #include "laser/sensor/sensor_laser_2d.h" #include "core/math/covariance.h" -using namespace wolf; -using namespace laserscanutils; +namespace wolf +{ -namespace wolf{ -// Constructor ProcessorLoopClosureIcp::ProcessorLoopClosureIcp(ParamsProcessorLoopClosureIcpPtr _params) : - ProcessorBase("ProcessorLoopClosureIcp", 2, _params), + ProcessorLoopClosure("ProcessorLoopClosureIcp", 2, _params), params_loop_closure_icp_(_params), - key_frames_skipped_(0) + frames_skipped_(0) { - icp_tools_ptr_ = std::make_shared<ICP>(); + icp_tools_ptr_ = std::make_shared<laserscanutils::ICP>(); } void ProcessorLoopClosureIcp::configure(SensorBasePtr _sensor) @@ -21,218 +19,150 @@ void ProcessorLoopClosureIcp::configure(SensorBasePtr _sensor) laser_scan_params_ = sensor_laser_->getScanParams(); } -void ProcessorLoopClosureIcp::processCapture(CaptureBasePtr _capture_ptr) -{ -} - -void ProcessorLoopClosureIcp::processKeyFrame(FrameBasePtr _keyframe_ptr, const double& _time_tolerance) +bool ProcessorLoopClosureIcp::voteFindLoopClosures(CaptureBasePtr cap) { - // TODO: iterate all stored KF - // packs buffer_pack_kf_.selectFirstPackBefore(_keyframe_ptr->getTimeStamp(),_time_tolerance); - // if not _keyframe_ptr->hasCaptureOf(getSensor()) - //{ - // link _keyframe_ptr to select(const TimeStamp& _time_stamp, const double& _time_tolerance) if not nullptr - // else return - //} - - // PROCESS - if (_keyframe_ptr != nullptr) { - WOLF_INFO("KF TS", _keyframe_ptr->getTimeStamp()); - } else - WOLF_INFO("keyframe nullptr"); - - auto kf_cptr = _keyframe_ptr->getCaptureOf(getSensor()); - auto cptr = buffer_capture_.select(_keyframe_ptr->getTimeStamp(), _time_tolerance); - - WOLF_INFO_COND(cptr != nullptr, "cptr timestamp ", cptr->getTimeStamp()); - - if (kf_cptr != nullptr) - { - // WOLF_INFO("Linking capture"); - // kf_cptr->link(_keyframe_ptr); - } - else + // process 1 and skip 'frames_to_wait' frames + if (frames_skipped_ >= params_loop_closure_icp_->frames_to_wait) { - WOLF_INFO("Not Linking capture"); + frames_skipped_=0; + return true; } - - if(key_frames_skipped_ >= params_loop_closure_icp_->key_frames_to_wait) - { - key_frames_skipped_ = 0; - FrameBasePtrList candidates = selectCandidates(_keyframe_ptr, _time_tolerance); - WOLF_DEBUG("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% START EVALUATE CANDIDATES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); - auto candidates_map = evaluateCandidates(_keyframe_ptr, candidates); - WOLF_DEBUG("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% END EVALUATE CANDIDATES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); - if (not candidates_map.empty()) { - auto best = bestCandidate(candidates_map); - emplaceFeatureAndFactor(best); - // this->getProblem()->print(4,1,1,0); - - } else { - WOLF_DEBUG("%%%%%%%%%%%%%%%% Candidates map is empty :("); - } - } else { - key_frames_skipped_++; - } - } - -bool ProcessorLoopClosureIcp::triggerInCapture(CaptureBasePtr) const -{ + frames_skipped_++; return false; } -bool ProcessorLoopClosureIcp::triggerInKeyFrame(FrameBasePtr _keyframe_ptr, const double& _time_tolerance) const +std::map<double, MatchLoopClosurePtr> ProcessorLoopClosureIcp::findLoopClosures(CaptureBasePtr _capture) { - return true; -} - -bool ProcessorLoopClosureIcp::storeKeyFrame(FrameBasePtr) -{ - return false; -} + assert(std::dynamic_pointer_cast<CaptureLaser2d>(_capture)); + std::map<double, MatchLoopClosurePtr> match_map; -bool ProcessorLoopClosureIcp::storeCapture(CaptureBasePtr) -{ - return true; -} + auto cap_own = std::static_pointer_cast<CaptureLaser2d>(_capture); + auto frame_own = _capture->getFrame(); + auto frame_ref = frame_own->getPreviousFrame(); + auto frame_distance = 1; -bool ProcessorLoopClosureIcp::voteForKeyFrame() const -{ - return false; -} - -FrameBasePtrList ProcessorLoopClosureIcp::selectCandidates(FrameBasePtr _keyframe_ptr, const double &_time_tolerance) -{ - FrameBasePtrList candidates; - int key_frames_counter = 0; - const auto &trajectory = getProblem()->getTrajectory(); - //Consider only key_frames from 1 to n - match_past_key_frame_ - // std::copy_if(frames.begin(), frames.end(), std::back_inserter(candidates), [&](FrameBasePtr _frame) { if(_frame->isKey()) {key_frames_counter++; return (key_frames_counter % match_past_key_frame_ == 0); - // }else{ return false;};}); - for(auto it=trajectory->rbegin(); it != trajectory->rend(); it++) - { - // WOLF_DEBUG("TIMESTAMP KEY FRAME ", (*it)->id(), " ", (*it)->getTimeStamp()); - key_frames_counter++; - if (key_frames_counter > params_loop_closure_icp_->recent_key_frames_ignored and - (_keyframe_ptr->getP()->getState() - (*it)->getP()->getState()).norm() < laser_scan_params_.range_max_) - candidates.push_back(*it); - } - WOLF_DEBUG("%%%%%%%%%%%%%%%%%% CANDIDATES SIZE ", candidates.size()); - return candidates; -} -std::map<double, CapturesAligned> ProcessorLoopClosureIcp::evaluateCandidates(FrameBasePtr _keyframe_own, FrameBasePtrList &_keyframe_candidates) -{ - std::map<double, CapturesAligned> captures_candidates; - CaptureLaser2dPtr capture_current = std::static_pointer_cast<wolf::CaptureLaser2d>(_keyframe_own->getCaptureOf(this->getSensor())); - for(const auto& capture_own : _keyframe_own->getCaptureList()) + while (frame_ref) { - WOLF_DEBUG("CAPTURE TYPE OWN", capture_own->getType()); - if(capture_own->getType() == "CaptureLaser2d") + // not too recent frames + if (frame_distance > params_loop_closure_icp_->recent_frames_ignored) { - CaptureLaser2dPtr capture_laser_own = std::static_pointer_cast<wolf::CaptureLaser2d>(capture_own); - WOLF_DEBUG("CANDIDATES SIZE ", _keyframe_candidates.size()); - for (const auto &_keyframe_other : _keyframe_candidates) - for (const auto &capture_other : _keyframe_other->getCaptureList()) + // search CaptureLaser2d + for (auto cap : frame_ref->getCaptureList()) + { + auto cap_ref = std::static_pointer_cast<wolf::CaptureLaser2d>(cap); + // try to match if no too far (more than laser range) + if (cap_ref and + (frame_ref->getP()->getState() - frame_own->getP()->getState()).norm() < laser_scan_params_.range_max_) { - if (capture_other->getType() == "CaptureLaser2d") + // Initial guess for alignment + Eigen::Vector3d transform_guess; + Eigen::Isometry2d T_w_rown, T_w_rref, T_r_s, T_sref_sown; + + // Transformations + T_w_rown = Eigen::Translation2d(frame_own->getP()->getState()) * + Eigen::Rotation2Dd(frame_own->getO()->getState()(0)); + T_w_rref = Eigen::Translation2d(frame_ref->getP()->getState()) * + Eigen::Rotation2Dd(frame_ref->getO()->getState()(0)); + T_r_s = Eigen::Translation2d(this->getSensor()->getP()->getState()) * Eigen::Rotation2Dd(this->getSensor()->getO()->getState()(0)); + T_sref_sown = T_r_s.inverse() * T_w_rref.inverse() * T_w_rown * T_r_s; + + // Fill up initial guess + transform_guess.head(2) = T_sref_sown.translation(); + Eigen::Rotation2Dd R(T_sref_sown.rotation()); + transform_guess(2) = R.angle(); + + SensorLaser2dPtr sensor_own, sensor_ref; + sensor_own = std::static_pointer_cast<wolf::SensorLaser2d>(cap_own->getSensor()); + sensor_ref = std::static_pointer_cast<wolf::SensorLaser2d>(cap_ref->getSensor()); + + // WOLF_DEBUG("LOOP CLOSURE: Aligning key frames: ", frame_own->id(), " and ", frame_ref->id(), "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + // Finally align + // WOLF_DEBUG("Sensor own scan params\n"); + // sensor_own->getScanParams().print(); + // WOLF_DEBUG("Sensor ref 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 ", frame_own->id(), " and ", frame_ref->id()); + try { - CaptureLaser2dPtr capture_laser_other = std::static_pointer_cast<wolf::CaptureLaser2d>(capture_other); - - // Initial guess for alignment - Eigen::Vector3d transform_guess; - Eigen::Isometry2d T_w_rown, T_w_rother, T_r_s, T_sother_sown; - - // Transformations - T_w_rown = Eigen::Translation2d(_keyframe_own->getP()->getState()) * Eigen::Rotation2Dd(_keyframe_own->getO()->getState()(0)); - T_w_rother = - Eigen::Translation2d(_keyframe_other->getP()->getState()) * Eigen::Rotation2Dd(_keyframe_other->getO()->getState()(0)); - T_r_s = Eigen::Translation2d(this->getSensor()->getP()->getState()) * Eigen::Rotation2Dd(this->getSensor()->getO()->getState()(0)); - T_sother_sown = T_r_s.inverse() * T_w_rother.inverse() * T_w_rown * T_r_s; - - // Fill up initial guess - transform_guess.head(2) = T_sother_sown.translation(); - Eigen::Rotation2Dd R(T_sother_sown.rotation()); - transform_guess(2) = R.angle(); - - SensorLaser2dPtr sensor_own, sensor_other; - sensor_own = std::static_pointer_cast<wolf::SensorLaser2d>(capture_laser_own->getSensor()); - sensor_other = std::static_pointer_cast<wolf::SensorLaser2d>(capture_laser_other->getSensor()); - - // WOLF_DEBUG("LOOP CLOSURE: Aligning key frames: ", _keyframe_own->id(), " and ", _keyframe_other->id(), "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - // Finally align - // WOLF_DEBUG("Sensor own scan params\n"); - // sensor_own->getScanParams().print(); - // WOLF_DEBUG("Sensor other scan params\n"); - // sensor_other->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 ", _keyframe_own->id(), " and ", _keyframe_other->id()); - try + laserscanutils::icpOutput trf = + icp_tools_ptr_->align(cap_own->getScan(), + cap_ref->getScan(), + sensor_own->getScanParams(), + sensor_ref->getScanParams(), + params_loop_closure_icp_->icp_params, + transform_guess); + + double points_coeff_used = ((double)trf.nvalid / (double)fmin(cap_own->getScan().ranges_raw_.size(), + cap_ref->getScan().ranges_raw_.size())); + double mean_error = trf.error / trf.nvalid; + WOLF_DEBUG("DBG ------------------------------"); + WOLF_DEBUG("DBG valid? ", trf.valid, " m_er ", mean_error, " ", points_coeff_used * 100, "% own_id: ", frame_own->id(), + " ref_id: ", frame_ref->id()); + WOLF_DEBUG("DBG own_POSE: ", frame_own->getState().vector("PO").transpose(), " ref_POSE: ", frame_ref->getState().vector("PO").transpose(), + " Icp_guess: ", transform_guess.transpose(), " Icp_trf: ", trf.res_transf.transpose()); + + // WOLF_DEBUG("Covariance \n", trf.res_covar); + if (trf.valid == 1 and + mean_error < params_loop_closure_icp_->max_error_threshold and + points_coeff_used * 100 > params_loop_closure_icp_->min_points_percent) { - laserscanutils::icpOutput trf = - icp_tools_ptr_->align(capture_laser_own->getScan(), - capture_laser_other->getScan(), - sensor_own->getScanParams(), - sensor_other->getScanParams(), - params_loop_closure_icp_->icp_params, - transform_guess); - - double points_coeff_used = ((double)trf.nvalid / (double)fmin(capture_laser_own->getScan().ranges_raw_.size(), - capture_laser_other->getScan().ranges_raw_.size())); - double mean_error = trf.error / trf.nvalid; - WOLF_DEBUG("DBG ------------------------------"); - WOLF_DEBUG("DBG valid? ", trf.valid, " m_er ", mean_error, " ", points_coeff_used * 100, "% own_id: ", _keyframe_own->id(), - " other_id: ", _keyframe_other->id()); - WOLF_DEBUG("DBG own_POSE: ", _keyframe_own->getState().vector("PO").transpose(), " other_POSE: ", _keyframe_other->getState().vector("PO").transpose(), - " Icp_guess: ", transform_guess.transpose(), " Icp_trf: ", trf.res_transf.transpose()); - // WOLF_DEBUG("Covariance \n", trf.res_covar); - if (trf.valid == 1 and - mean_error < params_loop_closure_icp_->max_error_threshold and - points_coeff_used * 100 > params_loop_closure_icp_->min_points_percent) - { - WOLF_DEBUG("DBG ADDED ", _keyframe_own->id(), " and ", _keyframe_other->id()); - captures_candidates.emplace(mean_error, CapturesAligned({capture_laser_own, capture_laser_other, trf})); - } - else - { - WOLF_DEBUG("DBG DISCARDED ", _keyframe_own->id(), " and ", _keyframe_other->id()); - } + WOLF_DEBUG("DBG ADDED ", frame_own->id(), " and ", frame_ref->id()); + + auto match = std::make_shared<MatchLoopClosureIcp>(); + match->capture_reference_ptr_ = cap_ref; + match->capture_target_ptr_ = cap_own; + match->normalized_score_ = 1 - mean_error / params_loop_closure_icp_->max_error_threshold; + match->align_result_ = trf; + match_map.emplace(match->normalized_score_, match); } - catch (std::exception &e) + else { - WOLF_WARN(e.what()) + WOLF_DEBUG("DBG DISCARDED ", frame_own->id(), " and ", frame_ref->id()); } + + } + catch (std::exception &e) + { + WOLF_WARN("ProcessorLoopClosureIcp: ICP failed with error: ", e.what()); } } + } } + frame_ref = frame_ref->getPreviousFrame(); + frame_distance++; } - return captures_candidates; -} -CapturesAligned ProcessorLoopClosureIcp::bestCandidate(std::map<double, CapturesAligned> &_capture_candidates) -{ - return _capture_candidates.begin()->second; + + return match_map; } -FactorBasePtr ProcessorLoopClosureIcp::emplaceFeatureAndFactor(CapturesAligned &_captures_aligned) + +void ProcessorLoopClosureIcp::emplaceFactors(MatchLoopClosurePtr _match) { - assert(_captures_aligned.align_result.valid == 1); - if (not isCovariance(_captures_aligned.align_result.res_covar)) - _captures_aligned.align_result.res_covar = 1e-4 * Eigen::Matrix3d::Identity(); - - auto ftr = FeatureBase::emplace<FeatureICPAlign>(_captures_aligned.capture_own, - _captures_aligned.align_result); - return FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(ftr, - ftr, - _captures_aligned.capture_other->getFrame(), - shared_from_this(), - params_->apply_loss_function, - TOP_LOOP); + auto match_icp = std::static_pointer_cast<MatchLoopClosureIcp>(_match); + + assert(match_icp->align_result_.valid == 1); + + if (not isCovariance(match_icp->align_result_.res_covar)) + match_icp->align_result_.res_covar = 1e-4 * Eigen::Matrix3d::Identity(); + + auto ftr = FeatureBase::emplace<FeatureICPAlign>(match_icp->capture_target_ptr_, + match_icp->align_result_); + + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(ftr, + ftr, + match_icp->capture_reference_ptr_->getFrame(), + shared_from_this(), + params_->apply_loss_function, + TOP_LOOP); } + } // Register in the FactorySensor #include "core/processor/factory_processor.h"