diff --git a/demos/imu2d_static_init.yaml b/demos/imu2d_static_init.yaml deleted file mode 100644 index c30a7d38d2c1a5b997463c3ce41dc0b84233669d..0000000000000000000000000000000000000000 --- a/demos/imu2d_static_init.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: "ProcessorImu2d" # This must match the KEY used in the SensorFactory. Otherwise it is an error. - -time_tolerance: 0.0025 # Time tolerance for joining KFs -unmeasured_perturbation_std: 0.00001 - -keyframe_vote: - voting_active: true - voting_aux_active: false - max_time_span: 1.0 # seconds - max_buff_length: 20000 # motion deltas - dist_traveled: 999 # meters - angle_turned: 999 # radians (1 rad approx 57 deg, approx 60 deg) diff --git a/test/gtest_imu2d_static_init.cpp b/test/gtest_imu2d_static_init.cpp index fc472d915512f54e76e7839d01065bc5ea9fb1a1..b5324fbc9f89b395470479f5249b34ae0d9403ab 100644 --- a/test/gtest_imu2d_static_init.cpp +++ b/test/gtest_imu2d_static_init.cpp @@ -16,27 +16,23 @@ class ProcessorImu2dStaticInitTest : public testing::Test { public: //These can be accessed in fixtures - wolf::ProblemPtr _problem_ptr; - wolf::SensorBasePtr _sensor_ptr; - wolf::ProcessorMotionPtr _processor_motion; + wolf::ProblemPtr problem_ptr_; + wolf::SensorBasePtr sensor_ptr_; + wolf::ProcessorMotionPtr processor_motion_; wolf::TimeStamp t; wolf::TimeStamp t0; - double mti_clock, tmp; double dt; - bool second_frame; - bool third_frame; Vector6d data; Matrix6d data_cov; - VectorComposite x0c; // initial state composite - VectorComposite s0c; // initial sigmas composite - std::shared_ptr<wolf::CaptureImu> C; - FrameBasePtr _first_frame; - FrameBasePtr _last_frame; - SolverCeresPtr _solver; + FrameBasePtr KF0_; + FrameBasePtr first_frame_; + FrameBasePtr last_frame_; + SolverCeresPtr solver_; //a new of this will be created for each new test void SetUp() override { + WOLF_INFO("Doing setup..."); using namespace Eigen; using std::shared_ptr; using std::make_shared; @@ -46,218 +42,717 @@ class ProcessorImu2dStaticInitTest : public testing::Test std::string wolf_root = _WOLF_IMU_ROOT_DIR; // Wolf problem - _problem_ptr = Problem::create("POV", 2); - Vector3d extrinsics = (Vector3d() << 0,0, 0).finished(); - _sensor_ptr = _problem_ptr->installSensor("SensorImu2d", "Main Imu", extrinsics, wolf_root + "/demos/sensor_imu2d.yaml"); - ProcessorBasePtr processor_ptr = _problem_ptr->installProcessor("ProcessorImu2d", "Imu pre-integrator", "Main Imu", wolf_root + "/demos/imu2d_static_init.yaml"); - _processor_motion = std::static_pointer_cast<ProcessorMotion>(processor_ptr); + problem_ptr_ = Problem::create("POV", 2); + Vector3d extrinsics = (Vector3d() << 0,0,0).finished(); + sensor_ptr_ = problem_ptr_->installSensor("SensorImu2d", "Main Imu", extrinsics, wolf_root + "/test/yaml/sensor_imu2d_static_init.yaml"); + ProcessorBasePtr processor_ptr = problem_ptr_->installProcessor("ProcessorImu2d", "Imu pre-integrator", "Main Imu", wolf_root + "/test/yaml/imu2d_static_init.yaml"); + processor_motion_ = std::static_pointer_cast<ProcessorMotion>(processor_ptr); // Time and data variables - second_frame = false; - third_frame = false; - dt = 0.01; + dt = 0.1; t0.set(0); t = t0; data = Vector6d::Random(); data_cov = Matrix6d::Identity(); - _last_frame = nullptr; - - // Create one capture to store the Imu data arriving from (sensor / callback / file / etc.) - C = make_shared<CaptureImu>(t, _sensor_ptr, data, data_cov, Vector3d::Zero()); + last_frame_ = nullptr; + first_frame_ = nullptr; // Create the solver - _solver = make_shared<SolverCeres>(_problem_ptr); + solver_ = make_shared<SolverCeres>(problem_ptr_); + + // Set the origin + VectorComposite x_origin("POV", {Vector2d::Zero(), Vector1d::Zero(), Vector2d::Zero()}); + VectorComposite std_origin("POV", {0.001*Vector2d::Ones(), 0.001*Vector1d::Ones(), 0.001*Vector2d::Ones()}); + //KF0_ = problem_ptr_->setPriorFix(x_origin, t0, dt/2); + KF0_ = problem_ptr_->setPriorFactor(x_origin, std_origin, 0, 0.01); + } -}; + void TestStatic(const Vector3d& body_magnitudes, const Vector3d& bias_groundtruth, const Vector3d& bias_initial_guess, const string& test_name, int testing_var, bool small_tol) + { + //Data + data.head(2) = body_magnitudes.head(2); + data(5) = body_magnitudes(2); + data.head(2) += bias_groundtruth.head(2); + data(5) += bias_groundtruth(2); + + //Set bias initial guess + sensor_ptr_->getIntrinsic(t0)->setState(bias_initial_guess); + processor_motion_->setOrigin(KF0_); + +#if WRITE_CSV_FILE + std::fstream file_est; + file_est.open("./est2d-"+test_name+".csv", std::fstream::out); + // std::string header_est = "t,px,py,pz,qx,qy,qz,qw,vx,vy,vz,bax_est,bax_preint\n"; + std::string header_est; + if(testing_var == 0) header_est = "t;px;vx;o;bax_est;bg_est;bax_preint;bg_preint\n"; + if(testing_var == 1) header_est = "t;py;vy;o;bay_est;bg_est;bay_preint;bg_preint\n"; + //if(testing_var == 2) header_est = "t;pz;vz;oz:baz_est;bgz_est;baz_preint;bgz_preint\n"; + if(testing_var == 3) header_est = "t;pnorm;vnorm;o;banorm_est;bg_est;banorm_preint;bg_preint\n"; + file_est << header_est; +#endif + + + int n_frames = 0; + for(t = t0; t <= t0 + 9.9 + dt/2; t+=dt){ + WOLF_INFO("\n------------------------------------------------------------------------"); + + //Create and process capture + auto C = std::make_shared<CaptureImu>(t, sensor_ptr_, data, data_cov, KF0_->getCaptureList().front()); + C->process(); + + auto state = problem_ptr_->getState(); + auto bias_est = sensor_ptr_->getIntrinsic()->getState(); + auto bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + +#if WRITE_CSV_FILE + // pre-solve print to CSV + if(testing_var == 3){ + file_est << std::fixed << t << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'] << ";" + << bias_est.head(2).norm() << ";" + << bias_est(2) << ";" + << bias_preint.head(2).norm() << ";" + << bias_preint(2) << "\n"; + } + else + { + file_est << std::fixed << t << ";" + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'] << ";" + << bias_est(testing_var) << ";" + << bias_est(2) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(2) << "\n"; + + } +#endif + + // new frame + if (last_frame_ != processor_motion_->getOrigin()->getFrame()) + { + n_frames++; + last_frame_ = processor_motion_->getOrigin()->getFrame(); + + // impose static + last_frame_->getP()->setState(KF0_->getP()->getState()); + last_frame_->getO()->setState(KF0_->getO()->getState()); + last_frame_->getV()->setZero(); + + //Fix frame + last_frame_->fix(); + + //Solve + if(n_frames > 0) + { + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + //WOLF_INFO("Solver Report: ", report); + + state = problem_ptr_->getState(); + bias_est = sensor_ptr_->getIntrinsic()->getState(); + bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + + WOLF_INFO("The current problem is:"); + problem_ptr_->print(4); + +#if WRITE_CSV_FILE + // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result + if(testing_var == 3){ + file_est << std::fixed << t+dt/2 << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'] << ";" + << bias_est.head(2).norm() << ";" + << bias_est(2) << ";" + << bias_preint.head(2).norm() << ";" + << bias_preint(2) << "\n"; + } + else + { + file_est << std::fixed << t+dt/2 << ";" + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'] << ";" + << bias_est(testing_var) << ";" + << bias_est(2) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(2) << "\n"; + + } +#endif + } + + } + + + WOLF_INFO("Number of frames is ", n_frames); + WOLF_INFO("The state is: ", state); + WOLF_INFO("The estimated bias is: ", bias_est.transpose()); + WOLF_INFO("The preintegrated bias is: ", bias_preint.transpose()); + if(small_tol) + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-6); + } + } + else + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-3); + } + } + } +#if WRITE_CSV_FILE + file_est.close(); +#endif -/** - * SET TO TRUE TO PRODUCE CSV FILE - * SET TO FALSE TO STOP PRODUCING CSV FILE - */ -#define WRITE_CSV_FILE true + } -TEST_F(ProcessorImu2dStaticInitTest, static) -{ - // Set the origin - x0c['P'] = Vector2d(0, 0); - x0c['O'] = Vector1d(0); - x0c['V'] = Vector2d(0, 0); - - data_cov *= 1e-3; - data << 1, 2, 3, 4, 5, 6; - Vector3d bias_groundtruth; - bias_groundtruth << data(0), data(1), data(5); - //dt = 0.0001; - auto KF0 = _problem_ptr->setPriorFix(x0c, t0, dt/2); - KF0->fix(); - _processor_motion->setOrigin(KF0); - - WOLF_INFO("Data is: \n", data); - int size = 3; - second_frame = false; - _first_frame = nullptr; - - #if WRITE_CSV_FILE - std::fstream file_est; - file_est.open("./est.csv", std::fstream::out); - // std::string header_est = "t,px,py,pz,qx,qy,qz,qw,vx,vy,vz,bax_est,bax_preint\n"; - std::string header_est = "t;px;vx;bax_est;bax_preint\n"; - file_est << header_est; - #endif - - - int i = 1; - int n_frames = 0; - for(t = t0+dt; t <= t0 + 3.1 + dt/2; t+=dt){ - WOLF_INFO("Starting iteration ", i, " with timestamp ", t); - ++i; - C = std::make_shared<CaptureImu>(t, _sensor_ptr, data, data_cov, Vector3d::Zero()); - WOLF_INFO("Created new Capture"); - if(second_frame){ - //ASSERT_MATRIX_APPROX(_processor_motion->getState().vector("POV"), Vector5d::Zero(), 1e-9); - } - WOLF_INFO("Processing capture"); - C->process(); - WOLF_INFO("Doing the static initialization stuff"); - if (not _last_frame) - { - n_frames++; - _last_frame = _processor_motion->getOrigin()->getFrame(); - _first_frame = _last_frame; - - // fix Position and orientation - _last_frame->getP()->fix(); - _last_frame->getO()->fix(); - - // impose zero velocity - _last_frame->getV()->setZero(); - _last_frame->getV()->fix(); - - // impose zero odometry - _processor_motion->setOdometry(_sensor_ptr->getProblem()->stateZero(_processor_motion->getStateStructure())); - } - else - { - assert(_processor_motion->getOrigin() && "SubscriberImuEnableable::callback: nullptr origin"); - assert(_processor_motion->getOrigin()->getFrame() && "SubscriberImuEnableable::callback: nullptr origin frame"); - - // new frame - if (_last_frame != _processor_motion->getOrigin()->getFrame()) - { - n_frames++; - second_frame = true; - _last_frame = _processor_motion->getOrigin()->getFrame(); - - // fix Position and orientation - _last_frame->getP()->fix(); - _last_frame->getO()->fix(); - - // impose zero velocity - _last_frame->getV()->setZero(); - _last_frame->getV()->fix(); - - // impose zero odometry - _processor_motion->setOdometry(_sensor_ptr->getProblem()->stateZero(_processor_motion->getStateStructure())); - - // add zero displacement and rotation capture & feature & factor with all previous frames - assert(_sensor_ptr->getProblem()); - Eigen::VectorXd zero_motion = Eigen::VectorXd::Zero(size); - for (auto frm_pair = _sensor_ptr->getProblem()->getTrajectory()->begin(); - frm_pair != _sensor_ptr->getProblem()->getTrajectory()->end(); - frm_pair++) - { - if (frm_pair->second == _last_frame) - break; - auto capture_zero = CaptureBase::emplace<CaptureVoid>(_last_frame, _last_frame->getTimeStamp(), nullptr); - auto feature_zero = FeatureBase::emplace<FeatureBase>(capture_zero, - "FeatureZeroOdom", - zero_motion, - Eigen::MatrixXd::Identity(size,size) * 0.01); - - FactorBase::emplace<FactorRelativePose2d>(feature_zero, - feature_zero, - frm_pair->second, - nullptr, - false, - TOP_MOTION); - - } - } - } - WOLF_INFO("Static initialization done"); - if(_first_frame) - { - //WOLF_INFO("0 - The first frame is ", _first_frame->id(), " and it's currently estimated bias is: \n", _first_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); - //WOLF_INFO("its state vector is: \n", _first_frame->getStateVector()); - //WOLF_INFO_COND(second_frame, "The second frame has been created"); - //WOLF_INFO("1 - The last (current) frame is: ", _last_frame->id(), " and it's state vector is: \n", _last_frame->getStateVector()); - //WOLF_INFO("2 - The current frame's estimated bias is: \n", _last_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); - //WOLF_INFO("3 - The current state is (from _processor_motion): \n", _processor_motion->getState().vector("POV")); - } - //WOLF_INFO("The current problem is:"); - //_problem_ptr->print(4); - auto state = _problem_ptr->getState(); - auto bias_est = _sensor_ptr->getIntrinsic()->getState(); - auto bias_preint = _processor_motion->getLast()->getCalibrationPreint(); - - #if WRITE_CSV_FILE + void TestStaticZeroOdom(const Vector3d& body_magnitudes, const Vector3d& bias_groundtruth, const Vector3d& bias_initial_guess, const string& test_name, int testing_var, bool small_tol) + { + //Data + data.head(2) = body_magnitudes.head(2); + data(5) = body_magnitudes(2); + data.head(2) += bias_groundtruth.head(2); + data(5) += bias_groundtruth(2); + + //Set bias initial guess + sensor_ptr_->getIntrinsic(t0)->setState(bias_initial_guess); + processor_motion_->setOrigin(KF0_); + +#if WRITE_CSV_FILE + std::fstream file_est; + file_est.open("./est2dzeroodom-"+test_name+".csv", std::fstream::out); + // std::string header_est = "t,px,py,pz,qx,qy,qz,qw,vx,vy,vz,bax_est,bax_preint\n"; + std::string header_est; + if(testing_var == 0) header_est = "t;px;vx;o;bax_est;bg_est;bax_preint;bg_preint\n"; + if(testing_var == 1) header_est = "t;py;vy;o;bay_est;bg_est;bay_preint;bg_preint\n"; + //if(testing_var == 2) header_est = "t;pz;vz;oz:baz_est;bgz_est;baz_preint;bgz_preint\n"; + if(testing_var == 3) header_est = "t;pnorm;vnorm;o;banorm_est;bg_est;banorm_preint;bg_preint\n"; + file_est << header_est; +#endif + + int n_frames = 0; + for(t = t0; t <= t0 + 9.9 + dt/2; t+=dt){ + WOLF_INFO("\n------------------------------------------------------------------------"); + + //Create and process capture + auto C = std::make_shared<CaptureImu>(t, sensor_ptr_, data, data_cov, KF0_->getCaptureList().front()); + C->process(); + + auto state = problem_ptr_->getState(); + auto bias_est = sensor_ptr_->getIntrinsic()->getState(); + auto bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + +#if WRITE_CSV_FILE // pre-solve print to CSV + if(testing_var == 3){ + file_est << std::fixed << t << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'] << ";" + << bias_est.head(2).norm() << ";" + << bias_est(2) << ";" + << bias_preint.head(2).norm() << ";" + << bias_preint(2) << "\n"; + } + else + { file_est << std::fixed << t << ";" - << state['P'](0) << ";" - << state['V'](0) << ";" - << bias_est(0) << ";" - << bias_preint(0) << "\n"; - #endif - - if(_problem_ptr->getTrajectory()->getFrameMap().size() > n_frames) - { - WOLF_INFO("Solving..."); - std::string report = _solver->solve(SolverManager::ReportVerbosity::BRIEF); - - state = _problem_ptr->getState(); - bias_est = _sensor_ptr->getIntrinsic()->getState(); - bias_preint = _processor_motion->getLast()->getCalibrationPreint(); - - #if WRITE_CSV_FILE - // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'] << ";" + << bias_est(testing_var) << ";" + << bias_est(2) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(2) << "\n"; + + } +#endif + + // new frame + if (last_frame_ != processor_motion_->getOrigin()->getFrame()) + { + n_frames++; + last_frame_ = processor_motion_->getOrigin()->getFrame(); + + // impose zero odometry + processor_motion_->setOdometry(sensor_ptr_->getProblem()->stateZero(processor_motion_->getStateStructure())); + + // add zero displacement and rotation capture & feature & factor with all previous frames + assert(sensor_ptr_->getProblem()); + for (auto frm_pair = sensor_ptr_->getProblem()->getTrajectory()->begin(); + frm_pair != sensor_ptr_->getProblem()->getTrajectory()->end(); + frm_pair++) + { + if (frm_pair->second == last_frame_) + break; + auto capture_zero = CaptureBase::emplace<CaptureVoid>(last_frame_, last_frame_->getTimeStamp(), nullptr); + + auto feature_zero = FeatureBase::emplace<FeatureBase>(capture_zero, + "FeatureZeroOdom", + Vector3d::Zero(), + Eigen::MatrixXd::Identity(3,3) * 0.01); + + FactorBase::emplace<FactorRelativePose2d>(feature_zero, + feature_zero, + frm_pair->second, + nullptr, + false, + TOP_MOTION); + + } + + // impose static + last_frame_->getV()->setZero(); + + //Fix frame + last_frame_->getV()->fix(); + + //Solve + if(n_frames > 0) + { + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + //WOLF_INFO("Solver Report: ", report); + + state = problem_ptr_->getState(); + bias_est = sensor_ptr_->getIntrinsic()->getState(); + bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + + WOLF_INFO("The current problem is:"); + problem_ptr_->print(4); + +#if WRITE_CSV_FILE + // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result + if(testing_var == 3){ + file_est << std::fixed << t+dt/2 << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'] << ";" + << bias_est.head(2).norm() << ";" + << bias_est(2) << ";" + << bias_preint.head(2).norm() << ";" + << bias_preint(2) << "\n"; + } + else + { file_est << std::fixed << t+dt/2 << ";" - << state['P'](0) << ";" - << state['V'](0) << ";" - << bias_est(0) << ";" - << bias_preint(0) << "\n"; - #endif - - - WOLF_INFO("Solved"); - if(_first_frame) - { - //WOLF_INFO("4 - The first frame is ", _first_frame->id(), " and it's currently estimated bias is: \n", _first_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); - //WOLF_INFO("its state vector is: \n", _first_frame->getStateVector()); - //WOLF_INFO_COND(second_frame, "The second frame has been created"); - //WOLF_INFO("5 - The last (current) frame is: ", _last_frame->id(), " and it's state vector is: \n", _last_frame->getStateVector()); - //WOLF_INFO("6 - The current frame's estimated bias is: \n", _last_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); - //WOLF_INFO("7 - The current state is (from _processor_motion): \n", _processor_motion->getState().vector("POV")); - WOLF_INFO("Current frame is", _processor_motion->getLast()->id()); - WOLF_INFO("The state is \n:", state); - WOLF_INFO("The estimated bias is: \n", bias_est); - WOLF_INFO("The preintegrated bias is: \n", bias_preint); - } - if(n_frames > 1) - { - ASSERT_MATRIX_APPROX(state.vector("POV"), KF0->getState().vector("POV"), 1e-9); - ASSERT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-9); - } - if (n_frames > 2) - { - ASSERT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-9); - } - WOLF_INFO("------------------------------------------------------------------------\n"); + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'] << ";" + << bias_est(testing_var) << ";" + << bias_est(2) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(2) << "\n"; + + } +#endif + } + + } + + + + WOLF_INFO("Number of frames is ", n_frames); + WOLF_INFO("The state is: ", state); + WOLF_INFO("The estimated bias is: ", bias_est.transpose()); + WOLF_INFO("The preintegrated bias is: ", bias_preint.transpose()); + if(small_tol) + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-6); + } + } + else + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-3); + } + } } - } + +#if WRITE_CSV_FILE + file_est.close(); +#endif + + } +}; + + + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_aX_initial_guess_zero) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = (Vector3d() << 0.42, 0, 0).finished(); + Vector3d bias_initial_guess = Vector3d::Zero(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_aX_initial_guess_zero", 0, true); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_aX) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = (Vector3d() << 0.42, 0, 0).finished(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_aX", 0, true); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_gX_initial_guess_zero) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = (Vector3d() << 0, 0, 0.01).finished(); + Vector3d bias_initial_guess = Vector3d::Zero(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_gX_initial_guess_zero", 0, false); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_gX) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = (Vector3d() << 0, 0, 0.01).finished(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_gX", 0, false); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_aX_initial_guess_zero_zeroodom) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = (Vector3d() << 0.42, 0, 0).finished(); + Vector3d bias_initial_guess = Vector3d::Zero(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_aX_initial_guess_zero", 0, true); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_aX_zeroodom) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = (Vector3d() << 0.42, 0, 0).finished(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_aX", 0, true); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_gX_initial_guess_zero_zeroodom) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = (Vector3d() << 0, 0, 0.01).finished(); + Vector3d bias_initial_guess = Vector3d::Zero(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_gX_initial_guess_zero", 0, false); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_gX_zeroodom) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = (Vector3d() << 0, 0, 0.01).finished(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_gX", 0, false); } + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_a_random) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = Vector3d::Random()*100; + bias_initial_guess(2) = 0; + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_a_random", 3, true); +} + +TEST_F(ProcessorImu2dStaticInitTest, static_bias_zero_initial_guess_random) +{ + Vector3d body_magnitudes = Vector3d::Zero(); + Vector3d bias_groundtruth = Vector3d::Zero(); + Vector3d bias_initial_guess = Vector3d::Random()*0.01; + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_random", 3, false); +} +//class ProcessorImu2dStaticInitTest : public testing::Test +//{ +// +// public: //These can be accessed in fixtures +// wolf::ProblemPtr _problem_ptr; +// wolf::SensorBasePtr _sensor_ptr; +// wolf::ProcessorMotionPtr _processor_motion; +// wolf::TimeStamp t; +// wolf::TimeStamp t0; +// double mti_clock, tmp; +// double dt; +// bool second_frame; +// bool third_frame; +// Vector6d data; +// Matrix6d data_cov; +// VectorComposite x0c; // initial state composite +// VectorComposite s0c; // initial sigmas composite +// std::shared_ptr<wolf::CaptureImu> C; +// FrameBasePtr _first_frame; +// FrameBasePtr _last_frame; +// SolverCeresPtr _solver; +// +// //a new of this will be created for each new test +// void SetUp() override +// { +// using namespace Eigen; +// using std::shared_ptr; +// using std::make_shared; +// using std::static_pointer_cast; +// using namespace wolf::Constants; +// +// std::string wolf_root = _WOLF_IMU_ROOT_DIR; +// +// // Wolf problem +// _problem_ptr = Problem::create("POV", 2); +// Vector3d extrinsics = (Vector3d() << 0,0, 0).finished(); +// _sensor_ptr = _problem_ptr->installSensor("SensorImu2d", "Main Imu", extrinsics, wolf_root + "/demos/sensor_imu2d.yaml"); +// ProcessorBasePtr processor_ptr = _problem_ptr->installProcessor("ProcessorImu2d", "Imu pre-integrator", "Main Imu", wolf_root + "/demos/imu2d_static_init.yaml"); +// _processor_motion = std::static_pointer_cast<ProcessorMotion>(processor_ptr); +// +// // Time and data variables +// second_frame = false; +// third_frame = false; +// dt = 0.01; +// t0.set(0); +// t = t0; +// data = Vector6d::Random(); +// data_cov = Matrix6d::Identity(); +// _last_frame = nullptr; +// +// // Create one capture to store the Imu data arriving from (sensor / callback / file / etc.) +// C = make_shared<CaptureImu>(t, _sensor_ptr, data, data_cov, Vector3d::Zero()); +// +// // Create the solver +// _solver = make_shared<SolverCeres>(_problem_ptr); +// } +// +//}; +// +// +///** +// * SET TO TRUE TO PRODUCE CSV FILE +// * SET TO FALSE TO STOP PRODUCING CSV FILE +// */ +//#define WRITE_CSV_FILE true +// +//TEST_F(ProcessorImu2dStaticInitTest, static) +//{ +// // Set the origin +// x0c['P'] = Vector2d(0, 0); +// x0c['O'] = Vector1d(0); +// x0c['V'] = Vector2d(0, 0); +// +// data_cov *= 1e-3; +// data << 1, 2, 3, 4, 5, 6; +// Vector3d bias_groundtruth; +// bias_groundtruth << data(0), data(1), data(5); +// //dt = 0.0001; +// auto KF0 = _problem_ptr->setPriorFix(x0c, t0, dt/2); +// KF0->fix(); +// _processor_motion->setOrigin(KF0); +// +// WOLF_INFO("Data is: \n", data); +// int size = 3; +// second_frame = false; +// _first_frame = nullptr; +// +// #if WRITE_CSV_FILE +// std::fstream file_est; +// file_est.open("./est.csv", std::fstream::out); +// // std::string header_est = "t,px,py,pz,qx,qy,qz,qw,vx,vy,vz,bax_est,bax_preint\n"; +// std::string header_est = "t;px;vx;bax_est;bax_preint\n"; +// file_est << header_est; +// #endif +// +// +// int i = 1; +// int n_frames = 0; +// for(t = t0+dt; t <= t0 + 3.1 + dt/2; t+=dt){ +// WOLF_INFO("Starting iteration ", i, " with timestamp ", t); +// ++i; +// C = std::make_shared<CaptureImu>(t, _sensor_ptr, data, data_cov, Vector3d::Zero()); +// WOLF_INFO("Created new Capture"); +// if(second_frame){ +// //ASSERT_MATRIX_APPROX(_processor_motion->getState().vector("POV"), Vector5d::Zero(), 1e-9); +// } +// WOLF_INFO("Processing capture"); +// C->process(); +// WOLF_INFO("Doing the static initialization stuff"); +// if (not _last_frame) +// { +// n_frames++; +// _last_frame = _processor_motion->getOrigin()->getFrame(); +// _first_frame = _last_frame; +// +// // fix Position and orientation +// _last_frame->getP()->fix(); +// _last_frame->getO()->fix(); +// +// // impose zero velocity +// _last_frame->getV()->setZero(); +// _last_frame->getV()->fix(); +// +// // impose zero odometry +// _processor_motion->setOdometry(_sensor_ptr->getProblem()->stateZero(_processor_motion->getStateStructure())); +// } +// else +// { +// assert(_processor_motion->getOrigin() && "SubscriberImuEnableable::callback: nullptr origin"); +// assert(_processor_motion->getOrigin()->getFrame() && "SubscriberImuEnableable::callback: nullptr origin frame"); +// +// // new frame +// if (_last_frame != _processor_motion->getOrigin()->getFrame()) +// { +// n_frames++; +// second_frame = true; +// _last_frame = _processor_motion->getOrigin()->getFrame(); +// +// // fix Position and orientation +// _last_frame->getP()->fix(); +// _last_frame->getO()->fix(); +// +// // impose zero velocity +// _last_frame->getV()->setZero(); +// _last_frame->getV()->fix(); +// +// // impose zero odometry +// _processor_motion->setOdometry(_sensor_ptr->getProblem()->stateZero(_processor_motion->getStateStructure())); +// +// // add zero displacement and rotation capture & feature & factor with all previous frames +// assert(_sensor_ptr->getProblem()); +// Eigen::VectorXd zero_motion = Eigen::VectorXd::Zero(size); +// for (auto frm_pair = _sensor_ptr->getProblem()->getTrajectory()->begin(); +// frm_pair != _sensor_ptr->getProblem()->getTrajectory()->end(); +// frm_pair++) +// { +// if (frm_pair->second == _last_frame) +// break; +// auto capture_zero = CaptureBase::emplace<CaptureVoid>(_last_frame, _last_frame->getTimeStamp(), nullptr); +// auto feature_zero = FeatureBase::emplace<FeatureBase>(capture_zero, +// "FeatureZeroOdom", +// zero_motion, +// Eigen::MatrixXd::Identity(size,size) * 0.01); +// +// FactorBase::emplace<FactorRelativePose2d>(feature_zero, +// feature_zero, +// frm_pair->second, +// nullptr, +// false, +// TOP_MOTION); +// +// } +// } +// } +// WOLF_INFO("Static initialization done"); +// if(_first_frame) +// { +// //WOLF_INFO("0 - The first frame is ", _first_frame->id(), " and it's currently estimated bias is: \n", _first_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); +// //WOLF_INFO("its state vector is: \n", _first_frame->getStateVector()); +// //WOLF_INFO_COND(second_frame, "The second frame has been created"); +// //WOLF_INFO("1 - The last (current) frame is: ", _last_frame->id(), " and it's state vector is: \n", _last_frame->getStateVector()); +// //WOLF_INFO("2 - The current frame's estimated bias is: \n", _last_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); +// //WOLF_INFO("3 - The current state is (from _processor_motion): \n", _processor_motion->getState().vector("POV")); +// } +// //WOLF_INFO("The current problem is:"); +// //_problem_ptr->print(4); +// auto state = _problem_ptr->getState(); +// auto bias_est = _sensor_ptr->getIntrinsic()->getState(); +// auto bias_preint = _processor_motion->getLast()->getCalibrationPreint(); +// +// #if WRITE_CSV_FILE +// // pre-solve print to CSV +// file_est << std::fixed << t << ";" +// << state['P'](0) << ";" +// << state['V'](0) << ";" +// << bias_est(0) << ";" +// << bias_preint(0) << "\n"; +// #endif +// +// if(_problem_ptr->getTrajectory()->getFrameMap().size() > n_frames) +// { +// WOLF_INFO("Solving..."); +// std::string report = _solver->solve(SolverManager::ReportVerbosity::BRIEF); +// +// state = _problem_ptr->getState(); +// bias_est = _sensor_ptr->getIntrinsic()->getState(); +// bias_preint = _processor_motion->getLast()->getCalibrationPreint(); +// +// #if WRITE_CSV_FILE +// // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result +// file_est << std::fixed << t+dt/2 << ";" +// << state['P'](0) << ";" +// << state['V'](0) << ";" +// << bias_est(0) << ";" +// << bias_preint(0) << "\n"; +// #endif +// +// +// WOLF_INFO("Solved"); +// if(_first_frame) +// { +// //WOLF_INFO("4 - The first frame is ", _first_frame->id(), " and it's currently estimated bias is: \n", _first_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); +// //WOLF_INFO("its state vector is: \n", _first_frame->getStateVector()); +// //WOLF_INFO_COND(second_frame, "The second frame has been created"); +// //WOLF_INFO("5 - The last (current) frame is: ", _last_frame->id(), " and it's state vector is: \n", _last_frame->getStateVector()); +// //WOLF_INFO("6 - The current frame's estimated bias is: \n", _last_frame->getCaptureOf(_sensor_ptr)->getStateBlock('I')->getState()); +// //WOLF_INFO("7 - The current state is (from _processor_motion): \n", _processor_motion->getState().vector("POV")); +// WOLF_INFO("Current frame is", _processor_motion->getLast()->id()); +// WOLF_INFO("The state is \n:", state); +// WOLF_INFO("The estimated bias is: \n", bias_est); +// WOLF_INFO("The preintegrated bias is: \n", bias_preint); +// } +// if(n_frames > 1) +// { +// ASSERT_MATRIX_APPROX(state.vector("POV"), KF0->getState().vector("POV"), 1e-9); +// ASSERT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-9); +// } +// if (n_frames > 2) +// { +// ASSERT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-9); +// } +// WOLF_INFO("------------------------------------------------------------------------\n"); +// } +// } +//} +// int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); diff --git a/test/gtest_imu_static_init.cpp b/test/gtest_imu_static_init.cpp index 22d47ae5df4e59c93c5bb7a0d7dbca8c4072262d..9c2bdbe07c0f8fef4c302d6ad47ab3d5ad13b523 100644 --- a/test/gtest_imu_static_init.cpp +++ b/test/gtest_imu_static_init.cpp @@ -77,10 +77,9 @@ class ProcessorImuStaticInitTest : public testing::Test VectorComposite std_origin("POV", {0.001*Vector3d::Ones(), 0.001*Vector3d::Ones(), 0.001*Vector3d::Ones()}); KF0_ = problem_ptr_->setPriorFactor(x_origin, std_origin, 0, 0.01); - processor_motion_->setOrigin(KF0_); } - void TestStatic(const Vector6d& body_magnitudes, const Vector6d& bias_groundtruth, const Vector6d& bias_initial_guess, const string& test_name, int testing_var) + void TestStatic(const Vector6d& body_magnitudes, const Vector6d& bias_groundtruth, const Vector6d& bias_initial_guess, const string& test_name, int testing_var, bool small_tol) { //Data data = body_magnitudes; @@ -89,6 +88,7 @@ class ProcessorImuStaticInitTest : public testing::Test //Set bias initial guess sensor_ptr_->getIntrinsic(t0)->setState(bias_initial_guess); + processor_motion_->setOrigin(KF0_); #if WRITE_CSV_FILE std::fstream file_est; @@ -98,12 +98,13 @@ class ProcessorImuStaticInitTest : public testing::Test if(testing_var == 0) header_est = "t;px;vx;ox;bax_est;bgx_est;bax_preint;bgx_preint\n"; if(testing_var == 1) header_est = "t;py;vy;oy;bay_est;bgy_est;bay_preint;bgy_preint\n"; if(testing_var == 2) header_est = "t;pz;vz;oz:baz_est;bgz_est;baz_preint;bgz_preint\n"; + if(testing_var == 3) header_est = "t;pnorm;vnorm;onorm;banorm_est;bgnorm_est;banorm_preint;bgnorm_preint\n"; file_est << header_est; #endif int n_frames = 0; - for(t = t0; t <= t0 + 3.9 + dt/2; t+=dt){ + for(t = t0; t <= t0 + 9.9 + dt/2; t+=dt){ WOLF_INFO("\n------------------------------------------------------------------------"); //Create and process capture @@ -116,6 +117,18 @@ class ProcessorImuStaticInitTest : public testing::Test #if WRITE_CSV_FILE // pre-solve print to CSV + if(testing_var == 3){ + file_est << std::fixed << t << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'].norm() << ";" + << bias_est.head(3).norm() << ";" + << bias_est.tail(3).norm() << ";" + << bias_preint.head(3).norm() << ";" + << bias_preint.tail(3).norm() << "\n"; + } + else + { file_est << std::fixed << t << ";" << state['P'](testing_var) << ";" << state['V'](testing_var) << ";" @@ -124,6 +137,8 @@ class ProcessorImuStaticInitTest : public testing::Test << bias_est(testing_var+3) << ";" << bias_preint(testing_var) << ";" << bias_preint(testing_var+3) << "\n"; + + } #endif // new frame @@ -149,10 +164,210 @@ class ProcessorImuStaticInitTest : public testing::Test state = problem_ptr_->getState(); bias_est = sensor_ptr_->getIntrinsic()->getState(); bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + + WOLF_INFO("The current problem is:"); + problem_ptr_->print(4); + +#if WRITE_CSV_FILE + // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result + if(testing_var == 3){ + file_est << std::fixed << t+dt/2 << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'].norm() << ";" + << bias_est.head(3).norm() << ";" + << bias_est.tail(3).norm() << ";" + << bias_preint.head(3).norm() << ";" + << bias_preint.tail(3).norm() << "\n"; + } + else + { + file_est << std::fixed << t+dt/2 << ";" + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'](testing_var) << ";" + << bias_est(testing_var) << ";" + << bias_est(testing_var+3) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(testing_var+3) << "\n"; + + } +#endif + } + + } + + + + WOLF_INFO("Number of frames is ", n_frames); + WOLF_INFO("The state is: ", state); + WOLF_INFO("The estimated bias is: ", bias_est.transpose()); + WOLF_INFO("The preintegrated bias is: ", bias_preint.transpose()); + if(small_tol) + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-6); + } + } + else + { + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-3); + } + } + } + +#if WRITE_CSV_FILE + file_est.close(); +#endif + + } + + void TestStaticZeroOdom(const Vector6d& body_magnitudes, const Vector6d& bias_groundtruth, const Vector6d& bias_initial_guess, const string& test_name, int testing_var, bool small_tol) + { + //Data + data = body_magnitudes; + data.head(3) -= Quaterniond(Vector4d(KF0_->getO()->getState())).conjugate() * wolf::gravity(); + data += bias_groundtruth; + + //Set bias initial guess + sensor_ptr_->getIntrinsic(t0)->setState(bias_initial_guess); + processor_motion_->setOrigin(KF0_); + +#if WRITE_CSV_FILE + std::fstream file_est; + file_est.open("./estzeroodom-"+test_name+".csv", std::fstream::out); + // std::string header_est = "t,px,py,pz,qx,qy,qz,qw,vx,vy,vz,bax_est,bax_preint\n"; + std::string header_est; + if(testing_var == 0) header_est = "t;px;vx;ox;bax_est;bgx_est;bax_preint;bgx_preint\n"; + if(testing_var == 1) header_est = "t;py;vy;oy;bay_est;bgy_est;bay_preint;bgy_preint\n"; + if(testing_var == 2) header_est = "t;pz;vz;oz:baz_est;bgz_est;baz_preint;bgz_preint\n"; + if(testing_var == 3) header_est = "t;pnorm;vnorm;onorm;banorm_est;bgnorm_est;banorm_preint;bgnorm_preint\n"; + file_est << header_est; +#endif + + + int n_frames = 0; + for(t = t0; t <= t0 + 9.9 + dt/2; t+=dt){ + WOLF_INFO("\n------------------------------------------------------------------------"); + + //Create and process capture + auto C = std::make_shared<CaptureImu>(t, sensor_ptr_, data, data_cov, KF0_->getCaptureList().front()); + C->process(); + + auto state = problem_ptr_->getState(); + auto bias_est = sensor_ptr_->getIntrinsic()->getState(); + auto bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + +#if WRITE_CSV_FILE + // pre-solve print to CSV + if(testing_var == 3){ + file_est << std::fixed << t << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'].norm() << ";" + << bias_est.head(3).norm() << ";" + << bias_est.tail(3).norm() << ";" + << bias_preint.head(3).norm() << ";" + << bias_preint.tail(3).norm() << "\n"; + } + else + { + file_est << std::fixed << t << ";" + << state['P'](testing_var) << ";" + << state['V'](testing_var) << ";" + << state['O'](testing_var) << ";" + << bias_est(testing_var) << ";" + << bias_est(testing_var+3) << ";" + << bias_preint(testing_var) << ";" + << bias_preint(testing_var+3) << "\n"; + + } +#endif + + // new frame + if (last_frame_ != processor_motion_->getOrigin()->getFrame()) + { + n_frames++; + last_frame_ = processor_motion_->getOrigin()->getFrame(); + + // impose zero odometry + processor_motion_->setOdometry(sensor_ptr_->getProblem()->stateZero(processor_motion_->getStateStructure())); + + // add zero displacement and rotation capture & feature & factor with all previous frames + assert(sensor_ptr_->getProblem()); + for (auto frm_pair = sensor_ptr_->getProblem()->getTrajectory()->begin(); + frm_pair != sensor_ptr_->getProblem()->getTrajectory()->end(); + frm_pair++) + { + if (frm_pair->second == last_frame_) + break; + auto capture_zero = CaptureBase::emplace<CaptureVoid>(last_frame_, last_frame_->getTimeStamp(), nullptr); + + auto feature_zero = FeatureBase::emplace<FeatureBase>(capture_zero, + "FeatureZeroOdom", + Vector7d::Zero(), + Eigen::MatrixXd::Identity(6,6) * 0.01); + + FactorBase::emplace<FactorRelativePose3d>(feature_zero, + feature_zero, + frm_pair->second, + nullptr, + false, + TOP_MOTION); + + } + + // impose static + last_frame_->getV()->setZero(); + + //Fix frame + last_frame_->getV()->fix(); + + //Solve + if(n_frames > 0) + { + std::string report = solver_->solve(SolverManager::ReportVerbosity::FULL); + //WOLF_INFO("Solver Report: ", report); + + state = problem_ptr_->getState(); + bias_est = sensor_ptr_->getIntrinsic()->getState(); + bias_preint = processor_motion_->getLast()->getCalibrationPreint(); + + WOLF_INFO("The current problem is:"); + problem_ptr_->print(4); #if WRITE_CSV_FILE // post-solve print to CSV with time-stamp shifted by dt/2 to separate from pre-solve result - file_est << std::fixed << t+dt/2 << ";" + if(testing_var == 3){ + file_est << std::fixed << t+dt/2 << ";" + << state['P'].norm() << ";" + << state['V'].norm() << ";" + << state['O'].norm() << ";" + << bias_est.head(3).norm() << ";" + << bias_est.tail(3).norm() << ";" + << bias_preint.head(3).norm() << ";" + << bias_preint.tail(3).norm() << "\n"; + } + else + { + file_est << std::fixed << t+dt/2 << ";" << state['P'](testing_var) << ";" << state['V'](testing_var) << ";" << state['O'](testing_var) << ";" @@ -160,29 +375,46 @@ class ProcessorImuStaticInitTest : public testing::Test << bias_est(testing_var+3) << ";" << bias_preint(testing_var) << ";" << bias_preint(testing_var+3) << "\n"; + + } #endif } } - //WOLF_INFO("The current problem is:"); - //problem_ptr_->print(4); WOLF_INFO("Number of frames is ", n_frames); WOLF_INFO("The state is: ", state); WOLF_INFO("The estimated bias is: ", bias_est.transpose()); WOLF_INFO("The preintegrated bias is: ", bias_preint.transpose()); - if(n_frames == 2) + if(small_tol) { - //ASSERT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-4); - //ASSERT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-6); + } } - else if (n_frames > 2) + else { - EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-6); - EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-6); - EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-6); + if(n_frames == 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + } + else if (n_frames > 2) + { + EXPECT_MATRIX_APPROX(state.vector("POV"), KF0_->getState().vector("POV"), 1e-3); + EXPECT_MATRIX_APPROX(bias_est, bias_groundtruth, 1e-3); + EXPECT_MATRIX_APPROX(bias_preint, bias_groundtruth, 1e-3); + } } } @@ -201,7 +433,7 @@ TEST_F(ProcessorImuStaticInitTest, static_bias_aX_initial_guess_zero) Vector6d bias_groundtruth = (Vector6d() << 0.42, 0, 0, 0, 0, 0).finished(); Vector6d bias_initial_guess = Vector6d::Zero(); - TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_aX_initial_guess_zero", 0); + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_aX_initial_guess_zero", 0, true); } TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_aX) @@ -210,25 +442,81 @@ TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_aX) Vector6d bias_groundtruth = Vector6d::Zero(); Vector6d bias_initial_guess = (Vector6d() << 0.42, 0, 0, 0, 0, 0).finished(); - TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_aX", 0); + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_aX", 0, true); } TEST_F(ProcessorImuStaticInitTest, static_bias_gX_initial_guess_zero) { Vector6d body_magnitudes = Vector6d::Zero(); - Vector6d bias_groundtruth = (Vector6d() << 0, 0, 0, 0.1, 0, 0).finished(); + Vector6d bias_groundtruth = (Vector6d() << 0, 0, 0, 0.01, 0, 0).finished(); Vector6d bias_initial_guess = Vector6d::Zero(); - TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_gX_initial_guess_zero", 0); + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_gX_initial_guess_zero", 0, false); } TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_gX) { Vector6d body_magnitudes = Vector6d::Zero(); Vector6d bias_groundtruth = Vector6d::Zero(); - Vector6d bias_initial_guess = (Vector6d() << 0, 0, 0, 0.1, 0, 0).finished(); + Vector6d bias_initial_guess = (Vector6d() << 0, 0, 0, 0.01, 0, 0).finished(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_gX", 0, false); +} + +TEST_F(ProcessorImuStaticInitTest, static_bias_aX_initial_guess_zero_zeroodom) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = (Vector6d() << 0.42, 0, 0, 0, 0, 0).finished(); + Vector6d bias_initial_guess = Vector6d::Zero(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_aX_initial_guess_zero", 0, true); +} + +TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_aX_zeroodom) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = Vector6d::Zero(); + Vector6d bias_initial_guess = (Vector6d() << 0.42, 0, 0, 0, 0, 0).finished(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_aX", 0, true); +} + +TEST_F(ProcessorImuStaticInitTest, static_bias_gX_initial_guess_zero_zeroodom) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = (Vector6d() << 0, 0, 0, 0.01, 0, 0).finished(); + Vector6d bias_initial_guess = Vector6d::Zero(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_gX_initial_guess_zero", 0, false); +} + +TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_gX_zeroodom) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = Vector6d::Zero(); + Vector6d bias_initial_guess = (Vector6d() << 0, 0, 0, 0.01, 0, 0).finished(); + + TestStaticZeroOdom(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_gX", 0, false); +} + + +TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_a_random) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = Vector6d::Zero(); + Vector6d bias_initial_guess = Vector6d::Random()*100; + bias_initial_guess.tail(3) = Vector3d::Zero(); + + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_a_random", 3, true); +} + +TEST_F(ProcessorImuStaticInitTest, static_bias_zero_initial_guess_random) +{ + Vector6d body_magnitudes = Vector6d::Zero(); + Vector6d bias_groundtruth = Vector6d::Zero(); + Vector6d bias_initial_guess = Vector6d::Random()*0.01; - TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_gX", 0); + TestStatic(body_magnitudes, bias_groundtruth, bias_initial_guess, "static_bias_zero_initial_guess_random", 3, false); } //TEST_F(ProcessorImuStaticInitTest, static) diff --git a/test/gtest_processor_motion_intrinsics_update.cpp b/test/gtest_processor_motion_intrinsics_update.cpp index 4dbac962133c7d8da9f76e152a09e0749c1f2e71..a013072abf3064c5a2ac26302a1fcfe145f1a0b5 100644 --- a/test/gtest_processor_motion_intrinsics_update.cpp +++ b/test/gtest_processor_motion_intrinsics_update.cpp @@ -155,7 +155,7 @@ TEST_F(ProcessorImuTest, test1) * SET TO TRUE TO PRODUCE CSV FILE * SET TO FALSE TO STOP PRODUCING CSV FILE */ -#define WRITE_CSV_FILE false +#define WRITE_CSV_FILE true TEST_F(ProcessorImuTest, getState) { diff --git a/test/yaml/imu2d_static_init.yaml b/test/yaml/imu2d_static_init.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4978c8e2863908df72a336da1225667411d3022a --- /dev/null +++ b/test/yaml/imu2d_static_init.yaml @@ -0,0 +1,12 @@ +type: "ProcessorImu2d" # This must match the KEY used in the SensorFactory. Otherwise it is an error. + +time_tolerance: 0.0025 # Time tolerance for joining KFs +unmeasured_perturbation_std: 0.0001 + +keyframe_vote: + voting_active: true + voting_aux_active: false + max_time_span: 0.9999 # seconds + max_buff_length: 1000000000 # motion deltas + dist_traveled: 100000000000 # meters + angle_turned: 10000000000 # radians (1 rad approx 57 deg, approx 60 deg) diff --git a/test/yaml/sensor_imu2d_static_init.yaml b/test/yaml/sensor_imu2d_static_init.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ce810d4f5f397a7bcb8647e086c026b125fbc936 --- /dev/null +++ b/test/yaml/sensor_imu2d_static_init.yaml @@ -0,0 +1,9 @@ +type: "SensorImu2d" # This must match the KEY used in the SensorFactory. Otherwise it is an error. + +motion_variances: + a_noise: 0.053 # standard deviation of Acceleration noise (same for all the axis) in m/s2 + w_noise: 0.0011 # standard deviation of Gyroscope noise (same for all the axis) in rad/sec + ab_initial_stdev: 0.800 # m/s2 - initial bias + wb_initial_stdev: 0.350 # rad/sec - initial bias + ab_rate_stdev: 0.1 # m/s2/sqrt(s) + wb_rate_stdev: 0.0400 # rad/s/sqrt(s)