diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e62e51d0acafc4efed5b0d4eff606ec508531779..885c1f53a456deb4b74392d011d560cb7216bafe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -209,10 +209,6 @@ 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}) -# FactorPose3dWithExtrinsics class test -wolf_add_gtest(gtest_factor_pose_3d_with_extrinsics gtest_factor_pose_3d_with_extrinsics.cpp) -target_link_libraries(gtest_factor_pose_3d_with_extrinsics ${PLUGIN_NAME}) - # MakePosDef function test wolf_add_gtest(gtest_make_posdef gtest_make_posdef.cpp) target_link_libraries(gtest_make_posdef ${PLUGIN_NAME}) @@ -245,6 +241,11 @@ target_link_libraries(gtest_odom_2d ${PLUGIN_NAME}) wolf_add_gtest(gtest_processor_odom_3d gtest_processor_odom_3d.cpp) target_link_libraries(gtest_processor_odom_3d ${PLUGIN_NAME}) +# FactorPose3dWithExtrinsics class test +wolf_add_gtest(gtest_processor_and_factor_pose_3d_with_extrinsics gtest_processor_and_factor_pose_3d_with_extrinsics.cpp) +target_link_libraries(gtest_processor_and_factor_pose_3d_with_extrinsics ${PLUGIN_NAME}) + + # ProcessorTrackerFeatureDummy class test wolf_add_gtest(gtest_processor_tracker_feature_dummy gtest_processor_tracker_feature_dummy.cpp) target_link_libraries(gtest_processor_tracker_feature_dummy ${PLUGIN_NAME} dummy) diff --git a/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9146cc7e3f49d70a208439c83b709646828d924 --- /dev/null +++ b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp @@ -0,0 +1,329 @@ +/** + * \file gtest_factor_pose_with_extrinsics.cpp + * + * Created on: Feb a9, 2020 + * \author: mfourmy + */ + +#include "core/utils/utils_gtest.h" +#include "core/ceres_wrapper/solver_ceres.h" +#include "core/capture/capture_base.h" +#include "core/capture/capture_pose.h" +#include "core/sensor/sensor_pose.h" +#include "core/processor/processor_pose.h" +#include "core/factor/factor_relative_pose_3d.h" +#include "core/capture/capture_odom_3d.h" +#include "core/factor/factor_pose_3d_with_extrinsics.h" + + +using namespace Eigen; +using namespace wolf; +using std::cout; +using std::endl; + +const Vector3d zero3 = Vector3d::Zero(); + + + +class FactorPose3dWithExtrinsicsBase_Test : public testing::Test +{ + /** + Factor graph implemented common for all the tests + 2 KF is not enough since the SensorPose extrinsics translation would be unobservable along the first KF1-KF2 transformation axis of rotation + (KF1)----|Odom3d|----(KF2)----|Odom3d|----(KF3) + | | | + | | | + |Pose3dWE| |Pose3dWE| |Pose3dWE| + \ / + \ / + \____________(Extrinsics)_______________/ + */ + + // In this class, the poses of the 3 KF are chosen randomly and then measurement data for the factors are + // derived from random extrinsics + // The problem and Pose Sensor/Processor are implemented + + public: + + ProblemPtr problem_; + SolverCeresPtr solver_; + SensorBasePtr sensor_pose_; + FrameBasePtr KF1_; + FrameBasePtr KF2_; + FrameBasePtr KF3_; + + Vector7d pose1_; + Vector7d pose2_; + Vector7d pose3_; + Vector7d pose_12_; + Vector7d pose_23_; + Vector3d b_p_bm_; + Quaterniond b_q_m_; + + Vector7d pose1_meas_; + Vector7d pose2_meas_; + Vector7d pose3_meas_; + + void SetUp() override + { + // 2 random 3d positions + Vector3d w_p_wb1 = Vector3d::Random(); + Quaterniond w_q_b1 = Quaterniond::UnitRandom(); + Vector3d w_p_wb2 = Vector3d::Random(); + Quaterniond w_q_b2 = Quaterniond::UnitRandom(); + Vector3d w_p_wb3 = Vector3d::Random(); + Quaterniond w_q_b3 = Quaterniond::UnitRandom(); + + // pose vectors + pose1_ << w_p_wb1, w_q_b1.coeffs(); + pose2_ << w_p_wb2, w_q_b2.coeffs(); + pose3_ << w_p_wb3, w_q_b3.coeffs(); + + ///////////////////////////////////////// + // extrinsics ground truth and mocap data + // b_p_bm_ << 0.1,0.2,0.3; + b_p_bm_ << Vector3d::Random(); + b_q_m_ = Quaterniond::UnitRandom(); + + Vector3d w_p_wb1_meas = w_p_wb1 + w_q_b1 * b_p_bm_; + Vector3d w_p_wb2_meas = w_p_wb2 + w_q_b2 * b_p_bm_; + Vector3d w_p_wb3_meas = w_p_wb3 + w_q_b3 * b_p_bm_; + Quaterniond w_q_b1_meas = w_q_b1 * b_q_m_; + Quaterniond w_q_b2_meas = w_q_b2 * b_q_m_; + Quaterniond w_q_b3_meas = w_q_b3 * b_q_m_; + pose1_meas_ << w_p_wb1_meas, w_q_b1_meas.coeffs(); + pose2_meas_ << w_p_wb2_meas, w_q_b2_meas.coeffs(); + pose3_meas_ << w_p_wb3_meas, w_q_b3_meas.coeffs(); + + ///////////////////// + // relative odom data + Vector3d b1_p_b1b2 = w_q_b1.conjugate()*(w_p_wb2 - w_p_wb1); + Quaterniond b1_q_b2 = w_q_b1.conjugate()*w_q_b2; + pose_12_ << b1_p_b1b2, b1_q_b2.coeffs(); + + Vector3d b2_p_b2b3 = w_q_b2.conjugate()*(w_p_wb3 - w_p_wb2); + Quaterniond b2_q_b3 = w_q_b2.conjugate()*w_q_b3; + pose_23_ << b2_p_b2b3, b2_q_b3.coeffs(); + + // Problem and solver_ + problem_ = Problem::create("PO", 3); + solver_ = std::make_shared<SolverCeres>(problem_); + + // pose sensor and proc (to get extrinsics in the prb) + auto intr_sp = std::make_shared<ParamsSensorPose>(); + intr_sp->std_p = 1; + intr_sp->std_o = 1; + Vector7d extr; extr << b_p_bm_, b_q_m_.coeffs(); + sensor_pose_ = problem_->installSensor("SensorPose", "pose", extr, intr_sp); + auto params_proc = std::make_shared<ParamsProcessorPose>(); + auto proc_pose = problem_->installProcessor("ProcessorPose", "pose", sensor_pose_, params_proc); + } + + void TearDown() override{}; +}; + +class FactorPose3dWithExtrinsicsMANUAL_Test : public FactorPose3dWithExtrinsicsBase_Test +{ + public: + + void SetUp() override + { + FactorPose3dWithExtrinsicsBase_Test::SetUp(); + // Two frames + KF1_ = problem_->emplaceFrame(1, pose1_); + KF2_ = problem_->emplaceFrame(2, pose2_); + KF3_ = problem_->emplaceFrame(3, pose3_); + + /////////////////// + // Create factor graph + Matrix6d cov6d = sensor_pose_->getNoiseCov(); + // Odometry capture, feature and factor + auto cap_odom1 = CaptureBase::emplace<CapturePose>(KF2_, 2, nullptr, pose_12_, cov6d); + auto fea_odom1 = FeatureBase::emplace<FeatureBase>(cap_odom1, "odom", pose_12_, cov6d); + FactorRelativePose3dPtr fac_odomA = FactorBase::emplace<FactorRelativePose3d>(fea_odom1, fea_odom1, KF1_, nullptr, false, TOP_MOTION); + + auto cap_odom2 = CaptureBase::emplace<CapturePose>(KF3_, 3, nullptr, pose_23_, cov6d); + auto fea_odom2 = FeatureBase::emplace<FeatureBase>(cap_odom2, "odom", pose_23_, cov6d); + FactorRelativePose3dPtr fac_odom2 = FactorBase::emplace<FactorRelativePose3d>(fea_odom2, fea_odom2, KF2_, nullptr, false, TOP_MOTION); + + // Captures mocap + auto cap_mocap1 = CaptureBase::emplace<CapturePose>(KF1_, 1, sensor_pose_, pose1_meas_, cov6d); + auto fea_mocap1 = FeatureBase::emplace<FeatureBase>(cap_mocap1, "pose", pose1_meas_, cov6d); + FactorPose3dWithExtrinsicsPtr fac_mocap1 = FactorBase::emplace<FactorPose3dWithExtrinsics>(fea_mocap1, fea_mocap1, nullptr, false, TOP_MOTION); + + auto cap_mocap2 = CaptureBase::emplace<CapturePose>(KF2_, 2, sensor_pose_, pose2_meas_, cov6d); + auto fea_mocap2 = FeatureBase::emplace<FeatureBase>(cap_mocap2, "pose", pose2_meas_, cov6d); + FactorPose3dWithExtrinsicsPtr fac_mocap2 = FactorBase::emplace<FactorPose3dWithExtrinsics>(fea_mocap2, fea_mocap2, nullptr, false, TOP_MOTION); + + auto cap_mocap3 = CaptureBase::emplace<CapturePose>(KF3_, 3, sensor_pose_, pose3_meas_, cov6d); + auto fea_mocap3 = FeatureBase::emplace<FeatureBase>(cap_mocap3, "pose", pose3_meas_, cov6d); + FactorPose3dWithExtrinsicsPtr fac_mocap3 = FactorBase::emplace<FactorPose3dWithExtrinsics>(fea_mocap3, fea_mocap3, nullptr, false, TOP_MOTION); + + + } + + void TearDown() override{}; +}; + + +class FactorPose3dWithExtrinsics_ProcessorWithKeyFrameCallbackFirst_Test : public FactorPose3dWithExtrinsicsBase_Test +{ + public: + + void SetUp() override + { + FactorPose3dWithExtrinsicsBase_Test::SetUp(); + // Two frames + KF1_ = problem_->emplaceFrame(1, pose1_); + KF2_ = problem_->emplaceFrame(2, pose2_); + KF3_ = problem_->emplaceFrame(3, pose3_); + problem_->keyFrameCallback(KF1_, nullptr, 0.5); + problem_->keyFrameCallback(KF2_, nullptr, 0.5); + problem_->keyFrameCallback(KF3_, nullptr, 0.5); + + /////////////////// + // Create factor graph + Matrix6d cov6d = sensor_pose_->getNoiseCov(); + // Odometry capture, feature and factor + auto cap_odom1 = CaptureBase::emplace<CapturePose>(KF2_, 2, nullptr, pose_12_, cov6d); + auto fea_odom1 = FeatureBase::emplace<FeatureBase>(cap_odom1, "odom", pose_12_, cov6d); + FactorRelativePose3dPtr fac_odomA = FactorBase::emplace<FactorRelativePose3d>(fea_odom1, fea_odom1, KF1_, nullptr, false, TOP_MOTION); + + auto cap_odom2 = CaptureBase::emplace<CapturePose>(KF3_, 3, nullptr, pose_23_, cov6d); + auto fea_odom2 = FeatureBase::emplace<FeatureBase>(cap_odom2, "odom", pose_23_, cov6d); + FactorRelativePose3dPtr fac_odom2 = FactorBase::emplace<FactorRelativePose3d>(fea_odom2, fea_odom2, KF2_, nullptr, false, TOP_MOTION); + + // process mocap data + auto cap_mocap1 = std::make_shared<CapturePose>(1, sensor_pose_, pose1_meas_, cov6d); + auto cap_mocap2 = std::make_shared<CapturePose>(2, sensor_pose_, pose2_meas_, cov6d); + auto cap_mocap3 = std::make_shared<CapturePose>(3, sensor_pose_, pose3_meas_, cov6d); + cap_mocap1->process(); + cap_mocap2->process(); + cap_mocap3->process(); + + } + + void TearDown() override{}; +}; + + +class FactorPose3dWithExtrinsics_ProcessorWithProcessFirst_Test : public FactorPose3dWithExtrinsicsBase_Test +{ + public: + + void SetUp() override + { + FactorPose3dWithExtrinsicsBase_Test::SetUp(); + // Two frames + KF1_ = problem_->emplaceFrame(1, pose1_); + KF2_ = problem_->emplaceFrame(2, pose2_); + KF3_ = problem_->emplaceFrame(3, pose3_); + + Matrix6d cov6d = sensor_pose_->getNoiseCov(); + /////////////////// + // Create factor graph + // Odometry capture, feature and factor + auto cap_odom1 = CaptureBase::emplace<CapturePose>(KF2_, 2, nullptr, pose_12_, cov6d); + auto fea_odom1 = FeatureBase::emplace<FeatureBase>(cap_odom1, "odom", pose_12_, cov6d); + FactorRelativePose3dPtr fac_odomA = FactorBase::emplace<FactorRelativePose3d>(fea_odom1, fea_odom1, KF1_, nullptr, false, TOP_MOTION); + + auto cap_odom2 = CaptureBase::emplace<CapturePose>(KF3_, 3, nullptr, pose_23_, cov6d); + auto fea_odom2 = FeatureBase::emplace<FeatureBase>(cap_odom2, "odom", pose_23_, cov6d); + FactorRelativePose3dPtr fac_odom2 = FactorBase::emplace<FactorRelativePose3d>(fea_odom2, fea_odom2, KF2_, nullptr, false, TOP_MOTION); + + // process mocap data + auto cap_mocap1 = std::make_shared<CapturePose>(1, sensor_pose_, pose1_meas_, cov6d); + auto cap_mocap2 = std::make_shared<CapturePose>(2, sensor_pose_, pose2_meas_, cov6d); + auto cap_mocap3 = std::make_shared<CapturePose>(3, sensor_pose_, pose3_meas_, cov6d); + cap_mocap1->process(); + cap_mocap2->process(); + cap_mocap3->process(); + + // keyframe callback called after all mocap captures have been processed + problem_->keyFrameCallback(KF1_, nullptr, 0.5); + problem_->keyFrameCallback(KF2_, nullptr, 0.5); + problem_->keyFrameCallback(KF3_, nullptr, 0.5); + } + + void TearDown() override{}; +}; + + + + +TEST_F(FactorPose3dWithExtrinsicsMANUAL_Test, check_tree) +{ + ASSERT_TRUE(problem_->check(0)); +} + +TEST_F(FactorPose3dWithExtrinsicsMANUAL_Test, solve) +{ + // somehow by default the sensor extrinsics is fixed + // sensor_pose_->unfixExtrinsics(); + + problem_->perturb(); + + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + + problem_->print(4,1,1,1); + + SensorPosePtr sp = std::static_pointer_cast<SensorPose>(sensor_pose_); + ASSERT_MATRIX_APPROX(KF1_->getStateVector(), pose1_, 1e-6); + ASSERT_MATRIX_APPROX(KF2_->getStateVector(), pose2_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getP()->getState(), b_p_bm_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getO()->getState(), b_q_m_.coeffs(), 1e-6); + +} + + +TEST_F(FactorPose3dWithExtrinsics_ProcessorWithKeyFrameCallbackFirst_Test, check_tree) +{ + ASSERT_TRUE(problem_->check(0)); +} + +TEST_F(FactorPose3dWithExtrinsics_ProcessorWithKeyFrameCallbackFirst_Test, solve) +{ + // somehow by default the sensor extrinsics is fixed + sensor_pose_->unfixExtrinsics(); + + problem_->perturb(); + + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + + SensorPosePtr sp = std::static_pointer_cast<SensorPose>(sensor_pose_); + ASSERT_MATRIX_APPROX(KF1_->getStateVector(), pose1_, 1e-6); + ASSERT_MATRIX_APPROX(KF2_->getStateVector(), pose2_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getP()->getState(), b_p_bm_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getO()->getState(), b_q_m_.coeffs(), 1e-6); + +} + + +TEST_F(FactorPose3dWithExtrinsics_ProcessorWithProcessFirst_Test, check_tree) +{ + ASSERT_TRUE(problem_->check(0)); +} + +TEST_F(FactorPose3dWithExtrinsics_ProcessorWithProcessFirst_Test, solve) +{ + // somehow by default the sensor extrinsics is fixed + sensor_pose_->unfixExtrinsics(); + + problem_->perturb(); + + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + + SensorPosePtr sp = std::static_pointer_cast<SensorPose>(sensor_pose_); + ASSERT_MATRIX_APPROX(KF1_->getStateVector(), pose1_, 1e-6); + ASSERT_MATRIX_APPROX(KF2_->getStateVector(), pose2_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getP()->getState(), b_p_bm_, 1e-6); + ASSERT_MATRIX_APPROX(sp->getO()->getState(), b_q_m_.coeffs(), 1e-6); +} + + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/test/gtest_factor_pose_3d_with_extrinsics.cpp b/test/gtest_processor_pose.cpp similarity index 100% rename from test/gtest_factor_pose_3d_with_extrinsics.cpp rename to test/gtest_processor_pose.cpp