diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..8f284dd854c7257bc8e4af2bb9e9a056acf81d6c --- /dev/null +++ b/.clang-format @@ -0,0 +1,115 @@ +--- +Language: Cpp +BasedOnStyle: Google +IndentAccessModifiers: false +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 119 +# CommentPragmas: "^ IWYU pragma: ^\\.+" +CommentPragmas: '^\\.+' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +# IncludeCategories: +# - Regex: '^<pinocchio/fwd\.hpp>' +# Priority: 1 +# - Regex: '^<ext/.*\.h>' +# Priority: 3 +# - Regex: '^<.*\.h>' +# Priority: 2 +# - Regex: "^<.*" +# Priority: 3 +# - Regex: ".*" +# Priority: 4 +IncludeIsMainRegex: "([-_](test|unittest))?$" +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ff1e2eb36d460125006290be0be82e37296c54e9..76d222e0021420ee8c94f4ca631e8a3bbcab7526 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -112,7 +112,7 @@ stages: - cd build - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON .. - make -j$(nproc) - - ctest -j$(nproc) + - ctest --output-on-failure -j$(nproc) - make install ############ LICENSE HEADERS ############ diff --git a/CMakeLists.txt b/CMakeLists.txt index abe435bf640eab5cb095bda3aed702b14e617c03..c2538cb479c738ed6becd5a484ed21ecd3c88e2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,55 +100,64 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/internal/config.h.in "${WOLF_CONFIG_D # ============ HEADERS ============ SET(HDRS_MATH include/${PROJECT_NAME}/math/force_torque_delta_tools.h +include/${PROJECT_NAME}/math/force_torque_inertial_delta_tools.h ) SET(HDRS_CAPTURE -include/${PROJECT_NAME}/capture/capture_force_torque_preint.h +include/${PROJECT_NAME}/capture/capture_force_torque.h +include/${PROJECT_NAME}/capture/capture_force_torque_inertial.h include/${PROJECT_NAME}/capture/capture_inertial_kinematics.h include/${PROJECT_NAME}/capture/capture_leg_odom.h include/${PROJECT_NAME}/capture/capture_point_feet_nomove.h ) SET(HDRS_FACTOR +include/${PROJECT_NAME}/factor/factor_angular_momentum.h +include/${PROJECT_NAME}/factor/factor_force_torque_inertial.h +include/${PROJECT_NAME}/factor/factor_force_torque_inertial_dynamics.h include/${PROJECT_NAME}/factor/factor_force_torque.h -include/${PROJECT_NAME}/factor/factor_force_torque_preint.h include/${PROJECT_NAME}/factor/factor_inertial_kinematics.h include/${PROJECT_NAME}/factor/factor_point_feet_nomove.h include/${PROJECT_NAME}/factor/factor_point_feet_altitude.h ) SET(HDRS_FEATURE include/${PROJECT_NAME}/feature/feature_force_torque.h -include/${PROJECT_NAME}/feature/feature_force_torque_preint.h include/${PROJECT_NAME}/feature/feature_inertial_kinematics.h ) SET(HDRS_PROCESSOR -include/${PROJECT_NAME}/processor/processor_force_torque_preint.h +include/${PROJECT_NAME}/processor/processor_force_torque_inertial.h +include/${PROJECT_NAME}/processor/processor_force_torque_inertial_dynamics.h +include/${PROJECT_NAME}/processor/processor_force_torque.h include/${PROJECT_NAME}/processor/processor_inertial_kinematics.h include/${PROJECT_NAME}/processor/processor_point_feet_nomove.h ) SET(HDRS_SENSOR include/${PROJECT_NAME}/sensor/sensor_force_torque.h +include/${PROJECT_NAME}/sensor/sensor_force_torque_inertial.h include/${PROJECT_NAME}/sensor/sensor_inertial_kinematics.h include/${PROJECT_NAME}/sensor/sensor_point_feet_nomove.h ) # ============ SOURCES ============ SET(SRCS_CAPTURE -src/capture/capture_force_torque_preint.cpp +src/capture/capture_force_torque.cpp +src/capture/capture_force_torque_inertial.cpp src/capture/capture_inertial_kinematics.cpp src/capture/capture_leg_odom.cpp src/capture/capture_point_feet_nomove.cpp ) SET(SRCS_FEATURE src/feature/feature_force_torque.cpp -src/feature/feature_force_torque_preint.cpp src/feature/feature_inertial_kinematics.cpp - ) +) SET(SRCS_PROCESSOR -src/processor/processor_force_torque_preint.cpp +src/processor/processor_force_torque_inertial.cpp +src/processor/processor_force_torque_inertial_dynamics.cpp +src/processor/processor_force_torque.cpp src/processor/processor_inertial_kinematics.cpp src/processor/processor_point_feet_nomove.cpp ) SET(SRCS_SENSOR src/sensor/sensor_force_torque.cpp +src/sensor/sensor_force_torque_inertial.cpp src/sensor/sensor_inertial_kinematics.cpp src/sensor/sensor_point_feet_nomove.cpp ) diff --git a/demos/processor_ft_preint.yaml b/demos/processor_ft_preint.yaml index d9b49d70b860fe2e3029a4cc9bd62ffcc2fdc6c8..e9d8c3b37000e40a58ac253eaec04a96f0ae4232 100644 --- a/demos/processor_ft_preint.yaml +++ b/demos/processor_ft_preint.yaml @@ -1,5 +1,5 @@ -type: "ProcessorForceTorquePreint" # This must match the KEY used in the SensorFactory. Otherwise it is an error. -name: "PFTPreint" # This is ignored. The name provided to the SensorFactory prevails +type: "ProcessorForceTorque" # This must match the KEY used in the SensorFactory. Otherwise it is an error. +name: "PFT" # This is ignored. The name provided to the SensorFactory prevails time_tolerance: 0.0005 # Time tolerance for joining KFs unmeasured_perturbation_std: 0.0000000 sensor_ikin_name: "SenIK" diff --git a/demos/solo_imu_kine.cpp b/demos/solo_imu_kine.cpp index a9f52035066348852f9a296504e414520669656e..02a07f6c138bab362d7a2357aa23dc63d2b6826e 100644 --- a/demos/solo_imu_kine.cpp +++ b/demos/solo_imu_kine.cpp @@ -68,12 +68,12 @@ #include "bodydynamics/sensor/sensor_inertial_kinematics.h" #include "bodydynamics/sensor/sensor_force_torque.h" #include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" +#include "bodydynamics/capture/capture_force_torque_.h" #include "bodydynamics/capture/capture_leg_odom.h" #include "bodydynamics/processor/processor_inertial_kinematics.h" -#include "bodydynamics/processor/processor_force_torque_preint.h" +#include "bodydynamics/processor/processor_force_torque_.h" #include "bodydynamics/factor/factor_inertial_kinematics.h" -#include "bodydynamics/factor/factor_force_torque_preint.h" +#include "bodydynamics/factor/factor_force_torque_.h" #include "bodydynamics/sensor/sensor_point_feet_nomove.h" #include "bodydynamics/processor/processor_point_feet_nomove.h" diff --git a/demos/solo_imu_kine_mocap.cpp b/demos/solo_imu_kine_mocap.cpp index 82d2df1c42da66a66e6894fe49e037b4a04259cd..8ae580418ec8691b984c8a5c3a11e1c3ea81aab5 100644 --- a/demos/solo_imu_kine_mocap.cpp +++ b/demos/solo_imu_kine_mocap.cpp @@ -68,12 +68,12 @@ #include "bodydynamics/sensor/sensor_inertial_kinematics.h" #include "bodydynamics/sensor/sensor_force_torque.h" #include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" +#include "bodydynamics/capture/capture_force_torque_.h" #include "bodydynamics/capture/capture_leg_odom.h" #include "bodydynamics/processor/processor_inertial_kinematics.h" -#include "bodydynamics/processor/processor_force_torque_preint.h" +#include "bodydynamics/processor/processor_force_torque_.h" #include "bodydynamics/factor/factor_inertial_kinematics.h" -#include "bodydynamics/factor/factor_force_torque_preint.h" +#include "bodydynamics/factor/factor_force_torque_.h" #include "bodydynamics/capture/capture_point_feet_nomove.h" #include "bodydynamics/sensor/sensor_point_feet_nomove.h" diff --git a/demos/solo_real_povcdl_estimation.cpp b/demos/solo_real_povcdl_estimation.cpp index 9953db41444d0d1b1da732f5460c3bd398cd3a63..181fc3d44d83f1d89755f34abb5a24f4a806e4f3 100644 --- a/demos/solo_real_povcdl_estimation.cpp +++ b/demos/solo_real_povcdl_estimation.cpp @@ -58,12 +58,12 @@ #include "bodydynamics/sensor/sensor_inertial_kinematics.h" #include "bodydynamics/sensor/sensor_force_torque.h" #include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" +#include "bodydynamics/capture/capture_force_torque_.h" #include "bodydynamics/capture/capture_leg_odom.h" #include "bodydynamics/processor/processor_inertial_kinematics.h" -#include "bodydynamics/processor/processor_force_torque_preint.h" +#include "bodydynamics/processor/processor_force_torque_.h" #include "bodydynamics/factor/factor_inertial_kinematics.h" -#include "bodydynamics/factor/factor_force_torque_preint.h" +#include "bodydynamics/factor/factor_force_torque_.h" #include "bodydynamics/capture/capture_point_feet_nomove.h" #include "bodydynamics/sensor/sensor_point_feet_nomove.h" @@ -284,7 +284,7 @@ int main (int argc, char **argv) { SensorBasePtr sen_ft_base = problem->installSensor("SensorForceTorque", "SenFT", VectorXd(0), intr_ft); // SensorBasePtr sen_ft_base = problem->installSensor("SensorForceTorque", "SenFT", VectorXd(0), bodydynamics_root_dir + "/demos/sensor_ft.yaml"); SensorForceTorquePtr sen_ft = std::static_pointer_cast<SensorForceTorque>(sen_ft_base); - ParamsProcessorForceTorquePreintPtr params_sen_ft = std::make_shared<ParamsProcessorForceTorquePreint>(); + ParamsProcessorForceTorquePtr params_sen_ft = std::make_shared<ParamsProcessorForceTorque>(); params_sen_ft->sensor_ikin_name = "SenIK"; params_sen_ft->sensor_angvel_name = "SenImu"; params_sen_ft->nbc = nbc; @@ -296,9 +296,9 @@ int main (int argc, char **argv) { params_sen_ft->dist_traveled = 20000.0; params_sen_ft->angle_turned = 1000; params_sen_ft->voting_active = false; - ProcessorBasePtr proc_ft_base = problem->installProcessor("ProcessorForceTorquePreint", "PFTPreint", sen_ft, params_sen_ft); - // ProcessorBasePtr proc_ft_base = problem->installProcessor("ProcessorForceTorquePreint", "PFTPreint", "SenFT", bodydynamics_root_dir + "/demos/processor_ft_preint.yaml"); - ProcessorForceTorquePreintPtr proc_ftpreint = std::static_pointer_cast<ProcessorForceTorquePreint>(proc_ft_base); + ProcessorBasePtr proc_ft_base = problem->installProcessor("ProcessorForceTorque", "PFT", sen_ft, params_sen_ft); + // ProcessorBasePtr proc_ft_base = problem->installProcessor("ProcessorForceTorque", "PFT", "SenFT", bodydynamics_root_dir + "/demos/processor_ft_preint.yaml"); + ProcessorForceTorquePtr proc_ft = std::static_pointer_cast<ProcessorForceTorque>(proc_ft_base); // SENSOR + PROCESSOR POINT FEET NOMOVE ParamsSensorPointFeetNomovePtr intr_pfn = std::make_shared<ParamsSensorPointFeetNomove>(); @@ -355,7 +355,7 @@ int main (int argc, char **argv) { VectorXd meas_ikin(9); meas_ikin << i_p_ic, i_v_ic, i_omg_oi; auto CIKin0 = std::make_shared<CaptureInertialKinematics>(t0, sen_ikin, meas_ikin, i_Iw, i_Lc_gesti); CIKin0->process(); - proc_ftpreint->setOrigin(KF1); + proc_ft->setOrigin(KF1); //////////////////////////////////////////// // INITIAL BIAS FACTORS @@ -557,7 +557,7 @@ int main (int argc, char **argv) { CImu->process(); auto CIKin = std::make_shared<CaptureInertialKinematics>(ts, sen_ikin, meas_ikin, i_Iw, i_Lc_gesti); CIKin->process(); - auto CFTpreint = std::make_shared<CaptureForceTorquePreint>(ts, sen_ft, CIKin, CImu, cap_ftp_data, Qftp); + auto CFTpreint = std::make_shared<CaptureForceTorque>(ts, sen_ft, CIKin, CImu, cap_ftp_data, Qftp); CFTpreint->process(); diff --git a/doc/.gitkeep b/doc/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/doc/figures/.gitkeep b/doc/figures/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/doc/figures/dyn-model.pdf b/doc/figures/dyn-model.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e4c8ffd0da398795a7aaf4f54cf7f7967fa738c1 Binary files /dev/null and b/doc/figures/dyn-model.pdf differ diff --git a/include/bodydynamics/capture/capture_force_torque_preint.h b/include/bodydynamics/capture/capture_force_torque.h similarity index 94% rename from include/bodydynamics/capture/capture_force_torque_preint.h rename to include/bodydynamics/capture/capture_force_torque.h index 81db7bf05f90fc9048a907253b477b73f8c748a5..fab1ca4951a7ec09fef965b2176a5d8a848aee66 100644 --- a/include/bodydynamics/capture/capture_force_torque_preint.h +++ b/include/bodydynamics/capture/capture_force_torque.h @@ -32,9 +32,9 @@ namespace wolf { -WOLF_PTR_TYPEDEFS(CaptureForceTorquePreint); +WOLF_PTR_TYPEDEFS(CaptureForceTorque); -class CaptureForceTorquePreint : public CaptureMotion +class CaptureForceTorque : public CaptureMotion { public: /* @@ -51,7 +51,7 @@ class CaptureForceTorquePreint : public CaptureMotion qbl1, 4 : orientation of foot 1 in base frame qbl2, 4 : orientation of foot 2 in base frame */ - CaptureForceTorquePreint( + CaptureForceTorque( const TimeStamp& _init_ts, SensorBasePtr _sensor_ptr, CaptureInertialKinematicsPtr _capture_IK_ptr, // to get the pbc bias @@ -60,7 +60,7 @@ class CaptureForceTorquePreint : public CaptureMotion const Eigen::MatrixXd& _data_cov, CaptureBasePtr _capture_origin_ptr = nullptr); - ~CaptureForceTorquePreint() override; + ~CaptureForceTorque() override; CaptureBaseConstPtr getIkinCaptureOther() const { return cap_ikin_other_;} CaptureBasePtr getIkinCaptureOther(){ return cap_ikin_other_;} diff --git a/src/feature/feature_force_torque_preint.cpp b/include/bodydynamics/capture/capture_force_torque_inertial.h similarity index 56% rename from src/feature/feature_force_torque_preint.cpp rename to include/bodydynamics/capture/capture_force_torque_inertial.h index 1a21081d827843645a62c75508b3ceda10794c71..b6ba6d7752cd7c4427df706e84ffedd07ab9873e 100644 --- a/src/feature/feature_force_torque_preint.cpp +++ b/include/bodydynamics/capture/capture_force_torque_inertial.h @@ -19,18 +19,27 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // //--------LICENSE_END-------- -#include "bodydynamics/feature/feature_force_torque_preint.h" -namespace wolf { +#ifndef CAPTURE_FORCE_TORQUE_INERTIAL_H +#define CAPTURE_FORCE_TORQUE_INERTIAL_H -FeatureForceTorquePreint::FeatureForceTorquePreint(const Eigen::VectorXd& _delta_preintegrated, - const Eigen::MatrixXd& _delta_preintegrated_covariance, - const Eigen::Vector6d& _biases_preint, - const Eigen::Matrix<double,12,6>& _J_delta_biases) : - FeatureBase("FeatureForceTorquePreint", _delta_preintegrated, _delta_preintegrated_covariance), - pbc_bias_preint_(_biases_preint.head<3>()), - gyro_bias_preint_(_biases_preint.tail<3>()), - J_delta_bias_(_J_delta_biases) +#include <core/capture/capture_motion.h> + +namespace wolf { -} -} // namespace wolf +WOLF_PTR_TYPEDEFS(CaptureForceTorqueInertial); + +class CaptureForceTorqueInertial : public CaptureMotion +{ + public: + CaptureForceTorqueInertial(const TimeStamp& _init_ts, + SensorBasePtr _sensor_ptr, + const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + CaptureBasePtr _capture_origin_ptr = nullptr); + virtual ~CaptureForceTorqueInertial(); +}; + +} // namespace wolf + +#endif // CAPTURE_FORCE_TORQUE_INERTIAL_H \ No newline at end of file diff --git a/include/bodydynamics/factor/factor_angular_momentum.h b/include/bodydynamics/factor/factor_angular_momentum.h new file mode 100644 index 0000000000000000000000000000000000000000..5c120ae2eb369c9c7e373b66f5d55dbd2996f0ba --- /dev/null +++ b/include/bodydynamics/factor/factor_angular_momentum.h @@ -0,0 +1,172 @@ +//--------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-------- +#ifndef FACTOR_ANGULAR_MOMENTUM_H +#define FACTOR_ANGULAR_MOMENTUM_H + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include <core/feature/feature_motion.h> +#include <core/factor/factor_autodiff.h> + +namespace wolf +{ +using namespace Eigen; +using namespace bodydynamics; + +WOLF_PTR_TYPEDEFS(FactorAngularMomentum); + +/** + * @brief + * + * This factor evaluates the real angular velocity against the one obtained with the pre-integrated angular momentum + * and the moment of inertia. + * + * State blocks considered: + * - Frame + * 'L' angular momentum + * - Capture + * 'I' IMU biases + * - Sensor Force Torque Inertial + * 'i' inertia matrix components (we are assuming that it is a diagonal matrix) + * + * The residual computed has the following components, in this order + * - angular velocity error according to FT preintegration + */ +class FactorAngularMomentum : public FactorAutodiff<FactorAngularMomentum, 3, 3, 3, 6, 3> // State Blocks: L, I, i +{ + public: + FactorAngularMomentum(const FeatureMotionPtr& _ftr, // gets measurement and access to parents + const ProcessorBasePtr& _processor, // gets access to processor and sensor + bool _apply_loss_function, + FactorStatus _status = FAC_ACTIVE); + + ~FactorAngularMomentum() override = default; + + template <typename T> + bool operator()(const T* const _L, // ang momentum + const T* const _I, // IMU bias (acc and gyro) + const T* const _i, // inertia matrix + T* _res) const; // residual + + template <typename D1, typename D2, typename D3, typename D4> + bool residual(const MatrixBase<D1>& _L, + const MatrixBase<D2>& _I, + const MatrixBase<D3>& _i, + MatrixBase<D4>& _res) const; + + Vector3d residual() const; + + private: + Matrix<double, 12, 1> data; + Matrix<double, 3, 3> sqrt_info_upper_; +}; + +inline FactorAngularMomentum::FactorAngularMomentum(const FeatureBasePtr& _ftr, + const ProcessorBasePtr& _processor, + bool _apply_loss_function, + FactorStatus _status) + : FactorAutodiff<FactorAngularMomentum, 3, 3, 3, 6, 3>( + "FactorAngularMomentum", + TOP_MOTION, + _ftr, // parent feature + nullptr, // frame other + nullptr, // capture other + nullptr, // feature other + nullptr, // landmark other + _processor, // processor + _apply_loss_function, + _status, + _ftr->getFrame()->getStateBlock('L'), // previous frame L + _capture_origin->getStateBlock('I'), // previous frame IMU bias + _processor->getSensor()->getStateBlock('i'), // sensor i + data(ftr->getMeasurement()), + sqrt_info_upper_(_processor->getSensor()->getNoiseCov().block<3,3>(3,3))) +{ + // +} + +template <typename D1, typename D2, typename D3, typename D4> +inline bool FactorAngularMomentum::residual(const MatrixBase<D1>& _L, + const MatrixBase<D2>& _I, + const MatrixBase<D3>& _i, + MatrixBase<D4>& _res) const +{ + MatrixSizeCheck<3, 1>::check(_res); + + /* Will do the following: + * + * Compute the real angular velocity through the measurement and the IMU bias + * w_real = w_m - w_b + * + * Compute the angular velocity obtained by the relation between L pre-integrated and the i + * w_est = i^(-1)*L + * + * Compute error between them + * err = w_m - w_b - i^(-1)*L + * + * Compute residual + * res = W * err , with W the sqrt of the info matrix. + */ + + Matrix<T, 3, 1> w_real = data.segment<3>(3) - _I.segment<3>(3); + double Lx = _L(0); + double Ly = _L(1); + double Lz = _L(2); + double ixx = _i(0); + double iyy = _i(1); + double izz = _i(2); + Matrix<T, 3, 1> w_est = [Lx/ixx, Ly/iyy, Lz/izz]; + Matrix<T, 3, 1> err = w_real - w_est; + _res = sqrt_info_upper_ * err; + + return true; +} + +inline Vector3d FactorAngularMomentum::residual() const +{ + Vector3d res(3); + Vector3d L = getFrame()->getStateBlock('L')->getState(); + Vector6d I = getFeature()->getCapture()->getSensor()->getState(); + Vector3d i = getFeature()->getSensor()->getStateBlock('i')->getState(); + + residual(L, I, i, res); + return res; +} + +template <typename T> +inline bool FactorAngularMomentum::operator()(const T* const _L, + const T* const _I, + const T* const _i, + T* _res) const +{ + Map<const Matrix<T, 3, 1>> L(_L); + Map<const Matrix<T, 6, 1>> I(_I); + Map<const Matrix<T, 3, 1>> i(_i); + Map<Matrix<T, 3, 1>> res(_res); + + residual(L, I, i, res); + + return true; +} + +} // namespace wolf + +#endif // FACTOR_ANGULAR_MOMENTUM_H \ No newline at end of file diff --git a/include/bodydynamics/factor/factor_force_torque.h b/include/bodydynamics/factor/factor_force_torque.h index 958398a325c0e6c77fd5b70a2cabb4497fec746b..b9ff1b4c45b3e4108c0ed1e755c7a326044a3f76 100644 --- a/include/bodydynamics/factor/factor_force_torque.h +++ b/include/bodydynamics/factor/factor_force_torque.h @@ -19,264 +19,325 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // //--------LICENSE_END-------- -/** - * \file factor_force_torque.h - * - * Created on: Feb 19, 2020 - * \author: mfourmy - * - * Works only for 2 limbs - */ - - - #ifndef FACTOR_FORCE_TORQUE_H_ #define FACTOR_FORCE_TORQUE_H_ -#include <core/math/rotations.h> -#include <core/math/covariance.h> -#include <core/factor/factor_autodiff.h> -#include <core/feature/feature_base.h> - +//Wolf includes +#include "bodydynamics/capture/capture_force_torque.h" #include "bodydynamics/feature/feature_force_torque.h" +#include "bodydynamics/sensor/sensor_force_torque.h" +#include "core/factor/factor_autodiff.h" +#include "core/math/rotations.h" -namespace wolf -{ +//Eigen include +namespace wolf { + WOLF_PTR_TYPEDEFS(FactorForceTorque); -class FactorForceTorque : public FactorAutodiff<FactorForceTorque, 9, 3,3,3, 3,3,3,3,4> +//class +class FactorForceTorque : public FactorAutodiff<FactorForceTorque, 12, 3,3,3,4,3,6, 3,3,3,4> { public: - FactorForceTorque(const FeatureBasePtr& _feat, - const FrameBasePtr& _frame_other, - const StateBlockPtr& _sb_bp_other, - const ProcessorBasePtr& _processor_ptr, - bool _apply_loss_function, - FactorStatus _status = FAC_ACTIVE); - - ~FactorForceTorque() override { /* nothing */ } - - /* - _ck : COM position in world frame, time k - _cdk : COM velocity in world frame, time k - _Lck : Angular momentum in world frame, time k - _ckm : COM position in world frame, time k-1 - _cdkm : COM velocity in world frame, time k-1 - _Lckm : Angular momentum in world frame, time k-1 - _bpkm : COM position measurement bias, time k-1 - _qkm : Base orientation in world frame, time k-1 + FactorForceTorque(const FeatureForceTorquePtr& _ftr_ptr, + const CaptureForceTorquePtr& _cap_origin_ptr, // gets to bp1, bw1 + const ProcessorBasePtr& _processor_ptr, + const CaptureBasePtr& _cap_ikin_other, + const CaptureBasePtr& _cap_gyro_other, + bool _apply_loss_function, + FactorStatus _status = FAC_ACTIVE); + + ~FactorForceTorque() override = default; + + /** \brief : compute the residual from the state blocks being iterated by the solver. + -> computes the expected measurement + -> corrects actual measurement with new bias + -> compares the corrected measurement with the expected one + -> weights the result with the covariance of the noise (residual = sqrt_info_matrix * err;) */ template<typename T> - bool operator () ( - const T* const _ck, - const T* const _cdk, - const T* const _Lck, - const T* const _ckm, - const T* const _cdkm, - const T* const _Lckm, - const T* const _bpkm, - const T* const _qkm, - T* _res) const; - - void computeJac(const FeatureForceTorqueConstPtr& _feat, - const double& _mass, - const double& _dt, - const Eigen::VectorXd& _bp, - Eigen::Matrix<double, 9, 15> & _J_ny_nz) const; - - // void recomputeJac(const FeatureForceTorquePtr& _feat, - // const double& _dt, - // const Eigen::VectorXd& _bp, - // Eigen::Matrix<double, 9, 15>& _J_ny_nz) const; - - void retrieveMeasurementCovariance(const FeatureForceTorquePtr& feat, Eigen::Matrix<double,15,15>& cov); - - StateBlockPtr sb_bp_other_; - double mass_; - double dt_; - Eigen::Matrix<double,15,15> cov_meas_; - // Eigen::Matrix<double, 9, 15> J_ny_nz_; // cannot be modified in operator() since const function - // Eigen::Matrix9d errCov_; + bool operator ()(const T* const _c1, + const T* const _cd1, + const T* const _Lc1, + const T* const _q1, + const T* const _bp1, + const T* const _bw1, + const T* const _c2, + const T* const _cd2, + const T* const _Lc2, + const T* const _q2, + T* _res) const; + + /** \brief : compute the residual from the state blocks being iterated by the solver. (same as operator()) + -> computes the expected measurement + -> corrects actual measurement with new bias + -> compares the corrected measurement with the expected one + -> weights the result with the covariance of the noise (residual = sqrt_info_matrix * err;) + * params : + * _c1 : COM position at time t1 in world frame + * _cd1: COM velocity at time t1 in world frame + * _Lc1: Angular momentum at time t1 in world frame + * _q1 : Base orientation at time t1 + * _bp1: COM position measurement at time t1 in body frame + * _bw1: gyroscope bias at time t1 in body frame + * _c2 : COM position at time t2 in world frame + * _cd2: COM velocity at time t2 in world frame + * _Lc2: Angular momentum at time t2 in world frame + * _q2 : Base orientation at time t2 + * _res: Factor residuals (c,cd,Lc,o) O is rotation vector... NOT A QUATERNION + */ + template<typename D1, typename D2, typename D3, typename D4> + bool residual(const MatrixBase<D1> & _c1, + const MatrixBase<D1> & _cd1, + const MatrixBase<D1> & _Lc1, + const QuaternionBase<D2> & _q1, + const MatrixBase<D1> & _bp1, + const MatrixBase<D1> & _bw1, + const MatrixBase<D1> & _c2, + const MatrixBase<D1> & _cd2, + const MatrixBase<D1> & _Lc2, + const QuaternionBase<D3> & _q2, + MatrixBase<D4> & _res) const; + + // Matrix<double,12,1> residual() const; + // double cost() const; + + private: + /// Preintegrated delta + Vector3d dc_preint_; + Vector3d dcd_preint_; + Vector3d dLc_preint_; + Quaterniond dq_preint_; + + // Biases used during preintegration + Vector3d pbc_bias_preint_; + Vector3d gyro_bias_preint_; + + // Jacobians of preintegrated deltas wrt biases + Matrix3d J_dLc_pb_; + Matrix3d J_dc_wb_; + Matrix3d J_dcd_wb_; + Matrix3d J_dLc_wb_; + Matrix3d J_dq_wb_; + + /// Metrics + double dt_; ///< delta-time and delta-time-squared between keyframes + double mass_; ///< constant total robot mass + }; -} /* namespace wolf */ - - -namespace wolf { - -FactorForceTorque::FactorForceTorque( - const FeatureBasePtr& _feat, - const FrameBasePtr& _frame_other, - const StateBlockPtr& _sb_bp_other, - const ProcessorBasePtr& _processor_ptr, - bool _apply_loss_function, - FactorStatus _status) : - FactorAutodiff("FactorForceTorque", - TOP_GEOM, - _feat, - _frame_other, // _frame_other_ptr - nullptr, // _capture_other_ptr - nullptr, // _feature_other_ptr - nullptr, // _landmark_other_ptr - _processor_ptr, - _apply_loss_function, - _status, - _feat->getFrame()->getStateBlock('C'), // COM position, current - _feat->getFrame()->getStateBlock('D'), // COM velocity (bad name), current - _feat->getFrame()->getStateBlock('L'), // Angular momentum, current - _frame_other->getStateBlock('C'), // COM position, previous - _frame_other->getStateBlock('D'), // COM velocity (bad name), previous - _frame_other->getStateBlock('L'), // Angular momentum, previous - _sb_bp_other, // BC relative position bias, previous - _frame_other->getStateBlock('O') // Base orientation, previous - ), - sb_bp_other_(_sb_bp_other) +///////////////////// IMPLEMENTATION //////////////////////////// + +inline FactorForceTorque::FactorForceTorque( + const FeatureForceTorquePtr& _ftr_ptr, + const CaptureForceTorquePtr& _cap_origin_ptr, + const ProcessorBasePtr& _processor_ptr, + const CaptureBasePtr& _cap_ikin_other, + const CaptureBasePtr& _cap_gyro_other, + bool _apply_loss_function, + FactorStatus _status) : + FactorAutodiff<FactorForceTorque, 12, 3,3,3,4,3,6, 3,3,3,4>( + "FactorForceTorque", + TOP_MOTION, + _ftr_ptr, + _cap_origin_ptr->getFrame(), + _cap_origin_ptr, + nullptr, + nullptr, + _processor_ptr, + _apply_loss_function, + _status, + _cap_origin_ptr->getFrame()->getStateBlock('C'), + _cap_origin_ptr->getFrame()->getStateBlock('D'), + _cap_origin_ptr->getFrame()->getStateBlock('L'), + _cap_origin_ptr->getFrame()->getO(), + _cap_ikin_other->getSensorIntrinsic(), + _cap_gyro_other->getSensorIntrinsic(), + _ftr_ptr->getFrame()->getStateBlock('C'), + _ftr_ptr->getFrame()->getStateBlock('D'), + _ftr_ptr->getFrame()->getStateBlock('L'), + _ftr_ptr->getFrame()->getO() + ), + dc_preint_(_ftr_ptr->getMeasurement().head<3>()), // dc, dcd, dLc, dq at preintegration time + dcd_preint_(_ftr_ptr->getMeasurement().segment<3>(3)), + dLc_preint_(_ftr_ptr->getMeasurement().segment<3>(6)), + dq_preint_(_ftr_ptr->getMeasurement().data()+9), + pbc_bias_preint_( std::static_pointer_cast<FeatureForceTorque>(_ftr_ptr)->getPbcBiasPreint()), + gyro_bias_preint_(std::static_pointer_cast<FeatureForceTorque>(_ftr_ptr)->getGyroBiasPreint()), + J_dLc_pb_(_ftr_ptr->getJacobianBias().block<3,3>(6,0)), // Jac dLc wrt pbc bias + J_dc_wb_ (_ftr_ptr->getJacobianBias().block<3,3>(0,3)), // Jac dc wrt gyro bias + J_dcd_wb_(_ftr_ptr->getJacobianBias().block<3,3>(3,3)), // Jac dc wrt gyro bias + J_dLc_wb_(_ftr_ptr->getJacobianBias().block<3,3>(6,3)), // Jac dcd wrt gyro bias + J_dq_wb_ (_ftr_ptr->getJacobianBias().block<3,3>(9,3)), // Jac dLc wrt gyro bias + dt_(_ftr_ptr->getFrame()->getTimeStamp() - _cap_origin_ptr->getFrame()->getTimeStamp()), + mass_(std::static_pointer_cast<SensorForceTorque>(_ftr_ptr->getCapture()->getSensor())->getMass()) { - FeatureForceTorquePtr feat = std::static_pointer_cast<FeatureForceTorque>(_feat); - mass_ = feat->getMass(); - dt_ = feat->getDt(); - retrieveMeasurementCovariance(feat, cov_meas_); +// } - -void FactorForceTorque::retrieveMeasurementCovariance(const FeatureForceTorquePtr& feat, Eigen::Matrix<double,15,15>& cov) +template<typename T> +inline bool FactorForceTorque::operator ()(const T* const _c1, + const T* const _cd1, + const T* const _Lc1, + const T* const _q1, + const T* const _bp1, + const T* const _bw1, + const T* const _c2, + const T* const _cd2, + const T* const _Lc2, + const T* const _q2, + T* _res) const { - cov.setZero(); - cov.block<6,6>(0,0) = feat->getCovForcesMeas(); // reset some extra zeros - cov.block<6,6>(6,6) = feat->getCovTorquesMeas(); // reset some extra zeros - cov.block<3,3>(12,12) = feat->getCovPbcMeas(); -} + Map<const Matrix<T,3,1> > c1(_c1); + Map<const Matrix<T,3,1> > cd1(_cd1); + Map<const Matrix<T,3,1> > Lc1(_Lc1); + Map<const Quaternion<T> > q1(_q1); + Map<const Matrix<T,3,1> > bp1(_bp1); + Map<const Matrix<T,3,1> > bw1(_bw1 + 3); // bw1 = bimu = [ba, bw] + Map<const Matrix<T,3,1> > c2(_c2); + Map<const Matrix<T,3,1> > cd2(_cd2); + Map<const Matrix<T,3,1> > Lc2(_Lc2); + Map<const Quaternion<T> > q2(_q2); + Map<Matrix<T,12,1> > res(_res); + + residual(c1, cd1, Lc1, q1, bp1, bw1, c2, cd2, Lc2, q2, res); + return true; +} -void FactorForceTorque::computeJac(const FeatureForceTorqueConstPtr& _feat, - const double& _mass, - const double& _dt, - const Eigen::VectorXd& _bp, - Eigen::Matrix<double, 9, 15>& _J_ny_nz) const +template<typename D1, typename D2, typename D3, typename D4> +inline bool FactorForceTorque::residual(const MatrixBase<D1> & _c1, + const MatrixBase<D1> & _cd1, + const MatrixBase<D1> & _Lc1, + const QuaternionBase<D2> & _q1, + const MatrixBase<D1> & _bp1, + const MatrixBase<D1> & _bw1, + const MatrixBase<D1> & _c2, + const MatrixBase<D1> & _cd2, + const MatrixBase<D1> & _Lc2, + const QuaternionBase<D3> & _q2, + MatrixBase<D4> & _res) const { - _J_ny_nz.setZero(); - - // Measurements retrieval - Eigen::Map<const Eigen::Vector3d> f1 (_feat->getForcesMeas().data()); - Eigen::Map<const Eigen::Vector3d> f2 (_feat->getForcesMeas().data() + 3 ); - Eigen::Map<const Eigen::Vector3d> tau1(_feat->getTorquesMeas().data()); - Eigen::Map<const Eigen::Vector3d> tau2(_feat->getTorquesMeas().data() + 3 ); - Eigen::Map<const Eigen::Vector3d> pbl1(_feat->getKinMeas().data()); - Eigen::Map<const Eigen::Vector3d> pbl2(_feat->getKinMeas().data() + 3 ); - Eigen::Map<const Eigen::Quaterniond> bql1(_feat->getKinMeas().data() + 6); - Eigen::Map<const Eigen::Quaterniond> bql2(_feat->getKinMeas().data() + 10); - Eigen::Map<const Eigen::Vector3d> pbc (_feat->getPbcMeas().data()); - - Eigen::Matrix3d bRl1 = q2R(bql1); - Eigen::Matrix3d bRl2 = q2R(bql2); - _J_ny_nz.block<3,3>(0,0) = 0.5*bRl1*pow(_dt,2)/_mass; - _J_ny_nz.block<3,3>(0,3) = 0.5*bRl2*pow(_dt,2)/_mass; - _J_ny_nz.block<3,3>(3,0) = bRl1*_dt/_mass; - _J_ny_nz.block<3,3>(3,3) = bRl2*_dt/_mass; - _J_ny_nz.block<3,3>(6,0) = skew(pbl1 - pbc + _bp)*bRl1*_dt; - _J_ny_nz.block<3,3>(6,3) = skew(pbl2 - pbc + _bp)*bRl2*_dt; - _J_ny_nz.block<3,3>(6,6) = bRl1*_dt; - _J_ny_nz.block<3,3>(6,9) = bRl2*_dt; - _J_ny_nz.block<3,3>(6,12) = (skew(bRl1*f1) + skew(bRl2*f2))*_dt; + /* Help for the Imu residual function + * + * Notations: + * D_* : a motion in the Delta manifold -- implemented as 10-vectors with [Dp, Dq, Dv] + * T_* : a motion difference in the Tangent space to the manifold -- implemented as 9-vectors with [Dp, Do, Dv] + * b* : a bias + * J* : a Jacobian matrix + * + * We use the following functions from imu_tools.h: + * D = betweenStates(x1,x2,dt) : obtain a Delta from two states separated dt=t2-t1 + * D2 = plus(D1,T) : plus operator, D2 = D1 (+) T + * T = diff(D1,D2) : minus operator, T = D2 (-) D1 + * + * Two methods are possible (select with #define below this note) : + * + * Computations common to methods 1 and 2: + * D_exp = betweenStates(x1,x2,dt) // Predict delta from states + * T_step = J_preint * (b - b_preint) // compute the delta correction step due to bias change + * + * Method 1: + * D_corr = plus(D_preint, T_step) // correct the pre-integrated delta with correction step due to bias change + * T_err = diff(D_exp, D_corr) // compare against expected delta + * res = W.sqrt * T_err + * + * results in: + * res = W.sqrt * ( diff ( D_exp, plus(D_preint, J_preint * (b - b_preint) ) ) ) + * + * Method 2: + * T_diff = diff(D_preint, D_exp) // compare pre-integrated against expected delta + * T_err = T_diff - T_step // the difference should match the correction step due to bias change + * res = W.sqrt * err + * + * results in : + * res = W.sqrt * ( ( diff ( D_preint , D_exp ) ) - J_preint * (b - b_preint) ) + * + * NOTE: See optimization report at the end of this file for comparisons of both methods. + */ + + //needed typedefs + typedef typename D1::Scalar T; + + EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(D4, 12); + + // 1. Expected delta from states + Matrix<T, 3, 1 > dc_exp; + Matrix<T, 3, 1 > dcd_exp; + Matrix<T, 3, 1 > dLc_exp; + Quaternion<T> dq_exp; + + bodydynamics::betweenStates(_c1, _cd1, _Lc1, _q1, _c2, _cd2, _Lc2, _q2, dt_, dc_exp, dcd_exp, dLc_exp, dq_exp); + + // 2. Corrected integrated delta: delta_corr = delta_preint (+) J_bias * (bias_current - bias_preint) + + // 2.a. Compute the delta step in tangent space: step = J_bias * (bias - bias_preint) + Matrix<T, 3, 1> dc_step = J_dc_wb_ * (_bw1 - gyro_bias_preint_); + Matrix<T, 3, 1> dcd_step = J_dcd_wb_ * (_bw1 - gyro_bias_preint_); + Matrix<T, 3, 1> dLc_step = J_dLc_pb_ * (_bp1 - pbc_bias_preint_) + J_dLc_wb_ * (_bw1 - gyro_bias_preint_); + Matrix<T, 3, 1> do_step = J_dq_wb_ * (_bw1 - gyro_bias_preint_); + + // 2.b. Add the correction step to the preintegrated delta: delta_cor = delta_preint (+) step + Matrix<T,3,1> dc_correct; + Matrix<T,3,1> dcd_correct; + Matrix<T,3,1> dLc_correct; + Quaternion<T> dq_correct; + + bodydynamics::plus(dc_preint_.cast<T>(), dcd_preint_.cast<T>(), dLc_preint_.cast<T>(), dq_preint_.cast<T>(), + dc_step, dcd_step, dLc_step, do_step, + dc_correct, dcd_correct, dLc_correct, dq_correct); + + // 3. Delta error in minimal form: D_err = diff(D_exp , D_corr) + // Note the Dt here is zero because it's the delta-time between the same time stamps! + Matrix<T, 12, 1> d_error; + Map<Matrix<T, 3, 1> > dc_error (d_error.data() ); + Map<Matrix<T, 3, 1> > dcd_error(d_error.data() + 3); + Map<Matrix<T, 3, 1> > dLc_error(d_error.data() + 6); + Map<Matrix<T, 3, 1> > do_error (d_error.data() + 9); + + + bodydynamics::diff(dc_exp, dcd_exp, dLc_exp, dq_exp, + dc_correct, dcd_correct, dLc_correct, dq_correct, + dc_error, dcd_error, dLc_error, do_error); + + _res = getMeasurementSquareRootInformationUpper() * d_error; + + return true; } -// void FactorForceTorque::recomputeJac(const FeatureForceTorquePtr& _feat, -// const double& _dt, -// const Eigen::VectorXd& _bp, -// Eigen::Matrix<double, 9, 15>& _J_ny_nz) +// Matrix<double,12,1> FactorForceTorque::residual() const // { -// FeatureForceTorquePtr feat = std::static_pointer_cast<FeatureForceTorque>(_feat); - -// // Measurements retrieval -// // Eigen::Map<const Eigen::Vector3d> f1 (feat->getForcesMeas().data()); -// // Eigen::Map<const Eigen::Vector3d> f2 (feat->getForcesMeas().data() + 3 ); -// // Eigen::Map<const Eigen::Vector3d> tau1(feat->getTorquesMeas().data()); -// // Eigen::Map<const Eigen::Vector3d> tau2(feat->getTorquesMeas().data() + 3 ); -// Eigen::Map<const Eigen::Vector3d> pbl1(feat->getKinMeas().data()); -// Eigen::Map<const Eigen::Vector3d> pbl2(feat->getKinMeas().data() + 3 ); -// Eigen::Map<const Eigen::Quaterniond> bql1(feat->getKinMeas().data() + 6); -// Eigen::Map<const Eigen::Quaterniond> bql2(feat->getKinMeas().data() + 10); -// Eigen::Map<const Eigen::Vector3d> pbc (feat->getPbcMeas().data()); - -// Eigen::Matrix3d bRl1 = q2R(bql1); -// Eigen::Matrix3d bRl2 = q2R(bql2); -// // _J_ny_nz.block<3,3>(0,0) = 0.5*bRl1*pow(_dt,2)/_mass; -// // _J_ny_nz.block<3,3>(0,3) = 0.5*bRl2*pow(_dt,2)/_mass; -// // _J_ny_nz.block<3,3>(3,0) = bRl1*_dt/_mass; -// // _J_ny_nz.block<3,3>(3,3) = bRl2*_dt/_mass; -// _J_ny_nz.block<3,3>(6,0) = skew(pbl1 - pbc + _bp)*bRl1*_dt; -// _J_ny_nz.block<3,3>(6,3) = skew(pbl2 - pbc + _bp)*bRl2*_dt; -// // _J_ny_nz.block<3,3>(6,6) = bRl1*_dt; -// // _J_ny_nz.block<3,3>(6,9) = bRl2*_dt; -// // _J_ny_nz.block<3,3>(6,12) = (skew(bRl1*f1) + skew(bRl2*f2))*_dt; -// } +// Matrix<double,12,1> res; -template<typename T> -bool FactorForceTorque::operator () ( - const T* const _ck, - const T* const _cdk, - const T* const _Lck, - const T* const _ckm, - const T* const _cdkm, - const T* const _Lckm, - const T* const _bpkm, - const T* const _qkm, - T* _res) const -{ - auto feat = std::static_pointer_cast<const FeatureForceTorque>(getFeature()); - - // State variables instanciation - Eigen::Map<const Eigen::Matrix<T,3,1> > ck(_ck); - Eigen::Map<const Eigen::Matrix<T,3,1> > cdk(_cdk); - Eigen::Map<const Eigen::Matrix<T,3,1> > Lck(_Lck); - Eigen::Map<const Eigen::Matrix<T,3,1> > ckm(_ckm); - Eigen::Map<const Eigen::Matrix<T,3,1> > cdkm(_cdkm); - Eigen::Map<const Eigen::Matrix<T,3,1> > Lckm(_Lckm); - Eigen::Map<const Eigen::Matrix<T,3,1> > bpkm(_bpkm); - Eigen::Map<const Eigen::Quaternion<T> > qkm(_qkm); - Eigen::Map<Eigen::Matrix<T,9,1> > res(_res); - - // Retrieve all measurements - Eigen::Map<const Eigen::Vector3d> f1 (feat->getForcesMeas().data()); - Eigen::Map<const Eigen::Vector3d> f2 (feat->getForcesMeas().data() + 3 ); - Eigen::Map<const Eigen::Vector3d> tau1(feat->getTorquesMeas().data()); - Eigen::Map<const Eigen::Vector3d> tau2(feat->getTorquesMeas().data() + 3 ); - Eigen::Map<const Eigen::Vector3d> pbc (feat->getPbcMeas().data()); - Eigen::Map<const Eigen::Vector3d> pbl1(feat->getKinMeas().data()); - Eigen::Map<const Eigen::Vector3d> pbl2(feat->getKinMeas().data() + 3 ); - Eigen::Map<const Eigen::Quaterniond> bql1(feat->getKinMeas().data() + 6); - Eigen::Map<const Eigen::Quaterniond> bql2(feat->getKinMeas().data() + 10); - - // Recompute the error variable covariance according to the new bias bp estimation - - Eigen::Matrix<double, 9, 15> J_ny_nz; - computeJac(feat, mass_, dt_, sb_bp_other_->getState(), J_ny_nz); // bp - Eigen::Matrix9d errCov = J_ny_nz * cov_meas_ * J_ny_nz.transpose(); - errCov.block<6,6>(0,0) = errCov.block<6,6>(0,0) + 1e-12 * Eigen::Matrix6d::Identity(); - - // Error variable instanciation - Eigen::Matrix<T, 9, 1> err; - Eigen::Map<Eigen::Matrix<T, 3, 1> > err_c (err.data()); - Eigen::Map<Eigen::Matrix<T, 3, 1> > err_cd(err.data() + 3); - Eigen::Map<Eigen::Matrix<T, 3, 1> > err_Lc(err.data() + 6); - - err_c = qkm.conjugate()*(ck - ckm - cdkm * dt_ - 0.5*wolf::gravity()*pow(dt_, 2)) - - (0.5/mass_) * (bql1 * f1 * pow(dt_,2) + bql2 * f2 * pow(dt_,2)); - - err_cd = qkm.conjugate()*(cdk - cdkm - wolf::gravity()*dt_) - - (1/mass_) * (bql1 * f1 * dt_ + bql2 * f2 * dt_); - - err_Lc = qkm.conjugate()*(Lck - Lckm) - - ( bql1 * tau1 + (pbl1 - pbc + bpkm).cross(bql1 * f1) - + bql2 * tau2 + (pbl2 - pbc + bpkm).cross(bql2 * f2)); - - res = wolf::computeSqrtUpper(errCov.inverse()) * err; +// FrameBasePtr frm_prev = getFrameOther(); +// FrameBasePtr frm_curr = getFeature()->getFrame(); - return true; -} +// CaptureBaseWPtrList cap_lst = getCaptureOtherList(); // !! not retrieving the rigt captures... +// auto cap_ikin_other = cap_lst.front().lock(); +// auto cap_gyro_other = cap_lst.back().lock(); + +// Map<const Matrix<double,3,1> > c1( frm_prev->getStateBlock('C')->getState().data()); +// Map<const Matrix<double,3,1> > cd1(frm_prev->getStateBlock('D')->getState().data()); +// Map<const Matrix<double,3,1> > Lc1(frm_prev->getStateBlock('L')->getState().data()); +// Map<const Quaternion<double> > q1( frm_prev->getStateBlock('O')->getState().data()); +// Map<const Matrix<double,3,1> > bp1(cap_ikin_other->getStateBlock('I')->getState().data()); +// Map<const Matrix<double,3,1> > bw1(cap_gyro_other->getStateBlock('I')->getState().data() + 3); // bw1 = bimu = [ba, bw] +// Map<const Matrix<double,3,1> > c2 (frm_curr->getStateBlock('C')->getState().data()); +// Map<const Matrix<double,3,1> > cd2(frm_curr->getStateBlock('D')->getState().data()); +// Map<const Matrix<double,3,1> > Lc2(frm_curr->getStateBlock('L')->getState().data()); +// Map<const Quaternion<double> > q2 (frm_curr->getStateBlock('O')->getState().data()); + +// residual(c1, cd1, Lc1, q1, bp1, bw1, c2, cd2, Lc2, q2, res); + +// return res; +// } + +// double FactorForceTorque::cost() const +// { +// Matrix<double,12,1> toto = residual(); +// } } // namespace wolf -#endif /* FACTOR_FORCE_TORQUE_H_ */ +#endif diff --git a/include/bodydynamics/factor/factor_force_torque_inertial.h b/include/bodydynamics/factor/factor_force_torque_inertial.h new file mode 100644 index 0000000000000000000000000000000000000000..cfea278e30816f355353fbc833d4647c66cf6726 --- /dev/null +++ b/include/bodydynamics/factor/factor_force_torque_inertial.h @@ -0,0 +1,233 @@ +//--------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-------- +#ifndef FACTOR_FORCE_TORQUE_INERTIAL_H +#define FACTOR_FORCE_TORQUE_INERTIAL_H + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include <core/feature/feature_motion.h> +#include <core/factor/factor_autodiff.h> + +namespace wolf +{ +using namespace Eigen; +using namespace bodydynamics; + +WOLF_PTR_TYPEDEFS(FactorForceTorqueInertial); + +/** + * @brief Pre-integrated factor taking IMU and force-torque measurements (FTI) + * + * This factor evaluates the preintegrated FTI against pose, linear velocity, IMU bias and angular momentum. + * + * State blocks considered: + * - Frame 1 (origin) + * 'P' position + * 'V' velocity + * 'O' orientation + * 'L' angular momentum + * - Capture 1 (origin) + * 'I' IMU biases + * - Frame 2 + * 'P' position + * 'V' velocity + * 'O' orientation + * 'L' angular momentum + * + * The residual computed has the following components, in this order + * - position delta error according to IMU preintegration + * - velocity delta error according to IMU preintegration + * - position delta error according to FT preintegration + * - velocity delta error according to FT preintegration + * - angular momentum delta error according to FT preintegration + * - orientation delta error according to IMU preintegration + */ +class FactorForceTorqueInertial + : public FactorAutodiff<FactorForceTorqueInertial, 18, 3, 4, 3, 3, 6, 3, 4, 3, 3> // POVLB - POVL // TODO: add FT + // bias? +{ + public: + FactorForceTorqueInertial(const FeatureMotionPtr& _ftr, // gets measurement and access to parents + const CaptureBasePtr& _capture_origin, // gets biases + const ProcessorBasePtr& _processor, // gets access to processor and sensor + bool _apply_loss_function, + FactorStatus _status = FAC_ACTIVE); + + ~FactorForceTorqueInertial() override = default; + + template <typename T> + bool operator()(const T* const _p1, // position 1 + const T* const _q1, // orientation + const T* const _v1, // velocity + const T* const _L1, // ang momentum + const T* const _b1, // IMU bias (acc and gyro) + const T* const _p2, // position 2 + const T* const _q2, // orientation + const T* const _v2, // velocity + const T* const _L2, // ang momentum + T* _res) const; // residual + + template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6> + bool residual(const MatrixBase<D1>& _p1, + const QuaternionBase<D4>& _q1, + const MatrixBase<D1>& _v1, + const MatrixBase<D1>& _L1, + const MatrixBase<D2>& _b1, + const MatrixBase<D3>& _p2, + const QuaternionBase<D5>& _q2, + const MatrixBase<D3>& _v2, + const MatrixBase<D3>& _L2, + MatrixBase<D6>& _res) const; + + private: + double dt_; + Matrix<double, 19, 1> delta_preint_; + Vector6d bias_preint_; + Matrix<double, 18, 6> J_delta_bias_; + Matrix<double, 18, 18> sqrt_info_upper_; +}; + +inline FactorForceTorqueInertial::FactorForceTorqueInertial(const FeatureMotionPtr& _ftr, + const CaptureBasePtr& _capture_origin, + const ProcessorBasePtr& _processor, + bool _apply_loss_function, + FactorStatus _status) + : FactorAutodiff<FactorForceTorqueInertial, 18, 3, 4, 3, 3, 6, 3, 4, 3, 3>( + "FactorForceTorqueInertial", + TOP_MOTION, + _ftr, // parent feature + _capture_origin->getFrame(), // frame other + _capture_origin, // capture other + nullptr, // feature other + nullptr, // landmark other + _processor, // processor + _apply_loss_function, + _status, + _capture_origin->getFrame()->getStateBlock('P'), // previous frame P + _capture_origin->getFrame()->getStateBlock('O'), // previous frame O + _capture_origin->getFrame()->getStateBlock('V'), // previous frame V + _capture_origin->getFrame()->getStateBlock('L'), // previous frame L + _capture_origin->getSensorIntrinsic(), // previous frame IMU bias + _ftr->getFrame()->getStateBlock('P'), // current frame P + _ftr->getFrame()->getStateBlock('O'), // current frame O + _ftr->getFrame()->getStateBlock('V'), // current frame V + _ftr->getFrame()->getStateBlock('L')), // current frame L + dt_(_ftr->getFrame()->getTimeStamp() - _capture_origin->getFrame()->getTimeStamp()), + delta_preint_(_ftr->getDeltaPreint()), + bias_preint_(_ftr->getCalibrationPreint()), + J_delta_bias_(_ftr->getJacobianCalibration()), + sqrt_info_upper_(_ftr->getMeasurementSquareRootInformationUpper()) +{ + // +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6> +inline bool FactorForceTorqueInertial::residual(const MatrixBase<D1>& _p1, + const QuaternionBase<D4>& _q1, + const MatrixBase<D1>& _v1, + const MatrixBase<D1>& _L1, + const MatrixBase<D2>& _b1, + const MatrixBase<D3>& _p2, + const QuaternionBase<D5>& _q2, + const MatrixBase<D3>& _v2, + const MatrixBase<D3>& _L2, + MatrixBase<D6>& _res) const +{ + MatrixSizeCheck<18, 1>::check(_res); + + typedef typename D4::Scalar T; + typedef Map<Matrix<T, 3, 1>> Matrixt; + + /* Will do the following: + * + * Compute estimated delta, using betweenStates() + * d_est = x2 (-) x1 + * + * Compute correction step according to bias change, this is a linear operation + * d_step = J_delta_bias * (b1 - bias_preint) + * + * Correct preintegrated delta with new bias, using plus() + * d_corr = d_preint (+) d_step + * + * Compute error, using diff() + * d_err = d_corr (-) d_est + * + * Compute residual + * res = W * d_err , with W the sqrt of the info matrix. + */ + + /* WARNING -- order of state blocks might be confusing!! + * Frame blocks are ordered as follows: "POVL" -- pos, ori, vel, ang_momentum + * Factor blocks are ordered as follows: "POVL-B-POVL" -- B is IMU bias + * Delta blocks are ordered as follows: "PVpvLO" -- where "P,V,O" are from IMU, and "p,v,L" from FT + */ + + Matrix<double, 3, 1> f; + Matrix<T, 19, 1> delta_est; // delta_est = x2 (-) x1, computed with betweenStates(x1,x2,dt) + Matrixt dpi(&delta_est(0)); // 'P' + Matrixt dvi(&delta_est(3)); // 'V' + Matrixt dpd(&delta_est(6)); // 'p' + Matrixt dvd(&delta_est(9)); // 'v' + Matrixt dL(&delta_est(12)); // 'L' + Map<Quaternion<T>> dq(&delta_est(15)); // 'O' + fti::betweenStates(_p1, _v1, _L1, _q1, _p2, _v2, _L2, _q2, dt_, dpi, dvi, dpd, dvd, dL, dq); + + Matrix<T, 19, 1> delta_preint = delta_preint_.cast<T>(); + Matrix<T, 18, 1> delta_step = + J_delta_bias_ * (_b1 - bias_preint_); // canviar _b1 per calib = (I,C,i,m), de mida 13 Matrix<T,13,1> + Matrix<T, 19, 1> delta_corr = fti::plus(delta_preint, delta_step); + Matrix<T, 18, 1> delta_err = fti::diff(delta_est, delta_corr); + _res = sqrt_info_upper_ * delta_err; + + return true; +} + +template <typename T> +inline bool FactorForceTorqueInertial::operator()(const T* const _p1, + const T* const _q1, + const T* const _v1, + const T* const _L1, + const T* const _b1, + const T* const _p2, + const T* const _q2, + const T* const _v2, + const T* const _L2, + T* _res) const +{ + Map<const Matrix<T, 3, 1>> p1(_p1); + Map<const Quaternion<T>> q1(_q1); + Map<const Matrix<T, 3, 1>> v1(_v1); + Map<const Matrix<T, 3, 1>> L1(_L1); + Map<const Matrix<T, 6, 1>> b1(_b1); + Map<const Matrix<T, 3, 1>> p2(_p2); + Map<const Quaternion<T>> q2(_q2); + Map<const Matrix<T, 3, 1>> v2(_v2); + Map<const Matrix<T, 3, 1>> L2(_L2); + Map<Matrix<T, 18, 1>> res(_res); + + residual(p1, q1, v1, L1, b1, p2, q2, v2, L2, res); + + return true; +} + +} // namespace wolf + +#endif // FACTOR_FORCE_TORQUE_INERTIAL_H \ No newline at end of file diff --git a/include/bodydynamics/factor/factor_force_torque_inertial_dynamics.h b/include/bodydynamics/factor/factor_force_torque_inertial_dynamics.h new file mode 100644 index 0000000000000000000000000000000000000000..abece6e0d706e25da604a03d8c13f70dafdb2ed6 --- /dev/null +++ b/include/bodydynamics/factor/factor_force_torque_inertial_dynamics.h @@ -0,0 +1,285 @@ +//--------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-------- +#ifndef FACTOR_FORCE_TORQUE_INERTIAL_DYNAMICS_H +#define FACTOR_FORCE_TORQUE_INERTIAL_DYNAMICS_H + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include <core/feature/feature_motion.h> +#include <core/factor/factor_autodiff.h> + +namespace wolf +{ +using namespace Eigen; +using namespace bodydynamics; + +WOLF_PTR_TYPEDEFS(FactorForceTorqueInertialDynamics); + +/** + * @brief Pre-integrated factor taking IMU and force-torque measurements (FTI) + * + * This factor evaluates the preintegrated FTI against pose, linear velocity, IMU bias and angular momentum. + * + * State blocks considered: + * - Frame 1 (origin) + * 'P' position + * 'V' velocity + * 'O' orientation + * 'L' angular momentum + * - Capture 1 (origin) + * 'I' IMU biases + * - Frame 2 + * 'P' position + * 'V' velocity + * 'O' orientation + * 'L' angular momentum + * - Sensor Force Torque Inertial + * 'C' position of the center of mass + * 'i' inertia matrix components (we are assuming that it is a diagonal matrix) + * 'm' mass + * + * The residual computed has the following components, in this order + * - position delta error according to IMU preintegration + * - velocity delta error according to IMU preintegration + * - position delta error according to FT preintegration + * - velocity delta error according to FT preintegration + * - angular momentum delta error according to FT preintegration + * - orientation delta error according to IMU preintegration + */ +class FactorForceTorqueInertialDynamics + : public FactorAutodiff<FactorForceTorqueInertialDynamics, 18, 3, 4, 3, 3, 6, 3, 4, 3, 3, 3, 3, 1> // POVLB - POVL + // - Cim + // // TODO: add + // FT bias? +{ + public: + FactorForceTorqueInertialDynamics(const FeatureMotionPtr& _ftr, // gets measurement and access to parents + const CaptureBasePtr& _capture_origin, // gets biases + const ProcessorBasePtr& _processor, // gets access to processor and sensor + bool _apply_loss_function, + FactorStatus _status = FAC_ACTIVE); + + ~FactorForceTorqueInertialDynamics() override = default; + + template <typename T> + bool operator()(const T* const _p1, // position 1 + const T* const _q1, // orientation + const T* const _v1, // velocity + const T* const _L1, // ang momentum + const T* const _b1, // IMU bias (acc and gyro) + const T* const _p2, // position 2 + const T* const _q2, // orientation + const T* const _v2, // velocity + const T* const _L2, // ang momentum + const T* const _C, // center of mass + const T* const _i, // inertia matrix + const T* const _m, // mass + T* _res) const; // residual + + template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8> + bool residual(const MatrixBase<D1>& _p1, + const QuaternionBase<D4>& _q1, + const MatrixBase<D1>& _v1, + const MatrixBase<D1>& _L1, + const MatrixBase<D2>& _b1, + const MatrixBase<D3>& _p2, + const QuaternionBase<D5>& _q2, + const MatrixBase<D3>& _v2, + const MatrixBase<D3>& _L2, + const MatrixBase<D6>& _C, + const MatrixBase<D6>& _i, + const D7& _m, + MatrixBase<D8>& _res) const; + + VectorXd residual() const; + + VectorXd getCalibPreint() const + { + return calib_preint_; + } + + private: + double dt_; + Matrix<double, 19, 1> delta_preint_; + Matrix<double, 13, 1> calib_preint_; + Matrix<double, 18, 13> J_delta_calib_; + Matrix<double, 18, 18> sqrt_info_upper_; +}; + +inline FactorForceTorqueInertialDynamics::FactorForceTorqueInertialDynamics(const FeatureMotionPtr& _ftr, + const CaptureBasePtr& _capture_origin, + const ProcessorBasePtr& _processor, + bool _apply_loss_function, + FactorStatus _status) + : FactorAutodiff<FactorForceTorqueInertialDynamics, 18, 3, 4, 3, 3, 6, 3, 4, 3, 3, 3, 3, 1>( + "FactorForceTorqueInertialDynamics", + TOP_MOTION, + _ftr, // parent feature + _capture_origin->getFrame(), // frame other + _capture_origin, // capture other + nullptr, // feature other + nullptr, // landmark other + _processor, // processor + _apply_loss_function, + _status, + _capture_origin->getFrame()->getStateBlock('P'), // previous frame P + _capture_origin->getFrame()->getStateBlock('O'), // previous frame O + _capture_origin->getFrame()->getStateBlock('V'), // previous frame V + _capture_origin->getFrame()->getStateBlock('L'), // previous frame L + _capture_origin->getStateBlock('I'), // previous frame IMU bias + _ftr->getFrame()->getStateBlock('P'), // current frame P + _ftr->getFrame()->getStateBlock('O'), // current frame O + _ftr->getFrame()->getStateBlock('V'), // current frame V + _ftr->getFrame()->getStateBlock('L'), // current frame L + _ftr->getCapture()->getStateBlock('C'), // sensor C + _ftr->getCapture()->getStateBlock('i'), // sensor i + _ftr->getCapture()->getStateBlock('m')), // sensor m + dt_(_ftr->getFrame()->getTimeStamp() - _capture_origin->getFrame()->getTimeStamp()), + delta_preint_(_ftr->getDeltaPreint()), + calib_preint_(_ftr->getCalibrationPreint()), + J_delta_calib_(_ftr->getJacobianCalibration()), + sqrt_info_upper_(_ftr->getMeasurementSquareRootInformationUpper()) +{ + // +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8> +inline bool FactorForceTorqueInertialDynamics::residual(const MatrixBase<D1>& _p1, + const QuaternionBase<D4>& _q1, + const MatrixBase<D1>& _v1, + const MatrixBase<D1>& _L1, + const MatrixBase<D2>& _b1, + const MatrixBase<D3>& _p2, + const QuaternionBase<D5>& _q2, + const MatrixBase<D3>& _v2, + const MatrixBase<D3>& _L2, + const MatrixBase<D6>& _C, + const MatrixBase<D6>& _i, + const D7& _m, + MatrixBase<D8>& _res) const +{ + MatrixSizeCheck<18, 1>::check(_res); + + typedef typename D4::Scalar T; + typedef Map<Matrix<T, 3, 1>> Matrixt; + + /* Will do the following: + * + * Compute estimated delta, using betweenStates() + * d_est = x2 (-) x1 + * + * Compute correction step according to bias change, this is a linear operation + * d_step = J_delta_calib * (calib - bias_preint) + * + * Correct preintegrated delta with new bias, using plus() + * d_corr = d_preint (+) d_step + * + * Compute error, using diff() + * d_err = d_corr (-) d_est + * + * Compute residual + * res = W * d_err , with W the sqrt of the info matrix. + */ + + /* WARNING -- order of state blocks might be confusing!! + * Frame blocks are ordered as follows: "POVL" -- pos, ori, vel, ang_momentum + * Factor blocks are ordered as follows: "POVL-B-POVL" -- B is IMU bias + * Delta blocks are ordered as follows: "PVpvLO" -- where "P,V,O" are from IMU, and "p,v,L" from FT + */ + + Matrix<double, 3, 1> f; + Matrix<T, 19, 1> delta_est; // delta_est = x2 (-) x1, computed with betweenStates(x1,x2,dt) + Matrixt dpi(&delta_est(0)); // 'P' + Matrixt dvi(&delta_est(3)); // 'V' + Matrixt dpd(&delta_est(6)); // 'p' + Matrixt dvd(&delta_est(9)); // 'v' + Matrixt dL(&delta_est(12)); // 'L' + Map<Quaternion<T>> dq(&delta_est(15)); // 'O' + fti::betweenStates(_p1, _v1, _L1, _q1, _p2, _v2, _L2, _q2, dt_, dpi, dvi, dpd, dvd, dL, dq); + + Matrix<T, 19, 1> delta_preint = delta_preint_.cast<T>(); + Matrix<T, 13, 1> calib; + calib << _b1, _C, _i, _m; + Matrix<T, 18, 1> delta_step = J_delta_calib_ * (calib - calib_preint_); + Matrix<T, 19, 1> delta_corr = fti::plus(delta_preint, delta_step); + Matrix<T, 18, 1> delta_err = fti::diff(delta_est, delta_corr); + _res = sqrt_info_upper_ * delta_err; + + return true; +} + +inline VectorXd FactorForceTorqueInertialDynamics::residual() const +{ + VectorXd res(18); + Vector3d p0 = getFrameOther()->getStateBlock('P')->getState(); // previous frame P + Quaterniond q0 = Quaterniond(getFrameOther()->getStateBlock('O')->getState().data()); + Vector3d v0 = getFrameOther()->getStateBlock('V')->getState(); + Vector3d L0 = getFrameOther()->getStateBlock('L')->getState(); + Vector6d bias = getCaptureOther()->getSensorIntrinsic()->getState(); + Vector3d p1 = getFrame()->getStateBlock('P')->getState(); // previous frame P + Quaterniond q1 = Quaterniond(getFrame()->getStateBlock('O')->getState().data()); + Vector3d v1 = getFrame()->getStateBlock('V')->getState(); + Vector3d L1 = getFrame()->getStateBlock('L')->getState(); + Vector3d C = getCapture()->getSensor()->getStateBlockDynamic('C')->getState(); + Vector3d i = getCapture()->getSensor()->getStateBlockDynamic('i')->getState(); + double m = getCapture()->getSensor()->getStateBlockDynamic('m')->getState()(0); + + residual(p0, q0, v0, L0, bias, p1, q1, v1, L1, C, i, m, res); + return res; +} + +template <typename T> +inline bool FactorForceTorqueInertialDynamics::operator()(const T* const _p1, + const T* const _q1, + const T* const _v1, + const T* const _L1, + const T* const _b1, + const T* const _p2, + const T* const _q2, + const T* const _v2, + const T* const _L2, + const T* const _C, + const T* const _i, + const T* const _m, + T* _res) const +{ + Map<const Matrix<T, 3, 1>> p1(_p1); + Map<const Quaternion<T>> q1(_q1); + Map<const Matrix<T, 3, 1>> v1(_v1); + Map<const Matrix<T, 3, 1>> L1(_L1); + Map<const Matrix<T, 6, 1>> b1(_b1); + Map<const Matrix<T, 3, 1>> p2(_p2); + Map<const Quaternion<T>> q2(_q2); + Map<const Matrix<T, 3, 1>> v2(_v2); + Map<const Matrix<T, 3, 1>> L2(_L2); + Map<const Matrix<T, 3, 1>> C(_C); + Map<const Matrix<T, 3, 1>> i(_i); + const T& m = *_m; + Map<Matrix<T, 18, 1>> res(_res); + + residual(p1, q1, v1, L1, b1, p2, q2, v2, L2, C, i, m, res); + + return true; +} + +} // namespace wolf + +#endif // FACTOR_FORCE_TORQUE_INERTIAL_H \ No newline at end of file diff --git a/include/bodydynamics/factor/factor_force_torque_preint.h b/include/bodydynamics/factor/factor_force_torque_preint.h deleted file mode 100644 index 841bb63d6f94ffa9dc4ec115e5f4a3c3dd06d20a..0000000000000000000000000000000000000000 --- a/include/bodydynamics/factor/factor_force_torque_preint.h +++ /dev/null @@ -1,343 +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-------- -#ifndef FACTOR_FORCE_TORQUE_PREINT_THETA_H_ -#define FACTOR_FORCE_TORQUE_PREINT_THETA_H_ - -//Wolf includes -#include "bodydynamics/capture/capture_force_torque_preint.h" -#include "bodydynamics/feature/feature_force_torque_preint.h" -#include "bodydynamics/sensor/sensor_force_torque.h" -#include "core/factor/factor_autodiff.h" -#include "core/math/rotations.h" - -//Eigen include - -namespace wolf { - -WOLF_PTR_TYPEDEFS(FactorForceTorquePreint); - -//class -class FactorForceTorquePreint : public FactorAutodiff<FactorForceTorquePreint, 12, 3,3,3,4,3,6, 3,3,3,4> -{ - public: - FactorForceTorquePreint(const FeatureForceTorquePreintPtr& _ftr_ptr, - const CaptureForceTorquePreintPtr& _cap_origin_ptr, // gets to bp1, bw1 - const ProcessorBasePtr& _processor_ptr, - const CaptureBasePtr& _cap_ikin_other, - const CaptureBasePtr& _cap_gyro_other, - bool _apply_loss_function, - FactorStatus _status = FAC_ACTIVE); - - ~FactorForceTorquePreint() override = default; - - /** \brief : compute the residual from the state blocks being iterated by the solver. - -> computes the expected measurement - -> corrects actual measurement with new bias - -> compares the corrected measurement with the expected one - -> weights the result with the covariance of the noise (residual = sqrt_info_matrix * err;) - */ - template<typename T> - bool operator ()(const T* const _c1, - const T* const _cd1, - const T* const _Lc1, - const T* const _q1, - const T* const _bp1, - const T* const _bw1, - const T* const _c2, - const T* const _cd2, - const T* const _Lc2, - const T* const _q2, - T* _res) const; - - /** \brief : compute the residual from the state blocks being iterated by the solver. (same as operator()) - -> computes the expected measurement - -> corrects actual measurement with new bias - -> compares the corrected measurement with the expected one - -> weights the result with the covariance of the noise (residual = sqrt_info_matrix * err;) - * params : - * _c1 : COM position at time t1 in world frame - * _cd1: COM velocity at time t1 in world frame - * _Lc1: Angular momentum at time t1 in world frame - * _q1 : Base orientation at time t1 - * _bp1: COM position measurement at time t1 in body frame - * _bw1: gyroscope bias at time t1 in body frame - * _c2 : COM position at time t2 in world frame - * _cd2: COM velocity at time t2 in world frame - * _Lc2: Angular momentum at time t2 in world frame - * _q2 : Base orientation at time t2 - * _res: Factor residuals (c,cd,Lc,o) O is rotation vector... NOT A QUATERNION - */ - template<typename D1, typename D2, typename D3, typename D4> - bool residual(const MatrixBase<D1> & _c1, - const MatrixBase<D1> & _cd1, - const MatrixBase<D1> & _Lc1, - const QuaternionBase<D2> & _q1, - const MatrixBase<D1> & _bp1, - const MatrixBase<D1> & _bw1, - const MatrixBase<D1> & _c2, - const MatrixBase<D1> & _cd2, - const MatrixBase<D1> & _Lc2, - const QuaternionBase<D3> & _q2, - MatrixBase<D4> & _res) const; - - // Matrix<double,12,1> residual() const; - // double cost() const; - - private: - /// Preintegrated delta - Vector3d dc_preint_; - Vector3d dcd_preint_; - Vector3d dLc_preint_; - Quaterniond dq_preint_; - - // Biases used during preintegration - Vector3d pbc_bias_preint_; - Vector3d gyro_bias_preint_; - - // Jacobians of preintegrated deltas wrt biases - Matrix3d J_dLc_pb_; - Matrix3d J_dc_wb_; - Matrix3d J_dcd_wb_; - Matrix3d J_dLc_wb_; - Matrix3d J_dq_wb_; - - /// Metrics - double dt_; ///< delta-time and delta-time-squared between keyframes - double mass_; ///< constant total robot mass - -}; - -///////////////////// IMPLEMENTATION //////////////////////////// - -inline FactorForceTorquePreint::FactorForceTorquePreint( - const FeatureForceTorquePreintPtr& _ftr_ptr, - const CaptureForceTorquePreintPtr& _cap_origin_ptr, - const ProcessorBasePtr& _processor_ptr, - const CaptureBasePtr& _cap_ikin_other, - const CaptureBasePtr& _cap_gyro_other, - bool _apply_loss_function, - FactorStatus _status) : - FactorAutodiff<FactorForceTorquePreint, 12, 3,3,3,4,3,6, 3,3,3,4>( - "FactorForceTorquePreint", - TOP_MOTION, - _ftr_ptr, - _cap_origin_ptr->getFrame(), - _cap_origin_ptr, - nullptr, - nullptr, - _processor_ptr, - _apply_loss_function, - _status, - _cap_origin_ptr->getFrame()->getStateBlock('C'), - _cap_origin_ptr->getFrame()->getStateBlock('D'), - _cap_origin_ptr->getFrame()->getStateBlock('L'), - _cap_origin_ptr->getFrame()->getO(), - _cap_ikin_other->getSensorIntrinsic(), - _cap_gyro_other->getSensorIntrinsic(), - _ftr_ptr->getFrame()->getStateBlock('C'), - _ftr_ptr->getFrame()->getStateBlock('D'), - _ftr_ptr->getFrame()->getStateBlock('L'), - _ftr_ptr->getFrame()->getO() - ), - dc_preint_(_ftr_ptr->getMeasurement().head<3>()), // dc, dcd, dLc, dq at preintegration time - dcd_preint_(_ftr_ptr->getMeasurement().segment<3>(3)), - dLc_preint_(_ftr_ptr->getMeasurement().segment<3>(6)), - dq_preint_(_ftr_ptr->getMeasurement().data()+9), - pbc_bias_preint_( std::static_pointer_cast<FeatureForceTorquePreint>(_ftr_ptr)->getPbcBiasPreint()), - gyro_bias_preint_(std::static_pointer_cast<FeatureForceTorquePreint>(_ftr_ptr)->getGyroBiasPreint()), - J_dLc_pb_(_ftr_ptr->getJacobianBias().block<3,3>(6,0)), // Jac dLc wrt pbc bias - J_dc_wb_ (_ftr_ptr->getJacobianBias().block<3,3>(0,3)), // Jac dc wrt gyro bias - J_dcd_wb_(_ftr_ptr->getJacobianBias().block<3,3>(3,3)), // Jac dc wrt gyro bias - J_dLc_wb_(_ftr_ptr->getJacobianBias().block<3,3>(6,3)), // Jac dcd wrt gyro bias - J_dq_wb_ (_ftr_ptr->getJacobianBias().block<3,3>(9,3)), // Jac dLc wrt gyro bias - dt_(_ftr_ptr->getFrame()->getTimeStamp() - _cap_origin_ptr->getFrame()->getTimeStamp()), - mass_(std::static_pointer_cast<SensorForceTorque>(_ftr_ptr->getCapture()->getSensor())->getMass()) -{ -// -} - -template<typename T> -inline bool FactorForceTorquePreint::operator ()(const T* const _c1, - const T* const _cd1, - const T* const _Lc1, - const T* const _q1, - const T* const _bp1, - const T* const _bw1, - const T* const _c2, - const T* const _cd2, - const T* const _Lc2, - const T* const _q2, - T* _res) const -{ - Map<const Matrix<T,3,1> > c1(_c1); - Map<const Matrix<T,3,1> > cd1(_cd1); - Map<const Matrix<T,3,1> > Lc1(_Lc1); - Map<const Quaternion<T> > q1(_q1); - Map<const Matrix<T,3,1> > bp1(_bp1); - Map<const Matrix<T,3,1> > bw1(_bw1 + 3); // bw1 = bimu = [ba, bw] - Map<const Matrix<T,3,1> > c2(_c2); - Map<const Matrix<T,3,1> > cd2(_cd2); - Map<const Matrix<T,3,1> > Lc2(_Lc2); - Map<const Quaternion<T> > q2(_q2); - Map<Matrix<T,12,1> > res(_res); - - residual(c1, cd1, Lc1, q1, bp1, bw1, c2, cd2, Lc2, q2, res); - - return true; -} - -template<typename D1, typename D2, typename D3, typename D4> -inline bool FactorForceTorquePreint::residual(const MatrixBase<D1> & _c1, - const MatrixBase<D1> & _cd1, - const MatrixBase<D1> & _Lc1, - const QuaternionBase<D2> & _q1, - const MatrixBase<D1> & _bp1, - const MatrixBase<D1> & _bw1, - const MatrixBase<D1> & _c2, - const MatrixBase<D1> & _cd2, - const MatrixBase<D1> & _Lc2, - const QuaternionBase<D3> & _q2, - MatrixBase<D4> & _res) const -{ - /* Help for the Imu residual function - * - * Notations: - * D_* : a motion in the Delta manifold -- implemented as 10-vectors with [Dp, Dq, Dv] - * T_* : a motion difference in the Tangent space to the manifold -- implemented as 9-vectors with [Dp, Do, Dv] - * b* : a bias - * J* : a Jacobian matrix - * - * We use the following functions from imu_tools.h: - * D = betweenStates(x1,x2,dt) : obtain a Delta from two states separated dt=t2-t1 - * D2 = plus(D1,T) : plus operator, D2 = D1 (+) T - * T = diff(D1,D2) : minus operator, T = D2 (-) D1 - * - * Two methods are possible (select with #define below this note) : - * - * Computations common to methods 1 and 2: - * D_exp = betweenStates(x1,x2,dt) // Predict delta from states - * T_step = J_preint * (b - b_preint) // compute the delta correction step due to bias change - * - * Method 1: - * D_corr = plus(D_preint, T_step) // correct the pre-integrated delta with correction step due to bias change - * T_err = diff(D_exp, D_corr) // compare against expected delta - * res = W.sqrt * T_err - * - * results in: - * res = W.sqrt * ( diff ( D_exp, plus(D_preint, J_preint * (b - b_preint) ) ) ) - * - * Method 2: - * T_diff = diff(D_preint, D_exp) // compare pre-integrated against expected delta - * T_err = T_diff - T_step // the difference should match the correction step due to bias change - * res = W.sqrt * err - * - * results in : - * res = W.sqrt * ( ( diff ( D_preint , D_exp ) ) - J_preint * (b - b_preint) ) - * - * NOTE: See optimization report at the end of this file for comparisons of both methods. - */ - - //needed typedefs - typedef typename D1::Scalar T; - - EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(D4, 12); - - // 1. Expected delta from states - Matrix<T, 3, 1 > dc_exp; - Matrix<T, 3, 1 > dcd_exp; - Matrix<T, 3, 1 > dLc_exp; - Quaternion<T> dq_exp; - - bodydynamics::betweenStates(_c1, _cd1, _Lc1, _q1, _c2, _cd2, _Lc2, _q2, dt_, dc_exp, dcd_exp, dLc_exp, dq_exp); - - // 2. Corrected integrated delta: delta_corr = delta_preint (+) J_bias * (bias_current - bias_preint) - - // 2.a. Compute the delta step in tangent space: step = J_bias * (bias - bias_preint) - Matrix<T, 3, 1> dc_step = J_dc_wb_ * (_bw1 - gyro_bias_preint_); - Matrix<T, 3, 1> dcd_step = J_dcd_wb_ * (_bw1 - gyro_bias_preint_); - Matrix<T, 3, 1> dLc_step = J_dLc_pb_ * (_bp1 - pbc_bias_preint_) + J_dLc_wb_ * (_bw1 - gyro_bias_preint_); - Matrix<T, 3, 1> do_step = J_dq_wb_ * (_bw1 - gyro_bias_preint_); - - // 2.b. Add the correction step to the preintegrated delta: delta_cor = delta_preint (+) step - Matrix<T,3,1> dc_correct; - Matrix<T,3,1> dcd_correct; - Matrix<T,3,1> dLc_correct; - Quaternion<T> dq_correct; - - bodydynamics::plus(dc_preint_.cast<T>(), dcd_preint_.cast<T>(), dLc_preint_.cast<T>(), dq_preint_.cast<T>(), - dc_step, dcd_step, dLc_step, do_step, - dc_correct, dcd_correct, dLc_correct, dq_correct); - - // 3. Delta error in minimal form: D_err = diff(D_exp , D_corr) - // Note the Dt here is zero because it's the delta-time between the same time stamps! - Matrix<T, 12, 1> d_error; - Map<Matrix<T, 3, 1> > dc_error (d_error.data() ); - Map<Matrix<T, 3, 1> > dcd_error(d_error.data() + 3); - Map<Matrix<T, 3, 1> > dLc_error(d_error.data() + 6); - Map<Matrix<T, 3, 1> > do_error (d_error.data() + 9); - - - bodydynamics::diff(dc_exp, dcd_exp, dLc_exp, dq_exp, - dc_correct, dcd_correct, dLc_correct, dq_correct, - dc_error, dcd_error, dLc_error, do_error); - - _res = getMeasurementSquareRootInformationUpper() * d_error; - - return true; -} - -// Matrix<double,12,1> FactorForceTorquePreint::residual() const -// { -// Matrix<double,12,1> res; - - -// FrameBasePtr frm_prev = getFrameOther(); -// FrameBasePtr frm_curr = getFeature()->getFrame(); - -// CaptureBaseWPtrList cap_lst = getCaptureOtherList(); // !! not retrieving the rigt captures... -// auto cap_ikin_other = cap_lst.front().lock(); -// auto cap_gyro_other = cap_lst.back().lock(); - -// Map<const Matrix<double,3,1> > c1( frm_prev->getStateBlock('C')->getState().data()); -// Map<const Matrix<double,3,1> > cd1(frm_prev->getStateBlock('D')->getState().data()); -// Map<const Matrix<double,3,1> > Lc1(frm_prev->getStateBlock('L')->getState().data()); -// Map<const Quaternion<double> > q1( frm_prev->getStateBlock('O')->getState().data()); -// Map<const Matrix<double,3,1> > bp1(cap_ikin_other->getStateBlock('I')->getState().data()); -// Map<const Matrix<double,3,1> > bw1(cap_gyro_other->getStateBlock('I')->getState().data() + 3); // bw1 = bimu = [ba, bw] -// Map<const Matrix<double,3,1> > c2 (frm_curr->getStateBlock('C')->getState().data()); -// Map<const Matrix<double,3,1> > cd2(frm_curr->getStateBlock('D')->getState().data()); -// Map<const Matrix<double,3,1> > Lc2(frm_curr->getStateBlock('L')->getState().data()); -// Map<const Quaternion<double> > q2 (frm_curr->getStateBlock('O')->getState().data()); - -// residual(c1, cd1, Lc1, q1, bp1, bw1, c2, cd2, Lc2, q2, res); - -// return res; -// } - -// double FactorForceTorquePreint::cost() const -// { -// Matrix<double,12,1> toto = residual(); -// } - -} // namespace wolf - -#endif diff --git a/include/bodydynamics/feature/feature_force_torque.h b/include/bodydynamics/feature/feature_force_torque.h index 14594821f3171f7628199e4b3b5f76026f4f7f63..c9fdd98d49b1619db1bf1d424cddfb59ae84a3b9 100644 --- a/include/bodydynamics/feature/feature_force_torque.h +++ b/include/bodydynamics/feature/feature_force_torque.h @@ -23,70 +23,73 @@ #define FEATURE_FORCE_TORQUE_H_ //Wolf includes -#include "core/feature/feature_base.h" +#include <core/feature/feature_base.h> +#include <core/common/wolf.h> //std includes + namespace wolf { - + +//WOLF_PTR_TYPEDEFS(CaptureImu); WOLF_PTR_TYPEDEFS(FeatureForceTorque); -//class FeatureApriltag class FeatureForceTorque : public FeatureBase { public: - FeatureForceTorque( - const double& _dt, - const double& _mass, - const Eigen::Vector6d& _forces_meas, - const Eigen::Vector6d& _torques_meas, - const Eigen::Vector3d& _pbc_meas, - const Eigen::Matrix<double,14,1>& _kin_meas, - const Eigen::Matrix6d& _cov_forces_meas, - const Eigen::Matrix6d& _cov_torques_meas, - const Eigen::Matrix3d& _cov_pbc_meas, - const Eigen::Matrix<double,14,14>& _cov_kin_meas = Eigen::Matrix<double,14,14>::Zero() - ); - - ~FeatureForceTorque() override; - - const double & getDt() const {return dt_;} - const double & getMass() const {return mass_;} - void setDt(const double & _dt){dt_ = _dt;} - void setMass(const double & _mass){mass_ = _mass;} - - const Eigen::Vector6d& getForcesMeas() const {return forces_meas_;} - const Eigen::Vector6d& getTorquesMeas() const {return torques_meas_;} - const Eigen::Vector3d& getPbcMeas() const {return pbc_meas_;} - const Eigen::Matrix<double,14,1>& getKinMeas() const {return kin_meas_;} - const Eigen::Matrix6d& getCovForcesMeas() const {return cov_forces_meas_;} - const Eigen::Matrix6d& getCovTorquesMeas() const {return cov_torques_meas_;} - const Eigen::Matrix3d& getCovPbcMeas() const {return cov_pbc_meas_;} - const Eigen::Matrix<double,14,14>& getCovKinMeas() const {return cov_kin_meas_;} - - void setForcesMeas(const Eigen::Vector6d& _forces_meas){forces_meas_ = _forces_meas;} - void setTorquesMeas(const Eigen::Vector6d& _torques_meas){torques_meas_ = _torques_meas;} - void setKinMeas(const Eigen::Matrix<double,14,1>& _kin_meas){kin_meas_ = _kin_meas;} - void setPbcMeas(const Eigen::Vector3d& _pbc_meas){pbc_meas_ = _pbc_meas;} - void setCovForcesMeas(const Eigen::Matrix6d& _cov_forces_meas){cov_forces_meas_ = _cov_forces_meas;} - void setCovTorquesMeas(const Eigen::Matrix6d& _cov_torques_meas){cov_torques_meas_ = _cov_torques_meas;} - void setCovPbcMeas(const Eigen::Matrix3d& _cov_pbc_meas){cov_pbc_meas_ = _cov_pbc_meas;} - void setCovKinMeas(const Eigen::Matrix<double,14,14>& _cov_kin_meas){cov_kin_meas_ = _cov_kin_meas;} - + /** \brief Constructor from and measures + * + * \param _measurement the measurement + * \param _meas_covariance the noise of the measurement + * \param _J_delta_bias Jacobians of preintegrated delta wrt [pbc,gyro] biases + * \param _pbc_bias COM position relative to bias bias of origin frame time + * \param _gyro_bias gyroscope bias of origin frame time + * \param _cap_ft_ptr pointer to parent CaptureMotion (CaptureForceTorque) + */ + FeatureForceTorque(const Eigen::VectorXd& _delta_preintegrated, + const Eigen::MatrixXd& _delta_preintegrated_covariance, + const Eigen::Vector6d& _biases_preint, + const Eigen::Matrix<double,12,6>& _J_delta_biases); + +// /** \brief Constructor from capture pointer +// * +// * \param _cap_imu_ptr pointer to parent CaptureMotion +// */ +// FeatureForceTorque(CaptureMotionPtr _cap_imu_ptr); + + ~FeatureForceTorque() override = default; + + const Eigen::Vector3d& getPbcBiasPreint() const; + const Eigen::Vector3d& getGyroBiasPreint() const; + const Eigen::Matrix<double, 12, 6>& getJacobianBias() const; + private: - double dt_; - double mass_; - Eigen::Vector6d forces_meas_; - Eigen::Vector6d torques_meas_; - Eigen::Vector3d pbc_meas_; - Eigen::Matrix<double,14,1> kin_meas_; - Eigen::Matrix6d cov_forces_meas_; - Eigen::Matrix6d cov_torques_meas_; - Eigen::Matrix3d cov_pbc_meas_; - Eigen::Matrix<double,14,14> cov_kin_meas_; + // Used biases + Eigen::Vector3d pbc_bias_preint_; ///< Acceleration bias used for delta preintegration + Eigen::Vector3d gyro_bias_preint_; ///< Gyrometer bias used for delta preintegration + + Eigen::Matrix<double, 12, 6> J_delta_bias_; + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW; }; +inline const Eigen::Vector3d& FeatureForceTorque::getPbcBiasPreint() const +{ + return pbc_bias_preint_; +} + +inline const Eigen::Vector3d& FeatureForceTorque::getGyroBiasPreint() const +{ + return gyro_bias_preint_; +} + +inline const Eigen::Matrix<double, 12, 6>& FeatureForceTorque::getJacobianBias() const +{ + return J_delta_bias_; +} + } // namespace wolf -#endif +#endif // FEATURE_FORCE_TORQUE_H_ diff --git a/include/bodydynamics/feature/feature_force_torque_preint.h b/include/bodydynamics/feature/feature_force_torque_preint.h deleted file mode 100644 index 9a03d2a73e668123b29dd948c7988795ea45f9cf..0000000000000000000000000000000000000000 --- a/include/bodydynamics/feature/feature_force_torque_preint.h +++ /dev/null @@ -1,95 +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-------- -#ifndef FEATURE_FORCE_TORQUE_PREINT_H_ -#define FEATURE_FORCE_TORQUE_PREINT_H_ - -//Wolf includes -#include <core/feature/feature_base.h> -#include <core/common/wolf.h> - -//std includes - -namespace wolf { - -//WOLF_PTR_TYPEDEFS(CaptureImu); -WOLF_PTR_TYPEDEFS(FeatureForceTorquePreint); - -class FeatureForceTorquePreint : public FeatureBase -{ - public: - - /** \brief Constructor from and measures - * - * \param _measurement the measurement - * \param _meas_covariance the noise of the measurement - * \param _J_delta_bias Jacobians of preintegrated delta wrt [pbc,gyro] biases - * \param _pbc_bias COM position relative to bias bias of origin frame time - * \param _gyro_bias gyroscope bias of origin frame time - * \param _cap_ftpreint_ptr pointer to parent CaptureMotion (CaptureForceTorquePreint) - */ - FeatureForceTorquePreint(const Eigen::VectorXd& _delta_preintegrated, - const Eigen::MatrixXd& _delta_preintegrated_covariance, - const Eigen::Vector6d& _biases_preint, - const Eigen::Matrix<double,12,6>& _J_delta_biases); - -// /** \brief Constructor from capture pointer -// * -// * \param _cap_imu_ptr pointer to parent CaptureMotion -// */ -// FeatureForceTorquePreint(CaptureMotionPtr _cap_imu_ptr); - - ~FeatureForceTorquePreint() override = default; - - const Eigen::Vector3d& getPbcBiasPreint() const; - const Eigen::Vector3d& getGyroBiasPreint() const; - const Eigen::Matrix<double, 12, 6>& getJacobianBias() const; - - private: - - // Used biases - Eigen::Vector3d pbc_bias_preint_; ///< Acceleration bias used for delta preintegration - Eigen::Vector3d gyro_bias_preint_; ///< Gyrometer bias used for delta preintegration - - Eigen::Matrix<double, 12, 6> J_delta_bias_; - - public: - EIGEN_MAKE_ALIGNED_OPERATOR_NEW; -}; - -inline const Eigen::Vector3d& FeatureForceTorquePreint::getPbcBiasPreint() const -{ - return pbc_bias_preint_; -} - -inline const Eigen::Vector3d& FeatureForceTorquePreint::getGyroBiasPreint() const -{ - return gyro_bias_preint_; -} - -inline const Eigen::Matrix<double, 12, 6>& FeatureForceTorquePreint::getJacobianBias() const -{ - return J_delta_bias_; -} - -} // namespace wolf - -#endif // FEATURE_FORCE_TORQUE_PREINT_H_ diff --git a/include/bodydynamics/math/force_torque_inertial_delta_tools.h b/include/bodydynamics/math/force_torque_inertial_delta_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..42081eba2c3aa4e5e1bc68cf349fa6a57de6cc20 --- /dev/null +++ b/include/bodydynamics/math/force_torque_inertial_delta_tools.h @@ -0,0 +1,1498 @@ +//--------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-------- +/* + * force_torque_inertial_delta_tools.h + * + * Created on: Aug 8, 2021 + * Author: jsola + */ + +#ifndef MATH_FORCE_TORQUE_INERTIAL_DELTA_TOOLS_H_ +#define MATH_FORCE_TORQUE_INERTIAL_DELTA_TOOLS_H_ + +#include <core/common/wolf.h> +#include <core/math/rotations.h> +#include <core/state_block/state_composite.h> + +/* + * Most functions in this file are explained in the document: + * + * https://www.overleaf.com/project/6107c87f581beb50f732ce5f + * + * The functions relate manipulations of Delta motion magnitudes used for Force torque and inertial sensors + * pre-integration. See the following figure for the flow of calculations of the deltas + * + * https://gitlab.iri.upc.edu/mobile_robotics/wolf_projects/wolf_lib/plugins/bodydynamics/-/blob/12-add-functionality-for-aerial-vehicles/doc/figures/dyn-model.pdf + * + * The Delta, of type VectorXd(19) and size 19, is defined as + * Delta = [Dpi, Dvi, Dpd, Dvd, DL, Dq] + * with + * Dpi : position delta as integrated by IMU + * Dvi : velocity delta as integrated by IMU + * Dpd : position delta as integrated by force-torque + * Dvd : velocity delta as integrated by force-torque + * DL : CoM Angular momentum delta as integrated by force-torque + * Dq : quaternion delta as integrated by IMU + * + * In VectorComposite classes representing deltas, these blocks are identified with the following keys + * 'P': position delta as integrated by IMU + * 'V': velocity delta as integrated by IMU + * 'p': position delta as integrated by FT + * 'v': velocity delta as integrated by FT + * 'L': angular momentum delta as integrated by FT + * 'O': orientation delta as integrated by IMU + * + * When Deltas are represented in vector form VectorXd, the order of the blocks is "PVpvLO". + * + * In Frame classes, blocks are identified with the following keys + * 'P': position + * 'V': velocity + * 'L': angular momentum + * 'O': orientation + * + * When states are represented in vector form VectorXd, the order of the blocks is "PVLO". + * Such Frame states are therefore of size 13, VectorXd(13). + * + * Functions involving group-like operations on deltas are listed below: + * + * - identity: I = Delta at the origin, + * with Dpi = [0,0,0], Dvi = [0,0,0], Dpd = [0,0,0], Dvd = [0,0,0], DL = [0,0,0], Dq = [0,0,0,1] + * - inverse: so that D * D.inv = I + * - compose: Dc = D1 * D2 + * - between: Db = D1.inv * D2, so that D2 = D1 * Db + * - composeOverState: x2 = x1 [*] D' + * where D' is a Delta with just the IMU or the FT parts for the "PV" blocks, + * that is "PVLO" or "pvLO", + * as decided by the user when providing the delta. + * - composeOverDelta: x2 = x1 [*] D + * where x is a state "PVLO" and D is a delta "PVpvLO". + * A second parameter `fti::Method` is provided + * to select between IMU "PV" and FT "pv" delta blocks to create the state "PVLO". + * - betweenStates: D = x2 (-) x1, so that x2 = x1 [*] D + * where D is a full delta "PVpvLO", but the "PV" and "pv" blocks are equal. + * - log_fti: go from Delta manifold to tangent space (equivalent to log() in rotations) + * - exp_fti: go from tangent space to delta manifold (equivalent to exp() in rotations) + * - plus: D2 = D1 * exp_fti(d) + * - diff: d = log_fti( D2 (-) D1 ) + * + * For creating the deltas from real data, the following vectors are defined: + * - data[6+np] : [acc[3], gyro[3], prop_speeds[np]], with acc, gyro from IMU, prop_speeds of any size `np` + * - bias[12] : [acc_b[3], gyro_b[3], force_b[3], torque_b[3]] each a 3d vector + * - model[7] : [com[3], inertia[3], mass[1]], with + * - com[3] : position of CoM wrt. Base frame + * - inertia[3]: a 3-vector with [Ixx, Iyy, Izz], so that the inertia matrix is inertia.asDiagonal() + * - mass[1] : a scalar, in Kg + * - tangent[12]: [acc, angvel, force, torque], a vector of intermediate magnitudes, each piece a 3-vector + * + * The design of the math in this file assumes the IMU frame matches the Base frame exactly. + * + * Also a structure Propeller holds important data for a propeller: + * - position : position wrt Base frame + * - rotation : rotation matrix wrt. Base frame + * - c_T : thrust param, thrust = c_T * n^2, with `n` propeller speed in rad/s + * - c_M : torque param, torque = - dir * c_M * n^2, with `n` propeller speed in rad/s, and `dir` the blade + * direction below + * - dir_ccw : direction of blades: clockwise = -1, counter-clockwise = -1. + * + * The following functions are defined, with all Jacobians: + * - data2tangent : obtain tangent from data + * - forces2acc : obtain acceleration from forces + * - tangent2delta : obtain delta from tangent + * - data2delta : obtain deltas from data (uses the three above) + */ + +namespace wolf +{ +namespace bodydynamics +{ +namespace fti +{ +using namespace Eigen; + +/** \brief method to compose deltas with states + */ +enum Method +{ + IMU, ///< Will take IMU parts of the Delta for P and V + FT ///< Will take force-torque parts of the delta for P and V +}; + +struct Propeller +{ + Propeller(const Vector3d& _position, + const Matrix3d& _rotation, + const double& _direction_ccw, /// 1: Counter clock wise (CCW) seen from above; -1: CW + double _c_T, + double _c_M) + : position(_position), rotation(_rotation), direction_ccw(_direction_ccw), c_T(_c_T), c_M(_c_M){}; + + Vector3d position; + Matrix3d rotation; + double direction_ccw; + double c_T; // thrust + double c_M; // torque +}; + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6> +inline void identity(MatrixBase<D1>& dpi, + MatrixBase<D2>& dvi, + MatrixBase<D3>& dpd, + MatrixBase<D4>& dvd, + MatrixBase<D5>& dL, + QuaternionBase<D6>& dq) +{ + dpi = MatrixBase<D1>::Zero(3, 1); + dvi = MatrixBase<D2>::Zero(3, 1); + dpd = MatrixBase<D3>::Zero(3, 1); + dvd = MatrixBase<D4>::Zero(3, 1); + dL = MatrixBase<D5>::Zero(3, 1); + dq = QuaternionBase<D6>::Identity(); +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6> +inline void identity(MatrixBase<D1>& dpi, + MatrixBase<D2>& dvi, + MatrixBase<D3>& dpd, + MatrixBase<D4>& dvd, + MatrixBase<D5>& dL, + MatrixBase<D6>& dq) +{ + typedef typename D1::Scalar T1; + typedef typename D2::Scalar T2; + typedef typename D3::Scalar T3; + typedef typename D4::Scalar T4; + typedef typename D5::Scalar T5; + typedef typename D6::Scalar T6; + dpi << T1(0), T1(0), T1(0); + dvi << T2(0), T2(0), T2(0); + dpd << T3(0), T3(0), T3(0); + dvd << T4(0), T4(0), T4(0); + dL << T5(0), T5(0), T5(0); + dq << T6(0), T6(0), T6(0), T6(1); +} + +template <typename T = double> +inline Matrix<T, 19, 1> identity() +{ + Matrix<T, 19, 1> ret; + ret << T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), + T(1); + return ret; +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + class T> +inline void inverse(MatrixBase<D1>& dpi, + MatrixBase<D2>& dvi, + MatrixBase<D3>& dpd, + MatrixBase<D4>& dvd, + MatrixBase<D5>& dL, + QuaternionBase<D6>& dq, + const T dt, + MatrixBase<D7>& idpi, + MatrixBase<D8>& idvi, + MatrixBase<D9>& idpd, + MatrixBase<D10>& idvd, + MatrixBase<D11>& idL, + QuaternionBase<D12>& idq) +{ + MatrixSizeCheck<3, 1>::check(dpi); + MatrixSizeCheck<3, 1>::check(dvi); + MatrixSizeCheck<3, 1>::check(dpd); + MatrixSizeCheck<3, 1>::check(dvd); + MatrixSizeCheck<3, 1>::check(dL); + MatrixSizeCheck<3, 1>::check(idpi); + MatrixSizeCheck<3, 1>::check(idvi); + MatrixSizeCheck<3, 1>::check(idpd); + MatrixSizeCheck<3, 1>::check(idvd); + MatrixSizeCheck<3, 1>::check(idL); + + idpi = -(dq.conjugate() * (dpi - dvi * typename D3::Scalar(dt))); + idvi = -(dq.conjugate() * dvi); + idpd = -(dq.conjugate() * (dpd - dvd * typename D3::Scalar(dt))); + idvd = -(dq.conjugate() * dvd); + idL = -(dq.conjugate() * dL); + idq = (dq.conjugate()); +} + +template <typename D1, typename D2, class T> +inline void inverse(const MatrixBase<D1>& d, T dt, MatrixBase<D2>& id) +{ + MatrixSizeCheck<19, 1>::check(d); + MatrixSizeCheck<19, 1>::check(id); + + Map<const Matrix<typename D1::Scalar, 3, 1>> dpi(&d(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvi(&d(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dpd(&d(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvd(&d(9)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dL(&d(12)); + Map<const Quaternion<typename D1::Scalar>> dq(&d(15)); + Map<Matrix<typename D2::Scalar, 3, 1>> idpi(&id(0)); + Map<Matrix<typename D2::Scalar, 3, 1>> idvi(&id(3)); + Map<Matrix<typename D2::Scalar, 3, 1>> idpd(&id(6)); + Map<Matrix<typename D2::Scalar, 3, 1>> idvd(&id(9)); + Map<Matrix<typename D2::Scalar, 3, 1>> idL(&id(12)); + Map<Quaternion<typename D1::Scalar>> idq(&id(15)); + + inverse(dpi, dvi, dpd, dvd, dL, dq, dt, idpi, idvi, idpd, idvd, idL, idq); +} + +template <typename D, class T> +inline Matrix<typename D::Scalar, 19, 1> inverse(const MatrixBase<D>& d, T dt) +{ + Matrix<typename D::Scalar, 19, 1> id; + inverse(d, dt, id); + return id; +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + typename D19, + typename D14, + typename D15, + typename D16, + typename D17, + typename D18, + class T> +inline void compose(const MatrixBase<D1>& dpi1, + const MatrixBase<D2>& dvi1, + const MatrixBase<D3>& dpd1, + const MatrixBase<D4>& dvd1, + const MatrixBase<D5>& dL1, + const QuaternionBase<D6>& dq1, + const MatrixBase<D7>& dpi2, + const MatrixBase<D8>& dvi2, + const MatrixBase<D9>& dpd2, + const MatrixBase<D10>& dvd2, + const MatrixBase<D11>& dL2, + const QuaternionBase<D12>& dq2, + const T dt, + MatrixBase<D19>& sum_pi, + MatrixBase<D14>& sum_vi, + MatrixBase<D15>& sum_pd, + MatrixBase<D16>& sum_vd, + MatrixBase<D17>& sum_L, + QuaternionBase<D18>& sum_q) +{ + MatrixSizeCheck<3, 1>::check(dpi1); + MatrixSizeCheck<3, 1>::check(dvi1); + MatrixSizeCheck<3, 1>::check(dpd1); + MatrixSizeCheck<3, 1>::check(dvd1); + MatrixSizeCheck<3, 1>::check(dL1); + MatrixSizeCheck<3, 1>::check(dpi2); + MatrixSizeCheck<3, 1>::check(dvi2); + MatrixSizeCheck<3, 1>::check(dpd2); + MatrixSizeCheck<3, 1>::check(dvd2); + MatrixSizeCheck<3, 1>::check(dL2); + MatrixSizeCheck<3, 1>::check(sum_pi); + MatrixSizeCheck<3, 1>::check(sum_vi); + MatrixSizeCheck<3, 1>::check(sum_pd); + MatrixSizeCheck<3, 1>::check(sum_vd); + MatrixSizeCheck<3, 1>::check(sum_L); + + sum_pi = dpi1 + dvi1 * dt + dq1 * dpi2; + sum_vi = dvi1 + dq1 * dvi2; + sum_pd = dpd1 + dvd1 * dt + dq1 * dpd2; + sum_vd = dvd1 + dq1 * dvd2; + sum_L = dL1 + dq1 * dL2; + sum_q = dq1 * dq2; +} + +template <typename D1, typename D2, typename D3, class T> +inline void compose(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2, T dt, MatrixBase<D3>& sum) +{ + MatrixSizeCheck<19, 1>::check(d1); + MatrixSizeCheck<19, 1>::check(d2); + MatrixSizeCheck<19, 1>::check(sum); + + Map<const Matrix<typename D1::Scalar, 3, 1>> dpi1(&d1(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvi1(&d1(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dpd1(&d1(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvd1(&d1(9)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dL1(&d1(12)); + Map<const Quaternion<typename D1::Scalar>> dq1(&d1(15)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpi2(&d2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvi2(&d2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpd2(&d2(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvd2(&d2(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dL2(&d2(12)); + Map<const Quaternion<typename D2::Scalar>> dq2(&d2(15)); + Map<Matrix<typename D3::Scalar, 3, 1>> sum_pi(&sum(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> sum_vi(&sum(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> sum_pd(&sum(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> sum_vd(&sum(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> sum_L(&sum(12)); + Map<Quaternion<typename D3::Scalar>> sum_q(&sum(15)); + + compose(dpi1, dvi1, dpd1, dvd1, dL1, dq1, dpi2, dvi2, dpd2, dvd2, dL2, dq2, dt, sum_pi, sum_vi, sum_pd, sum_vd, + sum_L, sum_q); +} + +template <typename D1, typename D2, class T> +inline Matrix<typename D1::Scalar, 19, 1> compose(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2, T dt) +{ + Matrix<typename D1::Scalar, 19, 1> ret; + compose(d1, d2, dt, ret); + return ret; +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, class T> +inline void compose(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2, + T dt, + MatrixBase<D3>& sum, + MatrixBase<D4>& J_sum_d1, + MatrixBase<D5>& J_sum_d2) +{ + MatrixSizeCheck<19, 1>::check(d1); + MatrixSizeCheck<19, 1>::check(d2); + MatrixSizeCheck<19, 1>::check(sum); + MatrixSizeCheck<18, 18>::check(J_sum_d1); + MatrixSizeCheck<18, 18>::check(J_sum_d2); + + // Some useful temporaries + Map<const Quaternion<typename D1::Scalar>> dq1(&d1(15)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpi2(&d2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvi2(&d2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpd2(&d2(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvd2(&d2(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dL2(&d2(12)); + Map<const Quaternion<typename D2::Scalar>> dq2(&d2(15)); + + Matrix<typename D1::Scalar, 3, 3> dR1 = dq1.matrix(); + Matrix<typename D2::Scalar, 3, 3> dR2 = dq2.matrix(); + + // JACOBIANS OF COMPOSE() JAC WRT D1 JAC WRT D2 + // dpi1 dvi1 dpd1 dvd1 dL1 dR1 dpi2 dvi2 dpd2 dvd2 dL2 dR2 + // ------------------------------------------ -------------------------- + // sum_pi = dpi1 + dvi1*dt + dq1*dpi2; // I, I*dt, 0, 0 , 0, -dR1 * dpi2_x // dR1, 0, 0, 0, 0, 0 + // sum_vi = dvi1 + dq1*dvi2; // 0, I , 0, 0 , 0, -dR1 * dvi2_x // 0, dR1, 0, 0, 0, 0 + // sum_pd = dpd1 + dvd1*dt + dq1*dpd2; // 0, 0 , I, I*dt, 0, -dR1 * dpd2_x // 0, 0, dR1, 0, 0, 0 + // sum_vd = dvd1 + dq1*dvd2; // 0, 0 , 0, I , 0, -dR1 * dvd2_x // 0, 0, 0, dR1, 0, 0 + // sum_L = dL1 + dq1*dL2; // 0, 0 , 0, 0 , I, -dR1 * dL2_x // 0, 0, 0, 0, dR1, 0 + // sum_q = dq1*dq2; // 0, 0 , 0, 0 , 0, dR2.tr // 0, 0, 0, 0, 0, I + + // // Jac wrt first delta + J_sum_d1.setIdentity(); + J_sum_d1.template block<3, 3>(0, 3) = Matrix3d::Identity() * dt; + J_sum_d1.template block<3, 3>(0, 15) = -dR1 * skew(dpi2); + J_sum_d1.template block<3, 3>(3, 15) = -dR1 * skew(dvi2); + J_sum_d1.template block<3, 3>(6, 9) = Matrix3d::Identity() * dt; + J_sum_d1.template block<3, 3>(6, 15) = -dR1 * skew(dpd2); + J_sum_d1.template block<3, 3>(9, 15) = -dR1 * skew(dvd2); + J_sum_d1.template block<3, 3>(12, 15) = -dR1 * skew(dL2); + J_sum_d1.template block<3, 3>(15, 15) = dR2.transpose(); + + // // // Jac wrt second delta + J_sum_d2.setIdentity(); + J_sum_d2.template block<3, 3>(0, 0) = dR1; + J_sum_d2.template block<3, 3>(3, 3) = dR1; + J_sum_d2.template block<3, 3>(6, 6) = dR1; + J_sum_d2.template block<3, 3>(9, 9) = dR1; + J_sum_d2.template block<3, 3>(12, 12) = dR1; + + // compose deltas -- done here to avoid aliasing when calling with input + // `d1` and result `sum` referencing the same variable + compose(d1, d2, dt, sum); +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + typename D13, + typename D14, + typename D15, + typename D16, + typename D17, + typename D18, + class T> +inline void between(const MatrixBase<D1>& dpi1, + const MatrixBase<D2>& dvi1, + const MatrixBase<D3>& dpd1, + const MatrixBase<D4>& dvd1, + const MatrixBase<D5>& dL1, + const QuaternionBase<D6>& dq1, + const MatrixBase<D7>& dpi2, + const MatrixBase<D8>& dvi2, + const MatrixBase<D9>& dpd2, + const MatrixBase<D10>& dvd2, + const MatrixBase<D11>& dL2, + const QuaternionBase<D12>& dq2, + const T dt, + MatrixBase<D13>& bet_pi, + MatrixBase<D14>& bet_vi, + MatrixBase<D15>& bet_pd, + MatrixBase<D16>& bet_vd, + MatrixBase<D17>& bet_L, + QuaternionBase<D18>& bet_q) +{ + MatrixSizeCheck<3, 1>::check(dpi1); + MatrixSizeCheck<3, 1>::check(dvi1); + MatrixSizeCheck<3, 1>::check(dpd1); + MatrixSizeCheck<3, 1>::check(dvd1); + MatrixSizeCheck<3, 1>::check(dL1); + MatrixSizeCheck<3, 1>::check(dpi2); + MatrixSizeCheck<3, 1>::check(dvi2); + MatrixSizeCheck<3, 1>::check(dpd2); + MatrixSizeCheck<3, 1>::check(dvd2); + MatrixSizeCheck<3, 1>::check(dL2); + MatrixSizeCheck<3, 1>::check(bet_pi); + MatrixSizeCheck<3, 1>::check(bet_vi); + MatrixSizeCheck<3, 1>::check(bet_pd); + MatrixSizeCheck<3, 1>::check(bet_vd); + MatrixSizeCheck<3, 1>::check(bet_L); + + bet_pi = dq1.conjugate() * (dpi2 - dpi1 - dvi1 * dt); + bet_vi = dq1.conjugate() * (dvi2 - dvi1); + bet_pd = dq1.conjugate() * (dpd2 - dpd1 - dvd1 * dt); + bet_vd = dq1.conjugate() * (dvd2 - dvd1); + bet_L = dq1.conjugate() * (dL2 - dL1); + bet_q = dq1.conjugate() * dq2; +} + +template <typename D1, typename D2, typename D3, class T> +inline void between(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2, T dt, MatrixBase<D3>& d2_minus_d1) +{ + MatrixSizeCheck<19, 1>::check(d1); + MatrixSizeCheck<19, 1>::check(d2); + MatrixSizeCheck<19, 1>::check(d2_minus_d1); + + Map<const Matrix<typename D1::Scalar, 3, 1>> dpi1(&d1(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvi1(&d1(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dpd1(&d1(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvd1(&d1(9)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dL1(&d1(12)); + Map<const Quaternion<typename D1::Scalar>> dq1(&d1(15)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpi2(&d2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvi2(&d2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpd2(&d2(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvd2(&d2(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dL2(&d2(12)); + Map<const Quaternion<typename D2::Scalar>> dq2(&d2(15)); + Map<Matrix<typename D3::Scalar, 3, 1>> bet_pi(&d2_minus_d1(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> bet_vi(&d2_minus_d1(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> bet_pd(&d2_minus_d1(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> bet_vd(&d2_minus_d1(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> bet_L(&d2_minus_d1(12)); + Map<Quaternion<typename D3::Scalar>> bet_q(&d2_minus_d1(15)); + + between(dpi1, dvi1, dpd1, dvd1, dL1, dq1, dpi2, dvi2, dpd2, dvd2, dL2, dq2, dt, bet_pi, bet_vi, bet_pd, bet_vd, + bet_L, bet_q); +} + +template <typename D1, typename D2, class T> +inline Matrix<typename D1::Scalar, 19, 1> between(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2, T dt) +{ + Matrix<typename D1::Scalar, 19, 1> diff; + between(d1, d2, dt, diff); + return diff; +} + +inline VectorComposite composeOverState(const VectorComposite& x, + const VectorComposite& d, + double dt, + Method method = IMU) +{ + // Maps to have nice names + Map<const Vector3d> p(x.at('P').data()); // state POVL + Map<const Vector3d> v(x.at('V').data()); + Map<const Quaterniond> q(x.at('O').data()); + Map<const Vector3d> L(x.at('L').data()); + Map<const Vector3d> dpi(d.at('P').data()); // delta PVO from IMU + Map<const Vector3d> dvi(d.at('V').data()); + Map<const Quaterniond> dq(d.at('O').data()); + Map<const Vector3d> dpd(d.at('p').data()); // delta pvL from FT + Map<const Vector3d> dvd(d.at('v').data()); + Map<const Vector3d> dL(d.at('L').data()); + + const Vector3d& g_dt = gravity() * dt; + + VectorComposite x_plus_d; + switch (method) + { + case IMU: + default: + x_plus_d['P'] = p + v * dt + 0.5 * g_dt * dt + q * dpi; + x_plus_d['V'] = v + g_dt + q * dvi; + break; + case FT: + x_plus_d['P'] = p + v * dt + 0.5 * g_dt * dt + q * dpd; + x_plus_d['V'] = v + g_dt + q * dvd; + break; + } + x_plus_d['L'] = L + q * dL; + x_plus_d['O'] = (q * dq).coeffs(); + + return x_plus_d; +} + +template <typename D1, typename D2, typename D3, class T> +inline void composeOverState(const MatrixBase<D1>& x, + const MatrixBase<D2>& d, + T dt, + MatrixBase<D3>& x_plus_d, + Method method = IMU) +{ + MatrixSizeCheck<13, 1>::check(x); + MatrixSizeCheck<19, 1>::check(d); + MatrixSizeCheck<13, 1>::check(x_plus_d); + + Map<const Matrix<typename D1::Scalar, 3, 1>> p(&x(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> v(&x(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> L(&x(6)); + Map<const Quaternion<typename D1::Scalar>> q(&x(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpi(&d(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvi(&d(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpd(&d(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvd(&d(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dL(&d(12)); + Map<const Quaternion<typename D2::Scalar>> dq(&d(15)); + Map<Matrix<typename D3::Scalar, 3, 1>> p_plus_d(&x_plus_d(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> v_plus_d(&x_plus_d(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> L_plus_d(&x_plus_d(6)); + Map<Quaternion<typename D3::Scalar>> q_plus_d(&x_plus_d(9)); + + const Vector3d& g_dt = gravity() * dt; + + switch (method) + { + case IMU: + default: + p_plus_d = p + v * dt + 0.5 * g_dt * dt + q * dpi; + v_plus_d = v + g_dt + q * dvi; + break; + case FT: + p_plus_d = p + v * dt + 0.5 * g_dt * dt + q * dpd; + v_plus_d = v + g_dt + q * dvd; + break; + } + L_plus_d = L + q * dL; + q_plus_d = q * dq; +} + +template <typename D1, typename D2, class T> +inline Matrix<typename D1::Scalar, 13, 1> composeOverState(const MatrixBase<D1>& x, + const MatrixBase<D2>& d, + T dt, + Method method = IMU) +{ + Matrix<typename D1::Scalar, 13, 1> ret; + composeOverState(x, d, dt, ret, method); + return ret; +} + +template <typename D1, + typename D2, + typename D5, + typename D6, + typename D7, + typename D8, + typename D11, + typename D12, + typename D13, + typename D14, + typename D15, + typename D16, + typename D17, + typename D18, + class T> +inline void betweenStates(const MatrixBase<D1>& p1, + const MatrixBase<D2>& v1, + const MatrixBase<D5>& L1, + const QuaternionBase<D6>& q1, + const MatrixBase<D7>& p2, + const MatrixBase<D8>& v2, + const MatrixBase<D11>& L2, + const QuaternionBase<D12>& q2, + const T dt, + MatrixBase<D13>& dpi, + MatrixBase<D14>& dvi, + MatrixBase<D15>& dpd, + MatrixBase<D16>& dvd, + MatrixBase<D17>& dL, + QuaternionBase<D18>& dq) +{ + MatrixSizeCheck<3, 1>::check(p1); + MatrixSizeCheck<3, 1>::check(v1); + MatrixSizeCheck<3, 1>::check(L1); + MatrixSizeCheck<3, 1>::check(p2); + MatrixSizeCheck<3, 1>::check(v2); + MatrixSizeCheck<3, 1>::check(L2); + MatrixSizeCheck<3, 1>::check(dpi); + MatrixSizeCheck<3, 1>::check(dvi); + MatrixSizeCheck<3, 1>::check(dpd); + MatrixSizeCheck<3, 1>::check(dvd); + MatrixSizeCheck<3, 1>::check(dL); + + dpi = q1.conjugate() * (p2 - p1 - v1 * dt - 0.5 * gravity() * dt * dt); + dvi = q1.conjugate() * (v2 - v1 - gravity() * dt); + dpd = dpi; + dvd = dvi; + dL = q1.conjugate() * (L2 - L1); + dq = q1.conjugate() * q2; +} + +template <typename D1, typename D2, typename D3, class T> +inline void betweenStates(const MatrixBase<D1>& x1, const MatrixBase<D2>& x2, T dt, MatrixBase<D3>& x2_minus_x1) +{ + MatrixSizeCheck<13, 1>::check(x1); + MatrixSizeCheck<13, 1>::check(x2); + MatrixSizeCheck<19, 1>::check(x2_minus_x1); + + Map<const Matrix<typename D1::Scalar, 3, 1>> p1(&x1(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> v1(&x1(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> L1(&x1(6)); + Map<const Quaternion<typename D1::Scalar>> q1(&x1(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> p2(&x2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> v2(&x2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> L2(&x2(6)); + Map<const Quaternion<typename D2::Scalar>> q2(&x2(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> dpi(&x2_minus_x1(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> dvi(&x2_minus_x1(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> dpd(&x2_minus_x1(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> dvd(&x2_minus_x1(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> dL(&x2_minus_x1(12)); + Map<Quaternion<typename D3::Scalar>> dq(&x2_minus_x1(15)); + + betweenStates(p1, v1, L1, q1, p2, v2, L2, q2, dt, dpi, dvi, dpd, dvd, dL, dq); +} + +template <typename D1, typename D2, class T> +inline Matrix<typename D1::Scalar, 19, 1> betweenStates(const MatrixBase<D1>& x1, const MatrixBase<D2>& x2, T dt) +{ + Matrix<typename D1::Scalar, 19, 1> ret; + betweenStates(x1, x2, dt, ret); + return ret; +} + +inline void betweenStates(const VectorComposite& x1, const VectorComposite& x2, double dt, VectorComposite& ret) +{ + Map<const Quaterniond> q1(x1.at('O').data()); + Map<const Quaterniond> q2(x2.at('O').data()); + + ret['P'] = q1.conjugate() * (x2.at('P') - x1.at('P') - x1.at('V') * dt - 0.5 * gravity() * dt * dt); + ret['V'] = q1.conjugate() * (x2.at('V') - x1.at('V') - gravity() * dt); + ret['p'] = ret.at('P'); + ret['v'] = ret.at('V'); + ret['L'] = q1.conjugate() * (x2.at('L') - x1.at('L')); + ret['O'] = (q1.conjugate() * q2).coeffs(); +} + +inline VectorComposite betweenStates(const VectorComposite& x1, const VectorComposite& x2, double dt) +{ + VectorComposite ret; + betweenStates(x1, x2, dt, ret); + return ret; +} + +template <typename Derived> +Matrix<typename Derived::Scalar, 18, 1> log_fti(const MatrixBase<Derived>& delta_in) +{ + MatrixSizeCheck<19, 1>::check(delta_in); + + Matrix<typename Derived::Scalar, 18, 1> ret; + + Map<const Matrix<typename Derived::Scalar, 3, 1>> dpi_in(&delta_in(0)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dvi_in(&delta_in(3)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dpd_in(&delta_in(6)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dvd_in(&delta_in(9)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dL_in(&delta_in(12)); + Map<const Quaternion<typename Derived::Scalar>> dq_in(&delta_in(15)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dpi_ret(&ret(0)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dvi_ret(&ret(3)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dpd_ret(&ret(6)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dvd_ret(&ret(9)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dL_ret(&ret(12)); + Map<Matrix<typename Derived::Scalar, 3, 1>> do_ret(&ret(15)); + + dpi_ret = dpi_in; + dvi_ret = dvi_in; + dpd_ret = dpd_in; + dvd_ret = dvd_in; + dL_ret = dL_in; + do_ret = log_q(dq_in); + + return ret; +} + +template <typename Derived> +Matrix<typename Derived::Scalar, 19, 1> exp_fti(const MatrixBase<Derived>& d_alg_in) +{ + MatrixSizeCheck<18, 1>::check(d_alg_in); + + Matrix<typename Derived::Scalar, 19, 1> ret; + + Map<const Matrix<typename Derived::Scalar, 3, 1>> dpi_alg_in(&d_alg_in(0)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dvi_alg_in(&d_alg_in(3)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dpd_alg_in(&d_alg_in(6)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dvd_alg_in(&d_alg_in(9)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> dL_alg_in(&d_alg_in(12)); + Map<const Matrix<typename Derived::Scalar, 3, 1>> do_alg_in(&d_alg_in(15)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dpi_ret(&ret(0)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dvi_ret(&ret(3)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dpd_ret(&ret(6)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dvd_ret(&ret(9)); + Map<Matrix<typename Derived::Scalar, 3, 1>> dL_ret(&ret(12)); + Map<Quaternion<typename Derived::Scalar>> dq_ret(&ret(15)); + + dpi_ret = dpi_alg_in; + dvi_ret = dvi_alg_in; + dpd_ret = dpd_alg_in; + dvd_ret = dvd_alg_in; + dL_ret = dL_alg_in; + dq_ret = exp_q(do_alg_in); + + return ret; +} + +template <typename D1, typename D2, typename D3> +void exp_fti(const MatrixBase<D1>& _tangent, MatrixBase<D2>& _delta, MatrixBase<D3>& _jacobian_delta_tangent) +{ + MatrixSizeCheck<18, 1>::check(_tangent); + MatrixSizeCheck<19, 1>::check(_delta); + MatrixSizeCheck<18, 18>::check(_jacobian_delta_tangent); + + _delta = fti::exp_fti(_tangent); + _jacobian_delta_tangent.setIdentity(); + _jacobian_delta_tangent.template block<3, 3>(15, 15) = jac_SO3_right(_tangent.template segment<3>(15)); +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + typename D13, + typename D14, + typename D15, + typename D16, + typename D17, + typename D18> +inline void plus(const MatrixBase<D1>& dpi1, + const MatrixBase<D2>& dvi1, + const MatrixBase<D3>& dpd1, + const MatrixBase<D4>& dvd1, + const MatrixBase<D5>& dL1, + const QuaternionBase<D6>& dq1, + const MatrixBase<D7>& diff_pi2, + const MatrixBase<D8>& diff_vi2, + const MatrixBase<D9>& diff_pd2, + const MatrixBase<D10>& diff_vd2, + const MatrixBase<D11>& diff_L2, + const MatrixBase<D12>& diff_o2, + MatrixBase<D13>& plus_pi, + MatrixBase<D14>& plus_vi, + MatrixBase<D15>& plus_pd, + MatrixBase<D16>& plus_vd, + MatrixBase<D17>& plus_L, + QuaternionBase<D18>& plus_q) +{ + plus_pi = dpi1 + diff_pi2; + plus_vi = dvi1 + diff_vi2; + plus_pd = dpd1 + diff_pd2; + plus_vd = dvd1 + diff_vd2; + plus_L = dL1 + diff_L2; + plus_q = dq1 * exp_q(diff_o2); +} + +// Composite plus +template <typename D1, typename D2, typename D3> +inline void plus(const MatrixBase<D1>& d1, const MatrixBase<D2>& diff2, MatrixBase<D3>& d) +{ + Map<const Matrix<typename D1::Scalar, 3, 1>> dpi1(&d1(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvi1(&d1(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dpd1(&d1(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvd1(&d1(9)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dL1(&d1(12)); + Map<const Quaternion<typename D1::Scalar>> dq1(&d1(15)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_dpi2(&diff2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_dvi2(&diff2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_dpd2(&diff2(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_dvd2(&diff2(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_dL2(&diff2(12)); + Map<const Matrix<typename D2::Scalar, 3, 1>> diff_do2(&diff2(15)); + Map<Matrix<typename D3::Scalar, 3, 1>> d_pi(&d(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> d_vi(&d(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> d_pd(&d(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> d_vd(&d(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> d_L(&d(12)); + Map<Quaternion<typename D3::Scalar>> d_q(&d(15)); + + plus(dpi1, dvi1, dpd1, dvd1, dL1, dq1, diff_dpi2, diff_dvi2, diff_dpd2, diff_dvd2, diff_dL2, diff_do2, d_pi, d_vi, + d_pd, d_vd, d_L, d_q); +} + +template <typename D1, typename D2> +inline Matrix<typename D1::Scalar, 19, 1> plus(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2) +{ + Matrix<typename D1::Scalar, 19, 1> ret; + plus(d1, d2, ret); + return ret; +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + typename D13, + typename D14, + typename D15, + typename D16, + typename D17, + typename D18> +inline void diff(const MatrixBase<D1>& dpi1, + const MatrixBase<D2>& dvi1, + const MatrixBase<D3>& dpd1, + const MatrixBase<D4>& dvd1, + const MatrixBase<D5>& dL1, + const QuaternionBase<D6>& dq1, + const MatrixBase<D7>& dpi2, + const MatrixBase<D8>& dvi2, + const MatrixBase<D9>& dpd2, + const MatrixBase<D10>& dvd2, + const MatrixBase<D11>& dL2, + const QuaternionBase<D12>& dq2, + MatrixBase<D13>& diff_pi, + MatrixBase<D14>& diff_vi, + MatrixBase<D15>& diff_pd, + MatrixBase<D16>& diff_vd, + MatrixBase<D17>& diff_L, + MatrixBase<D18>& diff_o) +{ + diff_pi = dpi2 - dpi1; + diff_vi = dvi2 - dvi1; + diff_pd = dpd2 - dpd1; + diff_vd = dvd2 - dvd1; + diff_L = dL2 - dL1; + diff_o = log_q(dq1.conjugate() * dq2); +} + +template <typename D1, typename D2, typename D3> +inline void diff(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2, MatrixBase<D3>& diff_d1_d2) +{ + Map<const Matrix<typename D1::Scalar, 3, 1>> dpi1(&d1(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvi1(&d1(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dpd1(&d1(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dvd1(&d1(9)); + Map<const Matrix<typename D1::Scalar, 3, 1>> dL1(&d1(12)); + Map<const Quaternion<typename D1::Scalar>> dq1(&d1(15)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpi2(&d2(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvi2(&d2(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dpd2(&d2(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dvd2(&d2(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> dL2(&d2(12)); + Map<const Quaternion<typename D1::Scalar>> dq2(&d2(15)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_pi(&diff_d1_d2(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_vi(&diff_d1_d2(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_pd(&diff_d1_d2(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_vd(&diff_d1_d2(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_L(&diff_d1_d2(12)); + Map<Matrix<typename D3::Scalar, 3, 1>> diff_o(&diff_d1_d2(15)); + + diff(dpi1, dvi1, dpd1, dvd1, dL1, dq1, dpi2, dvi2, dpd2, dvd2, dL2, dq2, diff_pi, diff_vi, diff_pd, diff_vd, + diff_L, diff_o); +} + +template <typename D1, typename D2> +inline Matrix<typename D1::Scalar, 18, 1> diff(const MatrixBase<D1>& d1, const MatrixBase<D2>& d2) +{ + Matrix<typename D1::Scalar, 18, 1> ret; + diff(d1, d2, ret); + return ret; +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12> +void data2tangent(const MatrixBase<D1>& _data_acc, + const MatrixBase<D2>& _data_angvel, + const MatrixBase<D3>& _data_motor_speeds, + const MatrixBase<D4>& _bias_acc, + const MatrixBase<D5>& _bias_angvel, + const MatrixBase<D6>& _bias_force, + const MatrixBase<D7>& _bias_torque, + const MatrixBase<D8>& _com, + const std::list<Propeller>& _propellers, + MatrixBase<D9>& _tangent_acc, + MatrixBase<D10>& _tangent_angvel, + MatrixBase<D11>& _tangent_force, + MatrixBase<D12>& _tangent_torque) +{ + // check sizes + // TODO + + // helpers + const auto& r_bc = _com; + + _tangent_force.setZero(); + _tangent_torque.setZero(); + + unsigned int i = 0; + for (const Propeller& propeller : _propellers) + { + const Vector3d& r_bi = propeller.position; + const Matrix3d& R_bi = propeller.rotation; + double direction = propeller.direction_ccw; + const double& c_T = propeller.c_T; + const double& c_M = propeller.c_M; + const double c_M_by_c_T = c_M / c_T; + + const auto& n_i = _data_motor_speeds(i); + + Vector3d f_bi = R_bi.rightCols(1) * c_T * n_i * n_i; + + _tangent_force += f_bi; + _tangent_torque += (r_bi - r_bc).cross(f_bi) - direction * c_M_by_c_T * f_bi; + + i++; + } + + _tangent_acc = _data_acc - _bias_acc; + _tangent_angvel = _data_angvel - _bias_angvel; + _tangent_force += _bias_force; + _tangent_torque += _bias_torque; +} + +template <typename D1, typename D2, typename D3, typename D4> +void data2tangent(const MatrixBase<D1>& _data, + const MatrixBase<D2>& _bias, + const MatrixBase<D3>& _com, + const std::list<Propeller>& _propellers, + MatrixBase<D4>& _tangent) +{ + const int Np = _data.size() - 6; + Map<const Matrix<typename D1::Scalar, 3, 1>> am(&_data(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> wm(&_data(3)); + Map<const Matrix<typename D1::Scalar, Dynamic, 1>> n(&_data(6), Np); + + Map<const Matrix<typename D2::Scalar, 3, 1>> ab(&_bias(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> wb(&_bias(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> fb(&_bias(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> mb(&_bias(9)); + + Map<Matrix<typename D4::Scalar, 3, 1>> a(&_tangent(0)); + Map<Matrix<typename D4::Scalar, 3, 1>> w(&_tangent(3)); + Map<Matrix<typename D4::Scalar, 3, 1>> f(&_tangent(6)); + Map<Matrix<typename D4::Scalar, 3, 1>> m(&_tangent(9)); + + data2tangent(am, wm, n, ab, wb, fb, mb, _com, _propellers, a, w, f, m); +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7> +void data2tangent(const MatrixBase<D1>& _data, + const MatrixBase<D2>& _bias, + const MatrixBase<D3>& _com, + const std::list<Propeller>& _propellers, + MatrixBase<D4>& _tangent, + MatrixBase<D5>& _J_tan_data, + MatrixBase<D6>& _J_tan_bias, + MatrixBase<D7>& _J_tan_com) +{ + const unsigned int np = _propellers.size(); + Map<const Matrix<typename D1::Scalar, 3, 1>> am(&_data(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> wm(&_data(3)); + Map<const Matrix<typename D1::Scalar, Dynamic, 1>> n(&_data(6), np); + Map<const Matrix<typename D2::Scalar, 3, 1>> ab(&_bias(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> wb(&_bias(3)); + Map<const Matrix<typename D2::Scalar, 3, 1>> fb(&_bias(6)); + Map<const Matrix<typename D2::Scalar, 3, 1>> mb(&_bias(9)); + + Map<Matrix<typename D4::Scalar, 3, 1>> a(&_tangent(0)); + Map<Matrix<typename D4::Scalar, 3, 1>> w(&_tangent(3)); + Map<Matrix<typename D4::Scalar, 3, 1>> f(&_tangent(6)); + Map<Matrix<typename D4::Scalar, 3, 1>> m(&_tangent(9)); + + data2tangent(_data, _bias, _com, _propellers, _tangent); + + // blocks + const unsigned int A = 0; // acc + const unsigned int W = 3; // gyro + const unsigned int F = 6; // force + const unsigned int M = 9; // torque + const unsigned int N = 6; // motor speeds + const unsigned int C = 0; // CoM + + const Matrix3d I_33 = Matrix3d::Identity(); + + // jac wrt data + _J_tan_data.setIdentity(); + // _J_tan_data.block<3,3>(A,A) = I_33; + // _J_tan_data.block<3,3>(W,W) = I_33; + + unsigned int i = 0; + for (const Propeller& propeller : _propellers) + { + const auto& ri = propeller.position; + const auto& Ri = propeller.rotation; + const auto& dir = propeller.direction_ccw; + const auto& cTi = propeller.c_T; + const auto& cMi = propeller.c_M; + const auto& ni = n(i); + + Vector3d J_fi_ni = 2 * cTi * ni * Ri.rightCols(1); + + Vector3d J_mi_ni = skew(ri - _com) * J_fi_ni - 2 * dir * cMi * ni * Ri.rightCols(1); + + _J_tan_data.block(F, N + i, 3, 1) = J_fi_ni; + _J_tan_data.block(M, N + i, 3, 1) = J_mi_ni; + + i++; + } + + // jac wrt bias + _J_tan_bias.setIdentity(); + _J_tan_bias.template block<3, 3>(A, A) = -I_33; + _J_tan_bias.template block<3, 3>(W, W) = -I_33; + // _J_tan_bias.block<3,3>(F,F) = I_33; + // _J_tan_bias.block<3,3>(M,M) = I_33; + + // jac wrt com + _J_tan_com.setZero(); + // __J_tan_com.block<3,3>(A,C) = 0; + // __J_tan_com.block<3,3>(W,C) = 0; + // __J_tan_com.block<3,3>(F,C) = 0; + + // The total torque is + // m = sum_i m_i + // with + // m_i = (r_i - r_com) x f_bi + (terms not related to r_com). + // Then, + // J_mi_com = [f_bi]x + // and thus + // J_m_com = sum_i [f_bi]x . + // But since for any set of a_i + // sum [a_i]x = [sum a_i]x + // we have that + // J_m_com = sum [f_bi]x = [sum fbi]x = [ft]x + // where + // ft = sum_i f_bi + // is the total force. To get it, remove the bias which was added in the function: + // ft = f - fb + + _J_tan_com.template block<3, 3>(M, C) = skew(f - fb); +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, class T> +void forces2acc(const MatrixBase<D1>& _force, + const MatrixBase<D2>& _torque, + const MatrixBase<D3>& _angvel, + const MatrixBase<D4>& _com, + const MatrixBase<D5>& _inertia, + const T _mass, + MatrixBase<D6>& _acc) +{ + typedef typename D6::Scalar TT; // take scalar from return type + typedef Matrix<TT, 3, 3> Matrix3t; + typedef Matrix<TT, 3, 1> Vector3t; + + const Matrix3t& I = _inertia.asDiagonal(); + const Matrix3t& I_inv = ((typename D5::Scalar)(1.0) / _inertia.array()).matrix().asDiagonal(); + + const Vector3t& angacc = I_inv * (_torque - _angvel.cross(I * _angvel)); + + _acc = _force / _mass - angacc.cross(_com) - _angvel.cross(_angvel.cross(_com)); +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + class T> +void forces2acc(const MatrixBase<D1>& _force, + const MatrixBase<D2>& _torque, + const MatrixBase<D3>& _angvel, + const MatrixBase<D4>& _com, + const MatrixBase<D5>& _inertia, + const T _mass, + MatrixBase<D6>& _acc, + MatrixBase<D7>& _J_acc_force, + MatrixBase<D8>& _J_acc_torque, + MatrixBase<D9>& _J_acc_angvel, + MatrixBase<D10>& _J_acc_com, + MatrixBase<D11>& _J_acc_inertia, + MatrixBase<D12>& _J_acc_mass) +{ + typedef typename D6::Scalar S; // take scalar from return type + typedef Matrix<S, 3, 3> Matrix3t; + typedef Matrix<S, 3, 1> Vector3t; + + // constants + const Matrix3t& I = _inertia.asDiagonal(); + const Matrix3t& I_inv = ((typename D5::Scalar)(1.0) / _inertia.array()).matrix().asDiagonal(); + + // variables + const Vector3t& angacc = I_inv * (_torque - _angvel.cross(I * _angvel)); // (36) + const Matrix3t& angvel_x = skew(_angvel); + const Matrix3t& com_x = skew(_com); + const Matrix3t& angacc_x = skew(angacc); + + _acc = _force / _mass - angacc.cross(_com) - _angvel.cross(_angvel.cross(_com)); // (35) + + // Jacobian blocks + const Matrix3t& tmp = (_angvel.cross(I * _angvel) - _torque).asDiagonal(); + const Matrix3t& J_Iw_i = _angvel.asDiagonal(); // (58) + const Matrix3t& J_angacc_inertia = I_inv * (I_inv * tmp - angvel_x * J_Iw_i); // (57) + const Matrix3t& J_acc_angacc = com_x; // (56) + const Matrix3t& J_angacc_torque = I_inv; // (53) + const Matrix3t& J_angvelcom_angvel = -com_x; // (39) + const Matrix3t& J_angacc_angvel = I_inv * (skew(I * _angvel) - angvel_x * I); // (38) + + // Output Jacobians + _J_acc_force = Matrix<S, 3, 3>::Identity() / _mass; // (59) + _J_acc_torque = J_acc_angacc * J_angacc_torque; // (?) + _J_acc_angvel = + J_acc_angacc * J_angacc_angvel - angvel_x * J_angvelcom_angvel + skew(_angvel.cross(_com)); // (37) + _J_acc_com = -angacc_x - angvel_x * angvel_x; // (51) + _J_acc_inertia = J_acc_angacc * J_angacc_inertia; // (55) + _J_acc_mass = -_force / (_mass * _mass); // (50) +} + +template <typename D1, + typename D2, + typename D3, + typename D4, + typename D5, + typename D6, + typename D7, + typename D8, + typename D9, + typename D10, + typename D11, + typename D12, + class T, + class DT> +void tangent2delta(const MatrixBase<D1>& _tangent_acc_imu, + const MatrixBase<D2>& _tangent_angvel_imu, + const MatrixBase<D3>& _tangent_force, + const MatrixBase<D4>& _tangent_torque, + const MatrixBase<D5>& _com, + const MatrixBase<D6>& _inertia, + const T _mass, + const DT _dt, + MatrixBase<D7>& _delta_p_imu, + MatrixBase<D8>& _delta_v_imu, + MatrixBase<D9>& _delta_p_dyn, + MatrixBase<D10>& _delta_v_dyn, + MatrixBase<D11>& _delta_L, + QuaternionBase<D12>& _delta_q) +{ + DT dt2 = _dt * _dt; + + Matrix<typename D3::Scalar, 3, 1> acc_d; + + forces2acc(_tangent_force, _tangent_torque, _tangent_angvel_imu, _com, _inertia, _mass, acc_d); + + _delta_p_imu = 0.5 * _tangent_acc_imu * dt2; + _delta_v_imu = _tangent_acc_imu * _dt; + _delta_p_dyn = 0.5 * acc_d * dt2; + _delta_v_dyn = acc_d * _dt; + _delta_L = _tangent_torque * _dt; + _delta_q = exp_q(_tangent_angvel_imu * _dt); +} + +template <typename D1, typename D2, typename D3, typename D4, class T, class DT> +void tangent2delta(const MatrixBase<D1>& _tangent, + const MatrixBase<D2>& _com, + const MatrixBase<D3>& _inertia, + const T _mass, + const DT _dt, + MatrixBase<D4>& _delta) +{ + // use shorter names + Map<const Matrix<typename D1::Scalar, 3, 1>> acc_i(&_tangent(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> angvel(&_tangent(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> force(&_tangent(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> torque(&_tangent(9)); + Map<Matrix<typename D4::Scalar, 3, 1>> _delta_p_imu(&_delta(0)); + Map<Matrix<typename D4::Scalar, 3, 1>> _delta_v_imu(&_delta(3)); + Map<Matrix<typename D4::Scalar, 3, 1>> _delta_p_dyn(&_delta(6)); + Map<Matrix<typename D4::Scalar, 3, 1>> _delta_v_dyn(&_delta(9)); + Map<Matrix<typename D4::Scalar, 3, 1>> _delta_L(&_delta(12)); + Map<Quaternion<typename D4::Scalar>> _delta_q(&_delta(15)); + + DT dt2 = _dt * _dt; + + Matrix<typename D1::Scalar, 3, 1> acc_d; + + forces2acc(force, torque, angvel, _com, _inertia, _mass, acc_d); + + _delta_p_imu = 0.5 * acc_i * dt2; + _delta_v_imu = acc_i * _dt; + _delta_p_dyn = 0.5 * acc_d * dt2; + _delta_v_dyn = acc_d * _dt; + _delta_L = torque * _dt; + _delta_q = exp_q(angvel * _dt); +} + +template <typename D1, typename D2, typename D3, class T> +void tangent2delta(const MatrixBase<D1>& _tangent, const MatrixBase<D2>& _model, const T _dt, MatrixBase<D3>& _delta) +{ + // use shorter names + Map<const Matrix<typename D1::Scalar, 3, 1>> acc_i(&_tangent(0)); + Map<const Matrix<typename D1::Scalar, 3, 1>> angvel(&_tangent(3)); + Map<const Matrix<typename D1::Scalar, 3, 1>> force(&_tangent(6)); + Map<const Matrix<typename D1::Scalar, 3, 1>> torque(&_tangent(9)); + Map<const Matrix<typename D2::Scalar, 3, 1>> com(&_model(0)); + Map<const Matrix<typename D2::Scalar, 3, 1>> inertia(&_model(3)); + const typename D2::Scalar& mass(_model(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_p_imu(&_delta(0)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_v_imu(&_delta(3)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_p_dyn(&_delta(6)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_v_dyn(&_delta(9)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_L(&_delta(12)); + Map<Quaternion<typename D3::Scalar>> _delta_q(&_delta(15)); + + Matrix<typename D1::Scalar, 3, 1> acc_d; + + forces2acc(force, torque, angvel, com, inertia, mass, acc_d); + + T dt2 = _dt * _dt; + + // delta + _delta_p_imu = 0.5 * acc_i * dt2; + _delta_v_imu = acc_i * _dt; + _delta_p_dyn = 0.5 * acc_d * dt2; + _delta_v_dyn = acc_d * _dt; + _delta_L = torque * _dt; + _delta_q = exp_q(angvel * _dt); +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, class T> +void tangent2delta(const MatrixBase<D1>& _tangent, + const MatrixBase<D2>& _model, + const T _dt, + MatrixBase<D3>& _delta, + MatrixBase<D4>& _J_delta_tangent, + MatrixBase<D5>& _J_delta_model) +{ + // blocks + const unsigned int A = 0; // acc + const unsigned int W = 3; // gyro + const unsigned int F = 6; // force + const unsigned int TQ = 9; // torque + const unsigned int C = 0; // CoM + const unsigned int I = 3; // inertia + const unsigned int M = 6; // mass + const unsigned int PI = 0; // position IMU + const unsigned int VI = 3; // velocity IMU + const unsigned int PD = 6; // position DYN + const unsigned int VD = 9; // velocity DYN + const unsigned int L = 12; // ang momentum + const unsigned int Q = 15; // orientation + + // use shorter names + Map<const Matrix<typename D1::Scalar, 3, 1>> acc_i(&_tangent(A)); + Map<const Matrix<typename D1::Scalar, 3, 1>> angvel(&_tangent(W)); + Map<const Matrix<typename D1::Scalar, 3, 1>> force(&_tangent(F)); + Map<const Matrix<typename D1::Scalar, 3, 1>> torque(&_tangent(TQ)); + Map<const Matrix<typename D2::Scalar, 3, 1>> com(&_model(C)); + Map<const Matrix<typename D2::Scalar, 3, 1>> inertia(&_model(I)); + const typename D2::Scalar& mass(_model(M)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_p_imu(&_delta(PI)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_v_imu(&_delta(VI)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_p_dyn(&_delta(PD)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_v_dyn(&_delta(VD)); + Map<Matrix<typename D3::Scalar, 3, 1>> _delta_L(&_delta(L)); + Map<Quaternion<typename D3::Scalar>> _delta_q(&_delta(Q)); + + Matrix<typename D1::Scalar, 3, 1> acc_d; + + Matrix<typename D1::Scalar, 3, 3> J_accd_force; + Matrix<typename D1::Scalar, 3, 3> J_accd_torque; + Matrix<typename D1::Scalar, 3, 3> J_accd_angvel; + Matrix<typename D1::Scalar, 3, 3> J_accd_com; + Matrix<typename D1::Scalar, 3, 3> J_accd_inertia; + Matrix<typename D1::Scalar, 3, 1> J_accd_mass; + + forces2acc(force, torque, angvel, com, inertia, mass, + acc_d, + J_accd_force, J_accd_torque, J_accd_angvel, + J_accd_com, J_accd_inertia, J_accd_mass); + + T dt2 = _dt * _dt; + + // delta + _delta_p_imu = 0.5 * acc_i * dt2; + _delta_v_imu = acc_i * _dt; + _delta_p_dyn = 0.5 * acc_d * dt2; + _delta_v_dyn = acc_d * _dt; + _delta_q = exp_q(angvel * _dt); + _delta_L = torque * _dt; + + // temporaries + const Matrix<typename D1::Scalar, 3, 3>& I_dt = Matrix<typename D1::Scalar, 3, 3>::Identity() * _dt; + const Matrix<typename D1::Scalar, 3, 3>& I_dt2_2 = 0.5 * Matrix<typename D1::Scalar, 3, 3>::Identity() * dt2; + + // jacobians + Matrix<typename D1::Scalar, 3, 3> J_dpi_acci = I_dt2_2; + Matrix<typename D1::Scalar, 3, 3> J_dvi_acci = I_dt; + const auto& J_dpd_accd = I_dt2_2; + const auto& J_dvd_accd = I_dt; + Matrix<typename D1::Scalar, 3, 3> J_dq_angvel = jac_SO3_right(angvel * _dt) * _dt; + Matrix<typename D1::Scalar, 3, 3> J_dl_torque = I_dt; + + _J_delta_tangent.setZero(); + + _J_delta_tangent.template block<3, 3>(PI, A) = J_dpi_acci; + _J_delta_tangent.template block<3, 3>(VI, A) = J_dvi_acci; + + _J_delta_tangent.template block<3, 3>(PD, W) = J_dpd_accd * J_accd_angvel; + _J_delta_tangent.template block<3, 3>(PD, F) = J_dpd_accd * J_accd_force; + _J_delta_tangent.template block<3, 3>(PD, TQ) = J_dpd_accd * J_accd_torque; + + _J_delta_tangent.template block<3, 3>(VD, W) = J_dvd_accd * J_accd_angvel; + _J_delta_tangent.template block<3, 3>(VD, F) = J_dvd_accd * J_accd_force; + _J_delta_tangent.template block<3, 3>(VD, TQ) = J_dvd_accd * J_accd_torque; + + _J_delta_tangent.template block<3, 3>(Q, W) = J_dq_angvel; + _J_delta_tangent.template block<3, 3>(L, TQ) = J_dl_torque; + + _J_delta_model.setZero(); + + _J_delta_model.template block<3, 3>(PD, C) = J_dpd_accd * J_accd_com; + _J_delta_model.template block<3, 3>(PD, I) = J_dpd_accd * J_accd_inertia; + _J_delta_model.template block<3, 1>(PD, M) = J_dpd_accd * J_accd_mass; + + _J_delta_model.template block<3, 3>(VD, C) = J_dvd_accd * J_accd_com; + _J_delta_model.template block<3, 3>(VD, I) = J_dvd_accd * J_accd_inertia; + _J_delta_model.template block<3, 1>(VD, M) = J_dvd_accd * J_accd_mass; +} + +template <typename D1, typename D2, typename D3, typename D4, class T> +void data2delta(const MatrixBase<D1>& _data, + const MatrixBase<D2>& _bias, + const MatrixBase<D3>& _model, + const std::list<Propeller>& _propellers, + const T _dt, + MatrixBase<D4>& _delta) +{ + Map<const Matrix<typename D2::Scalar, 3, 1>> com(&_model(0)); + + Matrix<typename D4::Scalar, 12, 1> tangent; + + data2tangent(_data, _bias, com, _propellers, tangent); + + tangent2delta(tangent, _model, _dt, _delta); +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, class T> +void data2delta(const MatrixBase<D1>& _data, + const MatrixBase<D2>& _bias, + const MatrixBase<D3>& _model, + const std::list<Propeller>& _propellers, + const T _dt, + MatrixBase<D4>& _delta, + MatrixBase<D5>& _J_delta_data, + MatrixBase<D6>& _J_delta_bias, + MatrixBase<D7>& _J_delta_model) +{ + const unsigned int np = _propellers.size(); + Map<const Matrix<typename D2::Scalar, 3, 1>> com(&_model(0)); + + Matrix<typename D4::Scalar, 12, 1> tangent; + + Matrix<typename D4::Scalar, 12, Dynamic> J_tangent_data(12, 6 + np); + Matrix<typename D4::Scalar, 12, 12> J_tangent_bias; + Matrix<typename D4::Scalar, 12, 7> J_tangent_model; + Matrix<typename D4::Scalar, 18, 12> J_delta_tangent; + + J_tangent_model.setZero(12, 7); + + Matrix<typename D4::Scalar, 12, 3> J_tangent_com; + data2tangent(_data, _bias, com, _propellers, tangent, J_tangent_data, J_tangent_bias, J_tangent_com); + J_tangent_model.template block<12, 3>(0, 0) = J_tangent_com; + + tangent2delta(tangent, _model, _dt, _delta, J_delta_tangent, _J_delta_model); + + _J_delta_data = J_delta_tangent * J_tangent_data; + _J_delta_bias = J_delta_tangent * J_tangent_bias; + _J_delta_model = _J_delta_model + J_delta_tangent * J_tangent_model; +} + + +} // namespace fti +} // namespace bodydynamics +} // namespace wolf + +#endif /* MATH_FORCE_TORQUE_INERTIAL_DELTA_TOOLS_H_ */ diff --git a/include/bodydynamics/processor/processor_force_torque_preint.h b/include/bodydynamics/processor/processor_force_torque.h similarity index 79% rename from include/bodydynamics/processor/processor_force_torque_preint.h rename to include/bodydynamics/processor/processor_force_torque.h index 6cd089107cfebd55fad2a51ff1b6cf5eddb0b036..1b8197fc27350412ec330feb802d0a5751bcb916 100644 --- a/include/bodydynamics/processor/processor_force_torque_preint.h +++ b/include/bodydynamics/processor/processor_force_torque.h @@ -19,24 +19,24 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // //--------LICENSE_END-------- -#ifndef PROCESSOR_FORCE_TORQUE_PREINT_H -#define PROCESSOR_FORCE_TORQUE_PREINT_H +#ifndef PROCESSOR_FORCE_TORQUE_H +#define PROCESSOR_FORCE_TORQUE_H // Wolf core #include <core/processor/processor_motion.h> namespace wolf { -WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorForceTorquePreint); +WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorForceTorque); -struct ParamsProcessorForceTorquePreint : public ParamsProcessorMotion +struct ParamsProcessorForceTorque : public ParamsProcessorMotion { std::string sensor_ikin_name; std::string sensor_angvel_name; int nbc; // Number of contacts int dimc; // Dimension of the contact: 3D == point feet = 3D force, 6D = humanoid = wrench - ParamsProcessorForceTorquePreint() = default; - ParamsProcessorForceTorquePreint(std::string _unique_name, const ParamsServer& _server): + ParamsProcessorForceTorque() = default; + ParamsProcessorForceTorque(std::string _unique_name, const ParamsServer& _server): ParamsProcessorMotion(_unique_name, _server) { sensor_ikin_name = _server.getParam<std::string>(_unique_name + "/sensor_ikin_name"); @@ -44,7 +44,7 @@ struct ParamsProcessorForceTorquePreint : public ParamsProcessorMotion nbc = _server.getParam<int>(_unique_name + "/nbc_"); dimc = _server.getParam<int>(_unique_name + "/dimc_"); } - ~ParamsProcessorForceTorquePreint() override = default; + ~ParamsProcessorForceTorque() override = default; std::string print() const override { return ParamsProcessorMotion::print() + "\n" @@ -56,16 +56,16 @@ struct ParamsProcessorForceTorquePreint : public ParamsProcessorMotion } }; -WOLF_PTR_TYPEDEFS(ProcessorForceTorquePreint); +WOLF_PTR_TYPEDEFS(ProcessorForceTorque); //class -class ProcessorForceTorquePreint : public ProcessorMotion{ +class ProcessorForceTorque : public ProcessorMotion{ public: - ProcessorForceTorquePreint(ParamsProcessorForceTorquePreintPtr _params_motion_force_torque_preint); - ~ProcessorForceTorquePreint() override; + ProcessorForceTorque(ParamsProcessorForceTorquePtr _params_motion_force_torque); + ~ProcessorForceTorque() override; void configure(SensorBasePtr _sensor) override; - WOLF_PROCESSOR_CREATE(ProcessorForceTorquePreint, ParamsProcessorForceTorquePreint); + WOLF_PROCESSOR_CREATE(ProcessorForceTorque, ParamsProcessorForceTorque); protected: void computeCurrentDelta(const Eigen::VectorXd& _data, @@ -104,12 +104,11 @@ class ProcessorForceTorquePreint : public ProcessorMotion{ const VectorXd& _calib, const VectorXd& _calib_preint, const CaptureBasePtr& _capture_origin) override; - FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_motion) override; - FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion, - CaptureBasePtr _capture_origin) override; + void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override; + private: - ParamsProcessorForceTorquePreintPtr params_motion_force_torque_preint_; + ParamsProcessorForceTorquePtr params_motion_force_torque_; SensorBasePtr sensor_ikin_; SensorBasePtr sensor_angvel_; int nbc_; @@ -124,17 +123,17 @@ class ProcessorForceTorquePreint : public ProcessorMotion{ namespace wolf{ -inline void ProcessorForceTorquePreint::configure(SensorBasePtr _sensor) +inline void ProcessorForceTorque::configure(SensorBasePtr _sensor) { - sensor_ikin_ = _sensor->getProblem()->findSensor(params_motion_force_torque_preint_->sensor_ikin_name); - sensor_angvel_ = _sensor->getProblem()->findSensor(params_motion_force_torque_preint_->sensor_angvel_name); + sensor_ikin_ = _sensor->getProblem()->findSensor(params_motion_force_torque_->sensor_ikin_name); + sensor_angvel_ = _sensor->getProblem()->findSensor(params_motion_force_torque_->sensor_angvel_name); }; -inline Eigen::VectorXd ProcessorForceTorquePreint::deltaZero() const +inline Eigen::VectorXd ProcessorForceTorque::deltaZero() const { return (Eigen::VectorXd(13) << 0,0,0, 0,0,0, 0,0,0, 0,0,0,1 ).finished(); // com, com vel, ang momentum, orientation } } // namespace wolf -#endif // PROCESSOR_FORCE_TORQUE_PREINT_H +#endif // PROCESSOR_FORCE_TORQUE_H diff --git a/include/bodydynamics/processor/processor_force_torque_inertial.h b/include/bodydynamics/processor/processor_force_torque_inertial.h new file mode 100644 index 0000000000000000000000000000000000000000..c51dff8a183d77f33856abd55fc665e44b703024 --- /dev/null +++ b/include/bodydynamics/processor/processor_force_torque_inertial.h @@ -0,0 +1,245 @@ +//--------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-------- +/* + * processor_force_torque_inertial.h + * + * Created on: Aug 19, 2021 + * Author: jsola + */ + +#ifndef PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_H_ +#define PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_H_ + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" + +#include <core/processor/processor_motion.h> + +namespace wolf +{ + +WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorForceTorqueInertial); + +struct ParamsProcessorForceTorqueInertial : public ParamsProcessorMotion +{ + ParamsProcessorForceTorqueInertial() = default; + ParamsProcessorForceTorqueInertial(std::string _unique_name, const ParamsServer& _server) + : ParamsProcessorMotion(_unique_name, _server) + { + n_propellers = _server.getParam<int>(prefix + _unique_name + "/n_propellers"); + } + + std::string print() const override + { + return ParamsProcessorMotion::print() + "\n" // + + "n_propellers: " + std::to_string(n_propellers); // + } + + int n_propellers; +}; + +WOLF_PTR_TYPEDEFS(ProcessorForceTorqueInertial); +class ProcessorForceTorqueInertial : public ProcessorMotion +{ + public: + ProcessorForceTorqueInertial( + ParamsProcessorForceTorqueInertialPtr _params_force_torque_inertial); + ~ProcessorForceTorqueInertial() override; + void configure(SensorBasePtr _sensor) override; + WOLF_PROCESSOR_CREATE(ProcessorForceTorqueInertial, ParamsProcessorForceTorqueInertial); + + protected: + ParamsProcessorForceTorqueInertialPtr params_force_torque_inertial_; + SensorForceTorqueInertialPtr sensor_fti_; + Matrix6d imu_drift_cov_; + + public: + /** \brief convert raw CaptureMotion data to the delta-state format. + * + * This function accepts raw data and time step dt, + * and computes the value of the delta-state and its covariance. Note that these values are + * held by the members delta_ and delta_cov_. + * + * @param _data measured motion data + * @param _data_cov covariance + * @param _dt time step + * @param _delta computed delta + * @param _delta_cov covariance + * @param _calib current state of the calibrated parameters + * @param _jacobian_calib Jacobian of the delta wrt calib + * + * Rationale: + * + * The delta-state format must be compatible for integration using + * the composition functions doing the math in this class: statePlusDelta() and deltaPlusDelta(). + * See the class documentation for some Eigen::VectorXd suggestions. + * + * The data format is generally not the same as the delta format, + * because it is the format of the raw data provided by the Capture, + * which is unaware of the needs of this processor. + * + * Additionally, sometimes the data format is in the form of a + * velocity, or a higher derivative, while the delta is in the form of an increment. + * In such cases, converting from data to delta-state needs integrating + * the data over the period dt. + * + * Two trivial implementations would establish: + * - If `_data` is an increment: + * + * delta_ = _data; + * delta_cov_ = _data_cov + * - If `_data` is a velocity: + * + * delta_ = _data * _dt + * delta_cov_ = _data_cov * _dt. + * + * However, other more complicated relations are possible. + * In general, we'll have a nonlinear + * function relating `delta_` to `_data` and `_dt`, as follows: + * + * delta_ = f ( _data, _dt) + * + * The delta covariance is obtained by classical uncertainty propagation of the data covariance, + * that is, through the Jacobians of `f()`, + * + * delta_cov_ = F_data * _data_cov * F_data.transpose + * + * where `F_data = d_f/d_data` is the Jacobian of `f()`. + */ + void computeCurrentDelta(const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + const Eigen::VectorXd& _calib, + const double _dt, + Eigen::VectorXd& _delta, + Eigen::MatrixXd& _delta_cov, + Eigen::MatrixXd& _jacobian_calib) const override; + + /** \brief composes a delta-state on top of another delta-state + * \param _delta1 the first delta-state + * \param _delta2 the second delta-state + * \param _dt2 the second delta-state's time delta + * \param _delta1_plus_delta2 the delta2 composed on top of delta1. It has the format of delta-state. + * + * This function implements the composition (+) so that _delta1_plus_delta2 = _delta1 (+) _delta2. + */ + void deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2) const override; + + /** \brief composes a delta-state on top of another delta-state, and computes the Jacobians + * \param _delta1 the first delta-state + * \param _delta2 the second delta-state + * \param _dt2 the second delta-state's time delta + * \param _delta1_plus_delta2 the delta2 composed on top of delta1. It has the format of delta-state. + * \param _jacobian1 the jacobian of the composition w.r.t. _delta1. + * \param _jacobian2 the jacobian of the composition w.r.t. _delta2. + * + * This function implements the composition (+) so that _delta1_plus_delta2 = _delta1 (+) _delta2 and its + * jacobians. + */ + void deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2, + Eigen::MatrixXd& _jacobian1, + Eigen::MatrixXd& _jacobian2) const override; + + /** \brief composes a delta-state on top of a state + * \param _x the initial state + * \param _delta the delta-state + * \param _x_plus_delta the updated state. It has the same format as the initial state. + * \param _dt the time interval spanned by the Delta + * + * This function implements the composition (+) so that _x2 = _x1 (+) _delta. + */ + void statePlusDelta(const VectorComposite& _x, + const Eigen::VectorXd& _delta, + const double _dt, + VectorComposite& _x_plus_delta) const override; + + /** \brief Delta zero + * \return a delta state equivalent to the null motion. + * + * Examples (see documentation of the the class for info on Eigen::VectorXd): + * - 2d odometry: a 3-vector with all zeros, e.g. VectorXd::Zero(3) + * - 3d odometry: delta type is a PQ vector: 7-vector with [0,0,0, 0,0,0,1] + * - IMU: PQVBB 10-vector with [0,0,0, 0,0,0,1, 0,0,0] // No biases in the delta !! + */ + Eigen::VectorXd deltaZero() const override; + + /** \brief correct the preintegrated delta + * This produces a delta correction according to the appropriate manifold. + * @param delta_preint : the preintegrated delta to correct + * @param delta_step : an increment in the tangent space of the manifold + * + * We want to do + * + * delta_corr = delta_preint (+) d_step + * + * where the operator (+) is the right-plus operator on the delta's manifold. + * + * Note: usually in motion pre-integration we have + * + * delta_step = Jac_delta_calib * (calib - calib_pre) + * + */ + Eigen::VectorXd correctDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta_step) const override; + + protected: + /** \brief emplace a CaptureMotion + * \param _frame_own frame owning the Capture to create + * \param _sensor Sensor that produced the Capture + * \param _ts time stamp + * \param _data a vector of motion data + * \param _data_cov covariances matrix of the motion data data + * \param _calib calibration vector + * \param _calib_preint calibration vector used during pre-integration + * \param _capture_origin_ptr capture acting as the origin of motions for the MorionBuffer of the created MotionCapture + */ + CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own, + const SensorBasePtr& _sensor, + const TimeStamp& _ts, + const VectorXd& _data, + const MatrixXd& _data_cov, + const VectorXd& _calib, + const VectorXd& _calib_preint, + const CaptureBasePtr& _capture_origin_ptr) override; + + void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override; + + + void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override; + + public: + virtual VectorXd getCalibration(const CaptureBaseConstPtr _capture = nullptr) const override; +}; + +inline Eigen::VectorXd ProcessorForceTorqueInertial::deltaZero() const +{ + return bodydynamics::fti::identity(); +} + +} /* namespace wolf */ + +#endif /* PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_H_ */ diff --git a/include/bodydynamics/processor/processor_force_torque_inertial_dynamics.h b/include/bodydynamics/processor/processor_force_torque_inertial_dynamics.h new file mode 100644 index 0000000000000000000000000000000000000000..dd9eef9ee86e6fd6daab00cdd142fc6713217d59 --- /dev/null +++ b/include/bodydynamics/processor/processor_force_torque_inertial_dynamics.h @@ -0,0 +1,267 @@ +//--------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-------- +/* + * processor_force_torque_inertial_dynamics.h + * + * Created on: Aug 19, 2021 + * Author: jsola + */ + +#ifndef PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_DYNAMICS_H_ +#define PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_DYNAMICS_H_ + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" + +#include <core/processor/processor_motion.h> + +namespace wolf +{ + +WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorForceTorqueInertialDynamics); + +struct ParamsProcessorForceTorqueInertialDynamics : public ParamsProcessorMotion +{ + ParamsProcessorForceTorqueInertialDynamics() = default; + ParamsProcessorForceTorqueInertialDynamics(std::string _unique_name, const ParamsServer& _server) + : ParamsProcessorMotion(_unique_name, _server) + { + // n_propellers = _server.getParam<int>(prefix + _unique_name + "/n_propellers"); + } + + std::string print() const override + { + return ParamsProcessorMotion::print() + "\n"; // + // + "n_propellers: " + std::to_string(n_propellers); // + } + + // int n_propellers; +}; + +WOLF_PTR_TYPEDEFS(ProcessorForceTorqueInertialDynamics); +class ProcessorForceTorqueInertialDynamics : public ProcessorMotion +{ + public: + ProcessorForceTorqueInertialDynamics( + ParamsProcessorForceTorqueInertialDynamicsPtr _params_force_torque_inertial_dynamics_); + ~ProcessorForceTorqueInertialDynamics() override; + void configure(SensorBasePtr _sensor) override; + WOLF_PROCESSOR_CREATE(ProcessorForceTorqueInertialDynamics, + ParamsProcessorForceTorqueInertialDynamics); + + protected: + ParamsProcessorForceTorqueInertialDynamicsPtr params_force_torque_inertial_dynamics_; + SensorForceTorqueInertialPtr sensor_fti_; + Matrix6d imu_drift_cov_; + Matrix3d gyro_noise_cov_; + + public: + /** \brief convert raw CaptureMotion data to the delta-state format. + * + * This function accepts raw data and time step dt, + * and computes the value of the delta-state and its covariance. Note that these values are + * held by the members delta_ and delta_cov_. + * + * @param _data measured motion data + * @param _data_cov covariance + * @param _dt time step + * @param _delta computed delta + * @param _delta_cov covariance + * @param _calib current state of the calibrated parameters + * @param _jacobian_calib Jacobian of the delta wrt calib + * + * Rationale: + * + * The delta-state format must be compatible for integration using + * the composition functions doing the math in this class: statePlusDelta() and deltaPlusDelta(). + * See the class documentation for some Eigen::VectorXd suggestions. + * + * The data format is generally not the same as the delta format, + * because it is the format of the raw data provided by the Capture, + * which is unaware of the needs of this processor. + * + * Additionally, sometimes the data format is in the form of a + * velocity, or a higher derivative, while the delta is in the form of an increment. + * In such cases, converting from data to delta-state needs integrating + * the data over the period dt. + * + * Two trivial implementations would establish: + * - If `_data` is an increment: + * + * delta_ = _data; + * delta_cov_ = _data_cov + * - If `_data` is a velocity: + * + * delta_ = _data * _dt + * delta_cov_ = _data_cov * _dt. + * + * However, other more complicated relations are possible. + * In general, we'll have a nonlinear + * function relating `delta_` to `_data` and `_dt`, as follows: + * + * delta_ = f ( _data, _dt) + * + * The delta covariance is obtained by classical uncertainty propagation of the data covariance, + * that is, through the Jacobians of `f()`, + * + * delta_cov_ = F_data * _data_cov * F_data.transpose + * + * where `F_data = d_f/d_data` is the Jacobian of `f()`. + */ + void computeCurrentDelta(const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + const Eigen::VectorXd& _calib, + const double _dt, + Eigen::VectorXd& _delta, + Eigen::MatrixXd& _delta_cov, + Eigen::MatrixXd& _jacobian_calib) const override; + + /** \brief composes a delta-state on top of another delta-state + * \param _delta1 the first delta-state + * \param _delta2 the second delta-state + * \param _dt2 the second delta-state's time delta + * \param _delta1_plus_delta2 the delta2 composed on top of delta1. It has the format of delta-state. + * + * This function implements the composition (+) so that _delta1_plus_delta2 = _delta1 (+) _delta2. + */ + void deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2) const override; + + /** \brief composes a delta-state on top of another delta-state, and computes the Jacobians + * \param _delta1 the first delta-state + * \param _delta2 the second delta-state + * \param _dt2 the second delta-state's time delta + * \param _delta1_plus_delta2 the delta2 composed on top of delta1. It has the format of delta-state. + * \param _jacobian1 the jacobian of the composition w.r.t. _delta1. + * \param _jacobian2 the jacobian of the composition w.r.t. _delta2. + * + * This function implements the composition (+) so that _delta1_plus_delta2 = _delta1 (+) _delta2 and its + * jacobians. + */ + void deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2, + Eigen::MatrixXd& _jacobian1, + Eigen::MatrixXd& _jacobian2) const override; + + /** \brief composes a delta-state on top of a state + * \param _x the initial state + * \param _delta the delta-state + * \param _x_plus_delta the updated state. It has the same format as the initial state. + * \param _dt the time interval spanned by the Delta + * + * This function implements the composition (+) so that _x2 = _x1 (+) _delta. + */ + void statePlusDelta(const VectorComposite& _x, + const Eigen::VectorXd& _delta, + const double _dt, + VectorComposite& _x_plus_delta) const override; + + /** \brief Delta zero + * \return a delta state equivalent to the null motion. + * + * Examples (see documentation of the the class for info on Eigen::VectorXd): + * - 2d odometry: a 3-vector with all zeros, e.g. VectorXd::Zero(3) + * - 3d odometry: delta type is a PQ vector: 7-vector with [0,0,0, 0,0,0,1] + * - IMU: PQVBB 10-vector with [0,0,0, 0,0,0,1, 0,0,0] // No biases in the delta !! + */ + Eigen::VectorXd deltaZero() const override; + + /** \brief correct the preintegrated delta + * This produces a delta correction according to the appropriate manifold. + * @param delta_preint : the preintegrated delta to correct + * @param delta_step : an increment in the tangent space of the manifold + * + * We want to do + * + * delta_corr = delta_preint (+) d_step + * + * where the operator (+) is the right-plus operator on the delta's manifold. + * + * Note: usually in motion pre-integration we have + * + * delta_step = Jac_delta_calib * (calib - calib_pre) + * + */ + Eigen::VectorXd correctDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta_step) const override; + + public: + virtual VectorXd getCalibration(const CaptureBaseConstPtr _capture = nullptr) const override; + + protected: + /** \brief emplace a CaptureMotion + * \param _frame_own frame owning the Capture to create + * \param _sensor Sensor that produced the Capture + * \param _ts time stamp + * \param _data a vector of motion data + * \param _data_cov covariances matrix of the motion data data + * \param _calib calibration vector + * \param _calib_preint calibration vector used during pre-integration + * \param _capture_origin_ptr capture acting as the origin of motions for the MorionBuffer of the created MotionCapture + */ + CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own, + const SensorBasePtr& _sensor, + const TimeStamp& _ts, + const VectorXd& _data, + const MatrixXd& _data_cov, + const VectorXd& _calib, + const VectorXd& _calib_preint, + const CaptureBasePtr& _capture_origin_ptr) override; + + /** \brief Emplace features and factors + * + * This processor emplaces tree possible factors (with its features): + * - A Force-torque-inertial pre-integrated factor -- the motion factor + * - An Angular-momentum factor -- links angular momentum with angular velocity and inertia + * - An IMU bias drift factor -- this one only if the IMU biases are dynamic + * + * \param _capture_origin: origin of the integrated delta + * \param _capture_own: final of the integrated delta + */ + void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override; + + void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override; + + bool voteForKeyFrame() const override; + + private: + void data2tangent(const Eigen::VectorXd& _data, ///< [a, w, f, t] raw measures + const Eigen::VectorXd& _bias, ///< [ab, wb] + const Eigen::VectorXd& _model, ///< [com, inertia, mass] + Eigen::VectorXd& _tangent, ///< [a, w, f, t] calibrated + Eigen::MatrixXd& _J_tangent_data, + Eigen::MatrixXd& _J_tangent_bias, + Eigen::MatrixXd& _J_tangent_model) const; +}; + +inline Eigen::VectorXd ProcessorForceTorqueInertialDynamics::deltaZero() const +{ + return bodydynamics::fti::identity(); +} + +} /* namespace wolf */ + +#endif /* PROCESSOR_PROCESSOR_FORCE_TORQUE_INERTIAL_DYNAMICS_H_ */ diff --git a/include/bodydynamics/sensor/sensor_force_torque_inertial.h b/include/bodydynamics/sensor/sensor_force_torque_inertial.h new file mode 100644 index 0000000000000000000000000000000000000000..2d509f80415ce98f27d84ee11440086f0fc83595 --- /dev/null +++ b/include/bodydynamics/sensor/sensor_force_torque_inertial.h @@ -0,0 +1,142 @@ +//--------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-------- +#ifndef SENSOR_FORCE_TORQUE_INERTIAL_H +#define SENSOR_FORCE_TORQUE_INERTIAL_H + +// wolf includes +#include "core/sensor/sensor_base.h" +#include "core/utils/params_server.h" +#include <iostream> + +namespace wolf +{ + +WOLF_STRUCT_PTR_TYPEDEFS(ParamsSensorForceTorqueInertial); + +struct ParamsSensorForceTorqueInertial : public ParamsSensorBase +{ + // noise std dev + double acc_noise_std; + double gyro_noise_std; + double acc_drift_std; + double gyro_drift_std; + double force_noise_std; + double torque_noise_std; + Vector3d com; + Vector3d inertia; + double mass; + + ParamsSensorForceTorqueInertial() = default; + ParamsSensorForceTorqueInertial(std::string _unique_name, const ParamsServer& _server) + : ParamsSensorBase(_unique_name, _server) + { + acc_noise_std = _server.getParam<double>(prefix + _unique_name + "/acc_noise_std"); + gyro_noise_std = _server.getParam<double>(prefix + _unique_name + "/gyro_noise_std"); + acc_drift_std = _server.getParam<double>(prefix + _unique_name + "/acc_drift_std"); + gyro_drift_std = _server.getParam<double>(prefix + _unique_name + "/gyro_drift_std"); + force_noise_std = _server.getParam<double>(prefix + _unique_name + "/force_noise_std"); + torque_noise_std = _server.getParam<double>(prefix + _unique_name + "/torque_noise_std"); + com = _server.getParam<Vector3d>(prefix + _unique_name + "/com"); + inertia = _server.getParam<Vector3d>(prefix + _unique_name + "/inertia"); + mass = _server.getParam<double>(prefix + _unique_name + "/mass"); + } + ~ParamsSensorForceTorqueInertial() override = default; + std::string print() const override + { + return ParamsSensorBase::print() + "\n" // + + "acc_noise_std: " + std::to_string(acc_noise_std) + "\n" // + + "gyro_noise_std: " + std::to_string(gyro_noise_std) + "\n" // + + "acc_drift_std: " + std::to_string(acc_drift_std) + "\n" // + + "gyro_drift_std: " + std::to_string(gyro_drift_std) + "\n" // + + "force_noise_std: " + std::to_string(force_noise_std) + "\n" // + + "torque_noise_std: " + std::to_string(torque_noise_std) + "\n" // + + "com: print not implemented." + "\n" // + + "inertia: print not implemented. \n" // + + "mass: " + std::to_string(mass) + "\n"; // + } +}; + +WOLF_PTR_TYPEDEFS(SensorForceTorqueInertial); + +class SensorForceTorqueInertial : public SensorBase +{ + protected: + ParamsSensorForceTorqueInertialPtr params_fti_; + + public: + SensorForceTorqueInertial(const VectorComposite& _states, ParamsSensorForceTorqueInertialPtr _params); + SensorForceTorqueInertial(const VectorXd& _extrinsics, ParamsSensorForceTorqueInertialPtr _params); + ~SensorForceTorqueInertial() override = default; + + WOLF_SENSOR_CREATE(SensorForceTorqueInertial, ParamsSensorForceTorqueInertial, 7); + + // getters return by copy + Vector6d getImuBias() const; // Imu bias [acc, gyro] + Vector3d getAccBias() const; // Acc bias + Vector3d getGyroBias() const; // Gyro bias + double getMass() const; // Total mass + Vector3d getInertia() const; // Inertia vector (diagonal of inertia matrix) + Vector3d getCom() const; // centre of mass + Vector7d getModel() const; // dynamic model [com, inertia, mass] + ParamsSensorForceTorqueInertialPtr getParamsSensorForceTorqueInertial(){return params_fti_;} +}; + +inline double SensorForceTorqueInertial::getMass() const +{ + return getStateBlock('m')->getState()(0); +} + +inline Vector6d SensorForceTorqueInertial::getImuBias() const +{ + return getStateBlock('I')->getState(); +} + +inline Vector3d SensorForceTorqueInertial::getAccBias() const +{ + return getStateBlock('I')->getState().head<3>(); +} + +inline Vector3d SensorForceTorqueInertial::getGyroBias() const +{ + return getStateBlock('I')->getState().tail<3>(); +} + +inline Vector3d SensorForceTorqueInertial::getInertia() const +{ + return getStateBlock('i')->getState(); +} + +inline Vector3d SensorForceTorqueInertial::getCom() const +{ + return getStateBlock('C')->getState(); +} + +inline Vector7d SensorForceTorqueInertial::getModel() const +{ + Vector7d model; + model << getCom(), getInertia(), getMass(); + return model; +} + +} // namespace wolf + +#endif // SENSOR_FORCE_TORQUE_H diff --git a/src/capture/capture_force_torque_preint.cpp b/src/capture/capture_force_torque.cpp similarity index 87% rename from src/capture/capture_force_torque_preint.cpp rename to src/capture/capture_force_torque.cpp index 83e145e4b85a4895a81dce2251fcddd10681460d..3b09bdda9e821497e4e7cc77300a8e06cb656e71 100644 --- a/src/capture/capture_force_torque_preint.cpp +++ b/src/capture/capture_force_torque.cpp @@ -20,13 +20,13 @@ // //--------LICENSE_END-------- #include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" +#include "bodydynamics/capture/capture_force_torque.h" #include "bodydynamics/sensor/sensor_force_torque.h" #include "core/state_block/state_quaternion.h" namespace wolf { -CaptureForceTorquePreint::CaptureForceTorquePreint( +CaptureForceTorque::CaptureForceTorque( const TimeStamp& _init_ts, SensorBasePtr _sensor_ptr, CaptureInertialKinematicsPtr _capture_IK_ptr, // to get the pbc bias @@ -34,14 +34,14 @@ CaptureForceTorquePreint::CaptureForceTorquePreint( const Eigen::VectorXd& _data, const Eigen::MatrixXd& _data_cov, // TODO: no uncertainty from kinematics CaptureBasePtr _capture_origin_ptr) : - CaptureMotion("CaptureForceTorquePreint", _init_ts, _sensor_ptr, _data, _data_cov, + CaptureMotion("CaptureForceTorque", _init_ts, _sensor_ptr, _data, _data_cov, _capture_origin_ptr, nullptr, nullptr, nullptr), cap_ikin_other_(_capture_IK_ptr), cap_gyro_other_(_capture_motion_ptr) { } -CaptureForceTorquePreint::~CaptureForceTorquePreint() +CaptureForceTorque::~CaptureForceTorque() { } diff --git a/src/capture/capture_force_torque_inertial.cpp b/src/capture/capture_force_torque_inertial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85a7f3603e24aaa04de9bfaafa5b33f32aa58da4 --- /dev/null +++ b/src/capture/capture_force_torque_inertial.cpp @@ -0,0 +1,50 @@ +//--------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 "bodydynamics/capture/capture_force_torque_inertial.h" + +#include <core/state_block/state_block_derived.h> + +namespace wolf +{ +CaptureForceTorqueInertial::CaptureForceTorqueInertial(const TimeStamp& _init_ts, + SensorBasePtr _sensor_ptr, + const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + CaptureBasePtr _capture_origin_ptr) + : CaptureMotion("CaptureForceTorqueInertial", + _init_ts, + _sensor_ptr, + _data, + _data_cov, + _capture_origin_ptr, + nullptr, // position is static + nullptr, // orientation is static + (_sensor_ptr->isStateBlockDynamic('I') // dynamic intrinsics are IMU bias + ? std::make_shared<StateParams6>(_sensor_ptr->getStateBlockDynamic('I')->getState(), false) + : nullptr)) +{ + // +} + +CaptureForceTorqueInertial::~CaptureForceTorqueInertial() {} + +} // namespace wolf \ No newline at end of file diff --git a/src/feature/feature_force_torque.cpp b/src/feature/feature_force_torque.cpp index 83c12548fd923e7621b56465ecedbcc12b673500..04eacfa7e8c4c02944bb2fd6a151f05551030c47 100644 --- a/src/feature/feature_force_torque.cpp +++ b/src/feature/feature_force_torque.cpp @@ -20,35 +20,18 @@ // //--------LICENSE_END-------- #include "bodydynamics/feature/feature_force_torque.h" +namespace wolf +{ -namespace wolf { - -WOLF_PTR_TYPEDEFS(FeatureForceTorque); +FeatureForceTorque::FeatureForceTorque(const Eigen::VectorXd& _delta_preintegrated, + const Eigen::MatrixXd& _delta_preintegrated_covariance, + const Eigen::Vector6d& _biases_preint, + const Eigen::Matrix<double, 12, 6>& _J_delta_biases) + : FeatureBase("FeatureForceTorque", _delta_preintegrated, _delta_preintegrated_covariance), + pbc_bias_preint_(_biases_preint.head<3>()), + gyro_bias_preint_(_biases_preint.tail<3>()), + J_delta_bias_(_J_delta_biases) +{ +} -FeatureForceTorque::FeatureForceTorque( - const double& _dt, - const double& _mass, - const Eigen::Vector6d& _forces_meas, - const Eigen::Vector6d& _torques_meas, - const Eigen::Vector3d& _pbc_meas, - const Eigen::Matrix<double,14,1>& _kin_meas, - const Eigen::Matrix6d& _cov_forces_meas, - const Eigen::Matrix6d& _cov_torques_meas, - const Eigen::Matrix3d& _cov_pbc_meas, - const Eigen::Matrix<double,14,14>& _cov_kin_meas) : - FeatureBase("FORCETORQUE", 42*Eigen::Matrix1d::Identity(), 42*Eigen::Matrix1d::Identity(), UNCERTAINTY_IS_COVARIANCE), // Pass dummmy values -> we are bypassing the way meas and cov is usually stored because too many of them == vector is too big -> error prone - dt_(_dt), - mass_(_mass), - forces_meas_(_forces_meas), - torques_meas_(_torques_meas), - pbc_meas_(_pbc_meas), - kin_meas_(_kin_meas), - cov_forces_meas_(_cov_forces_meas), - cov_torques_meas_(_cov_torques_meas), - cov_pbc_meas_(_cov_pbc_meas), - cov_kin_meas_(_cov_kin_meas) -{} - -FeatureForceTorque::~FeatureForceTorque(){} - -} // namespace wolf +} // namespace wolf diff --git a/src/processor/processor_force_torque.cpp b/src/processor/processor_force_torque.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd7f5806463b14bbe56ee458d542048675879b7a --- /dev/null +++ b/src/processor/processor_force_torque.cpp @@ -0,0 +1,300 @@ +//--------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-------- +// wolf +#include "bodydynamics/math/force_torque_delta_tools.h" +#include "bodydynamics/capture/capture_force_torque.h" +#include "bodydynamics/feature/feature_force_torque.h" +#include "bodydynamics/processor/processor_force_torque.h" +#include "bodydynamics/factor/factor_force_torque.h" + +namespace wolf +{ + +int compute_data_size(int nbc, int dimc) +{ + // compute the size of the data/body vectors used by processorMotion API depending + // on the number of contacts (nbc) and contact dimension (dimc) + // result: forces (+torques for 6D contacts) + pbc + wb + contact/base positions + contact/base orientations + return nbc * dimc + 3 + 3 + nbc * 3 + nbc * 4; +} + +using namespace Eigen; + +ProcessorForceTorque::ProcessorForceTorque(ParamsProcessorForceTorquePtr _params_motion_force_torque) + : ProcessorMotion("ProcessorForceTorque", + "CDLO", + 3, + 13, + 13, + 12, + compute_data_size(_params_motion_force_torque->nbc, _params_motion_force_torque->dimc), + 6, + _params_motion_force_torque), + params_motion_force_torque_(std::make_shared<ParamsProcessorForceTorque>(*_params_motion_force_torque)), + nbc_(_params_motion_force_torque->nbc), + dimc_(_params_motion_force_torque->dimc) + +{ + // +} + +ProcessorForceTorque::~ProcessorForceTorque() +{ + // +} + +bool ProcessorForceTorque::voteForKeyFrame() const +{ + // time span + if (getBuffer().back().ts_ - getBuffer().front().ts_ > params_motion_force_torque_->max_time_span) + { + WOLF_DEBUG("PM: vote: time span"); + return true; + } + // buffer length + if (getBuffer().size() > params_motion_force_torque_->max_buff_length) + { + WOLF_DEBUG("PM: vote: buffer length"); + return true; + } + + // Some other measure of movement? + + // WOLF_DEBUG( "PM: do not vote" ); + return false; +} + +CaptureMotionPtr ProcessorForceTorque::emplaceCapture(const FrameBasePtr& _frame_own, + const SensorBasePtr& _sensor, + const TimeStamp& _ts, + const VectorXd& _data, + const MatrixXd& _data_cov, + const VectorXd& _calib, + const VectorXd& _calib_preint, + const CaptureBasePtr& _capture_origin) +{ + // Here we have to retrieve the origin capture no + // !! PROBLEM: + // when doing setOrigin, emplaceCapture is called 2 times + // - first on the first KF (usually created by setPrior), this one contains the biases + // - secondly on the inner frame (last) which does not contains other captures + auto capture_ikin = _frame_own->getCaptureOf(sensor_ikin_); + auto capture_angvel = _frame_own->getCaptureOf(sensor_angvel_); + if ((capture_ikin == nullptr) || (capture_angvel == nullptr)) + { + // We have to retrieve the bias from a former Keyframe: origin + capture_ikin = origin_ptr_->getFrame()->getCaptureOf(sensor_ikin_); + capture_angvel = origin_ptr_->getFrame()->getCaptureOf(sensor_angvel_); + } + auto cap = + CaptureBase::emplace<CaptureForceTorque>(_frame_own, + _ts, + _sensor, + std::static_pointer_cast<CaptureInertialKinematics>(capture_ikin), + std::static_pointer_cast<CaptureMotion>(capture_angvel), + _data, + _data_cov, + _capture_origin); + + // Cannot be recovered from the _calib and _calib_preint which are initialized using calib_size_ + // which is zero in our case since the calibration stateblocks are not actually in the FTPreint sensor/captures + + auto cap_ft = std::static_pointer_cast<CaptureForceTorque>(cap); + Vector6d calib = getCalibration(cap_ft); + setCalibration(cap_ft, calib); + cap_ft->setCalibrationPreint(calib); + + return cap; +} + +// FeatureBasePtr ProcessorForceTorque::emplaceFeature(CaptureMotionPtr _capture_motion) +// { +// // Retrieving the origin capture and getting the bias from here should be done here? +// auto feature = FeatureBase::emplace<FeatureForceTorque>( +// _capture_motion, +// _capture_motion->getBuffer().back().delta_integr_, +// _capture_motion->getBuffer().back().delta_integr_cov_ + unmeasured_perturbation_cov_, +// _capture_motion->getCalibrationPreint(), +// _capture_motion->getBuffer().back().jacobian_calib_); +// return feature; +// } + +Eigen::VectorXd ProcessorForceTorque::correctDelta(const Eigen::VectorXd& delta_preint, + const Eigen::VectorXd& delta_step) const +{ + return bodydynamics::plus(delta_preint, delta_step); +} + +VectorXd ProcessorForceTorque::getCalibration(const CaptureBaseConstPtr _capture) const +{ + VectorXd bias_vec(6); + + if (_capture) // access from capture is quicker + { + auto cap_ft(std::static_pointer_cast<const CaptureForceTorque>(_capture)); + + // get calib part from Ikin capture + bias_vec.segment<3>(0) = cap_ft->getIkinCaptureOther()->getSensorIntrinsic()->getState(); + + // get calib part from IMU capture + bias_vec.segment<3>(3) = + cap_ft->getGyroCaptureOther() + ->getSensorIntrinsic() + ->getState() + .tail<3>(); // TODO: only valib for IMU bias sb or any other of the structure [XXXX, gyro_bias] + } + else // access from sensor is slower + { + // get calib part from Ikin capture + bias_vec.segment<3>(0) = sensor_ikin_->getIntrinsic()->getState(); + + // get calib part from IMU capture + bias_vec.segment<3>(3) = + sensor_angvel_->getIntrinsic() + ->getState() + .tail<3>(); // TODO: only valib for IMU bias sb or any other of the structure [XXXX, gyro_bias] + } + return bias_vec; +} + +void ProcessorForceTorque::setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) +{ + CaptureForceTorquePtr cap_ft(std::static_pointer_cast<CaptureForceTorque>(_capture)); + + // set calib part in Ikin capture + cap_ft->getIkinCaptureOther()->getSensorIntrinsic()->setState(_calibration.head<3>()); + + // set calib part in IMU capture + Vector6d calib_imu = cap_ft->getGyroCaptureOther()->getSensorIntrinsic()->getState(); + calib_imu.tail<3>() = _calibration.tail<3>(); + + cap_ft->getGyroCaptureOther()->getSensorIntrinsic()->setState(calib_imu); +} + +void ProcessorForceTorque::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) +{ + // Retrieving the origin capture and getting the bias from here should be done here? + auto feature = FeatureBase::emplace<FeatureForceTorque>( + _capture_own, + _capture_own->getBuffer().back().delta_integr_, + _capture_own->getBuffer().back().delta_integr_cov_ + unmeasured_perturbation_cov_, + _capture_own->getCalibrationPreint(), + _capture_own->getBuffer().back().jacobian_calib_); + + CaptureForceTorquePtr cap_ft_origin = std::static_pointer_cast<CaptureForceTorque>(_capture_origin); + FeatureForceTorquePtr ftr_ft = std::static_pointer_cast<FeatureForceTorque>(feature); + CaptureForceTorquePtr cap_ft_new = std::static_pointer_cast<CaptureForceTorque>(ftr_ft->getCapture()); + + FactorBase::emplace<FactorForceTorque>(ftr_ft, + ftr_ft, + cap_ft_origin, + shared_from_this(), + cap_ft_origin->getIkinCaptureOther(), // to retrieve sb_bp1 + cap_ft_origin->getGyroCaptureOther(), // to retrieve sb_w1 + false); +} + +void ProcessorForceTorque::computeCurrentDelta(const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + const Eigen::VectorXd& _calib, + const double _dt, + Eigen::VectorXd& _delta, + Eigen::MatrixXd& _delta_cov, + Eigen::MatrixXd& _jac_delta_calib) const +{ + assert(_data.size() == data_size_ && "Wrong data size!"); + + // create delta + MatrixXd jac_body_bias(data_size_ - nbc_, 6); + VectorXd body(data_size_); + bodydynamics::debiasData(_data, _calib, nbc_, dimc_, body, jac_body_bias); + + MatrixXd jac_delta_body(12, data_size_ - nbc_); + bodydynamics::body2delta(body, + _dt, + std::static_pointer_cast<const SensorForceTorque>(getSensor())->getMass(), + nbc_, + dimc_, + _delta, + jac_delta_body); // Jacobians tested in bodydynamics_tools + + // [f], [tau], pbc, wb + MatrixXd jac_delta_body_reduced = jac_delta_body.leftCols(nbc_ * dimc_ + 6); + + // compute delta_cov (using uncertainty on f,tau,pbc) + _delta_cov = jac_delta_body_reduced * _data_cov * + jac_delta_body_reduced.transpose(); // trivially: jac_body_delta = Identity + // _delta_cov = jac_delta_body * _data_cov * jac_delta_body.transpose(); // trivially: jac_body_delta = Identity + + // compute jacobian_calib + _jac_delta_calib = jac_delta_body * jac_body_bias; +} + +void ProcessorForceTorque::deltaPlusDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta, + const double _dt, + Eigen::VectorXd& _delta_preint_plus_delta) const +{ + _delta_preint_plus_delta = bodydynamics::compose(_delta_preint, _delta, _dt); +} + +void ProcessorForceTorque::statePlusDelta(const VectorComposite& _x, + const Eigen::VectorXd& _delta, + const double _dt, + VectorComposite& _x_plus_delta) const +{ + assert(_delta.size() == 13 && "Wrong _delta vector size"); + assert(_dt >= 0 && "Time interval _dt is negative!"); + + // verbose way : create Eigen vectors, then compute, then convert back to Composite + + auto x = _x.vector("CDLO"); + + VectorXd x_plus_delta = bodydynamics::composeOverState(x, _delta, _dt); + + _x_plus_delta = VectorComposite(x_plus_delta, "CDLO", {3, 3, 3, 4}); +} + +void ProcessorForceTorque::deltaPlusDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta, + const double _dt, + Eigen::VectorXd& _delta_preint_plus_delta, + Eigen::MatrixXd& _jacobian_delta_preint, + Eigen::MatrixXd& _jacobian_delta) const +{ + bodydynamics::compose(_delta_preint, + _delta, + _dt, + _delta_preint_plus_delta, + _jacobian_delta_preint, + _jacobian_delta); // jacobians tested in bodydynamics_tools +} + +} // namespace wolf + +// Register in the FactorySensor +#include "core/processor/factory_processor.h" + +namespace wolf +{ +WOLF_REGISTER_PROCESSOR(ProcessorForceTorque) +} diff --git a/src/processor/processor_force_torque_inertial.cpp b/src/processor/processor_force_torque_inertial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc3256c2674eb1205a59c98ece711c9fb2f734c8 --- /dev/null +++ b/src/processor/processor_force_torque_inertial.cpp @@ -0,0 +1,225 @@ +//--------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-------- +/* + * processor_force_torque_inertial.cpp + * + * Created on: Aug 19, 2021 + * Author: jsola + */ + +// bodydynamics +#include "bodydynamics/processor/processor_force_torque_inertial.h" +#include "bodydynamics/factor/factor_force_torque_inertial.h" +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" + +// wolf +#include <core/state_block/state_composite.h> +#include <core/capture/capture_motion.h> +#include <core/factor/factor_block_difference.h> + +namespace wolf +{ + +ProcessorForceTorqueInertial::ProcessorForceTorqueInertial( + ParamsProcessorForceTorqueInertialPtr _params_force_torque_inertial) + : ProcessorMotion("ProcessorForceTorqueInertial", + "POVL", + 3, // dim + 13, // state size [p, q, v, L] + 19, // delta size [pi, vi, pd, vd, L, q] + 18, // delta cov size + 12, // data size [a, w, f, t] + 6, // calib size [ab, wb] + _params_force_torque_inertial), + params_force_torque_inertial_(_params_force_torque_inertial) +{ + // TODO Auto-generated constructor stub +} + +ProcessorForceTorqueInertial::~ProcessorForceTorqueInertial() +{ + // TODO Auto-generated destructor stub +} + +CaptureMotionPtr ProcessorForceTorqueInertial::emplaceCapture(const FrameBasePtr& _frame_own, + const SensorBasePtr& _sensor, + const TimeStamp& _ts, + const VectorXd& _data, + const MatrixXd& _data_cov, + const VectorXd& _calib, + const VectorXd& _calib_preint, + const CaptureBasePtr& _capture_origin_ptr) +{ + CaptureMotionPtr capture = CaptureBase::emplace<CaptureMotion>( + _frame_own, "CaptureForceTorqueInertial", _ts, _sensor, _data, _data_cov, _capture_origin_ptr); + setCalibration(capture, _calib); + capture->setCalibrationPreint(_calib_preint); + return capture; +} + +void ProcessorForceTorqueInertial::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, + CaptureMotionPtr _capture_own) +{ + // 1. Feature and factor FTI -- force-torque-inertial + //---------------------------------------------------- + auto motion = _capture_own->getBuffer().back(); + auto ftr_fti = FeatureBase::emplace<FeatureMotion>(_capture_own, + "FeatureMotion", + motion.delta_integr_, + motion.delta_integr_cov_ + unmeasured_perturbation_cov_, + _capture_own->getCalibrationPreint(), + motion.jacobian_calib_); + + FactorBase::emplace<FactorForceTorqueInertial>( + ftr_fti, ftr_fti, _capture_origin, shared_from_this(), params_->apply_loss_function); + + + // 2. Feature and factor bias -- IMU bias drift for acc and gyro + //--------------------------------------------------------------- + // - This factor only if IMU biases are Dynamic (check sensor->isStateBlockDynamic('I')) + // - feature has zero measurement size 6, with the cov of the bias drift size 6x6 + // - factor relates bias(last capture) and bias(origin capture) + if (getSensor()->isStateBlockDynamic('I')) + { + const auto& sb_imubias_own = _capture_own->getStateBlock('I'); + const auto& sb_imubias_origin = _capture_origin->getStateBlock('I'); + if (sb_imubias_own != sb_imubias_origin) // make sure it's two different state blocks! -- just in case + { + auto dt = _capture_own->getTimeStamp() - _capture_origin->getTimeStamp(); + auto ftr_bias = + FeatureBase::emplace<FeatureBase>(_capture_own, + "FeatureBase", + Vector6d::Zero(), // mean IMU drift is zero + imu_drift_cov_ * dt); // IMU drift cov specified in continuous time + FactorBase::emplace<FactorBlockDifference>(ftr_bias, + ftr_bias, + sb_imubias_own, // IMU bias block at t=own + sb_imubias_origin, // IMU bias block at t=origin + nullptr, // frame other + _capture_origin, // origin capture + nullptr, // feature other + nullptr, // landmark other + 0, // take all of first state block + -1, // take all of first state block + 0, // take all of first second block + -1, // take all of first second block + shared_from_this(), // this processor + false); // loss function + } + } +} + + +void ProcessorForceTorqueInertial::setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) +{ + _capture->getStateBlock('I')->setState(_calibration); // Set IMU bias +} + +void ProcessorForceTorqueInertial::configure(SensorBasePtr _sensor) +{ + sensor_fti_ = std::static_pointer_cast<SensorForceTorqueInertial>(_sensor); + + + auto acc_drift_std = sensor_fti_->getParamsSensorForceTorqueInertial()->acc_drift_std; + auto gyro_drift_std = sensor_fti_->getParamsSensorForceTorqueInertial()->gyro_drift_std; + + ArrayXd imu_drift_sigmas(6); + imu_drift_sigmas << acc_drift_std, acc_drift_std, acc_drift_std, gyro_drift_std, gyro_drift_std, gyro_drift_std; + imu_drift_cov_ = imu_drift_sigmas.square().matrix().asDiagonal(); +} + +void ProcessorForceTorqueInertial::computeCurrentDelta(const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + const Eigen::VectorXd& _calib, + const double _dt, + Eigen::VectorXd& _delta, + Eigen::MatrixXd& _delta_cov, + Eigen::MatrixXd& _jacobian_calib) const +{ + // compute tangent by removing IMU bias + Matrix<double, 12, 1> tangent = _data; + tangent.head<6>() -= _calib; // J_tangent_data = Identity(12,12) + Matrix<double, 12, 6> J_tangent_calib; // J_tangent_calib = [-Identity(6,6) ; Zero(6,6)] + J_tangent_calib.topRows<6>() = -Matrix6d::Identity(); + J_tangent_calib.bottomRows<6>() = Matrix6d::Zero(); + + // go from tangent to delta. We need to dynamic model for this + const Matrix<double, 7, 1>& model = sensor_fti_->getModel(); + Matrix<double, 18, 12> J_delta_tangent; + Matrix<double, 18, 7> J_delta_model; + fti::tangent2delta(tangent, model, _dt, _delta, J_delta_tangent, J_delta_model); + + // Compute cov and compose jacobians + const auto& J_delta_data = J_delta_tangent; // * J_tangent_data, which is the Identity; + _delta_cov = J_delta_data * _data_cov * J_delta_data.transpose(); + _jacobian_calib = J_delta_tangent * J_tangent_calib; +} + +void ProcessorForceTorqueInertial::deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2) const +{ + bodydynamics::fti::compose(_delta1, _delta2, _dt2, _delta1_plus_delta2); +} + +void ProcessorForceTorqueInertial::deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2, + Eigen::MatrixXd& _jacobian1, + Eigen::MatrixXd& _jacobian2) const +{ + bodydynamics::fti::compose(_delta1, _delta2, _dt2, _delta1_plus_delta2, _jacobian1, _jacobian2); +} + +void ProcessorForceTorqueInertial::statePlusDelta(const VectorComposite& _x, + const Eigen::VectorXd& _delta, + const double _dt, + VectorComposite& _x_plus_delta) const +{ + auto x = _x.vector("PVLO"); + VectorXd x_plus_delta = bodydynamics::fti::composeOverState(x, _delta, _dt); + _x_plus_delta = VectorComposite(x_plus_delta, "PVLO", {3, 3, 3, 4}); +} + +Eigen::VectorXd ProcessorForceTorqueInertial::correctDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta_step) const +{ + return fti::plus(_delta_preint, _delta_step); +} + +VectorXd ProcessorForceTorqueInertial::getCalibration(const CaptureBaseConstPtr _capture) const +{ + if (_capture) + return _capture->getStateBlock('I')->getState(); // IMU bias + else + return getSensor()->getStateBlock('I')->getState(); // IMU bias +} +} /* namespace wolf */ + +#include <core/processor/factory_processor.h> +namespace wolf +{ +WOLF_REGISTER_PROCESSOR(ProcessorForceTorqueInertial); +WOLF_REGISTER_PROCESSOR_AUTO(ProcessorForceTorqueInertial); +} // namespace wolf \ No newline at end of file diff --git a/src/processor/processor_force_torque_inertial_dynamics.cpp b/src/processor/processor_force_torque_inertial_dynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..678074d6f632f70b47712da7ffcf056fa8a64146 --- /dev/null +++ b/src/processor/processor_force_torque_inertial_dynamics.cpp @@ -0,0 +1,503 @@ +//--------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-------- +/* + * processor_force_torque_inertial_dynamics.cpp + * + * Created on: Aug 1, 2022 + * Author: asanjuan + */ + +// bodydynamics +#include "bodydynamics/processor/processor_force_torque_inertial_dynamics.h" +#include "bodydynamics/factor/factor_force_torque_inertial_dynamics.h" +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include "bodydynamics/capture/capture_force_torque_inertial.h" + +// wolf +#include <core/state_block/state_composite.h> +#include <core/capture/capture_motion.h> +#include <core/factor/factor_block_difference.h> + +namespace wolf +{ + +ProcessorForceTorqueInertialDynamics::ProcessorForceTorqueInertialDynamics( + ParamsProcessorForceTorqueInertialDynamicsPtr _params_force_torque_inertial_dynamics) + : ProcessorMotion("ProcessorForceTorqueInertialDynamics", + "POVL", + 3, // dim + 13, // state size [p, q, v, L] + 19, // delta size [pi, vi, pd, vd, L, q] + 18, // delta cov size + 12, // data size [a, w, f, t] + 13, // calib size [ab, wb, c, i, m] + _params_force_torque_inertial_dynamics), + params_force_torque_inertial_dynamics_(_params_force_torque_inertial_dynamics) +{ + // +} + +ProcessorForceTorqueInertialDynamics::~ProcessorForceTorqueInertialDynamics() +{ + // +} + +CaptureMotionPtr ProcessorForceTorqueInertialDynamics::emplaceCapture(const FrameBasePtr& _frame_own, + const SensorBasePtr& _sensor, + const TimeStamp& _ts, + const VectorXd& _data, + const MatrixXd& _data_cov, + const VectorXd& _calib, + const VectorXd& _calib_preint, + const CaptureBasePtr& _capture_origin_ptr) +{ + CaptureMotionPtr capture = CaptureBase::emplace<CaptureForceTorqueInertial>( + _frame_own, _ts, _sensor, _data, _data_cov, _capture_origin_ptr); + setCalibration(capture, _calib); + capture->setCalibrationPreint(_calib_preint); + return capture; +} + +void ProcessorForceTorqueInertialDynamics::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, + CaptureMotionPtr _capture_own) +{ + // 1. Feature and factor FTI -- force-torque-inertial + //---------------------------------------------------- + auto motion = _capture_own->getBuffer().back(); + auto ftr_fti = FeatureBase::emplace<FeatureMotion>(_capture_own, + "FeatureMotion", + motion.delta_integr_, + motion.delta_integr_cov_ + unmeasured_perturbation_cov_, + _capture_own->getCalibrationPreint(), + motion.jacobian_calib_); + + FactorBase::emplace<FactorForceTorqueInertialDynamics>( + ftr_fti, ftr_fti, _capture_origin, shared_from_this(), params_->apply_loss_function); + + // 2. Feature and factor L -- angular momentum + //--------------------------------------------- + // - feature has the current gyro measurement + // - factor relates w_measured, IMU_bias(last capture), L(last capture) and i(sensor) + + // auto measurement_gyro = motion.data_.segment<3>(3); + // auto meas_cov = sensor_fti_->getNoiseCov().block<3,3>(3,3); // ?? + + // 3. Feature and factor bias -- IMU bias drift for acc and gyro + //--------------------------------------------------------------- + // - This factor only if IMU biases are Dynamic (check sensor->isStateBlockDynamic('I')) + // - feature has zero measurement size 6, with the cov of the bias drift size 6x6 + // - factor relates bias(last capture) and bias(origin capture) + if (getSensor()->isStateBlockDynamic('I')) + { + const auto& sb_imubias_own = _capture_own->getStateBlock('I'); + const auto& sb_imubias_origin = _capture_origin->getStateBlock('I'); + if (sb_imubias_own != sb_imubias_origin) // make sure it's two different state blocks! -- just in case + { + auto dt = _capture_own->getTimeStamp() - _capture_origin->getTimeStamp(); + auto ftr_bias = + FeatureBase::emplace<FeatureBase>(_capture_own, + "FeatureBase", + Vector6d::Zero(), // mean IMU drift is zero + imu_drift_cov_ * dt); // IMU drift cov specified in continuous time + FactorBase::emplace<FactorBlockDifference>(ftr_bias, + ftr_bias, + sb_imubias_own, // IMU bias block at t=own + sb_imubias_origin, // IMU bias block at t=origin + nullptr, // frame other + _capture_origin, // origin capture + nullptr, // feature other + nullptr, // landmark other + 0, // take all of first state block + -1, // take all of first state block + 0, // take all of first second block + -1, // take all of first second block + shared_from_this(), // this processor + false); // loss function + } + } +} + +void ProcessorForceTorqueInertialDynamics::setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) +{ + _capture->getStateBlock('I')->setState(_calibration.segment<6>(0)); // Set IMU bias + _capture->getStateBlock('C')->setState(_calibration.segment<3>(6)); // Set C + _capture->getStateBlock('i')->setState(_calibration.segment<3>(9)); // Set i + _capture->getStateBlock('m')->setState(_calibration.segment<1>(12)); // Set m +} + +void ProcessorForceTorqueInertialDynamics::configure(SensorBasePtr _sensor) +{ + sensor_fti_ = std::static_pointer_cast<SensorForceTorqueInertial>(_sensor); + + auto gyro_noise_std = sensor_fti_->getParamsSensorForceTorqueInertial()->gyro_noise_std; + auto acc_drift_std = sensor_fti_->getParamsSensorForceTorqueInertial()->acc_drift_std; + auto gyro_drift_std = sensor_fti_->getParamsSensorForceTorqueInertial()->gyro_drift_std; + + ArrayXd imu_drift_sigmas(6); + imu_drift_sigmas << acc_drift_std, acc_drift_std, acc_drift_std, gyro_drift_std, gyro_drift_std, gyro_drift_std; + imu_drift_cov_ = imu_drift_sigmas.square().matrix().asDiagonal(); + gyro_noise_cov_ = Array3d(gyro_noise_std, gyro_noise_std, gyro_noise_std).square().matrix().asDiagonal(); +} + +void ProcessorForceTorqueInertialDynamics::data2tangent(const Eigen::VectorXd& _data, + const Eigen::VectorXd& _bias, + const Eigen::VectorXd& _model, + Eigen::VectorXd& _tangent, + Eigen::MatrixXd& _J_tangent_data, + Eigen::MatrixXd& _J_tangent_bias, + Eigen::MatrixXd& _J_tangent_model) const + +{ + const Vector6d& awd = _data.segment<6>(0); // this is (a,w) of data in a 6-vector + const Vector3d& fd = _data.segment<3>(6); + const Vector3d& td = _data.segment<3>(9); + const Vector3d& c = _model.segment<3>(0); + const Matrix3d& fd_cross = skew(fd); + const Matrix3d& c_cross = skew(c); + + // tangent(a,w) = data(a,w) - bias(a,w) + // tangent(f) = data(f) + // tangent(t) = data(t) - model(c) x data(f) + _tangent.segment<6>(0) = awd - _bias; + _tangent.segment<3>(6) = fd; + _tangent.segment<3>(9) = td - c.cross(fd); // c x fd + + // J_tangent_data + _J_tangent_data.setIdentity(12, 12); + _J_tangent_data.block<3, 3>(9, 6) = -c_cross; // J_tt_fd = -c_cross + + // J_tangent_bias = [-Identity(6,6) ; Zero(6,6)] + _J_tangent_bias.topRows<6>() = -Matrix6d::Identity(); + _J_tangent_bias.bottomRows<6>() = Matrix6d::Zero(); + + // J_tangent_model + _J_tangent_model.setZero(12, 7); + _J_tangent_model.block<3, 3>(9, 0) = fd_cross; // J_tt_c = fd_cross +} + +void ProcessorForceTorqueInertialDynamics::computeCurrentDelta(const Eigen::VectorXd& _data, + const Eigen::MatrixXd& _data_cov, + const Eigen::VectorXd& _calib, + const double _dt, + Eigen::VectorXd& _delta, + Eigen::MatrixXd& _delta_cov, + Eigen::MatrixXd& _jacobian_calib) const +{ + // compute tangent by removing IMU bias + + /** + * Notes on the computation of `tangent` and `delta` from `data` and `calib`, and its Jacobians: + * ============================================================================================= + * + * Overview: from sensor data and system calibration parameters, we want to compute a delta. + * We proceed in two steps: + * - tangent = f ( data, bias, model ) + * - delta = g (tangent, model, dt ) + * + * Notice that the `model` part appears in both stages of the computation, f() and g(). + * This means that the chain rule for `J_delta_model` will have a double path. + * + * 1) On a first stage, we want to obtain `tangent` + * ------------------------------------------------ + * We have the following partitions of our variables: + * + * tangent = [at,wt,ft,tt] -- tangent to the delta manifold. (a,w) at BASE, (f,t) at COM + * data = [ad,wd,fd,td] -- sensor data, all measured at BASE + * bias = [ab,wb] -- IMU bias. This we called `I` in previous versions + * model = [c,i,m] -- system dynamic model, where c = r_base_com (CoM wrt BASE) + * calib = [bias,model] -- system calibration parameters + * + * we have the function: + * + * tangent = f (data, bias, model) + * + * we need to obtain: + * - tangent + * - J_tangent_data + * - J_tangent_bias + * - J_tangent_model + * + * We express each one of the blocks of `tangent` wrt the blocks of `data`, `bias` and `model`: + * + * at = ad - ab --> J_at_ad = I33 ; J_at_ab = -I33 + * wt = wd - wb --> J_wt_wd = I33 ; J_wt_wb = -I33 + * ft = fd --> J_ft_fd = I33 + * tt = td - c x fd --> J_tt_td = I33 ; J_tt_c = skew(fd) = fd_x ; J_tt_fd = -skew(c) = -c_x + * + * note: to obtain `tt` (at COM) we need to account for the torque measurement `td` (at BASE) + * and add the torque produced by the force `fd` applied at the lever arm of BASE wrt COM, which is `-c`, + * obtaining `tt = td - c x fd` + * + * So we can assemble the tangent vector as + * + * tangent = [at,wt,ft,tt] + * + * and all the Jacobian blocks as: + * + * ad wd fd td + * J_tangent_data = [ I33 033 033 033 at + * 033 I33 033 033 wt + * 033 033 I33 033 ft + * 033 033 -c_x I33 ] tt + * + * ab wb + * J_tangent_bias = [ -I33 033 at + * 033 -I33 wt + * 033 033 ft + * 033 033 ] tt + * + * c i m + * J_tangent_model = [ 033 033 031 at + * 033 033 031 wt + * 033 033 031 ft + * fd_x 033 031 ] tt + * + * and from here on, we just need to fill in the matrices. + * + * Notations: + * I33 : 3x3 Identity matrix + * 033 : 3x3 zero matrix + * 031 : 3x1 zero vector + * + * + * 2) On a second stage, we need to obtain the `delta` from the previous results. + * ------------------------------------------------------------------------------ + * + * We have: + * + * delta = g (tangent, model, dt) + * + * we need to obtain: + * + * delta + * J_delta_tangent + * J_delta_model + * + * these are obtained directly by the function g() = tangent2delta(). + * + * Note 1: It is unclear to me (JS, 4-aug-2022) if this function tangent2delta() is completely accurate + * with regard to the two different reference frames: BASE and COM. It is possible that + * we have to revise the math. + * + * Note 2: It is even possible that the function tangent2delta() is OK, but then that the function + * betweenStates() does not account for the difference in reference frames. So we also need to revise + * the math inside betweenStates() with regard to the two reference frames BASE and COM. + * + * 3) On a third stage, we need to apply the chain rule for the functions f() and g(), + * that is delta = g( f( data, bias, model), model) -- yes, it's a mess!! + * + * J_delta_data = J_delta_tangent * J_tangent_data + * J_delta_model = J_delta_model + J_delta_tangent * J_tangent_model <-- remember all Jacobians are PARTIAL + * derivatives. + * J_delta_bias = J_delta_tangent * J_tangent_bias + * + * Finally we can assemble the Jacobian `J_delta_calib` as the concatenation of bias and model: + * + * J_delta_calib = [ J_delta_bias | J_delta_model ] + * + * 4) On a fourth stage, we compute the covariance of the delta: + * + * Q_delta = J_delta_data * Q_data * J_delta_data.transpose() + * + * 5) The function returns the following quantities, which relate to the variables used above: + * + * - _delta = delta + * - _delta_cov = Q_delta + * - _jacobian_calib = J_delta_calib + * + * --------------------------------------------------------------- + * + * Notes: Jacobians of cross product + * + * a, b in R^3 + * + * a_x, b_x, skew-symmetric matrices, in R^3x3 + * + * a_x = [0 -az ay ; az 0 -ax ; -ay ax 0] + * + * a x b = a_x b + * + * a x b = - b x a = - b_x a + * + * J_(axb)_a = -b_x + * + * J_(axb)_b = a_x + */ + + // Matrix<double, 12, 1> tangent = _data; + // tangent.head<6>() -= _calib.head<6>(); // J_tangent_data = Identity(12,12) + // Matrix<double, 12, 6> J_tangent_I; // J_tangent_I = [-Identity(6,6) ; Zero(6,6)] + // J_tangent_I.topRows<6>() = -Matrix6d::Identity(); + // J_tangent_I.bottomRows<6>() = Matrix6d::Zero(); + + // // go from tangent to delta. We need to dynamic model for this + // const Matrix<double, 7, 1>& model = sensor_fti_->getModel(); + // Matrix<double, 18, 12> J_delta_tangent; + // Matrix<double, 18, 7> J_delta_model; + // fti::tangent2delta(tangent, model, _dt, _delta, J_delta_tangent, J_delta_model); + + // // Compute cov and compose jacobians + // Matrix<double, 18, 6> J_delta_I = J_delta_tangent * J_tangent_I; + // _jacobian_calib << J_delta_I, J_delta_model; // J_delta_calib = _jacobian_calib = [J_delta_I ; J_delta_model] + // const auto& J_delta_data = J_delta_tangent; // * J_tangent_data, which is the Identity; + // _delta_cov = J_delta_data * _data_cov * J_delta_data.transpose(); + + ////////////////////// NOU CODI QUE HAURE DE REVISAR I CANVIAR PEL QUE HI HA ADALT ////////////////////////// + + /* + * 1. tangent = f(data, bias, model) --> J_tangent_data, J_tangent_bias, J_tangent_model + * + * data = [ad,wd,fd,td] + * bias = [ab,wb] + * model = [c,i,m] + * tangent = [at,wt,ft,tt] + */ + + Vector6d bias = _calib.segment<6>(0); + Vector7d model = _calib.segment<7>(6); + VectorXd tangent(12); + MatrixXd J_tangent_data(12, 12); + MatrixXd J_tangent_bias(12, 6); + MatrixXd J_tangent_model(12, 7); + + data2tangent(_data, bias, model, tangent, J_tangent_data, J_tangent_bias, J_tangent_model); + + // 2. go from tangent to delta. We need again the dynamic model for this + Matrix<double, 18, 12> J_delta_tangent; + Matrix<double, 18, 7> J_delta_model; + fti::tangent2delta( + tangent, model, _dt, _delta, J_delta_tangent, J_delta_model); // Aquà ja farà bé ell sol el J_delta_model? + + // 3. Compose jacobians + Matrix<double, 18, 6> J_delta_bias = J_delta_tangent * J_tangent_bias; + J_delta_model += J_delta_tangent * J_tangent_model; + _jacobian_calib << J_delta_bias, J_delta_model; // J_delta_calib = [J_delta_bias ; J_delta_model] + const auto& J_delta_data = J_delta_tangent * J_tangent_data; + + // 4. compute covariance + _delta_cov = J_delta_data * _data_cov * J_delta_data.transpose(); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +void ProcessorForceTorqueInertialDynamics::deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2) const +{ + bodydynamics::fti::compose(_delta1, _delta2, _dt2, _delta1_plus_delta2); +} + +void ProcessorForceTorqueInertialDynamics::deltaPlusDelta(const Eigen::VectorXd& _delta1, + const Eigen::VectorXd& _delta2, + const double _dt2, + Eigen::VectorXd& _delta1_plus_delta2, + Eigen::MatrixXd& _jacobian1, + Eigen::MatrixXd& _jacobian2) const +{ + bodydynamics::fti::compose(_delta1, _delta2, _dt2, _delta1_plus_delta2, _jacobian1, _jacobian2); +} + +void ProcessorForceTorqueInertialDynamics::statePlusDelta(const VectorComposite& _x, + const Eigen::VectorXd& _delta, + const double _dt, + VectorComposite& _x_plus_delta) const +{ + auto x = _x.vector("PVLO"); + VectorXd x_plus_delta = bodydynamics::fti::composeOverState(x, _delta, _dt); + _x_plus_delta = VectorComposite(x_plus_delta, "PVLO", {3, 3, 3, 4}); +} + +Eigen::VectorXd ProcessorForceTorqueInertialDynamics::correctDelta(const Eigen::VectorXd& _delta_preint, + const Eigen::VectorXd& _delta_step) const +{ + return fti::plus(_delta_preint, _delta_step); +} + +VectorXd ProcessorForceTorqueInertialDynamics::getCalibration(const CaptureBaseConstPtr _capture) const +{ + if (_capture) + { + VectorXd calibration(13); + const Vector6d& I_calib = _capture->getStateBlock('I')->getState(); + const Vector3d& C_calib = _capture->getStateBlock('C')->getState(); + const Vector3d& i_calib = _capture->getStateBlock('i')->getState(); + const Vector1d& m_calib = _capture->getStateBlock('m')->getState(); + calibration << I_calib, C_calib, i_calib, m_calib; + return calibration; + } + else + { + VectorXd calibration(13); + const Vector6d& I_calib = getSensor()->getStateBlockDynamic('I')->getState(); + const Vector3d& C_calib = getSensor()->getStateBlockDynamic('C')->getState(); + const Vector3d& i_calib = getSensor()->getStateBlockDynamic('i')->getState(); + const Vector1d& m_calib = getSensor()->getStateBlockDynamic('m')->getState(); + calibration << I_calib, C_calib, i_calib, m_calib; + return calibration; + } +} + +bool ProcessorForceTorqueInertialDynamics::voteForKeyFrame() const +{ + // time span + if (getBuffer().back().ts_ - getBuffer().front().ts_ > params_force_torque_inertial_dynamics_->max_time_span) + { + WOLF_DEBUG("PM: vote: time span"); + return true; + } + // buffer length + if (getBuffer().size() > params_force_torque_inertial_dynamics_->max_buff_length) + { + WOLF_DEBUG("PM: vote: buffer length"); + return true; + } + // dist_traveled + VectorComposite X0 = getOrigin()->getFrame()->getState(); + VectorComposite X1; + double dt = getBuffer().back().ts_ - getOrigin()->getFrame()->getTimeStamp(); + statePlusDelta(X0, getBuffer().back().delta_integr_, dt, X1); + double dist = (X1.at('P') - X0.at('P')).norm(); + if (dist > params_force_torque_inertial_dynamics_->dist_traveled) + { + WOLF_DEBUG("PM: vote: distance traveled"); + return true; + } + // angle turned + double angle = 2.0 * acos(getMotion().delta_integr_(15)); + if (angle > params_force_torque_inertial_dynamics_->angle_turned) + { + WOLF_DEBUG("PM: vote: angle turned"); + return true; + } + + return false; +} + +} /* namespace wolf */ + +#include <core/processor/factory_processor.h> +namespace wolf +{ +WOLF_REGISTER_PROCESSOR(ProcessorForceTorqueInertialDynamics); +WOLF_REGISTER_PROCESSOR_AUTO(ProcessorForceTorqueInertialDynamics); +} // namespace wolf \ No newline at end of file diff --git a/src/processor/processor_force_torque_preint.cpp b/src/processor/processor_force_torque_preint.cpp deleted file mode 100644 index 1b84f786077a0f65ee3cc944bf8ebe4f54511499..0000000000000000000000000000000000000000 --- a/src/processor/processor_force_torque_preint.cpp +++ /dev/null @@ -1,272 +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-------- -// wolf -#include "bodydynamics/math/force_torque_delta_tools.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" -#include "bodydynamics/feature/feature_force_torque_preint.h" -#include "bodydynamics/processor/processor_force_torque_preint.h" -#include "bodydynamics/factor/factor_force_torque_preint.h" - -namespace wolf { - -int compute_data_size(int nbc, int dimc){ - // compute the size of the data/body vectors used by processorMotion API depending - // on the number of contacts (nbc) and contact dimension (dimc) - // result: forces (+torques for 6D contacts) + pbc + wb + contact/base positions + contact/base orientations - return nbc*dimc + 3 + 3 + nbc*3 + nbc*4; -} - -using namespace Eigen; - -ProcessorForceTorquePreint::ProcessorForceTorquePreint(ParamsProcessorForceTorquePreintPtr _params_motion_force_torque_preint) : - ProcessorMotion("ProcessorForceTorquePreint", "CDLO", 3, 13, 13, 12, - compute_data_size(_params_motion_force_torque_preint->nbc, _params_motion_force_torque_preint->dimc), - 6, _params_motion_force_torque_preint), - params_motion_force_torque_preint_(std::make_shared<ParamsProcessorForceTorquePreint>(*_params_motion_force_torque_preint)), - nbc_(_params_motion_force_torque_preint->nbc), - dimc_(_params_motion_force_torque_preint->dimc) - -{ - // -} - -ProcessorForceTorquePreint::~ProcessorForceTorquePreint() -{ - // -} - -bool ProcessorForceTorquePreint::voteForKeyFrame() const -{ - // time span - if (getBuffer().back().ts_ - getBuffer().front().ts_ > params_motion_force_torque_preint_->max_time_span) - { - WOLF_DEBUG( "PM: vote: time span" ); - return true; - } - // buffer length - if (getBuffer().size() > params_motion_force_torque_preint_->max_buff_length) - { - WOLF_DEBUG( "PM: vote: buffer length" ); - return true; - } - - // Some other measure of movement? - - //WOLF_DEBUG( "PM: do not vote" ); - return false; -} - - -CaptureMotionPtr ProcessorForceTorquePreint::emplaceCapture(const FrameBasePtr& _frame_own, - const SensorBasePtr& _sensor, - const TimeStamp& _ts, - const VectorXd& _data, - const MatrixXd& _data_cov, - const VectorXd& _calib, - const VectorXd& _calib_preint, - const CaptureBasePtr& _capture_origin) -{ - - // Here we have to retrieve the origin capture no - // !! PROBLEM: - // when doing setOrigin, emplaceCapture is called 2 times - // - first on the first KF (usually created by setPrior), this one contains the biases - // - secondly on the inner frame (last) which does not contains other captures - auto capture_ikin = _frame_own->getCaptureOf(sensor_ikin_); - auto capture_angvel = _frame_own->getCaptureOf(sensor_angvel_); - if ((capture_ikin == nullptr) || (capture_angvel == nullptr)){ - // We have to retrieve the bias from a former Keyframe: origin - capture_ikin = origin_ptr_->getFrame()->getCaptureOf(sensor_ikin_); - capture_angvel = origin_ptr_->getFrame()->getCaptureOf(sensor_angvel_); - } - auto cap = CaptureBase::emplace<CaptureForceTorquePreint>( - _frame_own, - _ts, - _sensor, - std::static_pointer_cast<CaptureInertialKinematics>(capture_ikin), - std::static_pointer_cast<CaptureMotion>(capture_angvel), - _data, - _data_cov, - _capture_origin); - - // Cannot be recovered from the _calib and _calib_preint which are initialized using calib_size_ - // which is zero in our case since the calibration stateblocks are not actually in the FTPreint sensor/captures - - auto cap_ftpreint = std::static_pointer_cast<CaptureForceTorquePreint>(cap); - Vector6d calib = getCalibration(cap_ftpreint); - setCalibration(cap_ftpreint, calib); - cap_ftpreint->setCalibrationPreint(calib); - - return cap; -} - -FeatureBasePtr ProcessorForceTorquePreint::emplaceFeature(CaptureMotionPtr _capture_motion) -{ - // Retrieving the origin capture and getting the bias from here should be done here? - auto feature = FeatureBase::emplace<FeatureForceTorquePreint>(_capture_motion, - _capture_motion->getBuffer().back().delta_integr_, - _capture_motion->getBuffer().back().delta_integr_cov_ + unmeasured_perturbation_cov_, - _capture_motion->getCalibrationPreint(), - _capture_motion->getBuffer().back().jacobian_calib_); - return feature; -} - -Eigen::VectorXd ProcessorForceTorquePreint::correctDelta (const Eigen::VectorXd& delta_preint, - const Eigen::VectorXd& delta_step) const -{ - return bodydynamics::plus(delta_preint, delta_step); -} - -VectorXd ProcessorForceTorquePreint::getCalibration (const CaptureBaseConstPtr _capture) const -{ - - VectorXd bias_vec(6); - - if (_capture) // access from capture is quicker - { - auto cap_ft(std::static_pointer_cast<const CaptureForceTorquePreint>(_capture)); - - // get calib part from Ikin capture - bias_vec.segment<3>(0) = cap_ft->getIkinCaptureOther()->getSensorIntrinsic()->getState(); - - // get calib part from IMU capture - bias_vec.segment<3>(3) = cap_ft->getGyroCaptureOther()->getSensorIntrinsic()->getState().tail<3>(); // TODO: only valib for IMU bias sb or any other of the structure [XXXX, gyro_bias] - } - else // access from sensor is slower - { - // get calib part from Ikin capture - bias_vec.segment<3>(0) = sensor_ikin_->getIntrinsic()->getState(); - - // get calib part from IMU capture - bias_vec.segment<3>(3) = sensor_angvel_->getIntrinsic()->getState().tail<3>(); // TODO: only valib for IMU bias sb or any other of the structure [XXXX, gyro_bias] - } - return bias_vec; -} - -void ProcessorForceTorquePreint::setCalibration (const CaptureBasePtr _capture, const VectorXd& _calibration) -{ - CaptureForceTorquePreintPtr cap_ft(std::static_pointer_cast<CaptureForceTorquePreint>(_capture)); - - // set calib part in Ikin capture - cap_ft->getIkinCaptureOther()->getSensorIntrinsic()->setState(_calibration.head<3>()); - - // set calib part in IMU capture - Vector6d calib_imu = cap_ft->getGyroCaptureOther()->getSensorIntrinsic()->getState(); - calib_imu.tail<3>() = _calibration.tail<3>(); - - cap_ft->getGyroCaptureOther()->getSensorIntrinsic()->setState(calib_imu); -} - -FactorBasePtr ProcessorForceTorquePreint::emplaceFactor(FeatureBasePtr _feature_motion, CaptureBasePtr _capture_origin) -{ - CaptureForceTorquePreintPtr cap_ftpreint_origin = std::static_pointer_cast<CaptureForceTorquePreint>(_capture_origin); - FeatureForceTorquePreintPtr ftr_ftpreint = std::static_pointer_cast<FeatureForceTorquePreint>(_feature_motion); - CaptureForceTorquePreintPtr cap_ftpreint_new = std::static_pointer_cast<CaptureForceTorquePreint>(ftr_ftpreint->getCapture()); - - auto fac_ftpreint = FactorBase::emplace<FactorForceTorquePreint>( - ftr_ftpreint, - ftr_ftpreint, - cap_ftpreint_origin, - shared_from_this(), - cap_ftpreint_origin->getIkinCaptureOther(), // to retrieve sb_bp1 - cap_ftpreint_origin->getGyroCaptureOther(), // to retrieve sb_w1 - false); - - return fac_ftpreint; -} - -void ProcessorForceTorquePreint::computeCurrentDelta( - const Eigen::VectorXd& _data, - const Eigen::MatrixXd& _data_cov, - const Eigen::VectorXd& _calib, - const double _dt, - Eigen::VectorXd& _delta, - Eigen::MatrixXd& _delta_cov, - Eigen::MatrixXd& _jac_delta_calib) const -{ - assert(_data.size() == data_size_ && "Wrong data size!"); - - // create delta - MatrixXd jac_body_bias(data_size_-nbc_,6); - VectorXd body(data_size_); - bodydynamics::debiasData(_data, _calib, nbc_, dimc_, body, jac_body_bias); - - MatrixXd jac_delta_body(12,data_size_-nbc_); - bodydynamics::body2delta(body, _dt, std::static_pointer_cast<const SensorForceTorque>(getSensor())->getMass(), - nbc_, dimc_, - _delta, jac_delta_body); // Jacobians tested in bodydynamics_tools - - // [f], [tau], pbc, wb - MatrixXd jac_delta_body_reduced = jac_delta_body.leftCols(nbc_*dimc_+6); - - // compute delta_cov (using uncertainty on f,tau,pbc) - _delta_cov = jac_delta_body_reduced * _data_cov * jac_delta_body_reduced.transpose(); // trivially: jac_body_delta = Identity - // _delta_cov = jac_delta_body * _data_cov * jac_delta_body.transpose(); // trivially: jac_body_delta = Identity - - // compute jacobian_calib - _jac_delta_calib = jac_delta_body * jac_body_bias; -} - -void ProcessorForceTorquePreint::deltaPlusDelta(const Eigen::VectorXd& _delta_preint, - const Eigen::VectorXd& _delta, - const double _dt, - Eigen::VectorXd& _delta_preint_plus_delta) const -{ - _delta_preint_plus_delta = bodydynamics::compose(_delta_preint, _delta, _dt); -} - -void ProcessorForceTorquePreint::statePlusDelta(const VectorComposite& _x, - const Eigen::VectorXd& _delta, - const double _dt, - VectorComposite& _x_plus_delta) const -{ - assert(_delta.size() == 13 && "Wrong _delta vector size"); - assert(_dt >= 0 && "Time interval _dt is negative!"); - - // verbose way : create Eigen vectors, then compute, then convert back to Composite - - auto x = _x.vector("CDLO"); - - VectorXd x_plus_delta = bodydynamics::composeOverState(x, _delta, _dt); - - _x_plus_delta = VectorComposite(x_plus_delta, "CDLO", {3,3,3,4}); -} - -void ProcessorForceTorquePreint::deltaPlusDelta(const Eigen::VectorXd& _delta_preint, - const Eigen::VectorXd& _delta, - const double _dt, - Eigen::VectorXd& _delta_preint_plus_delta, - Eigen::MatrixXd& _jacobian_delta_preint, - Eigen::MatrixXd& _jacobian_delta) const -{ - bodydynamics::compose(_delta_preint, _delta, _dt, _delta_preint_plus_delta, _jacobian_delta_preint, _jacobian_delta); // jacobians tested in bodydynamics_tools -} - -} // namespace wolf - -// Register in the FactorySensor -#include "core/processor/factory_processor.h" - -namespace wolf -{ -WOLF_REGISTER_PROCESSOR(ProcessorForceTorquePreint) -} diff --git a/src/sensor/sensor_force_torque_inertial.cpp b/src/sensor/sensor_force_torque_inertial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa38e8ab62d4bd0bdc0fee48bd15c227ea52fc8d --- /dev/null +++ b/src/sensor/sensor_force_torque_inertial.cpp @@ -0,0 +1,82 @@ +//--------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 "bodydynamics/sensor/sensor_force_torque_inertial.h" + +#include <core/state_block/factory_state_block.h> +#include <core/state_block/state_block_derived.h> + +namespace wolf +{ + +// TODO: remove this constructor after merging MR !448 +SensorForceTorqueInertial::SensorForceTorqueInertial(const VectorXd& _extrinsics, + ParamsSensorForceTorqueInertialPtr _params) + : SensorBase("SensorForceTorqueInertial", + FactoryStateBlock::create("StatePoint3d", _extrinsics.head(3), true), + FactoryStateBlock::create("StateQuaternion", _extrinsics.tail(4), true), + FactoryStateBlock::create("StateParams6", Vector6d::Zero(), false), // IMU bias + 12, + false, + false, + false), + params_fti_(_params) +{ + addStateBlock('C', FactoryStateBlock::create("StateParams3", params_fti_->com, true)); // centre of mass + addStateBlock('i', FactoryStateBlock::create("StateParams3", params_fti_->inertia, true)); // inertial vector + addStateBlock('m', FactoryStateBlock::create("StateParams1", Vector1d(params_fti_->mass), true)); // total mass + setStateBlockDynamic('I', false); + setStateBlockDynamic('C', false); + setStateBlockDynamic('i', false); + setStateBlockDynamic('m', false); +} + +// TODO: Adapt this API to that of MR !448 +SensorForceTorqueInertial::SensorForceTorqueInertial(const VectorComposite& _states, + ParamsSensorForceTorqueInertialPtr _params) + : SensorBase("SensorForceTorqueInertial", + FactoryStateBlock::create("StatePoint3d", _states.at('P'), false), + FactoryStateBlock::create("StateQuaternion", _states.at('O'), false), + FactoryStateBlock::create("StateParams6", _states.at('I'), false), // IMU bias + 12, + false, + false, + false), + params_fti_(_params) +{ + auto sbc = emplaceStateBlock<StateParams3>('C', getProblem(), _states.at('C'), true); // centre of mass + auto sbi = emplaceStateBlock<StateParams3>('i', getProblem(), _states.at('i'), true); // inertial vector + auto sbm = emplaceStateBlock<StateParams1>('m', getProblem(), _states.at('m'), true); // total mass + setStateBlockDynamic('I', false); + setStateBlockDynamic('C', false); + setStateBlockDynamic('i', false); + setStateBlockDynamic('m', false); +} + +} // namespace wolf + +#include <core/sensor/factory_sensor.h> +namespace wolf +{ +WOLF_REGISTER_SENSOR(SensorForceTorqueInertial); +WOLF_REGISTER_SENSOR_AUTO(SensorForceTorqueInertial); +} // namespace wolf \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5850e2db6763d74e6385f16417ed48102091ba5b..52e1fd976f3619fc9b303b7c9ef601c740d10677 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,14 +16,20 @@ wolf_add_gtest(gtest_feature_inertial_kinematics gtest_feature_inertial_kinemati wolf_add_gtest(gtest_factor_inertial_kinematics gtest_factor_inertial_kinematics.cpp) -wolf_add_gtest(gtest_factor_force_torque gtest_factor_force_torque.cpp) +wolf_add_gtest(gtest_factor_force_torque_inertial gtest_factor_force_torque_inertial.cpp) + +wolf_add_gtest(gtest_factor_force_torque_inertial_dynamics gtest_factor_force_torque_inertial_dynamics.cpp) wolf_add_gtest(gtest_force_torque_delta_tools gtest_force_torque_delta_tools.cpp) +wolf_add_gtest(gtest_force_torque_inertial_delta_tools gtest_force_torque_inertial_delta_tools.cpp) + # TODO: revive those -# wolf_add_gtest(gtest_feature_force_torque_preint gtest_feature_force_torque_preint.cpp) +# wolf_add_gtest(gtest_feature_force_torque gtest_feature_force_torque.cpp) # wolf_add_gtest(gtest_processor_inertial_kinematics gtest_processor_inertial_kinematics.cpp) -# wolf_add_gtest(gtest_processor_force_torque_preint gtest_processor_force_torque_preint.cpp) +# wolf_add_gtest(gtest_processor_force_torque gtest_processor_force_torque.cpp) + +wolf_add_gtest(gtest_processor_force_torque_inertial_dynamics gtest_processor_force_torque_inertial_dynamics.cpp) wolf_add_gtest(gtest_processor_point_feet_nomove gtest_processor_point_feet_nomove.cpp) @@ -31,3 +37,5 @@ wolf_add_gtest(gtest_sensor_force_torque gtest_sensor_force_torque.cpp) wolf_add_gtest(gtest_sensor_inertial_kinematics gtest_sensor_inertial_kinematics.cpp) +wolf_add_gtest(gtest_solve_problem_force_torque_inertial_dynamics gtest_solve_problem_force_torque_inertial_dynamics.cpp) + diff --git a/test/gtest_factor_force_torque.cpp b/test/gtest_factor_force_torque.cpp deleted file mode 100644 index 0ccf5135e3d1bbe45a2c07cdbd68cf530dc9c571..0000000000000000000000000000000000000000 --- a/test/gtest_factor_force_torque.cpp +++ /dev/null @@ -1,865 +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 gtest_factor_inertial_kinematics.cpp - * - * Created on: Janv 29, 2020 - * \author: Mederic Fourmy - */ - -/* - - -Organisation: For each test, the problem, sensors, factors (if possible) are instanciated in a base class inheriting fromtesting::Test only. -Then, each test case is derived in a child class which defines the data processed by the different sensors and expected estimated test values. -Finally, each of these child classes is used in one or several Test in which basically the expected values are compared against estimation and -solve is done with a perturbated system. - -Tests list: - -FactorInertialKinematics_2KF_Meas0_bpfix,ZeroMvt: -FactorInertialKinematics_2KF_1p_bpfix,ZeroMvt: -FactorInertialKinematics_2KF_m1p_pfix,ZeroMvt: -FactorInertialKinematics_2KF_1v_bfix,ZeroMvt: -*/ - - -// debug -#include <iostream> -#include <fstream> - -#include <core/utils/utils_gtest.h> -#include <core/utils/logging.h> - -// WOLF -#include <core/problem/problem.h> -#include <core/ceres_wrapper/solver_ceres.h> -#include <core/math/rotations.h> -#include <core/capture/capture_base.h> -#include <core/feature/feature_base.h> -#include <core/factor/factor_block_absolute.h> -#include <core/state_block/state_block_derived.h> - -// Imu -// #include "imu/internal/config.h" -// #include "imu/capture/capture_imu.h" -// #include "imu/processor/processor_imu.h" -// #include "imu/sensor/sensor_imu.h" - -// BODYDYNAMICS -#include "bodydynamics/internal/config.h" -#include "bodydynamics/sensor/sensor_inertial_kinematics.h" -#include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/feature/feature_inertial_kinematics.h" -#include "bodydynamics/factor/factor_inertial_kinematics.h" -#include "bodydynamics/feature/feature_force_torque.h" -#include "bodydynamics/factor/factor_force_torque.h" - -// ODOM3d -#include "core/capture/capture_pose.h" -#include "core/feature/feature_pose.h" -#include "core/factor/factor_relative_pose_3d.h" - -#include <Eigen/Eigenvalues> - -using namespace wolf; -using namespace Eigen; -using namespace std; - - -// SOME CONSTANTS -const double Acc1 = 1; -const double Acc2 = 3; -const Vector3d zero3 = Vector3d::Zero(); -const Vector6d zero6 = Vector6d::Zero(); - - - -Matrix9d computeKinJac(const Vector3d& w_unb, - const Vector3d& p_unb, - const Matrix3d& Iq) -{ - Matrix9d J; J.setZero(); - - Matrix3d wx = skew(w_unb); - Matrix3d px = skew(p_unb); - // Starting from top left, to the right and then next row - J.topLeftCorner<3,3>() = Matrix3d::Identity(); - // J.block<3,3>(0,3) = Matrix3d::Zero(); - // J.topRightCorner<3,3>() = Matrix3d::Zero(); - J.block<3,3>(3,0) = -wx; - J.block<3,3>(3,3) = -Matrix3d::Identity(); - J.block<3,3>(3,6) = px; - // J.bottomLeftCorner<3,3>() = Matrix3d::Zero(); - // J.block<3,3>(6,3) = Matrix3d::Zero(); - J.bottomRightCorner<3,3>() = -Iq; - - return J; -} - -Matrix9d computeKinCov(const Matrix3d& Qp, - const Matrix3d& Qv, - const Matrix3d& Qw, - const Vector3d& w_unb, - const Vector3d& p_unb, - const Matrix3d& Iq) -{ - Matrix9d cov; cov.setZero(); - - Matrix3d wx = skew(w_unb); - Matrix3d px = skew(p_unb); - // Starting from top left, to the right and then next row - cov.topLeftCorner<3,3>() = Qp; - cov.block<3,3>(0,3) = Qp * wx; - // cov.topRightCorner<3,3>() = Matrix3d::Zero(); - cov.block<3,3>(3,0) = cov.block<3,3>(0,3).transpose(); - cov.block<3,3>(3,3) = -wx*Qp*wx + Qv - px*Qw*px; - cov.block<3,3>(3,6) = -px*Qw*Iq; - // cov.bottomLeftCorner<3,3>() = Matrix3d::Zero(); - cov.block<3,3>(6,3) = Iq*Qw*px; - cov.bottomRightCorner<3,3>() = Iq*Qw*Iq; - - return cov; -} - - -void perturbateIfUnFixed(const FrameBasePtr& KF, char sb_name){ - if(!KF->getStateBlock(sb_name)->isFixed()) - { - if (sb_name == 'O') - { - double max_rad_pert = M_PI / 3; - Vector3d do_pert = max_rad_pert*Vector3d::Random(); - Quaterniond curr_state_q(KF->getO()->getState().data()); - KF->getStateBlock('O')->setState((curr_state_q * exp_q(do_pert)).coeffs()); - } - else - { - VectorXd pert; - pert.resize(KF->getStateBlock(sb_name)->getSize()); - pert.setRandom(); pert *= 0.5; - KF->getStateBlock(sb_name)->setState(KF->getStateBlock(sb_name)->getState() + pert); - } - } -} - -void perturbateAllIfUnFixed(const FrameBasePtr& KF) -{ - for (auto it: KF->getStateBlockMap()){ - perturbateIfUnFixed(KF, it.first); - } -} - -FrameBasePtr createKFWithCDLI(const ProblemPtr& problem, - const TimeStamp& t, - VectorComposite x_origin, - Vector3d c, - Vector3d cd, - Vector3d Lc, - Vector6d bias_imu) -{ - FrameBasePtr KF = FrameBase::emplace<FrameBase>(problem->getTrajectory(), t, "POV", x_origin); - StateBlockPtr sbc = make_shared<StatePoint3d>(c); - KF->addStateBlock('C', sbc, problem); - StateBlockPtr sbd = make_shared<StateVector3d>(cd); - KF->addStateBlock('D', sbd, problem); - StateBlockPtr sbL = make_shared<StateVector3d>(Lc); - KF->addStateBlock('L', sbL, problem); - StateBlockPtr sbbimu = make_shared<StateParams6>(bias_imu); - KF->addStateBlock('I', sbbimu, problem); // Simulating IMU - return KF; -} - -class FactorInertialKinematics_2KF : public testing::Test -{ - public: - double mass_; - wolf::TimeStamp tA_; - wolf::TimeStamp tB_; - ProblemPtr problem_; - SolverCeresPtr solver_; - VectorComposite x_origin_; // initial state - VectorComposite s_origin_; // initial sigmas for prior - SensorInertialKinematicsPtr SIK_; - CaptureInertialKinematicsPtr CIKA_, CIKB_; - FrameBasePtr KFA_; - FrameBasePtr KFB_; - Matrix3d Qp_, Qv_, Qw_; - Vector3d bias_p_; - Vector6d bias_imu_; - FeatureInertialKinematicsPtr FIKA_; - FeatureInertialKinematicsPtr FIKB_; - FeatureForceTorquePtr FFTAB_; - // SensorImuPtr sen_imu_; - // ProcessorImuPtr processor_imu_; - - protected: - void SetUp() override - { - - std::string bodydynamics_root = _WOLF_BODYDYNAMICS_ROOT_DIR; - - mass_ = 10.0; // Small 10 kg robot - //===================================================== SETTING PROBLEM - // WOLF PROBLEM - problem_ = Problem::create("POV", 3); - - VectorXd extr_ik(0); - ParamsSensorInertialKinematicsPtr intr_ik = std::make_shared<ParamsSensorInertialKinematics>(); - intr_ik->std_pbc = 0.1; - intr_ik->std_vbc = 0.1; - auto senik = problem_->installSensor("SensorInertialKinematics", "senIK", extr_ik, intr_ik); - SIK_ = std::static_pointer_cast<SensorInertialKinematics>(senik); - - // CERES WRAPPER - solver_ = std::make_shared<SolverCeres>(problem_); - solver_->getSolverOptions().max_num_iterations = 1e3; - // solver_->getSolverOptions().minimizer_type = ceres::TRUST_REGION; //ceres::TRUST_REGION;ceres::LINE_SEARCH - // solver_->getSolverOptions().max_line_search_step_contraction = 1e-3; - - // INSTALL Imu SENSOR - // Vector7d extrinsics; extrinsics << 0,0,0,0,0,0,1; - // SensorBasePtr sen_imu = problem_->installSensor("SensorImu", "Main Imu Sensor", extrinsics, bodydynamics_root + "/demos/sensor_imu.yaml"); - // sen_imu_ = std::static_pointer_cast<SensorImu>(sen_imu); - // ProcessorBasePtr prc_imu = problem_->installProcessor("ProcessorImu", "Imu PROC", "Main Imu Sensor", bodydynamics_root + "/demos/processor_imu.yaml"); - // Vector6d data = zero6; - // wolf::CaptureImuPtr imu_ptr = std::make_shared<CaptureImu>(0, sen_imu_, data, sen_imu_->getNoiseCov(), zero6); - // // sen_imu_->process(imu_ptr); - - // ======================= INITIALIZATION KFA WITH PRIOR + INERTIAL KINEMATIC FACTOR - tA_.set(0); - x_origin_ = VectorComposite("POV", {Vector3d(0,0,0), Quaterniond::Identity().coeffs(), Vector3d(0,0,0)}); - s_origin_ = VectorComposite("POV", {1e-3*Vector3d(1,1,1), 1e-3*Vector3d(1,1,1), 1e-3*Vector3d(1,1,1)}); - KFA_ = problem_->setPriorFactor(x_origin_, s_origin_, tA_); - - - // ADD THE STATEBLOCKS AND SET THEIR DEFAULT VALUES - bias_p_ = zero3; - bias_imu_ = zero6; - StateBlockPtr sbcA = make_shared<StatePoint3d>(zero3); KFA_->addStateBlock('C', sbcA, problem_); - StateBlockPtr sbdA = make_shared<StateVector3d>(zero3); KFA_->addStateBlock('D', sbdA, problem_); - StateBlockPtr sbLA = make_shared<StateVector3d>(zero3); KFA_->addStateBlock('L', sbLA, problem_); - StateBlockPtr sbbimuA = make_shared<StateParams6>(bias_imu_); KFA_->addStateBlock('I', sbbimuA, problem_); - - // Fix the one we cannot estimate - // KFA_->getP()->fix(); - // KFA_->getO()->fix(); - // KFA_->getV()->fix(); - KFA_->getStateBlock('I')->fix(); // Imu - - // INERTIAL KINEMATICS FACTOR - // Measurements - Vector3d pBC_measA = zero3; - Vector3d vBC_measA = zero3; - Vector3d w_measA = zero3; - Vector9d meas_ikinA; meas_ikinA << pBC_measA, vBC_measA, w_measA; - // momentum parameters - Matrix3d Iq; Iq.setIdentity(); - Vector3d Lq = zero3; - - Qp_ = pow(1e-2, 2)*Matrix3d::Identity(); - Qv_ = pow(1e-2, 2)*Matrix3d::Identity(); - Qw_ = pow(1e-2, 2)*Matrix3d::Identity(); - Eigen::Matrix9d Q_ikin_err = computeKinCov(Qp_, Qv_, Qw_, meas_ikinA.head<3>()-bias_p_, meas_ikinA.tail<3>()-bias_imu_.tail<3>(), Iq); - - CIKA_ = CaptureBase::emplace<CaptureInertialKinematics>(KFA_, tA_, SIK_, meas_ikinA, Iq, Lq, bias_p_); - CIKA_->getStateBlock('I')->fix(); // IK bias - FIKA_ = FeatureBase::emplace<FeatureInertialKinematics>(CIKA_, meas_ikinA, Q_ikin_err, Iq, Lq, FeatureBase::UncertaintyType::UNCERTAINTY_IS_COVARIANCE); - FactorBase::emplace<FactorInertialKinematics>(FIKA_, FIKA_, sbbimuA, nullptr, false); - - - // =============== NEW KFB WITH CORRESPONDING STATEBLOCKS - tB_.set(1); - - KFB_ = createKFWithCDLI(problem_, tB_, x_origin_, - zero3, zero3, zero3, bias_imu_); - // Fix the one we cannot estimate - // KFB_->getP()->fix(); - KFB_->getO()->fix(); // Not in the FT factor, so should be fixed (or absolute factor) - // KFB_->getV()->fix(); - KFB_->getStateBlock('I')->fix(); // Imu - - - // // ================ PROCESS Imu DATA - // Vector6d data_imu; - // data_imu << -wolf::gravity(), 0,0,0; - // CaptureImuPtr cap_imu = std::make_shared<CaptureImu>(tB_, sen_imu_, data_imu, sen_imu_->getNoiseCov(), bias_imu_); //set data on Imu (measure only gravity here) - // // process data in capture - // // sen_imu_->process(cap_imu); - - // ================ FACTOR INERTIAL KINEMATICS ON KFB - Vector3d pBC_measB = zero3; - Vector3d vBC_measB = zero3; - Vector3d w_measB = zero3; - Vector9d meas_ikinB; meas_ikinB << pBC_measB, vBC_measB, w_measB; - CIKB_ = CaptureBase::emplace<CaptureInertialKinematics>(KFB_, tB_, SIK_, meas_ikinB, Iq, Lq, bias_p_); - CIKB_->getSensorIntrinsic()->fix(); // IK bias - FIKB_ = FeatureBase::emplace<FeatureInertialKinematics>(CIKB_, meas_ikinB, Q_ikin_err, Iq, Lq, FeatureBase::UncertaintyType::UNCERTAINTY_IS_COVARIANCE); - auto fac_inkinB = FactorBase::emplace<FactorInertialKinematics>(FIKB_, FIKB_, KFB_->getStateBlock('I'), nullptr, false); - - - // FORCE TORQUE FACTOR BETWEEEN KFA AND KFB - Vector3d f1; f1 << -mass_*wolf::gravity()/2; // Only gravity -> static robot on both feet - Vector3d tau1; tau1 << 0,0,0; - Vector3d pbl1; pbl1 << 0,0,0; - Vector4d bql1; bql1 << 0,0,0,1; - Vector3d f2; f2 << -mass_*wolf::gravity()/2; // Only gravity -> static robot on both feet - Vector3d tau2; tau2 << 0,0,0; - Vector3d pbl2; pbl2 << 0,0,0; - Vector4d bql2; bql2 << 0,0,0,1; - Vector3d pbc; pbc << 0,0,0; - // aggregated meas - Vector6d f_meas; f_meas << f1, f2; - Vector6d tau_meas; tau_meas << tau1, tau2; - Matrix<double,14,1> kin_meas; kin_meas << pbl1, pbl2, bql1, bql2; - - Matrix6d cov_f = 1e-4 * Matrix6d::Identity(); - Matrix6d cov_tau = 1e-4 * Matrix6d::Identity(); - Matrix3d cov_pbc = 1e-4 * Matrix3d::Identity(); - - CaptureBasePtr cap_ftB = CaptureBase::emplace<CaptureBase>(KFB_, "CaptureForceTorque", tB_, nullptr); - FFTAB_ = FeatureBase::emplace<FeatureForceTorque>(cap_ftB, - tB_ - tA_, mass_, - f_meas, tau_meas, pbc, kin_meas, - cov_f, cov_tau, cov_pbc); - - FactorForceTorquePtr fac_ftAB = FactorBase::emplace<FactorForceTorque>(FFTAB_, FFTAB_, KFA_, CIKA_->getSensorIntrinsic(), nullptr, false); - - } -}; - - -class FactorInertialKinematics_2KF_foot1turned : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - cout << "\n\n\nFactorInertialKinematics_2KF_foot1turned" << endl; - FactorInertialKinematics_2KF::SetUp(); - problem_->print(3,false,true,true); - Vector4d WqL; WqL.setRandom(); WqL.normalize(); // random unitary quaternion - Quaterniond quat_WqL(WqL.data()); - Matrix<double, 14, 1> kin_meas; kin_meas << zero3, zero3, WqL, 0,0,0,1; - Vector3d f1 = -mass_*wolf::gravity()/2; - f1 = quat_WqL.conjugate() * f1; // Rotate force meas accordingly - Vector3d f2 = -mass_*wolf::gravity()/2; - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setKinMeas(kin_meas); - FFTAB_->setForcesMeas(fmeas); - // problem_->print(3,false,true,true); - } -}; - -class FactorInertialKinematics_2KF_foot2turned : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector4d WqL; WqL.setRandom(); WqL.normalize(); // random unitary quaternion - Quaterniond quat_WqL(WqL.data()); - Matrix<double, 14, 1> kin_meas; kin_meas << zero3, zero3, 0,0,0,1, WqL; - Vector3d f1 = -mass_*wolf::gravity()/2; - Vector3d f2 = -mass_*wolf::gravity()/2; - f2 = quat_WqL.conjugate() * f2; // Rotate force meas accordingly - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setKinMeas(kin_meas); - FFTAB_->setForcesMeas(fmeas); - } -}; - -class FactorInertialKinematics_2KF_singleContactFoot2turned : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector4d WqL; WqL.setRandom(); WqL.normalize(); // random unitary quaternion - Quaterniond quat_WqL(WqL.data()); - Matrix<double, 14, 1> kin_meas; kin_meas << zero3, zero3, WqL, 0,0,0,1; - Vector3d f1 = -mass_*wolf::gravity(); - f1 = quat_WqL.conjugate() * f1; // Rotate force meas accordingly - Vector3d f2 = zero3; - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setKinMeas(kin_meas); - FFTAB_->setForcesMeas(fmeas); - } -}; - -class FactorInertialKinematics_2KF_singleContactFoot1turned : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector4d WqL; WqL.setRandom(); WqL.normalize(); // random unitary quaternion - Quaterniond quat_WqL(WqL.data()); - Matrix<double, 14, 1> kin_meas; kin_meas << zero3, zero3, 0,0,0,1, WqL; - Vector3d f1 = zero3; - Vector3d f2 = -mass_*wolf::gravity(); - f2 = quat_WqL.conjugate() * f2; // Rotate force meas accordingly - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setKinMeas(kin_meas); - FFTAB_->setForcesMeas(fmeas); - } -}; - - - - -class FactorInertialKinematics_2KF_ForceX : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector3d f1 = -mass_*wolf::gravity()/2 + ((Vector3d()<<mass_*Acc1/2,0,0).finished()); - Vector3d f2 = -mass_*wolf::gravity()/2 + ((Vector3d()<<mass_*Acc1/2,0,0).finished()); - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setForcesMeas(fmeas); - } -}; - -class FactorInertialKinematics_2KF_ForceY : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector3d f1 = -mass_*wolf::gravity()/2 + ((Vector3d()<<0,mass_*Acc2/2,0).finished()); - Vector3d f2 = -mass_*wolf::gravity()/2 + ((Vector3d()<<0,mass_*Acc2/2,0).finished()); - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setForcesMeas(fmeas); - } -}; - -class FactorInertialKinematics_2KF_ForceZ : public FactorInertialKinematics_2KF -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF::SetUp(); - Vector3d f1 = -mass_*wolf::gravity()/2 + ((Vector3d()<<0,0,mass_*Acc1/2).finished()); - Vector3d f2 = -mass_*wolf::gravity()/2 + ((Vector3d()<<0,0,mass_*Acc1/2).finished()); - Vector6d fmeas; fmeas << f1, f2; - FFTAB_->setForcesMeas(fmeas); - } -}; - -class FactorInertialKinematics_3KF_ForceX : public FactorInertialKinematics_2KF_ForceX -{ - public: - FrameBasePtr KFC_; - CaptureInertialKinematicsPtr CIKC_; - TimeStamp tC_; - - protected: - void SetUp() override - { - FactorInertialKinematics_2KF_ForceX::SetUp(); - tC_.set(2); - - // KFB_->getStateBlock("O")->unfix(); - - KFC_ = createKFWithCDLI(problem_, tC_, x_origin_, - zero3, zero3, zero3, bias_imu_); - // Fix the one we cannot estimate - // KFB_->getP()->fix(); - // KFC_->getO()->fix(); // Not in the FT factor, so should be fixed (or absolute factor) - // KFB_->getV()->fix(); - KFC_->getStateBlock('I')->fix(); - - // ================ FACTOR INERTIAL KINEMATICS ON KFB - Vector3d pBC_measC = zero3; - Vector3d vBC_measC = zero3; - Vector3d w_measC = zero3; - Vector9d meas_ikinC; meas_ikinC << pBC_measC, vBC_measC, w_measC; - Matrix3d Iq; Iq.setIdentity(); - Vector3d Lq = zero3; - Eigen::Matrix9d Q_ikin_errC = computeKinCov(Qp_, Qv_, Qw_, meas_ikinC.head<3>()-bias_p_, meas_ikinC.tail<3>()-bias_imu_.tail<3>(), Iq); - - CIKC_ = CaptureBase::emplace<CaptureInertialKinematics>(KFC_, tC_, SIK_, meas_ikinC, Iq, Lq, bias_p_); - CIKC_->getStateBlock('I')->fix(); // IK bias - auto feat_ikin_C = FeatureBase::emplace<FeatureInertialKinematics>(CIKC_, meas_ikinC, Q_ikin_errC, Iq, Lq, FeatureBase::UncertaintyType::UNCERTAINTY_IS_COVARIANCE); - FactorBase::emplace<FactorInertialKinematics>(feat_ikin_C, feat_ikin_C, CIKC_->getStateBlock('I'), nullptr, false); - - - // FORCE TORQUE FACTOR BETWEEEN KFA AND KFB - Vector3d f1; f1 << -mass_*wolf::gravity()/2; // Only gravity -> static robot on both feet - Vector3d tau1; tau1 << 0,0,0; - Vector3d pbl1; pbl1 << 0,0,0; - Vector4d bql1; bql1 << 0,0,0,1; - Vector3d f2; f2 << -mass_*wolf::gravity()/2; // Only gravity -> static robot on both feet - Vector3d tau2; tau2 << 0,0,0; - Vector3d pbl2; pbl2 << 0,0,0; - Vector4d bql2; bql2 << 0,0,0,1; - Vector3d pbc; pbc << 0,0,0; - // aggregated meas - Vector6d f_meas; f_meas << f1, f2; - Vector6d tau_meas; tau_meas << tau1, tau2; - Matrix<double,14,1> kin_meas; kin_meas << pbl1, pbl2, bql1, bql2; - - Matrix6d cov_f = 1e-4 * Matrix6d::Identity(); - Matrix6d cov_tau = 1e-4 * Matrix6d::Identity(); - Matrix3d cov_pbc = 1e-4 * Matrix3d::Identity(); - - CaptureBasePtr cap_ftC = CaptureBase::emplace<CaptureBase>(KFC_, "CaptureForceTorque", tC_, nullptr); - auto feat_ftBC = FeatureBase::emplace<FeatureForceTorque>(cap_ftC, - tC_ - tB_, mass_, - f_meas, tau_meas, pbc, kin_meas, - cov_f, cov_tau, cov_pbc); - FactorForceTorquePtr fac_ftBC = FactorBase::emplace<FactorForceTorque>(feat_ftBC, feat_ftBC, KFB_, CIKB_->getSensorIntrinsic(), nullptr, false); - } -}; - -class FactorInertialKinematics_2KF_ForceX_Odom3d : public FactorInertialKinematics_2KF_ForceX -{ - protected: - void SetUp() override - { - FactorInertialKinematics_2KF_ForceX::SetUp(); - - Vector7d pose_A_B; pose_A_B << (tB_-tA_)*Acc1/2,0,0, 0,0,0,1; - Matrix6d rel_pose_cov = Matrix6d::Identity(); - rel_pose_cov.topLeftCorner(3, 3) *= pow(1e-3, 2); - rel_pose_cov.bottomRightCorner(3, 3) *= pow(M_TORAD*1e-3, 2); - - CaptureBasePtr cap_pose_base = CaptureBase::emplace<CapturePose>(KFB_, tB_, nullptr, pose_A_B, rel_pose_cov); - FeatureBasePtr ftr_pose_base = FeatureBase::emplace<FeaturePose>(cap_pose_base, pose_A_B, rel_pose_cov); - FactorBase::emplace<FactorRelativePose3d>(ftr_pose_base, ftr_pose_base, KFA_, nullptr, false, TOP_MOTION); - - // With Odom3d the bias on relative base COM position is observable, we unfix the KFB state block - CIKB_->getStateBlock('I')->unfix(); - // However this test is not sufficient to observe the bias at KFA - // CIKA_->getStateBlock('I')->unfix(); // this is not observable - } -}; - - - - - -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// -// =============== TEST FONCTIONS ====================== -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// - - -TEST_F(FactorInertialKinematics_2KF,ZeroMvt) -{ - Vector3d c_exp = zero3; - Vector3d cd_exp = zero3; - Vector3d Lc_exp = zero3; - Vector3d bp_exp = zero3; - - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); // due to opposite - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), bp_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), bp_exp, 1e-5); -} - -TEST_F(FactorInertialKinematics_2KF_foot1turned,ZeroMvt) -{ - Vector3d c_exp = zero3; - Vector3d cd_exp = zero3; - Vector3d Lc_exp = zero3; - Vector3d bp_exp = zero3; - - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); // due to opposite - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), bp_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), bp_exp, 1e-5); -} - -TEST_F(FactorInertialKinematics_2KF_foot2turned,ZeroMvt) -{ - Vector3d c_exp = zero3; - Vector3d cd_exp = zero3; - Vector3d Lc_exp = zero3; - Vector3d bp_exp = zero3; - - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); // due to opposite - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), bp_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), bp_exp, 1e-5); -} - - -TEST_F(FactorInertialKinematics_2KF_singleContactFoot1turned,ZeroMvt) -{ - Vector3d c_exp = zero3; - Vector3d cd_exp = zero3; - Vector3d Lc_exp = zero3; - Vector3d bp_exp = zero3; - - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); // due to opposite - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), bp_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), bp_exp, 1e-5); -} - - -TEST_F(FactorInertialKinematics_2KF_singleContactFoot2turned,ZeroMvt) -{ - Vector3d c_exp = zero3; - Vector3d cd_exp = zero3; - Vector3d Lc_exp = zero3; - Vector3d bp_exp = zero3; - - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), bp_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), c_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), cd_exp, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), Lc_exp, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), bp_exp, 1e-5); -} - - - -TEST_F(FactorInertialKinematics_2KF_ForceX,ZeroMvt) -{ - // problem_->print(4,true,true,true); - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), (Vector3d()<<(tB_-tA_)*Acc1/2, 0, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), (Vector3d()<<(tB_-tA_)*Acc1, 0, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), zero3, 1e-5); -} - - -TEST_F(FactorInertialKinematics_2KF_ForceY,ZeroMvt) -{ - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), (Vector3d()<<0, (tB_ - tA_)*Acc2/2, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), (Vector3d()<<0, (tB_ - tA_)*Acc2, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), zero3, 1e-5); -} - - -TEST_F(FactorInertialKinematics_2KF_ForceZ,ZeroMvt) -{ - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), (Vector3d()<<0, 0, (tB_-tA_)*Acc1/2).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), (Vector3d()<<0, 0, (tB_-tA_)*Acc1).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), zero3, 1e-5); -} - -// TEST_F(FactorInertialKinematics_3KF_ForceX,ZeroMvt) -// { -// string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - -// perturbateAllIfUnFixed(KFA_); -// perturbateAllIfUnFixed(KFB_); -// perturbateAllIfUnFixed(KFC_); - -// // problem_->print(4,true,true,true); -// report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; -// // std::cout << report << std::endl; -// // problem_->print(4,true,true,true); - -// ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); -// // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); -// ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); -// ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), (Vector3d()<<(tB_-tA_)*Acc1/2, 0, 0).finished(), 1e-5); -// ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), (Vector3d()<<(tB_-tA_)*Acc1, 0, 0).finished(), 1e-5); -// ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(KFC_->getStateBlock('C')->getState(), (Vector3d()<<(tC_-tB_)*Acc1/2 + (tB_-tA_)*Acc1, 0, 0).finished(), 1e-5); // + -> due to initial vel of KFB -// ASSERT_MATRIX_APPROX(KFC_->getStateBlock('D')->getState(), (Vector3d()<<(tC_-tB_)*Acc1, 0, 0).finished(), 1e-5); // No acc btw B and C -> same vel -// ASSERT_MATRIX_APPROX(KFC_->getStateBlock('L')->getState(), zero3, 1e-5); -// ASSERT_MATRIX_APPROX(CIKC_->getStateBlock('I')->getState(), zero3, 1e-5); -// } - - -TEST_F(FactorInertialKinematics_2KF_ForceX_Odom3d,ZeroMvt) -{ - string report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - - perturbateAllIfUnFixed(KFA_); - perturbateAllIfUnFixed(KFB_); - - // problem_->print(4,true,true,true); - report = solver_->solve(SolverManager::ReportVerbosity::BRIEF); // 0: nothing, 1: BriefReport, 2: FullReport; - // std::cout << report << std::endl; - // problem_->print(4,true,true,true); - - ASSERT_MATRIX_APPROX(KFA_->getP()->getState(), x_origin_.at('P'), 1e-5); - // ASSERT_MATRIX_APPROX(KFA_->getO()->getState(), x_origin_.at('O'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getV()->getState(), x_origin_.at('V'), 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('C')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('D')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFA_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKA_->getStateBlock('I')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('C')->getState(), (Vector3d()<<(tB_-tA_)*Acc1/2, 0, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('D')->getState(), (Vector3d()<<(tB_-tA_)*Acc1, 0, 0).finished(), 1e-5); - ASSERT_MATRIX_APPROX(KFB_->getStateBlock('L')->getState(), zero3, 1e-5); - ASSERT_MATRIX_APPROX(CIKB_->getStateBlock('I')->getState(), zero3, 1e-5); -} - - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/test/gtest_factor_force_torque_inertial.cpp b/test/gtest_factor_force_torque_inertial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72e5f59675df274e4387876ae644c081a61798ab --- /dev/null +++ b/test/gtest_factor_force_torque_inertial.cpp @@ -0,0 +1,136 @@ +//--------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 "bodydynamics/factor/factor_force_torque_inertial.h" +#include "bodydynamics/processor/processor_force_torque_inertial.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" +#include "bodydynamics/internal/config.h" + +#include <core/utils/params_server.h> +#include <core/yaml/parser_yaml.h> + +#include <core/utils/utils_gtest.h> +#include <core/utils/logging.h> + +#include <Eigen/Dense> + +using namespace wolf; +using namespace bodydynamics::fti; + +// Register StateBlock of type "L" +#include <core/state_block/state_block_derived.h> +#include <core/state_block/factory_state_block.h> +WOLF_REGISTER_STATEBLOCK_WITH_KEY(L, StateVector3d); + +class Test_FactorForceTorqueInertialPreint : public testing::Test +{ + public: + ProblemPtr problem; + SensorForceTorqueInertialPtr sensor; + ProcessorForceTorqueInertialPtr processor; + FrameBasePtr frame_origin, frame_last, frame; + CaptureMotionPtr capture_origin, capture_last, capture; + FeatureMotionPtr feature; + FactorForceTorqueInertialPtr factor; + + VectorXd data; + MatrixXd data_cov; + VectorXd delta; + MatrixXd delta_cov; + Vector6d bias; + MatrixXd J_delta_bias; + + protected: + void SetUp() override + { + data = VectorXd::Random(12); // [ a, w, f, t ] + data_cov = MatrixXd::Identity(12, 12); + + std::string wolf_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + ParserYaml parser = ParserYaml("problem_force_torque_inertial.yaml", wolf_root + "/test/yaml/"); + ParamsServer server = ParamsServer(parser.getParams()); + problem = Problem::autoSetup(server); + + sensor = std::static_pointer_cast<SensorForceTorqueInertial>(problem->getHardware()->getSensorList().front()); + processor = std::static_pointer_cast<ProcessorForceTorqueInertial>(sensor->getProcessorList().front()); + + problem->print(4, 1, 1, 1); + + capture = std::make_shared<CaptureMotion>("CaptureForceTorqueInertial", 0.0, sensor, data, data_cov, nullptr); + capture->process(); + + problem->print(4, 1, 1, 1); + + capture_origin = std::static_pointer_cast<CaptureMotion>(processor->getOrigin()); + frame_origin = capture_origin->getFrame(); + + ASSERT_EQ(capture_origin->getTimeStamp(), frame_origin->getTimeStamp()); + ASSERT_EQ(capture_origin->getTimeStamp(), frame_origin->getCaptureOf(sensor)->getTimeStamp()); + + capture = std::make_shared<CaptureMotion>("CaptureForceTorqueInertial", 0.1, sensor, data, data_cov, nullptr); + capture->process(); + + capture_last = std::static_pointer_cast<CaptureMotion>(processor->getLast()); + frame_last = capture_last->getFrame(); + } +}; + +TEST(Dummy, force_load_libwolfbodydynamics_so_at_launch_time) +{ + ParamsSensorForceTorqueInertialPtr p = std::make_shared<ParamsSensorForceTorqueInertial>(); + p->acc_noise_std = 1; + p->force_noise_std = 1; + p->gyro_noise_std = 1; + p->torque_noise_std = 1; + SensorForceTorqueInertial s((Vector7d() << 1, 2, 3, 0, 0, 0, 1).finished(), p); +} + +TEST_F(Test_FactorForceTorqueInertialPreint, constructor) +{ + feature = FeatureMotion::emplace<FeatureMotion>(capture_last, "FeatureFTI", VectorXd(19), + MatrixXd::Identity(18, 18), VectorXd(6), MatrixXd(18, 6)); + FactorForceTorqueInertial f(feature, capture_origin, processor, false); + + WOLF_INFO("f id: ", f.id()); + + ASSERT_EQ(f.getCaptureOther(), capture_origin); +} + +TEST_F(Test_FactorForceTorqueInertialPreint, emplace) +{ + feature = FeatureMotion::emplace<FeatureMotion>(capture_last, "FeatureFTI", VectorXd(19), + MatrixXd::Identity(18, 18), VectorXd(6), MatrixXd(18, 6)); + factor = FactorBase::emplace<FactorForceTorqueInertial>(feature, feature, capture_origin, processor, false); + // this ^^^ will create a warning "ADDING FACTOR id TO FEATURE id THAT IS NOT CONNECTED WITH PROBLEM." + + problem->print(4, 1, 1, 1); // feature and factor not in the tree since they belong to last_ptr with no KF. + + ASSERT_EQ(factor->getCaptureOther(), capture_origin); + ASSERT_EQ(factor->getCapture(), capture_last); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_factor_force_torque_inertial_dynamics.cpp b/test/gtest_factor_force_torque_inertial_dynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..846807390641df274b1b0d9cf8542183e6948067 --- /dev/null +++ b/test/gtest_factor_force_torque_inertial_dynamics.cpp @@ -0,0 +1,357 @@ +//--------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 "bodydynamics/factor/factor_force_torque_inertial_dynamics.h" +#include "bodydynamics/processor/processor_force_torque_inertial_dynamics.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" +#include "bodydynamics/internal/config.h" + +#include <core/sensor/factory_sensor.h> + +#include <core/utils/utils_gtest.h> +#include <core/utils/logging.h> +#include <core/utils/params_server.h> +#include <core/yaml/parser_yaml.h> +#include <core/state_block/state_block_derived.h> +#include <core/state_block/factory_state_block.h> + +#include <Eigen/Dense> +#include <Eigen/Geometry> + +using namespace wolf; +using namespace bodydynamics::fti; + +WOLF_REGISTER_STATEBLOCK_WITH_KEY(L, StateVector3d); + +class Test_FactorForceTorqueInertialDynamics_yaml : public testing::Test +{ + public: + ProblemPtr P; + SensorForceTorqueInertialPtr S; + ProcessorForceTorqueInertialDynamicsPtr p; + FrameBasePtr F0, F1, F; + CaptureMotionPtr C0, C1, C; + FeatureMotionPtr f1; + FactorForceTorqueInertialDynamicsPtr c1; + + VectorXd data; + MatrixXd data_cov; + VectorXd delta; + MatrixXd delta_cov; + Matrix<double, 13, 1> calib; + MatrixXd J_delta_calib; + + protected: + void SetUp() override + { + std::string wolf_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + ParserYaml parser = ParserYaml("problem_force_torque_inertial_dynamics.yaml", wolf_root + "/test/yaml/"); + ParamsServer server = ParamsServer(parser.getParams()); + P = Problem::autoSetup(server); + + S = std::static_pointer_cast<SensorForceTorqueInertial>(P->getHardware()->getSensorList().front()); + p = std::static_pointer_cast<ProcessorForceTorqueInertialDynamics>(S->getProcessorList().front()); + + data = VectorXd::Zero(12); // [ a, w, f, t ] + data_cov = MatrixXd::Identity(12, 12); + C = std::make_shared<CaptureMotion>("CaptureMotion", 0.0, S, data, data_cov, nullptr); + + C->process(); + + C0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + F0 = C0->getFrame(); + + ASSERT_EQ(C0->getTimeStamp(), F0->getTimeStamp()); + ASSERT_EQ(C0->getTimeStamp(), F0->getCaptureOf(S)->getTimeStamp()); + + C = std::make_shared<CaptureMotion>("CaptureMotion", 1.0, S, data, data_cov, nullptr); + C->process(); + + C1 = std::static_pointer_cast<CaptureMotion>(p->getLast()); + F1 = C1->getFrame(); + + F1->link(P); // by the face + + VectorXd delta_preint(VectorXd::Zero(19)); + delta_preint.head<3>() = -0.5*gravity(); + delta_preint.segment<3>(3) = -gravity(); + delta_preint.segment<3>(6) = -0.5*gravity(); + delta_preint.segment<3>(9) = -gravity(); + delta_preint(18) = 1; + MatrixXd delta_preint_cov(MatrixXd::Identity(18, 18)); + VectorXd calib_preint(VectorXd::Zero(13)); + MatrixXd jacobian_calib(MatrixXd::Zero(18, 13)); + f1 = FeatureBase::emplace<FeatureMotion>(C1, "FeatureMotion", delta_preint, delta_preint_cov, calib_preint, + jacobian_calib); + + c1 = FactorBase::emplace<FactorForceTorqueInertialDynamics>(f1, f1, C0, p, false); + + F1->getStateBlock('P')->setState(Vector3d(0,0,0)); + F1->getStateBlock('V')->setState(Vector3d(0,0,0)); + F1->getStateBlock('O')->setState(Vector4d(0,0,0,1)); + F1->getStateBlock('L')->setState(Vector3d(0,0,0)); + + } + +}; + + +class Test_FactorForceTorqueInertialDynamics : public testing::Test +{ + public: + ProblemPtr P; + SensorForceTorqueInertialPtr S; + ProcessorForceTorqueInertialDynamicsPtr p; + FrameBasePtr F0, F1; + CaptureMotionPtr C0, C1; + FeatureMotionPtr f1; + FactorForceTorqueInertialDynamicsPtr c1; + + VectorXd data; + MatrixXd data_cov; + VectorXd delta; + MatrixXd delta_cov; + Matrix<double, 13, 1> calib; + MatrixXd J_delta_calib; + + protected: + void SetUp() override + { + data = VectorXd::Zero(12); // [ a, w, f, t ] + data_cov = MatrixXd::Identity(12, 12); + + // crear un problem + P = Problem::create("POVL", 3); + + // crear un sensor + auto params_sensor = std::make_shared<ParamsSensorForceTorqueInertial>(); + Vector7d extrinsics; + extrinsics << 0, 0, 0, 0, 0, 0, 1; + S = SensorBase::emplace<SensorForceTorqueInertial>(P->getHardware(), extrinsics, params_sensor); + S->setStateBlockDynamic('I', true); + + // crear processador + auto params_processor = std::make_shared<ParamsProcessorForceTorqueInertialDynamics>(); + p = ProcessorBase::emplace<ProcessorForceTorqueInertialDynamics>(S, params_processor); + + // crear dos frame + VectorXd state(13); + state << 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0; + VectorComposite state_c(state, "POVL", {3, 4, 3, 3}); + F0 = FrameBase::emplace<FrameBase>(P->getTrajectory(), 0.0, "POVL", state_c); + F1 = FrameBase::emplace<FrameBase>(P->getTrajectory(), 1.0, "POVL", state_c); + + // crear dues capture + VectorXd data(VectorXd::Zero(12)); + MatrixXd data_cov(MatrixXd::Identity(12, 12)); + Vector6d bias(Vector6d::Zero()); + auto sb_bias = std::make_shared<StateParams6>(bias); + C0 = CaptureBase::emplace<CaptureMotion>(F0, "CaptureMotion", 0.0, S, data, data_cov, nullptr, nullptr, + nullptr, sb_bias); + C1 = CaptureBase::emplace<CaptureMotion>(F1, "CaptureMotion", 1.0, S, data, data_cov, C0, nullptr, nullptr, + sb_bias); + + // crear un feature + VectorXd delta_preint(VectorXd::Zero(19)); + delta_preint.head<3>() = -0.5*gravity(); + delta_preint.segment<3>(3) = -gravity(); + delta_preint.segment<3>(6) = -0.5*gravity(); + delta_preint.segment<3>(9) = -gravity(); + delta_preint(18) = 1; + MatrixXd delta_preint_cov(MatrixXd::Identity(18, 18)); + VectorXd calib_preint(VectorXd::Zero(13)); + MatrixXd jacobian_calib(MatrixXd::Zero(18, 13)); + f1 = FeatureBase::emplace<FeatureMotion>(C1, "FeatureMotion", delta_preint, delta_preint_cov, calib_preint, + jacobian_calib); + + // crear un factor + c1 = FactorBase::emplace<FactorForceTorqueInertialDynamics>(f1, f1, C0, p, false); + } +}; + +// TEST(FactorForceTorqueInertialDynamics_yaml, force_registraion) +// { +// FactorySensor::registerCreator("SensorForceTorqueInertial", SensorForceTorqueInertial::create); +// } + +//Test to see if the constructor works (not yaml files) +TEST_F(Test_FactorForceTorqueInertialDynamics, constructor) +{ + ASSERT_EQ(c1->getCapture(), C1); + ASSERT_EQ(c1->getCalibPreint().size(), 13); +} + +//Test to see if the constructor works (yaml files) +TEST_F(Test_FactorForceTorqueInertialDynamics_yaml, constructor) +{ + ASSERT_EQ(c1->getCapture(), C1); + ASSERT_EQ(c1->getCalibPreint().size(), 13); +} + +//Test en el que no hi ha moviment (not yaml files) +TEST_F(Test_FactorForceTorqueInertialDynamics, residual) +{ + VectorXd res_exp = VectorXd::Zero(18); + Matrix<double, 18, 1> res; + VectorXd bias = VectorXd::Zero(6); + + P->print(4,1,1,1); + + c1->residual(F0->getStateBlock('P')->getState(), // p0 + Quaterniond(F0->getStateBlock('O')->getState().data()), // q0 + F0->getStateBlock('V')->getState(), // v0 + F0->getStateBlock('L')->getState(), // L0 + bias, // I + F1->getStateBlock('P')->getState(), // p1 + Quaterniond(F1->getStateBlock('O')->getState().data()), // q1 + F1->getStateBlock('V')->getState(), // v1 + F1->getStateBlock('L')->getState(), // L1 + S->getCom(), // C + S->getInertia(), // i + S->getMass(), // m + res); + + ASSERT_MATRIX_APPROX(res_exp, res, 1e-8); +} + +//Test en el que no hi ha moviment (yaml files) +TEST_F(Test_FactorForceTorqueInertialDynamics_yaml, residual) +{ + VectorXd res_exp = VectorXd::Zero(18); + Matrix<double, 18, 1> res; + VectorXd bias = VectorXd::Zero(6); + + c1->residual(F0->getStateBlock('P')->getState(), // p0 + Quaterniond(F0->getStateBlock('O')->getState().data()), // q0 + F0->getStateBlock('V')->getState(), // v0 + F0->getStateBlock('L')->getState(), // L0 + bias, // I + F1->getStateBlock('P')->getState(), // p1 + Quaterniond(F1->getStateBlock('O')->getState().data()), // q1 + F1->getStateBlock('V')->getState(), // v1 + F1->getStateBlock('L')->getState(), // L1 + S->getCom(), // C + S->getInertia(), // i + S->getMass(), // m + res); + + ASSERT_MATRIX_APPROX(res_exp, res, 1e-8); +} + + +//Test en el que s'avança 1m en direcció x (not yaml files) +TEST_F(Test_FactorForceTorqueInertialDynamics, residualx) +{ + VectorXd res_exp = VectorXd::Zero(18); + Matrix<double, 18, 1> res; + VectorXd bias = VectorXd::Zero(6); + + //provem el moviment de P=(0,0,0) a P=(1,0,0) i v, o i L queden igual + F1->getStateBlock('P')->setState(Vector3d (1,0,0)); + + //Hem de crear un nou feature i un nou factor ja que la delta preint canvia. + VectorXd delta_preint(VectorXd::Zero(19)); + delta_preint.head<3>() = -0.5*gravity(); + delta_preint(0) = 1; + delta_preint.segment<3>(3) = -gravity(); + delta_preint.segment<3>(6) = -0.5*gravity(); + delta_preint(6) = 1; + delta_preint.segment<3>(9) = -gravity(); + delta_preint(18) = 1; + MatrixXd delta_preint_cov(MatrixXd::Identity(18, 18)); + VectorXd calib_preint(VectorXd::Zero(13)); + MatrixXd jacobian_calib(MatrixXd::Zero(18, 13)); + FeatureMotionPtr f2 = FeatureBase::emplace<FeatureMotion>(C1, "FeatureMotion", delta_preint, delta_preint_cov, calib_preint,jacobian_calib); + + FactorForceTorqueInertialDynamicsPtr c2 = FactorBase::emplace<FactorForceTorqueInertialDynamics>(f2, f2, C0, p, false); + + + c2->residual(F0->getStateBlock('P')->getState(), // p0 + Quaterniond(F0->getStateBlock('O')->getState().data()), // q0 + F0->getStateBlock('V')->getState(), // v0 + F0->getStateBlock('L')->getState(), // L0 + bias, // I + F1->getStateBlock('P')->getState(), // p1 + Quaterniond(F1->getStateBlock('O')->getState().data()), // q1 + F1->getStateBlock('V')->getState(), // v1 + F1->getStateBlock('L')->getState(), // L1 + S->getCom(), // C + S->getInertia(), // i + S->getMass(), // m + res); + + ASSERT_MATRIX_APPROX(res_exp, res, 1e-8); +} + +//Test en el que s'avança 1m en direcció x (fitxers yaml) + +TEST_F(Test_FactorForceTorqueInertialDynamics_yaml, residualx) +{ + VectorXd res_exp = VectorXd::Zero(18); + Matrix<double, 18, 1> res; + VectorXd bias = VectorXd::Zero(6); + + //provem el moviment de P=(0,0,0) a P=(1,0,0) i v, o i L queden igual + F1->getStateBlock('P')->setState(Vector3d (1,0,0)); + + //Hem de crear un nou feature i un nou factor ja que la delta preint canvia. + VectorXd delta_preint(VectorXd::Zero(19)); + delta_preint.head<3>() = -0.5*gravity(); + delta_preint(0) = 1; + delta_preint.segment<3>(3) = -gravity(); + delta_preint.segment<3>(6) = -0.5*gravity(); + delta_preint(6) = 1; + delta_preint.segment<3>(9) = -gravity(); + delta_preint(18) = 1; + MatrixXd delta_preint_cov(MatrixXd::Identity(18, 18)); + VectorXd calib_preint(VectorXd::Zero(13)); + MatrixXd jacobian_calib(MatrixXd::Zero(18, 13)); + FeatureMotionPtr f2 = FeatureBase::emplace<FeatureMotion>(C1, "FeatureMotion", delta_preint, delta_preint_cov, calib_preint,jacobian_calib); + + FactorForceTorqueInertialDynamicsPtr c2 = FactorBase::emplace<FactorForceTorqueInertialDynamics>(f2, f2, C0, p, false); + + + c2->residual(F0->getStateBlock('P')->getState(), // p0 + Quaterniond(F0->getStateBlock('O')->getState().data()), // q0 + F0->getStateBlock('V')->getState(), // v0 + F0->getStateBlock('L')->getState(), // L0 + bias, // I + F1->getStateBlock('P')->getState(), // p1 + Quaterniond(F1->getStateBlock('O')->getState().data()), // q1 + F1->getStateBlock('V')->getState(), // v1 + F1->getStateBlock('L')->getState(), // L1 + S->getCom(), // C + S->getInertia(), // i + S->getMass(), // m + res); + + ASSERT_MATRIX_APPROX(res_exp, res, 1e-8); +} + + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + //::testing::GTEST_FLAG(filter) = "Test_FactorForceTorqueInertialDynamics_yaml.residual"; + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/test/gtest_force_torque_inertial_delta_tools.cpp b/test/gtest_force_torque_inertial_delta_tools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58152170e01188454a1793a0958d087878680dde --- /dev/null +++ b/test/gtest_force_torque_inertial_delta_tools.cpp @@ -0,0 +1,691 @@ +//--------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-------- +/* + * gtest_force_torque_inertial_tools.cpp + * + * Created on: Aug 8, 2021 + * Author: jsola + */ + +#include "bodydynamics/math/force_torque_inertial_delta_tools.h" +#include <core/utils/utils_gtest.h> + +#include <iostream> +#include <iomanip> + +using namespace Eigen; +using namespace wolf; +using namespace bodydynamics; +using namespace fti; + +TEST(FTI_tools, identity) +{ + VectorXd id_true; + id_true.setZero(19); + id_true(18) = 1.0; + + VectorXd id = identity<double>(); + ASSERT_MATRIX_APPROX(id, id_true, 1e-10); +} + +TEST(FTI_tools, identity_split) +{ + VectorXd pi(3), vi(3), pd(3), vd(3), L(3), qv(4); + Quaterniond q; + + identity(pi, vi, pd, vd, L, q); + ASSERT_MATRIX_APPROX(pi, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(vi, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(pd, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(vd, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(L, Vector3d::Zero(), 1e-10); + ASSERT_QUATERNION_APPROX(q, Quaterniond::Identity(), 1e-10); + + identity(pi, vi, pd, vd, L, qv); + ASSERT_MATRIX_APPROX(pi, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(vi, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(pd, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(vd, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(L, Vector3d::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(qv, (Vector4d() << 0, 0, 0, 1).finished(), 1e-10); +} + +TEST(FTI_tools, inverse) +{ + VectorXd d(19), id(19), iid(19), iiid(19), I(19); + Vector4d qv; + double dt = 0.1; + + qv = (Vector4d() << 9, 10, 11, 12).finished().normalized(); + d << 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, qv; + + id = inverse(d, dt); + + compose(id, d, dt, I); + ASSERT_MATRIX_APPROX(I, identity(), 1e-10); + compose(d, id, -dt, I); // Observe -dt is reversed !! + ASSERT_MATRIX_APPROX(I, identity(), 1e-10); + + inverse(id, -dt, iid); // Observe -dt is reversed !! + ASSERT_MATRIX_APPROX(d, iid, 1e-10); + iiid = inverse(iid, dt); + ASSERT_MATRIX_APPROX(id, iiid, 1e-10); +} + +TEST(FTI_tools, compose_between) +{ + VectorXd dx1(19), dx2(19), dx3(19); + Matrix<double, 19, 1> d1, d2, d3; + double dt = 0.1; + + dx1 << VectorXd::Random(15), Vector4d::Random().normalized(); + d1 = dx1; + dx2 << VectorXd::Random(15), Vector4d::Random().normalized(); + d2 = dx2; + + // combinations of templates for sum() + compose(dx1, dx2, dt, dx3); + compose(d1, d2, dt, d3); + ASSERT_MATRIX_APPROX(d3, dx3, 1e-10); + + compose(d1, dx2, dt, dx3); + d3 = compose(dx1, d2, dt); + ASSERT_MATRIX_APPROX(d3, dx3, 1e-10); + + // // minus(d1, d3) should recover delta_2 + Matrix<double, 19, 1> bet; + between(d1, d3, dt, bet); + ASSERT_MATRIX_APPROX(bet, d2, 1e-10); + + // minus(d3, d1, -dt) should recover inverse(d2, dt) + bet = between(d3, d1, -dt); + ASSERT_MATRIX_APPROX(bet, inverse(d2, dt), 1e-10); +}; + +TEST(FTI_tools, compose_between_with_state) +{ + VectorXd x(13), d(19), x2(13), x3(13), d2(19), d3(19); + double dt = 0.1; + + x << VectorXd::Random(9), Vector4d::Random().normalized(); + d << VectorXd::Random(15), Vector4d::Random().normalized(); + + { + // 1. compose over state with IMU delta + composeOverState(x, d, dt, x2); + x3 = composeOverState(x, d, dt); + ASSERT_MATRIX_APPROX(x3, x2, 1e-10); + + // betweenStates(x, x2) should recover d + betweenStates(x, x2, dt, d2); + d3 = betweenStates(x, x2, dt); + ASSERT_MATRIX_APPROX(d2, d3, 1e-10); + VectorComposite dc(d, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorComposite d2c(d2, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorComposite d3c(d3, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorXd de = dc.vector("PVLO"); + VectorXd d2e = d2c.vector("PVLO"); + VectorXd d3e = d3c.vector("PVLO"); + ASSERT_MATRIX_APPROX(d2c.vector("PVLO"), dc.vector("PVLO"), 1e-10); + ASSERT_MATRIX_APPROX(d3c.vector("PVLO"), dc.vector("PVLO"), 1e-10); + } + { + // 2. compose over state with FT delta + composeOverState(x, d, dt, x2, fti::FT); + x3 = composeOverState(x, d, dt, fti::FT); + ASSERT_MATRIX_APPROX(x3, x2, 1e-10); + + // betweenStates(x, x2) should recover d + betweenStates(x, x2, dt, d2); + d3 = betweenStates(x, x2, dt); + ASSERT_MATRIX_APPROX(d2, d3, 1e-10); + VectorComposite dc(d, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorComposite d2c(d2, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorComposite d3c(d3, "PVpvLO", {3, 3, 3, 3, 3, 4}); + VectorXd de = dc.vector("pvLO"); + VectorXd d2e = d2c.vector("pvLO"); + VectorXd d3e = d3c.vector("pvLO"); + ASSERT_MATRIX_APPROX(d2c.vector("pvLO"), dc.vector("pvLO"), 1e-10); + ASSERT_MATRIX_APPROX(d3c.vector("pvLO"), dc.vector("pvLO"), 1e-10); + } + + composeOverState(x, d, dt, x2); + VectorComposite ddc(d, "PVpvLO", {3, 3, 3, 3, 3, 4}); + ddc.at('p') = ddc.at('P'); + ddc.at('v') = ddc.at('V'); + ASSERT_MATRIX_APPROX(betweenStates(x, x2, dt), ddc.vector("PVpvLO"), 1e-10); + + // x + (x2 - x) = x2 + ASSERT_MATRIX_APPROX(composeOverState(x, betweenStates(x, x2, dt), dt), x2, 1e-10); +} + +TEST(FTI_tools, compose_between_with_state_composite) +{ + double dt = 0.1; + + VectorComposite x(VectorXd::Random(13), "PVLO", {3,3,3,4}); + x.at('O').normalize(); + VectorComposite d(VectorXd::Random(19), "PVpvLO", {3,3,3,3,3,4}); + d.at('O').normalize(); + + VectorComposite xi = composeOverState(x, d, dt, fti::IMU); + VectorComposite xf = composeOverState(x, d, dt, fti::FT); + + ASSERT_MATRIX_APPROX(xi.at('L'), xf.at('L'), 1e-10); + ASSERT_MATRIX_APPROX(xi.at('O'), xf.at('O'), 1e-10); + + // Obtained delta must conform to IMU "PV" + VectorComposite d2 = betweenStates(x, xi, dt); + + ASSERT_MATRIX_APPROX(d2.at('P'), d.at('P'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('V'), d.at('V'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('p'), d.at('P'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('v'), d.at('V'), 1e-10); + + // Obtained delta must conform to FT "pv" + d2 = betweenStates(x, xf, dt); + + ASSERT_MATRIX_APPROX(d2.at('P'), d.at('p'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('V'), d.at('v'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('p'), d.at('p'), 1e-10); + ASSERT_MATRIX_APPROX(d2.at('v'), d.at('v'), 1e-10); +} + +TEST(FTI_tools, exp_log) +{ + VectorXd tangent(18); + tangent << VectorXd::Random(15), .1 * Vector3d::Random(); // use angles in the ball theta < pi + + // transform to delta + VectorXd delta = exp_fti(tangent); + + // expected delta + Vector3d dpi = tangent.segment<3>(0); + Vector3d dvi = tangent.segment<3>(3); + Vector3d dpd = tangent.segment<3>(6); + Vector3d dvd = tangent.segment<3>(9); + Vector3d dL = tangent.segment<3>(12); + Quaterniond dq = v2q(tangent.segment<3>(15)); + VectorXd delta_true(19); + delta_true << dpi, dvi, dpd, dvd, dL, dq.coeffs(); + ASSERT_MATRIX_APPROX(delta, delta_true, 1e-10); + + // transform to a new tangent -- should be the original tangent + VectorXd d_from_delta = log_fti(delta); + ASSERT_MATRIX_APPROX(d_from_delta, tangent, 1e-10); + + // transform to a new delta -- should be the previous delta + VectorXd delta_from_d = exp_fti(d_from_delta); + ASSERT_MATRIX_APPROX(delta_from_d, delta, 1e-10); +} + +TEST(FTI_tools, plus) +{ + VectorXd d1(19), d2(19), d3(19); + VectorXd err(18); + Vector4d qv = Vector4d::Random().normalized(); + d1 << VectorXd::Random(15), qv; + err << Matrix<double, 18, 1>::Random(); + + d3.head<3>() = d1.head<3>() + err.head<3>(); + d3.segment<3>(3) = d1.segment<3>(3) + err.segment<3>(3); + d3.segment<3>(6) = d1.segment<3>(6) + err.segment<3>(6); + d3.segment<3>(9) = d1.segment<3>(9) + err.segment<3>(9); + d3.segment<3>(12) = d1.segment<3>(12) + err.segment<3>(12); + d3.tail<4>() = (Quaterniond(qv.data()) * exp_q(err.tail<3>())).coeffs(); + + plus(d1, err, d2); + ASSERT_MATRIX_APPROX(diff(d3, d2), VectorXd::Zero(18), 1e-10); +} + +TEST(FTI_tools, diff) +{ + VectorXd d1(19), d2(19); + VectorXd err(18); + Vector4d qv = Vector4d::Random().normalized(); + d1 << VectorXd::Random(15), qv; + d2 = d1; + + diff(d1, d2, err); + ASSERT_MATRIX_APPROX(err, VectorXd::Zero(18), 1e-10); + ASSERT_MATRIX_APPROX(diff(d1, d2), VectorXd::Zero(18), 1e-10); + + VectorXd d3(19); + d3.setRandom(); + d3.tail<4>().normalize(); + err.head<3>() = d3.head<3>() - d1.head<3>(); + err.segment<3>(3) = d3.segment<3>(3) - d1.segment<3>(3); + err.segment<3>(6) = d3.segment<3>(6) - d1.segment<3>(6); + err.segment<3>(9) = d3.segment<3>(9) - d1.segment<3>(9); + err.segment<3>(12) = d3.segment<3>(12) - d1.segment<3>(12); + err.tail<3>() = log_q(Quaterniond(d1.data() + 15).conjugate() * Quaterniond(d3.data() + 15)); + + ASSERT_MATRIX_APPROX(err, diff(d1, d3), 1e-10); +} + +TEST(FTI_tools, compose_jacobians) +{ + VectorXd d1(19), d2(19), d3(19), d1_pert(19), d2_pert(19), d3_pert(19), d3_pert_approx(19); // deltas + VectorXd h(18), j_h(18); // tangent perturbation + Matrix<double, 18, 18> J_d3_d1, J_d3_d2, J_d3_d1_num, J_d3_d2_num; + Vector4d qv1, qv2; + double dt = 0.1; + double dx = 1e-6; + qv1 = Vector4d::Random().normalized(); + d1 << VectorXd::Random(15), qv1; + qv2 = Vector4d::Random().normalized(); + d2 << VectorXd::Random(15), qv2; + + // analytical jacobians + compose(d1, d2, dt, d3, J_d3_d1, J_d3_d2); + + ASSERT_MATRIX_APPROX(d3, compose(d1, d2, dt), 1e-12); + + // Check Jacobians using f( x (+) h ) ~ f(x) (+) J_x_d * h + // this implies + // - compose( d1(+)h , d2 ) ~ compose( d1, d2) (+) J_d3_d1*h + // - compose( d1 , d2(+)h ) ~ compose( d1, d2) (+) J_d3_d2*h + + h = dx * VectorXd::Random(18); + + // Jac wrt. d1 + d1_pert = plus(d1, h); + d3_pert = compose(d1_pert, d2, dt); + j_h = J_d3_d1 * h; + d3_pert_approx = plus(d3, j_h); + + ASSERT_MATRIX_APPROX(d3_pert, d3_pert_approx, 1e-8); + + // Jac wrt. d2 + d2_pert = plus(d2, h); + d3_pert = compose(d1, d2_pert, dt); + j_h = J_d3_d2 * h; + d3_pert_approx = plus(d3, j_h); + + ASSERT_MATRIX_APPROX(d3_pert, d3_pert_approx, 1e-12); +} + +TEST(FTI_tools, data2tangent_hover) +{ + Vector3d am(1, 2, 3), wm(1, 2, 3), ab(0, 0, 0), wb(0, 0, 0), fb(0, 0, 0), mb(0, 0, 0), r_bc(0, 0, 0); + + std::list<Propeller> propellers; + propellers.emplace_back(Vector3d(1, 1, 0), Matrix3d::Identity(), 1, 1, 1); // FL + propellers.emplace_back(Vector3d(-1, 1, 0), Matrix3d::Identity(), -1, 1, 1); // RL + propellers.emplace_back(Vector3d(-1, -1, 0), Matrix3d::Identity(), 1, 1, 1); // RR + propellers.emplace_back(Vector3d(1, -1, 0), Matrix3d::Identity(), -1, 1, 1); // FR + + Vector3d a, w, f, m; + + Vector4d n(2, 2, 2, 2); // FL, RL, RR, FR + + data2tangent(am, wm, n, ab, wb, fb, mb, r_bc, propellers, a, w, f, m); + + ASSERT_MATRIX_APPROX(a, am - ab, 1e-12); + ASSERT_MATRIX_APPROX(w, wm - wb, 1e-12); + ASSERT_MATRIX_APPROX(f, Vector3d(0, 0, 16), 1e-12); + ASSERT_MATRIX_APPROX(m, Vector3d::Zero(), 1e-12); +} + +TEST(FTI_tools, data2tangent_yawing) +{ + Vector3d am(1, 2, 3), wm(1, 2, 3), ab(0, 0, 0), wb(0, 0, 0), fb(0, 0, 0), mb(0, 0, 0), r_bc(0, 0, 0); + + std::list<Propeller> propellers; + propellers.emplace_back(Vector3d(1, 1, 0), Matrix3d::Identity(), 1, 1, 1); // FL + propellers.emplace_back(Vector3d(-1, 1, 0), Matrix3d::Identity(), -1, 1, 1); // RL + propellers.emplace_back(Vector3d(-1, -1, 0), Matrix3d::Identity(), 1, 1, 1); // RR + propellers.emplace_back(Vector3d(1, -1, 0), Matrix3d::Identity(), -1, 1, 1); // FR + + Vector3d a, w, f, m; + + Vector4d n(2, 0, 2, 0); // FL, RL, RR, FR + + data2tangent(am, wm, n, ab, wb, fb, mb, r_bc, propellers, a, w, f, m); + + ASSERT_MATRIX_APPROX(a, am - ab, 1e-12); + ASSERT_MATRIX_APPROX(w, wm - wb, 1e-12); + ASSERT_MATRIX_APPROX(f, Vector3d(0, 0, 8), 1e-12); + ASSERT_MATRIX_APPROX(m, Vector3d(0, 0, -8), 1e-12); +} + +TEST(FTI_tools, data2tangent_jacobians) +{ + VectorXd data(10), bias(12), tangent(12); + Vector3d r_bc; + + std::list<Propeller> propellers; + propellers.emplace_back(Vector3d(1, 1, 0), Matrix3d::Identity(), 1, 1, 1); // FL + propellers.emplace_back(Vector3d(-1, 1, 0), Matrix3d::Identity(), -1, 1, 1); // RL + propellers.emplace_back(Vector3d(-1, -1, 0), Matrix3d::Identity(), 1, 1, 1); // RR + propellers.emplace_back(Vector3d(1, -1, 0), Matrix3d::Identity(), -1, 1, 1); // FR + + MatrixXd J_tan_data(12, 10), J_tan_bias(12, 12), J_tan_com(12, 3); + + data.setRandom(); + bias.setRandom(); + r_bc.setRandom(); + + data2tangent(data, bias, r_bc, propellers, tangent, J_tan_data, J_tan_bias, J_tan_com); + + // all jacobians + VectorXd tangent_pert(12), tangent_pert_aprox(12); + double dx = 1e-6; + VectorXd pert; + + // jac wrt data + pert = data.Random(10) * dx; + VectorXd data_pert = data + pert; + data2tangent(data_pert, bias, r_bc, propellers, tangent_pert); + tangent_pert_aprox = tangent + J_tan_data * pert; + + ASSERT_MATRIX_APPROX(tangent_pert, tangent_pert_aprox, 1e-10); + + // jac wrt bias + pert = bias.Random(12) * dx; + VectorXd bias_pert = bias + pert; + data2tangent(data, bias_pert, r_bc, propellers, tangent_pert); + tangent_pert_aprox = tangent + J_tan_bias * pert; + + ASSERT_MATRIX_APPROX(tangent_pert, tangent_pert_aprox, 1e-10); + + // jac wrt com + pert = r_bc.Random(3) * dx; + VectorXd r_bc_pert = r_bc + pert; + data2tangent(data, bias, r_bc_pert, propellers, tangent_pert); + tangent_pert_aprox = tangent + J_tan_com * pert; + + ASSERT_MATRIX_APPROX(tangent_pert, tangent_pert_aprox, 1e-10); +} + +TEST(FTI_tools, forces2acc_onlyforce) +{ + Vector3d force(1, 2, 3); + Vector3d torque(0, 0, 0); + Vector3d angvel(0, 0, 0); + Vector3d com(0, 0, 0); + Vector3d inertia(1, 1, 1); + double mass(10); + Vector3d acc; + + // + forces2acc(force, torque, angvel, com, inertia, mass, acc); + ASSERT_MATRIX_APPROX(acc, force / mass, 1e-12); + + // + angvel << 3, 2, 1; + + forces2acc(force, torque, angvel, com, inertia, mass, acc); + ASSERT_MATRIX_APPROX(acc, force / mass, 1e-12); + + // + angvel << 0, 0, 0; + com << 1, 2, 3; + + forces2acc(force, torque, angvel, com, inertia, mass, acc); + ASSERT_MATRIX_APPROX(acc, force / mass, 1e-12); + + // must not pass + angvel << 3, 2, 1; + com << 1, 2, 3; + + forces2acc(force, torque, angvel, com, inertia, mass, acc); + // ASSERT_MATRIX_APPROX(acc, force/mass, 1e-12); +} + +TEST(FTI_tools, forces2acc_jacobians_matlab) +{ + Vector3d force(1, 2, 3); + Vector3d torque(4, 5, 6); + Vector3d angvel(3, 2, 1); + Vector3d com(6, 5, 4); + Vector3d inertia(1, -2, 3); + double mass = 2; + + Vector3d acc; + Matrix3d J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, J_acc_inertia; + Vector3d J_acc_mass; + + forces2acc(force, torque, angvel, com, inertia, mass, + acc, + J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, + J_acc_inertia, J_acc_mass); + + // Matlab results using symbolic Jacobians: + // + // acc = + // 50.5000 + // -65.0000 + // 22.5000 + // J_acc_force = + // 0.5000 0 0 + // 0 0.5000 0 + // 0 0 0.5000 + // J_acc_torque = + // 0 2.0000 1.6667 + // 4.0000 0 -2.0000 + // -5.0000 -3.0000 0 + // J_acc_angvel = + // 0 24 12 + // 6 -60 -38 + // 12 36 4 + // J_acc_com = + // 5.0000 2.0000 2.5000 + // -14.0000 10.0000 -8.0000 + // -8.5000 4.0000 13.0000 + // J_acc_inertia = + // 4.0000 1.0000 -7.3333 + // 12.0000 20.0000 8.0000 + // -21.0000 -26.5000 1.0000 + // J_acc_mass = + // -0.2500 + // -0.5000 + // -0.7500 + + WOLF_INFO("acc: \n", acc); + WOLF_INFO("J_acc_force: \n", J_acc_force); + WOLF_INFO("J_acc_torque: \n", J_acc_torque); + WOLF_INFO("J_acc_angvel: \n", J_acc_angvel); + WOLF_INFO("J_acc_com: \n", J_acc_com); + WOLF_INFO("J_acc_inertia: \n", J_acc_inertia); + WOLF_INFO("J_acc_mass: \n", J_acc_mass); +} + +TEST(FTI_tools, forces2acc_jacobians) +{ + Vector3d force, torque, angvel, com, inertia; + double mass; + Vector3d acc; + + force .setRandom().normalize(); + torque .setRandom().normalize(); + angvel .setRandom().normalize(); + com .setRandom().normalize(); + inertia .setRandom().normalize(); inertia *= 100; + + inertia = (inertia.array() * inertia.array()).matrix(); // make positive + mass = 2.0; + + MatrixXd J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, J_acc_inertia, J_acc_mass; + + // analytical jacs + forces2acc(force, torque, angvel, com, inertia, mass, acc, J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, + J_acc_inertia, J_acc_mass); + + // linear approximations + Vector3d pert, acc_pert, acc_pert_approx; + double dx = 1e-5; + pert = dx * Vector3d::Random().normalized(); + + // force + forces2acc(force + pert, torque, angvel, com, inertia, mass, acc_pert); + acc_pert_approx = acc + J_acc_force * pert; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); + + // torque + forces2acc(force, torque + pert, angvel, com, inertia, mass, acc_pert); + acc_pert_approx = acc + J_acc_torque * pert; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); + + // angvel + forces2acc(force, torque, angvel + pert, com, inertia, mass, acc_pert); + acc_pert_approx = acc + J_acc_angvel * pert; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); + + // com + forces2acc(force, torque, angvel, com + pert, inertia, mass, acc_pert); + acc_pert_approx = acc + J_acc_com * pert; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); + + // inertia + forces2acc(force, torque, angvel, com, inertia + pert, mass, acc_pert); + acc_pert_approx = acc + J_acc_inertia * pert; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); + + // mass + forces2acc(force, torque, angvel, com, inertia, mass + dx, acc_pert); + acc_pert_approx = acc + J_acc_mass * dx; + + ASSERT_MATRIX_APPROX(acc_pert, acc_pert_approx, 1e-8); +} + +TEST(FTI_tools, tangent2delta_jacobians) +{ + VectorXd tangent(12), delta(19), model(7); + MatrixXd J_delta_tangent(18, 12), J_delta_model(18, 7); + double dt = 1; + + tangent.setRandom(); + model.setRandom(); + model.tail(4) *= 10; // augment inertia and mass + + tangent2delta(tangent, model, dt, delta, J_delta_tangent, J_delta_model); + + // linear approximations + VectorXd pert, delta_pert(19), delta_pert_approx(19), tangent_pert(12), model_pert(7); + double dx = 1e-6; + + // tangent + pert = dx * tangent.Random(12); + tangent_pert = tangent + pert; + tangent2delta(tangent_pert, model, dt, delta_pert); + VectorXd step = J_delta_tangent * pert; + delta_pert_approx = plus(delta, step); + + ASSERT_MATRIX_APPROX(delta_pert, delta_pert_approx, 1e-9); + + // model + pert = dx * model.Random(7); + model_pert = model + pert; + tangent2delta(tangent, model_pert, dt, delta_pert); + step = J_delta_model * pert; + delta_pert_approx = plus(delta, step); + + ASSERT_MATRIX_APPROX(delta_pert, delta_pert_approx, 1e-9); +} + +TEST(FTI_tools, data2delta_hovering) +{ + VectorXd data(10), bias(12), model(7), delta(19); + double dt = 1; + + std::list<Propeller> propellers; + propellers.emplace_back(Vector3d(1, 1, 0), Matrix3d::Identity(), 1, 1, 1); // FL + propellers.emplace_back(Vector3d(-1, 1, 0), Matrix3d::Identity(), -1, 1, 1); // RL + propellers.emplace_back(Vector3d(-1, -1, 0), Matrix3d::Identity(), 1, 1, 1); // RR + propellers.emplace_back(Vector3d(1, -1, 0), Matrix3d::Identity(), -1, 1, 1); // FR + + Vector4d n(2, 2, 2, 2); // FL, RL, RR, FR + + data << 0, 0, 0, 0, 0, 0, n; // acc, gyro, propeller speeds + bias.setZero(); + model << 0, 0, 0, 1, 1, 1, 1; // com, inertia, mass + + data2delta(data, bias, model, propellers, dt, delta); + + VectorXd delta_exp(19); + delta_exp << 0, 0, 0, 0, 0, 0, 0, 0, 4 * 0.5 * 2 * 2, 0, 0, 4 * 2 * 2, 0, 0, 0, 0, 0, 0, 1; + + ASSERT_MATRIX_APPROX(delta, delta_exp, 1e-12); +} + +TEST(FTI_tools, data2delta_jacobians) +{ + VectorXd data(10), bias(12), model(7), delta(19); + double dt = 1; + MatrixXd J_delta_data(18, 10), J_delta_bias(18, 12), J_delta_model(18, 7); + + std::list<Propeller> propellers; + propellers.emplace_back(Vector3d(1, 1, 0), Matrix3d::Identity(), 1, 1, 1); // FL + propellers.emplace_back(Vector3d(-1, 1, 0), Matrix3d::Identity(), -1, 1, 1); // RL + propellers.emplace_back(Vector3d(-1, -1, 0), Matrix3d::Identity(), 1, 1, 1); // RR + propellers.emplace_back(Vector3d(1, -1, 0), Matrix3d::Identity(), -1, 1, 1); // FR + + VectorXd pert, step, data_pert(10), bias_pert(12), model_pert(7), delta_pert(19), delta_pert_approx(19); + double dx = 1e-6; + + data.setRandom(); // acc, gyro, propeller speeds + bias.setRandom(); + model.setRandom(); // com, inertia, mass + + data2delta(data, bias, model, propellers, dt, delta, J_delta_data, J_delta_bias, J_delta_model); + + // data + pert = dx * data.Random(10); + data_pert = data + pert; + data2delta(data_pert, bias, model, propellers, dt, delta_pert); + step = J_delta_data * pert; + delta_pert_approx = plus(delta, step); + + ASSERT_MATRIX_APPROX(delta_pert, delta_pert_approx, 1e-8); + + // bias + pert = dx * bias.Random(12); + bias_pert = bias + pert; + data2delta(data, bias_pert, model, propellers, dt, delta_pert); + step = J_delta_bias * pert; + delta_pert_approx = plus(delta, step); + + ASSERT_MATRIX_APPROX(delta_pert, delta_pert_approx, 1e-8); + + // model + pert = dx * model.Random(7); + model_pert = model + pert; + data2delta(data, bias, model_pert, propellers, dt, delta_pert); + step = J_delta_model * pert; + delta_pert_approx = plus(delta, step); + + ASSERT_MATRIX_APPROX(delta_pert, delta_pert_approx, 1e-8); +} + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + // ::testing::GTEST_FLAG(filter) = "FTI_tools.identity*"; + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_processor_force_torque_preint.cpp b/test/gtest_processor_force_torque.cpp similarity index 96% rename from test/gtest_processor_force_torque_preint.cpp rename to test/gtest_processor_force_torque.cpp index 3465ea6187358cffe667af6a2dd326d279e31e47..b00181b9bd356dbabce6e2d45c0d36a8626ac497 100644 --- a/test/gtest_processor_force_torque_preint.cpp +++ b/test/gtest_processor_force_torque.cpp @@ -21,7 +21,7 @@ //--------LICENSE_END-------- /** - * \file gtest_factor_force_torque_preint.cpp + * \file gtest_factor_force_torque.cpp * * Created on: March 20, 2020 * \author: Mederic Fourmy @@ -57,9 +57,9 @@ #include "bodydynamics/sensor/sensor_inertial_kinematics.h" #include "bodydynamics/sensor/sensor_force_torque.h" #include "bodydynamics/capture/capture_inertial_kinematics.h" -#include "bodydynamics/capture/capture_force_torque_preint.h" +#include "bodydynamics/capture/capture_force_torque.h" #include "bodydynamics/processor/processor_inertial_kinematics.h" -#include "bodydynamics/processor/processor_force_torque_preint.h" +#include "bodydynamics/processor/processor_force_torque.h" #include <Eigen/Eigenvalues> @@ -81,7 +81,7 @@ const Vector3d PBCY = {0, -0.1, 0}; // Y axis offset const Vector3d PBCZ = {0, 0, -0.1}; // Z axis offset -class ProcessorForceTorquePreintImuOdom3dIkin2KF : public testing::Test +class ProcessorForceTorqueImuOdom3dIkin2KF : public testing::Test { public: wolf::TimeStamp t0_; @@ -92,7 +92,7 @@ public: SensorForceTorquePtr sen_ft_; ProcessorImuPtr proc_imu_; ProcessorInertialKinematicsPtr proc_inkin_; - ProcessorForceTorquePreintPtr proc_ftpreint_; + ProcessorForceTorquePtr proc_ft_; FrameBasePtr KF1_; void SetUp() override @@ -138,7 +138,7 @@ public: SensorBasePtr sen_ft_base = problem_->installSensor("SensorForceTorque", "SenFT", VectorXd(0), intr_ft); // SensorBasePtr sen_ft_base = problem_->installSensor("SensorForceTorque", "SenFT", VectorXd(0), bodydynamics_root_dir + "/demos/sensor_ft.yaml"); sen_ft_ = std::static_pointer_cast<SensorForceTorque>(sen_ft_base); - ParamsProcessorForceTorquePreintPtr params_sen_ft = std::make_shared<ParamsProcessorForceTorquePreint>(); + ParamsProcessorForceTorquePtr params_sen_ft = std::make_shared<ParamsProcessorForceTorque>(); params_sen_ft->sensor_ikin_name = "SenIK"; params_sen_ft->sensor_angvel_name = "SenImu"; params_sen_ft->nbc = 2; @@ -150,16 +150,16 @@ public: params_sen_ft->dist_traveled = 20000.0; params_sen_ft->angle_turned = 1000; params_sen_ft->voting_active = true; - ProcessorBasePtr proc_ft_base = problem_->installProcessor("ProcessorForceTorquePreint", "PFTPreint", sen_ft_, params_sen_ft); - // ProcessorBasePtr proc_ft_base = problem_->installProcessor("ProcessorForceTorquePreint", "PFTPreint", "SenFT", bodydynamics_root_dir + "/demos/processor_ft_preint"); - proc_ftpreint_ = std::static_pointer_cast<ProcessorForceTorquePreint>(proc_ft_base); + ProcessorBasePtr proc_ft_base = problem_->installProcessor("ProcessorForceTorque", "PFT", sen_ft_, params_sen_ft); + // ProcessorBasePtr proc_ft_base = problem_->installProcessor("ProcessorForceTorque", "PFT", "SenFT", bodydynamics_root_dir + "/demos/processor_ft_preint"); + proc_ft_ = std::static_pointer_cast<ProcessorForceTorque>(proc_ft_base); } void TearDown() override {} }; -class Cst2KFZeroMotion : public ProcessorForceTorquePreintImuOdom3dIkin2KF +class Cst2KFZeroMotion : public ProcessorForceTorqueImuOdom3dIkin2KF { public: FrameBasePtr KF2_; @@ -212,7 +212,7 @@ public: void SetUp() override { - ProcessorForceTorquePreintImuOdom3dIkin2KF::SetUp(); + ProcessorForceTorqueImuOdom3dIkin2KF::SetUp(); t0_.set(0.0); TimeStamp t1; t1.set(1*DT); TimeStamp t2; t2.set(2*DT); @@ -246,14 +246,14 @@ public: proc_imu_->setOrigin(KF1_); auto CIKin0 = std::make_shared<CaptureInertialKinematics>(t0_, sen_ikin_, meas_ikin_, Iq_, Lq_); CIKin0->process(); - proc_ftpreint_->setOrigin(KF1_); + proc_ft_->setOrigin(KF1_); // T1 CaptureImuPtr CImu1 = std::make_shared<CaptureImu>(t1, sen_imu_, acc_gyr_meas_, acc_gyr_cov_); CImu1->process(); auto CIKin1 = std::make_shared<CaptureInertialKinematics>(t1, sen_ikin_, meas_ikin_, Iq_, Lq_); CIKin1->process(); - auto CFTpreint1 = std::make_shared<CaptureForceTorquePreint>(t1, sen_ft_, CIKin1, CImu1, cap_ftp_data_, Qftp_); + auto CFTpreint1 = std::make_shared<CaptureForceTorque>(t1, sen_ft_, CIKin1, CImu1, cap_ftp_data_, Qftp_); CFTpreint1->process(); @@ -265,7 +265,7 @@ public: CImu2->process(); auto CIKin2 = std::make_shared<CaptureInertialKinematics>(t2, sen_ikin_, meas_ikin_, Iq_, Lq_); CIKin2->process(); - auto CFTpreint2 = std::make_shared<CaptureForceTorquePreint>(t2, sen_ft_, CIKin2, CImu2, cap_ftp_data_, Qftp_); + auto CFTpreint2 = std::make_shared<CaptureForceTorque>(t2, sen_ft_, CIKin2, CImu2, cap_ftp_data_, Qftp_); CFTpreint2->process(); changeForData3(); @@ -275,7 +275,7 @@ public: CImu3->process(); CaptureInertialKinematicsPtr CIKin3 = std::make_shared<CaptureInertialKinematics>(t3, sen_ikin_, meas_ikin_, Iq_, Lq_); CIKin3->process(); - auto CFTpreint3 = std::make_shared<CaptureForceTorquePreint>(t3, sen_ft_, CIKin3, CImu3, cap_ftp_data_, Qftp_); + auto CFTpreint3 = std::make_shared<CaptureForceTorque>(t3, sen_ft_, CIKin3, CImu3, cap_ftp_data_, Qftp_); CFTpreint3->process(); // T4, just here to make sure the KF at t3 is created @@ -283,7 +283,7 @@ public: CImu4->process(); CaptureInertialKinematicsPtr CIKin4 = std::make_shared<CaptureInertialKinematics>(t4, sen_ikin_, meas_ikin_, Iq_, Lq_); CIKin4->process(); - auto CFTpreint4 = std::make_shared<CaptureForceTorquePreint>(t4, sen_ft_, CIKin3, CImu3, cap_ftp_data_, Qftp_); + auto CFTpreint4 = std::make_shared<CaptureForceTorque>(t4, sen_ft_, CIKin3, CImu3, cap_ftp_data_, Qftp_); CFTpreint4->process(); ///////////////////////////////////////////// diff --git a/test/gtest_processor_force_torque_inertial.cpp b/test/gtest_processor_force_torque_inertial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b8be3e23b05e5c64f1a63ad0e36c9c3559f81e2c --- /dev/null +++ b/test/gtest_processor_force_torque_inertial.cpp @@ -0,0 +1,79 @@ +//--------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-------- +/* + * gtest_processor_force_torque_inertial.cpp + * + * Created on: Aug 19, 2021 + * Author: jsola + */ + +#include "bodydynamics/processor/processor_force_torque_inertial.h" + +#include "core/utils/utils_gtest.h" +#include "core/utils/logging.h" + +using namespace wolf; + +/* + // You may use this to make some methods of Foo public + WOLF_PTR_TYPEDEFS(FooPublic); + class FooPublic : public Foo + { + // You may use this to make some methods of Foo public + } + + class TestInit : public testing::Test + { + public: + // You may use this to initialize stuff + // Combine it with TEST_F(FooTest, testName) { } + void SetUp() override + { + // Init all you want here + // e.g. FooPublic foo; + } + void TearDown() override {} // you can delete this if unused + }; + + TEST_F(TestInit, testName) + { + // Use class TestInit + } + */ + +TEST(TestGroup, testName) +{ + // individual tests +} + +int main (int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + + // restrict to a group of tests only + //::testing::GTEST_FLAG(filter) = "TestInit.*"; + + // restrict to this test only + //::testing::GTEST_FLAG(filter) = "TestInit.testName"; + + return RUN_ALL_TESTS(); +} diff --git a/test/gtest_processor_force_torque_inertial_dynamics.cpp b/test/gtest_processor_force_torque_inertial_dynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25df6c6d1d00683e5a77bc16f1a90511f41d991a --- /dev/null +++ b/test/gtest_processor_force_torque_inertial_dynamics.cpp @@ -0,0 +1,265 @@ +//--------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 "bodydynamics/factor/factor_force_torque_inertial_dynamics.h" +#include "bodydynamics/processor/processor_force_torque_inertial_dynamics.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" +#include "bodydynamics/internal/config.h" + +#include <core/sensor/factory_sensor.h> + +#include <core/utils/utils_gtest.h> +#include <core/utils/logging.h> +#include <core/utils/params_server.h> +#include <core/yaml/parser_yaml.h> +#include <core/state_block/state_block_derived.h> +#include <core/state_block/factory_state_block.h> + +#include <Eigen/Dense> +#include <Eigen/Geometry> + +using namespace wolf; +using namespace bodydynamics::fti; + +WOLF_REGISTER_STATEBLOCK_WITH_KEY(L, StateVector3d); + +class Test_ProcessorForceTorqueInertialDynamics_yaml : public testing::Test +{ + public: + ProblemPtr P; + SensorForceTorqueInertialPtr S; + ProcessorForceTorqueInertialDynamicsPtr p; + + protected: + void SetUp() override + { + std::string wolf_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + ParserYaml parser = ParserYaml("problem_force_torque_inertial_dynamics_processor_test.yaml", wolf_root + "/test/yaml/"); + ParamsServer server = ParamsServer(parser.getParams()); + P = Problem::autoSetup(server); + + S = std::static_pointer_cast<SensorForceTorqueInertial>(P->getHardware()->getSensorList().front()); + p = std::static_pointer_cast<ProcessorForceTorqueInertialDynamics>(S->getProcessorList().front()); + } +}; + +TEST_F(Test_ProcessorForceTorqueInertialDynamics_yaml, force_registraion) +{ + FactorySensor::registerCreator("SensorForceTorqueInertial", SensorForceTorqueInertial::create); +} + +// Test to see if the processor works (yaml files). Here the dron is not moving +TEST_F(Test_ProcessorForceTorqueInertialDynamics_yaml, not_moving_test) +{ + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = - gravity(); + data.segment<3>(6) = - 1.952*gravity(); + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.2, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.4, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C4_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.8, S, data, data_cov, nullptr); + CaptureMotionPtr C5_1 = std::make_shared<CaptureMotion>("CaptureMotion", 1.0, S, data, data_cov, nullptr); + + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + C1_0->process(); + C2_0->process(); + C3_0->process(); + C4_0->process(); + C5_1->process(); + + CaptureMotionPtr C_KF1 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + + //We check that, effectively, the dron has not moved + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('P')->getState(), C_KF0->getFrame()->getStateBlock('P')->getState(), 1e-8); + ASSERT_QUATERNION_APPROX(Quaterniond(C_KF1->getFrame()->getStateBlock('O')->getState().data()), Quaterniond(C_KF0->getFrame()->getStateBlock('O')->getState().data()), 1e-8); + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('V')->getState(), C_KF0->getFrame()->getStateBlock('V')->getState(), 1e-8); + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('L')->getState(), C_KF0->getFrame()->getStateBlock('L')->getState(), 1e-8); + + + //Checking that the captures we have taken are the correct ones by looking both time stands + ASSERT_EQ(C_KF0->getTimeStamp(), C0_0->getTimeStamp()); + ASSERT_EQ(C_KF1->getTimeStamp(), C5_1->getTimeStamp()); + + + Vector3d p0 = C_KF0->getFrame()->getStateBlock('P')->getState(); + Quaterniond q0 = Quaterniond(C_KF0->getFrame()->getStateBlock('O')->getState().data()); + Vector3d v0 = C_KF0->getFrame()->getStateBlock('V')->getState(); + Vector3d L0 = C_KF0->getFrame()->getStateBlock('L')->getState(); + + Vector3d p1 = C_KF1->getFrame()->getStateBlock('P')->getState(); + Quaterniond q1 = Quaterniond(C_KF1->getFrame()->getStateBlock('O')->getState().data()); + Vector3d v1 = C_KF1->getFrame()->getStateBlock('V')->getState(); + Vector3d L1 = C_KF1->getFrame()->getStateBlock('L')->getState(); + + Vector3d dpi; + Vector3d dvi; + Vector3d dpd; + Vector3d dvd; + Vector3d dL; + Quaterniond dq; + double dt = 1.0; + VectorXd betw_states(19); + + betweenStates(p0,v0,L0,q0,p1,v1,L1,q1,dt,dpi,dvi,dpd,dvd,dL,dq); + + betw_states << dpi, dvi, dpd, dvd, dL, dq.coeffs(); + + VectorXd delta_preintegrated = C_KF1->getFeatureList().back()->getMeasurement(); + + //Cheking that the preintegrated delta calculated by the processor is the same as the one that is correct for sure + ASSERT_MATRIX_APPROX(betw_states, delta_preintegrated, 1e-8); + +} + +//Test to see if the processor works (yaml files). Here the dron is moving just 1m in x direction +TEST_F(Test_ProcessorForceTorqueInertialDynamics_yaml, 1m_x_moving_test__com_zero) +{ + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data(0) = 2; + data.segment<3>(6) = - 1.952*gravity(); + data(6) = 1.952*2; + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + // Set CoM to zero so that an acceleration on X does not produce a torque, thereby producing non-null 'L' at KF1 + S->getStateBlock('C')->setState(Vector3d::Zero()); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.3, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureMotion>("CaptureMotion", 1.0, S, data, data_cov, nullptr); + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + C1_0->process(); + C2_0->process(); + C3_0->process(); + + CaptureMotionPtr C_KF1 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + P->print(4,1,1,1); + + C_KF1->getBuffer().print(1,1,1,0,0); + + //We check that, effectively, the drone has moved 1m in the x direction, the x component of the + //velocity is now 2m/s and nothing else has changed from the original state + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('P')->getState(), Vector3d(1,0,0), 1e-8); + ASSERT_QUATERNION_APPROX(Quaterniond(C_KF1->getFrame()->getStateBlock('O')->getState().data()), Quaterniond(C_KF0->getFrame()->getStateBlock('O')->getState().data()), 1e-8); + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('V')->getState(), Vector3d(2,0,0), 1e-8); + ASSERT_MATRIX_APPROX(C_KF1->getFrame()->getStateBlock('L')->getState(), C_KF0->getFrame()->getStateBlock('L')->getState(), 1e-8); + + + + Vector3d p0 = C_KF0->getFrame()->getStateBlock('P')->getState(); + Quaterniond q0 = Quaterniond(C_KF0->getFrame()->getStateBlock('O')->getState().data()); + Vector3d v0 = C_KF0->getFrame()->getStateBlock('V')->getState(); + Vector3d L0 = C_KF0->getFrame()->getStateBlock('L')->getState(); + + Vector3d p1 = C_KF1->getFrame()->getStateBlock('P')->getState(); + Quaterniond q1 = Quaterniond(C_KF1->getFrame()->getStateBlock('O')->getState().data()); + Vector3d v1 = C_KF1->getFrame()->getStateBlock('V')->getState(); + Vector3d L1 = C_KF1->getFrame()->getStateBlock('L')->getState(); + + Vector3d dpi; + Vector3d dvi; + Vector3d dpd; + Vector3d dvd; + Vector3d dL; + Quaterniond dq; + double dt = 1.0; + VectorXd betw_states(19); + + betweenStates(p0,v0,L0,q0,p1,v1,L1,q1,dt,dpi,dvi,dpd,dvd,dL,dq); + + betw_states << dpi, dvi, dpd, dvd, dL, dq.coeffs(); + + VectorXd delta_preintegrated = C_KF1->getFeatureList().back()->getMeasurement(); + + //Cheking that the preintegrated delta calculated by the processor is the same as the one that is correct for sure + ASSERT_MATRIX_APPROX(betw_states, delta_preintegrated, 1e-8); + +} + +//Test to see if the voteForKeyFrame works (distance traveled) +TEST_F(Test_ProcessorForceTorqueInertialDynamics_yaml, VoteForKeyFrame_dist) +{ + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data(0) = 2; + data.segment<3>(6) = - 1.952*gravity(); + data(6) = 1.952*2; + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + p->setMaxTimeSpan(999); + p->setDistTraveled(0.995); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.3, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureMotion>("CaptureMotion", 1.0, S, data, data_cov, nullptr); + C0_0->process(); + C1_0->process(); + C2_0->process(); + C3_0->process(); + + P->print(4,1,1,1); + +} + +//Test to see if the voteForKeyFrame works (buffer) +TEST_F(Test_ProcessorForceTorqueInertialDynamics_yaml, VoteForKeyFrame_buffer) +{ + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data(0) = 2; + data.segment<3>(6) = - 1.952*gravity(); + data(6) = 1.952*2; + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + p->setMaxTimeSpan(999); + p->setMaxBuffLength(3); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.3, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureMotion>("CaptureMotion", 0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureMotion>("CaptureMotion", 1.0, S, data, data_cov, nullptr); + C0_0->process(); + C1_0->process(); + C2_0->process(); + C3_0->process(); + + P->print(4,1,1,1); + +} + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + // ::testing::GTEST_FLAG(filter) = "Test_ProcessorForceTorqueInertialDynamics_yaml.not_moving_test"; + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/test/gtest_solve_problem_force_torque_inertial_dynamics.cpp b/test/gtest_solve_problem_force_torque_inertial_dynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc97b1c52db99beb3495c61311a48976c171352e --- /dev/null +++ b/test/gtest_solve_problem_force_torque_inertial_dynamics.cpp @@ -0,0 +1,313 @@ +//--------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 "bodydynamics/factor/factor_force_torque_inertial_dynamics.h" +#include "bodydynamics/processor/processor_force_torque_inertial_dynamics.h" +#include "bodydynamics/sensor/sensor_force_torque_inertial.h" +#include "bodydynamics/internal/config.h" +#include "bodydynamics/capture/capture_force_torque_inertial.h" + +#include <core/sensor/factory_sensor.h> + +#include <core/utils/utils_gtest.h> +#include <core/utils/logging.h> +#include <core/utils/params_server.h> +#include <core/yaml/parser_yaml.h> +#include <core/state_block/state_block_derived.h> +#include <core/state_block/factory_state_block.h> +#include <core/ceres_wrapper/solver_ceres.h> + +#include <Eigen/Dense> +#include <Eigen/Geometry> + +using namespace wolf; +using namespace bodydynamics::fti; + +WOLF_REGISTER_STATEBLOCK_WITH_KEY(L, StateVector3d); + +class Test_SolveProblemForceTorqueInertialDynamics_yaml : public testing::Test +{ + public: + ProblemPtr P; + SensorForceTorqueInertialPtr S; + ProcessorForceTorqueInertialDynamicsPtr p; + SolverCeresPtr solver; + Vector3d cdm_true; + Vector3d inertia_true; + double mass_true; + + protected: + void SetUp() override + { + std::string wolf_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + ParserYaml parser = + ParserYaml("problem_force_torque_inertial_dynamics_solve_test.yaml", wolf_root + "/test/yaml/"); + ParamsServer server = ParamsServer(parser.getParams()); + P = Problem::autoSetup(server); + // CERES WRAPPER + solver = std::make_shared<SolverCeres>(P); + // solver->getSolverOptions().max_num_iterations = 1e4; + // solver->getSolverOptions().function_tolerance = 1e-15; + // solver->getSolverOptions().gradient_tolerance = 1e-15; + // solver->getSolverOptions().parameter_tolerance = 1e-15; + + S = std::static_pointer_cast<SensorForceTorqueInertial>(P->getHardware()->getSensorList().front()); + p = std::static_pointer_cast<ProcessorForceTorqueInertialDynamics>(S->getProcessorList().front()); + + cdm_true = {0, 0, 0.0341}; + inertia_true = {0.017598, 0.017957, 0.029599}; + mass_true = 1.952; + } +}; + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, force_registraion) +{ + FactorySensor::registerCreator("SensorForceTorqueInertial", SensorForceTorqueInertial::create); +} + +// Hover test. +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, mass_only_hovering) +{ + double mass_guess = S->getStateBlock('m')->getState()(0); + + // Data + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); // accelerometer + data.segment<3>(6) = -mass_true * gravity(); // force + MatrixXd data_cov = MatrixXd::Identity(12, 12) * 1e-6; + + // We fix the parameters of the sensor except for the mass + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->fix(); + S->getStateBlock('C')->fix(); + S->getStateBlock('i')->fix(); + S->getStateBlock('m')->unfix(); + // S->setStateBlockDynamic('I'); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureForceTorqueInertial>(0.2, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureForceTorqueInertial>(0.4, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureForceTorqueInertial>(0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C4_0 = std::make_shared<CaptureForceTorqueInertial>(0.8, S, data, data_cov, nullptr); + CaptureMotionPtr C5_1 = std::make_shared<CaptureForceTorqueInertial>(1.0, S, data, data_cov, nullptr); + + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + C1_0->process(); + C2_0->process(); + C3_0->process(); + C4_0->process(); + C5_1->process(); + + CaptureMotionPtr C_KF1 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + C_KF0->getFrame()->fix(); + C_KF1->getFrame()->fix(); + + P->print(4, 1, 1, 1); + + WOLF_INFO("======== SOLVE PROBLEM =======") + std::string report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); + + WOLF_INFO("True mass : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass: ", S->getStateBlock('m')->getState(), " Kg."); + WOLF_INFO("-----------------------------"); + + + for (TimeStamp t = p->getLast()->getTimeStamp() + 1.0 ; t <= 7.0 ; t += 1.0) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->process(); + p->getOrigin()->getFrame()->fix(); + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO("Estimated mass: ", S->getStateBlock('m')->getState(), " Kg."); + WOLF_INFO("-----------------------------"); + } + + P->print(4, 1, 1, 1); + + + ASSERT_NEAR(S->getStateBlock('m')->getState()(0), mass_true, 1e-3); +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, cdm_only_hovering) +{ + S->getStateBlock('C')->setState(Vector3d(0.01, 0.01, 0.033)); + S->getStateBlock('m')->setState(Vector1d(mass_true)); + Vector3d cdm_guess = S->getStateBlock('C')->getState(); + + // Data + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data.segment<3>(6) = -mass_true * gravity(); + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + // We fix the parameters of the sensor except for the cdm + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->fix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->fix(); + S->getStateBlock('m')->fix(); + // S->setStateBlockDynamic('I'); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>( 0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureForceTorqueInertial>( 0.2, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureForceTorqueInertial>( 0.4, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureForceTorqueInertial>( 0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C4_0 = std::make_shared<CaptureForceTorqueInertial>( 0.8, S, data, data_cov, nullptr); + CaptureMotionPtr C5_1 = std::make_shared<CaptureForceTorqueInertial>( 1.0, S, data, data_cov, nullptr); + + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + C1_0->process(); + C2_0->process(); + C3_0->process(); + C4_0->process(); + C5_1->process(); + + CaptureMotionPtr C_KF1 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + C_KF0->getFrame()->fix(); + C_KF1->getFrame()->getStateBlock('L')->setState(Vector3d::Zero()); // Make sure L is not moving due to torques + C_KF1->getFrame()->fix(); + + P->print(4, 1, 1, 1); + + WOLF_INFO("======== SOLVE PROBLEM =======") + std::string report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); // should show a very low iteration number (possibly 1) + + WOLF_INFO("True cdm : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess cdm : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated cdm: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("-----------------------------"); + + for (TimeStamp t = p->getLast()->getTimeStamp() + 1.0 ; t <= 11.0 ; t += 1.0) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>( t, S, data, data_cov, nullptr); + C->process(); + p->getOrigin()->getFrame()->getStateBlock('L')->setState(Vector3d::Zero()); // Make sure L is not moving due to torques + p->getOrigin()->getFrame()->fix(); + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO("Estimated cdm : ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("-----------------------------"); + } + + + ASSERT_MATRIX_APPROX(S->getStateBlock('C')->getState().head(2), cdm_true.head(2), 1e-5); // cdm_z is not observable while hovering +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, mass_and_cdm_hovering) +{ + S->getStateBlock('C')->setState(Vector3d(0.005, 0.005, 0.05)); + S->getStateBlock('m')->setState(Vector1d(1.900)); + Vector3d cdm_guess = S->getStateBlock('C')->getState(); + double mass_guess = S->getStateBlock('m')->getState()(0); + + // Data + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data.segment<3>(6) = -mass_true * gravity(); + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + // We fix the parameters of the sensor except for the cdm and the mass + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->fix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->fix(); + S->getStateBlock('m')->unfix(); + // S->setStateBlockDynamic('I'); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + CaptureMotionPtr C1_0 = std::make_shared<CaptureForceTorqueInertial>(0.2, S, data, data_cov, nullptr); + CaptureMotionPtr C2_0 = std::make_shared<CaptureForceTorqueInertial>(0.4, S, data, data_cov, nullptr); + CaptureMotionPtr C3_0 = std::make_shared<CaptureForceTorqueInertial>(0.6, S, data, data_cov, nullptr); + CaptureMotionPtr C4_0 = std::make_shared<CaptureForceTorqueInertial>(0.8, S, data, data_cov, nullptr); + CaptureMotionPtr C5_1 = std::make_shared<CaptureForceTorqueInertial>(1.0, S, data, data_cov, nullptr); + + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + C1_0->process(); + C2_0->process(); + C3_0->process(); + C4_0->process(); + C5_1->process(); + + CaptureMotionPtr C_KF1 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + C_KF0->getFrame()->fix(); + C_KF1->getFrame()->getStateBlock('L')->setState(Vector3d::Zero()); // Make sure L is not moving due to torques + C_KF1->getFrame()->fix(); + + P->print(4, 1, 1, 1); + + WOLF_INFO("======== SOLVE PROBLEM =======") + std::string report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO(report); // should show a very low iteration number (possibly 1) + + WOLF_INFO("True mass : ", mass_true, " Kg."); + WOLF_INFO("True cdm : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Guess cdm : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated mass: ", S->getStateBlock('m')->getState(), " Kg."); + WOLF_INFO("Estimated cdm : ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("-----------------------------"); + + + // Do a number of iterations with more keyframes, solving so that the calibration gets better and better + // Here we take advantage of the sliding window, thereby getting rid progressively + // of the old factors, which contained calibration parameters far from the converged values, + // which if not eliminated contaminate the overall solution. + // This is due to these first factors relying on the linearization Jacobian to correct the + // poorly preintegrated delta. + for (TimeStamp t = p->getLast()->getTimeStamp() + 1.0 ; t <= 7.0 ; t += 1.0) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->setTimeStamp(t); + C->process(); + p->getOrigin()->getFrame()->getStateBlock('L')->setState(Vector3d::Zero()); // Make sure L is not moving due to torques + p->getOrigin()->getFrame()->fix(); + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO("Estimated mass: ", S->getStateBlock('m')->getState(), " Kg."); + WOLF_INFO("Estimated cdm : ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("-----------------------------"); + } + + auto cdm_estimated = S->getStateBlock('C')->getState(); + auto mass_estimated = S->getStateBlock('m')->getState()(0); + + + ASSERT_NEAR(mass_estimated, mass_true, 1e-6); + ASSERT_MATRIX_APPROX(cdm_estimated.head(2), cdm_true.head(2), 1e-6); // cdm_z is not observable while hovering +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + // ::testing::GTEST_FLAG(filter) = "Test_SolveProblemForceTorqueInertialDynamics_yaml.cdm_only_hovering"; + return RUN_ALL_TESTS(); +} diff --git a/test/matlab/forces2acc.m b/test/matlab/forces2acc.m new file mode 100644 index 0000000000000000000000000000000000000000..bd40e583279d6c56267964c711f094ec6575c338 --- /dev/null +++ b/test/matlab/forces2acc.m @@ -0,0 +1,115 @@ +%% +function [acc, J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, J_acc_inertia, J_acc_mass] = forces2acc(force, torque, angvel, com, inertia, mass) + +I = diag(inertia); + +angacc = inv(I) * (torque - cross(angvel, I * angvel)); + +acc = force / mass - cross(angacc, com) - cross(angvel, cross(angvel, com)); + +% Jacobians + +fx = force(1); +fy = force(2); +fz = force(3); +tx = torque(1); +ty = torque(2); +tz = torque(3); +wx = angvel(1); +wy = angvel(2); +wz = angvel(3); +cx = com(1); +cy = com(2); +cz = com(3); +ix = inertia(1); +iy = inertia(2); +iz = inertia(3); + +J_acc_force = [... + [1/mass, 0, 0] + [ 0, 1/mass, 0] + [ 0, 0, 1/mass]]; + +J_acc_torque = [... +[ 0, -cz/iy, cy/iz] +[ cz/ix, 0, -cx/iz] +[-cy/ix, cx/iy, 0]]; + + +J_acc_angvel = [... +[-((cy*iy*wy + cz*iz*wz)*(iy - ix + iz))/(iy*iz), 2*cx*wy - cy*wx + (cy*(ix*wx - iy*wx))/iz, 2*cx*wz - cz*wx + (cz*(ix*wx - iz*wx))/iy] +[ 2*cy*wx - cx*wy - (cx*(ix*wy - iy*wy))/iz, -((cx*ix*wx + cz*iz*wz)*(ix - iy + iz))/(ix*iz), 2*cy*wz - cz*wy + (cz*(iy*wy - iz*wy))/ix] +[ 2*cz*wx - cx*wz - (cx*(ix*wz - iz*wz))/iy, 2*cz*wy - cy*wz - (cy*(iy*wz - iz*wz))/ix, -((cx*ix*wx + cy*iy*wy)*(ix + iy - iz))/(ix*iy)]]; + + +J_acc_com = [... +[ wy^2 + wz^2, (tz + ix*wx*wy - iy*wx*wy)/iz - wx*wy, - wx*wz - (ty - ix*wx*wz + iz*wx*wz)/iy] +[- wx*wy - (tz + ix*wx*wy - iy*wx*wy)/iz, wx^2 + wz^2, (tx + iy*wy*wz - iz*wy*wz)/ix - wy*wz] +[ (ty - ix*wx*wz + iz*wx*wz)/iy - wx*wz, - wy*wz - (tx + iy*wy*wz - iz*wy*wz)/ix, wx^2 + wy^2]]; + + +J_acc_inertia = [... +[ (cy*wx*wy)/iz + (cz*wx*wz)/iy, (cz*(ty - ix*wx*wz + iz*wx*wz))/iy^2 - (cy*wx*wy)/iz, - (cy*(tz + ix*wx*wy - iy*wx*wy))/iz^2 - (cz*wx*wz)/iy] +[- (cz*(tx + iy*wy*wz - iz*wy*wz))/ix^2 - (cx*wx*wy)/iz, (cx*wx*wy)/iz + (cz*wy*wz)/ix, (cx*(tz + ix*wx*wy - iy*wx*wy))/iz^2 - (cz*wy*wz)/ix] +[ (cy*(tx + iy*wy*wz - iz*wy*wz))/ix^2 - (cx*wx*wz)/iy, - (cx*(ty - ix*wx*wz + iz*wx*wz))/iy^2 - (cy*wy*wz)/ix, (cx*wx*wz)/iy + (cy*wy*wz)/ix]]; + + +J_acc_mass = [... +-fx/mass^2 +-fy/mass^2 +-fz/mass^2]; + + +end + +%% +function f() +%% +syms fx fy fz tx ty tz wx wy wz cx cy cz ix iy iz mass real; + +force = [fx;fy;fz]; +torque = [tx;ty;tz]; +angvel = [wx;wy;wz]; +com = [cx;cy;cz]; +inertia = [ix;iy;iz]; + +[acc, J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, J_acc_inertia, J_acc_mass] = forces2acc(force, torque, angvel, com, inertia, mass); + +J_acc_force_ = simplify(jacobian(acc, force)); +J_acc_torque_ = simplify(jacobian(acc, torque)); +J_acc_angvel_ = simplify(jacobian(acc, angvel)); +J_acc_com_ = simplify(jacobian(acc, com)); +J_acc_inertia_ = simplify(jacobian(acc, inertia)); +J_acc_mass_ = simplify(jacobian(acc, mass)); + +simplify(J_acc_force_ - J_acc_force) +simplify(J_acc_torque_ - J_acc_torque) +simplify(J_acc_angvel_ - J_acc_angvel) +simplify(J_acc_com_ - J_acc_com) +simplify(J_acc_inertia_ - J_acc_inertia) +simplify(J_acc_mass_ - J_acc_mass) + + +end + +function g() +%% +force = [1;2;3]; +torque = [4;5;6]; +angvel = [3;2;1]; +com = [6;5;4]; +inertia = [1;-2;3]; +mass = 2; + +[acc, J_acc_force, J_acc_torque, J_acc_angvel, J_acc_com, J_acc_inertia, J_acc_mass] = forces2acc(force, torque, angvel, com, inertia, mass); + +acc +J_acc_force +J_acc_torque +J_acc_angvel +J_acc_com +J_acc_inertia +J_acc_mass + +%% +end \ No newline at end of file diff --git a/test/yaml/problem_force_torque_inertial.yaml b/test/yaml/problem_force_torque_inertial.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e2ae117aee579fb6f444cce6f378b2f1c673e739 --- /dev/null +++ b/test/yaml/problem_force_torque_inertial.yaml @@ -0,0 +1,39 @@ +config: + problem: + frame_structure: "POVL" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + V: [0,0,0] + L: [0,0,0] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + V: [1,1,1] + L: [1,1,1] + time_tolerance: 0.1 + tree_manager: + type: "None" + map: + type: "MapBase" + plugin: "core" + sensors: + - + name: "sensor FTI" + type: "SensorForceTorqueInertial" + plugin: "bodydynamics" + extrinsic: + pose: [0,0,0, 0,0,0,1] + follow: "./sensor_force_torque_inertial.yaml" + + processors: + - + name: "proc FTI" + type: "ProcessorForceTorqueInertial" + sensor_name: "sensor FTI" + plugin: "bodydynamics" + follow: "processor_force_torque_inertial.yaml" + \ No newline at end of file diff --git a/test/yaml/problem_force_torque_inertial_dynamics.yaml b/test/yaml/problem_force_torque_inertial_dynamics.yaml new file mode 100644 index 0000000000000000000000000000000000000000..207b69bba5597a63cc41167fbe7a3106ffa4606a --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics.yaml @@ -0,0 +1,39 @@ +config: + problem: + frame_structure: "POVL" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + V: [0,0,0] + L: [0,0,0] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + V: [1,1,1] + L: [1,1,1] + time_tolerance: 0.1 + tree_manager: + type: "None" + map: + type: "MapBase" + plugin: "core" + sensors: + - + name: "sensor FTI" + type: "SensorForceTorqueInertial" + plugin: "bodydynamics" + extrinsic: + pose: [0,0,0, 0,0,0,1] + follow: "./sensor_force_torque_inertial.yaml" + + processors: + - + name: "proc FTID" + type: "ProcessorForceTorqueInertialDynamics" + sensor_name: "sensor FTI" + plugin: "bodydynamics" + follow: "processor_force_torque_inertial_dynamics.yaml" + \ No newline at end of file diff --git a/test/yaml/problem_force_torque_inertial_dynamics_processor_test.yaml b/test/yaml/problem_force_torque_inertial_dynamics_processor_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..233368f38accb87f4bbd4500e37bede35dd1378a --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics_processor_test.yaml @@ -0,0 +1,58 @@ +config: + problem: + frame_structure: "POVL" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + V: [0,0,0] + L: [0,0,0] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + V: [1,1,1] + L: [1,1,1] + time_tolerance: 0.1 + tree_manager: + type: "None" + map: + type: "MapBase" + plugin: "core" + sensors: + - + name: "sensor FTI" + type: "SensorForceTorqueInertial" + plugin: "bodydynamics" + extrinsic: + pose: [0,0,0, 0,0,0,1] + force_noise_std: 0.1 # std dev of force noise in N + torque_noise_std: 0.1 # std dev of torque noise in N/m + acc_noise_std: 0.01 # std dev of acc noise in m/s2 + gyro_noise_std: 0.01 # std dev of gyro noise in rad/s + acc_drift_std: 0.00001 # std dev of acc drift m/s2/sqrt(s) + gyro_drift_std: 0.00001 # std dev of gyro drift rad/s/sqrt(s) + + com: [0,0,0.0341] # center of mass [m] + inertia: [0.017598,0.017957,0.029599] # moments of inertia i_xx, i_yy, i_zz [kg m2] + mass: 1.952 # mass [kg] + + processors: + - + name: "proc FTID" + type: "ProcessorForceTorqueInertialDynamics" + sensor_name: "sensor FTI" + plugin: "bodydynamics" + time_tolerance: 0.1 + apply_loss_function: false + unmeasured_perturbation_std: 0.0001 + state_getter: true + state_priority: 1 + n_propellers: 3 + keyframe_vote: + voting_active: true + max_time_span: 0.995 + max_buff_length: 999 # motion deltas + dist_traveled: 999 # meters + angle_turned: 999 # radians (1 rad approx 57 deg, approx 60 deg) diff --git a/test/yaml/problem_force_torque_inertial_dynamics_solve_test.yaml b/test/yaml/problem_force_torque_inertial_dynamics_solve_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8eb8204126f94bc008bcb01587ae2fa5482fec6b --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics_solve_test.yaml @@ -0,0 +1,65 @@ +config: + problem: + frame_structure: "POVL" + dimension: 3 + prior: + mode: "factor" + $state: + P: [0,0,0] + O: [0,0,0,1] + V: [0,0,0] + L: [0,0,0] + $sigma: + P: [0.31, 0.31, 0.31] + O: [0.31, 0.31, 0.31] + V: [1,1,1] + L: [1,1,1] + time_tolerance: 0.1 + tree_manager: + type: "TreeManagerSlidingWindow" + plugin: "core" + n_frames: 3 + n_fix_first_frames: 1 + viral_remove_empty_parent: true + map: + type: "MapBase" + plugin: "core" + sensors: + - + name: "sensor FTI" + type: "SensorForceTorqueInertial" + plugin: "bodydynamics" + extrinsic: + pose: [0,0,0, 0,0,0,1] + + # IMU + force_noise_std: 0.1 # std dev of force noise in N + torque_noise_std: 0.1 # std dev of torque noise in N/m + acc_noise_std: 0.01 # std dev of acc noise in m/s2 + gyro_noise_std: 0.01 # std dev of gyro noise in rad/s + acc_drift_std: 0.00001 # std dev of acc drift m/s2/sqrt(s) + gyro_drift_std: 0.00001 # std dev of gyro drift rad/s/sqrt(s) + + # Dynamics + com: [0,0,0.0341] # center of mass [m] + inertia: [0.017598,0.017957,0.029599] # moments of inertia i_xx, i_yy, i_zz [kg m2] + mass: 1.9 # mass [kg] + + processors: + - + name: "proc FTID" + type: "ProcessorForceTorqueInertialDynamics" + sensor_name: "sensor FTI" + plugin: "bodydynamics" + time_tolerance: 0.1 + apply_loss_function: false + unmeasured_perturbation_std: 0.0001 + state_getter: true + state_priority: 1 + n_propellers: 3 + keyframe_vote: + voting_active: true + max_time_span: 0.995 + max_buff_length: 999 # motion deltas + dist_traveled: 999 # meters + angle_turned: 999 # radians (1 rad approx 57 deg, approx 60 deg) diff --git a/test/yaml/processor_force_torque_inertial.yaml b/test/yaml/processor_force_torque_inertial.yaml new file mode 100644 index 0000000000000000000000000000000000000000..beafed9ea44a627f2c5b2ea30490bfd11db7868b --- /dev/null +++ b/test/yaml/processor_force_torque_inertial.yaml @@ -0,0 +1,13 @@ + time_tolerance: 0.1 + apply_loss_function: false + unmeasured_perturbation_std: 0.00000001 + state_getter: true + state_priority: 1 + n_propellers: 3 + keyframe_vote: + voting_active: false + max_time_span: 1 + max_buff_length: 5 # motion deltas + dist_traveled: 1 # meters + angle_turned: 1 # radians (1 rad approx 57 deg, approx 60 deg) + diff --git a/test/yaml/processor_force_torque_inertial_dynamics.yaml b/test/yaml/processor_force_torque_inertial_dynamics.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a7160e79986f6252a1b8355adad7d8853fecb9c7 --- /dev/null +++ b/test/yaml/processor_force_torque_inertial_dynamics.yaml @@ -0,0 +1,13 @@ + time_tolerance: 0.1 + apply_loss_function: false + unmeasured_perturbation_std: 0.00000001 + state_getter: true + state_priority: 1 + n_propellers: 3 + keyframe_vote: + voting_active: false + max_time_span: 999 + max_buff_length: 999 # motion deltas + dist_traveled: 999 # meters + angle_turned: 999 # radians (1 rad approx 57 deg, approx 60 deg) + diff --git a/test/processor_imu.yaml b/test/yaml/processor_imu.yaml similarity index 100% rename from test/processor_imu.yaml rename to test/yaml/processor_imu.yaml diff --git a/test/yaml/sensor_force_torque_inertial.yaml b/test/yaml/sensor_force_torque_inertial.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e6d323ba4b29ca53840bd4ae4618ca6348d2d057 --- /dev/null +++ b/test/yaml/sensor_force_torque_inertial.yaml @@ -0,0 +1,10 @@ +force_noise_std: 0.1 # std dev of force noise in N +torque_noise_std: 0.1 # std dev of torque noise in N/m +acc_noise_std: 0.01 # std dev of acc noise in m/s2 +gyro_noise_std: 0.01 # std dev of gyro noise in rad/s +acc_drift_std: 0.00001 # std dev of acc drift m/s2/sqrt(s) +gyro_drift_std: 0.00001 # std dev of gyro drift rad/s/sqrt(s) + +com: [0,0,0.0341] # center of mass [m] +inertia: [0.017598,0.017957,0.029599] # moments of inertia i_xx, i_yy, i_zz [kg m2] +mass: 1.952 # mass [kg] \ No newline at end of file diff --git a/test/sensor_imu.yaml b/test/yaml/sensor_imu.yaml similarity index 100% rename from test/sensor_imu.yaml rename to test/yaml/sensor_imu.yaml