diff --git a/CMakeLists.txt b/CMakeLists.txt index 55c694583426d7ae5178cfa8328836779a1f3d6e..c94b33bfb04c9751c727f62bb16557cd1ef8f508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,7 @@ SET(HDRS_FACTOR include/core/factor/factor_relative_pose_2d_with_extrinsics.h include/core/factor/factor_relative_pose_3d.h include/core/factor/factor_pose_3d_with_extrinsics.h + include/core/factor/factor_relative_pose_3d_with_extrinsics.h include/core/factor/factor_velocity_local_direction_3d.h ) SET(HDRS_FEATURE 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 2247a898bb4d5030ed1b07ef6422d72a9ca968e8..61962bb3cdd1b29b76e48999868c76d85392a002 100644 --- a/include/core/factor/factor_relative_pose_2d_with_extrinsics.h +++ b/include/core/factor/factor_relative_pose_2d_with_extrinsics.h @@ -37,61 +37,104 @@ WOLF_PTR_TYPEDEFS(FactorRelativePose2dWithExtrinsics); class FactorRelativePose2dWithExtrinsics : public FactorAutodiff<FactorRelativePose2dWithExtrinsics, 3, 2, 1, 2, 1, 2, 1> { public: + /** \brief Class constructor Frame-Frame + */ FactorRelativePose2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, const FrameBasePtr& _frame_other_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function, const FactorTopology& _top, - FactorStatus _status = FAC_ACTIVE) : - FactorAutodiff<FactorRelativePose2dWithExtrinsics, 3, 2, 1, 2, 1, 2, 1>("FactorRelativePose2dWithExtrinsics", - _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(), - _ftr_ptr->getFrame()->getO(), - _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!"); - // - } + FactorStatus _status = FAC_ACTIVE); + + /** \brief Class constructor Frame-Landmark + */ + FactorRelativePose2dWithExtrinsics(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); ~FactorRelativePose2dWithExtrinsics() override = default; template<typename T> - bool operator ()(const T* const _p1, - const T* const _o1, - const T* const _p2, - const T* const _o2, - const T* const _sp, - const T* const _so, + bool 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; }; +FactorRelativePose2dWithExtrinsics::FactorRelativePose2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, + const FrameBasePtr& _frame_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status) : + FactorAutodiff("FactorRelativePose2dWithExtrinsics", + _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(), + _ftr_ptr->getFrame()->getO(), + _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!"); + // +} + +FactorRelativePose2dWithExtrinsics::FactorRelativePose2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, + const LandmarkBasePtr& _lmk_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status) : + FactorAutodiff("FactorRelativePose2dWithExtrinsics", + _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(), + _lmk_other_ptr->getO(), + _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 FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p1, - const T* const _o1, - const T* const _p2, - const T* const _o2, - const T* const _ps, - const T* const _os, +inline bool FactorRelativePose2dWithExtrinsics::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 { // MAPS Eigen::Map<Eigen::Matrix<T,3,1> > res(_residuals); - Eigen::Map<const Eigen::Matrix<T,2,1> > p1(_p1); - Eigen::Map<const Eigen::Matrix<T,2,1> > p2(_p2); - Eigen::Map<const Eigen::Matrix<T,2,1> > ps(_ps); - T o1 = _o1[0]; - T o2 = _o2[0]; - T os = _os[0]; + 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]; Eigen::Matrix<T, 3, 1> expected_measurement; Eigen::Matrix<T, 3, 1> er; // error @@ -112,8 +155,8 @@ inline bool FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p1, // 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>(-os)*(-ps + Eigen::Rotation2D<T>(-o1)*(-p1 + p2 + Eigen::Rotation2D<T>(o2)*ps)); - expected_measurement(2) = o2 - o1; + 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; // 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 new file mode 100644 index 0000000000000000000000000000000000000000..5f065f0bae985d849cdd6702ce823ef80bfc31d3 --- /dev/null +++ b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h @@ -0,0 +1,194 @@ +#ifndef _FACTOR_TELATIVE_POSE_3D_WITH_EXTRINSICS +#define _FACTOR_TELATIVE_POSE_3D_WITH_EXTRINSICS + +//Wolf includes +#include "core/common/wolf.h" +#include "core/math/rotations.h" +#include "core/factor/factor_autodiff.h" +#include "core/sensor/sensor_base.h" +#include "core/landmark/landmark_base.h" +#include "core/feature/feature_base.h" + +namespace wolf +{ + +WOLF_PTR_TYPEDEFS(FactorRelativePose3dWithExtrinsics); + +class FactorRelativePose3dWithExtrinsics : public FactorAutodiff<FactorRelativePose3dWithExtrinsics, 6, 3, 4, 3, 4, 3, 4> +{ + public: + + /** \brief Class constructor Frame-Frame + */ + FactorRelativePose3dWithExtrinsics(const FeatureBasePtr& _feature_ptr, + const FrameBasePtr& _frame_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status = FAC_ACTIVE); + /** \brief Class constructor Frame-Landmark + */ + FactorRelativePose3dWithExtrinsics(const FeatureBasePtr& _feature_ptr, + const LandmarkBasePtr& _landmark_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top = TOP_LMK, + FactorStatus _status = FAC_ACTIVE); + + /** \brief Class Destructor + */ + ~FactorRelativePose3dWithExtrinsics() override; + + /** brief : compute the residual from the state blocks being iterated by the solver. + **/ + template<typename T> + bool 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; + + Eigen::Vector6d residual() const; + double cost() const; + + // print function only for double (not Jet) + template<typename T, int Rows, int Cols> + void print(int kf, int lmk, const std::string s, const Eigen::Matrix<T, Rows, Cols> _M) const + { + // jet prints nothing + } + template<int Rows, int Cols> + void print(int kf, int lmk, const std::string s, const Eigen::Matrix<double, Rows, Cols> _M) const + { + // double prints stuff + WOLF_TRACE("KF", kf, " L", lmk, "; ", s, _M); + } +}; + +} // namespace wolf + +namespace wolf +{ + +FactorRelativePose3dWithExtrinsics::FactorRelativePose3dWithExtrinsics(const FeatureBasePtr& _feature_ptr, + const FrameBasePtr& _frame_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status) : + FactorAutodiff("FactorRelativePose3dWithExtrinsics", + _top, + _feature_ptr, + _frame_other_ptr, + nullptr, + nullptr, + nullptr, + _processor_ptr, + _apply_loss_function, + _status, + _frame_other_ptr->getP(), + _frame_other_ptr->getO(), + _feature_ptr->getFrame()->getP(), + _feature_ptr->getFrame()->getO(), + _feature_ptr->getCapture()->getSensorP(), + _feature_ptr->getCapture()->getSensorO()) +{ +} + +FactorRelativePose3dWithExtrinsics::FactorRelativePose3dWithExtrinsics(const FeatureBasePtr& _feature_ptr, + const LandmarkBasePtr& _landmark_other_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status) : + FactorAutodiff("FactorRelativePose3dWithExtrinsics", + _top, + _feature_ptr, + nullptr, + nullptr, + nullptr, + _landmark_other_ptr, + _processor_ptr, + _apply_loss_function, + _status, + _feature_ptr->getFrame()->getP(), + _feature_ptr->getFrame()->getO(), + _landmark_other_ptr->getP(), + _landmark_other_ptr->getO(), + _feature_ptr->getCapture()->getSensorP(), + _feature_ptr->getCapture()->getSensorO()) +{ +} + +FactorRelativePose3dWithExtrinsics::~FactorRelativePose3dWithExtrinsics() +{ + // +} + +template<typename T> +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 +{ + // 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_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<Eigen::Matrix<T,6,1>> residuals(_residuals); + + // 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); + + // Measurement + Eigen::Vector3d p_c_l_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 ) + + // Residual + residuals = getMeasurementSquareRootInformationUpper().cast<T>() * err; + + return true; +} + +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()); + + return res; +} + +double FactorRelativePose3dWithExtrinsics::cost() const +{ + return residual().squaredNorm(); +} + +} // namespace wolf + +#endif /* _FACTOR_TELATIVE_POSE_3D_WITH_EXTRINSICS */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 668be04a81a6dcdba8bfc793b28f663ac6b2aeb0..9479b901549a8777580ab7455507a4f9a83a9567 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -218,6 +218,10 @@ target_link_libraries(gtest_factor_relative_pose_2d_with_extrinsics ${PLUGIN_NAM wolf_add_gtest(gtest_factor_relative_pose_3d gtest_factor_relative_pose_3d.cpp) target_link_libraries(gtest_factor_relative_pose_3d ${PLUGIN_NAME}) +# FactorRelativePose3dWithExtrinsics class test +wolf_add_gtest(gtest_factor_relative_pose_3d_with_extrinsics gtest_factor_relative_pose_3d_with_extrinsics.cpp) +target_link_libraries(gtest_factor_relative_pose_3d_with_extrinsics ${PLUGIN_NAME}) + # FactorVelocityLocalDirection3d class test wolf_add_gtest(gtest_factor_velocity_local_direction_3d gtest_factor_velocity_local_direction_3d.cpp) target_link_libraries(gtest_factor_velocity_local_direction_3d ${PLUGIN_NAME}) diff --git a/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp b/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b57c7b8b71d69e3454fe84a4ca5ca0a5b17076a --- /dev/null +++ b/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp @@ -0,0 +1,304 @@ +#include <core/factor/factor_relative_pose_3d_with_extrinsics.h> +#include <core/utils/utils_gtest.h> + +#include "core/common/wolf.h" +#include "core/utils/logging.h" + +#include "core/state_block/state_quaternion.h" +#include "core/ceres_wrapper/solver_ceres.h" +#include "core/capture/capture_pose.h" +#include "core/feature/feature_pose.h" + + + +using namespace Eigen; +using namespace wolf; +using std::static_pointer_cast; + + + +// Use the following in case you want to initialize tests with predefines variables or methods. +class FactorRelativePose3dWithExtrinsics_class : public testing::Test{ + public: + Vector3d pos_camera, pos_robot, pos_landmark; + Vector3d euler_camera, euler_robot, euler_landmark; + Quaterniond quat_camera, quat_robot, quat_landmark; + Vector4d vquat_camera, vquat_robot, vquat_landmark; // quaternions as vectors + Vector7d pose_camera, pose_robot, pose_landmark; + + ProblemPtr problem; + SolverCeresPtr solver; + + SensorBasePtr S; + FrameBasePtr F1; + CapturePosePtr c1; + FeaturePosePtr f1; + LandmarkBasePtr lmk1; + FactorRelativePose3dWithExtrinsicsPtr fac; + + Eigen::Matrix6d meas_cov; + + void SetUp() override + { + // configuration + + /* We have three poses to take into account: + * - pose of the sensor (extrinsincs) + * - pose of the landmark + * - pose of the robot (Keyframe) + * + * The measurement provides the pose of the landmark wrt sensor's current pose in the world. + * Camera's current pose in World is the composition of the robot pose with the sensor extrinsics. + * + * The robot has a sensor looking forward + * S: pos = (0,0,0), ori = (0, 0, 0) + * + * There is a point at the origin + * P: pos = (0,0,0) + * + * Therefore, P projects exactly at the origin of the sensor, + * f: p = (0,0) + * + * We form a Wolf tree with 1 frames F1, 1 capture C1, + * 1 feature f1 (measurement), 1 landmark l and 1 relative kf landmark constraint c1: + * + * Frame F1, Capture C1, feature f1, landmark l1, constraint c1 + * + * The frame pose F1, and the sensor pose S + * in the robot frame are variables subject to optimization + * + * We perform a number of tests based on this configuration.*/ + + + // sensor is at origin of robot and looking forward + // robot is at (0,0,0) + // landmark is right in front of sensor. Its orientation wrt sensor is identity. + pos_camera << 0,0,0; + pos_robot << 0,0,0; //robot is at origin + pos_landmark << 0,1,0; + euler_camera << 0,0,0; + //euler_camera << -M_PI_2, 0, -M_PI_2; + euler_robot << 0,0,0; + euler_landmark << 0,0,0; + quat_camera = e2q(euler_camera); + quat_robot = e2q(euler_robot); + quat_landmark = e2q(euler_landmark); + vquat_camera = quat_camera.coeffs(); + vquat_robot = quat_robot.coeffs(); + vquat_landmark = quat_landmark.coeffs(); + pose_camera << pos_camera, vquat_camera; + pose_robot << pos_robot, vquat_robot; + pose_landmark << pos_landmark, vquat_landmark; + + // Build problem + problem = Problem::create("PO", 3); + solver = std::make_shared<SolverCeres>(problem); + + // Create sensor to be able to initialize (a camera for instance) + S = SensorBase::emplace<SensorBase>(problem->getHardware(), "SensorPose", + std::make_shared<StateBlock>(pos_camera, true), + std::make_shared<StateQuaternion>(vquat_camera, true), + std::make_shared<StateBlock>(Vector4d::Zero(), false), // fixed + Vector1d::Zero()); + + // ParamsSensorCameraPtr params_camera = std::make_shared<ParamsSensorCamera>(); + // S = problem->installSensor("SensorCamera", "canonical", pose_camera, params_camera); + // camera = std::static_pointer_cast<SensorCamera>(S); + + + // F1 is be origin KF + VectorComposite x0(pose_robot, "PO", {3,4}); + VectorComposite s0("PO", {Vector3d(1,1,1), Vector3d(1,1,1)}); + F1 = problem->setPriorFactor(x0, s0, 0.0, 0.1); + + + // the sensor is at origin as well as the robot. The measurement matches with the pose of the tag wrt sensor (and also wrt robot and world) + meas_cov = Eigen::Matrix6d::Identity(); + meas_cov.topLeftCorner(3,3) *= 1e-2; + meas_cov.bottomRightCorner(3,3) *= 1e-3; + + //emplace feature and landmark + c1 = std::static_pointer_cast<CapturePose>(CaptureBase::emplace<CapturePose>(F1, 0, S, pose_landmark, meas_cov)); + f1 = std::static_pointer_cast<FeaturePose>(FeatureBase::emplace<FeaturePose>(c1, pose_landmark, meas_cov)); + lmk1 = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkPose", + std::make_shared<StateBlock>(pos_landmark), + std::make_shared<StateQuaternion>(Quaterniond(vquat_landmark))); + } +}; + + +TEST_F(FactorRelativePose3dWithExtrinsics_class, Constructor) +{ + auto factor = std::make_shared<FactorRelativePose3dWithExtrinsics>(f1, + lmk1, + nullptr, + false); + + ASSERT_TRUE(factor->getType() == "FactorRelativePose3dWithExtrinsics"); +} + +///////////////////////////////////////////// +// Tree not ok with this gtest implementation +///////////////////////////////////////////// +TEST_F(FactorRelativePose3dWithExtrinsics_class, Check_tree) +{ + ASSERT_TRUE(problem->check(1)); + + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + ASSERT_TRUE(problem->check(1)); +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_F1_P_perturbated) +{ + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + // unfix F1, perturbate state + F1->unfix(); + F1->getP()->perturb(); + + std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6); +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_F1_O_perturbated) +{ + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + // unfix F1, perturbate state + F1->unfix(); + F1->getO()->perturb(); + + std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6); +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, Check_initialization) +{ + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6); + ASSERT_MATRIX_APPROX(f1->getMeasurement(), pose_landmark, 1e-6); + ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6); + +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_P_perturbated) +{ + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + // unfix lmk1, perturbate state + lmk1->unfix(); + lmk1->getP()->perturb(); + + std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6); + ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6); +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_O_perturbated) +{ + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + // unfix F1, perturbate state + lmk1->unfix(); + lmk1->getO()->perturb(); + + std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6); + ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6); + +} + +TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_PO_perturbated) +{ + // Change setup + Vector3d p_w_r, p_r_c, p_c_l, p_w_l; + Quaterniond q_w_r, q_r_c, q_c_l, q_w_l; + p_w_r << 1, 2, 3; + p_r_c << 4, 5, 6; + p_c_l << 7, 8, 9; + q_w_r.coeffs() << 1, 2, 3, 4; q_w_r.normalize(); + q_r_c.coeffs() << 4, 5, 6, 7; q_r_c.normalize(); + q_c_l.coeffs() << 7, 8, 9, 0; q_c_l.normalize(); + + q_w_l = q_w_r * q_r_c * q_c_l; + p_w_l = p_w_r + q_w_r * (p_r_c + q_r_c * p_c_l); + + // Change feature (remove and emplace) + Vector7d meas; + meas << p_c_l, q_c_l.coeffs(); + f1->remove(); + f1 = std::static_pointer_cast<FeaturePose>(FeatureBase::emplace<FeaturePose>(c1, meas, meas_cov)); + + // emplace factor + auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1, + f1, + lmk1, + nullptr, + false); + + // Change Landmark + lmk1->getP()->setState(p_w_l); + lmk1->getO()->setState(q_w_l.coeffs()); + ASSERT_TRUE(lmk1->getP()->stateUpdated()); + ASSERT_TRUE(lmk1->getO()->stateUpdated()); + + // Change Frame + F1->getP()->setState(p_w_r); + F1->getO()->setState(q_w_r.coeffs()); + F1->fix(); + ASSERT_TRUE(F1->getP()->stateUpdated()); + ASSERT_TRUE(F1->getO()->stateUpdated()); + + // Change sensor extrinsics + S->getP()->setState(p_r_c); + S->getO()->setState(q_r_c.coeffs()); + ASSERT_TRUE(S->getP()->stateUpdated()); + ASSERT_TRUE(S->getO()->stateUpdated()); + + Vector7d t_w_r, t_w_l; + t_w_r << p_w_r, q_w_r.coeffs(); + t_w_l << p_w_l, q_w_l.coeffs(); + ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), t_w_r, 1e-6); + ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), t_w_l, 1e-6); + + // unfix LMK, perturbate state + lmk1->unfix(); + lmk1->perturb(); + + std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport + ASSERT_MATRIX_APPROX(F1->getState().vector("PO").transpose(), t_w_r.transpose(), 1e-6); + ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO").transpose(), t_w_l.transpose(), 1e-6); + +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +