diff --git a/CMakeLists.txt b/CMakeLists.txt index ac07260c67f935f162754f71d9341a04cb3d7130..a1d16d9bdc66fae4b37a592d417d6d3c00322265 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,12 +132,13 @@ SET(HDRS_FACTOR include/${PROJECT_NAME}/factor/factor_diff_drive.h include/${PROJECT_NAME}/factor/factor_distance_3d.h include/${PROJECT_NAME}/factor/factor_pose_2d.h + include/${PROJECT_NAME}/factor/factor_pose_2d_with_extrinsics.h include/${PROJECT_NAME}/factor/factor_pose_3d.h + include/${PROJECT_NAME}/factor/factor_pose_3d_with_extrinsics.h include/${PROJECT_NAME}/factor/factor_quaternion_absolute.h include/${PROJECT_NAME}/factor/factor_relative_pose_2d.h include/${PROJECT_NAME}/factor/factor_relative_pose_2d_with_extrinsics.h include/${PROJECT_NAME}/factor/factor_relative_pose_3d.h - include/${PROJECT_NAME}/factor/factor_pose_3d_with_extrinsics.h include/${PROJECT_NAME}/factor/factor_relative_pose_3d_with_extrinsics.h include/${PROJECT_NAME}/factor/factor_velocity_local_direction_3d.h ) @@ -290,7 +291,6 @@ SET(SRCS_PROCESSOR src/processor/processor_motion.cpp src/processor/processor_odom_2d.cpp src/processor/processor_odom_3d.cpp - src/processor/processor_pose.cpp src/processor/processor_tracker.cpp src/processor/processor_tracker_feature.cpp src/processor/processor_tracker_landmark.cpp diff --git a/include/core/capture/capture_pose.h b/include/core/capture/capture_pose.h index e7313e1f5e73851978574ba757e826400ef44e4c..983da16c49ad9bcc23bff502b80b0e8267723eb1 100644 --- a/include/core/capture/capture_pose.h +++ b/include/core/capture/capture_pose.h @@ -43,14 +43,18 @@ class CapturePose : public CaptureBase public: - CapturePose(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const Eigen::VectorXd& _data, const Eigen::MatrixXd& _data_covariance); + CapturePose(const TimeStamp& _ts, + SensorBasePtr _sensor_ptr, + const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_covariance); ~CapturePose() override; virtual void emplaceFeatureAndFactor(); - Eigen::VectorXd getData(){ return data_;} - Eigen::MatrixXd getDataCovariance(){ return data_covariance_;} - + Eigen::VectorXd getData() const { return data_;} + Eigen::MatrixXd getDataCovariance() const { return data_covariance_;} + void setData(const Eigen::VectorXd& _data); + void setDataCovariance(const Eigen::MatrixXd& _data_covariance); }; } //namespace wolf \ No newline at end of file diff --git a/include/core/factor/factor_pose_2d.h b/include/core/factor/factor_pose_2d.h index 30d4c716834738159fd08ecbe3147438d34bdadf..8b65c4f71f94daa2e751cec491b8e05a1920f148 100644 --- a/include/core/factor/factor_pose_2d.h +++ b/include/core/factor/factor_pose_2d.h @@ -64,7 +64,7 @@ inline bool FactorPose2d::operator ()(const T* const _p, const T* const _o, T* _ { // measurement // Eigen::Matrix<T,3,1> meas = getMeasurement().cast<T>(); - auto& meas = getMeasurement(); + auto& meas = getMeasurement(); // error Eigen::Matrix<T,3,1> er; diff --git a/include/core/factor/factor_pose_2d_with_extrinsics.h b/include/core/factor/factor_pose_2d_with_extrinsics.h new file mode 100644 index 0000000000000000000000000000000000000000..c9b103a3d50bca689f84dfea672bfdcb0fc20c18 --- /dev/null +++ b/include/core/factor/factor_pose_2d_with_extrinsics.h @@ -0,0 +1,92 @@ +//--------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(FactorPose2dWithExtrinsics); + +//class +class FactorPose2dWithExtrinsics : public FactorAutodiff<FactorPose2dWithExtrinsics, 3, 2, 1, 2, 1> +{ + public: + FactorPose2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr, + const ProcessorBasePtr& _processor_ptr, + bool _apply_loss_function, + const FactorTopology& _top, + FactorStatus _status = FAC_ACTIVE) : + FactorAutodiff<FactorPose2dWithExtrinsics, 3, 2, 1, 2, 1>("FactorPose2dWithExtrinsics", + _top, + _ftr_ptr, + nullptr, nullptr, nullptr, nullptr, + _processor_ptr, + _apply_loss_function, + _status, + _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!"); + } + + ~FactorPose2dWithExtrinsics() override = default; + + template<typename T> + bool operator ()(const T* const _p, + const T* const _o, + const T* const _sp, + const T* const _so, + T* _residuals) const; +}; + +template<typename T> +inline bool FactorPose2dWithExtrinsics::operator ()(const T* const _p, + const T* const _o, + const T* const _sp, + const T* const _so, + T* _residuals) const +{ + // MAPS + Eigen::Map<const Eigen::Matrix<T,2,1> > p(_p); + Eigen::Map<const Eigen::Matrix<T,2,1> > sp(_sp); + + Eigen::Matrix<T, 3, 1> err; // error + err.head(2) = getMeasurement().head(2) - (p.head(2) + Eigen::Rotation2D<T>(_o[0]) * sp.head(2)); + err(2) = pi2pi(getMeasurement()(2) - (_o[0] + _so[0])); + + // Residuals + Eigen::Map<Eigen::Matrix<T,3,1> > res(_residuals); + res = getMeasurementSquareRootInformationUpper().cast<T>() * err; + + return true; +} + +} // namespace wolf \ No newline at end of file diff --git a/include/core/processor/processor_pose.h b/include/core/processor/processor_pose.h index 3ee3b2315356bff7025ec476d6bf332cfbdfc712..a6be027ee15d0b6be2abc4437e2b3d54d76259c3 100644 --- a/include/core/processor/processor_pose.h +++ b/include/core/processor/processor_pose.h @@ -40,13 +40,13 @@ struct ParamsProcessorPose : public ParamsProcessorBase return ParamsProcessorBase::print(); } }; - -WOLF_PTR_TYPEDEFS(ProcessorPose); //class -class ProcessorPose : public ProcessorBase{ +template <unsigned int DIM> +class ProcessorPose : public ProcessorBase +{ public: - ProcessorPose(ParamsProcessorPosePtr _params_pfnomove); + ProcessorPose(ParamsProcessorPosePtr _params_pose); ~ProcessorPose() override = default; WOLF_PROCESSOR_CREATE(ProcessorPose, ParamsProcessorPose); @@ -62,8 +62,138 @@ class ProcessorPose : public ProcessorBase{ void createFactorIfNecessary(); protected: - ParamsProcessorPosePtr params_pfnomove_; - bool is_2d_; + ParamsProcessorPosePtr params_pose_; }; -} /* namespace wolf */ \ No newline at end of file +typedef ProcessorPose<2> ProcessorPose2d; +typedef ProcessorPose<3> ProcessorPose3d; + +WOLF_DECLARED_PTR_TYPEDEFS(ProcessorPose2d); +WOLF_DECLARED_PTR_TYPEDEFS(ProcessorPose3d); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION // +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "core/sensor/sensor_pose.h" +#include "core/capture/capture_pose.h" +#include "core/factor/factor_pose_3d_with_extrinsics.h" + +namespace wolf { + +template<unsigned int DIM> +inline ProcessorPose<DIM>::ProcessorPose(ParamsProcessorPosePtr _params_pose) : + ProcessorBase("ProcessorPose", DIM, _params_pose), + params_pose_(_params_pose) +{ + static_assert(DIM == 2 or DIM == 3); +} + +template<unsigned int DIM> +void ProcessorPose<DIM>::configure(SensorBasePtr _sensor) +{ + if (not std::dynamic_pointer_cast<SensorPose<DIM>>(_sensor)) + { + throw std::runtime_error("Configuring a ProcessorPose with a sensor " + _sensor->getType() + ", should be SensorPose" + toString(DIM) + "d"); + } +} + +template<unsigned int DIM> +void ProcessorPose<DIM>::createFactorIfNecessary() +{ + auto kf_it_last = buffer_frame_.getContainer().end(); + auto kf_it = buffer_frame_.getContainer().begin(); + while (kf_it != buffer_frame_.getContainer().end()) + { + TimeStamp t = kf_it->first; + auto cap_it = buffer_capture_.selectIterator(t, getTimeTolerance()); + + // if capture with corresponding timestamp is not found, assume you will get it later + if (cap_it != buffer_capture_.getContainer().end()) + { + // if a corresponding capture exists, link it to the KF and create a factor + auto cap = std::static_pointer_cast<CapturePose>(cap_it->second); + cap->link(kf_it->second); + FeatureBasePtr feat = FeatureBase::emplace<FeatureBase>(cap, "Pose", cap->getData(), cap->getDataCovariance()); + + if (DIM == 2) + { + // TODO + throw std::runtime_error("FactorPose2dWithExtrinsics not implemented"); + } + else + { + FactorBase::emplace<FactorPose3dWithExtrinsics>(feat, feat, shared_from_this(), false, TOP_ABS); + } + // erase removes range [first, last): it does not removes last + // so increment the iterator so that it points to the next element in the container + buffer_capture_.getContainer().erase(buffer_capture_.getContainer().begin(), std::next(cap_it)); + + // we cannot erase on the kf buffer since we are looping over it so we store the iterator for later + kf_it_last = kf_it; + } + + kf_it++; + } + + // whatever happened, remove very old captures + buffer_capture_.removeUpTo(buffer_frame_.getContainer().begin()->first - 5); + + // now we erase the kf buffer if there was a match + if (kf_it_last != buffer_frame_.getContainer().end()){ + buffer_frame_.getContainer().erase(buffer_frame_.getContainer().begin(), std::next(kf_it_last)); + } + +} + +template<unsigned int DIM> +inline void ProcessorPose<DIM>::processCapture(CaptureBasePtr _capture) +{ + if (!_capture) + { + WOLF_ERROR("Received capture is nullptr."); + return; + } + // nothing to do if any of the two buffer is empty + if(buffer_frame_.empty()){ + WOLF_DEBUG("processCapture: Frame buffer empty, time ", _capture->getTimeStamp()); + return; + } + if(buffer_frame_.empty()){ + WOLF_DEBUG("processCapture: Capture buffer empty, time ", _capture->getTimeStamp()); + return; + } + + createFactorIfNecessary(); + +} + +template<unsigned int DIM> +inline void ProcessorPose<DIM>::processKeyFrame(FrameBasePtr _keyframe_ptr) +{ + if (!_keyframe_ptr) + { + WOLF_ERROR("Received KF is nullptr."); + return; + } + // nothing to do if any of the two buffer is empty + if(buffer_frame_.empty()){ + WOLF_DEBUG("ProcessorPose: Frame buffer empty, time ", _keyframe_ptr->getTimeStamp()); + return; + } + if(buffer_frame_.empty()){ + WOLF_DEBUG("ProcessorPose: Capture buffer empty, time ", _keyframe_ptr->getTimeStamp()); + return; + } + + createFactorIfNecessary(); +} +} /* namespace wolf */ + +// Register in the FactoryProcessor +#include "core/processor/factory_processor.h" +namespace wolf { +WOLF_REGISTER_PROCESSOR(ProcessorPose2d); +WOLF_REGISTER_PROCESSOR(ProcessorPose3d); +} // namespace wolf \ No newline at end of file diff --git a/schema/processor/ProcessorPose2d.schema b/schema/processor/ProcessorPose2d.schema new file mode 100644 index 0000000000000000000000000000000000000000..3c97f7469fc38b34aa0998e8c96fc47d177e4aa9 --- /dev/null +++ b/schema/processor/ProcessorPose2d.schema @@ -0,0 +1 @@ +follow: ProcessorBase.schema \ No newline at end of file diff --git a/schema/processor/ProcessorPose3d.schema b/schema/processor/ProcessorPose3d.schema new file mode 100644 index 0000000000000000000000000000000000000000..3c97f7469fc38b34aa0998e8c96fc47d177e4aa9 --- /dev/null +++ b/schema/processor/ProcessorPose3d.schema @@ -0,0 +1 @@ +follow: ProcessorBase.schema \ No newline at end of file diff --git a/src/capture/capture_pose.cpp b/src/capture/capture_pose.cpp index 113ac5710753ffd6c81de747977b914046fefc5c..c493d8fc720411a957c4b16640d8a7d97571dff4 100644 --- a/src/capture/capture_pose.cpp +++ b/src/capture/capture_pose.cpp @@ -28,7 +28,9 @@ CapturePose::CapturePose(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const data_(_data), data_covariance_(_data_covariance) { - // + assert(_data.size() == 3 or _data.size() == 7); + assert(_data_covariance.rows() == _data_covariance.cols()); + assert(_data_covariance.rows() == 3 or _data_covariance.rows() == 6); } CapturePose::~CapturePose() @@ -36,6 +38,21 @@ CapturePose::~CapturePose() // } +void CapturePose::setData(const Eigen::VectorXd& _data) +{ + assert(_data.size() == 3 or _data.size() == 7); + + data_ = _data; +} + +void CapturePose::setDataCovariance(const Eigen::MatrixXd& _data_covariance) +{ + assert(_data_covariance.rows() == _data_covariance.cols()); + assert(_data_covariance.rows() == 3 or _data_covariance.rows() == 6); + + data_covariance_ = _data_covariance; +} + void CapturePose::emplaceFeatureAndFactor() { // Emplace feature diff --git a/src/processor/processor_pose.cpp b/src/processor/processor_pose.cpp deleted file mode 100644 index 10a20636ef40cfb0bfca5e8019fb90d31b40cb1c..0000000000000000000000000000000000000000 --- a/src/processor/processor_pose.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//--------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-------- -/** - * \file processor_pose.cpp - * - * Created on: Feb 19, 2020 - * \author: mfourmy - */ - -#include "core/processor/processor_pose.h" - -#include "core/sensor/sensor_pose.h" -#include "core/capture/capture_pose.h" -#include "core/factor/factor_pose_3d_with_extrinsics.h" - - -namespace wolf -{ - -ProcessorPose::ProcessorPose(ParamsProcessorPosePtr _params_pfnomove) : - ProcessorBase("ProcessorPose", 0, _params_pfnomove), - params_pfnomove_(std::make_shared<ParamsProcessorPose>(*_params_pfnomove)) -{ - -} - -void ProcessorPose::configure(SensorBasePtr _sensor) -{ - auto sen_pose_2d = std::dynamic_pointer_cast<SensorPose2d>(_sensor); - auto sen_pose_3d = std::dynamic_pointer_cast<SensorPose3d>(_sensor); - - if (not sen_pose_2d and not sen_pose_3d) - { - throw std::runtime_error("Configuring a ProcessorPose with a sensor " + _sensor->getType() + ", should be SensorPose2d or SensorPose3d"); - } - is_2d_ = sen_pose_2d != nullptr; -} - -void ProcessorPose::createFactorIfNecessary() -{ - auto kf_it_last = buffer_frame_.getContainer().end(); - auto kf_it = buffer_frame_.getContainer().begin(); - while (kf_it != buffer_frame_.getContainer().end()) - { - TimeStamp t = kf_it->first; - auto cap_it = buffer_capture_.selectIterator(t, getTimeTolerance()); - - // if capture with corresponding timestamp is not found, assume you will get it later - if (cap_it != buffer_capture_.getContainer().end()) - { - // if a corresponding capture exists, link it to the KF and create a factor - auto cap = std::static_pointer_cast<CapturePose>(cap_it->second); - cap->link(kf_it->second); - FeatureBasePtr feat = FeatureBase::emplace<FeatureBase>(cap, "Pose", cap->getData(), cap->getDataCovariance()); - - if (is_2d_) - { - // TODO - throw std::runtime_error("FactorPose2dWithExtrinsics not implemented"); - } - else - { - FactorBase::emplace<FactorPose3dWithExtrinsics>(feat, feat, shared_from_this(), false, TOP_ABS); - } - // erase removes range [first, last): it does not removes last - // so increment the iterator so that it points to the next element in the container - buffer_capture_.getContainer().erase(buffer_capture_.getContainer().begin(), std::next(cap_it)); - - // we cannot erase on the kf buffer since we are looping over it so we store the iterator for later - kf_it_last = kf_it; - } - - kf_it++; - } - - // whatever happened, remove very old captures - buffer_capture_.removeUpTo(buffer_frame_.getContainer().begin()->first - 5); - - // now we erase the kf buffer if there was a match - if (kf_it_last != buffer_frame_.getContainer().end()){ - buffer_frame_.getContainer().erase(buffer_frame_.getContainer().begin(), std::next(kf_it_last)); - } - -} - -inline void ProcessorPose::processCapture(CaptureBasePtr _capture) -{ - if (!_capture) - { - WOLF_ERROR("Received capture is nullptr."); - return; - } - // nothing to do if any of the two buffer is empty - if(buffer_frame_.empty()){ - WOLF_DEBUG("processCapture: Frame buffer empty, time ", _capture->getTimeStamp()); - return; - } - if(buffer_frame_.empty()){ - WOLF_DEBUG("processCapture: Capture buffer empty, time ", _capture->getTimeStamp()); - return; - } - - createFactorIfNecessary(); - -} - -inline void ProcessorPose::processKeyFrame(FrameBasePtr _keyframe_ptr) -{ - if (!_keyframe_ptr) - { - WOLF_ERROR("Received KF is nullptr."); - return; - } - // nothing to do if any of the two buffer is empty - if(buffer_frame_.empty()){ - WOLF_DEBUG("ProcessorPose: Frame buffer empty, time ", _keyframe_ptr->getTimeStamp()); - return; - } - if(buffer_frame_.empty()){ - WOLF_DEBUG("ProcessorPose: Capture buffer empty, time ", _keyframe_ptr->getTimeStamp()); - return; - } - - createFactorIfNecessary(); -} - - -} /* namespace wolf */ - -// Register in the FactoryProcessor -#include "core/processor/factory_processor.h" -namespace wolf { -WOLF_REGISTER_PROCESSOR(ProcessorPose); -} // namespace wolf diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7299924ed68ba9bcf93f1a191bfd3401a1062a0a..ddbdd9a5d31f42c4b3b33dc5ef59cbd709da88c5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -147,9 +147,15 @@ wolf_add_gtest(gtest_factor_odom_2d_autodiff gtest_factor_odom_2d_autodiff.cpp) # FactorPose2d class test wolf_add_gtest(gtest_factor_pose_2d gtest_factor_pose_2d.cpp) +# FactorPose2dWithExtrinsics class test +wolf_add_gtest(gtest_factor_pose_2d_with_extrinsics gtest_factor_pose_2d_with_extrinsics.cpp) + # FactorPose3d class test wolf_add_gtest(gtest_factor_pose_3d gtest_factor_pose_3d.cpp) +# FactorPose3dWithExtrinsics class test +wolf_add_gtest(gtest_factor_pose_3d_with_extrinsics gtest_factor_pose_3d_with_extrinsics.cpp) + # FactorRelativePose2d class test wolf_add_gtest(gtest_factor_relative_pose_2d gtest_factor_relative_pose_2d.cpp) diff --git a/test/gtest_factor_pose_2d_with_extrinsics.cpp b/test/gtest_factor_pose_2d_with_extrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..459d557d7630170a681544fb5483a2f4cc4363ef --- /dev/null +++ b/test/gtest_factor_pose_2d_with_extrinsics.cpp @@ -0,0 +1,144 @@ +//--------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_pose_2d_with_extrinsics.h" +#include "core/capture/capture_pose.h" +#include "core/sensor/sensor_pose.h" +#include "core/processor/processor_pose.h" +#include "core/math/rotations.h" + +using namespace Eigen; +using namespace wolf; +using std::cout; +using std::endl; + +std::string wolf_root = _WOLF_ROOT_DIR; + +// Input odometry data and covariance +Matrix3d data_cov = 0.1*Matrix3d::Identity(); + +// Problem and solver +ProblemPtr problem_ptr = Problem::create("PO", 2); +SolverCeres solver(problem_ptr); + +// Sensor +auto sensor_pose2d = problem_ptr->installSensor("SensorPose2d", wolf_root + "/test/yaml/sensor_pose_2d.yaml", {wolf_root}); +auto processor_pose2d = ProcessorBase::emplace<ProcessorPose2d>(sensor_pose2d, std::make_shared<ParamsProcessorPose>()); + +// Two frames +FrameBasePtr frm = problem_ptr->emplaceFrame(0, "PO", Vector3d::Zero() ); + +// Capture for frm +auto cap = CaptureBase::emplace<CapturePose>(frm, 0, sensor_pose2d, Vector3d::Zero(), data_cov); + +TEST(FactorPose2dWithExtrinsics, check_tree) +{ + ASSERT_TRUE(problem_ptr->check(0)); +} + +TEST(FactorPose2dWithExtrinsics, solve_f_fix_s) +{ + for (int i = 0; i < 1e2; i++) + { + // Random pose + Vector3d x0 = Vector3d::Random() * 10; + pi2pi(x0(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // measurement + Vector3d meas; + meas(2) = pi2pi(x0(2) + xs(2)); + meas.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * xs.head<2>(); + + // Set states + frm->setState(x0,"PO"); + sensor_pose2d->setState(xs, "PO"); + + // feature & factor with meas measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap, "FeaturePose2d", meas, data_cov); + FactorBase::emplace<FactorPose2dWithExtrinsics>(fea1, fea1, processor_pose2d, false, TOP_ABS); + + // Perturb frm, fix the extrinsics + frm->unfix(); + sensor_pose2d->fix(); + frm->perturb(10); + + // solve for frm1 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + + ASSERT_POSE2d_APPROX(frm->getStateVector("PO"), x0, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +TEST(FactorPose2dWithExtrinsics, fix_f_solve_s) +{ + for (int i = 0; i < 1e2; i++) + { + // Random pose + Vector3d x0 = Vector3d::Random() * 10; + pi2pi(x0(2)); + + // Random extrinsics + Vector3d xs = Vector3d::Random(); + pi2pi(xs(2)); + + // measurement + Vector3d meas; + meas(2) = pi2pi(x0(2) + xs(2)); + meas.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * xs.head<2>(); + + // Set states + frm->setState(x0,"PO"); + sensor_pose2d->setState(xs, "PO"); + + // feature & factor with meas measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap, "FeaturePose2d", meas, data_cov); + FactorBase::emplace<FactorPose2dWithExtrinsics>(fea1, fea1, processor_pose2d, false, TOP_ABS); + + // Perturb extrinsics, fix the frame + frm->fix(); + sensor_pose2d->unfix(); + sensor_pose2d->perturb(1); + + // solve for frm1 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + + ASSERT_POSE2d_APPROX(sensor_pose2d->getStateVector("PO"), xs, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_factor_pose_3d_with_extrinsics.cpp b/test/gtest_factor_pose_3d_with_extrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4631da1cf4e5279f3d64628036115e13d31ba5be --- /dev/null +++ b/test/gtest_factor_pose_3d_with_extrinsics.cpp @@ -0,0 +1,144 @@ +//--------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_pose_3d_with_extrinsics.h" +#include "core/capture/capture_pose.h" +#include "core/sensor/sensor_pose.h" +#include "core/processor/processor_pose.h" +#include "core/math/rotations.h" + +using namespace Eigen; +using namespace wolf; +using std::cout; +using std::endl; + +std::string wolf_root = _WOLF_ROOT_DIR; + +// Input odometry data and covariance +Matrix6d data_cov = 0.1*Matrix6d::Identity(); + +// Problem and solver +ProblemPtr problem_ptr = Problem::create("PO", 3); +SolverCeres solver(problem_ptr); + +// Sensor +auto sensor_pose3d = problem_ptr->installSensor("SensorPose3d", wolf_root + "/test/yaml/sensor_pose_3d.yaml", {wolf_root}); +auto processor_pose2d = ProcessorBase::emplace<ProcessorPose3d>(sensor_pose3d, std::make_shared<ParamsProcessorPose>()); + +// Two frames +FrameBasePtr frm = problem_ptr->emplaceFrame(0, "PO", (Vector7d() << 0, 0, 0, 0, 0, 0, 1).finished() ); + +// Capture for frm +auto cap = CaptureBase::emplace<CapturePose>(frm, 0, sensor_pose3d, Vector7d::Zero(), data_cov); + +TEST(FactorPose2dWithExtrinsics, check_tree) +{ + ASSERT_TRUE(problem_ptr->check(0)); +} + +TEST(FactorPose2dWithExtrinsics, solve_f_fix_s) +{ + for (int i = 0; i < 1e2; i++) + { + // Random pose + Vector7d x0 = Vector7d::Random() * 10; + x0.tail(4).normalize(); + + // Random extrinsics + Vector7d xs = Vector7d::Random(); + xs.tail(4).normalize(); + + // measurement + Vector7d meas; + meas.head<3>() = x0.head<3>() + Quaterniond(x0.tail<4>()) * xs.head<3>(); + meas.tail<4>() = (Quaterniond(x0.tail<4>()) * Quaterniond(xs.tail<4>())).coeffs(); + + // Set states + frm->setState(x0,"PO"); + sensor_pose3d->setState(xs, "PO"); + + // feature & factor with meas measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap, "FeaturePose3d", meas, data_cov); + FactorBase::emplace<FactorPose3dWithExtrinsics>(fea1, fea1, processor_pose2d, false, TOP_ABS); + + // Perturb frm, fix the extrinsics + frm->unfix(); + sensor_pose3d->fix(); + frm->perturb(10); + + // solve for frm1 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + + ASSERT_POSE3d_APPROX(frm->getStateVector("PO"), x0, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +TEST(FactorPose2dWithExtrinsics, fix_f_solve_s) +{ + for (int i = 0; i < 1e2; i++) + { + // Random pose + Vector7d x0 = Vector7d::Random() * 10; + x0.tail(4).normalize(); + + // Random extrinsics + Vector7d xs = Vector7d::Random(); + xs.tail(4).normalize(); + + // measurement + Vector7d meas; + meas.head<3>() = x0.head<3>() + Quaterniond(x0.tail<4>()) * xs.head<3>(); + meas.tail<4>() = (Quaterniond(x0.tail<4>()) * Quaterniond(xs.tail<4>())).coeffs(); + + // Set states + frm->setState(x0,"PO"); + sensor_pose3d->setState(xs, "PO"); + + // feature & factor with meas measurement + auto fea1 = FeatureBase::emplace<FeatureBase>(cap, "FeaturePose3d", meas, data_cov); + FactorBase::emplace<FactorPose3dWithExtrinsics>(fea1, fea1, processor_pose2d, false, TOP_ABS); + + // Perturb extrinsics, fix the frame + frm->fix(); + sensor_pose3d->unfix(); + sensor_pose3d->perturb(1); + + // solve for frm1 + std::string report = solver.solve(SolverManager::ReportVerbosity::FULL); + + ASSERT_POSE3d_APPROX(sensor_pose3d->getStateVector("PO"), xs, 1e-6); + + // remove feature (and factor) for the next loop + fea1->remove(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp index c7897da7108bd0001116bf1c497901ecaad5f7dd..b5e8188673f1ba77d38e984a14472da22d7b628f 100644 --- a/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp +++ b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp @@ -129,7 +129,7 @@ class FactorPose3dWithExtrinsicsBase_Test : public testing::Test auto params_proc = std::make_shared<ParamsProcessorPose>(); params_proc->name = "pose processor"; params_proc->time_tolerance = 0.5; - auto proc_pose = ProcessorBase::emplace<ProcessorPose>(sensor_pose_, params_proc); + auto proc_pose = ProcessorBase::emplace<ProcessorPose3d>(sensor_pose_, params_proc); } void TearDown() override{};