diff --git a/include/gnss/processor/processor_gnss_fix.h b/include/gnss/processor/processor_gnss_fix.h index ce27d06339b96c1fbcea291e5efedfdb3ab356d9..d4f9b8ac08309ae17bd1f7ff246630cfefa2b0c4 100644 --- a/include/gnss/processor/processor_gnss_fix.h +++ b/include/gnss/processor/processor_gnss_fix.h @@ -18,11 +18,13 @@ WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorGnssFix); struct ParamsProcessorGnssFix : public ParamsProcessorBase { - bool fix_from_raw, init_enu_map; + bool fix_from_raw, init_enu_map, remove_outliers; GnssUtils::Options compute_pos_opt; double max_time_span; double dist_traveled; double enu_map_init_dist_min; + double enu_map_fix_time; + double outlier_residual_th; ParamsProcessorGnssFix() = default; ParamsProcessorGnssFix(std::string _unique_name, const ParamsServer& _server): @@ -33,7 +35,10 @@ struct ParamsProcessorGnssFix : public ParamsProcessorBase init_enu_map = _server.getParam<bool> (prefix + _unique_name + "/init_enu_map"); if (init_enu_map) enu_map_init_dist_min = _server.getParam<double> (prefix + _unique_name + "/enu_map_init_dist_min"); + enu_map_fix_time = _server.getParam<double> (prefix + _unique_name + "/enu_map_fix_time"); fix_from_raw = _server.getParam<bool> (prefix + _unique_name + "/fix_from_raw"); + remove_outliers = _server.getParam<bool> (prefix + _unique_name + "/remove_outliers"); + outlier_residual_th = _server.getParam<double> (prefix + _unique_name + "/outlier_residual_th"); // COMPUTE POS PARAMS (only if compute fix from yaw) if (fix_from_raw) @@ -67,7 +72,9 @@ struct ParamsProcessorGnssFix : public ParamsProcessorBase "init_enu_map: " + std::to_string(init_enu_map) + "\n" + (init_enu_map ? "enu_map_init_dist_min: "+ std::to_string(enu_map_init_dist_min) + "\n" : "") + - "enu_map_init_dist_min: " + std::to_string(enu_map_init_dist_min) + "\n" + + "enu_map_fix_time: " + std::to_string(enu_map_fix_time) + "\n" + + "remove_outliers: " + std::to_string(remove_outliers) + "\n" + + "outlier_residual_th: " + std::to_string(outlier_residual_th) + "\n" + "keyframe_vote/max_time_span: " + std::to_string(max_time_span) + "\n" + (fix_from_raw ? "gnss/sateph: " + std::to_string(compute_pos_opt.sateph) + "\n" + @@ -149,7 +156,7 @@ class ProcessorGnssFix : public ProcessorBase private: FactorBasePtr emplaceFactor(FeatureBasePtr _ftr_ptr); - bool rejectOutlier(FactorBasePtr ctr_ptr); + bool detectOutlier(FactorBasePtr ctr_ptr); }; diff --git a/include/gnss/processor/processor_tracker_gnss.h b/include/gnss/processor/processor_tracker_gnss.h index 2339456f70b1205d0f5437e1d3ddb9f3df48572b..aa3a19a7d0cb1b15ef9d3e5cd18379d19f956a8c 100644 --- a/include/gnss/processor/processor_tracker_gnss.h +++ b/include/gnss/processor/processor_tracker_gnss.h @@ -20,6 +20,7 @@ struct ParamsProcessorTrackerGnss : public ParamsProcessorTrackerFeature bool remove_outliers, remove_outliers_tdcp, remove_outliers_with_fix; double outlier_residual_th; bool init_frames; + double enu_map_fix_time; ParamsProcessorTrackerGnss() = default; ParamsProcessorTrackerGnss(std::string _unique_name, const ParamsServer& _server): @@ -30,6 +31,7 @@ struct ParamsProcessorTrackerGnss : public ParamsProcessorTrackerFeature outlier_residual_th = _server.getParam<double> (prefix + _unique_name + "/outlier_residual_th"); init_frames = _server.getParam<bool> (prefix + _unique_name + "/init_frames"); max_time_span = _server.getParam<double> (prefix + _unique_name + "/keyframe_vote/max_time_span"); + enu_map_fix_time = _server.getParam<double> (prefix + _unique_name + "/enu_map_fix_time"); // GNSS OPTIONS (see rtklib.h) gnss_opt.sateph = _server.getParam<int> (prefix + _unique_name + "/gnss/sateph"); // satellite ephemeris/clock (0:broadcast ephemeris,1:precise ephemeris,2:broadcast + SBAS,3:ephemeris option: broadcast + SSR_APC,4:broadcast + SSR_COM,5: QZSS LEX ephemeris @@ -74,6 +76,7 @@ struct ParamsProcessorTrackerGnss : public ParamsProcessorTrackerFeature + "remove_outliers: " + std::to_string(remove_outliers) + "\n" + "outlier_residual_th: " + std::to_string(outlier_residual_th) + "\n" + "init_frames: " + std::to_string(init_frames) + "\n" + + "enu_map_fix_time: " + std::to_string(enu_map_fix_time) + "\n" + "keyframe_vote/max_time_span: " + std::to_string(max_time_span) + "\n" + "gnss/sateph: " + std::to_string(gnss_opt.sateph) + "\n" + "gnss/ionoopt: " + std::to_string(gnss_opt.ionoopt) + "\n" @@ -125,6 +128,7 @@ class ProcessorTrackerGnss : public ProcessorTrackerFeature GnssUtils::ComputePosOutput fix_incoming_, fix_last_; unsigned int outliers_pseudorange_, outliers_tdcp_, inliers_pseudorange_, inliers_tdcp_; std::map<int, unsigned int> sat_outliers_; + TimeStamp first_ts_; /** \brief Track provided features in \b _capture * \param _features_in input list of features in \b last to track diff --git a/src/processor/processor_gnss_fix.cpp b/src/processor/processor_gnss_fix.cpp index 13322f533e7a2fa3cd293f8ba1ae0918f7406c43..86cbd606326c71b30891ebcd379e69645ab77b18 100644 --- a/src/processor/processor_gnss_fix.cpp +++ b/src/processor/processor_gnss_fix.cpp @@ -48,29 +48,6 @@ void ProcessorGnssFix::processCapture(CaptureBasePtr _capture) FrameBasePtr new_frame = nullptr; FactorBasePtr new_fac = nullptr; - // ALREADY CREATED KF - PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( incoming_capture_, params_gnss_->time_tolerance); - if (KF_pack and (last_KF_capture_==nullptr or KF_pack->key_frame != last_KF_capture_->getFrame())) - { - WOLF_DEBUG( "PR ", getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp()); - new_frame = KF_pack->key_frame; - } - // MAKE KF - else if (permittedKeyFrame() && voteForKeyFrame()) - { - WOLF_DEBUG("PR ", getName()," emplacing KF TS = ", incoming_capture_->getTimeStamp()); - new_frame = getProblem()->emplaceFrame(KEY, incoming_capture_->getTimeStamp()); - KF_created = true; - } - - // ESTABLISH FACTOR - if (new_frame == nullptr) - return; - - // ESTABLISH FACTOR - // link capture - incoming_capture_->link(new_frame); - // emplace features if (israw) { @@ -102,18 +79,43 @@ void ProcessorGnssFix::processCapture(CaptureBasePtr _capture) } auto incoming_feature = FeatureBase::emplace<FeatureGnssFix>(incoming_capture_, incoming_pos_out_); + // ALREADY CREATED KF + PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( incoming_capture_, params_gnss_->time_tolerance); + if (KF_pack and last_KF_capture_ and KF_pack->key_frame == last_KF_capture_->getFrame()) + KF_pack = nullptr; + if (KF_pack) + { + WOLF_DEBUG("PR ", getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp()); + new_frame = KF_pack->key_frame; + } + // MAKE KF + else if (permittedKeyFrame() && voteForKeyFrame()) + { + WOLF_DEBUG("PR ", getName()," emplacing KF TS = ", incoming_capture_->getTimeStamp()); + new_frame = getProblem()->emplaceFrame(KEY, incoming_capture_->getTimeStamp()); + KF_created = true; + } + // OTHERWISE return + else + return; + assert(new_frame); + + // ESTABLISH FACTOR // emplace factor new_fac = emplaceFactor(incoming_feature); // outlier rejection (only can be evaluated if ENU defined and ENU-MAP initialized) WOLF_DEBUG("ProcessorGnssFix: outlier rejection"); - if (sensor_gnss_->isEnuDefined() && sensor_gnss_->isEnuMapInitialized()) - if (rejectOutlier(new_fac)) + if (params_gnss_->remove_outliers and sensor_gnss_->isEnuDefined() and sensor_gnss_->isEnuMapInitialized()) + if (detectOutlier(new_fac)) { - new_fac->remove(); + new_frame->remove(); return; } + // link capture + incoming_capture_->link(new_frame); + // store last KF last_KF_capture_ = incoming_capture_; last_KF_feature_ = incoming_feature; @@ -146,6 +148,15 @@ void ProcessorGnssFix::processCapture(CaptureBasePtr _capture) } } + // Fix ENU-MAP + if (incoming_capture_->getTimeStamp() - first_capture_->getTimeStamp() > params_gnss_->enu_map_fix_time) + { + sensor_gnss_->getEnuMapTranslation()->fix(); + sensor_gnss_->getEnuMapRoll()->fix(); + sensor_gnss_->getEnuMapPitch()->fix(); + sensor_gnss_->getEnuMapYaw()->fix(); + } + // Notify if KF created if (KF_created) getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_gnss_->time_tolerance); @@ -163,37 +174,6 @@ FactorBasePtr ProcessorGnssFix::emplaceFactor(FeatureBasePtr _ftr) return FactorBase::emplace<FactorGnssFix3d>(_ftr, _ftr, sensor_gnss_, shared_from_this(), params_->apply_loss_function, FAC_ACTIVE); } -bool ProcessorGnssFix::rejectOutlier(FactorBasePtr fac) -{ - //WOLF_DEBUG( "PR ", getName()," rejectOutlier..."); - // Cast feature - auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature()); - // copy states - Eigen::VectorXd x(gnss_ftr->getCapture()->getFrame()->getP()->getState()); - Eigen::VectorXd o(gnss_ftr->getCapture()->getFrame()->getO()->getState()); - Eigen::VectorXd x_antena(sensor_gnss_->getP()->getState()); - Eigen::VectorXd t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState()); - Eigen::VectorXd roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState()); - Eigen::VectorXd pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState()); - Eigen::VectorXd yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState()); - // create double* array of a copy of the state - double* parameters[7] = {x.data(), o.data(), x_antena.data(), t_ENU_map.data(), roll_ENU_map.data(), - pitch_ENU_map.data(), yaw_ENU_map.data()}; - // create residuals pointer - Eigen::VectorXd residuals(3); - // evaluate the factor in this state - fac->evaluate(parameters, residuals.data(), nullptr); - // discard if residual too high evaluated at the current estimation - if (residuals.norm() > 1e3) - { - WOLF_WARN("Discarding GNSS FIX Factor, considered OUTLIER"); - WOLF_TRACE("Feature: ", fac->getMeasurement().transpose(),"\nError: ",(fac->getMeasurementSquareRootInformationUpper().inverse()*residuals).transpose()); - fac->remove(); - return true; - } - return false; -} - bool ProcessorGnssFix::voteForKeyFrame() const { //WOLF_DEBUG("voteForKeyFrame..."); @@ -214,7 +194,7 @@ bool ProcessorGnssFix::voteForKeyFrame() const // ENU not defined if (!sensor_gnss_->isEnuDefined()) { - WOLF_DEBUG("KF because of enu not defined"); + WOLF_INFO("KF because of enu not defined"); return true; } @@ -225,7 +205,7 @@ bool ProcessorGnssFix::voteForKeyFrame() const !first_capture_->isRemoving() and (first_feature_->getMeasurement()-incoming_pos_out_.pos).norm() > params_gnss_->enu_map_init_dist_min) { - WOLF_DEBUG("KF because of enu map not initialized"); + WOLF_INFO("KF because of enu map not initialized"); assert(first_capture_ != nullptr); return true; } @@ -233,7 +213,7 @@ bool ProcessorGnssFix::voteForKeyFrame() const // Distance criterion (ENU defined and ENU-MAP initialized) if (last_KF_capture_ != nullptr && (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm() > params_gnss_->dist_traveled) { - WOLF_DEBUG("KF because of distance criterion: ", (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm()); + WOLF_INFO("KF because of distance criterion: ", (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm()); return true; } // TODO: more alternatives? @@ -242,6 +222,39 @@ bool ProcessorGnssFix::voteForKeyFrame() const return false; } +bool ProcessorGnssFix::detectOutlier(FactorBasePtr fac) +{ + //WOLF_DEBUG( "PR ", getName()," rejectOutlier..."); + // Cast feature + auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature()); + // copy states + Eigen::VectorXd x(gnss_ftr->getCapture()->getFrame()->getP()->getState()); + Eigen::VectorXd o(gnss_ftr->getCapture()->getFrame()->getO()->getState()); + Eigen::VectorXd x_antena(sensor_gnss_->getP()->getState()); + Eigen::VectorXd t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState()); + Eigen::VectorXd roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState()); + Eigen::VectorXd pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState()); + Eigen::VectorXd yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState()); + // create double* array of a copy of the state + double* parameters[7] = {x.data(), o.data(), x_antena.data(), t_ENU_map.data(), roll_ENU_map.data(), + pitch_ENU_map.data(), yaw_ENU_map.data()}; + // create residuals pointer + Eigen::Vector3d residual; + // evaluate the factor in this state + fac->evaluate(parameters, residual.data(), nullptr); + // discard if residual too high evaluated at the current estimation + if (residual.norm() > params_gnss_->outlier_residual_th) + { + WOLF_WARN("Discarding GNSS FIX Factor, considered OUTLIER"); + WOLF_TRACE("Feature: ", fac->getMeasurement().transpose(), + "\n\tError: ",(fac->getMeasurementSquareRootInformationUpper().inverse()*residual).transpose(), + "\n\tResidual: ",residual.transpose(), + "\n\tResidual norm: ",residual.norm(), "(max: ", params_gnss_->outlier_residual_th, ")"); + return true; + } + return false; +} + bool ProcessorGnssFix::storeKeyFrame(FrameBasePtr _frame_ptr) { return true; diff --git a/src/processor/processor_tracker_gnss.cpp b/src/processor/processor_tracker_gnss.cpp index 68991ea0d3a61355a171f126297fac0d2683300c..47a8d4a5e77bcb773a4e42edcf7e420395df85f3 100644 --- a/src/processor/processor_tracker_gnss.cpp +++ b/src/processor/processor_tracker_gnss.cpp @@ -14,7 +14,8 @@ ProcessorTrackerGnss::ProcessorTrackerGnss(ParamsProcessorTrackerGnssPtr _params outliers_pseudorange_(0), outliers_tdcp_(0), inliers_pseudorange_(0), - inliers_tdcp_(0) + inliers_tdcp_(0), + first_ts_() //invalid timestamp { } @@ -59,6 +60,14 @@ void ProcessorTrackerGnss::preProcess() WOLF_INFO("setting ECEF-ENU: ", fix_incoming_.pos.transpose()); sensor_gnss_->setEcefEnu(fix_incoming_.pos, true); } + // Fix ENU-MAP + if (incoming_ptr_->getTimeStamp() - first_ts_ > params_tracker_gnss_->enu_map_fix_time) + { + sensor_gnss_->getEnuMapTranslation()->fix(); + sensor_gnss_->getEnuMapRoll()->fix(); + sensor_gnss_->getEnuMapPitch()->fix(); + sensor_gnss_->getEnuMapYaw()->fix(); + } WOLF_DEBUG("TS: ", incoming_ptr_->getTimeStamp(), " - Fix solution (ECEF): ", fix_incoming_.pos.transpose(), " - Fix solution (GEO): ", fix_incoming_.lat_lon.transpose()); @@ -88,6 +97,10 @@ void ProcessorTrackerGnss::preProcess() untracked_incoming_features_[feat->satNumber()] = feat; } + // store as first timestamp (if any not-filtered satellite) + if (!untracked_incoming_features_.empty() and !first_ts_.ok()) + first_ts_ = incoming_ptr_->getTimeStamp(); + WOLF_INFO("ProcessorTrackerGnss::preProcess()", "\n\tinitial observations: ", n_initial, "\n\tRTKLIB discarded: ", fix_incoming_.discarded_sats.size(),