diff --git a/include/core/factor/factor_relative_pose_2d_with_extrinsics.h b/include/core/factor/factor_relative_pose_2d_with_extrinsics.h index b0eea0f2b8f91f00a9a2bc8a7bda6ce1be66dc45..fb89f046ed7d967cc428bb8355dc4cf760da40b8 100644 --- a/include/core/factor/factor_relative_pose_2d_with_extrinsics.h +++ b/include/core/factor/factor_relative_pose_2d_with_extrinsics.h @@ -132,31 +132,49 @@ inline bool FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p_re Eigen::Map<const Eigen::Matrix<T,2,1> > p_ref(_p_ref); Eigen::Map<const Eigen::Matrix<T,2,1> > p_target(_p_target); Eigen::Map<const Eigen::Matrix<T,2,1> > p_sensor(_p_sensor); - T o_ref = _o_ref[0]; - T o_target = _o_target[0]; - T o_sensor = _o_sensor[0]; + const T& o_ref = _o_ref[0]; + const T& o_target = _o_target[0]; + const T& o_sensor = _o_sensor[0]; Eigen::Matrix<T, 3, 1> expected_measurement; Eigen::Matrix<T, 3, 1> er; // error - // Expected measurement - // r1_p_r1_s1 = ps - // r2_p_r2_s2 = ps - // r1_R_s1 = R(os) - // r2_R_s2 = R(os) - // w_R_r1 = R(o1) - // w_R_r2 = R(o2) - // ---------------------------------------- - - // s1_p_s1_s2 = s1_R_r1*r1_p_s1_r1 + - // s1_R_r1*r1_R_w*w_p_r1_w + - // s1_R_r1*r1_R_w*w_p_w_r2 + - // s1_R_r1*r1_R_w*w_R_r2*r2_p_r2_s2 - - // s1_p_s1_s2 = s1_R_r1*(r1_p_s1_r1 + r1_R_w*(w_p_r1_w + w_p_w_r2 + w_R_r2*r2_p_r2_s2)) - - expected_measurement.head(2) = Eigen::Rotation2D<T>(-o_sensor)*(-p_sensor + Eigen::Rotation2D<T>(-o_ref)*(-p_ref + p_target + Eigen::Rotation2D<T>(o_target)*p_sensor)); - expected_measurement(2) = o_target - o_ref; + // FRAME CASE + if (not getFrameOtherList().empty()) + { + // Expected measurement + // r1_p_s1 = p_sensor + // r2_p_s2 = p_sensor + // r1_R_s1 = R(o_sensor) + // r2_R_s2 = R(o_sensor) + // w_p_r1 = p_ref + // w_p_r2 = p_target + // w_R_r1 = R(o_ref) + // w_R_r2 = R(o_target) + // ---------------------------------------- + // s1_p_s2 = s1_R_r1*(r1_R_w*(w_p_r2 + w_R_r2*r2_p_s2 - w_p_r1) - r1_p_s1) + + expected_measurement.head(2) = Eigen::Rotation2D<T>(-o_sensor)*(-p_sensor + Eigen::Rotation2D<T>(-o_ref)*(-p_ref + p_target + Eigen::Rotation2D<T>(o_target)*p_sensor)); + expected_measurement(2) = o_target - o_ref; + } + // LANDMARK CASE + else if (not getLandmarkOtherList().empty()) + { + // Expected measurement + // r1_p_s1 = p_sensor + // r2_p_s2 = p_sensor + // r1_R_s1 = R(o_sensor) + // r2_R_s2 = R(o_sensor) + // w_p_r1 = p_ref + // w_p_r2 = p_target + // w_R_r1 = R(o_ref) + // w_R_r2 = R(o_target) + // ---------------------------------------- + // s1_p_s2 = s1_R_r1*(r1_R_w*(w_p_r2 - w_p_r1) - r1_p_s1) + + expected_measurement.head(2) = Eigen::Rotation2D<T>(-o_sensor)*(-p_sensor + Eigen::Rotation2D<T>(-o_ref)*(-p_ref + p_target)); + expected_measurement(2) = o_target - o_ref; + } // Error er = expected_measurement - getMeasurement().cast<T>(); diff --git a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h index 19245f6bcf630f737e4a01aa78b4a11030d8264a..a72d9683a8f9818e6dc242a67e2770a29fa1692e 100644 --- a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h +++ b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h @@ -150,38 +150,57 @@ inline FactorRelativePose3dWithExtrinsics::~FactorRelativePose3dWithExtrinsics() template<typename T> inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_ref, - const T* const _o_ref, - const T* const _p_target, - const T* const _o_target, - const T* const _p_sensor, - const T* const _o_sensor, - T* _residuals) const + const T* const _o_ref, + const T* const _p_target, + const T* const _o_target, + const T* const _p_sensor, + const T* const _o_sensor, + T* _residuals) const { // Maps - Eigen::Map<const Eigen::Matrix<T,3,1>> p_r_c(_p_sensor); - Eigen::Map<const Eigen::Quaternion<T>> q_r_c(_o_sensor); + Eigen::Map<const Eigen::Matrix<T,3,1>> p_r_s(_p_sensor); + Eigen::Map<const Eigen::Quaternion<T>> q_r_s(_o_sensor); Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_r(_p_ref); Eigen::Map<const Eigen::Quaternion<T>> q_w_r(_o_ref); - Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_l(_p_target); - Eigen::Map<const Eigen::Quaternion<T>> q_w_l(_o_target); + Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_t(_p_target); + Eigen::Map<const Eigen::Quaternion<T>> q_w_t(_o_target); Eigen::Map<Eigen::Matrix<T,6,1>> residuals(_residuals); + // WOLF_INFO("p_sensor: ", p_r_s.transpose()); + // WOLF_INFO("o_sensor: ", q_r_s.coeffs().transpose()); + // WOLF_INFO("p_ref: ", p_w_r.transpose()); + // WOLF_INFO("o_ref: ", q_w_r.coeffs().transpose()); + // WOLF_INFO("p_target: ", p_w_t.transpose()); + // WOLF_INFO("o_target: ", q_w_t.coeffs().transpose()); + // Expected measurement - Eigen::Quaternion<T> q_c_w = (q_w_r * q_r_c).conjugate(); - Eigen::Quaternion<T> q_c_l = q_c_w * q_w_l; - Eigen::Matrix<T,3,1> p_c_l = q_c_w * (-(p_w_r + q_w_r * p_r_c) + p_w_l); + Eigen::Quaternion<T> expected_q; + Eigen::Matrix<T,3,1> expected_p; + // FRAME CASE + if (not getFrameOtherList().empty()) + { + expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t * q_r_s; + expected_p = q_r_s.conjugate() * q_w_r.conjugate() * (p_w_t + q_w_t * p_r_s - (p_w_r + q_w_r * p_r_s)); + } + // LANDMARK CASE + else if (not getLandmarkOtherList().empty()) + { + expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t; + expected_p = q_r_s.conjugate() * q_w_r.conjugate() * (p_w_t - (p_w_r + q_w_r * p_r_s)); + } // Measurement - Eigen::Vector3d p_c_l_meas(getMeasurement().head<3>()); + Eigen::Vector3d p_meas(getMeasurement().head<3>()); Eigen::Quaterniond q_c_l_meas(getMeasurement().data() + 3 ); Eigen::Quaternion<T> q_l_c_meas = q_c_l_meas.conjugate().cast<T>(); //Eigen::Matrix<T,3,1> p_l_c_meas = -q_l_c_meas * p_c_l_meas.cast<T>(); // Error Eigen::Matrix<T, 6, 1> err; - err.head(3) = q_l_c_meas * (p_c_l_meas.cast<T>() - p_c_l); - //err.tail(3) = wolf::log_q(q_l_c_meas * q_c_l); // true error function for which the covariance should be computed - err.tail(3) = T(2)*(q_l_c_meas * q_c_l).vec(); // 1rst order approximation of sin function ( 2*sin(aa/2) ~ aa ) + // err.head(3) = q_l_c_meas * (p_meas.cast<T>() - expected_p); + err.head(3) = (p_meas - expected_p); + err.tail(3) = wolf::log_q(q_l_c_meas * expected_q); // true error function for which the covariance should be computed + //err.tail(3) = T(2)*(q_l_c_meas * expected_q).vec(); // 1rst order approximation of sin function ( 2*sin(aa/2) ~ aa ) // Residual residuals = getMeasurementSquareRootInformationUpper().cast<T>() * err; @@ -192,15 +211,27 @@ inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_re inline Eigen::Vector6d FactorRelativePose3dWithExtrinsics::residual() const { Eigen::Vector6d res; - double * p_camera, * o_camera, * p_ref, * o_ref, * p_target, * o_target; - p_camera = getCapture()->getSensorP()->getState().data(); - o_camera = getCapture()->getSensorO()->getState().data(); - p_ref = getCapture()->getFrame()->getP()->getState().data(); - o_ref = getCapture()->getFrame()->getO()->getState().data(); - p_target = getLandmarkOther()->getP()->getState().data(); - o_target = getLandmarkOther()->getO()->getState().data(); - - operator() (p_camera, o_camera, p_ref, o_ref, p_target, o_target, res.data()); + Eigen::VectorXd p_camera, o_camera, p_ref, o_ref, p_target, o_target; + p_camera = getCapture()->getSensorP()->getState(); + o_camera = getCapture()->getSensorO()->getState(); + // FRAME CASE + if (not getFrameOtherList().empty()) + { + p_ref = getFrameOther()->getP()->getState(); + o_ref = getFrameOther()->getO()->getState(); + p_target = getCapture()->getFrame()->getP()->getState(); + o_target = getCapture()->getFrame()->getO()->getState(); + } + // LANDMARK CASE + else if (not getLandmarkOtherList().empty()) + { + p_ref = getCapture()->getFrame()->getP()->getState(); + o_ref = getCapture()->getFrame()->getO()->getState(); + p_target = getLandmarkOther()->getP()->getState(); + o_target = getLandmarkOther()->getO()->getState(); + } + + operator() (p_ref.data(), o_ref.data(), p_target.data(), o_target.data(), p_camera.data(), o_camera.data(), res.data()); return res; } diff --git a/include/core/factor/factor_relative_position_2d.h b/include/core/factor/factor_relative_position_2d.h new file mode 100644 index 0000000000000000000000000000000000000000..8c52033c6add806a4d809857477063558e85317f --- /dev/null +++ b/include/core/factor/factor_relative_position_2d.h @@ -0,0 +1,260 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Solà Ortega (jsola@iri.upc.edu) +// All rights reserved. +// +// This file is part of WOLF +// WOLF is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#pragma once + +//Wolf includes +#include "core/factor/factor_analytic.h" +#include "core/landmark/landmark_base.h" +#include "core/math/rotations.h" +#include <Eigen/StdVector> + +namespace wolf { + +WOLF_PTR_TYPEDEFS(FactorRelativePosition2d); + +//class +class FactorRelativePosition2d : public FactorAnalytic +{ + public: + + /** \brief Constructor of category FAC_FRAME + **/ + FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr, + const FrameBasePtr& _frame_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status = FAC_ACTIVE) : + FactorAnalytic("FactorRelativePosition2d", + _top, + _ftr_ptr, + _frame_other_ptr, + nullptr, + nullptr, + nullptr, + _processor_ptr, + _apply_loss_function, + _status, + _frame_other_ptr->getP(), + _frame_other_ptr->getO(), + _ftr_ptr->getFrame()->getP()) + { + // + } + + /** \brief Constructor of category FAC_FEATURE + **/ + FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr, + const FeatureBasePtr& _ftr_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status = FAC_ACTIVE) : + FactorAnalytic("FactorRelativePosition2d", + _top, + _ftr_ptr, + nullptr, + nullptr, + _ftr_other_ptr, + nullptr, + _processor_ptr, + _apply_loss_function, + _status, + _ftr_ptr->getFrame()->getP(), + _ftr_ptr->getFrame()->getO(), + _ftr_other_ptr->getFrame()->getP() ) + { + // + } + + /** \brief Constructor of category FAC_LANDMARK + **/ + FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr, + const LandmarkBasePtr& _landmark_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status = FAC_ACTIVE) : + FactorAnalytic("FactorRelativePosition2d", + _top, + _ftr_ptr, + nullptr, + nullptr, + nullptr, + _landmark_other_ptr, + _processor_ptr, + _apply_loss_function, + _status, + _ftr_ptr->getFrame()->getP(), + _ftr_ptr->getFrame()->getO(), + _landmark_other_ptr->getP()) + { + // + } + + ~FactorRelativePosition2d() override = default; + + /** \brief Returns the factor residual size + **/ + unsigned int getSize() const override + { + return 2; + } + + /** \brief Returns the residual evaluated in the states provided + * + * Returns the residual evaluated in the states provided in a std::vector of mapped Eigen::VectorXd + **/ + Eigen::VectorXd evaluateResiduals( + const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector) const override; + + /** \brief Returns the jacobians evaluated in the states provided + * + * Returns the jacobians evaluated in the states provided in std::vector of mapped Eigen::VectorXd. + * IMPORTANT: only fill the jacobians of the state blocks specified in _compute_jacobian. + * + * \param _st_vector is a vector containing the mapped eigen vectors of all state blocks involved in the factor + * \param jacobians is an output vector of mapped eigen matrices that sould contain the jacobians w.r.t each state block + * \param _compute_jacobian is a vector that specifies whether the ith jacobian sould be computed or not + * + **/ + void evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector, + std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians, + const std::vector<bool>& _compute_jacobian) const override; + void evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector, + std::vector<Eigen::MatrixXd>& jacobians, + const std::vector<bool>& _compute_jacobian) const override; + + /** \brief Returns the pure jacobians (without measurement noise) evaluated in the state blocks values + * \param jacobians is an output vector of mapped eigen matrices that sould contain the jacobians w.r.t each state block + * + **/ + void evaluatePureJacobians(std::vector<Eigen::MatrixXd>& jacobians) const override; + +}; + +/// IMPLEMENTATION /// + +inline Eigen::VectorXd FactorRelativePosition2d::evaluateResiduals( + const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector) const +{ + Eigen::VectorXd residual(2); + Eigen::VectorXd expected_measurement(2); + // Expected measurement + Eigen::Matrix2d R = Eigen::Rotation2D<double>(-_st_vector[1](0)).matrix(); + expected_measurement = R * (_st_vector[2] - _st_vector[0]); // rotar menys l'angle de primer (-_o1) + // Residual + residual = expected_measurement - getMeasurement(); + residual = getMeasurementSquareRootInformationUpper() * residual; + return residual; +} + +inline void FactorRelativePosition2d::evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector, + std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians, + const std::vector<bool>& _compute_jacobian) const +{ + assert(jacobians.size() == 3); + assert(_st_vector.size() == 3); + assert(_compute_jacobian.size() == 3); + + if (_compute_jacobian[0]) + { + jacobians[0] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) << + -cos(_st_vector[1](0)), -sin(_st_vector[1](0)), + sin(_st_vector[1](0)), -cos(_st_vector[1](0))).finished(); + } + if (_compute_jacobian[1]) + { + jacobians[1] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,1) << + -(_st_vector[2](0) - _st_vector[0](0)) * sin(_st_vector[1](0)) + + (_st_vector[2](1) - _st_vector[0](1)) * cos(_st_vector[1](0)), + -(_st_vector[2](0) - _st_vector[0](0)) * cos(_st_vector[1](0)) - + (_st_vector[2](1) - _st_vector[0](1)) * sin(_st_vector[1](0))).finished(); + } + if (_compute_jacobian[2]) + { + jacobians[2] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) << + cos(_st_vector[1](0)), sin(_st_vector[1](0)), + -sin(_st_vector[1](0)), cos(_st_vector[1](0))).finished(); + } +} + +inline void FactorRelativePosition2d::evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector, + std::vector<Eigen::MatrixXd>& jacobians, + const std::vector<bool>& _compute_jacobian) const +{ + assert(jacobians.size() == 3); + assert(_st_vector.size() == 3); + assert(_compute_jacobian.size() == 3); + + if (_compute_jacobian[0]) + { + jacobians[0] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) << + -cos(_st_vector[1](0)), -sin(_st_vector[1](0)), + sin(_st_vector[1](0)), -cos(_st_vector[1](0))).finished(); + } + if (_compute_jacobian[1]) + { + jacobians[1] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,1) << + -(_st_vector[2](0) - _st_vector[0](0)) * sin(_st_vector[1](0)) + + (_st_vector[2](1) - _st_vector[0](1)) * cos(_st_vector[1](0)), + -(_st_vector[2](0) - _st_vector[0](0)) * cos(_st_vector[1](0)) - + (_st_vector[2](1) - _st_vector[0](1)) * sin(_st_vector[1](0))).finished(); + } + if (_compute_jacobian[2]) + { + jacobians[2] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) << + cos(_st_vector[1](0)), sin(_st_vector[1](0)), + -sin(_st_vector[1](0)), cos(_st_vector[1](0))).finished(); + } +} + +inline void FactorRelativePosition2d::evaluatePureJacobians(std::vector<Eigen::MatrixXd>& jacobians) const +{ + assert(jacobians.size() == 3); + + jacobians[0] = (Eigen::MatrixXd(2,2) << + -cos(getStateBlockPtrVector()[1]->getState()(0)), + -sin(getStateBlockPtrVector()[1]->getState()(0)), + sin(getStateBlockPtrVector()[1]->getState()(0)), + -cos(getStateBlockPtrVector()[1]->getState()(0))).finished(); + + jacobians[1] = (Eigen::MatrixXd(2,1) << + -(getStateBlockPtrVector()[2]->getState()(0) + - getStateBlockPtrVector()[0]->getState()(0)) * sin(getStateBlockPtrVector()[1]->getState()(0)) + + (getStateBlockPtrVector()[2]->getState()(1) + - getStateBlockPtrVector()[0]->getState()(1)) * cos(getStateBlockPtrVector()[1]->getState()(0)), + -(getStateBlockPtrVector()[2]->getState()(0) + - getStateBlockPtrVector()[0]->getState()(0)) * cos(getStateBlockPtrVector()[1]->getState()(0)) + - (getStateBlockPtrVector()[2]->getState()(1) + - getStateBlockPtrVector()[0]->getState()(1)) * sin(getStateBlockPtrVector()[1]->getState()(0))).finished(); + + jacobians[2] = (Eigen::MatrixXd(2,2) << + cos(getStateBlockPtrVector()[1]->getState()(0)), + sin(getStateBlockPtrVector()[1]->getState()(0)), + -sin(getStateBlockPtrVector()[1]->getState()(0)), + cos(getStateBlockPtrVector()[1]->getState()(0))).finished(); +} + +} // namespace wolf + +#endif diff --git a/include/core/factor/factor_relative_position_2d_with_extrinsics copy.h b/include/core/factor/factor_relative_position_2d_with_extrinsics copy.h new file mode 100644 index 0000000000000000000000000000000000000000..5e62f6e0079852d738930493b23112c9ded14df5 --- /dev/null +++ b/include/core/factor/factor_relative_position_2d_with_extrinsics copy.h @@ -0,0 +1,130 @@ +//--------LICENSE_START-------- +// +// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. +// Authors: Joan Solà Ortega (jsola@iri.upc.edu) +// All rights reserved. +// +// This file is part of WOLF +// WOLF is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//--------LICENSE_END-------- +#pragma once + +//Wolf includes +#include "core/factor/factor_autodiff.h" +#include "core/frame/frame_base.h" +#include "core/math/rotations.h" + +//#include "ceres/jet.h" + +namespace wolf { + +WOLF_PTR_TYPEDEFS(FactorRelativePosition2dWithExtrinsics); + +//class +class FactorRelativePosition2dWithExtrinsics : public FactorAutodiff<FactorRelativePosition2dWithExtrinsics, 2, 2, 1, 2, 2, 1> +{ + public: + + /** \brief Class constructor Frame-Landmark + */ + FactorRelativePosition2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, + const LandmarkBasePtr& _lmk_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top = TOP_LMK, + FactorStatus _status = FAC_ACTIVE); + + ~FactorRelativePosition2dWithExtrinsics() override = default; + + template<typename T> + bool operator ()(const T* const _p_ref, + const T* const _o_ref, + const T* const _p_target, + const T* const _p_sensor, + const T* const _o_sensor, + T* _residuals) const; +}; + +inline FactorRelativePosition2dWithExtrinsics::FactorRelativePosition2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, + const LandmarkBasePtr& _lmk_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status) : + FactorAutodiff("FactorRelativePosition2dWithExtrinsics", + _top, + _ftr_ptr, + nullptr, nullptr, nullptr, _lmk_other_ptr, + _processor_ptr, + _apply_loss_function, + _status, + _ftr_ptr->getFrame()->getP(), + _ftr_ptr->getFrame()->getO(), + _lmk_other_ptr->getP(), + _ftr_ptr->getCapture()->getSensorP(), + _ftr_ptr->getCapture()->getSensorO()) +{ + assert(_ftr_ptr->getCapture()->getSensorP() != nullptr && "No extrinsics found!"); + assert(_ftr_ptr->getCapture()->getSensorO() != nullptr && "No extrinsics found!"); + // +} + +template<typename T> +inline bool FactorRelativePosition2dWithExtrinsics::operator ()(const T* const _p_ref, + const T* const _o_ref, + const T* const _p_target, + const T* const _p_sensor, + const T* const _o_sensor, + T* _residuals) const +{ + // MAPS + Eigen::Map<Eigen::Matrix<T,2,1> > res(_residuals); + Eigen::Map<const Eigen::Matrix<T,2,1> > p_ref(_p_ref); + Eigen::Map<const Eigen::Matrix<T,2,1> > p_target(_p_target); + Eigen::Map<const Eigen::Matrix<T,2,1> > p_sensor(_p_sensor); + T o_ref = _o_ref[0]; + T o_sensor = _o_sensor[0]; + + // Expected measurement + Eigen::Matrix<T, 2, 1> expected_measurement = Eigen::Rotation2D<T>(-o_sensor) * + (-p_sensor + Eigen::Rotation2D<T>(-o_ref) * (p_target - p_ref)); + + // Residuals + res = getMeasurementSquareRootInformationUpper() * (expected_measurement - getMeasurement()); + + //////////////////////////////////////////////////////// + // print Jacobian. Uncomment this as you wish (remember to uncomment #include "ceres/jet.h" above): +// using ceres::Jet; +// Eigen::MatrixXs J(3,6); +// J.row(0) = ((Jet<Scalar, 6>)(er(0))).v; +// J.row(1) = ((Jet<Scalar, 6>)(er(1))).v; +// J.row(2) = ((Jet<Scalar, 6>)(er(2))).v; +// J.row(0) = ((Jet<Scalar, 6>)(res(0))).v; +// J.row(1) = ((Jet<Scalar, 6>)(res(1))).v; +// J.row(2) = ((Jet<Scalar, 6>)(res(2))).v; +// if (sizeof(er(0)) != sizeof(double)) +// { +// std::cout << "FactorRelativePosition2dWithExtrinsics::Jacobian(c" << id() << ") = \n " << J << std::endl; +// std::cout << "FactorRelativePosition2dWithExtrinsics::Weighted Jacobian(c" << id() << ") = \n " << J << std::endl; +// std::cout << "FactorRelativePosition2dWithExtrinsics::Sqrt Info(c" << id() << ") = \n" << getMeasurementSquareRootInformationUpper() << std::endl; +// } + //////////////////////////////////////////////////////// + + return true; +} + +} // namespace wolf + +#endif diff --git a/include/core/problem/problem.h b/include/core/problem/problem.h index 46500c6e225e5f2f07928c2656d950a1462749ad..d2de44d0b38a58d5313a8f07574755e4866260d6 100644 --- a/include/core/problem/problem.h +++ b/include/core/problem/problem.h @@ -62,6 +62,12 @@ struct PriorOptions }; WOLF_STRUCT_PTR_TYPEDEFS(PriorOptions); +struct Reference +{ + StateBlockPtr p_ptr; + StateBlockPtr o_ptr; +}; + /** \brief Wolf problem node element in the Wolf Tree */ class Problem : public std::enable_shared_from_this<Problem> @@ -90,6 +96,8 @@ class Problem : public std::enable_shared_from_this<Problem> VectorComposite transformation_; mutable std::mutex mut_transform_; + Reference local_reference_; + private: // CAUTION: THESE METHODS ARE PRIVATE, DO NOT MAKE THEM PUBLIC !! Problem(const std::string& _frame_structure, SizeEigen _dim, MapBasePtr _map); // USE create() below !! void setup(); @@ -105,6 +113,10 @@ class Problem : public std::enable_shared_from_this<Problem> public: SizeEigen getDim() const; const StateStructure& getFrameStructure() const; + StateBlockPtr getLocalReferenceP(); + StateBlockConstPtr getLocalReferenceP() const; + StateBlockPtr getLocalReferenceO(); + StateBlockConstPtr getLocalReferenceO() const; protected: void appendToStructure(const StateStructure& _structure); @@ -453,6 +465,26 @@ class Problem : public std::enable_shared_from_this<Problem> namespace wolf { +inline StateBlockPtr Problem::getLocalReferenceP() +{ + return local_reference_.p_ptr; +} + +inline StateBlockConstPtr Problem::getLocalReferenceP() const +{ + return local_reference_.p_ptr; +} + +inline StateBlockPtr Problem::getLocalReferenceO() +{ + return local_reference_.o_ptr; +} + +inline StateBlockConstPtr Problem::getLocalReferenceO() const +{ + return local_reference_.o_ptr; +} + inline TreeManagerBaseConstPtr Problem::getTreeManager() const { return tree_manager_; diff --git a/include/core/utils/utils_gtest.h b/include/core/utils/utils_gtest.h index b89b9102ab64073b729744ec5d6c7d4d8098b143..905f25c8b26a3fa1caa79cb6e72be4f6d07cfcc1 100644 --- a/include/core/utils/utils_gtest.h +++ b/include/core/utils/utils_gtest.h @@ -19,16 +19,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // //--------LICENSE_END-------- -/** - * \file utils_gtest.h - * \brief Some utils for gtest - * \author Jeremie Deray - * Created on: 26/09/2016 - * Eigen macros extension by: Joan Sola on 26/04/2017 - */ - -#ifndef WOLF_UTILS_GTEST_H -#define WOLF_UTILS_GTEST_H +#pragma once #include <gtest/gtest.h> @@ -144,24 +135,58 @@ TEST(Test, Foo) }, \ C_expect, C_actual); -#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) +#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::Quaterniond lhs, const Eigen::Quaterniond rhs) { \ + return lhs.angularDistance(rhs) < precision; \ + }, \ + C_expect, C_actual); + +#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::Quaterniond lhs, const Eigen::Quaterniond rhs) { \ + return lhs.angularDistance(rhs) < precision; \ + }, \ + C_expect, C_actual); -#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) +#define EXPECT_QUATERNION_VECTOR_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \ + }, \ + C_expect, C_actual); -#define EXPECT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ - MatrixXd er = lhs - rhs; \ +#define ASSERT_QUATERNION_VECTOR_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \ + }, \ + C_expect, C_actual); + +#define EXPECT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + assert(lhs.size() == 3 and rhs.size() == 3); \ + Eigen::VectorXd er = lhs - rhs; \ er(2) = pi2pi((double)er(2)); \ return er.isMuchSmallerThan(1, precision); \ }, \ C_expect, C_actual); -#define ASSERT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \ - MatrixXd er = lhs - rhs; \ +#define ASSERT_POSE2d_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + assert(lhs.size() == 3 and rhs.size() == 3); \ + Eigen::VectorXd er = lhs - rhs; \ er(2) = pi2pi((double)er(2)); \ return er.isMuchSmallerThan(1, precision); \ }, \ C_expect, C_actual); -} // namespace testing +#define EXPECT_POSE3d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + assert(lhs.size() == 7 and rhs.size() == 7); \ + Eigen::Vector4d er; \ + er.head(3) = lhs.head(3) - rhs.head(3); \ + er(3) = Eigen::Quaterniond(Eigen::Vector4d(lhs.tail(4))).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs.tail(4)))); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define ASSERT_POSE3d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \ + assert(lhs.size() == 7 and rhs.size() == 7); \ + Eigen::Vector4d er; \ + er.head(3) = lhs.head(3) - rhs.head(3); \ + er(3) = Eigen::Quaterniond(Eigen::Vector4d(lhs.tail(4))).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs.tail(4)))); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); -#endif /* WOLF_UTILS_GTEST_H */ +} // namespace testing \ No newline at end of file diff --git a/src/map/map_base.cpp b/src/map/map_base.cpp index 66367305a0b9ad82e9ef5987c3bdacb9e9a1eeea..b629aba0805d22efc6b23b72e0057ffcc6a30643 100644 --- a/src/map/map_base.cpp +++ b/src/map/map_base.cpp @@ -69,17 +69,12 @@ void MapBase::load(const std::string& _map_file_dot_yaml) { YAML::Node map = YAML::LoadFile(_map_file_dot_yaml); - unsigned int nlandmarks = map["nlandmarks"].as<unsigned int>(); - - assert(nlandmarks == map["landmarks"].size() && "Number of landmarks in map file does not match!"); - - for (unsigned int i = 0; i < nlandmarks; i++) + for (unsigned int i = 0; i < map["landmarks"].size(); i++) { YAML::Node lmk_node = map["landmarks"][i]; LandmarkBasePtr lmk_ptr = FactoryLandmark::create(lmk_node["type"].as<std::string>(), lmk_node); lmk_ptr->link(shared_from_this()); } - } void MapBase::save(const std::string& _map_file_yaml, const std::string& _map_name) @@ -87,10 +82,8 @@ void MapBase::save(const std::string& _map_file_yaml, const std::string& _map_na YAML::Emitter emitter; emitter << YAML::BeginMap; - emitter << "map name" << _map_name; - emitter << "date-time" << dateTimeNow(); // Get date and time for archiving purposes - - emitter << "nlandmarks" << getLandmarkList().size(); + emitter << "map_name" << _map_name; + emitter << "date_time" << dateTimeNow(); // Get date and time for archiving purposes emitter << "landmarks" << YAML::BeginSeq; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6fa50395a7ee2af50d290b32e543b18bac534869..812ee1f8e4233825bb78aae2029cbd349c34afc1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -172,6 +172,7 @@ wolf_add_gtest(gtest_factor_relative_pose_3d gtest_factor_relative_pose_3d.cpp) # FactorRelativePose3dWithExtrinsics class test wolf_add_gtest(gtest_factor_relative_pose_3d_with_extrinsics gtest_factor_relative_pose_3d_with_extrinsics.cpp) +wolf_add_gtest(gtest_factor_relative_pose_3d_with_extrinsics_new gtest_factor_relative_pose_3d_with_extrinsics_new.cpp) # FactorVelocityLocalDirection3d class test wolf_add_gtest(gtest_factor_velocity_local_direction_3d gtest_factor_velocity_local_direction_3d.cpp) diff --git a/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp b/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp index ee2bc076da1c48876250197c2fa1179ede489306..76744a478fddc50146f3bea9b3598803cad93b46 100644 --- a/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp +++ b/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp @@ -25,8 +25,9 @@ #include "core/factor/factor_relative_pose_2d_with_extrinsics.h" #include "core/capture/capture_odom_2d.h" #include "core/sensor/sensor_odom_2d.h" -#include "core/processor/processor_odom_2d.h" #include "core/math/rotations.h" +#include "core/state_block/state_block_derived.h" +#include "core/state_block/state_angle.h" using namespace Eigen; @@ -36,6 +37,8 @@ using std::endl; std::string wolf_root = _WOLF_ROOT_DIR; +int N = 1;//1e2 + // Input odometry data and covariance Matrix3d data_cov = 0.1*Matrix3d::Identity(); @@ -45,13 +48,18 @@ SolverCeres solver(problem_ptr); // Sensor auto sensor_odom2d = problem_ptr->installSensor("SensorOdom2d", "odom", Vector3d(0,0,0), wolf_root + "/test/yaml/sensor_odom_2d.yaml"); -auto processor_odom2d = problem_ptr->installProcessor("ProcessorOdom2d", "odom", sensor_odom2d, std::make_shared<ParamsProcessorOdom2d>()); // Two frames FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, Vector3d::Zero() ); FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, Vector3d::Zero() ); -// Capture from frm1 to frm0 +// Landmark +LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), + "LandmarkPose2d", + std::make_shared<StatePoint2d>(Vector2d::Zero()), + std::make_shared<StateAngle>(Vector1d::Zero())); + +// Capture auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, sensor_odom2d, Vector3d::Zero(), data_cov); TEST(FactorRelativePose2dWithExtrinsics, check_tree) @@ -59,9 +67,10 @@ TEST(FactorRelativePose2dWithExtrinsics, check_tree) ASSERT_TRUE(problem_ptr->check(0)); } -TEST(FactorRelativePose2dWithExtrinsics, fix_0_solve) +// FRAME TO FRAME ========================================================================= +TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame1) { - for (int i = 0; i < 1e2; i++) + for (int i = 0; i < N; i++) { // Random delta Vector3d delta = Vector3d::Random() * 10; @@ -88,7 +97,7 @@ TEST(FactorRelativePose2dWithExtrinsics, fix_0_solve) // feature & factor with delta measurement auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov); - FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add // Perturb frm1, fix the rest frm0->fix(); @@ -107,9 +116,9 @@ TEST(FactorRelativePose2dWithExtrinsics, fix_0_solve) } } -TEST(FactorRelativePose2dWithExtrinsics, fix_1_solve) +TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame0) { - for (int i = 0; i < 1e2; i++) + for (int i = 0; i < N; i++) { // Random delta Vector3d delta = Vector3d::Random() * 10; @@ -136,7 +145,7 @@ TEST(FactorRelativePose2dWithExtrinsics, fix_1_solve) // feature & factor with delta measurement auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov); - FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add // Perturb frm0, fix the rest frm1->fix(); @@ -155,9 +164,9 @@ TEST(FactorRelativePose2dWithExtrinsics, fix_1_solve) } } -TEST(FactorRelativePose2dWithExtrinsics, extrinsics_p_solve) +TEST(FactorRelativePose2dWithExtrinsics, frame_solve_extrinsics_p) { - for (int i = 0; i < 1e2; i++) + for (int i = 0; i < N; i++) { // Random delta Vector3d delta = Vector3d::Random() * 10; @@ -184,7 +193,7 @@ TEST(FactorRelativePose2dWithExtrinsics, extrinsics_p_solve) // feature & factor with delta measurement auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov); - FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add // Perturb sensor P, fix the rest frm1->fix(); @@ -203,9 +212,9 @@ TEST(FactorRelativePose2dWithExtrinsics, extrinsics_p_solve) } } -TEST(FactorRelativePose2dWithExtrinsics, extrinsics_o_solve) +TEST(FactorRelativePose2dWithExtrinsics, frame_solve_extrinsics_o) { - for (int i = 0; i < 1e2; i++) + for (int i = 0; i < N; i++) { // Random delta Vector3d delta = Vector3d::Random() * 10; @@ -232,7 +241,7 @@ TEST(FactorRelativePose2dWithExtrinsics, extrinsics_o_solve) // feature & factor with delta measurement auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov); - FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add // Perturb sensor O, fix the rest frm1->fix(); @@ -253,6 +262,201 @@ TEST(FactorRelativePose2dWithExtrinsics, extrinsics_o_solve) } } +// FRAME TO LANDMARK ========================================================================= +TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_lmk) +{ + for (int i = 0; i < N; i++) + { + // Random delta + Vector3d delta = Vector3d::Random() * 10; + pi2pi(delta(2)); + + // Random frame pose + Vector3d x_f = Vector3d::Random() * 10; + pi2pi(x_f(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // landmark pose + Vector3d x_l; + x_l(2) = pi2pi(x_f(2) + delta(2)); + x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()); + + // Set states + frm1->setState(x_f); + lmk->setState(x_l); + cap1->setData(delta); + sensor_odom2d->setState(xs); + + // feature & factor with delta measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov); + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add + + // Perturb landmark, fix the rest + frm1->fix(); + lmk->unfix(); + sensor_odom2d->getP()->fix(); + sensor_odom2d->getO()->fix(); + lmk->perturb(5); + + // solve for landmark + std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF); + + ASSERT_POSE2d_APPROX(lmk->getStateVector(), x_l, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_frame) +{ + for (int i = 0; i < N; i++) + { + // Random delta + Vector3d delta = Vector3d::Random() * 10; + pi2pi(delta(2)); + + // Random frame pose + Vector3d x_f = Vector3d::Random() * 10; + pi2pi(x_f(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // landmark pose + Vector3d x_l; + x_l(2) = pi2pi(x_f(2) + delta(2)); + x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()); + + // Set states + frm1->setState(x_f); + lmk->setState(x_l); + cap1->setData(delta); + sensor_odom2d->setState(xs); + + // feature & factor with delta measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov); + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add + + // Perturb frm0, fix the rest + frm1->unfix(); + lmk->fix(); + sensor_odom2d->getP()->fix(); + sensor_odom2d->getO()->fix(); + frm1->perturb(5); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF); + + ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_p_solve) +{ + for (int i = 0; i < N; i++) + { + // Random delta + Vector3d delta = Vector3d::Random() * 10; + pi2pi(delta(2)); + + // Random frame pose + Vector3d x_f = Vector3d::Random() * 10; + pi2pi(x_f(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // landmark pose + Vector3d x_l; + x_l(2) = pi2pi(x_f(2) + delta(2)); + x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()); + + // Set states + frm1->setState(x_f); + lmk->setState(x_l); + cap1->setData(delta); + sensor_odom2d->setState(xs); + + // feature & factor with delta measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov); + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add + + // Perturb sensor P, fix the rest + frm1->fix(); + lmk->fix(); + sensor_odom2d->getP()->unfix(); + sensor_odom2d->getO()->fix(); + sensor_odom2d->getP()->perturb(1); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF); + + ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_o_solve) +{ + for (int i = 0; i < N; i++) + { + // Random delta + Vector3d delta = Vector3d::Random() * 10; + pi2pi(delta(2)); + + // Random frame pose + Vector3d x_f = Vector3d::Random() * 10; + pi2pi(x_f(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // landmark pose + Vector3d x_l; + x_l(2) = pi2pi(x_f(2) + delta(2)); + x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()); + + // Set states + frm1->setState(x_f); + lmk->setState(x_l); + cap1->setData(delta); + sensor_odom2d->setState(xs); + + // feature & factor with delta measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov); + FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add + + // Perturb sensor O, fix the rest + frm1->fix(); + lmk->fix(); + sensor_odom2d->getP()->fix(); + sensor_odom2d->getO()->unfix(); + sensor_odom2d->getO()->perturb(.2); + + //std::cout << "Sensor O (perturbed): " << sensor_odom2d->getO()->getState().transpose() << std::endl; + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF); + + ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); diff --git a/test/gtest_factor_relative_pose_3d_with_extrinsics_new.cpp b/test/gtest_factor_relative_pose_3d_with_extrinsics_new.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b83aa4a8d014415fbd80405c47197e6eddaaba5 --- /dev/null +++ b/test/gtest_factor_relative_pose_3d_with_extrinsics_new.cpp @@ -0,0 +1,377 @@ + //--------LICENSE_START-------- + // + // Copyright (C) 2020,2021,2022 Institut de Robòtica i Informà tica Industrial, CSIC-UPC. + // Authors: Joan Solà Ortega (jsola@iri.upc.edu) + // All rights reserved. + // + // This file is part of WOLF + // WOLF is free software: you can redistribute it and/or modify + // it under the terms of the GNU Lesser General Public License as published by + // the Free Software Foundation, either version 3 of the License, or + // at your option) any later version. + // + // This program is distributed in the hope that it will be useful, + // but WITHOUT ANY WARRANTY; without even the implied warranty of + // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + // GNU Lesser General Public License for more details. + // + // You should have received a copy of the GNU Lesser General Public License + // along with this program. If not, see <http://www.gnu.org/licenses/>. + // + //--------LICENSE_END-------- + #include "core/utils/utils_gtest.h" + + #include "core/ceres_wrapper/solver_ceres.h" + #include "core/factor/factor_relative_pose_3d_with_extrinsics.h" + #include "core/capture/capture_odom_3d.h" + #include "core/sensor/sensor_odom_3d.h" + #include "core/math/rotations.h" + #include "core/state_block/state_block_derived.h" + #include "core/state_block/state_quaternion.h" + + + using namespace Eigen; + using namespace wolf; + using std::cout; + using std::endl; + + std::string wolf_root = _WOLF_ROOT_DIR; + + int N = 1;//1e2 + + // Vectors + Vector7d delta, x_0, x_1, x_f, x_l, x_s; + + // Input odometry data and covariance + Matrix6d data_cov = (Vector6d() << 1e-3, 1e-3, 1e-3, 1e-6, 1e-6, 1e-6).finished().asDiagonal(); + + // Problem and solver + ProblemPtr problem_ptr = Problem::create("PO", 3); + + SolverCeres solver(problem_ptr); + + // Sensor + auto sensor_odom3d = problem_ptr->installSensor("SensorOdom3d", "odom", (Vector7d() << 0,0,0,0,0,0,1).finished(), wolf_root + "/test/yaml/sensor_odom_3d.yaml"); + + // Two frames + FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, (Vector7d() << 0,0,0,0,0,0,1).finished() ); + FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, (Vector7d() << 0,0,0,0,0,0,1).finished() ); + + // Landmark + LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), + "LandmarkPose3d", + std::make_shared<StatePoint3d>(Vector3d::Zero()), + std::make_shared<StateQuaternion>(Quaterniond::Identity().coeffs())); + + // Capture + auto cap1 = CaptureBase::emplace<CaptureOdom3d>(frm1, 1, sensor_odom3d, Vector7d::Zero(), data_cov); + // Feature + FeatureBasePtr fea = nullptr; + // Factor + FactorRelativePose3dWithExtrinsicsPtr fac = nullptr; + + void generateRandomProblemFrame() + { + // Random delta + delta = Vector7d::Random() * 1e2; + delta.tail<4>().normalize(); + auto q_delta = Quaterniond(delta.tail<4>()); + + // Random frame 0 pose + x_0 = Vector7d::Random() * 1e2; + x_0.tail<4>().normalize(); + auto q_0 = Quaterniond(x_0.tail<4>()); + + // Random extrinsics + x_s = Vector7d::Random() * 1e2; + x_s.tail<4>().normalize(); + auto q_s = Quaterniond(x_s.tail<4>()); + + // Random frame 1 pose + auto q_1 = q_0 * q_s * q_delta * q_s.conjugate(); + x_1.head<3>() = x_0.head<3>() + q_0 * (x_s.head<3>() + q_s * delta.head<3>()) - q_1 * x_s.head<3>(); + x_1.tail<4>() = q_1.coeffs(); + + WOLF_INFO("x_0: ", x_0.transpose()); + WOLF_INFO("x_s: ", x_s.transpose()); + WOLF_INFO("delta: ", delta.transpose()); + WOLF_INFO("x_1: ", x_1.transpose()); + + // Set states + frm0->setState(x_0); + frm1->setState(x_1); + cap1->setData(delta); + sensor_odom3d->setState(x_s); + + // feature & factor with delta measurement + fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom3d", delta, data_cov); + fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add + } + + void generateRandomProblemLandmark() + { + // Random delta + delta = Vector7d::Random() * 10; + delta.tail<4>().normalize(); + auto q_delta = Quaterniond(delta.tail<4>()); + + // Random frame pose + x_f = Vector7d::Random() * 10; + x_f.tail<4>().normalize(); + auto q_f = Quaterniond(x_f.tail<4>()); + + // Random extrinsics + x_s = Vector7d::Random() * 10; + x_s.tail<4>().normalize(); + auto q_s = Quaterniond(x_s.tail<4>()); + + // landmark pose + x_l.head<3>() = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta.head<3>()); + x_l.tail<4>() = (q_f * q_s * q_delta).coeffs(); + + // Set states + frm1->setState(x_f); + lmk->setState(x_l); + cap1->setData(delta); + sensor_odom3d->setState(x_s); + + // feature & factor with delta measurement + fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov); + fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add + } + + TEST(FactorRelativePose3dWithExtrinsics, check_tree) + { + ASSERT_TRUE(problem_ptr->check(0)); + } + + // FRAME TO FRAME ========================================================================= + TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame1) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemFrame(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb frm1, fix the rest + frm0->fix(); + frm1->unfix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->fix(); + frm1->perturb(1); + + // solve for frm1 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_1, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame0) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemFrame(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb frm0, fix the rest + frm1->fix(); + frm0->unfix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->fix(); + frm0->perturb(1); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(frm0->getStateVector(), x_0, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_p) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemFrame(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb sensor P, fix the rest + frm1->fix(); + frm0->fix(); + sensor_odom3d->getP()->unfix(); + sensor_odom3d->getO()->fix(); + WOLF_INFO("sensor P before perturbing: ", sensor_odom3d->getP()->getState().transpose()); + sensor_odom3d->getP()->perturb(1); + WOLF_INFO("sensor P after perturbing: ", sensor_odom3d->getP()->getState().transpose()); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO("sensor P after solving: ", sensor_odom3d->getP()->getState().transpose()); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_o) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemFrame(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb sensor O, fix the rest + frm1->fix(); + frm0->fix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->unfix(); + sensor_odom3d->getO()->perturb(.2); + + //std::cout << "Sensor O (perturbed): " << sensor_odom3d->getO()->getState().transpose() << std::endl; + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + // FRAME TO LANDMARK ========================================================================= + TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_lmk) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemLandmark(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb landmark, fix the rest + frm1->fix(); + lmk->unfix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->fix(); + lmk->perturb(1); + + // solve for landmark + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(lmk->getStateVector(), x_l, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_frame) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemLandmark(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb frm0, fix the rest + frm1->unfix(); + lmk->fix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->fix(); + frm1->perturb(1); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_f, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_p_solve) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemLandmark(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb sensor P, fix the rest + frm1->fix(); + lmk->fix(); + sensor_odom3d->getP()->unfix(); + sensor_odom3d->getO()->fix(); + sensor_odom3d->getP()->perturb(1); + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_o_solve) + { + for (int i = 0; i < N; i++) + { + // random setup + generateRandomProblemLandmark(); + + ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS); + + // Perturb sensor O, fix the rest + frm1->fix(); + lmk->fix(); + sensor_odom3d->getP()->fix(); + sensor_odom3d->getO()->unfix(); + sensor_odom3d->getO()->perturb(.2); + + //std::cout << "Sensor O (perturbed): " << sensor_odom3d->getO()->getState().transpose() << std::endl; + + // solve for frm0 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-6); + + // remove feature (and factor) for the next loop + fea->remove(); + } + } + + int main(int argc, char **argv) + { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + } diff --git a/test/gtest_map_yaml.cpp b/test/gtest_map_yaml.cpp index 9ca7544ee6c9f28013f4037642de45504eca0b92..f0957a5abd90436d96d6e3a4e2788ad2d5c54faa 100644 --- a/test/gtest_map_yaml.cpp +++ b/test/gtest_map_yaml.cpp @@ -40,6 +40,9 @@ #include <iostream> using namespace wolf; +std::string wolf_root = _WOLF_ROOT_DIR; +std::string map_path = wolf_root + "/test/yaml/maps"; + TEST(MapYaml, save_2d) { ProblemPtr problem = Problem::create("PO", 2); @@ -62,9 +65,6 @@ TEST(MapYaml, save_2d) LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p2_sb, o2_sb); LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p3_sb, o3_sb); - std::string wolf_root = _WOLF_ROOT_DIR; - std::string map_path = wolf_root + "/test/yaml"; - // Check Problem functions std::string filename = map_path + "/map_2d_problem.yaml"; std::cout << "Saving map to file: " << filename << std::endl; @@ -80,89 +80,54 @@ TEST(MapYaml, load_2d) { ProblemPtr problem = Problem::create("PO", 2); - std::string wolf_root = _WOLF_ROOT_DIR; - std::string map_path = wolf_root + "/test/yaml"; - - // Check Problem functions - std::string filename = map_path + "/map_2d_problem.yaml"; - std::cout << "Loading map to file: " << filename << std::endl; - problem->loadMap(filename); - - ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); + std::vector<std::string> maps{"map_2d_map", "map_2d_problem", "map_2d_manual"}; - for (auto lmk : problem->getMap()->getLandmarkList()) + for (auto map : maps) { - if (lmk->id() == 1) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() == nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - } - else if (lmk->id() == 2) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - ASSERT_FALSE(lmk->getO()->isFixed()); - } - else if (lmk->id() == 3) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12); - ASSERT_TRUE(lmk->getP()->isFixed()); - ASSERT_TRUE(lmk->getO()->isFixed()); - } - } - - // empty the map - { - auto lmk_list = problem->getMap()->getLandmarkList(); - for (auto lmk : lmk_list) - lmk->remove(); - } - ASSERT_TRUE(problem->getMap()->getLandmarkList().empty()); + std::string filename = map_path + "/" + map + ".yaml"; + std::cout << "Loading map to file: " << filename << std::endl; + problem->loadMap(filename); - // Check Map functions - filename = map_path + "/map_2d_map.yaml"; - std::cout << "Loading map to file: " << filename << std::endl; - problem->getMap()->load(filename); + ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); - ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); - - for (auto lmk : problem->getMap()->getLandmarkList()) - { - if (lmk->id() == 1) + for (auto lmk : problem->getMap()->getLandmarkList()) { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() == nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); + if (lmk->id() == 1) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() == nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12); + ASSERT_FALSE(lmk->getP()->isFixed()); + } + else if (lmk->id() == 2) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() != nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12); + ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12); + ASSERT_FALSE(lmk->getP()->isFixed()); + ASSERT_FALSE(lmk->getO()->isFixed()); + } + else if (lmk->id() == 3) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() != nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12); + ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12); + ASSERT_TRUE(lmk->getP()->isFixed()); + ASSERT_TRUE(lmk->getO()->isFixed()); + } } - else if (lmk->id() == 2) + // empty the map { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - ASSERT_FALSE(lmk->getO()->isFixed()); - } - else if (lmk->id() == 3) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12); - ASSERT_TRUE(lmk->getP()->isFixed()); - ASSERT_TRUE(lmk->getO()->isFixed()); + auto lmk_list = problem->getMap()->getLandmarkList(); + for (auto lmk : lmk_list) + lmk->remove(); } + ASSERT_TRUE(problem->getMap()->getLandmarkList().empty()); } } + TEST(MapYaml, save_3d) { ProblemPtr problem = Problem::create("PO", 3); @@ -185,9 +150,6 @@ TEST(MapYaml, save_3d) LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p2_sb, q2_sb); LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p3_sb, q3_sb); - std::string wolf_root = _WOLF_ROOT_DIR; - std::string map_path = wolf_root + "/test/yaml"; - // Check Problem functions std::string filename = map_path + "/map_3d_problem.yaml"; std::cout << "Saving map to file: " << filename << std::endl; @@ -203,99 +165,60 @@ TEST(MapYaml, load_3d) { ProblemPtr problem = Problem::create("PO", 3); - std::string wolf_root = _WOLF_ROOT_DIR; - std::string map_path = wolf_root + "/test/yaml"; - - // Check Problem functions - std::string filename = map_path + "/map_3d_problem.yaml"; - std::cout << "Loading map to file: " << filename << std::endl; - problem->loadMap(filename); - - ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); - - for (auto lmk : problem->getMap()->getLandmarkList()) - { - if (lmk->id() == 1) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() == nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - } - else if (lmk->id() == 2) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - ASSERT_FALSE(lmk->getO()->isFixed()); - ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); - } - else if (lmk->id() == 3) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12); - ASSERT_TRUE(lmk->getP()->isFixed()); - ASSERT_TRUE(lmk->getO()->isFixed()); - ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); - } - } + std::vector<std::string> maps{"map_3d_map", "map_3d_problem", "map_3d_manual"}; - // empty the map + for (auto map : maps) { - auto lmk_list = problem->getMap()->getLandmarkList(); - for (auto lmk : lmk_list) - lmk->remove(); - } - ASSERT_TRUE(problem->getMap()->getLandmarkList().empty()); + std::string filename = map_path + "/" + map + ".yaml"; - // Check Map functions - filename = map_path + "/map_3d_map.yaml"; - std::cout << "Loading map to file: " << filename << std::endl; - problem->getMap()->load(filename); + std::cout << "Loading map to file: " << filename << std::endl; + problem->loadMap(filename); - ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); + ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3); - for (auto lmk : problem->getMap()->getLandmarkList()) - { - if (lmk->id() == 1) - { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() == nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - } - else if (lmk->id() == 2) + for (auto lmk : problem->getMap()->getLandmarkList()) { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12); - ASSERT_FALSE(lmk->getP()->isFixed()); - ASSERT_FALSE(lmk->getO()->isFixed()); - ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); + if (lmk->id() == 1) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() == nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12); + ASSERT_FALSE(lmk->getP()->isFixed()); + } + else if (lmk->id() == 2) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() != nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12); + ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12); + ASSERT_FALSE(lmk->getP()->isFixed()); + ASSERT_FALSE(lmk->getO()->isFixed()); + ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); + } + else if (lmk->id() == 3) + { + ASSERT_TRUE(lmk->getP() != nullptr); + ASSERT_TRUE(lmk->getO() != nullptr); + ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12); + ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12); + ASSERT_TRUE(lmk->getP()->isFixed()); + ASSERT_TRUE(lmk->getO()->isFixed()); + ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); + } } - else if (lmk->id() == 3) + + // empty the map { - ASSERT_TRUE(lmk->getP() != nullptr); - ASSERT_TRUE(lmk->getO() != nullptr); - ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12); - ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12); - ASSERT_TRUE(lmk->getP()->isFixed()); - ASSERT_TRUE(lmk->getO()->isFixed()); - ASSERT_TRUE(lmk->getO()->hasLocalParametrization()); + auto lmk_list = problem->getMap()->getLandmarkList(); + for (auto lmk : lmk_list) + lmk->remove(); } + ASSERT_TRUE(problem->getMap()->getLandmarkList().empty()); } } TEST(MapYaml, remove_map_files) { - std::string wolf_root = _WOLF_ROOT_DIR; - std::string map_path = wolf_root + "/test/yaml"; - std::string filename = map_path + "/map_2d_problem.yaml"; ASSERT_TRUE(std::remove(filename.c_str()) == 0); filename = map_path + "/map_2d_map.yaml"; diff --git a/test/yaml/maps/map_2d_manual.yaml b/test/yaml/maps/map_2d_manual.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a2276ad153b4adfbae3679d42b1aba801f15bb3 --- /dev/null +++ b/test/yaml/maps/map_2d_manual.yaml @@ -0,0 +1,13 @@ +map_name: 2d map saved from Map::save() +date_time: 21/09/2022 at 12:00:59 +useless_parameter: [1, 6, 3] +landmarks: + - + id: 1 + type: LandmarkBase + position: [1, 2] + position fixed: false + transformable: true + another_useless_param: "wtf" + - {id: 2, type: LandmarkBase, position: [3, 4], position fixed: false, orientation: [2], orientation fixed: false, transformable: true} + - {id: 3, type: LandmarkBase, position: [5, 6], position fixed: true, orientation: [3], orientation fixed: true, transformable: true} \ No newline at end of file diff --git a/test/yaml/maps/map_3d_manual.yaml b/test/yaml/maps/map_3d_manual.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4b66ed90f51224d03b70cf484ac333da74e777f4 --- /dev/null +++ b/test/yaml/maps/map_3d_manual.yaml @@ -0,0 +1,25 @@ +#map_name: 3d map saved from Problem::saveMap() +#date_time: 21/09/2022 at 12:00:59 +landmarks: + - + id: 10 + type: LandmarkBase + position: [1, 2, 3] + position fixed: false + transformable: true + - + id: 11 + type: LandmarkBase + position: [4, 5, 6] + position fixed: false + orientation: [0, 1, 0, 0] + orientation fixed: false + transformable: true + - + id: 12 + type: LandmarkBase + position: [7, 8, 9] + position fixed: true + orientation: [0, 0, 1, 0] + orientation fixed: true + transformable: true \ No newline at end of file