diff --git a/.gitignore b/.gitignore index ea3b11ae3f1e21c872d7f069c6d6b56ca83c2a54..b690efc9b285d63727f435a9db3ee8b2039aee82 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ src/examples/map_apriltag_save.yaml build_release/ .clangd wolfcore.found +/wolf.found diff --git a/CMakeLists.txt b/CMakeLists.txt index b84a4aa218a28e25b767d517f055f0c901bf8e8d..976709646ff5d2d052630537fe6178a4251c1653 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,7 @@ SET(HDRS_TREE_MANAGER include/core/tree_manager/factory_tree_manager.h include/core/tree_manager/tree_manager_base.h include/core/tree_manager/tree_manager_sliding_window.h + include/core/tree_manager/tree_manager_sliding_window_dual_rate.h ) SET(HDRS_YAML include/core/yaml/parser_yaml.h @@ -381,6 +382,7 @@ SET(SRCS_SOLVER ) SET(SRCS_TREE_MANAGER src/tree_manager/tree_manager_sliding_window.cpp + src/tree_manager/tree_manager_sliding_window_dual_rate.cpp ) SET(SRCS_YAML src/yaml/parser_yaml.cpp diff --git a/README.md b/README.md index ad5473fec99242beacf3889f0818e3f4cd93caa7..c1e960469ccdcaf2b9918905b7e10536c9e9af59 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ WOLF - Windowed Localization Frames =================================== -For installation guide and code documentation, please visit the [documentation website](http://mobile_robotics.pages.iri.upc-csic.es/wolf_projects/wolf_lib/wolf_doc/). \ No newline at end of file +For installation guide and code documentation, please visit the [documentation website](http://mobile_robotics.pages.iri.upc-csic.es/wolf_projects/wolf_lib/wolf-doc-sphinx/). \ No newline at end of file diff --git a/include/core/ceres_wrapper/iteration_update_callback.h b/include/core/ceres_wrapper/iteration_update_callback.h index 27e5d758a84821dcaada80e9062c917c7af50020..1717be546ab820d24e13985227dd19753530440e 100644 --- a/include/core/ceres_wrapper/iteration_update_callback.h +++ b/include/core/ceres_wrapper/iteration_update_callback.h @@ -16,10 +16,11 @@ namespace wolf { class IterationUpdateCallback : public ceres::IterationCallback { public: - explicit IterationUpdateCallback(ProblemPtr _problem, bool verbose = false) + explicit IterationUpdateCallback(ProblemPtr _problem, const int& min_num_iterations, bool verbose = false) : problem_(_problem) - , verbose_(verbose) + , min_num_iterations_(min_num_iterations) , initial_cost_(0) + , verbose_(verbose) {} ~IterationUpdateCallback() {} @@ -29,8 +30,9 @@ class IterationUpdateCallback : public ceres::IterationCallback if (summary.iteration == 0) initial_cost_ = summary.cost; - if (problem_->getStateBlockNotificationMapSize() != 0 or - problem_->getFactorNotificationMapSize() != 0) + else if (summary.iteration >= min_num_iterations_ and + (problem_->getStateBlockNotificationMapSize() != 0 or + problem_->getFactorNotificationMapSize() != 0)) { if (summary.cost >= initial_cost_) { @@ -48,8 +50,9 @@ class IterationUpdateCallback : public ceres::IterationCallback private: ProblemPtr problem_; - bool verbose_; + int min_num_iterations_; double initial_cost_; + bool verbose_; }; } diff --git a/include/core/ceres_wrapper/solver_ceres.h b/include/core/ceres_wrapper/solver_ceres.h index e4306aa8ca126185a6343435405c1bcb5940e650..62e2ab1a2da15ecec5ea372150a3c83c8b6b3bea 100644 --- a/include/core/ceres_wrapper/solver_ceres.h +++ b/include/core/ceres_wrapper/solver_ceres.h @@ -23,6 +23,7 @@ WOLF_STRUCT_PTR_TYPEDEFS(ParamsCeres); struct ParamsCeres : public ParamsSolver { bool update_immediately = false; + int min_num_iterations = 1; ceres::Solver::Options solver_options; ceres::Problem::Options problem_options; ceres::Covariance::Options covariance_options; @@ -39,15 +40,21 @@ struct ParamsCeres : public ParamsSolver loadHardcodedValues(); // stop solver whenever the problem is updated (via ceres::iterationCallback) - update_immediately = _server.getParam<bool>(prefix + "update_immediately"); + update_immediately = _server.getParam<bool>(prefix + "update_immediately"); + if (update_immediately) + min_num_iterations = _server.getParam<int>(prefix + "min_num_iterations"); // ceres solver options - solver_options.max_num_iterations = _server.getParam<int>(prefix + "max_num_iterations"); + solver_options.max_num_iterations = _server.getParam<int>(prefix + "max_num_iterations"); + solver_options.num_threads = _server.getParam<int>(prefix + "n_threads"); + covariance_options.num_threads = _server.getParam<int>(prefix + "n_threads"); } void loadHardcodedValues() { solver_options = ceres::Solver::Options(); + solver_options.minimizer_type = ceres::TRUST_REGION; //ceres::LINE_SEARCH; + solver_options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT; //ceres::DOGLEG; problem_options = ceres::Problem::Options(); covariance_options = ceres::Covariance::Options(); problem_options.cost_function_ownership = ceres::DO_NOT_TAKE_OWNERSHIP; @@ -62,7 +69,6 @@ struct ParamsCeres : public ParamsSolver #else covariance_options.algorithm_type = ceres::SPARSE_QR;//ceres::DENSE_SVD; #endif - covariance_options.num_threads = 1; covariance_options.apply_loss_function = false; } @@ -76,6 +82,7 @@ class SolverCeres : public SolverManager std::map<FactorBasePtr, ceres::ResidualBlockId> fac_2_residual_idx_; std::map<FactorBasePtr, ceres::CostFunctionPtr> fac_2_costfunction_; + // this map is only for handling automatic destruction of localParametrizationWrapper objects std::map<StateBlockPtr, LocalParametrizationWrapperPtr> state_blocks_local_param_; ceres::Solver::Summary summary_; @@ -99,10 +106,10 @@ class SolverCeres : public SolverManager std::unique_ptr<ceres::Problem>& getCeresProblem(); - void computeCovariances(CovarianceBlocksToBeComputed _blocks + bool computeCovariances(CovarianceBlocksToBeComputed _blocks = CovarianceBlocksToBeComputed::ROBOT_LANDMARKS) override; - void computeCovariances(const std::vector<StateBlockPtr>& st_list) override; + bool computeCovariances(const std::vector<StateBlockPtr>& st_list) override; bool hasConverged() override; @@ -116,6 +123,10 @@ class SolverCeres : public SolverManager const Eigen::SparseMatrixd computeHessian() const; + virtual int numStateBlocksDerived() const override; + + virtual int numFactorsDerived() const override; + protected: bool checkDerived(std::string prefix="") const override; @@ -138,7 +149,14 @@ class SolverCeres : public SolverManager bool isFactorRegisteredDerived(const FactorBasePtr& fac_ptr) const override; - bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) override; + bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) const override; + + bool isStateBlockFixedDerived(const StateBlockPtr& st) override; + + bool hasThisLocalParametrizationDerived(const StateBlockPtr& st, + const LocalParametrizationBasePtr& local_param) override; + + bool hasLocalParametrizationDerived(const StateBlockPtr& st) const override; }; inline ceres::Solver::Summary SolverCeres::getSummary() @@ -158,16 +176,39 @@ inline ceres::Solver::Options& SolverCeres::getSolverOptions() inline bool SolverCeres::isFactorRegisteredDerived(const FactorBasePtr& fac_ptr) const { - return fac_2_residual_idx_.find(fac_ptr) != fac_2_residual_idx_.end() - && fac_2_costfunction_.find(fac_ptr) != fac_2_costfunction_.end(); + return fac_2_residual_idx_.count(fac_ptr) == 1 and + fac_2_costfunction_.count(fac_ptr) == 1; } -inline bool SolverCeres::isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) +inline bool SolverCeres::isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) const { - return state_blocks_local_param_.find(state_ptr) != state_blocks_local_param_.end() - && ceres_problem_->HasParameterBlock(getAssociatedMemBlockPtr(state_ptr)); + if (state_blocks_.count(state_ptr) == 0) + return false; + return ceres_problem_->HasParameterBlock(getAssociatedMemBlockPtr(state_ptr)); } +inline bool SolverCeres::isStateBlockFixedDerived(const StateBlockPtr& st) +{ + if (state_blocks_.count(st) == 0) + return false; + return ceres_problem_->IsParameterBlockConstant(getAssociatedMemBlockPtr(st)); +}; + +inline bool SolverCeres::hasLocalParametrizationDerived(const StateBlockPtr& st) const +{ + return state_blocks_local_param_.count(st) == 1; +}; + +inline int SolverCeres::numStateBlocksDerived() const +{ + return ceres_problem_->NumParameterBlocks(); +} + +inline int SolverCeres::numFactorsDerived() const +{ + return ceres_problem_->NumResidualBlocks(); +}; + } // namespace wolf #endif diff --git a/include/core/processor/processor_base.h b/include/core/processor/processor_base.h index acfa2231c8ba692497d3ce69c9dcccd11f2e4382..84595cc3f6fd7c72374a30cb324d92ad2efbfd10 100644 --- a/include/core/processor/processor_base.h +++ b/include/core/processor/processor_base.h @@ -18,6 +18,7 @@ class SensorBase; // std #include <memory> #include <map> +#include <chrono> namespace wolf { @@ -274,6 +275,18 @@ class ProcessorBase : public NodeBase, public std::enable_shared_from_this<Proce unsigned int id() const; + // PROFILING + unsigned int n_capture_callback_; + unsigned int n_kf_callback_; + std::chrono::microseconds duration_capture_; + std::chrono::microseconds duration_kf_; + std::chrono::time_point<std::chrono::high_resolution_clock> start_capture_; + std::chrono::time_point<std::chrono::high_resolution_clock> start_kf_; + void startCaptureProfiling(); + void stopCaptureProfiling(); + void startKFProfiling(); + void stopKFProfiling(); + protected: /** \brief process an incoming capture * @@ -393,6 +406,26 @@ class ProcessorBase : public NodeBase, public std::enable_shared_from_this<Proce bool check(CheckLog& _log, std::shared_ptr<NodeBase> _node_ptr, bool _verbose, std::ostream& _stream, std::string _tabs = "") const; }; +inline void ProcessorBase::startCaptureProfiling() +{ + start_capture_ = std::chrono::high_resolution_clock::now(); +} + +inline void ProcessorBase::stopCaptureProfiling() +{ + duration_capture_ += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start_capture_); +} + +inline void ProcessorBase::startKFProfiling() +{ + start_kf_ = std::chrono::high_resolution_clock::now(); +} + +inline void ProcessorBase::stopKFProfiling() +{ + duration_kf_ += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start_kf_); +} + inline bool ProcessorBase::isVotingActive() const { return params_->voting_active; diff --git a/include/core/processor/processor_motion.h b/include/core/processor/processor_motion.h index 1f62a82a39dfcf5ae2de6183d341051d5785b45c..6a0f15a90885f5438c18c2cf01ed714c488ba599 100644 --- a/include/core/processor/processor_motion.h +++ b/include/core/processor/processor_motion.h @@ -247,11 +247,11 @@ class ProcessorMotion : public ProcessorBase, public IsMotion double updateDt(); void integrateOneStep(); - void reintegrateBuffer(CaptureMotionPtr _capture_ptr); + void reintegrateBuffer(CaptureMotionPtr _capture_ptr) const; void splitBuffer(const wolf::CaptureMotionPtr& capture_source, TimeStamp ts_split, const FrameBasePtr& keyframe_target, - const wolf::CaptureMotionPtr& capture_target); + const wolf::CaptureMotionPtr& capture_target) const; /** Pre-process incoming Capture * @@ -416,14 +416,26 @@ class ProcessorMotion : public ProcessorBase, public IsMotion virtual Eigen::VectorXd correctDelta(const Eigen::VectorXd& delta_preint, const Eigen::VectorXd& delta_step) const = 0; + /** \brief merge two consecutive capture motions into the second one + * Merge two consecutive capture motions into the second one. + * The first capture motion is not removed. + * If the second capture has feature and factor emplaced, they are replaced by a new ones. + * @param cap_prev : the first capture motion to be merged (input) + * @param cap_target : the second capture motion (modified) + */ + void mergeCaptures(CaptureMotionConstPtr cap_prev, + CaptureMotionPtr cap_target); + protected: /** \brief emplace a CaptureMotion - * \param _ts time stamp + * \param _frame_own frame owning the Capture to create * \param _sensor Sensor that produced the Capture + * \param _ts time stamp * \param _data a vector of motion data - * \param _sata_cov covariances matrix of the motion data data - * \param _frame_own frame owning the Capture to create - * \param _frame_origin frame acting as the origin of motions for the MorionBuffer of the created MotionCapture + * \param _data_cov covariances matrix of the motion data data + * \param _calib calibration vector + * \param _calib_preint calibration vector used during pre-integration + * \param _capture_origin_ptr capture acting as the origin of motions for the MorionBuffer of the created MotionCapture */ virtual CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own, const SensorBasePtr& _sensor, @@ -491,18 +503,19 @@ class ProcessorMotion : public ProcessorBase, public IsMotion protected: // helpers to avoid allocation - double dt_; ///< Time step - Eigen::VectorXd x_; ///< current state - Eigen::VectorXd delta_; ///< current delta - Eigen::MatrixXd delta_cov_; ///< current delta covariance - Eigen::VectorXd delta_integrated_; ///< integrated delta - Eigen::MatrixXd delta_integrated_cov_; ///< integrated delta covariance - Eigen::VectorXd calib_preint_; ///< calibration vector used during pre-integration - Eigen::MatrixXd jacobian_delta_preint_; ///< jacobian of delta composition w.r.t previous delta integrated - Eigen::MatrixXd jacobian_delta_; ///< jacobian of delta composition w.r.t current delta - Eigen::MatrixXd jacobian_calib_; ///< jacobian of delta preintegration wrt calibration params - Eigen::MatrixXd jacobian_delta_calib_; ///< jacobian of delta wrt calib params - Eigen::MatrixXd unmeasured_perturbation_cov_; ///< Covariance of unmeasured DoF to avoid singularity + mutable double dt_; ///< Time step + mutable Eigen::VectorXd x_; ///< current state + mutable Eigen::VectorXd delta_; ///< current delta + mutable Eigen::MatrixXd delta_cov_; ///< current delta covariance + mutable Eigen::VectorXd delta_integrated_; ///< integrated delta + mutable Eigen::MatrixXd delta_integrated_cov_; ///< integrated delta covariance + mutable Eigen::VectorXd calib_preint_; ///< calibration vector used during pre-integration + mutable Eigen::MatrixXd jacobian_delta_preint_; ///< jacobian of delta composition w.r.t previous delta integrated + mutable Eigen::MatrixXd jacobian_delta_; ///< jacobian of delta composition w.r.t current delta + mutable Eigen::MatrixXd jacobian_calib_; ///< jacobian of delta preintegration wrt calibration params + mutable Eigen::MatrixXd jacobian_delta_calib_; ///< jacobian of delta wrt calib params + + Eigen::MatrixXd unmeasured_perturbation_cov_; ///< Covariance of unmeasured DoF to avoid singularity }; } diff --git a/include/core/solver/solver_manager.h b/include/core/solver/solver_manager.h index 17341101c0d2ee4ee7074d220fda006e42fd0b31..c2dce560c5a701f2d5131c34c31fa489da96a4f2 100644 --- a/include/core/solver/solver_manager.h +++ b/include/core/solver/solver_manager.h @@ -56,7 +56,8 @@ class SolverManager { ALL, ///< All blocks and all cross-covariances ALL_MARGINALS, ///< All marginals - ROBOT_LANDMARKS ///< marginals of landmarks and current robot pose plus cross covariances of current robot and all landmarks + ROBOT_LANDMARKS, ///< marginals of landmarks and current robot pose plus cross covariances of current robot and all landmarks + GAUSS }; /** @@ -69,6 +70,11 @@ class SolverManager FULL }; + // PROFILING + unsigned int n_solve_; + std::chrono::microseconds duration_manager_; + std::chrono::microseconds duration_solver_; + protected: ProblemPtr wolf_problem_; @@ -96,9 +102,9 @@ class SolverManager */ std::string solve(const ReportVerbosity report_level); - virtual void computeCovariances(const CovarianceBlocksToBeComputed blocks) = 0; + virtual bool computeCovariances(const CovarianceBlocksToBeComputed blocks) = 0; - virtual void computeCovariances(const std::vector<StateBlockPtr>& st_list) = 0; + virtual bool computeCovariances(const std::vector<StateBlockPtr>& st_list) = 0; virtual bool hasConverged() = 0; @@ -119,31 +125,47 @@ class SolverManager ReportVerbosity getVerbosity() const; - virtual bool isStateBlockRegistered(const StateBlockPtr& state_ptr); + virtual bool isStateBlockRegistered(const StateBlockPtr& state_ptr) const final; + + virtual bool isStateBlockFloating(const StateBlockPtr& state_ptr) const final; + + virtual bool isFactorRegistered(const FactorBasePtr& fac_ptr) const final; + virtual bool isStateBlockFixed(const StateBlockPtr& st) final; + + virtual bool hasThisLocalParametrization(const StateBlockPtr& st, + const LocalParametrizationBasePtr& local_param) final; + + virtual bool hasLocalParametrization(const StateBlockPtr& st) const final; + + virtual int numFactors() const final; + virtual int numStateBlocks() const final; + virtual int numStateBlocksFloating() const final; - virtual bool isFactorRegistered(const FactorBasePtr& fac_ptr) const; + virtual int numFactorsDerived() const = 0; + virtual int numStateBlocksDerived() const = 0; - bool check(std::string prefix="") const; + virtual bool check(std::string prefix="") const final; protected: std::map<StateBlockPtr, Eigen::VectorXd> state_blocks_; std::map<StateBlockPtr, FactorBasePtrList> state_blocks_2_factors_; std::set<FactorBasePtr> factors_; + std::set<StateBlockPtr> floating_state_blocks_; virtual Eigen::VectorXd& getAssociatedMemBlock(const StateBlockPtr& state_ptr); const double* getAssociatedMemBlockPtr(const StateBlockPtr& state_ptr) const; - virtual double* getAssociatedMemBlockPtr(const StateBlockPtr& state_ptr); + double* getAssociatedMemBlockPtr(const StateBlockPtr& state_ptr); private: // SolverManager functions - void addFactor(const FactorBasePtr& fac_ptr); - void removeFactor(const FactorBasePtr& fac_ptr); - void addStateBlock(const StateBlockPtr& state_ptr); - void removeStateBlock(const StateBlockPtr& state_ptr); - void updateStateBlockState(const StateBlockPtr& state_ptr); - void updateStateBlockStatus(const StateBlockPtr& state_ptr); - void updateStateBlockLocalParametrization(const StateBlockPtr& state_ptr); + virtual void addFactor(const FactorBasePtr& fac_ptr) final; + virtual void removeFactor(const FactorBasePtr& fac_ptr) final; + virtual void addStateBlock(const StateBlockPtr& state_ptr) final; + virtual void removeStateBlock(const StateBlockPtr& state_ptr) final; + virtual void updateStateBlockState(const StateBlockPtr& state_ptr) final; + virtual void updateStateBlockStatus(const StateBlockPtr& state_ptr) final; + virtual void updateStateBlockLocalParametrization(const StateBlockPtr& state_ptr) final; protected: // Derived virtual functions @@ -154,9 +176,13 @@ class SolverManager virtual void removeStateBlockDerived(const StateBlockPtr& state_ptr) = 0; virtual void updateStateBlockStatusDerived(const StateBlockPtr& state_ptr) = 0; virtual void updateStateBlockLocalParametrizationDerived(const StateBlockPtr& state_ptr) = 0; - virtual bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) = 0; + virtual bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) const = 0; virtual bool isFactorRegisteredDerived(const FactorBasePtr& fac_ptr) const = 0; virtual bool checkDerived(std::string prefix="") const = 0; + virtual bool isStateBlockFixedDerived(const StateBlockPtr& st) = 0; + virtual bool hasThisLocalParametrizationDerived(const StateBlockPtr& st, + const LocalParametrizationBasePtr& local_param) = 0; + virtual bool hasLocalParametrizationDerived(const StateBlockPtr& st) const = 0; }; // Params (here becaure it needs of declaration of SolverManager::ReportVerbosity) diff --git a/include/core/state_block/local_parametrization_angle.h b/include/core/state_block/local_parametrization_angle.h index 2236c1d62e94c6223f13529d2be55d9fd60baf00..460835cb9bf03c92cfb73f8516248816212c6571 100644 --- a/include/core/state_block/local_parametrization_angle.h +++ b/include/core/state_block/local_parametrization_angle.h @@ -27,7 +27,7 @@ class LocalParametrizationAngle : public LocalParametrizationBase bool minus(Eigen::Map<const Eigen::VectorXd>& _x1, Eigen::Map<const Eigen::VectorXd>& _x2, Eigen::Map<Eigen::VectorXd>& _x2_minus_x1) override; - + bool isValid(const Eigen::VectorXd& state, double tolerance) override; }; inline LocalParametrizationAngle::LocalParametrizationAngle() : @@ -64,6 +64,13 @@ inline bool LocalParametrizationAngle::minus(Eigen::Map<const Eigen::VectorXd>& return true; } +inline bool LocalParametrizationAngle::isValid(const Eigen::VectorXd& _state, double tolerance) +{ + //Any real is a valid angle because we use the pi2pi function. Also + //the types don't match. In this case the argument is + //Eigen::Map not Eigen::VectorXd + return true; +} } /* namespace wolf */ #endif /* LOCAL_PARAMETRIZATION_ANGLE_H_ */ diff --git a/include/core/state_block/local_parametrization_base.h b/include/core/state_block/local_parametrization_base.h index 14f2ce38f1dd55b6571884710eb7ed7b7640f985..8494ec3a89215af8287584c8255b241af3486b45 100644 --- a/include/core/state_block/local_parametrization_base.h +++ b/include/core/state_block/local_parametrization_base.h @@ -31,7 +31,7 @@ class LocalParametrizationBase{ virtual bool minus(Eigen::Map<const Eigen::VectorXd>& _x1, Eigen::Map<const Eigen::VectorXd>& _x2, Eigen::Map<Eigen::VectorXd>& _x2_minus_x1) = 0; - + virtual bool isValid(const Eigen::VectorXd& state, double tolerance) = 0; unsigned int getLocalSize() const; unsigned int getGlobalSize() const; }; diff --git a/include/core/state_block/local_parametrization_homogeneous.h b/include/core/state_block/local_parametrization_homogeneous.h index 30785bb2cae80e94117d6895657b96617b6adefb..b8bbb981c6cec93c9e4fbfa29049a12f16d137a9 100644 --- a/include/core/state_block/local_parametrization_homogeneous.h +++ b/include/core/state_block/local_parametrization_homogeneous.h @@ -47,6 +47,7 @@ class LocalParametrizationHomogeneous : public LocalParametrizationBase bool minus(Eigen::Map<const Eigen::VectorXd>& _h1, Eigen::Map<const Eigen::VectorXd>& _h2, Eigen::Map<Eigen::VectorXd>& _h2_minus_h1) override; + bool isValid(const Eigen::VectorXd& state, double tolerance) override; }; } // namespace wolf diff --git a/include/core/state_block/local_parametrization_quaternion.h b/include/core/state_block/local_parametrization_quaternion.h index 75804f85dabc9d9480ef045944f195fe1a472bb9..c637ab6bb07a23b0e5dd5528d6a1c240327e1473 100644 --- a/include/core/state_block/local_parametrization_quaternion.h +++ b/include/core/state_block/local_parametrization_quaternion.h @@ -73,6 +73,12 @@ public: bool minus(Eigen::Map<const Eigen::VectorXd>& _q1, Eigen::Map<const Eigen::VectorXd>& _q2, Eigen::Map<Eigen::VectorXd>& _q2_minus_q1) override; + // inline bool isValid(const Eigen::VectorXd& state) override; + // template<QuaternionDeltaReference DeltaReference> + bool isValid(const Eigen::VectorXd& _state, double tolerance) override + { + return _state.size() == global_size_ && fabs(1.0 - _state.norm()) < tolerance; + } }; typedef LocalParametrizationQuaternion<DQ_GLOBAL> LocalParametrizationQuaternionGlobal; diff --git a/include/core/state_block/state_block.h b/include/core/state_block/state_block.h index c8706e2b6d081802d4a3b2590538b2e45df0584b..5406fe0c105c67d87e4793ba86a2e52f81bbfa87 100644 --- a/include/core/state_block/state_block.h +++ b/include/core/state_block/state_block.h @@ -159,6 +159,8 @@ public: void plus(const Eigen::VectorXd& _dv); + bool isValid(double tolerance = Constants::EPS); + static StateBlockPtr create (const Eigen::VectorXd& _state, bool _fixed = false); }; @@ -319,7 +321,10 @@ inline StateBlockPtr StateBlock::create (const Eigen::VectorXd& _state, bool _fi { return std::make_shared<StateBlock>(_state, _fixed); } - +inline bool StateBlock::isValid(double tolerance) +{ + return local_param_ptr_ ? local_param_ptr_->isValid(state_, tolerance) : true; +} }// namespace wolf #endif diff --git a/include/core/tree_manager/tree_manager_sliding_window.h b/include/core/tree_manager/tree_manager_sliding_window.h index 0ff5f31ed2734b7840518bf18dfc32073dac4cec..ce44fe003a8dfa58bb9a938e804af3e26fe576df 100644 --- a/include/core/tree_manager/tree_manager_sliding_window.h +++ b/include/core/tree_manager/tree_manager_sliding_window.h @@ -1,7 +1,7 @@ #ifndef INCLUDE_TREE_MANAGER_SLIDING_WINDOW_H_ #define INCLUDE_TREE_MANAGER_SLIDING_WINDOW_H_ -#include "../tree_manager/tree_manager_base.h" +#include "core/tree_manager/tree_manager_base.h" namespace wolf { @@ -15,20 +15,20 @@ struct ParamsTreeManagerSlidingWindow : public ParamsTreeManagerBase ParamsTreeManagerSlidingWindow(std::string _unique_name, const wolf::ParamsServer & _server) : ParamsTreeManagerBase(_unique_name, _server) { - n_key_frames = _server.getParam<unsigned int>(prefix + "/n_key_frames"); - fix_first_key_frame = _server.getParam<bool> (prefix + "/fix_first_key_frame"); + n_frames = _server.getParam<unsigned int>(prefix + "/n_frames"); + fix_first_frame = _server.getParam<bool> (prefix + "/fix_first_frame"); viral_remove_empty_parent = _server.getParam<bool> (prefix + "/viral_remove_empty_parent"); } std::string print() const { return "\n" + ParamsTreeManagerBase::print() + "\n" - + "n_key_frames: " + std::to_string(n_key_frames) + "\n" - + "fix_first_key_frame: " + std::to_string(fix_first_key_frame) + "\n" + + "n_frames: " + std::to_string(n_frames) + "\n" + + "fix_first_frame: " + std::to_string(fix_first_frame) + "\n" + "viral_remove_empty_parent: " + std::to_string(viral_remove_empty_parent) + "\n"; } - unsigned int n_key_frames; - bool fix_first_key_frame; + unsigned int n_frames; + bool fix_first_frame; bool viral_remove_empty_parent; }; @@ -43,7 +43,7 @@ class TreeManagerSlidingWindow : public TreeManagerBase ~TreeManagerSlidingWindow() override{} - void keyFrameCallback(FrameBasePtr _key_frame) override; + void keyFrameCallback(FrameBasePtr _frame) override; protected: ParamsTreeManagerSlidingWindowPtr params_sw_; diff --git a/include/core/tree_manager/tree_manager_sliding_window_dual_rate.h b/include/core/tree_manager/tree_manager_sliding_window_dual_rate.h new file mode 100644 index 0000000000000000000000000000000000000000..e9fcbd1332fd2d4eca3f7afa04c2aee167ad1980 --- /dev/null +++ b/include/core/tree_manager/tree_manager_sliding_window_dual_rate.h @@ -0,0 +1,51 @@ +#ifndef INCLUDE_TREE_MANAGER_SLIDING_WINDOW_DUAL_RATE_H_ +#define INCLUDE_TREE_MANAGER_SLIDING_WINDOW_DUAL_RATE_H_ + +#include "core/tree_manager/tree_manager_sliding_window.h" + +namespace wolf +{ + +WOLF_STRUCT_PTR_TYPEDEFS(ParamsTreeManagerSlidingWindowDualRate) +WOLF_PTR_TYPEDEFS(TreeManagerSlidingWindowDualRate) + +struct ParamsTreeManagerSlidingWindowDualRate : public ParamsTreeManagerSlidingWindow +{ + ParamsTreeManagerSlidingWindowDualRate() = default; + ParamsTreeManagerSlidingWindowDualRate(std::string _unique_name, const wolf::ParamsServer & _server) : + ParamsTreeManagerSlidingWindow(_unique_name, _server) + { + n_frames_recent = _server.getParam<unsigned int>(prefix + "/n_frames_recent"); + assert(n_frames_recent <= n_frames); + rate_old_frames = _server.getParam<unsigned int>(prefix + "/rate_old_frames"); + } + std::string print() const + { + return "\n" + ParamsTreeManagerBase::print() + "\n" + + "n_frames_recent: " + std::to_string(n_frames_recent) + "\n" + + "rate_old_frames: " + std::to_string(rate_old_frames) + "\n"; + } + + unsigned int n_frames_recent, rate_old_frames; +}; + +class TreeManagerSlidingWindowDualRate : public TreeManagerSlidingWindow +{ + public: + TreeManagerSlidingWindowDualRate(ParamsTreeManagerSlidingWindowDualRatePtr _params); + ; + WOLF_TREE_MANAGER_CREATE(TreeManagerSlidingWindowDualRate, ParamsTreeManagerSlidingWindowDualRate) + + ~TreeManagerSlidingWindowDualRate() override{} + + void keyFrameCallback(FrameBasePtr _key_frame) override; + + protected: + ParamsTreeManagerSlidingWindowDualRatePtr params_swdr_; + unsigned int count_frames_; + //TrajectoryIter first_recent_frame_it_; +}; + +} /* namespace wolf */ + +#endif /* INCLUDE_TREE_MANAGER_SLIDING_WINDOW_DUAL_RATE_H_ */ diff --git a/src/capture/capture_base.cpp b/src/capture/capture_base.cpp index 94df0d2c9d77aba7e61ee7f70b87aafc927d6fa6..47990c97d0190f0780a475780fc6c7294501a102 100644 --- a/src/capture/capture_base.cpp +++ b/src/capture/capture_base.cpp @@ -56,7 +56,6 @@ CaptureBase::CaptureBase(const std::string& _type, CaptureBase::~CaptureBase() { - removeStateBlocks(getProblem()); } void CaptureBase::remove(bool viral_remove_empty_parent) @@ -224,6 +223,8 @@ void CaptureBase::setProblem(ProblemPtr _problem) if (_problem == nullptr || _problem == this->getProblem()) return; + assert(getFrame() and getFrame()->isKey()); + NodeBase::setProblem(_problem); registerNewStateBlocks(_problem); @@ -233,8 +234,6 @@ void CaptureBase::setProblem(ProblemPtr _problem) void CaptureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const { - _stream << _tabs << "Cap" << id() << " " << getType() << " ts = " << std::setprecision(3) << getTimeStamp(); - if(getSensor() != nullptr) { _stream << " -> Sen" << getSensor()->id(); @@ -247,7 +246,8 @@ void CaptureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _s { _stream << " <-- "; for (auto cby : getConstrainedByList()) - _stream << "Fac" << cby->id() << " \t"; + if (cby) + _stream << "Fac" << cby->id() << " \t"; } _stream << std::endl; @@ -257,7 +257,8 @@ void CaptureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _s for (const auto& key : getStructure()) { auto sb = getStateBlock(key); - _stream << _tabs << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " )" << std::endl; + if (sb) + _stream << _tabs << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " ) @ " << sb << std::endl; } } else if (_metric) @@ -272,7 +273,8 @@ void CaptureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _s for (const auto& key : getStructure()) { const auto& sb = getStateBlock(key); - _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "]; "; + if (sb) + _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] @ " << sb; } _stream << std::endl; } @@ -284,7 +286,8 @@ void CaptureBase::print(int _depth, bool _constr_by, bool _metric, bool _state_b printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 3) for (auto f : getFeatureList()) - f->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (f) + f->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog CaptureBase::localCheck(bool _verbose, CaptureBasePtr _cap_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/capture/capture_motion.cpp b/src/capture/capture_motion.cpp index da17a57af80971b21f4a69322ba0c2ade54225d4..bad56dae309cca61a119594e3b396444409e267b 100644 --- a/src/capture/capture_motion.cpp +++ b/src/capture/capture_motion.cpp @@ -108,7 +108,7 @@ void CaptureMotion::printHeader(int _depth, bool _constr_by, bool _metric, bool _stream << (sb->isFixed() ? "Fix" : "Est"); if (_metric) _stream << std::setprecision(2) << " (" << sb->getState().transpose() << " )"; - _stream << std::endl; + _stream << " @ " << sb << std::endl; } } diff --git a/src/ceres_wrapper/solver_ceres.cpp b/src/ceres_wrapper/solver_ceres.cpp index 033b0f9100b2befc728157cb0fb8d282d9808dd0..e0995c7be03642c74d9e75b4ce2f266a165d81f3 100644 --- a/src/ceres_wrapper/solver_ceres.cpp +++ b/src/ceres_wrapper/solver_ceres.cpp @@ -25,7 +25,9 @@ SolverCeres::SolverCeres(const ProblemPtr& _wolf_problem, ceres_problem_ = wolf::make_unique<ceres::Problem>(params_ceres_->problem_options); if (params_ceres_->update_immediately) - getSolverOptions().callbacks.push_back(new IterationUpdateCallback(wolf_problem_, params_ceres_->verbose != SolverManager::ReportVerbosity::QUIET)); + getSolverOptions().callbacks.push_back(new IterationUpdateCallback(wolf_problem_, + params_ceres_->min_num_iterations, + params_ceres_->verbose != SolverManager::ReportVerbosity::QUIET)); } SolverCeres::~SolverCeres() @@ -51,7 +53,7 @@ std::string SolverCeres::solveDerived(const ReportVerbosity report_level) // run Ceres Solver ceres::Solve(getSolverOptions(), ceres_problem_.get(), &summary_); - // if termination by user (Iteration callback, update and solve again) + /*/ if termination by user (Iteration callback, update and solve again) // solve until max iterations reached if (params_ceres_->update_immediately) { @@ -68,7 +70,7 @@ std::string SolverCeres::solveDerived(const ReportVerbosity report_level) ceres::Solve(getSolverOptions(), ceres_problem_.get(), &summary_); } getSolverOptions().max_num_iterations = max_num_iterations; - } + }*/ std::string report; //return report @@ -80,14 +82,11 @@ std::string SolverCeres::solveDerived(const ReportVerbosity report_level) return report; } -void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) +bool SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) { // update problem update(); - // CLEAR STORED COVARIANCE BLOCKS IN WOLF PROBLEM - wolf_problem_->clearCovariance(); - // CREATE DESIRED COVARIANCES LIST std::vector<std::pair<StateBlockPtr, StateBlockPtr>> state_block_pairs; std::vector<std::pair<const double*, const double*>> double_pairs; @@ -115,12 +114,17 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) } // double loop all against all (without repetitions) for (unsigned int i = 0; i < all_state_blocks.size(); i++) + { + assert(isStateBlockRegisteredDerived(all_state_blocks[i])); for (unsigned int j = i; j < all_state_blocks.size(); j++) { + assert(isStateBlockRegisteredDerived(all_state_blocks[i])); + state_block_pairs.emplace_back(all_state_blocks[i],all_state_blocks[j]); double_pairs.emplace_back(getAssociatedMemBlockPtr(all_state_blocks[i]), getAssociatedMemBlockPtr(all_state_blocks[j])); } + } break; } case CovarianceBlocksToBeComputed::ALL_MARGINALS: @@ -129,26 +133,36 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) for(auto fr_ptr : *wolf_problem_->getTrajectory()) if (fr_ptr->isKey()) for (const auto& key1 : wolf_problem_->getFrameStructure()) + { + const auto& sb1 = fr_ptr->getStateBlock(key1); + assert(isStateBlockRegisteredDerived(sb1)); + for (const auto& key2 : wolf_problem_->getFrameStructure()) { - const auto& sb1 = fr_ptr->getStateBlock(key1); const auto& sb2 = fr_ptr->getStateBlock(key2); + assert(isStateBlockRegisteredDerived(sb2)); + state_block_pairs.emplace_back(sb1, sb2); double_pairs.emplace_back(getAssociatedMemBlockPtr(sb1), getAssociatedMemBlockPtr(sb2)); if (sb1 == sb2) break; } + } // landmark state blocks for(auto l_ptr : wolf_problem_->getMap()->getLandmarkList()) for (auto sb : l_ptr->getUsedStateBlockVec()) + { + assert(isStateBlockRegisteredDerived(sb)); for(auto sb2 : l_ptr->getUsedStateBlockVec()) { + assert(isStateBlockRegisteredDerived(sb2)); state_block_pairs.emplace_back(sb, sb2); double_pairs.emplace_back(getAssociatedMemBlockPtr(sb), getAssociatedMemBlockPtr(sb2)); if (sb == sb2) break; } + } // // loop all marginals (PO marginals) // for (unsigned int i = 0; 2*i+1 < all_state_blocks.size(); i++) // { @@ -167,6 +181,9 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) //robot-robot auto last_key_frame = wolf_problem_->getLastFrame(); + assert(isStateBlockRegisteredDerived(last_key_frame->getP())); + assert(isStateBlockRegisteredDerived(last_key_frame->getO())); + state_block_pairs.emplace_back(last_key_frame->getP(), last_key_frame->getP()); state_block_pairs.emplace_back(last_key_frame->getP(), last_key_frame->getO()); state_block_pairs.emplace_back(last_key_frame->getO(), last_key_frame->getO()); @@ -187,6 +204,8 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) for (auto state_it = landmark_state_blocks.begin(); state_it != landmark_state_blocks.end(); state_it++) { + assert(isStateBlockRegisteredDerived(*state_it)); + // robot - landmark state_block_pairs.emplace_back(last_key_frame->getP(), *state_it); state_block_pairs.emplace_back(last_key_frame->getO(), *state_it); @@ -198,20 +217,65 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) // landmark marginal for (auto next_state_it = state_it; next_state_it != landmark_state_blocks.end(); next_state_it++) { - state_block_pairs.emplace_back(*state_it, *next_state_it); - double_pairs.emplace_back(getAssociatedMemBlockPtr((*state_it)), + assert(isStateBlockRegisteredDerived(*next_state_it)); + + state_block_pairs.emplace_back(*state_it, *next_state_it); + double_pairs.emplace_back(getAssociatedMemBlockPtr((*state_it)), getAssociatedMemBlockPtr((*next_state_it))); } } } break; } + case CovarianceBlocksToBeComputed::GAUSS: + { + // State blocks: + // - Last KF: PV + + // last KF + FrameBasePtr last_frame = wolf_problem_->getLastFrame(); + if (not last_frame) + return false; + + // Error + if (not last_frame->hasStateBlock('P') or + not isStateBlockRegisteredDerived(last_frame->getStateBlock('P')) or + not last_frame->hasStateBlock('V') or + not isStateBlockRegisteredDerived(last_frame->getStateBlock('V'))) + { + WOLF_WARN("SolverCeres::computeCovariances: last KF have null or unregistered state blocks P or V, returning..."); + WOLF_WARN_COND(not last_frame->getStateBlock('P'), "SolverCeres::computeCovariances: KF state block 'P' not found"); + WOLF_WARN_COND(last_frame->getStateBlock('P') and not isStateBlockRegisteredDerived(last_frame->getStateBlock('P')), "SolverCeres::computeCovariances: KF state block 'P' not registered ", last_frame->getStateBlock('P')); + WOLF_WARN_COND(not last_frame->getStateBlock('V'), "SolverCeres::computeCovariances: KF state block 'V' not found"); + WOLF_WARN_COND(last_frame->getStateBlock('V') and not isStateBlockRegisteredDerived(last_frame->getStateBlock('V')), "SolverCeres::computeCovariances: KF state block 'V' not registered ", last_frame->getStateBlock('V')); + + return false; + } + + // only marginals of P and V + state_block_pairs.emplace_back(last_frame->getStateBlock('P'), + last_frame->getStateBlock('P')); + double_pairs.emplace_back(getAssociatedMemBlockPtr(last_frame->getStateBlock('P')), + getAssociatedMemBlockPtr(last_frame->getStateBlock('P'))); + state_block_pairs.emplace_back(last_frame->getStateBlock('V'), + last_frame->getStateBlock('V')); + double_pairs.emplace_back(getAssociatedMemBlockPtr(last_frame->getStateBlock('V')), + getAssociatedMemBlockPtr(last_frame->getStateBlock('V'))); + + break; + } + default: + throw std::runtime_error("SolverCeres::computeCovariances: Unknown CovarianceBlocksToBeComputed enum value"); + } //std::cout << "pairs... " << double_pairs.size() << std::endl; // COMPUTE DESIRED COVARIANCES if (covariance_->Compute(double_pairs, ceres_problem_.get())) { + // CLEAR STORED COVARIANCE BLOCKS IN WOLF PROBLEM + wolf_problem_->clearCovariance(); + // STORE DESIRED COVARIANCES for (unsigned int i = 0; i < double_pairs.size(); i++) { @@ -220,34 +284,39 @@ void SolverCeres::computeCovariances(const CovarianceBlocksToBeComputed _blocks) wolf_problem_->addCovarianceBlock(state_block_pairs[i].first, state_block_pairs[i].second, cov); // std::cout << "covariance got switch: " << std::endl << cov << std::endl; } + return true; } - else - std::cout << "WARNING: Couldn't compute covariances!" << std::endl; + + WOLF_WARN("SolverCeres::computeCovariances: Couldn't compute covariances!"); + return false; } -void SolverCeres::computeCovariances(const std::vector<StateBlockPtr>& st_list) + +bool SolverCeres::computeCovariances(const std::vector<StateBlockPtr>& st_list) { //std::cout << "SolverCeres: computing covariances..." << std::endl; // update problem update(); - // CLEAR STORED COVARIANCE BLOCKS IN WOLF PROBLEM - wolf_problem_->clearCovariance(); - // CREATE DESIRED COVARIANCES LIST std::vector<std::pair<StateBlockPtr, StateBlockPtr>> state_block_pairs; std::vector<std::pair<const double*, const double*>> double_pairs; // double loop all against all (without repetitions) - for (auto st_it1 = st_list.begin(); st_it1 != st_list.end(); st_it1++){ + for (auto st_it1 = st_list.begin(); st_it1 != st_list.end(); st_it1++) + { if (*st_it1 == nullptr){ continue; } + assert(isStateBlockRegisteredDerived(*st_it1)); + for (auto st_it2 = st_it1; st_it2 != st_list.end(); st_it2++) { if (*st_it2 == nullptr){ continue; } + assert(isStateBlockRegisteredDerived(*st_it2)); + state_block_pairs.emplace_back(*st_it1, *st_it2); double_pairs.emplace_back(getAssociatedMemBlockPtr((*st_it1)), getAssociatedMemBlockPtr((*st_it2))); @@ -258,6 +327,10 @@ void SolverCeres::computeCovariances(const std::vector<StateBlockPtr>& st_list) // COMPUTE DESIRED COVARIANCES if (covariance_->Compute(double_pairs, ceres_problem_.get())) + { + // CLEAR STORED COVARIANCE BLOCKS IN WOLF PROBLEM + wolf_problem_->clearCovariance(); + // STORE DESIRED COVARIANCES for (unsigned int i = 0; i < double_pairs.size(); i++) { @@ -266,13 +339,16 @@ void SolverCeres::computeCovariances(const std::vector<StateBlockPtr>& st_list) wolf_problem_->addCovarianceBlock(state_block_pairs[i].first, state_block_pairs[i].second, cov); // std::cout << "covariance got from st_list: " << std::endl << cov << std::endl; } - else - std::cout << "WARNING: Couldn't compute covariances!" << std::endl; + return true; + } + + WOLF_WARN("SolverCeres::computeCovariances: Couldn't compute covariances!"); + return false; } void SolverCeres::addFactorDerived(const FactorBasePtr& fac_ptr) { - assert(fac_2_costfunction_.find(fac_ptr) == fac_2_costfunction_.end() && "adding a factor that is already in the fac_2_costfunction_ map"); + assert(!isFactorRegisteredDerived(fac_ptr) && "adding a factor that is already in the fac_2_costfunction_ map"); auto cost_func_ptr = createCostFunction(fac_ptr); fac_2_costfunction_[fac_ptr] = cost_func_ptr; @@ -281,6 +357,7 @@ void SolverCeres::addFactorDerived(const FactorBasePtr& fac_ptr) res_block_mem.reserve(fac_ptr->getStateBlockPtrVector().size()); for (const StateBlockPtr& st : fac_ptr->getStateBlockPtrVector()) { + assert(isStateBlockRegisteredDerived(st) && "adding a factor that involves a floating or not registered sb"); res_block_mem.emplace_back( getAssociatedMemBlockPtr(st) ); } @@ -312,7 +389,7 @@ void SolverCeres::removeFactorDerived(const FactorBasePtr& _fac_ptr) void SolverCeres::addStateBlockDerived(const StateBlockPtr& state_ptr) { assert(state_ptr); - assert(!ceres_problem_->HasParameterBlock(getAssociatedMemBlockPtr(state_ptr)) && "adding a state block already added"); + assert(!isStateBlockRegisteredDerived(state_ptr) && "adding a state block already added"); assert(state_blocks_local_param_.count(state_ptr) == 0 && "local param stored for this state block"); ceres::LocalParameterization* local_parametrization_ptr = nullptr; @@ -339,7 +416,8 @@ void SolverCeres::removeStateBlockDerived(const StateBlockPtr& state_ptr) { //std::cout << "SolverCeres::removeStateBlock " << state_ptr.get() << " - " << getAssociatedMemBlockPtr(state_ptr) << std::endl; assert(state_ptr); - assert(ceres_problem_->HasParameterBlock(getAssociatedMemBlockPtr(state_ptr)) && "removing a state block that is not in ceres"); + assert(isStateBlockRegisteredDerived(state_ptr) && "removing a state block that is not in ceres"); + ceres_problem_->RemoveParameterBlock(getAssociatedMemBlockPtr(state_ptr)); state_blocks_local_param_.erase(state_ptr); } @@ -347,6 +425,8 @@ void SolverCeres::removeStateBlockDerived(const StateBlockPtr& state_ptr) void SolverCeres::updateStateBlockStatusDerived(const StateBlockPtr& state_ptr) { assert(state_ptr != nullptr); + assert(isStateBlockRegisteredDerived(state_ptr) && "updating status of a state block that is not in ceres"); + if (state_ptr->isFixed()) ceres_problem_->SetParameterBlockConstant(getAssociatedMemBlockPtr(state_ptr)); else @@ -549,7 +629,7 @@ const Eigen::SparseMatrixd SolverCeres::computeHessian() const StateBlockPtr sb = fac_ptr->getStateBlockPtrVector()[i]; if (!sb->isFixed()) { - assert(state_blocks_local_param_.count(sb) != 0 && "factor involving a state block not added"); + assert(state_blocks_.count(sb) != 0 && "factor involving a state block not added"); assert((A.cols() >= sb->getLocalSize() + jacobians[i].cols() - 1) && "bad A number of cols"); // insert since A_block_row has just been created so it's empty for sure @@ -579,6 +659,14 @@ const Eigen::SparseMatrixd SolverCeres::computeHessian() const return H; } +bool SolverCeres::hasThisLocalParametrizationDerived(const StateBlockPtr& st, + const LocalParametrizationBasePtr& local_param) +{ + return state_blocks_local_param_.count(st) == 1 + && state_blocks_local_param_.at(st)->getLocalParametrization() == local_param + && ceres_problem_->GetParameterization(getAssociatedMemBlockPtr(st)) + == state_blocks_local_param_.at(st).get(); +} } // namespace wolf #include "core/solver/factory_solver.h" diff --git a/src/feature/feature_base.cpp b/src/feature/feature_base.cpp index 69133345e0f57c28e3135aa3b50e0d99e179be50..b864e27c8172671572026ce4f83b4d279bb1b669 100644 --- a/src/feature/feature_base.cpp +++ b/src/feature/feature_base.cpp @@ -182,7 +182,8 @@ void FeatureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _s { _stream << " <--\t"; for (auto cby : getConstrainedByList()) - _stream << "Fac" << cby->id() << " \t"; + if (cby) + _stream << "Fac" << cby->id() << " \t"; } _stream << std::endl; if (_metric) @@ -193,10 +194,12 @@ void FeatureBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _s void FeatureBase::print(int _depth, bool _constr_by, bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const { + _stream << _tabs << "id: " << id() << std::endl; printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 4) for (auto c : getFactorList()) - c->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (c) + c->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog FeatureBase::localCheck(bool _verbose, FeatureBasePtr _ftr_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/frame/frame_base.cpp b/src/frame/frame_base.cpp index f7c7ce6ea257a48f73c68003b8c1efbfeb4f6a6b..20ab9331867cd18963b5bc85b0f42fa1c01b4a2f 100644 --- a/src/frame/frame_base.cpp +++ b/src/frame/frame_base.cpp @@ -303,12 +303,13 @@ void FrameBase::link(TrajectoryBasePtr _trj_ptr) void FrameBase::setProblem(ProblemPtr _problem) { + assert(isKey() && "Trying to setProblem to a non keyframe"); + if (_problem == nullptr || _problem == this->getProblem()) return; NodeBase::setProblem(_problem); - if (this->isKey()) - registerNewStateBlocks(_problem); + registerNewStateBlocks(_problem); for (auto cap : capture_list_) cap->setProblem(_problem); @@ -324,7 +325,8 @@ void FrameBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _sta { _stream << " <-- "; for (auto cby : getConstrainedByList()) - _stream << "Fac" << cby->id() << " \t"; + if (cby) + _stream << "Fac" << cby->id() << " \t"; } _stream << std::endl; @@ -333,10 +335,12 @@ void FrameBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _sta for (const auto& key : getStructure()) { auto sb = getStateBlock(key); - _stream << _tabs << " " << key - << "[" << (sb->isFixed() ? "Fix" : "Est") - << "] = ( " << std::setprecision(3) << sb->getState().transpose() << " )" - << std::endl; + if (sb) + _stream << _tabs << " " << key + << "[" << (sb->isFixed() ? "Fix" : "Est") + << "] = ( " << std::setprecision(3) << sb->getState().transpose() << " )" + << " @ " << sb + << std::endl; } } else if (_metric) @@ -351,7 +355,8 @@ void FrameBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _sta for (const auto& key : getStructure()) { const auto& sb = getStateBlock(key); - _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "]; "; + if (sb) + _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] @ " << sb; } _stream << std::endl; } @@ -362,7 +367,8 @@ void FrameBase::print(int _depth, bool _constr_by, bool _metric, bool _state_blo printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 2) for (auto C : getCaptureList()) - C->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (C) + C->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog FrameBase::localCheck(bool _verbose, FrameBasePtr _frm_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/hardware/hardware_base.cpp b/src/hardware/hardware_base.cpp index 72f07b8e216e6ca830ec9021dbb7ca1144d09860..e6e5a15c3c216d2abfa9242a48bb97081e22d016 100644 --- a/src/hardware/hardware_base.cpp +++ b/src/hardware/hardware_base.cpp @@ -30,7 +30,8 @@ void HardwareBase::print(int _depth, bool _constr_by, bool _metric, bool _state_ printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 1) for (auto S : getSensorList()) - S->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (S) + S->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog HardwareBase::localCheck(bool _verbose, HardwareBasePtr _hwd_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/landmark/landmark_base.cpp b/src/landmark/landmark_base.cpp index af2d01b68e40c33b129f799d93fe3d133459671b..c8f855dfb512a8e1438f07c0c9582db8d2c0f578 100644 --- a/src/landmark/landmark_base.cpp +++ b/src/landmark/landmark_base.cpp @@ -165,7 +165,8 @@ void LandmarkBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _ { _stream << "\t<-- "; for (auto cby : getConstrainedByList()) - _stream << "Fac" << cby->id() << " \t"; + if (cby) + _stream << "Fac" << cby->id() << " \t"; } _stream << std::endl; @@ -173,7 +174,8 @@ void LandmarkBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _ for (const auto& key : getStructure()) { auto sb = getStateBlock(key); - _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " )" << std::endl; + if (sb) + _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " ) @ " << sb << std::endl; } } else if (_metric) @@ -188,7 +190,8 @@ void LandmarkBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _ for (const auto& key : getStructure()) { const auto& sb = getStateBlock(key); - _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "]; "; + if (sb) + _stream << " " << key << "[" << (sb->isFixed() ? "Fix" : "Est") << "] @ " << sb; } _stream << std::endl; } diff --git a/src/map/map_base.cpp b/src/map/map_base.cpp index 67c37971b4e0517017f03b9cd798080fcd40808d..7f6db136f0d1644bb4d2ff193fc3c7d776fdfa26 100644 --- a/src/map/map_base.cpp +++ b/src/map/map_base.cpp @@ -99,7 +99,8 @@ void MapBase::print(int _depth, bool _constr_by, bool _metric, bool _state_block printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 1) for (auto L : getLandmarkList()) - L->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (L) + L->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog MapBase::localCheck(bool _verbose, MapBasePtr _map_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/problem/problem.cpp b/src/problem/problem.cpp index b151a6929ed93ecb4c231ffcd409df89ee3c36d1..bfcb33fa67482f2907ce1cdc715e4dacc44d60ed 100644 --- a/src/problem/problem.cpp +++ b/src/problem/problem.cpp @@ -605,6 +605,10 @@ void Problem::keyFrameCallback(FrameBasePtr _keyframe_ptr, ProcessorBasePtr _pro WOLF_DEBUG_COND(_processor_ptr!=nullptr,(_processor_ptr->isMotion() ? "PM " : "PT "), _processor_ptr->getName(), ": KF", _keyframe_ptr->id(), " Callback emitted with ts = ", _keyframe_ptr->getTimeStamp()); WOLF_DEBUG_COND(_processor_ptr==nullptr,"External callback: KF", _keyframe_ptr->id(), " Callback emitted with ts = ", _keyframe_ptr->getTimeStamp()); + // pause processor profiling + if (_processor_ptr) + _processor_ptr->stopCaptureProfiling(); + for (auto sensor : hardware_ptr_->getSensorList()) for (auto processor : sensor->getProcessorList()) if (processor && (processor != _processor_ptr) ) @@ -629,6 +633,10 @@ void Problem::keyFrameCallback(FrameBasePtr _keyframe_ptr, ProcessorBasePtr _pro // notify tree manager if (tree_manager_) tree_manager_->keyFrameCallback(_keyframe_ptr); + + // resume processor profiling + if (_processor_ptr) + _processor_ptr->startCaptureProfiling(); } bool Problem::permitAuxFrame(ProcessorBasePtr _processor_ptr) const diff --git a/src/processor/processor_base.cpp b/src/processor/processor_base.cpp index 5c769cde325442b711ff36c8a18f557a289ed6f6..a4ff214e113e41c67725bae938988ff3d10d857b 100644 --- a/src/processor/processor_base.cpp +++ b/src/processor/processor_base.cpp @@ -12,7 +12,11 @@ ProcessorBase::ProcessorBase(const std::string& _type, int _dim, ParamsProcessor processor_id_(++processor_id_count_), params_(_params), dim_(_dim), - sensor_ptr_() + sensor_ptr_(), + n_capture_callback_(0), + n_kf_callback_(0), + duration_capture_(0), + duration_kf_(0) { // WOLF_DEBUG("constructed +p" , id()); } @@ -37,6 +41,9 @@ void ProcessorBase::keyFrameCallback(FrameBasePtr _keyframe_ptr, const double& _ assert(_keyframe_ptr != nullptr && "keyFrameCallback with a nullptr frame"); WOLF_DEBUG("P", isMotion() ? "M " : "T ", getName(), ": KF", _keyframe_ptr->id(), " callback received with ts = ", _keyframe_ptr->getTimeStamp()); + // profiling + n_kf_callback_++; + startKFProfiling(); // asking if key frame should be stored if (storeKeyFrame(_keyframe_ptr)) @@ -46,6 +53,8 @@ void ProcessorBase::keyFrameCallback(FrameBasePtr _keyframe_ptr, const double& _ if (triggerInKeyFrame(_keyframe_ptr, _time_tol_other)) processKeyFrame(_keyframe_ptr, _time_tol_other); + // profiling + stopKFProfiling(); } void ProcessorBase::captureCallback(CaptureBasePtr _capture_ptr) @@ -53,6 +62,10 @@ void ProcessorBase::captureCallback(CaptureBasePtr _capture_ptr) assert(_capture_ptr != nullptr && "captureCallback with a nullptr capture"); WOLF_DEBUG("P", isMotion() ? "M " : "T ", getName(), ": Capture ", _capture_ptr->id(), " callback received with ts = ", _capture_ptr->getTimeStamp()); + // profiling + n_capture_callback_++; + startCaptureProfiling(); + // apply prior in problem if not done (very first capture) if (getProblem() && !getProblem()->isPriorSet()) getProblem()->applyPriorOptions(_capture_ptr->getTimeStamp()); @@ -64,6 +77,9 @@ void ProcessorBase::captureCallback(CaptureBasePtr _capture_ptr) // if trigger, process directly without buffering if (triggerInCapture(_capture_ptr)) processCapture(_capture_ptr); + + // profiling + stopCaptureProfiling(); } void ProcessorBase::remove() @@ -220,7 +236,6 @@ void BufferPackKeyFrame::print(void) const void ProcessorBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const { _stream << _tabs << "Prc" << id() << " " << getType() << " \"" << getName() << "\"" << std::endl; - } void ProcessorBase::print(int _depth, bool _metric, bool _state_blocks, bool _constr_by, std::ostream& _stream, std::string _tabs) const diff --git a/src/processor/processor_motion.cpp b/src/processor/processor_motion.cpp index f56b8c80a344e864f16be3ca2f8f006a0996052f..d18ae7c312f4e19a98ed1ae9522b2b06aad2fd41 100644 --- a/src/processor/processor_motion.cpp +++ b/src/processor/processor_motion.cpp @@ -54,10 +54,45 @@ ProcessorMotion::~ProcessorMotion() // std::cout << "destructed -p-Mot" << id() << std::endl; } -void ProcessorMotion::splitBuffer(const wolf::CaptureMotionPtr& _capture_source, + +void ProcessorMotion::mergeCaptures(CaptureMotionConstPtr cap_prev, + CaptureMotionPtr cap_target) +{ + assert(cap_prev == cap_target->getOriginCapture() && "merging not consecutive capture motions"); + + // add prev buffer (discarding the first zero motion) + cap_target->getBuffer().pop_front(); + cap_target->getBuffer().insert(cap_target->getBuffer().begin(), + cap_prev->getBuffer().begin(), + cap_prev->getBuffer().end()); + + // change origin + cap_target->setOriginCapture(cap_prev->getOriginCapture()); + + // change calibration params for preintegration from origin + cap_target->setCalibrationPreint(getCalibration(cap_prev->getOriginCapture())); + + // reintegrate buffer + reintegrateBuffer(cap_target); + + // replace existing feature and factor (if they exist) + if (!cap_target->getFeatureList().empty()) + { + // remove feature and factor (automatically) + cap_target->getFeatureList().back()->remove(); + + assert(cap_target->getFeatureList().empty());// there should be one feature only + + // emplace new feature and factor + auto new_feature = emplaceFeature(cap_target); + emplaceFactor(new_feature, cap_prev->getOriginCapture()); + } +} + +void ProcessorMotion::splitBuffer(const CaptureMotionPtr& _capture_source, TimeStamp _ts_split, const FrameBasePtr& _keyframe_target, - const wolf::CaptureMotionPtr& _capture_target) + const CaptureMotionPtr& _capture_target) const { /** we are doing this: * @@ -732,7 +767,7 @@ void ProcessorMotion::integrateOneStep() statePlusDelta(odometry_, delta_, dt_, odometry_); } -void ProcessorMotion::reintegrateBuffer(CaptureMotionPtr _capture_ptr) +void ProcessorMotion::reintegrateBuffer(CaptureMotionPtr _capture_ptr) const { VectorXd calib = _capture_ptr->getCalibrationPreint(); diff --git a/src/processor/processor_tracker.cpp b/src/processor/processor_tracker.cpp index 91867f39528b54cc7335fcca91e7fcc108757576..2e03aaafb52183adfd1843da41e0ca4ed3b49b3e 100644 --- a/src/processor/processor_tracker.cpp +++ b/src/processor/processor_tracker.cpp @@ -83,8 +83,8 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr) FrameBasePtr kfrm = FrameBase::emplaceKeyFrame<FrameBase>(getProblem()->getTrajectory(), incoming_ptr_->getTimeStamp(), - getStateStructure(), - getProblem()->getState(getStateStructure())); + getProblem()->getFrameStructure(), + getProblem()->getState()); incoming_ptr_->link(kfrm); // Process info diff --git a/src/processor/processor_tracker_feature.cpp b/src/processor/processor_tracker_feature.cpp index 3804962f62fcbfba13b56e7c64866be7497eeaa8..cda95e5ed6259e7ef11d94d01c3fe939ffe61941 100644 --- a/src/processor/processor_tracker_feature.cpp +++ b/src/processor/processor_tracker_feature.cpp @@ -91,7 +91,7 @@ unsigned int ProcessorTrackerFeature::processKnown() if (known_features_last_.empty()) { - WOLF_TRACE("Empty last feature list, returning..."); + WOLF_DEBUG("Empty last feature list, returning..."); return 0; } diff --git a/src/sensor/sensor_base.cpp b/src/sensor/sensor_base.cpp index fefc4d332eca061b041585bf42c6248fb985d992..82fbe684558bd9791763b70c0bbedab68514b8f0 100644 --- a/src/sensor/sensor_base.cpp +++ b/src/sensor/sensor_base.cpp @@ -198,7 +198,7 @@ void SensorBase::registerNewStateBlocks() const auto key = pair_key_sbp.first; auto sbp = pair_key_sbp.second; - if (sbp && !isStateBlockInCapture(key)) + if (sbp and not isStateBlockDynamic(key))//!isStateBlockInCapture(key)) getProblem()->notifyStateBlock(sbp, ADD); } } @@ -479,7 +479,8 @@ void SensorBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _st for (auto& key : getStructure()) { auto sb = getStateBlockDynamic(key); - _stream << key << "[" << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "," << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " ); "; + if (sb) + _stream << key << "[" << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "," << (sb->isFixed() ? "Fix" : "Est") << "] = ( " << sb->getState().transpose() << " ) @ " << sb << " "; } _stream << std::endl; } @@ -489,7 +490,8 @@ void SensorBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _st for (auto& key : getStructure()) { auto sb = getStateBlockDynamic(key); - _stream << sb->getState().transpose() << " "; + if (sb) + _stream << sb->getState().transpose() << " "; } _stream << ")" << std::endl; } @@ -499,7 +501,8 @@ void SensorBase::printHeader(int _depth, bool _constr_by, bool _metric, bool _st for (auto& key : getStructure()) { auto sb = getStateBlockDynamic(key); - _stream << key << "[" << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "," << (sb->isFixed() ? "Fix" : "Est") << "]; "; + if (sb) + _stream << key << "[" << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "," << (sb->isFixed() ? "Fix" : "Est") << "] @ " << sb << " "; } _stream << std::endl; } @@ -511,7 +514,8 @@ void SensorBase::print(int _depth, bool _constr_by, bool _metric, bool _state_bl if (_depth >= 2) for (auto p : getProcessorList()) - p->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); + if (p) + p->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog SensorBase::localCheck(bool _verbose, SensorBasePtr _sen_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/solver/solver_manager.cpp b/src/solver/solver_manager.cpp index 4a74ca8194f9b2b6111fb69d4c97bb7a4875b60e..8a9e15d6e81b88905ffdd3fb81b0635013ebf426 100644 --- a/src/solver/solver_manager.cpp +++ b/src/solver/solver_manager.cpp @@ -12,6 +12,9 @@ SolverManager::SolverManager(const ProblemPtr& _problem) : SolverManager::SolverManager(const ProblemPtr& _problem, const ParamsSolverPtr& _params) : + n_solve_(0), + duration_manager_(0), + duration_solver_(0), wolf_problem_(_problem), params_(_params) { @@ -75,10 +78,19 @@ void SolverManager::update() } // UPDATE STATE BLOCKS (state, fix or local parameterization) + std::set<StateBlockPtr> new_floating_state_blocks; for (auto state_pair : state_blocks_) { auto state_ptr = state_pair.first; + // Check for "floating" state blocks (not involved in any factor -> not observable problem) + if (state_blocks_2_factors_.at(state_ptr).empty()) + { + WOLF_DEBUG("SolverManager::update(): StateBlock ", state_ptr, " is 'Floating' (not involved in any factor). Storing it apart."); + new_floating_state_blocks.insert(state_ptr); + continue; + } + // state update if (state_ptr->stateUpdated()) updateStateBlockState(state_ptr); @@ -92,6 +104,20 @@ void SolverManager::update() updateStateBlockLocalParametrization(state_ptr); } + // REMOVE NEWLY DETECTED "floating" STATE BLOCKS (will be added next update() call) + for (auto state_ptr : new_floating_state_blocks) + { + removeStateBlock(state_ptr); + floating_state_blocks_.insert(state_ptr); // This line must be AFTER removeStateBlock()! + } + // RESET FLAGS for floating state blocks: meaning "solver handled this change" (state, fix and local param will be set in addStateBlock) + for (auto state_ptr : floating_state_blocks_) + { + state_ptr->resetStateUpdated(); + state_ptr->resetFixUpdated(); + state_ptr->resetLocalParamUpdated(); + } + #ifdef _WOLF_DEBUG assert(check("after update()")); #endif @@ -109,11 +135,16 @@ std::string SolverManager::solve() std::string SolverManager::solve(const ReportVerbosity report_level) { + auto start = std::chrono::high_resolution_clock::now(); + n_solve_++; + // update problem update(); // call derived solver + auto start_derived = std::chrono::high_resolution_clock::now(); std::string report = solveDerived(report_level); + duration_solver_ += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start_derived); // update StateBlocks with optimized state value. /// @todo whatif someone has changed the state notification during opti ?? @@ -128,6 +159,8 @@ std::string SolverManager::solve(const ReportVerbosity report_level) stateblock_statevector.first->setState(stateblock_statevector.second, false); // false = do not raise the flag state_updated_ } + duration_manager_ += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start); + return report; } @@ -136,7 +169,7 @@ void SolverManager::addFactor(const FactorBasePtr& fac_ptr) // Warning if adding an already added if (factors_.count(fac_ptr) != 0) { - WOLF_WARN("Tried to add a factor that was already added !"); + WOLF_WARN("Tried to add the factor ", fac_ptr->id(), " that was already added !"); return; } @@ -150,10 +183,20 @@ void SolverManager::addFactor(const FactorBasePtr& fac_ptr) for (const auto& st : fac_ptr->getStateBlockPtrVector()) if (state_blocks_.count(st) == 0) { - WOLF_WARN("SolverManager::addFactor(): Factor ", fac_ptr->id(), " is notified to ADD but the involved state block ", st, " is not. Skipping, will be added later."); - // put back notification in problem (will be added next update() call) and do nothing - wolf_problem_->notifyFactor(fac_ptr, ADD); - return; + // Check if it was stored as a 'floating' state block + if (floating_state_blocks_.count(st) == 1) + { + WOLF_DEBUG("SolverManager::addFactor(): Factor ", fac_ptr->id(), " involves state block ", st, " stored as 'floating'. Adding the state block to the solver."); + floating_state_blocks_.erase(st); // This line must be BEFORE addStateBlock()! + addStateBlock(st); + } + else + { + WOLF_WARN("SolverManager::addFactor(): Factor ", fac_ptr->id(), " is notified to ADD but the involved state block ", st, " is not. Skipping, will be added later."); + // put back notification in problem (will be added next update() call) and do nothing + wolf_problem_->notifyFactor(fac_ptr, ADD); + return; + } } // factors @@ -176,7 +219,7 @@ void SolverManager::removeFactor(const FactorBasePtr& fac_ptr) // Warning if removing a missing factor if (factors_.count(fac_ptr) == 0) { - WOLF_WARN("Tried to remove a factor that is missing !"); + WOLF_WARN("Tried to remove factor ", fac_ptr->id(), " that is missing !"); return; } @@ -200,12 +243,19 @@ void SolverManager::addStateBlock(const StateBlockPtr& state_ptr) // Warning if adding an already added state block if (state_blocks_.count(state_ptr) != 0) { - WOLF_WARN("Tried to add a StateBlock that was already added !"); + WOLF_WARN("Tried to add the StateBloc ", state_ptr, " that was already added !"); + return; + } + // Warning if adding a floating state block + if (floating_state_blocks_.count(state_ptr) != 0) + { + WOLF_WARN("Tried to add the StateBloc ", state_ptr, " that is stored as floating !"); return; } assert(state_blocks_.count(state_ptr) == 0 && "SolverManager::addStateBlock state block already added"); assert(state_blocks_2_factors_.count(state_ptr) == 0 && "SolverManager::addStateBlock state block already added"); + assert(state_ptr->isValid() && "SolverManager::addStateBlock state block state not valid (local parameterization)"); // stateblock maps state_blocks_.emplace(state_ptr, state_ptr->getState()); @@ -222,10 +272,16 @@ void SolverManager::addStateBlock(const StateBlockPtr& state_ptr) void SolverManager::removeStateBlock(const StateBlockPtr& state_ptr) { + // check if stored as 'floating' state block + if (floating_state_blocks_.count(state_ptr) == 1) + { + floating_state_blocks_.erase(state_ptr); + return; + } // Warning if removing a missing state block if (state_blocks_.count(state_ptr) == 0) { - WOLF_WARN("Tried to remove a StateBlock that was not added !"); + WOLF_WARN("Tried to remove the StateBlock ", state_ptr, " that was not added !"); return; } @@ -270,8 +326,12 @@ void SolverManager::updateStateBlockStatus(const StateBlockPtr& state_ptr) void SolverManager::updateStateBlockState(const StateBlockPtr& state_ptr) { + assert(state_ptr && "SolverManager::updateStateBlockState null state block"); + assert(state_blocks_.count(state_ptr) == 1 && "SolverManager::updateStateBlockState unregistered state block"); + assert(state_ptr->isValid() && "SolverManager::updateStateBlockState state block state not valid (local parameterization)"); + assert(state_ptr->getState().size() == getAssociatedMemBlock(state_ptr).size()); + Eigen::VectorXd new_state = state_ptr->getState(); - // We assume the same size for the states in both WOLF and the solver. std::copy(new_state.data(),new_state.data()+new_state.size(),getAssociatedMemBlockPtr(state_ptr)); // reset flag state_ptr->resetStateUpdated(); @@ -291,8 +351,10 @@ Eigen::VectorXd& SolverManager::getAssociatedMemBlock(const StateBlockPtr& state auto it = state_blocks_.find(state_ptr); if (it == state_blocks_.end()) + { + WOLF_ERROR("Tried to retrieve the memory block of an unregistered StateBlock: ", state_ptr); throw std::runtime_error("Tried to retrieve the memory block of an unregistered StateBlock !"); - + } return it->second; } @@ -301,8 +363,10 @@ const double* SolverManager::getAssociatedMemBlockPtr(const StateBlockPtr& state auto it = state_blocks_.find(state_ptr); if (it == state_blocks_.end()) - throw std::runtime_error("Tried to retrieve the memory block of an unregistered StateBlock !"); - + { + WOLF_ERROR("Tried to retrieve the memory block const ptr of an unregistered StateBlock: ", state_ptr); + throw std::runtime_error("Tried to retrieve the memory block const ptr of an unregistered StateBlock !"); + } return it->second.data(); } @@ -311,8 +375,10 @@ double* SolverManager::getAssociatedMemBlockPtr(const StateBlockPtr& state_ptr) auto it = state_blocks_.find(state_ptr); if (it == state_blocks_.end()) - throw std::runtime_error("Tried to retrieve the memory block of an unregistered StateBlock !"); - + { + WOLF_ERROR("Tried to retrieve the memory block ptr of an unregistered StateBlock: ", state_ptr); + throw std::runtime_error("Tried to retrieve the memory block ptr of an unregistered StateBlock !"); + } return it->second.data(); } @@ -321,14 +387,67 @@ SolverManager::ReportVerbosity SolverManager::getVerbosity() const return params_->verbose; } -bool SolverManager::isStateBlockRegistered(const StateBlockPtr& state_ptr) +bool SolverManager::isStateBlockRegistered(const StateBlockPtr& state_ptr) const { - return state_blocks_.count(state_ptr) ==1 && isStateBlockRegisteredDerived(state_ptr); + return isStateBlockFloating(state_ptr) or (state_blocks_.count(state_ptr) == 1 and isStateBlockRegisteredDerived(state_ptr)); +} + +bool SolverManager::isStateBlockFloating(const StateBlockPtr& state_ptr) const +{ + return floating_state_blocks_.count(state_ptr) == 1; } bool SolverManager::isFactorRegistered(const FactorBasePtr& fac_ptr) const { - return isFactorRegisteredDerived(fac_ptr); + return factors_.count(fac_ptr) == 1 and isFactorRegisteredDerived(fac_ptr); +} + +bool SolverManager::isStateBlockFixed(const StateBlockPtr& st) +{ + if (!isStateBlockRegistered(st)) + return false; + + if (isStateBlockFloating(st)) + return st->isFixed(); + + return isStateBlockFixedDerived(st); +} + +bool SolverManager::hasThisLocalParametrization(const StateBlockPtr& st, const LocalParametrizationBasePtr& local_param) +{ + if (!isStateBlockRegistered(st)) + return false; + + if (isStateBlockFloating(st)) + return st->getLocalParametrization() == local_param; + + return hasThisLocalParametrizationDerived(st, local_param); +}; + +bool SolverManager::hasLocalParametrization(const StateBlockPtr& st) const +{ + if (!isStateBlockRegistered(st)) + return false; + + if (isStateBlockFloating(st)) + return st->hasLocalParametrization(); + + return hasLocalParametrizationDerived(st); +}; + +int SolverManager::numFactors() const +{ + return factors_.size(); +} + +int SolverManager::numStateBlocks() const +{ + return state_blocks_.size() + floating_state_blocks_.size(); +} + +int SolverManager::numStateBlocksFloating() const +{ + return floating_state_blocks_.size(); } double SolverManager::getPeriod() const @@ -357,15 +476,32 @@ bool SolverManager::check(std::string prefix) const ok = false; } - // factor involving state block in factors_ - for (auto fac : sb_fac_it->second) + // no factors involving state block + if (sb_fac_it->second.empty()) + { + WOLF_ERROR("SolverManager::check: state block ", sb_fac_it->first, " is in state_blocks_ but not involved in any factor - in ", prefix); + ok = false; + } + else { - if (factors_.count(fac) == 0) + // factor involving state block in factors_ + for (auto fac : sb_fac_it->second) { - WOLF_ERROR("SolverManager::check: factor ", fac->id(), " (involved in sb ", sb_fac_it->first, ") missing in factors_ map - in ", prefix); - ok = false; + if (factors_.count(fac) == 0) + { + WOLF_ERROR("SolverManager::check: factor ", fac->id(), " (involved in sb ", sb_fac_it->first, ") missing in factors_ map - in ", prefix); + ok = false; + } } } + + // can't be in both state_blocks_ and floating_state_blocks_ + if (floating_state_blocks_.count(sb_fac_it->first) == 1) + { + WOLF_ERROR("SolverManager::check: state block ", sb_fac_it->first, " is both in state_blocks_ and floating_state_blocks_ - in ", prefix); + ok = false; + } + sb_vec_it++; sb_fac_it++; } @@ -384,9 +520,38 @@ bool SolverManager::check(std::string prefix) const } } - // checkDerived + // CHECK DERIVED ---------------------- ok &= checkDerived(prefix); + // CHECK IF DERIVED IS UP TO DATE ---------------------- + // state blocks registered in derived solver + for (auto sb : state_blocks_) + if (!isStateBlockRegisteredDerived(sb.first)) + { + WOLF_ERROR("SolverManager::check: state block ", sb.first, " is in state_blocks_ but not registered in derived solver - in ", prefix); + ok = false; + } + + // factors registered in derived solver + for (auto fac : factors_) + if (!isFactorRegisteredDerived(fac)) + { + WOLF_ERROR("SolverManager::check: factor ", fac->id(), " is in factors_ but not registered in derived solver - in ", prefix); + ok = false; + } + + // numbers + if (numStateBlocksDerived() != state_blocks_.size()) + { + WOLF_ERROR("SolverManager::check: numStateBlocksDerived() = ", numStateBlocksDerived(), " DIFFERENT THAN state_blocks_.size() = ", state_blocks_.size(), " - in ", prefix); + ok = false; + } + if (numFactorsDerived() != numFactors()) + { + WOLF_ERROR("SolverManager::check: numFactorsDerived() = ", numFactorsDerived(), " DIFFERENT THAN numFactors() = ", numFactors(), " - in ", prefix); + ok = false; + } + return ok; } diff --git a/src/state_block/local_parametrization_homogeneous.cpp b/src/state_block/local_parametrization_homogeneous.cpp index b2fa1c111e028445333236e01384144aeafc31a6..846b14a49934c70c0d3879efd03f454ffab74605 100644 --- a/src/state_block/local_parametrization_homogeneous.cpp +++ b/src/state_block/local_parametrization_homogeneous.cpp @@ -60,5 +60,9 @@ bool LocalParametrizationHomogeneous::minus(Eigen::Map<const Eigen::VectorXd>& _ return true; } +bool LocalParametrizationHomogeneous::isValid(const Eigen::VectorXd& _state, double tolerance) +{ + return _state.size() == global_size_; +} } // namespace wolf diff --git a/src/trajectory/trajectory_base.cpp b/src/trajectory/trajectory_base.cpp index 8ab61006c9e46acfdeee6ad735257bdf9d776ff7..23f967716a28e65d9aa09a01b1cb60a345f220e5 100644 --- a/src/trajectory/trajectory_base.cpp +++ b/src/trajectory/trajectory_base.cpp @@ -91,8 +91,8 @@ void TrajectoryBase::print(int _depth, bool _constr_by, bool _metric, bool _stat printHeader(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs); if (_depth >= 1) for (auto F : *this) - F->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); - + if (F) + F->print(_depth, _constr_by, _metric, _state_blocks, _stream, _tabs + " "); } CheckLog TrajectoryBase::localCheck(bool _verbose, TrajectoryBasePtr _trj_ptr, std::ostream& _stream, std::string _tabs) const diff --git a/src/tree_manager/tree_manager_sliding_window.cpp b/src/tree_manager/tree_manager_sliding_window.cpp index 623c7bec91214b55f7d2d8f2034dbba3e01c5ccb..3c64b2ee87d2e26318788c4eddd547606edb6cb0 100644 --- a/src/tree_manager/tree_manager_sliding_window.cpp +++ b/src/tree_manager/tree_manager_sliding_window.cpp @@ -3,32 +3,22 @@ namespace wolf { -void TreeManagerSlidingWindow::keyFrameCallback(FrameBasePtr _key_frame) +void TreeManagerSlidingWindow::keyFrameCallback(FrameBasePtr _frame) { - int n_kf(0); - FrameBasePtr first_KF(nullptr), second_KF(nullptr); - for (auto frm : *getProblem()->getTrajectory()) - { - if (frm->isKey()) - { - n_kf++; - if (first_KF == nullptr) - first_KF = frm; - else if (second_KF == nullptr) - second_KF = frm; - } - } + int n_f = getProblem()->getTrajectory()->getFrameMap().size(); // in trajectory there are only key frames - // remove first KF if too many KF - if (n_kf > params_sw_->n_key_frames) + // remove first frame if too many frames + if (n_f > params_sw_->n_frames) { - WOLF_DEBUG("TreeManagerSlidingWindow removing first frame"); - first_KF->remove(params_sw_->viral_remove_empty_parent); - if (params_sw_->fix_first_key_frame) + if (params_sw_->fix_first_frame) { WOLF_DEBUG("TreeManagerSlidingWindow fixing new first frame"); - second_KF->fix(); + auto second_frame = std::next(getProblem()->getTrajectory()->begin())->second; + if (second_frame) + second_frame->fix(); } + WOLF_DEBUG("TreeManagerSlidingWindow removing first frame"); + getProblem()->getTrajectory()->getFirstFrame()->remove(params_sw_->viral_remove_empty_parent); } } diff --git a/src/tree_manager/tree_manager_sliding_window_dual_rate.cpp b/src/tree_manager/tree_manager_sliding_window_dual_rate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2009cd89edf486af2efa892f58615a1e0fa05b0f --- /dev/null +++ b/src/tree_manager/tree_manager_sliding_window_dual_rate.cpp @@ -0,0 +1,86 @@ +#include "core/tree_manager/tree_manager_sliding_window_dual_rate.h" +#include "core/capture/capture_motion.h" +#include "core/processor/processor_motion.h" + +namespace wolf +{ + +TreeManagerSlidingWindowDualRate::TreeManagerSlidingWindowDualRate(ParamsTreeManagerSlidingWindowDualRatePtr _params) : + TreeManagerSlidingWindow(_params), + params_swdr_(_params), + count_frames_(0) +{ + NodeBase::setType("TreeManagerSlidingWindowDualRate"); +} + +void TreeManagerSlidingWindowDualRate::keyFrameCallback(FrameBasePtr _key_frame) +{ + int n_f = getProblem()->getTrajectory()->getFrameMap().size(); // in trajectory there are only key frames + + // recent segment not complete + if (n_f <= params_swdr_->n_frames_recent) + return; + + + // REMOVE FIRST RECENT FRAME: all recent frames except one of each rate_old_frames + if (count_frames_ != 0) + { + WOLF_DEBUG("TreeManagerSlidingWindow removing the oldest of recent frames"); + FrameBasePtr remove_recent_frame = std::next(getProblem()->getTrajectory()->rbegin(), + params_swdr_->n_frames_recent)->second; + FrameBasePtr keep_recent_frame = std::next(getProblem()->getTrajectory()->rbegin(), + params_swdr_->n_frames_recent - 1)->second; + + // compose motion captures for all processors motion + for (auto is_motion : getProblem()->getProcessorIsMotionList()) + { + auto proc_motion = std::dynamic_pointer_cast<ProcessorMotion>(is_motion); + if (proc_motion == nullptr) + continue; + + auto cap_prev = std::static_pointer_cast<CaptureMotion>(remove_recent_frame->getCaptureOf(proc_motion->getSensor())); + auto cap_next = std::static_pointer_cast<CaptureMotion>(keep_recent_frame->getCaptureOf(proc_motion->getSensor())); + + // merge captures (if exist) + if (cap_prev and cap_next) + { + WOLF_DEBUG("TreeManagerSlidingWindow merging capture ", cap_prev->id(), " with ", cap_next->id()); + assert(cap_next->getOriginCapture() == cap_prev); + proc_motion->mergeCaptures(cap_prev, cap_next); + } + + } + + // remove frame + remove_recent_frame->remove(params_swdr_->viral_remove_empty_parent); + } + // REMOVE OLDEST FRAME: when first recent frame is kept, remove oldest frame (if max frames reached) + else if (n_f > params_swdr_->n_frames) + { + if (params_swdr_->fix_first_frame) + { + WOLF_DEBUG("TreeManagerSlidingWindow fixing new first frame"); + auto second_frame = *std::next(getProblem()->getTrajectory()->begin()); + if (second_frame) + second_frame->fix(); + } + WOLF_DEBUG("TreeManagerSlidingWindow removing first frame"); + getProblem()->getTrajectory()->getFirstFrame()->remove(params_swdr_->viral_remove_empty_parent); + } + + // iterate counter + count_frames_++; + if (count_frames_ == params_swdr_->rate_old_frames) + count_frames_ = 0; +} + + +} /* namespace wolf */ + +// Register in the FactoryTreeManager +#include "core/tree_manager/factory_tree_manager.h" +namespace wolf { +WOLF_REGISTER_TREE_MANAGER(TreeManagerSlidingWindowDualRate); +WOLF_REGISTER_TREE_MANAGER_AUTO(TreeManagerSlidingWindowDualRate); +} // namespace wolf + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 88bcfc88a48286994ec38aee96c3cb44eb25a200..4363e38a4d4df67a73ce92086b7a3ae071c4a561 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -139,6 +139,10 @@ target_link_libraries(gtest_shared_from_this ${PLUGIN_NAME}) wolf_add_gtest(gtest_solver_manager gtest_solver_manager.cpp) target_link_libraries(gtest_solver_manager ${PLUGIN_NAME}) +# SolverManagerMultithread test +wolf_add_gtest(gtest_solver_manager_multithread gtest_solver_manager_multithread.cpp) +target_link_libraries(gtest_solver_manager_multithread ${PLUGIN_NAME}) + # StateBlock class test wolf_add_gtest(gtest_state_block gtest_state_block.cpp) target_link_libraries(gtest_state_block ${PLUGIN_NAME}) @@ -250,15 +254,23 @@ wolf_add_gtest(gtest_sensor_diff_drive gtest_sensor_diff_drive.cpp) target_link_libraries(gtest_sensor_diff_drive ${PLUGIN_NAME}) IF (Ceres_FOUND) -# SolverCeres test -wolf_add_gtest(gtest_solver_ceres gtest_solver_ceres.cpp) -target_link_libraries(gtest_solver_ceres ${PLUGIN_NAME}) + # SolverCeres test + wolf_add_gtest(gtest_solver_ceres gtest_solver_ceres.cpp) + target_link_libraries(gtest_solver_ceres ${PLUGIN_NAME}) + + # SolverCeresMultithread test + wolf_add_gtest(gtest_solver_ceres_multithread gtest_solver_ceres_multithread.cpp) + target_link_libraries(gtest_solver_ceres_multithread ${PLUGIN_NAME}) ENDIF(Ceres_FOUND) # TreeManagerSlidingWindow class test wolf_add_gtest(gtest_tree_manager_sliding_window gtest_tree_manager_sliding_window.cpp) target_link_libraries(gtest_tree_manager_sliding_window ${PLUGIN_NAME}) +# TreeManagerSlidingWindowDualRate class test +wolf_add_gtest(gtest_tree_manager_sliding_window_dual_rate gtest_tree_manager_sliding_window_dual_rate.cpp) +target_link_libraries(gtest_tree_manager_sliding_window_dual_rate ${PLUGIN_NAME}) + # yaml conversions wolf_add_gtest(gtest_yaml_conversions gtest_yaml_conversions.cpp) target_link_libraries(gtest_yaml_conversions ${PLUGIN_NAME}) diff --git a/test/dummy/solver_manager_dummy.h b/test/dummy/solver_manager_dummy.h index 15db720a3c42f8151dd8745dc35d0232fc0fe885..885f955d217b5a6afcd8084ab5d951af26e5b374 100644 --- a/test/dummy/solver_manager_dummy.h +++ b/test/dummy/solver_manager_dummy.h @@ -17,7 +17,7 @@ WOLF_PTR_TYPEDEFS(SolverManagerDummy); class SolverManagerDummy : public SolverManager { public: - std::list<FactorBasePtr> factors_; + std::set<FactorBasePtr> factors_derived_; std::map<StateBlockPtr,bool> state_block_fixed_; std::map<StateBlockPtr,LocalParametrizationBasePtr> state_block_local_param_; @@ -26,35 +26,34 @@ class SolverManagerDummy : public SolverManager { }; - bool isStateBlockRegistered(const StateBlockPtr& st) override + bool isStateBlockFixedDerived(const StateBlockPtr& st) override { - return state_blocks_.find(st)!=state_blocks_.end(); + return state_block_fixed_.at(st); }; - bool isStateBlockFixed(const StateBlockPtr& st) const + bool hasThisLocalParametrizationDerived(const StateBlockPtr& st, const LocalParametrizationBasePtr& local_param) override { - return state_block_fixed_.at(st); + return state_block_local_param_.at(st) == local_param; }; - bool isFactorRegistered(const FactorBasePtr& fac_ptr) const override + bool hasLocalParametrizationDerived(const StateBlockPtr& st) const override { - return std::find(factors_.begin(), factors_.end(), fac_ptr) != factors_.end(); + return state_block_local_param_.at(st) != nullptr; }; - bool hasThisLocalParametrization(const StateBlockPtr& st, const LocalParametrizationBasePtr& local_param) const + virtual int numStateBlocksDerived() const override { - return state_block_local_param_.find(st) != state_block_local_param_.end() && state_block_local_param_.at(st) == local_param; - }; + return state_block_fixed_.size(); + } - bool hasLocalParametrization(const StateBlockPtr& st) const + virtual int numFactorsDerived() const override { - return state_block_local_param_.find(st) != state_block_local_param_.end(); + return factors_derived_.size(); }; - void computeCovariances(const CovarianceBlocksToBeComputed blocks) override {}; - void computeCovariances(const std::vector<StateBlockPtr>& st_list) override {}; - bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) override {return true;}; - bool isFactorRegisteredDerived(const FactorBasePtr& fac_ptr) const override {return true;}; + bool computeCovariances(const CovarianceBlocksToBeComputed blocks) override {return false;}; + bool computeCovariances(const std::vector<StateBlockPtr>& st_list) override {return false;}; + // The following are dummy implementations bool hasConverged() override { return true; } @@ -62,19 +61,17 @@ class SolverManagerDummy : public SolverManager double initialCost() override { return double(1); } double finalCost() override { return double(0); } - - protected: bool checkDerived(std::string prefix="") const override {return true;} std::string solveDerived(const ReportVerbosity report_level) override { return std::string("");}; void addFactorDerived(const FactorBasePtr& fac_ptr) override { - factors_.push_back(fac_ptr); + factors_derived_.insert(fac_ptr); }; void removeFactorDerived(const FactorBasePtr& fac_ptr) override { - factors_.remove(fac_ptr); + factors_derived_.erase(fac_ptr); }; void addStateBlockDerived(const StateBlockPtr& state_ptr) override { @@ -92,10 +89,16 @@ class SolverManagerDummy : public SolverManager }; void updateStateBlockLocalParametrizationDerived(const StateBlockPtr& state_ptr) override { - if (state_ptr->getLocalParametrization() == nullptr) - state_block_local_param_.erase(state_ptr); - else - state_block_local_param_[state_ptr] = state_ptr->getLocalParametrization(); + state_block_local_param_[state_ptr] = state_ptr->getLocalParametrization(); + }; + bool isStateBlockRegisteredDerived(const StateBlockPtr& state_ptr) const override + { + return state_block_fixed_.count(state_ptr) == 1 and state_block_local_param_.count(state_ptr) == 1; + }; + + bool isFactorRegisteredDerived(const FactorBasePtr& fac_ptr) const override + { + return factors_derived_.count(fac_ptr) == 1; }; }; diff --git a/test/gtest_problem.cpp b/test/gtest_problem.cpp index 657ddc48a3ee49d8aefa709379415e780641aeec..dc44348f5943d7702372e28ebfb4e8ea2a707876 100644 --- a/test/gtest_problem.cpp +++ b/test/gtest_problem.cpp @@ -113,7 +113,7 @@ TEST(Problem, SetOrigin_PO_2d) VectorComposite s0(Vector3d(sqrt(0.1),sqrt(0.1),sqrt(0.1)), "PO", {2,1}); P->setPriorFactor(x0, s0, t0, 1.0); - + WOLF_INFO("printing.-.."); P->print(4,1,1,1); // check that no sensor has been added @@ -246,7 +246,7 @@ TEST(Problem, StateBlocks) std::string wolf_root = _WOLF_ROOT_DIR; ProblemPtr P = Problem::create("PO", 3); - Eigen::Vector7d xs3d; + Eigen::Vector7d xs3d; xs3d << 1,2,3,0,0,0,1; // required normalized quaternion (solver manager checks this) Eigen::Vector3d xs2d; // 2 state blocks, fixed @@ -564,6 +564,6 @@ TEST(Problem, getState) int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); - ::testing::GTEST_FLAG(filter) = "Problem.getState"; + //::testing::GTEST_FLAG(filter) = "Problem.getState"; return RUN_ALL_TESTS(); } diff --git a/test/gtest_processor_motion.cpp b/test/gtest_processor_motion.cpp index 743b0fcc56a4088ceb87073a6941200fd74ccd33..d905bcad555b88e4cd92afed94771c2e7f46546d 100644 --- a/test/gtest_processor_motion.cpp +++ b/test/gtest_processor_motion.cpp @@ -289,6 +289,83 @@ TEST_F(ProcessorMotion_test, IntegrateCircleFixPrior) ASSERT_MATRIX_APPROX(problem->getState().vector("PO"), (Vector3d()<<0,0,0).finished(), 1e-8); } +TEST_F(ProcessorMotion_test, mergeCaptures) +{ + // Prior + Vector3d x0; x0 << 0, 0, 0; + Matrix3d P0; P0.setIdentity(); + + data << 1, 2*M_PI/10; // advance in circle + data_cov.setIdentity(); + TimeStamp t(0.0); + + for (int i = 0; i<10; i++) // one full turn exactly + { + t += dt; + capture->setTimeStamp(t); + capture->setData(data); + capture->setDataCovariance(data_cov); + processor->captureCallback(capture); + WOLF_DEBUG("t: ", t, " x: ", problem->getState().vector("PO").transpose()); + } + + SensorBasePtr S = processor->getSensor(); + + TimeStamp t_target = 8.5; + FrameBasePtr F_target = problem->emplaceKeyFrame(t_target); + CaptureMotionPtr C_source = std::dynamic_pointer_cast<CaptureMotion>(processor->getLast()); + CaptureMotionPtr C_target = CaptureBase::emplace<CaptureMotion>(F_target, + "ODOM 2d", + t_target, + sensor, + data, + nullptr); + + // copy initial buffer + MotionBuffer initial_buffer = C_source->getBuffer(); + + processor->splitBuffer(C_source, + t_target, + F_target, + C_target); + + C_target->getBuffer().print(1,1,1,0); + C_source->getBuffer().print(1,1,1,0); + + // merge captures + processor->mergeCaptures(C_target, C_source); + + C_target->getBuffer().print(1,1,1,0); + C_source->getBuffer().print(1,1,1,0); + + // Check that merged buffer is the initial one (before splitting) + EXPECT_EQ(C_source->getBuffer().size(), initial_buffer.size()); + + auto init_it = initial_buffer.begin(); + auto res_it = C_source->getBuffer().begin(); + while (init_it != initial_buffer.end()) + { + EXPECT_EQ(init_it->data_size_, init_it->data_size_); + EXPECT_EQ(init_it->delta_size_, init_it->delta_size_); + EXPECT_EQ(init_it->cov_size_, init_it->cov_size_); + EXPECT_EQ(init_it->calib_size_, init_it->calib_size_); + EXPECT_EQ(init_it->ts_, init_it->ts_); + + EXPECT_MATRIX_APPROX(init_it->data_ ,init_it->data_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->data_cov_,init_it->data_cov_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->delta_, init_it->delta_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->delta_cov_, init_it->delta_cov_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->delta_integr_, init_it->delta_integr_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->delta_integr_cov_, init_it->delta_integr_cov_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->jacobian_delta_, init_it->jacobian_delta_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->jacobian_delta_integr_, init_it->jacobian_delta_integr_, 1e-12); + EXPECT_MATRIX_APPROX(init_it->jacobian_calib_, init_it->jacobian_calib_, 1e-12); + + init_it++; + res_it++; + } +} + TEST_F(ProcessorMotion_test, splitBufferAutoPrior) { // Prior diff --git a/test/gtest_solver_ceres.cpp b/test/gtest_solver_ceres.cpp index a05fd74de52247abb7cc3047861d2ece21d5963c..e5efa2dff18a27131a13cb173e064edad2d0a3c2 100644 --- a/test/gtest_solver_ceres.cpp +++ b/test/gtest_solver_ceres.cpp @@ -1,5 +1,5 @@ /* - * gtest_solver_ceres.cpp + * gtest_solver_ptr.cpp * * Created on: Jun, 2018 * Author: jvallve @@ -13,6 +13,7 @@ #include "core/capture/capture_void.h" #include "core/factor/factor_pose_2d.h" #include "core/factor/factor_quaternion_absolute.h" +#include "core/factor/factor_block_absolute.h" #include "core/state_block/local_parametrization_angle.h" #include "core/state_block/local_parametrization_quaternion.h" @@ -27,604 +28,1490 @@ using namespace wolf; using namespace Eigen; -WOLF_PTR_TYPEDEFS(SolverCeresWrapper); -class SolverCeresWrapper : public SolverCeres +/* + * Following tests are the same as in gtest_solver_manager.cpp + * (modifications should be applied to both tests) + */ + +StateBlockPtr createStateBlock() +{ + Vector4d state; state << 1, 0, 0, 0; + return std::make_shared<StateBlock>(state); +} + +FactorBasePtr createFactor(StateBlockPtr sb_ptr) { - public: - SolverCeresWrapper(const ProblemPtr& wolf_problem) : - SolverCeres(wolf_problem) - { - }; - - bool isStateBlockRegisteredSolverCeres(const StateBlockPtr& st) - { - return ceres_problem_->HasParameterBlock(SolverManager::getAssociatedMemBlockPtr(st)); - }; - - bool isStateBlockRegisteredSolverManager(const StateBlockPtr& st) - { - return state_blocks_.find(st)!=state_blocks_.end(); - }; - - bool isStateBlockFixed(const StateBlockPtr& st) - { - return ceres_problem_->IsParameterBlockConstant(SolverManager::getAssociatedMemBlockPtr(st)); - }; - - int numStateBlocks() - { - return ceres_problem_->NumParameterBlocks(); - }; - - int numFactors() - { - return ceres_problem_->NumResidualBlocks(); - }; - - bool isFactorRegistered(const FactorBasePtr& fac_ptr) const override - { - return fac_2_residual_idx_.find(fac_ptr) != fac_2_residual_idx_.end() && fac_2_costfunction_.find(fac_ptr) != fac_2_costfunction_.end(); - }; - - bool hasThisLocalParametrization(const StateBlockPtr& st, const LocalParametrizationBasePtr& local_param) - { - return state_blocks_local_param_.find(st) != state_blocks_local_param_.end() && - state_blocks_local_param_.at(st)->getLocalParametrization() == local_param && - ceres_problem_->GetParameterization(getAssociatedMemBlockPtr(st)) == state_blocks_local_param_.at(st).get(); - }; - - bool hasLocalParametrization(const StateBlockPtr& st) const - { - return state_blocks_local_param_.find(st) != state_blocks_local_param_.end(); - }; - -}; + return std::make_shared<FactorBlockAbsolute>(std::make_shared<FeatureBase>("Dummy", + VectorXd::Zero(sb_ptr->getSize()), + MatrixXd::Identity(sb_ptr->getSize(),sb_ptr->getSize())), + sb_ptr, + nullptr, + false); +} TEST(SolverCeres, Create) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); - // check double ointers to branches - ASSERT_EQ(P, solver_ceres->getProblem()); + // check pointers + EXPECT_EQ(P, solver_ptr->getProblem()); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // run solver check + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddStateBlock) +//////////////////////////////////////////////////////// +// FLOATING STATE BLOCKS +TEST(SolverCeres, FloatingStateBlock_Add) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverCeres(sb_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check stateblock + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, DoubleAddStateBlock) +TEST(SolverCeres, FloatingStateBlock_DoubleAdd) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_ceres->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // add stateblock again + // notify stateblock again P->notifyStateBlock(sb_ptr,ADD); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverCeres(sb_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check stateblock + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, UpdateStateBlock) +TEST(SolverCeres, FloatingStateBlock_AddFix) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + // update solver - solver_ceres->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock unfixed - ASSERT_FALSE(solver_ceres->isStateBlockFixed(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); // Fix frame sb_ptr->fix(); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + // update solver manager - solver_ceres->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock fixed - ASSERT_TRUE(solver_ceres->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, FloatingStateBlock_AddFixed) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Fix state block + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddUpdateStateBlock) +TEST(SolverCeres, FloatingStateBlock_AddUpdateLocalParam) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Local param + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); + // Fix state block sb_ptr->fix(); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver manager - solver_ceres->update(); + solver_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock fixed - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverCeres(sb_ptr)); - ASSERT_TRUE(solver_ceres->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, FloatingStateBlock_AddLocalParamRemoveLocalParam) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Local param + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check stateblock localparam + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Remove local param + sb_ptr->removeLocalParametrization(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check stateblock localparam + EXPECT_FALSE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, RemoveStateBlock) +TEST(SolverCeres, FloatingStateBlock_Remove) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_ceres->update(); + solver_ptr->update(); - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_TRUE(solver_ceres->isStateBlockRegisteredSolverCeres(sb_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_FALSE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_EQ(solver_ceres->numStateBlocks(), 0); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check stateblock + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddRemoveStateBlock) +TEST(SolverCeres, FloatingStateBlock_AddRemove) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // remove state_block - P->notifyStateBlock(sb_ptr,REMOVE); + P->notifyStateBlock(sb_ptr,REMOVE); // should cancell out the ADD + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // update solver - solver_ceres->update(); + solver_ptr->update(); - // check no stateblocks - ASSERT_FALSE(solver_ceres->isStateBlockRegisteredSolverManager(sb_ptr)); - ASSERT_EQ(solver_ceres->numStateBlocks(), 0); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check state block + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, RemoveUpdateStateBlock) +TEST(SolverCeres, FloatingStateBlock_AddRemoveAdd) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); - // update solver - solver_ceres->update(); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // remove state_block - P->notifyStateBlock(sb_ptr,REMOVE); + P->notifyStateBlock(sb_ptr,REMOVE); // should cancell out the ADD + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); // again ADD in notification list + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // update solver - solver_ceres->update(); + solver_ptr->update(); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check state block + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, DoubleRemoveStateBlock) +TEST(SolverCeres, FloatingStateBlock_DoubleRemove) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // update solver + solver_ptr->update(); + // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); // update solver - solver_ceres->update(); + solver_ptr->update(); // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver manager - solver_ceres->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // checks + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddFactor) +TEST(SolverCeres, FloatingStateBlock_AddUpdated) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Fix + sb_ptr->fix(); + + // Set State + sb_ptr->setState(2*sb_ptr->getState()); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame( 0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorPose2dPtr c = FactorBase::emplace<FactorPose2d>(f,f,nullptr,false); + // Check flags have been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->stateUpdated()); + + // == When an ADD is notified: a ADD notification should be stored == + + // notify state block + P->notifyStateBlock(sb_ptr,ADD); + + // check notifications + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, ADD); + + // == Insert OTHER notifications == + + // Set State --> FLAG + sb_ptr->setState(2*sb_ptr->getState()); + + // Fix --> FLAG + sb_ptr->unfix(); + // Check flag has been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); // No new notifications (fix and set state are flags in sb) + + // == consume empties the notification map == + solver_ptr->update(); // it calls P->consumeStateBlockNotificationMap(); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + // Check flags have been reset + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + + // == When an REMOVE is notified: a REMOVE notification should be stored == + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, REMOVE); + + // == REMOVE + ADD: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + + // == UPDATES should clear the list of notifications == + // notify state block + P->notifyStateBlock(sb_ptr,ADD); // update solver - solver_ceres->update(); + solver_ptr->update(); - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c)); - ASSERT_EQ(solver_ceres->numFactors(), 1); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // After solver_manager->update, notifications should be empty + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // == ADD + REMOVE: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + P->notifyStateBlock(sb_ptr,REMOVE); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); } -TEST(SolverCeres, DoubleAddFactor) +//////////////////////////////////////////////////////// +// STATE BLOCKS AND FACTOR +TEST(SolverCeres, StateBlockAndFactor_Add) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorPose2dPtr c = FactorBase::emplace<FactorPose2d>(f,f,nullptr,false); + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - // add factor again - P->notifyFactor(c,ADD); + // notify stateblock (floating for the moment) + P->notifyStateBlock(sb_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + + // notify factor (state block now not floating) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); // update solver - solver_ceres->update(); + solver_ptr->update(); - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c)); - ASSERT_EQ(solver_ceres->numFactors(), 1); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, RemoveFactor) +TEST(SolverCeres, StateBlockAndFactor_DoubleAdd) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorPose2dPtr c = FactorBase::emplace<FactorPose2d>(f,f,nullptr,false); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // notify stateblock again + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); - // remove factor - P->notifyFactor(c,REMOVE); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); - // check factor - ASSERT_FALSE(solver_ceres->isFactorRegistered(c)); - ASSERT_EQ(solver_ceres->numFactors(), 0); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check stateblock + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddRemoveFactor) +TEST(SolverCeres, StateBlockAndFactor_Fix) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorPose2dPtr c = FactorBase::emplace<FactorPose2d>(f,f,nullptr,false); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // remove factor - P->notifyFactor(c,REMOVE); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); - ASSERT_TRUE(P->getFactorNotificationMapSize() == 0); // add+remove = empty + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // update solver - solver_ceres->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock unfixed + EXPECT_FALSE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); + + // Fix frame + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver manager + solver_ptr->update(); - // check factor - ASSERT_FALSE(solver_ceres->isFactorRegistered(c)); - ASSERT_EQ(solver_ceres->numFactors(), 0); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, DoubleRemoveFactor) +TEST(SolverCeres, StateBlockAndFactor_AddFixed) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorPose2dPtr c = FactorBase::emplace<FactorPose2d>(f,f,nullptr,false); + // Create State block + auto sb_ptr = createStateBlock(); - // update solver - solver_ceres->update(); + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); - // remove factor - P->notifyFactor(c,REMOVE); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // update solver - solver_ceres->update(); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); - // remove factor - P->notifyFactor(c,REMOVE); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); - // update solver - solver_ceres->update(); + // Fix state block + sb_ptr->fix(); - // check factor - ASSERT_FALSE(solver_ceres->isFactorRegistered(c)); - ASSERT_EQ(solver_ceres->numFactors(), 0); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddStateBlockLocalParam) +TEST(SolverCeres, StateBlockAndFactor_UpdateLocalParam) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector1d state; state << 1; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // Local param - LocalParametrizationBasePtr local_param_ptr = std::make_shared<LocalParametrizationAngle>(); - sb_ptr->setLocalParametrization(local_param_ptr); + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); - // update solver - solver_ceres->update(); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // check stateblock - ASSERT_TRUE(solver_ceres->hasLocalParametrization(sb_ptr)); - ASSERT_TRUE(solver_ceres->hasThisLocalParametrization(sb_ptr,local_param_ptr)); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Local param + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // Fix state block + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, RemoveLocalParam) +TEST(SolverCeres, StateBlockAndFactor_AddLocalParamRemoveLocalParam) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector1d state; state << 1; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); // Local param - LocalParametrizationBasePtr local_param_ptr = std::make_shared<LocalParametrizationAngle>(); - sb_ptr->setLocalParametrization(local_param_ptr); + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); - // update solver - solver_ceres->update(); + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check solver + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->check()); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // Remove local param sb_ptr->removeLocalParametrization(); + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check solver + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, StateBlockAndFactor_RemoveStateBlock) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_FALSE(solver_ceres->hasLocalParametrization(sb_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); // there is a factor involved, removal posponed (REMOVE in notification list) + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, AddLocalParam) +TEST(SolverCeres, StateBlockAndFactor_RemoveFactor) { - ProblemPtr P = Problem::create("PO", 2); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); // Create State block - Vector1d state; state << 1; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_FALSE(solver_ceres->hasLocalParametrization(sb_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // Local param - LocalParametrizationBasePtr local_param_ptr = std::make_shared<LocalParametrizationAngle>(); - sb_ptr->setLocalParametrization(local_param_ptr); + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // state block should be automatically stored as floating + + // check notifications + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); // update solver - solver_ceres->update(); + solver_ptr->update(); - // check stateblock - ASSERT_TRUE(solver_ceres->hasLocalParametrization(sb_ptr)); - ASSERT_TRUE(solver_ceres->hasThisLocalParametrization(sb_ptr,local_param_ptr)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, FactorsRemoveLocalParam) +TEST(SolverCeres, StateBlockAndFactor_AddRemoveStateBlock) { ProblemPtr P = Problem::create("PO", 3); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); // cancells out ADD notification - // Create (and add) 2 factors quaternion - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorQuaternionAbsolutePtr c1 = FactorBase::emplace<FactorQuaternionAbsolute>(f, f, F->getO(),nullptr,false); - FactorQuaternionAbsolutePtr c2 = FactorBase::emplace<FactorQuaternionAbsolute>(f, f, F->getO(),nullptr,false); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); // factor ADD notification is not executed since state block involved is missing (ADD notification added) + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check state block + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, StateBlockAndFactor_AddRemoveFactor) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); - // check local param - ASSERT_TRUE(solver_ceres->hasLocalParametrization(F->getO())); - ASSERT_TRUE(solver_ceres->hasThisLocalParametrization(F->getO(),F->getO()->getLocalParametrization())); + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c1)); - ASSERT_TRUE(solver_ceres->isFactorRegistered(c2)); - ASSERT_EQ(solver_ceres->numFactors(), 2); + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); - // remove local param - F->getO()->removeLocalParametrization(); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // cancells out ADD notification + + // check notifications + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); // state block should be automatically stored as floating + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, StateBlockAndFactor_DoubleRemoveStateBlock) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - // check local param - ASSERT_FALSE(solver_ceres->hasLocalParametrization(F->getO())); + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); // cancells out the ADD + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); // factor ADD is posponed + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c1)); - ASSERT_TRUE(solver_ceres->isFactorRegistered(c2)); - ASSERT_EQ(solver_ceres->numFactors(), 2); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // update solver manager + solver_ptr->update(); // repeated REMOVE should be ignored + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check state block + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverCeres, FactorsUpdateLocalParam) +TEST(SolverCeres, StateBlockAndFactor_DoubleRemoveFactor) { ProblemPtr P = Problem::create("PO", 3); - SolverCeresWrapperPtr solver_ceres = std::make_shared<SolverCeresWrapper>(P); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // cancells out ADD - // Create (and add) 2 factors quaternion - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - FactorQuaternionAbsolutePtr c1 = FactorBase::emplace<FactorQuaternionAbsolute>(f, f, F->getO(),nullptr,false); - FactorQuaternionAbsolutePtr c2 = FactorBase::emplace<FactorQuaternionAbsolute>(f, f, F->getO(),nullptr,false); + // check notifications + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); // state block should be automatically stored as floating - // check local param - ASSERT_TRUE(solver_ceres->hasLocalParametrization(F->getO())); - ASSERT_TRUE(solver_ceres->hasThisLocalParametrization(F->getO(),F->getO()->getLocalParametrization())); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c1)); - ASSERT_TRUE(solver_ceres->isFactorRegistered(c2)); - ASSERT_EQ(solver_ceres->numFactors(), 2); + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); - // remove local param - LocalParametrizationBasePtr local_param_ptr = std::make_shared<LocalParametrizationQuaternionGlobal>(); - F->getO()->setLocalParametrization(local_param_ptr); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(REMOVE, notif); // update solver - solver_ceres->update(); + solver_ptr->update(); // repeated REMOVE should be ignored + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, StateBlockAndFactor_AddUpdatedStateBlock) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // Fix + sb_ptr->fix(); + + // Change State + sb_ptr->setState(2*sb_ptr->getState()); + + // Check flags have been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->stateUpdated()); + + // == When an ADD is notified: a ADD notification should be stored == + + // notify state block + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // == Insert OTHER notifications == + + // Set State --> FLAG + sb_ptr->setState(2*sb_ptr->getState()); + + // Fix --> FLAG + sb_ptr->unfix(); + // Check flag has been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); // No new notifications (fix and set state are flags in sb) + + // == consume empties the notification map == + solver_ptr->update(); // it calls P->consumeStateBlockNotificationMap(); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + // Check flags have been reset + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + + // == When an REMOVE is notified: a REMOVE notification should be stored == + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, REMOVE); + + // == REMOVE + ADD: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + + // == UPDATES should clear the list of notifications == + // notify state block + P->notifyStateBlock(sb_ptr,ADD); + + // update solver + solver_ptr->update(); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // After solver_manager->update, notifications should be empty - // check local param - ASSERT_TRUE(solver_ceres->hasLocalParametrization(F->getO())); - ASSERT_TRUE(solver_ceres->hasThisLocalParametrization(F->getO(),local_param_ptr)); + // == ADD + REMOVE: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + P->notifyStateBlock(sb_ptr,REMOVE); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); +} + +//////////////////////////////////////////////////////// +// ONLY FACTOR +TEST(SolverCeres, OnlyFactor_Add) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); // factor ADD should be posponed (in the notification list again) + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, OnlyFactor_Remove) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); // ADD factor should be posponed (in the notification list again) + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // notify factor + P->notifyFactor(fac_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // update solver + solver_ptr->update(); // nothing to update + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverCeres, OnlyFactor_AddRemove) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // notify factor + P->notifyFactor(fac_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // update solver + solver_ptr->update(); // nothing to update - // check factor - ASSERT_TRUE(solver_ceres->isFactorRegistered(c1)); - ASSERT_TRUE(solver_ceres->isFactorRegistered(c2)); - ASSERT_EQ(solver_ceres->numFactors(), 2); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); - // run ceres manager check - ASSERT_TRUE(solver_ceres->check()); + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } int main(int argc, char **argv) diff --git a/test/gtest_solver_ceres_multithread.cpp b/test/gtest_solver_ceres_multithread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..91fa716cb45d02c14bf9dad6068c4d6f4bae6780 --- /dev/null +++ b/test/gtest_solver_ceres_multithread.cpp @@ -0,0 +1,81 @@ +/* + * gtest_solver_ptr.cpp + * + * Created on: Jun, 2018 + * Author: jvallve + */ + +#include "core/utils/utils_gtest.h" + +#include "core/problem/problem.h" +#include "core/sensor/sensor_base.h" +#include "core/state_block/state_block.h" +#include "core/capture/capture_void.h" +#include "core/factor/factor_pose_2d.h" +#include "core/factor/factor_quaternion_absolute.h" +#include "core/factor/factor_block_absolute.h" +#include "core/state_block/local_parametrization_angle.h" +#include "core/state_block/local_parametrization_quaternion.h" + +#include "core/solver/solver_manager.h" +#include "core/ceres_wrapper/solver_ceres.h" +#include "core/ceres_wrapper/local_parametrization_wrapper.h" + +#include "ceres/ceres.h" + +#include <iostream> + +using namespace wolf; +using namespace Eigen; + +/* + * Following tests are the same as in gtest_solver_manager_multithread.cpp + * (modifications should be applied to both tests) + */ + +TEST(SolverCeresMultithread, MultiThreadingTruncatedNotifications) +{ + double Dt = 5.0; + ProblemPtr P = Problem::create("PO", 2); + auto solver_ptr = std::make_shared<SolverCeres>(P); + + // loop updating (without sleep) + std::thread t([&](){ + auto start_t = std::chrono::high_resolution_clock::now(); + while (true) + { + solver_ptr->update(); + ASSERT_TRUE(solver_ptr->check()); + if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_t).count() > Dt) + break; + }}); + + // loop emplacing and removing frames (window of 10 KF) + auto start = std::chrono::high_resolution_clock::now(); + TimeStamp ts(0); + while (true) + { + // Emplace Frame, Capture, feature and factor pose 2d + FrameBasePtr F = P->emplaceKeyFrame(ts, P->stateZero()); + auto C = CaptureBase::emplace<CaptureVoid>(F, ts, nullptr); + auto f = FeatureBase::emplace<FeatureBase>(C, "FeaturePose2d", Vector3d::Zero(), Matrix3d::Identity()); + auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + + ts += 1.0; + + if (P->getTrajectory()->getFrameMap().size() > 10) + (*P->getTrajectory()->begin())->remove(); + + if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start).count() > Dt) + break; + } + + t.join(); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/test/gtest_solver_manager.cpp b/test/gtest_solver_manager.cpp index 30bf42ca390c4488c5227be99a2c59a5a1cb98c3..2eb3f46e927270a7e61abb03734d01898722f471 100644 --- a/test/gtest_solver_manager.cpp +++ b/test/gtest_solver_manager.cpp @@ -13,8 +13,8 @@ #include "core/state_block/state_block.h" #include "core/capture/capture_void.h" #include "core/factor/factor_pose_2d.h" -#include "core/state_block/local_parametrization_base.h" -#include "core/state_block/local_parametrization_angle.h" +#include "core/factor/factor_block_absolute.h" +#include "core/state_block/local_parametrization_quaternion.h" #include "dummy/solver_manager_dummy.h" #include <iostream> @@ -23,571 +23,1490 @@ using namespace wolf; using namespace Eigen; +/* + * Following tests are the same as in gtest_solver_ceres.cpp + * (modifications should be applied to both tests) + */ + +StateBlockPtr createStateBlock() +{ + Vector4d state; state << 1, 0, 0, 0; + return std::make_shared<StateBlock>(state); +} + +FactorBasePtr createFactor(StateBlockPtr sb_ptr) +{ + return std::make_shared<FactorBlockAbsolute>(std::make_shared<FeatureBase>("Dummy", + VectorXd::Zero(sb_ptr->getSize()), + MatrixXd::Identity(sb_ptr->getSize(),sb_ptr->getSize())), + sb_ptr, + nullptr, + false); +} + TEST(SolverManager, Create) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); - // check double pointers to branches - ASSERT_EQ(P, solver_manager_ptr->getProblem()); + // check pointers + EXPECT_EQ(P, solver_ptr->getProblem()); + + // run solver check + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddStateBlock) +//////////////////////////////////////////////////////// +// FLOATING STATE BLOCKS +TEST(SolverManager, FloatingStateBlock_Add) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check stateblock - ASSERT_TRUE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, DoubleAddStateBlock) +TEST(SolverManager, FloatingStateBlock_DoubleAdd) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_manager_ptr->update(); + solver_ptr->update(); - // add stateblock again + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // notify stateblock again P->notifyStateBlock(sb_ptr,ADD); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check stateblock - ASSERT_TRUE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, UpdateStateBlock) +TEST(SolverManager, FloatingStateBlock_AddFix) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock unfixed - ASSERT_FALSE(solver_manager_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); // Fix frame sb_ptr->fix(); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_TRUE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock fixed - ASSERT_TRUE(solver_manager_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddUpdateStateBlock) +TEST(SolverManager, FloatingStateBlock_AddFixed) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // Fix state block sb_ptr->fix(); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_TRUE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock fixed - ASSERT_TRUE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); - ASSERT_TRUE(solver_manager_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddUpdateLocalParamStateBlock) +TEST(SolverManager, FloatingStateBlock_AddUpdateLocalParam) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // Local param - LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationAngle>(); + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); sb_ptr->setLocalParametrization(local_ptr); // Fix state block sb_ptr->fix(); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_TRUE(sb_ptr->fixUpdated()); - ASSERT_TRUE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // check stateblock fixed - ASSERT_TRUE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); - ASSERT_TRUE(solver_manager_ptr->isStateBlockFixed(sb_ptr)); - ASSERT_TRUE(solver_manager_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddLocalParamRemoveLocalParamStateBlock) +TEST(SolverManager, FloatingStateBlock_AddLocalParamRemoveLocalParam) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); // Local param - LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationAngle>(); + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); sb_ptr->setLocalParametrization(local_ptr); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_TRUE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check stateblock localparam - ASSERT_TRUE(solver_manager_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); // Remove local param sb_ptr->removeLocalParametrization(); // check flags - ASSERT_FALSE(sb_ptr->stateUpdated()); - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_TRUE(sb_ptr->localParamUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check stateblock localparam - ASSERT_FALSE(solver_manager_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_FALSE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, RemoveStateBlock) +TEST(SolverManager, FloatingStateBlock_Remove) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check stateblock - ASSERT_FALSE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddRemoveStateBlock) +TEST(SolverManager, FloatingStateBlock_AddRemove) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // remove state_block - P->notifyStateBlock(sb_ptr,REMOVE); + P->notifyStateBlock(sb_ptr,REMOVE); // should cancel out the ADD + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // check state block - ASSERT_FALSE(solver_manager_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, RemoveUpdateStateBlock) +TEST(SolverManager, FloatingStateBlock_AddRemoveAdd) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add state_block + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // remove state_block - P->notifyStateBlock(sb_ptr,REMOVE); + P->notifyStateBlock(sb_ptr,REMOVE); // should cancell out the ADD + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); // again ADD in notification list + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check state block + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, DoubleRemoveStateBlock) +TEST(SolverManager, FloatingStateBlock_DoubleRemove) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // add stateblock + // notify stateblock P->notifyStateBlock(sb_ptr,ADD); + // update solver + solver_ptr->update(); + // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + // update solver manager - solver_manager_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddUpdatedStateBlock) +TEST(SolverManager, FloatingStateBlock_AddUpdated) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); // Fix sb_ptr->fix(); // Set State - Vector2d state_2 = 2*state; - sb_ptr->setState(state_2); + sb_ptr->setState(2*sb_ptr->getState()); // Check flags have been set true - ASSERT_TRUE(sb_ptr->fixUpdated()); - ASSERT_TRUE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->stateUpdated()); // == When an ADD is notified: a ADD notification should be stored == - // add state_block + // notify state block P->notifyStateBlock(sb_ptr,ADD); + // check notifications + // check notifications Notification notif; - ASSERT_EQ(P->getStateBlockNotificationMapSize(),1); - ASSERT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); - ASSERT_EQ(notif, ADD); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, ADD); // == Insert OTHER notifications == // Set State --> FLAG - state_2 = 2*state; - sb_ptr->setState(state_2); + sb_ptr->setState(2*sb_ptr->getState()); // Fix --> FLAG sb_ptr->unfix(); // Check flag has been set true - ASSERT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),1); // No new notifications (fix and set state are flags in sb) + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); // No new notifications (fix and set state are flags in sb) // == consume empties the notification map == - solver_manager_ptr->update(); // it calls P->consumeStateBlockNotificationMap(); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),0); + solver_ptr->update(); // it calls P->consumeStateBlockNotificationMap(); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // Check flags have been reset - ASSERT_FALSE(sb_ptr->fixUpdated()); - ASSERT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); // == When an REMOVE is notified: a REMOVE notification should be stored == // remove state_block P->notifyStateBlock(sb_ptr,REMOVE); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),1); - ASSERT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); - ASSERT_EQ(notif, REMOVE); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, REMOVE); // == REMOVE + ADD: notification map should be empty == P->notifyStateBlock(sb_ptr,ADD); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // == UPDATES should clear the list of notifications == - // add state_block + // notify state block P->notifyStateBlock(sb_ptr,ADD); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),0); // After solver_manager->update, notifications should be empty + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // After solver_manager->update, notifications should be empty + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); // == ADD + REMOVE: notification map should be empty == P->notifyStateBlock(sb_ptr,ADD); P->notifyStateBlock(sb_ptr,REMOVE); - ASSERT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); +} + +//////////////////////////////////////////////////////// +// STATE BLOCKS AND FACTOR +TEST(SolverManager, StateBlockAndFactor_Add) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock (floating for the moment) + P->notifyStateBlock(sb_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + + // notify factor (state block now not floating) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_DoubleAdd) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // notify stateblock again + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check stateblock + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_Fix) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock unfixed + EXPECT_FALSE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); + + // Fix frame + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_AddFixed) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Fix state block + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_UpdateLocalParam) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Local param + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); + + // Fix state block + sb_ptr->fix(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // check stateblock fixed + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFixed(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_AddLocalParamRemoveLocalParam) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // Local param + LocalParametrizationBasePtr local_ptr = std::make_shared<LocalParametrizationQuaternionLocal>(); + sb_ptr->setLocalParametrization(local_ptr); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver manager + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // check solver + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->hasThisLocalParametrization(sb_ptr,local_ptr)); + EXPECT_TRUE(solver_ptr->check()); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->localParamUpdated()); + + // Remove local param + sb_ptr->removeLocalParametrization(); + + // check flags + EXPECT_FALSE(sb_ptr->stateUpdated()); + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->localParamUpdated()); + + // update solver manager + solver_ptr->update(); + + // check solver + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->hasLocalParametrization(sb_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_RemoveStateBlock) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); // there is a factor involved, removal posponed (REMOVE in notification list) + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_RemoveFactor) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // state block should be automatically stored as floating + + // check notifications + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_EQ(P->getStateBlockNotificationMapSize(), 0); + + // update solver + solver_ptr->update(); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_AddRemoveStateBlock) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); // cancells out ADD notification + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); // factor ADD notification is not executed since state block involved is missing (ADD notification added) + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check state block + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddFactor) +TEST(SolverManager, StateBlockAndFactor_AddRemoveFactor) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // notification + // check notifications Notification notif; - ASSERT_TRUE(P->getFactorNotification(c,notif)); - ASSERT_EQ(notif, ADD); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // cancells out ADD notification + + // check notifications + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // state block should be automatically stored as floating - // check factor - ASSERT_TRUE(solver_manager_ptr->isFactorRegistered(c)); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(), 0); + + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, RemoveFactor) +TEST(SolverManager, StateBlockAndFactor_DoubleRemoveStateBlock) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); + + // notify factor + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); // cancells out the ADD - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // factor ADD is posponed + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); + + // check notifications + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(REMOVE, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver manager + solver_ptr->update(); // repeated REMOVE should be ignored + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // check state block + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, StateBlockAndFactor_DoubleRemoveFactor) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify stateblock + P->notifyStateBlock(sb_ptr,ADD); - // add factor - P->notifyFactor(c,REMOVE); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // notification + // check notifications Notification notif; - ASSERT_TRUE(P->getFactorNotification(c,notif)); - ASSERT_EQ(notif, REMOVE); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); // cancells out ADD + + // check notifications + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // update solver + solver_ptr->update(); // state block should be automatically stored as floating + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // remove state_block + P->notifyFactor(fac_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(REMOVE, notif); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // repeated REMOVE should be ignored - // check factor - ASSERT_FALSE(solver_manager_ptr->isFactorRegistered(c)); + // checks + EXPECT_TRUE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_TRUE(solver_ptr->isStateBlockFloating(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, AddRemoveFactor) +TEST(SolverManager, StateBlockAndFactor_AddUpdatedStateBlock) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // Fix + sb_ptr->fix(); + + // Change State + sb_ptr->setState(2*sb_ptr->getState()); + + // Check flags have been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + EXPECT_TRUE(sb_ptr->stateUpdated()); + + // == When an ADD is notified: a ADD notification should be stored == - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(0, P->stateZero() ); + // notify state block + P->notifyStateBlock(sb_ptr,ADD); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + // notify factor + P->notifyFactor(fac_ptr,ADD); - // notification + // check notifications Notification notif; - ASSERT_TRUE(P->getFactorNotification(c,notif)); - ASSERT_EQ(notif, ADD); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(ADD, notif); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // == Insert OTHER notifications == + + // Set State --> FLAG + sb_ptr->setState(2*sb_ptr->getState()); + + // Fix --> FLAG + sb_ptr->unfix(); + // Check flag has been set true + EXPECT_TRUE(sb_ptr->fixUpdated()); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); // No new notifications (fix and set state are flags in sb) + + // == consume empties the notification map == + solver_ptr->update(); // it calls P->consumeStateBlockNotificationMap(); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + // Check flags have been reset + EXPECT_FALSE(sb_ptr->fixUpdated()); + EXPECT_FALSE(sb_ptr->stateUpdated()); + + // == When an REMOVE is notified: a REMOVE notification should be stored == + + // remove state_block + P->notifyStateBlock(sb_ptr,REMOVE); - // add factor - P->notifyFactor(c,REMOVE); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),1); + EXPECT_TRUE(P->getStateBlockNotification(sb_ptr, notif)); + EXPECT_EQ(notif, REMOVE); - // notification - ASSERT_EQ(P->getFactorNotificationMapSize(), 0); // ADD+REMOVE cancels out - ASSERT_FALSE(P->getFactorNotification(c, notif)); // ADD+REMOVE cancels out + // == REMOVE + ADD: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + + // == UPDATES should clear the list of notifications == + // notify state block + P->notifyStateBlock(sb_ptr,ADD); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); + + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); // After solver_manager->update, notifications should be empty - // check factor - ASSERT_FALSE(solver_manager_ptr->isFactorRegistered(c)); + // == ADD + REMOVE: notification map should be empty == + P->notifyStateBlock(sb_ptr,ADD); + P->notifyStateBlock(sb_ptr,REMOVE); + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); } -TEST(SolverManager, DoubleRemoveFactor) +//////////////////////////////////////////////////////// +// ONLY FACTOR +TEST(SolverManager, OnlyFactor_Add) { - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); // Create State block - Vector2d state; state << 1, 2; - StateBlockPtr sb_ptr = std::make_shared<StateBlock>(state); + auto sb_ptr = createStateBlock(); - // Create (and add) factor point 2d - FrameBasePtr F = P->emplaceKeyFrame(TimeStamp(0), P->stateZero() ); + // Create Factor + auto fac_ptr = createFactor(sb_ptr); - auto C = CaptureBase::emplace<CaptureVoid>(F, 0, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeatureOdom2d", Vector3d::Zero(), Matrix3d::Identity()); - auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // factor ADD should be posponed (in the notification list again) + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); - // remove factor - P->notifyFactor(c,REMOVE); + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); +} + +TEST(SolverManager, OnlyFactor_Remove) +{ + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // ADD factor should be posponed (in the notification list again) - // remove factor - P->notifyFactor(c,REMOVE); + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // notify factor + P->notifyFactor(fac_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); // update solver - solver_manager_ptr->update(); + solver_ptr->update(); // nothing to update + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); - // check factor - ASSERT_FALSE(solver_manager_ptr->isFactorRegistered(c)); + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } -TEST(SolverManager, MultiThreadingTruncatedNotifications) +TEST(SolverManager, OnlyFactor_AddRemove) { - double Dt = 5.0; - ProblemPtr P = Problem::create("PO", 2); - SolverManagerDummyPtr solver_manager_ptr = std::make_shared<SolverManagerDummy>(P); - - // loop updating (without sleep) - std::thread t([&](){ - auto start_t = std::chrono::high_resolution_clock::now(); - while (true) - { - solver_manager_ptr->update(); - if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_t).count() > Dt) - break; - }}); - - // loop emplacing and removing frames (window of 10 KF) - auto start = std::chrono::high_resolution_clock::now(); - TimeStamp ts(0); - while (true) - { - // Emplace Frame, Capture, feature and factor pose 2d - FrameBasePtr F = P->emplaceKeyFrame(ts, P->stateZero()); - auto C = CaptureBase::emplace<CaptureVoid>(F, ts, nullptr); - auto f = FeatureBase::emplace<FeatureBase>(C, "FeaturePose2d", Vector3d::Zero(), Matrix3d::Identity()); - auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); - - ts += 1.0; - - if (P->getTrajectory()->getFrameMap().size() > 10) - (*P->getTrajectory()->begin())->remove(); - - if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start).count() > Dt) - break; - } - - t.join(); + ProblemPtr P = Problem::create("PO", 3); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // Create State block + auto sb_ptr = createStateBlock(); + + // Create Factor + auto fac_ptr = createFactor(sb_ptr); + + // notify factor (state block has not been notified) + P->notifyFactor(fac_ptr,ADD); + + // check notifications + Notification notif; + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_TRUE(P->getFactorNotification(fac_ptr, notif)); + EXPECT_EQ(ADD, notif); + + // notify factor + P->notifyFactor(fac_ptr,REMOVE); + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // update solver + solver_ptr->update(); // nothing to update + + // check notifications + EXPECT_EQ(P->getStateBlockNotificationMapSize(),0); + EXPECT_EQ(P->getFactorNotificationMapSize(),0); + + // checks + EXPECT_FALSE(solver_ptr->isStateBlockRegistered(sb_ptr)); + EXPECT_FALSE(solver_ptr->isFactorRegistered(fac_ptr)); + EXPECT_TRUE(solver_ptr->check()); } int main(int argc, char **argv) diff --git a/test/gtest_solver_manager_multithread.cpp b/test/gtest_solver_manager_multithread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0770b33aed43e5ebbc9a204e68428f7a0022c870 --- /dev/null +++ b/test/gtest_solver_manager_multithread.cpp @@ -0,0 +1,75 @@ +/* + * gtest_solver_manager.cpp + * + * Created on: Jun, 2018 + * Author: jvallve + */ + +#include "core/utils/utils_gtest.h" + + +#include "core/problem/problem.h" +#include "core/sensor/sensor_base.h" +#include "core/state_block/state_block.h" +#include "core/capture/capture_void.h" +#include "core/factor/factor_pose_2d.h" +#include "core/factor/factor_block_absolute.h" +#include "core/state_block/local_parametrization_quaternion.h" +#include "dummy/solver_manager_dummy.h" + +#include <iostream> +#include <thread> + +using namespace wolf; +using namespace Eigen; + +/* + * Following tests are the same as in gtest_solver_ceres_multithread.cpp + * (modifications should be applied to both tests) + */ + +TEST(SolverManagerMultithread, MultiThreadingTruncatedNotifications) +{ + double Dt = 5.0; + ProblemPtr P = Problem::create("PO", 2); + auto solver_ptr = std::make_shared<SolverManagerDummy>(P); + + // loop updating (without sleep) + std::thread t([&](){ + auto start_t = std::chrono::high_resolution_clock::now(); + while (true) + { + solver_ptr->update(); + ASSERT_TRUE(solver_ptr->check()); + if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_t).count() > Dt) + break; + }}); + + // loop emplacing and removing frames (window of 10 KF) + auto start = std::chrono::high_resolution_clock::now(); + TimeStamp ts(0); + while (true) + { + // Emplace Frame, Capture, feature and factor pose 2d + FrameBasePtr F = P->emplaceKeyFrame(ts, P->stateZero()); + auto C = CaptureBase::emplace<CaptureVoid>(F, ts, nullptr); + auto f = FeatureBase::emplace<FeatureBase>(C, "FeaturePose2d", Vector3d::Zero(), Matrix3d::Identity()); + auto c = FactorBase::emplace<FactorPose2d>(f, f, nullptr, false); + + ts += 1.0; + + if (P->getTrajectory()->getFrameMap().size() > 10) + (*P->getTrajectory()->begin())->remove(); + + if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start).count() > Dt) + break; + } + + t.join(); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_tree_manager_sliding_window.cpp b/test/gtest_tree_manager_sliding_window.cpp index b8ff8240e7e2c7e97fb4f56aeadb72d2b51a0027..0c5e7c2aec97277425289f3f28a9ba3d61dece1c 100644 --- a/test/gtest_tree_manager_sliding_window.cpp +++ b/test/gtest_tree_manager_sliding_window.cpp @@ -24,7 +24,7 @@ TEST(TreeManagerSlidingWindow, make_shared) P->setTreeManager(GM); - ASSERT_EQ(P->getTreeManager(), GM); + EXPECT_EQ(P->getTreeManager(), GM); } TEST(TreeManagerSlidingWindow, createParams) @@ -35,12 +35,12 @@ TEST(TreeManagerSlidingWindow, createParams) auto GM = TreeManagerSlidingWindow::create("tree_manager", ParamsGM); - ASSERT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(GM) != nullptr); + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(GM) != nullptr); P->setTreeManager(GM); - ASSERT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); - ASSERT_EQ(P->getTreeManager(), GM); + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); + EXPECT_EQ(P->getTreeManager(), GM); } TEST(TreeManagerSlidingWindow, createParamServer) @@ -52,12 +52,12 @@ TEST(TreeManagerSlidingWindow, createParamServer) auto GM = TreeManagerSlidingWindow::create("tree_manager", server); - ASSERT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(GM) != nullptr); + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(GM) != nullptr); P->setTreeManager(GM); - ASSERT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); - ASSERT_EQ(P->getTreeManager(), GM); + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); + EXPECT_EQ(P->getTreeManager(), GM); } TEST(TreeManagerSlidingWindow, autoConf) @@ -67,7 +67,7 @@ TEST(TreeManagerSlidingWindow, autoConf) ProblemPtr P = Problem::autoSetup(server); - ASSERT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindow>(P->getTreeManager()) != nullptr); } TEST(TreeManagerSlidingWindow, slidingWindowFixViral) @@ -80,7 +80,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowFixViral) // FRAME 1 ---------------------------------------------------------- auto F1 = P->getTrajectory()->getLastFrame(); - ASSERT_TRUE(F1 != nullptr); + EXPECT_TRUE(F1 != nullptr); Vector7d state = F1->getStateVector(); Vector7d zero_disp(state); @@ -100,7 +100,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowFixViral) auto c12 = FactorBase::emplace<FactorOdom3d>(f12, f12, F1, nullptr, false); // Check no frame removed - ASSERT_FALSE(F1->isRemoving()); + EXPECT_FALSE(F1->isRemoving()); // FRAME 3 ---------------------------------------------------------- auto F3 = P->emplaceKeyFrame(TimeStamp(3), "PO", 3, state); @@ -116,7 +116,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowFixViral) auto c23 = FactorBase::emplace<FactorOdom3d>(f23, f23, F2, nullptr, false); // Check no frame removed - ASSERT_FALSE(F1->isRemoving()); + EXPECT_FALSE(F1->isRemoving()); // FRAME 4 ---------------------------------------------------------- auto F4 = P->emplaceKeyFrame(TimeStamp(4), "PO", 3, state); @@ -132,11 +132,11 @@ TEST(TreeManagerSlidingWindow, slidingWindowFixViral) auto c34 = FactorBase::emplace<FactorOdom3d>(f34, f34, F3, nullptr, false); // Checks - ASSERT_TRUE(F1->isRemoving()); - ASSERT_TRUE(c12->isRemoving()); - ASSERT_TRUE(C12->isRemoving()); //Virally removed - ASSERT_TRUE(f12->isRemoving()); //Virally removed - ASSERT_TRUE(F2->isFixed()); //Fixed + EXPECT_TRUE(F1->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C12->isRemoving()); // Virally removed + EXPECT_TRUE(f12->isRemoving()); // Virally removed + EXPECT_TRUE(F2->isFixed()); //Fixed // FRAME 5 ---------------------------------------------------------- auto F5 = P->emplaceKeyFrame(TimeStamp(5), "PO", 3, state); @@ -152,18 +152,19 @@ TEST(TreeManagerSlidingWindow, slidingWindowFixViral) auto c45 = FactorBase::emplace<FactorOdom3d>(f45, f45, F4, nullptr, false); // Checks - ASSERT_TRUE(F1->isRemoving()); - ASSERT_TRUE(c12->isRemoving()); - ASSERT_TRUE(C12->isRemoving()); //Virally removed - ASSERT_TRUE(f12->isRemoving()); //Virally removed - ASSERT_TRUE(F2->isRemoving()); - ASSERT_TRUE(c2->isRemoving()); - ASSERT_TRUE(C2->isRemoving()); //Virally removed - ASSERT_TRUE(f2->isRemoving()); //Virally removed - ASSERT_TRUE(c23->isRemoving()); - ASSERT_TRUE(C23->isRemoving()); //Virally removed - ASSERT_TRUE(f23->isRemoving()); //Virally removed - ASSERT_TRUE(F3->isFixed()); //Fixed + EXPECT_TRUE(F1->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C12->isRemoving()); // Virally removed + EXPECT_TRUE(f12->isRemoving()); // Virally removed + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C2->isRemoving()); // Virally removed + EXPECT_TRUE(f2->isRemoving()); // Virally removed + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C23->isRemoving()); // Virally removed + EXPECT_TRUE(f23->isRemoving()); // Virally removed + + EXPECT_TRUE(F3->isFixed()); //Fixed } TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) @@ -176,7 +177,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) // FRAME 1 (prior) ---------------------------------------------------------- auto F1 = P->getTrajectory()->getLastFrame(); - ASSERT_TRUE(F1 != nullptr); + EXPECT_TRUE(F1 != nullptr); Vector7d state = F1->getStateVector(); Vector7d zero_disp(state); @@ -196,7 +197,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) auto c12 = FactorBase::emplace<FactorOdom3d>(f12, f12, F1, nullptr, false); // Check no frame removed - ASSERT_FALSE(F1->isRemoving()); + EXPECT_FALSE(F1->isRemoving()); // FRAME 3 ---------------------------------------------------------- auto F3 = P->emplaceKeyFrame(TimeStamp(3), "PO", 3, state); @@ -212,7 +213,7 @@ TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) auto c23 = FactorBase::emplace<FactorOdom3d>(f23, f23, F2, nullptr, false); // Check no frame removed - ASSERT_FALSE(F1->isRemoving()); + EXPECT_FALSE(F1->isRemoving()); // FRAME 4 ---------------------------------------------------------- auto F4 = P->emplaceKeyFrame(TimeStamp(4), "PO", 3, state); @@ -228,11 +229,11 @@ TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) auto c34 = FactorBase::emplace<FactorOdom3d>(f34, f34, F3, nullptr, false); // Checks - ASSERT_TRUE(F1->isRemoving()); - ASSERT_TRUE(c12->isRemoving()); - ASSERT_FALSE(C12->isRemoving()); //Not virally removed - ASSERT_FALSE(f12->isRemoving()); //Not virally removed - ASSERT_FALSE(F2->isFixed()); //Not fixed + EXPECT_TRUE(F1->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C12->isRemoving()); //Not virally removed + EXPECT_FALSE(f12->isRemoving()); //Not virally removed + EXPECT_FALSE(F2->isFixed()); //Not fixed // FRAME 5 ---------------------------------------------------------- auto F5 = P->emplaceKeyFrame(TimeStamp(5), "PO", 3, state); @@ -248,18 +249,18 @@ TEST(TreeManagerSlidingWindow, slidingWindowNoFixNoViral) auto c45 = FactorBase::emplace<FactorOdom3d>(f45, f45, F4, nullptr, false); // Checks - ASSERT_TRUE(F1->isRemoving()); - ASSERT_TRUE(c12->isRemoving()); - ASSERT_TRUE(C12->isRemoving()); - ASSERT_TRUE(f12->isRemoving()); - ASSERT_TRUE(F2->isRemoving()); - ASSERT_TRUE(c2->isRemoving()); - ASSERT_TRUE(C2->isRemoving()); - ASSERT_TRUE(f2->isRemoving()); - ASSERT_TRUE(c23->isRemoving()); - ASSERT_FALSE(C23->isRemoving()); //Not virally removed - ASSERT_FALSE(f23->isRemoving()); //Not virally removed - ASSERT_FALSE(F3->isFixed()); //Not fixed + EXPECT_TRUE(F1->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C23->isRemoving()); //Not virally removed + EXPECT_FALSE(f23->isRemoving()); //Not virally removed + EXPECT_FALSE(F3->isFixed()); //Not fixed } int main(int argc, char **argv) diff --git a/test/gtest_tree_manager_sliding_window_dual_rate.cpp b/test/gtest_tree_manager_sliding_window_dual_rate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae4112bc3c27d88e103694ffd0824f4b7b35de2d --- /dev/null +++ b/test/gtest_tree_manager_sliding_window_dual_rate.cpp @@ -0,0 +1,1066 @@ +#include "core/utils/utils_gtest.h" + + +#include "core/problem/problem.h" +#include "core/tree_manager/tree_manager_sliding_window_dual_rate.h" +#include "core/yaml/parser_yaml.h" +#include "core/capture/capture_void.h" +#include "core/capture/capture_odom_3d.h" +#include "core/feature/feature_base.h" +#include "core/factor/factor_odom_3d.h" +#include "core/factor/factor_pose_3d.h" +#include "core/solver/factory_solver.h" + +using namespace wolf; +using namespace Eigen; + +std::string wolf_root = _WOLF_ROOT_DIR; + +TEST(TreeManagerSlidingWindowDualRate, make_shared) +{ + ProblemPtr P = Problem::create("PO", 2); + + auto ParamsGM = std::make_shared<ParamsTreeManagerSlidingWindowDualRate>(); + + auto GM = std::make_shared<TreeManagerSlidingWindowDualRate>(ParamsGM); + + P->setTreeManager(GM); + + EXPECT_EQ(P->getTreeManager(), GM); +} + +TEST(TreeManagerSlidingWindowDualRate, createParams) +{ + ProblemPtr P = Problem::create("PO", 2); + + auto ParamsGM = std::make_shared<ParamsTreeManagerSlidingWindowDualRate>(); + + auto GM = TreeManagerSlidingWindowDualRate::create("tree_manager", ParamsGM); + + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindowDualRate>(GM) != nullptr); + + P->setTreeManager(GM); + + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindowDualRate>(P->getTreeManager()) != nullptr); + EXPECT_EQ(P->getTreeManager(), GM); +} + +TEST(TreeManagerSlidingWindowDualRate, createParamServer) +{ + ProblemPtr P = Problem::create("PO", 2); + + ParserYaml parser = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml", wolf_root); + ParamsServer server = ParamsServer(parser.getParams()); + + auto GM = TreeManagerSlidingWindowDualRate::create("tree_manager", server); + + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindowDualRate>(GM) != nullptr); + + P->setTreeManager(GM); + + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindowDualRate>(P->getTreeManager()) != nullptr); + EXPECT_EQ(P->getTreeManager(), GM); +} + +TEST(TreeManagerSlidingWindowDualRate, autoConf) +{ + ParserYaml parser = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml", wolf_root); + ParamsServer server = ParamsServer(parser.getParams()); + + ProblemPtr P = Problem::autoSetup(server); + + EXPECT_TRUE(std::dynamic_pointer_cast<TreeManagerSlidingWindowDualRate>(P->getTreeManager()) != nullptr); +} + +TEST(TreeManagerSlidingWindowDualRate, slidingWindowFixViral) +{ + /* sliding window dual rate: + * n_frames: 5 + * n_frames_recent: 3 + * rate_old_frames: 2 + */ + + ParserYaml parser = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml", wolf_root); + ParamsServer server = ParamsServer(parser.getParams()); + + ProblemPtr P = Problem::autoSetup(server); + P->applyPriorOptions(0); + + /* FRAME 1 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) ( )( )(F1) + */ + auto F1 = P->getTrajectory()->getLastFrame(); + ASSERT_TRUE(F1 != nullptr); + ASSERT_FALSE(F1->getCaptureList().empty()); + auto C1 = F1->getCaptureList().front(); + ASSERT_FALSE(C1->getFeatureList().empty()); + auto f1 = C1->getFeatureList().front(); + ASSERT_FALSE(f1->getFactorList().empty()); + auto c1 = f1->getFactorList().front(); + + Vector7d state = F1->getStateVector(); + Vector7d zero_disp(state); + Matrix6d cov = Matrix6d::Identity(); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + + /* FRAME 2 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) ( )(F1)(F2) + */ + auto F2 = P->emplaceKeyFrame(TimeStamp(2), "PO", 3, state); + P->keyFrameCallback(F2, nullptr, 0); + + // absolute factor + auto C2 = CaptureBase::emplace<CaptureVoid>(F2, TimeStamp(2), nullptr); + auto f2 = FeatureBase::emplace<FeatureBase>(C2, "absolute", state, cov); + auto c2 = FactorBase::emplace<FactorPose3d>(f2, f2, nullptr, false); + // displacement + auto C12 = CaptureBase::emplace<CaptureVoid>(F2, TimeStamp(2), nullptr); + auto f12 = FeatureBase::emplace<FeatureBase>(C12, "odom", zero_disp, cov); + auto c12 = FactorBase::emplace<FactorOdom3d>(f12, f12, F1, nullptr, false); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + + /* FRAME 3 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) (F1)(F2)(F3) + */ + auto F3 = P->emplaceKeyFrame(TimeStamp(3), "PO", 3, state); + P->keyFrameCallback(F3, nullptr, 0); + + // absolute factor + auto C3 = CaptureBase::emplace<CaptureVoid>(F3, TimeStamp(3), nullptr); + auto f3 = FeatureBase::emplace<FeatureBase>(C3, "absolute", state, cov); + auto c3 = FactorBase::emplace<FactorPose3d>(f3, f3, nullptr, false); + // displacement + auto C23 = CaptureBase::emplace<CaptureVoid>(F3, TimeStamp(3), nullptr); + auto f23 = FeatureBase::emplace<FeatureBase>(C23, "odom", zero_disp, cov); + auto c23 = FactorBase::emplace<FactorOdom3d>(f23, f23, F2, nullptr, false); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_FALSE(c23->isRemoving()); + EXPECT_FALSE(C23->isRemoving()); + EXPECT_FALSE(f23->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + EXPECT_FALSE(F3->isFixed()); + + /* FRAME 4 ---------------------------------------------------------- + * + * Sliding window: + * ( ) (F1)(F2)(F3)(F4) + */ + auto F4 = P->emplaceKeyFrame(TimeStamp(4), "PO", 3, state); + P->keyFrameCallback(F4, nullptr, 0); + + // absolute factor + auto C4 = CaptureBase::emplace<CaptureVoid>(F4, TimeStamp(4), nullptr); + auto f4 = FeatureBase::emplace<FeatureBase>(C4, "absolute", state, cov); + auto c4 = FactorBase::emplace<FactorPose3d>(f4, f4, nullptr, false); + // displacement + auto C34 = CaptureBase::emplace<CaptureVoid>(F4, TimeStamp(4), nullptr); + auto f34 = FeatureBase::emplace<FeatureBase>(C34, "odom", zero_disp, cov); + auto c34 = FactorBase::emplace<FactorOdom3d>(f34, f34, F3, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_FALSE(c23->isRemoving()); + EXPECT_FALSE(C23->isRemoving()); + EXPECT_FALSE(f23->isRemoving()); + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + + /* FRAME 5 ---------------------------------------------------------- + * + * Sliding window: + * ( ) (F1) (F3)(F4)(F5) + */ + auto F5 = P->emplaceKeyFrame(TimeStamp(5), "PO", 3, state); + P->keyFrameCallback(F5, nullptr, 0); + + // absolute factor + auto C5 = CaptureBase::emplace<CaptureVoid>(F5, TimeStamp(5), nullptr); + auto f5 = FeatureBase::emplace<FeatureBase>(C5, "absolute", state, cov); + auto c5 = FactorBase::emplace<FactorPose3d>(f5, f5, nullptr, false); + // displacement + auto C45 = CaptureBase::emplace<CaptureVoid>(F5, TimeStamp(5), nullptr); + auto f45 = FeatureBase::emplace<FeatureBase>(C45, "odom", zero_disp, cov); + auto c45 = FactorBase::emplace<FactorOdom3d>(f45, f45, F4, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C23->isRemoving()); // Virally removed + EXPECT_TRUE(f23->isRemoving()); // Virally removed + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_FALSE(c45->isRemoving()); + EXPECT_FALSE(C45->isRemoving()); + EXPECT_FALSE(f45->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + EXPECT_FALSE(F5->isFixed()); + + /* FRAME 6 ---------------------------------------------------------- + * + * Sliding window: + * (F1) (F3)(F4)(F5)(F6) + */ + auto F6 = P->emplaceKeyFrame(TimeStamp(6), "PO", 3, state); + P->keyFrameCallback(F6, nullptr, 0); + + // absolute factor + auto C6 = CaptureBase::emplace<CaptureVoid>(F6, TimeStamp(6), nullptr); + auto f6 = FeatureBase::emplace<FeatureBase>(C6, "absolute", state, cov); + auto c6 = FactorBase::emplace<FactorPose3d>(f6, f6, nullptr, false); + // displacement + auto C56 = CaptureBase::emplace<CaptureVoid>(F6, TimeStamp(6), nullptr); + auto f56 = FeatureBase::emplace<FeatureBase>(C56, "odom", zero_disp, cov); + auto c56 = FactorBase::emplace<FactorOdom3d>(f56, f56, F5, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C23->isRemoving()); // Virally removed + EXPECT_TRUE(f23->isRemoving()); // Virally removed + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_FALSE(c45->isRemoving()); + EXPECT_FALSE(C45->isRemoving()); + EXPECT_FALSE(f45->isRemoving()); + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + + /* FRAME 7 ---------------------------------------------------------- + * + * Sliding window: + * (F1) (F3) (F5)(F6)(F7) + */ + auto F7 = P->emplaceKeyFrame(TimeStamp(7), "PO", 3, state); + P->keyFrameCallback(F7, nullptr, 0); + + // absolute factor + auto C7 = CaptureBase::emplace<CaptureVoid>(F7, TimeStamp(7), nullptr); + auto f7 = FeatureBase::emplace<FeatureBase>(C7, "absolute", state, cov); + auto c7 = FactorBase::emplace<FactorPose3d>(f7, f7, nullptr, false); + // displacement + auto C67 = CaptureBase::emplace<CaptureVoid>(F7, TimeStamp(7), nullptr); + auto f67 = FeatureBase::emplace<FeatureBase>(C67, "odom", zero_disp, cov); + auto c67 = FactorBase::emplace<FactorOdom3d>(f67, f67, F6, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C23->isRemoving()); // Virally removed + EXPECT_TRUE(f23->isRemoving()); // Virally removed + + EXPECT_TRUE(F4->isRemoving()); + EXPECT_TRUE(c4->isRemoving()); + EXPECT_TRUE(C4->isRemoving()); + EXPECT_TRUE(f4->isRemoving()); + EXPECT_TRUE(c34->isRemoving()); + EXPECT_TRUE(C34->isRemoving()); + EXPECT_TRUE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_TRUE(c45->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C45->isRemoving()); // Virally removed + EXPECT_TRUE(f45->isRemoving()); // Virally removed + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F7->isRemoving()); + EXPECT_FALSE(c7->isRemoving()); + EXPECT_FALSE(C7->isRemoving()); + EXPECT_FALSE(f7->isRemoving()); + EXPECT_FALSE(c67->isRemoving()); + EXPECT_FALSE(C67->isRemoving()); + EXPECT_FALSE(f67->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + EXPECT_FALSE(F7->isFixed()); + + /* FRAME 8 ---------------------------------------------------------- + * + * Sliding window: + * (F3) (F5)(F6)(F7)(F8) + */ + auto F8 = P->emplaceKeyFrame(TimeStamp(8), "PO", 3, state); + P->keyFrameCallback(F8, nullptr, 0); + + // absolute factor + auto C8 = CaptureBase::emplace<CaptureVoid>(F8, TimeStamp(8), nullptr); + auto f8 = FeatureBase::emplace<FeatureBase>(C8, "absolute", state, cov); + auto c8 = FactorBase::emplace<FactorPose3d>(f8, f8, nullptr, false); + // displacement + auto C78 = CaptureBase::emplace<CaptureVoid>(F8, TimeStamp(8), nullptr); + auto f78 = FeatureBase::emplace<FeatureBase>(C78, "odom", zero_disp, cov); + auto c78 = FactorBase::emplace<FactorOdom3d>(f78, f78, F7, nullptr, false); + + // Checks + EXPECT_TRUE(F1->isRemoving()); // First frame removed + EXPECT_TRUE(c1->isRemoving()); + EXPECT_TRUE(C1->isRemoving()); + EXPECT_TRUE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C23->isRemoving()); // Virally removed + EXPECT_TRUE(f23->isRemoving()); // Virally removed + + EXPECT_TRUE(F4->isRemoving()); + EXPECT_TRUE(c4->isRemoving()); + EXPECT_TRUE(C4->isRemoving()); + EXPECT_TRUE(f4->isRemoving()); + EXPECT_TRUE(c34->isRemoving()); + EXPECT_TRUE(C34->isRemoving()); + EXPECT_TRUE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_TRUE(c45->isRemoving()); // Factor removed because involves removed frame + EXPECT_TRUE(C45->isRemoving()); // Virally removed + EXPECT_TRUE(f45->isRemoving()); // Virally removed + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F7->isRemoving()); + EXPECT_FALSE(c7->isRemoving()); + EXPECT_FALSE(C7->isRemoving()); + EXPECT_FALSE(f7->isRemoving()); + EXPECT_FALSE(c67->isRemoving()); + EXPECT_FALSE(C67->isRemoving()); + EXPECT_FALSE(f67->isRemoving()); + + EXPECT_FALSE(F8->isRemoving()); + EXPECT_FALSE(c8->isRemoving()); + EXPECT_FALSE(C8->isRemoving()); + EXPECT_FALSE(f8->isRemoving()); + EXPECT_FALSE(c78->isRemoving()); + EXPECT_FALSE(C78->isRemoving()); + EXPECT_FALSE(f78->isRemoving()); + + EXPECT_TRUE(F3->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + EXPECT_FALSE(F7->isFixed()); + EXPECT_FALSE(F8->isFixed()); +} + +TEST(TreeManagerSlidingWindowDualRate, slidingWindowNoFixNoViral) +{ + /* sliding window dual rate: + * n_frames: 5 + * n_frames_recent: 3 + * rate_old_frames: 2 + */ + + ParserYaml parser = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate2.yaml", wolf_root); + ParamsServer server = ParamsServer(parser.getParams()); + + ProblemPtr P = Problem::autoSetup(server); + P->applyPriorOptions(0); + + /* FRAME 1 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) ( )( )(F1) + */ + auto F1 = P->getTrajectory()->getLastFrame(); + ASSERT_TRUE(F1 != nullptr); + ASSERT_FALSE(F1->getCaptureList().empty()); + auto C1 = F1->getCaptureList().front(); + ASSERT_FALSE(C1->getFeatureList().empty()); + auto f1 = C1->getFeatureList().front(); + ASSERT_FALSE(f1->getFactorList().empty()); + auto c1 = f1->getFactorList().front(); + + Vector7d state = F1->getStateVector(); + Vector7d zero_disp(state); + Matrix6d cov = Matrix6d::Identity(); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + + /* FRAME 2 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) ( )(F1)(F2) + */ + auto F2 = P->emplaceKeyFrame(TimeStamp(2), "PO", 3, state); + P->keyFrameCallback(F2, nullptr, 0); + + // absolute factor + auto C2 = CaptureBase::emplace<CaptureVoid>(F2, TimeStamp(2), nullptr); + auto f2 = FeatureBase::emplace<FeatureBase>(C2, "absolute", state, cov); + auto c2 = FactorBase::emplace<FactorPose3d>(f2, f2, nullptr, false); + // displacement + auto C12 = CaptureBase::emplace<CaptureVoid>(F2, TimeStamp(2), nullptr); + auto f12 = FeatureBase::emplace<FeatureBase>(C12, "odom", zero_disp, cov); + auto c12 = FactorBase::emplace<FactorOdom3d>(f12, f12, F1, nullptr, false); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + + /* FRAME 3 ---------------------------------------------------------- + * + * Sliding window: + * ( ) ( ) (F1)(F2)(F3) + */ + auto F3 = P->emplaceKeyFrame(TimeStamp(3), "PO", 3, state); + P->keyFrameCallback(F3, nullptr, 0); + + // absolute factor + auto C3 = CaptureBase::emplace<CaptureVoid>(F3, TimeStamp(3), nullptr); + auto f3 = FeatureBase::emplace<FeatureBase>(C3, "absolute", state, cov); + auto c3 = FactorBase::emplace<FactorPose3d>(f3, f3, nullptr, false); + // displacement + auto C23 = CaptureBase::emplace<CaptureVoid>(F3, TimeStamp(3), nullptr); + auto f23 = FeatureBase::emplace<FeatureBase>(C23, "odom", zero_disp, cov); + auto c23 = FactorBase::emplace<FactorOdom3d>(f23, f23, F2, nullptr, false); + + // Check no frame removed + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_FALSE(c23->isRemoving()); + EXPECT_FALSE(C23->isRemoving()); + EXPECT_FALSE(f23->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + EXPECT_FALSE(F3->isFixed()); + + /* FRAME 4 ---------------------------------------------------------- + * + * Sliding window: + * ( ) (F1)(F2)(F3)(F4) + */ + auto F4 = P->emplaceKeyFrame(TimeStamp(4), "PO", 3, state); + P->keyFrameCallback(F4, nullptr, 0); + + // absolute factor + auto C4 = CaptureBase::emplace<CaptureVoid>(F4, TimeStamp(4), nullptr); + auto f4 = FeatureBase::emplace<FeatureBase>(C4, "absolute", state, cov); + auto c4 = FactorBase::emplace<FactorPose3d>(f4, f4, nullptr, false); + // displacement + auto C34 = CaptureBase::emplace<CaptureVoid>(F4, TimeStamp(4), nullptr); + auto f34 = FeatureBase::emplace<FeatureBase>(C34, "odom", zero_disp, cov); + auto c34 = FactorBase::emplace<FactorOdom3d>(f34, f34, F3, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_FALSE(F2->isRemoving()); + EXPECT_FALSE(c2->isRemoving()); + EXPECT_FALSE(C2->isRemoving()); + EXPECT_FALSE(f2->isRemoving()); + EXPECT_FALSE(c12->isRemoving()); + EXPECT_FALSE(C12->isRemoving()); + EXPECT_FALSE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_FALSE(c23->isRemoving()); + EXPECT_FALSE(C23->isRemoving()); + EXPECT_FALSE(f23->isRemoving()); + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F2->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + + /* FRAME 5 ---------------------------------------------------------- + * + * Sliding window: + * ( ) (F1) (F3)(F4)(F5) + */ + auto F5 = P->emplaceKeyFrame(TimeStamp(5), "PO", 3, state); + P->keyFrameCallback(F5, nullptr, 0); + + // absolute factor + auto C5 = CaptureBase::emplace<CaptureVoid>(F5, TimeStamp(5), nullptr); + auto f5 = FeatureBase::emplace<FeatureBase>(C5, "absolute", state, cov); + auto c5 = FactorBase::emplace<FactorPose3d>(f5, f5, nullptr, false); + // displacement + auto C45 = CaptureBase::emplace<CaptureVoid>(F5, TimeStamp(5), nullptr); + auto f45 = FeatureBase::emplace<FeatureBase>(C45, "odom", zero_disp, cov); + auto c45 = FactorBase::emplace<FactorOdom3d>(f45, f45, F4, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C23->isRemoving()); // Not virally removed + EXPECT_FALSE(f23->isRemoving()); // Not virally removed + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_FALSE(c45->isRemoving()); + EXPECT_FALSE(C45->isRemoving()); + EXPECT_FALSE(f45->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + EXPECT_FALSE(F5->isFixed()); + + /* FRAME 6 ---------------------------------------------------------- + * + * Sliding window: + * (F1) (F3)(F4)(F5)(F6) + */ + auto F6 = P->emplaceKeyFrame(TimeStamp(6), "PO", 3, state); + P->keyFrameCallback(F6, nullptr, 0); + + // absolute factor + auto C6 = CaptureBase::emplace<CaptureVoid>(F6, TimeStamp(6), nullptr); + auto f6 = FeatureBase::emplace<FeatureBase>(C6, "absolute", state, cov); + auto c6 = FactorBase::emplace<FactorPose3d>(f6, f6, nullptr, false); + // displacement + auto C56 = CaptureBase::emplace<CaptureVoid>(F6, TimeStamp(6), nullptr); + auto f56 = FeatureBase::emplace<FeatureBase>(C56, "odom", zero_disp, cov); + auto c56 = FactorBase::emplace<FactorOdom3d>(f56, f56, F5, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C23->isRemoving()); // Not virally removed + EXPECT_FALSE(f23->isRemoving()); // Not virally removed + + EXPECT_FALSE(F4->isRemoving()); + EXPECT_FALSE(c4->isRemoving()); + EXPECT_FALSE(C4->isRemoving()); + EXPECT_FALSE(f4->isRemoving()); + EXPECT_FALSE(c34->isRemoving()); + EXPECT_FALSE(C34->isRemoving()); + EXPECT_FALSE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_FALSE(c45->isRemoving()); + EXPECT_FALSE(C45->isRemoving()); + EXPECT_FALSE(f45->isRemoving()); + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F4->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + + /* FRAME 7 ---------------------------------------------------------- + * + * Sliding window: + * (F1) (F3) (F5)(F6)(F7) + */ + auto F7 = P->emplaceKeyFrame(TimeStamp(7), "PO", 3, state); + P->keyFrameCallback(F7, nullptr, 0); + + // absolute factor + auto C7 = CaptureBase::emplace<CaptureVoid>(F7, TimeStamp(7), nullptr); + auto f7 = FeatureBase::emplace<FeatureBase>(C7, "absolute", state, cov); + auto c7 = FactorBase::emplace<FactorPose3d>(f7, f7, nullptr, false); + // displacement + auto C67 = CaptureBase::emplace<CaptureVoid>(F7, TimeStamp(7), nullptr); + auto f67 = FeatureBase::emplace<FeatureBase>(C67, "odom", zero_disp, cov); + auto c67 = FactorBase::emplace<FactorOdom3d>(f67, f67, F6, nullptr, false); + + // Checks + EXPECT_FALSE(F1->isRemoving()); + EXPECT_FALSE(c1->isRemoving()); + EXPECT_FALSE(C1->isRemoving()); + EXPECT_FALSE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C23->isRemoving()); // Not virally removed + EXPECT_FALSE(f23->isRemoving()); // Not virally removed + + EXPECT_TRUE(F4->isRemoving()); + EXPECT_TRUE(c4->isRemoving()); + EXPECT_TRUE(C4->isRemoving()); + EXPECT_TRUE(f4->isRemoving()); + EXPECT_TRUE(c34->isRemoving()); + EXPECT_TRUE(C34->isRemoving()); + EXPECT_TRUE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_TRUE(c45->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C45->isRemoving()); // Not virally removed + EXPECT_FALSE(f45->isRemoving()); // Not virally removed + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F7->isRemoving()); + EXPECT_FALSE(c7->isRemoving()); + EXPECT_FALSE(C7->isRemoving()); + EXPECT_FALSE(f7->isRemoving()); + EXPECT_FALSE(c67->isRemoving()); + EXPECT_FALSE(C67->isRemoving()); + EXPECT_FALSE(f67->isRemoving()); + + EXPECT_FALSE(F1->isFixed()); + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + EXPECT_FALSE(F7->isFixed()); + + /* FRAME 8 ---------------------------------------------------------- + * + * Sliding window: + * (F3) (F5)(F6)(F7)(F8) + */ + auto F8 = P->emplaceKeyFrame(TimeStamp(8), "PO", 3, state); + P->keyFrameCallback(F8, nullptr, 0); + + // absolute factor + auto C8 = CaptureBase::emplace<CaptureVoid>(F8, TimeStamp(8), nullptr); + auto f8 = FeatureBase::emplace<FeatureBase>(C8, "absolute", state, cov); + auto c8 = FactorBase::emplace<FactorPose3d>(f8, f8, nullptr, false); + // displacement + auto C78 = CaptureBase::emplace<CaptureVoid>(F8, TimeStamp(8), nullptr); + auto f78 = FeatureBase::emplace<FeatureBase>(C78, "odom", zero_disp, cov); + auto c78 = FactorBase::emplace<FactorOdom3d>(f78, f78, F7, nullptr, false); + + // Checks + EXPECT_TRUE(F1->isRemoving()); // First frame removed + EXPECT_TRUE(c1->isRemoving()); + EXPECT_TRUE(C1->isRemoving()); + EXPECT_TRUE(f1->isRemoving()); + + EXPECT_TRUE(F2->isRemoving()); + EXPECT_TRUE(c2->isRemoving()); + EXPECT_TRUE(C2->isRemoving()); + EXPECT_TRUE(f2->isRemoving()); + EXPECT_TRUE(c12->isRemoving()); + EXPECT_TRUE(C12->isRemoving()); + EXPECT_TRUE(f12->isRemoving()); + + EXPECT_FALSE(F3->isRemoving()); + EXPECT_FALSE(c3->isRemoving()); + EXPECT_FALSE(C3->isRemoving()); + EXPECT_FALSE(f3->isRemoving()); + EXPECT_TRUE(c23->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C23->isRemoving()); // Not virally removed + EXPECT_FALSE(f23->isRemoving()); // Not virally removed + + EXPECT_TRUE(F4->isRemoving()); + EXPECT_TRUE(c4->isRemoving()); + EXPECT_TRUE(C4->isRemoving()); + EXPECT_TRUE(f4->isRemoving()); + EXPECT_TRUE(c34->isRemoving()); + EXPECT_TRUE(C34->isRemoving()); + EXPECT_TRUE(f34->isRemoving()); + + EXPECT_FALSE(F5->isRemoving()); + EXPECT_FALSE(c5->isRemoving()); + EXPECT_FALSE(C5->isRemoving()); + EXPECT_FALSE(f5->isRemoving()); + EXPECT_TRUE(c45->isRemoving()); // Factor removed because involves removed frame + EXPECT_FALSE(C45->isRemoving()); // Not virally removed + EXPECT_FALSE(f45->isRemoving()); // Not virally removed + + EXPECT_FALSE(F6->isRemoving()); + EXPECT_FALSE(c6->isRemoving()); + EXPECT_FALSE(C6->isRemoving()); + EXPECT_FALSE(f6->isRemoving()); + EXPECT_FALSE(c56->isRemoving()); + EXPECT_FALSE(C56->isRemoving()); + EXPECT_FALSE(f56->isRemoving()); + + EXPECT_FALSE(F7->isRemoving()); + EXPECT_FALSE(c7->isRemoving()); + EXPECT_FALSE(C7->isRemoving()); + EXPECT_FALSE(f7->isRemoving()); + EXPECT_FALSE(c67->isRemoving()); + EXPECT_FALSE(C67->isRemoving()); + EXPECT_FALSE(f67->isRemoving()); + + EXPECT_FALSE(F8->isRemoving()); + EXPECT_FALSE(c8->isRemoving()); + EXPECT_FALSE(C8->isRemoving()); + EXPECT_FALSE(f8->isRemoving()); + EXPECT_FALSE(c78->isRemoving()); + EXPECT_FALSE(C78->isRemoving()); + EXPECT_FALSE(f78->isRemoving()); + + EXPECT_FALSE(F3->isFixed()); + EXPECT_FALSE(F5->isFixed()); + EXPECT_FALSE(F6->isFixed()); + EXPECT_FALSE(F7->isFixed()); + EXPECT_FALSE(F8->isFixed()); +} + +TEST(TreeManagerSlidingWindowDualRate, slidingWindowWithProcessor) +{ + // SLIDING WINDOW DUAL RATE + ParserYaml parser = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate3.yaml", wolf_root); + ParamsServer server = ParamsServer(parser.getParams()); + ProblemPtr problem = Problem::autoSetup(server); + SolverManagerPtr solver = FactorySolver::create("SolverCeres", problem, server); + + // BASELINE + ParserYaml parser_bl = ParserYaml("test/yaml/params_tree_manager_sliding_window_dual_rate_baseline.yaml", wolf_root); + ParamsServer server_bl = ParamsServer(parser_bl.getParams()); + ProblemPtr problem_bl = Problem::autoSetup(server_bl); + SolverManagerPtr solver_bl = FactorySolver::create("SolverCeres", problem_bl, server); + + // aux variables + Vector7d data; + Matrix6d data_cov = Matrix6d::Identity(); + TimeStamp t(0.0); + double dt = 1; + CaptureMotionPtr capture = std::make_shared<CaptureOdom3d>(t, + problem->getSensor("odom"), + data, + data_cov); + CaptureMotionPtr capture_bl = std::make_shared<CaptureOdom3d>(t, + problem_bl->getSensor("odom"), + data, + data_cov); + + // START MOTION CAPTURES + for (int i = 0; i<20; i++) + { + t += dt; + WOLF_INFO("-------------------------- t: ", t); + + // random movement + data.setRandom(); data.tail<4>().normalize(); + + // sliding window process + capture->setTimeStamp(t); + capture->setData(data); + capture->process(); + + // baseline process + capture_bl->setTimeStamp(t); + capture_bl->setData(data); + capture_bl->process(); + + WOLF_INFO("state: ", problem->getState().vector("PO").transpose()); + + ASSERT_MATRIX_APPROX(problem->getState().vector("PO").transpose(), + problem_bl->getState().vector("PO").transpose(), + 1e-12); + + // Solve + solver->solve(); + solver_bl->solve(); + + ASSERT_MATRIX_APPROX(problem->getState().vector("PO").transpose(), + problem_bl->getState().vector("PO").transpose(), + 1e-12); + + ASSERT_TRUE(problem->check()); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/yaml/params_tree_manager_sliding_window1.yaml b/test/yaml/params_tree_manager_sliding_window1.yaml index 96ad94f72af7e8de1b9200a8e5ea44e3bc952a85..22242febd640b616bebe4ab0410867b0e373a5be 100644 --- a/test/yaml/params_tree_manager_sliding_window1.yaml +++ b/test/yaml/params_tree_manager_sliding_window1.yaml @@ -4,8 +4,6 @@ config: dimension: 3 prior: mode: "factor" - # state: [0,0,0,0,0,0,1] - # cov: [[6,6],.1,0,0,0,0,0, 0,.1,0,0,0,0, 0,0,.1,0,0,0, 0,0,0,.1,0,0, 0,0,0,0,.1,0, 0,0,0,0,0,.1] $state: P: [0,0,0] O: [0,0,0,1] @@ -15,8 +13,8 @@ config: time_tolerance: 0.1 tree_manager: type: "TreeManagerSlidingWindow" - n_key_frames: 3 - fix_first_key_frame: true + n_frames: 3 + fix_first_frame: true viral_remove_empty_parent: true sensors: - diff --git a/test/yaml/params_tree_manager_sliding_window2.yaml b/test/yaml/params_tree_manager_sliding_window2.yaml index 9ae33a2af5ab0695bd44948d33d2f9aa104cbc74..add5eff760050fecbde63c827bb75e77c531f86e 100644 --- a/test/yaml/params_tree_manager_sliding_window2.yaml +++ b/test/yaml/params_tree_manager_sliding_window2.yaml @@ -4,8 +4,6 @@ config: dimension: 3 prior: mode: "factor" - # state: [0,0,0,0,0,0,1] - # cov: [[6,6],.1,0,0,0,0,0, 0,.1,0,0,0,0, 0,0,.1,0,0,0, 0,0,0,.1,0,0, 0,0,0,0,.1,0, 0,0,0,0,0,.1] $state: P: [0,0,0] O: [0,0,0,1] @@ -15,8 +13,8 @@ config: time_tolerance: 0.1 tree_manager: type: "TreeManagerSlidingWindow" - n_key_frames: 3 - fix_first_key_frame: false + n_frames: 3 + fix_first_frame: false viral_remove_empty_parent: false sensors: - diff --git a/test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml b/test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..32ef665e0a6d92b94e9342d22c5e4ca9952613f1 --- /dev/null +++ b/test/yaml/params_tree_manager_sliding_window_dual_rate1.yaml @@ -0,0 +1,20 @@ +config: + problem: + frame_structure: "PO" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + time_tolerance: 0.1 + tree_manager: + type: "TreeManagerSlidingWindowDualRate" + n_frames: 5 + n_frames_recent: 3 + rate_old_frames: 2 + fix_first_frame: true + viral_remove_empty_parent: true diff --git a/test/yaml/params_tree_manager_sliding_window_dual_rate2.yaml b/test/yaml/params_tree_manager_sliding_window_dual_rate2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6b4795fb3ad70c77b02c9cffc74b964abfe64141 --- /dev/null +++ b/test/yaml/params_tree_manager_sliding_window_dual_rate2.yaml @@ -0,0 +1,20 @@ +config: + problem: + frame_structure: "PO" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + time_tolerance: 0.1 + tree_manager: + type: "TreeManagerSlidingWindowDualRate" + n_frames: 5 + n_frames_recent: 3 + rate_old_frames: 2 + fix_first_frame: false + viral_remove_empty_parent: false diff --git a/test/yaml/params_tree_manager_sliding_window_dual_rate3.yaml b/test/yaml/params_tree_manager_sliding_window_dual_rate3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b59022518aaf931890368185fb9b489611cda257 --- /dev/null +++ b/test/yaml/params_tree_manager_sliding_window_dual_rate3.yaml @@ -0,0 +1,55 @@ +config: + solver: + period: 1 + verbose: 2 + update_immediately: false + max_num_iterations: 10 + n_threads: 2 + problem: + frame_structure: "PO" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + time_tolerance: 0.1 + tree_manager: + type: "TreeManagerSlidingWindowDualRate" + n_frames: 5 + n_frames_recent: 3 + rate_old_frames: 2 + fix_first_frame: true + viral_remove_empty_parent: true + sensors: + - + type: "SensorOdom3d" + name: "odom" + plugin: "core" + k_disp_to_disp: 0.1 + k_disp_to_rot: 0.1 + k_rot_to_rot: 0.1 + min_disp_var: 0.1 + min_rot_var: 0.1 + extrinsic: + pose: [1,2,3,0,0,0,1] + processors: + - + type: "ProcessorOdom3d" + name: "my_proc_odom3d" + sensor_name: "odom" + plugin: "core" + apply_loss_function: false + time_tolerance: 0.01 # seconds + keyframe_vote: + voting_active: true + voting_aux_active: false + max_time_span: 0.2 # seconds + max_buff_length: 10 # motion deltas + dist_traveled: 10 # meters + angle_turned: 3.1 # radians (1 rad approx 57 deg, approx 60 deg) + + unmeasured_perturbation_std: 0.00111 diff --git a/test/yaml/params_tree_manager_sliding_window_dual_rate_baseline.yaml b/test/yaml/params_tree_manager_sliding_window_dual_rate_baseline.yaml new file mode 100644 index 0000000000000000000000000000000000000000..eba780939b0e4521ba7a5d571ed47d63749adc6e --- /dev/null +++ b/test/yaml/params_tree_manager_sliding_window_dual_rate_baseline.yaml @@ -0,0 +1,49 @@ +config: + solver: + period: 1 + verbose: 2 + update_immediately: false + max_num_iterations: 10 + problem: + frame_structure: "PO" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + time_tolerance: 0.1 + tree_manager: + type: "None" + sensors: + - + type: "SensorOdom3d" + name: "odom" + plugin: "core" + k_disp_to_disp: 0.1 + k_disp_to_rot: 0.1 + k_rot_to_rot: 0.1 + min_disp_var: 0.1 + min_rot_var: 0.1 + extrinsic: + pose: [1,2,3,0,0,0,1] + processors: + - + type: "ProcessorOdom3d" + name: "my_proc_odom3d" + sensor_name: "odom" + plugin: "core" + apply_loss_function: false + time_tolerance: 0.01 # seconds + keyframe_vote: + voting_active: true + voting_aux_active: false + max_time_span: 0.2 # seconds + max_buff_length: 10 # motion deltas + dist_traveled: 10 # meters + angle_turned: 3.1 # radians (1 rad approx 57 deg, approx 60 deg) + + unmeasured_perturbation_std: 0.00111