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/.gitignore b/.gitignore index 7ecae7d373f3c31b6d5e51edd4c90749667984b3..04169eb5e2228b32f411432a3680e120c71f03e6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,3 @@ src/CMakeFiles/cmake.check_cache src/examples/map_apriltag_save.yaml \.vscode/ - -wolf.found 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 9ca4dd5c2fccc846c026b27a12f511560f160c58..c2538cb479c738ed6becd5a484ed21ecd3c88e2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ MESSAGE("Starting ${PROJECT_NAME} CMakeLists ...") SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) -set(INCLUDE_INSTALL_DIR include/iri-algorithms/wolf) +set(INCLUDE_INSTALL_DIR include/wolf) set(LIB_INSTALL_DIR lib/) IF (NOT CMAKE_BUILD_TYPE) @@ -29,7 +29,7 @@ include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) if(COMPILER_SUPPORTS_CXX14) message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has C++14 support.") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + set(CMAKE_CXX_STANDARD 14) else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.") endif() @@ -93,65 +93,71 @@ IF(EXISTS "${WOLF_CONFIG_DIR}" AND NOT IS_DIRECTORY "${WOLF_CONFIG_DIR}") message(FATAL_ERROR "Bug: Specified CONFIG_DIR: " "${WOLF_CONFIG_DIR} exists, but is not a directory.") ENDIF() + # Configure config.h configure_file(${CMAKE_CURRENT_SOURCE_DIR}/internal/config.h.in "${WOLF_CONFIG_DIR}/config.h") -include_directories("${PROJECT_BINARY_DIR}/conf") - -# ============ INCLUDES ============ -INCLUDE_DIRECTORIES(BEFORE "include") # ============ HEADERS ============ SET(HDRS_MATH -include/bodydynamics/math/force_torque_delta_tools.h +include/${PROJECT_NAME}/math/force_torque_delta_tools.h +include/${PROJECT_NAME}/math/force_torque_inertial_delta_tools.h ) SET(HDRS_CAPTURE -include/bodydynamics/capture/capture_force_torque_preint.h -include/bodydynamics/capture/capture_inertial_kinematics.h -include/bodydynamics/capture/capture_leg_odom.h -include/bodydynamics/capture/capture_point_feet_nomove.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/bodydynamics/factor/factor_force_torque.h -include/bodydynamics/factor/factor_force_torque_preint.h -include/bodydynamics/factor/factor_inertial_kinematics.h -include/bodydynamics/factor/factor_point_feet_nomove.h -include/bodydynamics/factor/factor_point_feet_altitude.h +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_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/bodydynamics/feature/feature_force_torque.h -include/bodydynamics/feature/feature_force_torque_preint.h -include/bodydynamics/feature/feature_inertial_kinematics.h +include/${PROJECT_NAME}/feature/feature_force_torque.h +include/${PROJECT_NAME}/feature/feature_inertial_kinematics.h ) SET(HDRS_PROCESSOR -include/bodydynamics/processor/processor_force_torque_preint.h -include/bodydynamics/processor/processor_inertial_kinematics.h -include/bodydynamics/processor/processor_point_feet_nomove.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/bodydynamics/sensor/sensor_force_torque.h -include/bodydynamics/sensor/sensor_inertial_kinematics.h -include/bodydynamics/sensor/sensor_point_feet_nomove.h +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 ) @@ -221,28 +227,28 @@ install( ${LIB_INSTALL_DIR}/${PLUGIN_NAME}/cmake ) -# Specifies include directories to use when compiling the plugin target -# This way, include_directories does not need to be called in plugins depending on this one -target_include_directories(${PLUGIN_NAME} INTERFACE +target_include_directories(${PLUGIN_NAME} PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/conf> $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}> ) #install headers INSTALL(FILES ${HDRS_CAPTURE} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/capture) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/capture) INSTALL(FILES ${HDRS_FACTOR} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/factor) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/factor) INSTALL(FILES ${HDRS_FEATURE} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/feature) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/feature) INSTALL(FILES ${HDRS_MATH} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/math) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/math) INSTALL(FILES ${HDRS_PROCESSOR} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/processor) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/processor) INSTALL(FILES ${HDRS_SENSOR} - DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/sensor) + DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/sensor) INSTALL(FILES "${WOLF_CONFIG_DIR}/config.h" -DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/internal) +DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/internal) export(PACKAGE ${PLUGIN_NAME}) 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..bdec1d9bb1fa78d0d15d705335b7c223854b30d4 --- /dev/null +++ b/include/bodydynamics/factor/factor_angular_momentum.h @@ -0,0 +1,180 @@ +//--------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 + */ +class FactorAngularMomentum : public FactorAutodiff<FactorAngularMomentum, 3, 3, 6, 3, 4> // State Blocks: L, I, i, q +{ + public: + FactorAngularMomentum(const FeatureBasePtr& _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 + const T* const _q, // quaternion + T* _res) const; // residual + + template <typename D1, typename D2, typename D3, typename D4, typename D5> + bool residual(const MatrixBase<D1>& _L, + const MatrixBase<D2>& _I, + const MatrixBase<D3>& _i, + const QuaternionBase<D4>& _q, + MatrixBase<D5>& _res) const; + + Vector3d residual() const; + + private: + Vector3d measurement_ang_vel_; // gyroscope measurement data + Matrix3d sqrt_info_upper_; +}; + +inline FactorAngularMomentum::FactorAngularMomentum(const FeatureBasePtr& _ftr, + const ProcessorBasePtr& _processor, + bool _apply_loss_function, + FactorStatus _status) + : FactorAutodiff<FactorAngularMomentum, 3, 3, 6, 3, 4>( + "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'), // current linear momentum + _ftr->getCapture()->getStateBlock('I'), // IMU bias + _processor->getSensor()->getStateBlock('i'), // moment of inertia + _ftr->getFrame()->getStateBlock('O')), // quaternion + measurement_ang_vel_(_ftr->getMeasurement()), + sqrt_info_upper_(_ftr->getMeasurementSquareRootInformationUpper()) // Buscar funcio correcte +{ + // +} + +template <typename D1, typename D2, typename D3, typename D4, typename D5> +inline bool FactorAngularMomentum::residual(const MatrixBase<D1>& _L, + const MatrixBase<D2>& _I, + const MatrixBase<D3>& _i, + const QuaternionBase<D4>& _q, + MatrixBase<D5>& _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)*q.conjugate()*L + * + * Compute error between them + * err = w_m - w_b - i^(-1)*q.conjugate()*L + * + * Compute residual + * res = W * err , with W the sqrt of the info matrix. + */ + + typedef typename D5::Scalar T; + + Matrix<T, 3, 1> w_real = measurement_ang_vel_ - _I.template segment<3>(3); + Matrix<T, 3, 1> L_local; + L_local = _q.conjugate()*_L; //we transform que _L from the global frame to the local frame + const auto& Lx = L_local(0); + const auto& Ly = L_local(1); + const auto& Lz = L_local(2); + const auto& ixx = _i(0); + const auto& iyy = _i(1); + const auto& 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 = getCapture()->getStateBlock('I')->getState(); + Vector3d i = getSensor()->getStateBlock('i')->getState(); + Quaterniond q; + q.coeffs() = getFrame()->getStateBlock('O')->getState(); + + residual(L, I, i, q, res); + return res; +} + +template <typename T> +inline bool FactorAngularMomentum::operator()(const T* const _L, const T* const _I, const T* const _i, const T* const _q, 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<const Quaternion<T>> q(_q); + Map<Matrix<T, 3, 1>> res(_res); + + residual(L, I, i, q, res); + + return true; +} + +} // namespace wolf + +#endif // FACTOR_ANGULAR_MOMENTUM_H 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..e5a87d1bf0c6898ecbb5b5a10b3a5a4b4ad709f1 --- /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_angvel = + J_acc_angacc * J_angacc_angvel - angvel_x * J_angvelcom_angvel + skew(_angvel.cross(_com)); // (37) + _J_acc_force = Matrix<S, 3, 3>::Identity() / _mass; // (59) + _J_acc_torque = J_acc_angacc * J_angacc_torque; // (?) + _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..91a58b87620b66168be69be70ab84586917e605c --- /dev/null +++ b/include/bodydynamics/processor/processor_force_torque_inertial_dynamics.h @@ -0,0 +1,266 @@ +//--------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_; + + 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..21f42957039fea53471e2cef9eb60ef67d96e7ad --- /dev/null +++ b/include/bodydynamics/sensor/sensor_force_torque_inertial.h @@ -0,0 +1,156 @@ +//--------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 accb_initial_std; + double gyrob_initial_std; + double acc_drift_std; + double gyro_drift_std; + double force_noise_std; + double torque_noise_std; + Vector3d com; + Vector3d inertia; + double mass; + bool imu_bias_fix, com_fix, inertia_fix, mass_fix; + + 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"); + accb_initial_std = _server.getParam<double>(prefix + _unique_name + "/accb_initial_std"); + gyrob_initial_std = _server.getParam<double>(prefix + _unique_name + "/gyrob_initial_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"); + imu_bias_fix = _server.getParam<bool> (prefix + _unique_name + "/imu_bias_fix"); + com_fix = _server.getParam<bool> (prefix + _unique_name + "/com_fix"); + inertia_fix = _server.getParam<bool> (prefix + _unique_name + "/inertia_fix"); + mass_fix = _server.getParam<bool> (prefix + _unique_name + "/mass_fix"); + } + ~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" // + + "accb_initial_std: " + std::to_string(accb_initial_std) + "\n" // + + "gyrob_initial_std: " + std::to_string(gyrob_initial_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/capture/capture_inertial_kinematics.cpp b/src/capture/capture_inertial_kinematics.cpp index 3640f6b966c49cde3ed4fb8f3db94856fbf14bc7..3f3c387b70822ef2989996d0eb5de4e8c1ab50b9 100644 --- a/src/capture/capture_inertial_kinematics.cpp +++ b/src/capture/capture_inertial_kinematics.cpp @@ -23,6 +23,8 @@ #include "bodydynamics/capture/capture_inertial_kinematics.h" #include "bodydynamics/sensor/sensor_inertial_kinematics.h" #include "core/state_block/state_quaternion.h" +#include "core/state_block/state_block_derived.h" + namespace wolf { @@ -37,7 +39,7 @@ CaptureInertialKinematics::CaptureInertialKinematics(const TimeStamp& _init_ts, _sensor_ptr, nullptr, nullptr, - std::make_shared<StateBlock>(_bias_pbc, false)), + std::make_shared<StateParams3>(_bias_pbc, false)), data_(_data), B_I_q_(_B_I_q), B_Lc_m_(_B_Lc_m) @@ -56,7 +58,7 @@ CaptureInertialKinematics::CaptureInertialKinematics(const TimeStamp& _init_ts, _sensor_ptr, nullptr, nullptr, - std::make_shared<StateBlock>(3, false)), + std::make_shared<StateParams3>(Vector3d::Zero(), false)), data_(_data), B_I_q_(_B_I_q), B_Lc_m_(_B_Lc_m) 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..7739f6d27f7447dc207f596b2ca81a9f46739e65 --- /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 ", getName(), " vote: time span"); + return true; + } + // buffer length + if (getBuffer().size() > params_motion_force_torque_->max_buff_length) + { + WOLF_DEBUG("PM ", getName(), " vote: buffer length"); + return true; + } + + // Some other measure of movement? + + // WOLF_DEBUG( "PM ", getName(), " vote: 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..15c72e2f7a5dda65029222ad07e00b37c144dafe --- /dev/null +++ b/src/processor/processor_force_torque_inertial_dynamics.cpp @@ -0,0 +1,522 @@ +//--------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_angular_momentum.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 gyro_cov = motion.data_cov_.block<3, 3>(3, 3); + + auto ftr_base = FeatureBase::emplace<FeatureBase>(_capture_own, "FeatureBase", measurement_gyro, gyro_cov); + + FactorBase::emplace<FactorAngularMomentum>(ftr_base, ftr_base, shared_from_this(), params_->apply_loss_function); + + // 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 + params_->apply_loss_function); // 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 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 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.segment<6>(0) = awd - _bias; // awt = awd - awb + _tangent.segment<3>(6) = fd; // ft = fd + _tangent.segment<3>(9) = td - c.cross(fd); // tt = td - 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(). This function does: + * + * angacc = I.inv * ( tt - wt x ( I * wt ) ) + * --> J_aa_i, J_aa_tt, J_aa_wt + * acc_dyn = ft / _mass - angacc x com - wt x ( wt x com ) + * --> J_ad_ft, J_ad_m, J_ad_aa, J_ad_c, J_ad_wt + * ==> J_ad_wt, J_ad_ft, J_ad_tt, J_ad_c, J_ad_i, J_ad_m + * + * delta_p_imu = 0.5 * at * dt^2 --> J_dpi_at + * delta_v_imu = at * _dt --> J_dvi_at + * delta_p_dyn = 0.5 * acc_dyn * dt^2 --> J_dpd_ad + * delta_v_dyn = acc_dyn * _dt --> J_dvd_ad + * delta_L = tt * _dt --> J_dL_tt + * delta_q = exp_q(wt * _dt) --> J_dq_wt + * + * Assembling Jacobians gives: + * + * at wt ft tt + * J_delta_tangent = [ J_dpi_at 0 0 0 + * J_dvi_at 0 0 0 + * 0 J_dpd_wt J_dpd_ft J_dpd_tt + * 0 J_dvd_wt J_dvd_ft J_dvd_tt + * 0 0 0 J_dL_tt + * 0 J_dq_wt 0 0 ] + * with + * J_dpd_wt = J_dpd_ad * ( J_ad_wt + J_ad_aa * J_aa_wt ) + * J_dpd_ft = J_dpd_ad * J_ad_ft + * J_dpd_tt = J_dpd_ad * J_ad_aa * J_aa_tt + * J_dvd_wt = J_dvd_ad * ( J_ad*wt + J_ad_aa * J_aa_wt ) + * J_dvd_ft = J_dvd_ad * J_ad_ft + * J_dvd_tt = J_dvd_ad * J_ad_aa * J_aa_tt + * + * c i m + * J_delta_model = [ 0 0 0 + * 0 0 0 + * J_dpd_c J_dpd_i J_dpd_m + * J_dvd_c J_dvd_i J_dvd_m + * 0 0 0 + * 0 0 0 ] + * with + * J_dpd_c = J_dpd_ad * J_ad_c + * J_dpd_i = J_dpd_ad * J_ad_aa * J_aa_i + * J_dpd_m = J_dpd_ad * J_ad_m + * J_dvd_c = J_dvd_ad * J_ad_c + * J_dvd_i = J_dvd_ad * J_ad_aa * J_aa_i + * J_dvd_m = J_dvd_ad * J_ad_m + * + * 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 + */ + + /* + * 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); + + // 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 ", getName(), " vote: time span"); + return true; + } + // buffer length + if (getBuffer().size() > params_force_torque_inertial_dynamics_->max_buff_length) + { + WOLF_DEBUG("PM ", getName(), " 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 ", getName(), " 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 ", getName(), " 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..0c3a35d31d95e34543339bfe1e706264df19e287 --- /dev/null +++ b/src/sensor/sensor_force_torque_inertial.cpp @@ -0,0 +1,90 @@ +//--------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(), _params->imu_bias_fix), // IMU bias + 12, + false, + false, + false), + params_fti_(_params) +{ + addStateBlock('C', FactoryStateBlock::create("StateParams3", params_fti_->com, params_fti_->com_fix)); // centre of mass + addStateBlock('i', FactoryStateBlock::create("StateParams3", params_fti_->inertia, params_fti_->inertia_fix)); // inertial vector + addStateBlock('m', FactoryStateBlock::create("StateParams1", Vector1d(params_fti_->mass), params_fti_->mass_fix)); // total mass + setStateBlockDynamic('I', false); + setStateBlockDynamic('C', false); + setStateBlockDynamic('i', false); + setStateBlockDynamic('m', false); + + VectorXd noise_std_vector(12); + Vector3d acc_noise_std_vector = Vector3d::Ones() * params_fti_->acc_noise_std; + Vector3d gyro_noise_std_vector = Vector3d::Ones() * params_fti_->gyro_noise_std; + Vector3d force_noise_std_vector = Vector3d::Ones() * params_fti_->force_noise_std; + Vector3d torque_noise_std_vector =Vector3d::Ones() * params_fti_->torque_noise_std; + noise_std_vector << acc_noise_std_vector, gyro_noise_std_vector, force_noise_std_vector, torque_noise_std_vector; + setNoiseStd(noise_std_vector); +} + +// 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/src/sensor/sensor_inertial_kinematics.cpp b/src/sensor/sensor_inertial_kinematics.cpp index 33369c5f00e3d7a65383e28cb0d65d102bcc4de7..5cfaa76228efa3189d41ac757e3be82f6ceb8184 100644 --- a/src/sensor/sensor_inertial_kinematics.cpp +++ b/src/sensor/sensor_inertial_kinematics.cpp @@ -21,7 +21,7 @@ //--------LICENSE_END-------- #include "bodydynamics/sensor/sensor_inertial_kinematics.h" -#include "core/state_block/state_block.h" +#include "core/state_block/state_block_derived.h" #include "core/state_block/state_quaternion.h" namespace wolf { @@ -30,7 +30,7 @@ SensorInertialKinematics::SensorInertialKinematics(const Eigen::VectorXd& _extri SensorBase("SensorInertialKinematics", nullptr, // no position nullptr, // no orientation - std::make_shared<StateBlock>(Eigen::Vector3d(0,0,0), false), // bias; false = unfixed + std::make_shared<StateParams3>(Eigen::Vector3d(0,0,0), false), // bias; false = unfixed (Eigen::Vector6d() << _params->std_pbc,_params->std_pbc,_params->std_pbc, _params->std_vbc,_params->std_vbc,_params->std_vbc).finished(), false, false, true), diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5850e2db6763d74e6385f16417ed48102091ba5b..c6d84c19a4e3c5bf6140d5ae057524ad32cffd3c 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,7 @@ 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_simulation_problem_force_torque_inertial_dynamics gtest_simulation_problem_force_torque_inertial_dynamics.cpp) + +wolf_add_gtest(gtest_solve_problem_force_torque_inertial_dynamics gtest_solve_problem_force_torque_inertial_dynamics.cpp) + diff --git a/test/dades_simulacio_pep.csv b/test/dades_simulacio_pep.csv new file mode 100644 index 0000000000000000000000000000000000000000..e52426eecfc24f699e66baf17a7547171b0dcd50 --- /dev/null +++ b/test/dades_simulacio_pep.csv @@ -0,0 +1,381 @@ +timestamp,pos_x,pos_y,pos_z,quat_x,quat_y,quat_z,quat_w,vlin_x,vlin_y,vlin_z,vang_x,vang_y,vang_z,f_x,f_y,f_z,m_x,m_y,m_z +0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,30.24140688670505,0.00013608714916246711,0.0014521640071535336,5.128857392352534e-05 +0.02,1.225134672257865e-08,-1.2259260745627694e-09,0.002128145411468811,1.9224644385208175e-06,1.9212242769870645e-05,4.1019743724623865e-07,0.9999999998135127,-1.4317549759835751e-06,1.4326791822140724e-07,0.10640727057210408,0.00019224644386403226,0.0019212242771064925,4.101974372717375e-05,0.0,0.0,29.887893140652935,5.355186669397938e-05,0.000632055519802166,4.980384573760899e-05 +0.04,3.164793119129389e-07,-3.145199751668115e-08,0.006314985419629783,4.601336319320625e-06,4.678476956232382e-05,1.2166216785179206e-06,0.9999999988942664,1.3954502268081405e-06,-1.4561235248549083e-07,0.20934200098822997,0.00026788676993719067,0.002757252725995719,8.064227015362827e-05,0.0,0.0,29.540464010158914,6.529613184591554e-06,0.0001467468847405895,4.8374456304805384e-05 +0.06,1.2380977715680128e-06,-1.2186983166693075e-07,0.012492280418762047,7.372358116038826e-06,7.629798429331784e-05,2.406581495967047e-06,0.9999999970592371,8.064988856122653e-06,-8.22739440841769e-07,0.30886475336605246,0.00027710020369252434,0.002951321689266104,0.00011899536599298057,0.0,0.0,29.198910712249972,-1.8547764069065664e-05,-0.00012464283122537445,4.6998259156440075e-05 +0.08,3.097877446883576e-06,-3.0210021946895046e-07,0.020592960737205568,9.881332287758513e-06,0.00010416347763031251,3.968358568423615e-06,0.9999999945182907,1.98959164523503e-05,-2.0235505135256084e-06,0.4050340262559032,0.00025089220835405955,0.0027865498928493805,0.00015617630694591205,0.0,0.0,28.863025564019104,-3.0351715194498752e-05,-0.00026189346931571045,4.5673342991900894e-05 +0.1,6.184536975284903e-06,-5.976110773142468e-07,0.0305510856069671,1.196158501223723e-05,0.00012856755356196802,5.890994766694367e-06,0.9999999916463004,3.845464710395817e-05,-3.900771944711037e-06,0.4979062661756617,0.00020801493173149216,0.0024404086842509344,0.00019226117408965902,0.0,0.0,28.53260186078871,-3.437789080273923e-05,-0.0003172687898949089,4.4397966278411e-05 +0.12,1.0741866558453952e-05,-1.0286174094126725e-06,0.042301802349252376,1.3556311555146249e-05,0.00014877933433270332,8.164145814907884e-06,0.9999999888071414,6.491503804013118e-05,-6.559699256681581e-06,0.5875358781141918,0.0001594553377684207,0.0020211798764281767,0.00022731143112637397,0.0,0.0,28.20743377978916,-3.4066289298806396e-05,-0.00032464551987276824,4.3170511002704366e-05 +0.14,1.6964758003386982e-05,-1.6098581052830123e-06,0.05578130575425297,1.4669972065107598e-05,0.00016470242579285052,1.07779808308616e-05,0.9999999862708688,9.986526609854465e-05,-1.004211670665144e-05,0.6739752352525765,0.00011134016457341262,0.0015923118001836536,0.0002613784838798486,0.0,0.0,27.887316305105017,-3.1531907447757135e-05,-0.00030565857607245395,4.198945051493422e-05 +0.16,2.500042579451631e-05,-2.350860516675195e-06,0.07092679764020307,1.533841142411551e-05,0.00017658869745700473,1.3723113150197237e-05,0.9999999841964204,0.00014333164313205587,-1.433221477027244e-05,0.757274688107806,6.680824198505375e-05,0.0011886307843512953,0.0002945068023644371,0.0,0.0,27.572045165552257,-2.804161484570855e-05,-0.0002738395995421916,4.085332740515585e-05 +0.18,3.495241247522585e-05,-3.2564484559282456e-06,0.0876764465807755,1.561096353220603e-05,0.000184859339958222,1.69905522905314e-05,0.9999999826473215,0.00019489192643186188,-1.937047455305273e-05,0.8374825731884221,2.7208863368858957e-05,0.0008270689021236481,0.0003267360381102831,0.0,0.0,27.261416778835994,-2.432504458349527e-05,-0.00023740142256944985,3.9760738377768134e-05 +0.2,4.688585843664052e-05,-4.327344872539917e-06,0.10596934779176233,1.554014637097892e-05,0.00018999631381550708,2.0571670093161243e-05,0.9999999816183553,0.00025381091328892526,-2.5068584840409175e-05,0.9146452211820351,-7.139186698592418e-06,0.0005137031066314508,0.0003581024473668731,0.0,0.0,26.955228197803766,-2.0776635640817886e-05,-0.00020110674489681735,3.871032394162904e-05 +0.22,6.083310972943089e-05,-5.560782674882313e-06,0.12574548316854894,1.5176092622285518e-05,0.00019247944176311945,2.4458176297022176e-05,0.999999981061574,0.0003191658105763353,-3.132235016220051e-05,0.9888069646673194,-3.647410731137493e-05,0.0002483195893832517,0.00038863983868462926,0.0,0.0,26.653277056500798,-1.7586204964503693e-05,-0.00016751672606418744,3.770076138167401e-05 +0.24,7.679912868170565e-05,-6.9510768667761574e-06,0.14694568146580123,1.4563870747744718e-05,0.00019275261391040672,2.8642100326515915e-05,0.9999999809069766,0.0003899482422690305,-3.802159791118977e-05,1.0600101453376,-6.13020498670971e-05,2.7325064132054024e-05,0.00041838019682400354,0.0,0.0,26.355361514911202,-1.4822605152176038e-05,-0.00013781976443549837,3.673075994254958e-05 +0.26,9.47664248860185e-05,-8.490135874145494e-06,0.169511578610433,1.3742514482432002e-05,0.00019120813676605711,3.311577712114471e-05,0.9999999810769683,0.0004651400441809517,-4.5057099434768435e-05,1.1282951207251761,-8.222628020747176e-05,-0.00015443884682139983,0.0004473540879359478,0.0,0.0,26.06128020191197,-1.248665044184527e-05,-0.00011237648127615518,3.579905749707245e-05 +0.28,0.0001146993832187328,-1.0167904779292288e-05,0.19338557813850527,1.2745013010223804e-05,0.0001881820159120577,3.787183555034989e-05,0.9999999814954086,0.0005437650023037907,-5.2324952084938885e-05,1.1937002704183637,-9.98511068502665e-05,-0.0003026022483523487,0.0004755909181486874,0.0,0.0,25.770832156275954,-1.0543995748291124e-05,-9.107345409031797e-05,3.4904418193991524e-05 +0.3,0.00013654796202310516,-1.1972741640004973e-05,0.21851081174630843,1.1598797380307315e-05,0.00018395549439779657,4.290318844542166e-05,0.99999998209258,0.0006249207221359291,-5.972899744876074e-05,1.2562620017647255,-0.00011473224925839966,-0.000422641401451132,0.0005031190949293762,0.0,0.0,25.48381676568331,-8.945026727102956e-06,-7.354870443132544e-05,3.4045630751051904e-05 +0.32,0.00016025078983072265,-1.3891732485642351e-05,0.24483109994552332,1.0326442259004211e-05,0.00017875986641732912,4.820302361314376e-05,0.9999999828073715,0.0007077952482184457,-6.718182979192059e-05,1.3160147550539718,-0.0001273552949626109,-0.0005195511959160284,0.0005299661248519798,0.0,0.0,25.20003370373022,-7.636417926670802e-06,-5.933180811457106e-05,3.32215071621932e-05 +0.34,0.00018573771765226005,-1.5910952505941744e-05,0.2722909128120399,8.946419825077345e-06,0.00017278271549180994,5.376479542160372e-05,0.9999999835877207,0.000791672693879034,-7.460486386955082e-05,1.3729910081731154,-0.0001381304756607396,-0.0005977027007750253,0.0005561586705364821,0.0,0.0,24.919282864910798,-6.567422575320947e-06,-4.7927735263297855e-05,3.2430881669300074e-05 +0.36,0.00021293189584760944,-1.801568139927669e-05,0.30083533081772834,7.4738170352390416e-06,0.00016617447059009947,5.9582216700792056e-05,0.9999999843900733,0.0008759314520642052,-8.192782837096444e-05,1.4272212807251576,-0.00014739632136483074,-0.0006608113712693733,0.0005817225820474456,0.0,0.0,24.64136429751868,-5.69289873331158e-06,-3.886411208742224e-05,3.167260989641808e-05 +0.38,0.00024175144651356973,-2.0190580482276536e-05,0.33041000573419105,5.920973576027082e-06,0.0001590546571491374,6.564925080213585e-05,0.9999999851783669,0.0009600377908456243,-8.908795302508147e-05,1.4787341376012537,-0.00015542758413341926,-0.0007119675595164921,0.0006066829129003706,0.0,0.0,24.366078134381194,-4.974373055688375e-06,-3.171515259203783e-05,3.0945568083510296e-05 +0.4,0.00027211079856612533,-2.2419838396331563e-05,0.36096112159726595,4.298025521235641e-06,0.0001515175281673838,7.196010372789714e-05,0.9999999859228544,0.0010435369191785571,-9.602903355600483e-05,1.527556191995093,-0.00016244466125909436,-0.0007536985076862657,0.0006310639273148624,0.0,0.0,24.093224521305874,-4.37997698465864e-06,-2.6111103254233825e-05,3.0248652376453937e-05 +0.42,0.00030392174605596784,-2.46872913318105e-05,0.3924353557207975,2.6133556236762282e-06,0.00014363695178116296,7.850921628435798e-05,0.9999999865989496,0.0011260429953360142,-0.00010270049204560422,1.5737121078472105,-0.00016862292692206813,-0.0007880427000593195,0.0006548891029633633,0.0,0.0,23.82260354308645,-3.883782088376364e-06,-2.1739039836821128e-05,2.95807781491162e-05 +0.44,0.00033709428250247906,-2.697652277570891e-05,0.42477983974792916,8.739596396764638e-07,0.00013547054576643254,8.52912562395986e-05,0.9999999871861844,0.001207229057748464,-0.00010905650013402395,1.6172246017060508,-0.00017410112519403655,-0.0008166251707300359,0.000678181131856761,0.0,0.0,23.55401514688409,-3.4648590918617828e-06,-1.833881567026019e-05,2.8940879342710454e-05 +0.46,0.00037153725622228717,-2.927094695306558e-05,0.45794212072790047,-9.142580286413045e-07,0.00012706311323446766,9.230111048273954e-05,0.9999999876673171,0.001286817480569591,-0.00011505519663431869,1.6581144439918098,-0.00017898843551235287,-0.0008407273843924232,0.0007009619209358499,0.0,0.0,23.28725906276722,-3.1062536953729847e-06,-1.5696584158342297e-05,2.8327907813671338e-05 +0.48,0.0004071588843600551,-3.1553879414390404e-05,0.4918701222060494,-2.746244927451395e-06,0.00011844946562191448,9.953387719043742e-05,0.9999999880275947,0.001364571279465591,-0.00012065800684957773,1.696400459648282,-0.0001833700943022203,-0.0008613485070478094,0.0007232525932345714,0.0,0.0,23.022134721159553,-2.7939881004357403e-06,-1.3637397384913541e-05,2.774083268700711e-05 +0.5,0.0004438671568986315,-3.380859763213172e-05,0.5265121053144176,-4.617603312147996e-06,0.00010965673109070238,0.00010698485801072983,0.9999999882541595,0.001440286399052448,-0.00012582905553707214,1.7320995281671026,-0.00018731161013678182,-0.0008792568651640862,0.0007450734900301386,0.0,0.0,22.758441166909613,-2.516144231856643e-06,-1.201777398018855e-05,2.7178639715342917e-05 +0.52,0.0004815701564426534,-3.6018394005446324e-05,0.5618166298500358,-6.524422171465399e-06,0.00010070624669678814,0.00011464955027574895,0.9999999883355821,0.001513784984536765,-0.00013053465678020834,1.7652265829678462,-0.0001908616887990171,-0.0008950315689687848,0.0007664441741152649,0.0,0.0,22.49597696965455,-2.2620524688932164e-06,-1.0718733671533087e-05,2.6640330642770012e-05 +0.54,0.0005201763150304116,-3.816662332264446e-05,0.5977325153276241,-8.463127112576458e-06,9.161512638575555e-05,0.00012252363925452665,0.9999999882615008,0.0015849095608419749,-0.0001347428601365797,1.7957946101163627,-0.00019405401818309043,-0.0009090949299985471,0.0007873834341614194,0.0,0.0,22.234540130106808,-2.0215909726228887e-06,-9.639544573558645e-06,2.612492257810989e-05 +0.56,0.0005595946255653072,-4.023674647932752e-05,0.6342088019930675,-1.0430338277520445e-05,8.239758734523268e-05,0.00013060299045586693,0.9999999880223522,0.0016535179997827969,-0.00013842303188087653,1.8238146463624958,-0.00019690807011360425,-0.0009217366257355021,0.0008079092900505667,0.0,0.0,21.973927981841808,-1.7845907109848014e-06,-8.692274912180942e-06,2.5631447378592437e-05 +0.58,0.000599734822569925,-4.22123720808402e-05,0.6711947117836287,-1.2422727964662252e-05,7.306610652389625e-05,0.0001388836419895804,0.9999999876091769,0.0017194791392778374,-0.000141545451874534,1.8492957764768965,-0.00019942906926489473,-0.0009331306833716197,0.0008280389990108131,0.0,0.0,21.713937088108732,-1.5403367776833932e-06,-7.797150893829041e-06,2.515895104800238e-05 +0.6,0.0006405075447391281,-4.4077297453914274e-05,0.7086396092204232,-1.4436870999247184e-05,6.3632468399211e-05,0.00014736179699255714,0.9999999870134931,0.0017826689201577296,-0.00014408090980208148,1.8722451298649925,-0.00020160726608800175,-0.0009433463554822843,0.0008477890623779501,0.0,0.0,21.45436313312252,-1.2771554757096126e-06,-6.8786745212046085e-06,2.4706493152370404e-05 +0.62,0.0006818244900890152,-4.581555053893739e-05,0.7464929622182092,-1.646908211867505e-05,5.410875496982907e-05,0.00015603381612440723,0.99999998622723,0.0018429669198775345,-0.00014600028869160502,1.8926678764342992,-0.00020341663579537278,-0.0009523538982479262,0.0008671752328109703,0.0,0.0,21.195000807223682,-9.820774751201355e-07,-5.862434029113572e-06,2.4273146256859216e-05 +0.64,0.0007235985732570138,-4.741143411702785e-05,0.784704302797023,-1.851523572320959e-05,4.450831973999729e-05,0.00016489621013581664,0.9999999852427176,0.0019002531840888656,-0.0001472741284450862,1.9105672216891183,-0.0002048131118278433,-0.0009600261730955736,0.0008862125218045072,0.0,0.0,20.935643685211176,-6.405692328792512e-07,-4.6725349894805035e-06,2.3857995387433176e-05 +0.66,0.0007657440936044365,-4.884957383224674e-05,0.8232231876796332,-2.0570564317383497e-05,3.484677921072753e-05,0.00017394563251140198,0.9999999840527353,0.0019544052835758583,-0.0001478721673405305,1.9259444010242788,-0.00020573245119390835,-0.0009661368970309222,0.0009049152073612103,0.0,0.0,20.676084097061988,-2.363272203487199e-07,-3.2295850930230685e-06,2.3460137520346756e-05 +0.68,0.00080817692211612,-5.01149714871848e-05,0.8619991587581588,-2.2629432864851066e-05,2.5143048028651446e-05,0.00018317887218771836,0.9999999826506182,0.002005295553490065,-0.00014776286501515064,1.9387986731868971,-0.00020608782138622535,-0.0009703562780524738,0.0009232968417082108,0.0,0.0,20.41611299014797,2.4886809246726926e-07,-1.4491755564449704e-06,2.3078681104287097e-05 +0.7000000000000001,0.0008508147146017904,-5.119306510922231e-05,0.9009817034125251,-2.4685087133546914e-05,1.5420437336278718e-05,0.00019259284634617788,0.9999999810304259,0.002052788502806585,-0.0001469129162397186,1.9491273128721744,-0.00020576719426255668,-0.0009722446963724164,0.0009413702589631916,0.0,0.0,20.15551978194459,8.352392587562818e-07,7.591859008115875e-07,2.2712745617763053e-05 +0.72,0.0008935771583092904,-5.2069797295202786e-05,0.9401202146626866,-2.6729374950802616e-05,5.707829857251853e-06,0.00020218459327994823,0.9999999791871755,0.002096738413607229,-0.0001452867708143035,1.9569256024160053,-0.0002046306311694576,-0.0009712450324566849,0.0009591475826746313,0.0,0.0,19.894092202096353,1.5450387185200753e-06,3.492723536835385e-06,2.23614611660418e-05 +0.74,0.0009363862586978747,-5.273169333505811e-05,0.9793639511357344,-2.875244014628547e-05,-3.959060321305788e-06,0.00021195126533338508,0.9999999771171418,0.0021369871816076104,-0.0001428461811272871,1.9621868225436647,-0.00020250754515343576,-0.0009666741990283394,0.0009766402331763623,0.0,0.0,19.631616122559407,2.4025127408622282e-06,6.855645507175545e-06,2.2023968121615554e-05 +0.76,0.0009791666727349104,-5.3165950581615595e-05,1.0186619968281212,-3.0742389856649796e-05,-1.3536340585725084e-05,0.00022189012191221576,0.999999974818223,0.0021733624808538874,-0.0001395498052787567,1.9649022421300921,-0.00019919402991304503,-0.0009577144048695558,0.0009938589347135492,0.0,0.0,19.367875374382386,3.433798598906357e-06,1.0959012031963589e-05,2.1699416811902633e-05 +0.78,0.0010218460946226015,-5.336054046655318e-05,1.0579632206422716,-3.2684936814569306e-05,-2.2970507674419493e-05,0.00023199852256245462,0.9999999722903677,0.002205676366337275,-0.00013535290011075399,1.965061106923386,-0.00019445035045097007,-0.0009434046610062072,0.0010108137223060576,0.0,0.0,19.10265154951177,4.666750477499981e-06,1.592022552687755e-05,2.138696725701853e-05 +0.8,0.0010643556992898497,-5.3304324453961666e-05,1.097216235675808,-3.456301925481214e-05,-3.2196938046545044e-05,0.00024227392011590381,0.9999999695360507,0.002233724457983445,-0.00013020714490609027,1.9626506271791535,-0.00018799869654038915,-0.0009226330318399689,0.0010275139483265133,0.0,0.0,18.835723785820342,6.130689457539695e-06,2.186214369204187e-05,2.1085788962501225e-05 +0.8200000000000001,0.00110663064826069,-5.29871850665428e-05,1.1363693582405012,-3.635640214352373e-05,-4.11383131962036e-05,0.000252713853900019,0.999999966560779,0.0022572858775527327,-0.00012406064277050528,1.9576559641495144,-0.00017952130650774088,-0.0008941301319079633,0.0010439682887754987,0.0,0.0,18.566868533358154,7.856073285572585e-06,2.89118140897604e-05,2.079506076738212e-05 +0.84,0.0011486106616029356,-5.2400172909227246e-05,1.1753705665868723,-3.804126456798197e-05,-4.9702997386950746e-05,0.00026331594300986575,0.9999999633735935,0.0022761241357934277,-0.00011685815261471801,1.9500602153671027,-0.00016865907463323052,-0.0008564643701474082,0.0010601847492395734,0.0,0.0,18.29585929962213,9.874082403826812e-06,3.7198828491680214e-05,2.0513970751809363e-05 +0.86,0.0011902406585421546,-5.153567034587691e-05,1.2141674593091312,-3.9589779301633556e-05,-5.778339031448902e-05,0.0002740778796398478,0.9999999599875218,0.002289989189943598,-0.00010854160996990479,1.939844398661747,-0.00015501076015683312,-0.000808039444918882,0.001076170670520538,0.0,0.0,18.022466371440913,1.2216119986252494e-05,4.685329691617168e-05,2.0241716201427698e-05 +0.88,0.0012314714679759241,-5.038757215276142e-05,1.2527072134038761,-4.0969691765205035e-05,-6.52542822288666e-05,0.00028499742247276624,0.9999999564199151,0.0022986209104575795,-9.905099933325376e-05,1.9269874348462328,-0.00013813291876485934,-0.0007470945920626131,0.0010919327339182971,0.0,0.0,17.746456510892813,1.4913225338308855e-05,5.8003444410648086e-05,1.9977503628474362e-05 +0.9,0.0012722606085263245,-4.895148307493164e-05,1.2909365419547318,-4.214390681563898e-05,-7.197124401809258e-05,0.00029607239012358256,0.9999999526925843,0.0023017542096006654,-8.83256440428264e-05,1.9114661290085264,-0.00011754067797342899,-0.000671708082966203,0.0011074769661466384,0.0,0.0,17.467592622540536,1.799540185531967e-05,7.077283768980314e-05,1.972054884087476e-05 +0.92,0.0013125731359159274,-4.7224931747085886e-05,1.3288016514139456,-4.307009298466488e-05,-7.77690896164731e-05,0.00030730065463488746,0.9999999488316204,0.002299126092177917,-7.63059814900356e-05,1.8932551503523074,-9.270947577901866e-05,-0.0005798044586913464,0.0011228087438456122,0.0,0.0,17.185633389213617,2.149086275560208e-05,8.527725273066444e-05,1.9470077049578047e-05 +0.9400000000000001,0.0013523825533415126,-4.520759990929865e-05,1.3662481984509973,-4.370031492894159e-05,-8.246045279824388e-05,0.000318680135020521,0.9999999448667624,0.00229048488895564,-6.293589145205672e-05,1.8723270105374077,-6.307787699590453e-05,-0.0004691659697845806,0.0011379327976393941,0.0,0.0,16.90033287365644,2.542519959525258e-05,0.00010162119719525631,1.922532299194753e-05 +0.96,0.00139167177815442,-4.290156525194739e-05,1.4032212463376617,-4.3980705918357205e-05,-8.583452490884345e-05,0.0003302087908528987,0.9999999408301413,0.0022756019251244596,-4.8165643120346525e-05,1.8486520404893387,-2.8051574194079355e-05,-0.0003374486708371048,0.0011528532156588086,0.0,0.0,16.61144008367137,2.9820479796827115e-05,0.00011989410108381726,1.8985531042157285e-05 +0.98,0.0014304341555470817,-4.031155556692856e-05,1.4396652208389147,-4.385119316005755e-05,-8.76560043733514e-05,0.00034188461588831516,0.9999999367542016,0.002254285858331002,-3.1955521697119344e-05,1.8221983656761673,1.2991329174000386e-05,-0.00018220359839085954,0.0011675734464123776,0.0,0.0,16.318698499049297,3.469427954361226e-05,0.00014016618274487191,1.874995525769601e-05 +1.0,0.001468674507108085,-3.744521116902669e-05,1.475523865579948,-4.324528963016294e-05,-8.766431297689452e-05,0.0003537056317226157,0.9999999326685673,0.002226399892413293,-1.4280187954796318e-05,1.7929318798967526,6.069366548599252e-05,-9.034475892517283e-07,0.0011820963008337853,0.0,0.0,16.021845559789746,4.005865649203688e-05,0.000162483981261341,1.8517859292026906e-05 +1.02,0.001506410199064022,-3.431335178872146e-05,1.510740196861797,-4.2089966886916746e-05,-8.55731381328223e-05,0.0003656698814669736,0.9999999285956027,0.002191882033266351,4.866186302216029e-06,1.7608162176942668,0.0001157110987402225,0.00020902483046624857,0.0011964239532602425,0.0,0.0,15.720612117152145,4.591906158624148e-05,0.00018686551438923393,1.828851606651494e-05 +1.04,0.0015436722117995088,-3.093024333341369e-05,1.5452564579024297,-4.030562413648687e-05,-8.107036503488823e-05,0.00037777542342974575,0.9999999245443881,0.0021507685017854773,2.5463977482170326e-05,1.7258127256136366,0.0001787024890274656,0.0004501610427606256,0.0012105579409895301,0.0,0.0,15.414721852380449,5.227317854250568e-05,0.0002132949640330506,1.8061207047117212e-05 +1.06,0.0015805061888614685,-2.7313859075896887e-05,1.579014072487553,-3.780616975755779e-05,-7.381846844056831e-05,0.0003900203247850978,0.9999999205028336,0.0021032203554712794,4.7461351404589304e-05,1.687880432676413,0.0002503186314450222,0.0007250457948897646,0.0012244991619171106,0.0,0.0,15.103890673156826,5.910965842026439e-05,0.00024171669211869862,1.7835220906830366e-05 +1.08,0.0016169734401299278,-2.348612896326248e-05,1.6119535980273325,-3.4499232844681466e-05,-6.345544208650764e-05,0.0004024026552017028,0.999999916427653,0.002049553295313277,7.076669328609663e-05,1.646976020673815,0.00033118923316386253,0.001036126732878039,0.0012382478695524273,0.0,0.0,14.78782610594287,6.64066752843473e-05,0.00027202821923844045,1.7609851258719636e-05 +1.1,0.001653151870182683,-1.9473169847869088e-05,1.644014678033785,-3.028652465444309e-05,-4.9596356511907836e-05,0.00041492048039454743,0.9999999122319575,0.0019902705487267456,9.524025551524635e-05,1.6030537952105652,0.000421907895064738,0.001385695709064288,0.0012518036644383974,0.0,0.0,14.466226714867066,7.413015154134062e-05,0.0003040715069428934,1.738439302001736e-05 +1.12,0.0016891367990783976,-1.5305488516725146e-05,1.6751359940618071,-2.5064374160441842e-05,-3.1835656975890705e-05,0.0004275718555487583,0.9999999077702839,0.0019260986238547363,0.00012068507625292176,1.5560656589159119,0.0005230146641069133,0.0017758147814023405,0.001265165480635101,0.0,0.0,14.138781597017049,8.223137033946948e-05,0.0003376213971820752,1.7158136790890644e-05 +1.1400000000000001,0.0017250416387769808,-1.1018148416065836e-05,1.705255217199113,-1.8724469977156014e-05,-9.750348221072232e-06,0.00044035481854513527,0.9999999028209746,0.0018580256272138162,0.00014683724664945182,1.5059610889437332,0.0006349753485708337,0.0022082275401313595,0.001278331565449354,0.0,0.0,13.80517003347499,9.064349858056708e-05,0.0003723692799050826,1.6930360377925884e-05 +1.16,0.0017609983841042724,-6.650889904001482e-06,1.7343089592532526,-1.1154855233176231e-05,1.7095728334155853e-05,0.00045326738289072003,0.9999998970659871,0.0017873417270810202,0.0001733556286872019,1.4526871219146964,0.0007581561602975608,0.002684249879721549,0.0012912994499198814,0.0,0.0,13.46506142074264,9.927628443806569e-05,0.00040789885681447036,1.670031621867823e-05 +1.18,0.0017971578724005456,-2.2481926851401403e-06,1.762232723877023,-2.2412459029954387e-06,4.914622215584608e-05,0.00046630753022079317,0.9999998900684505,0.0017156812285402596,0.00019981114923178423,1.396188350965737,0.0008927912849149188,0.0032046304939722476,0.0013040659065590764,0.0,0.0,13.118115676956961,0.00010800783408559944,0.00044364906755506306,1.646721287969829e-05 +1.2,0.001833689760589928,2.1407322408881417e-06,1.788960858011283,8.131218807916963e-06,8.684474576009318e-05,0.00047947320218536975,0.9999998812486538,0.0016450655951205997,0.0002256758217759048,1.3364069417998015,0.0010389395616226913,0.0037693653638232734,0.001316626889284294,0.0,0.0,12.763984424651412,0.00011667179169583264,0.000478856545860773,1.62301878957993e-05 +1.22,0.0018707821621939675,6.462035288327101e-06,1.8144265042277703,2.007562588286322e-05,0.0001306247818319582,0.0004927622914551494,0.9999998698597214,0.0015779465841785191,0.0002503116830248811,1.2732826779231874,0.0011964243020104487,0.004377441291169292,0.0013289774481265906,0.0,0.0,12.402313416022983,0.00012503722475576406,0.0005124658746931687,1.5988267781081184e-05 +1.24,0.0019086408796754083,1.0657421569920133e-05,1.8385615548557541,3.370007573149198e-05,0.00018089592585745625,0.0005061726314679754,0.9999998549651075,0.0015172484105198784,0.0002729598889154119,1.2067530501367558,0.0013647468382522916,0.00502646950588779,0.0013411116079679064,0.0,0.0,12.032746912951565,0.00013277445813686395,0.0005429815915139469,1.574066721378354e-05 +1.26,0.0019474881573701191,1.4664463389358336e-05,1.8612966092214678,4.9103164316624205e-05,0.00023802466053654363,0.0005197019870884877,0.9999998354214789,0.0014664074321276716,0.00029273030956777246,1.136753412536819,0.0015429556615896146,0.005712138928228991,0.001353022467232675,0.0,0.0,11.654935085781528,0.00013941147585438962,0.0005682745189826144,1.548561289576385e-05 +1.28,0.0019875608806281847,1.8417050222092967e-05,1.8825609359823987,6.636760491775683e-05,0.00030230733977743723,0.0005333480371492095,0.9999998098727242,0.0014294072396541454,0.0003085920845113855,1.0632172376737414,0.0017294598767311575,0.00642743724921852,0.0013647013982765962,0.0,0.0,11.268545938273384,0.00014425774471393815,0.0005852589371075356,1.5219210598408263e-05 +1.3,0.0020291081355949298,2.1845998030147914e-05,1.9022824434773342,8.555102188837693e-05,0.00037393152895423456,0.0005471083403370218,0.9999997767643242,0.001410804837338228,0.0003193661559216246,0.9860765177858607,0.0019217456318929294,0.007161486743645971,0.0013761364004810364,0.0,0.0,10.873283639653724,0.0001462384938636374,0.0005892247126642602,1.4942654591777711e-05 +1.32,0.002072388007795087,2.487985069578418e-05,1.9203876622961615,0.00010667180092793813,0.000452917697180744,0.0005609803385766387,0.9999997343938378,0.001415741883191005,0.00032372112876311417,0.9052623762082537,0.002115880478763752,0.007897579793742147,0.001387315871783246,0.0,0.0,10.468914841980165,0.0001437506023028723,0.0005732024893719823,1.4650799549180987e-05 +1.34,0.0021176635803512992,2.7445882312585917e-05,1.9368017458250475,0.00012968825184094904,0.0005390340026444215,0.0005749613117272527,0.9999996810213448,0.0014499332787079097,0.00032017408560315613,0.8207059664420872,0.0023058427848359295,0.00861048864799949,0.0013982237773624377,0.0,0.0,10.055301888741688,0.00013426943913319445,0.000526359640621421,1.4340426720078128e-05 +1.36,0.0021651980159363184,2.947133283936838e-05,1.9514484958240934,0.00015446617897862987,0.0006316653651406534,0.0005890483456810099,0.9999996150804822,0.001519610763744835,0.00030710201587349134,0.7323397237714716,0.0024823593884057793,0.009261894921148407,0.0014088410341568775,0.0,0.0,9.632433219709114,0.00011370376003089211,0.00043145575262482083,1.4003848675377564e-05 +1.3800000000000001,0.002215248683886902,3.0884893181499103e-05,1.9642504195523571,0.0001807269266487254,0.0007296074635374591,0.0006032382666609615,0.9999995355570525,0.001631396274747139,0.0002827698340219759,0.6400989419775581,0.002630947924088747,0.009792883411318763,0.0014191421648980706,0.0,0.0,9.200422135240945,7.500704300589245e-05,0.0002601256889447623,1.363326179399456e-05 +1.4000000000000001,0.002268060372989986,3.161845892188782e-05,1.9751288191422973,0.00020795880852435535,0.0008307315784578456,0.0006175275797488216,0.999999442648778,0.0017920539225420185,0.00024538725357845426,0.5439233838847661,0.0027282462416125123,0.010111029107925744,0.0014290955257488273,0.0,0.0,8.759416630676654,5.9224872971941345e-06,-3.32982957525485e-05,1.32180045953285e-05 +1.42,0.0023238581072786957,3.160906621504949e-05,1.9840038979909491,0.00023526477275006728,0.0009314521890410814,0.0006319124110505699,0.9999993388667869,0.002008026943918258,0.00019321616091891395,0.4437581279282786,0.002735621846094594,0.010070677011409535,0.00143866255877871,0.0,0.0,8.309340800579037,-0.00011341178930313056,-0.0005170911025723091,1.2749079277874663e-05 +1.44,0.0023828409309284695,3.080062606355726e-05,1.990794839685318,0.00026111948911970034,0.0010259459483275921,0.0006463885196668561,0.9999992307164067,0.0022845607830903956,0.00012478000431080736,0.3395521865473096,0.002590095273873209,0.009448086550713304,0.0014478018790237022,0.0,0.0,7.849355230911976,-0.0003122847767576542,-0.0012865142670025198,1.2225268111288317e-05 +1.46,0.0024451787348663654,2.9144725531188213e-05,1.9954197680886347,0.0002830195104950638,0.0011050902671257028,0.0006609514491073395,0.9999991309089423,0.0026240603154565586,3.9284204395819835e-05,0.23125254935531062,0.0021936247815752753,0.00791340071447829,0.001456478488285123,0.0,0.0,7.376521136508651,-0.0006310843157646406,-0.0024685852540669018,1.166883701943257e-05 +1.48,0.002511014658898052,2.6599542888705284e-05,1.9977953650908702,0.0002970166657636101,0.001155098277566318,0.0006755969367364152,0.999999060548483,0.003023196107316387,-6.25805391149617e-05,0.11878704176514422,0.0014014031305057738,0.005000293827670246,0.0014646836290186681,0.0,0.0,6.8788084060216255,-0.0010997820664007119,-0.004150182446593764,1.1194493756706028e-05 +1.5,0.0025804731708883883,2.3126550075375276e-05,1.9978349656399208,0.0002974216378151039,0.0011567521938394495,0.0006903219158655309,0.9999990484597392,0.003468099049434656,-0.00017721066242340516,0.00198815504816445,3.890838443533018e-05,0.00016580286494318787,0.0014724950005191343,0.0,0.0,18.51836419726961,0.44936437173582827,1.5059908207012014,0.18729616038545208 +1.52,0.002632454171935659,2.6333855787167334e-05,1.9978915257913066,0.006554129811626445,0.01970297929125427,0.0020671325510581004,0.9997822577703992,0.002540103204491672,0.00017277702006931588,0.0028806506197515407,0.6268379216853844,1.8543740638136705,0.13787688761162972,0.0,0.0,18.35563541311824,0.16091224654634234,0.8159975151196931,0.19808217197896544 +1.54,0.0028187739886145263,-1.5946538692406762e-05,1.9978524915164102,0.015149946190354174,0.04822693980054579,0.004944754335420059,0.9987092624044628,0.009411636232906376,-0.0022150896124153202,-0.0012683832223961329,0.8604528666549536,2.8544249649275857,0.2862054540021749,0.0,0.0,19.191367665457445,0.011210885121456404,0.32355979405103674,0.19482003920099808 +1.56,0.003380144406601722,-0.0001739690393038598,1.9978011617876794,0.02385733888117927,0.08074607573453739,0.009358437501834608,0.9964051978399677,0.02803450838551585,-0.008328033539651414,0.0013774651963374455,0.8680676475032548,3.2618387537912286,0.43474862077143833,0.0,0.0,20.274927578463746,-0.056706446510644604,-0.0052345729602146784,0.18679455893791014 +1.58,0.004616516196606919,-0.0005209055197172559,1.9978823393594416,0.031686343137567685,0.11311639950896364,0.015266723153315278,0.992959003677824,0.0593507681870581,-0.018265806430643988,0.016863159924160882,0.7710427819009685,3.260619615407251,0.5780302745717278,0.0,0.0,21.172913353059755,-0.08731583468965642,-0.19474447464449696,0.1791026230297918 +1.6,0.006840618088389445,-0.0011196806970899937,1.9981998146618434,0.03823152504933126,0.1428922284969528,0.022615347300577706,0.9887409709292172,0.10209851266974644,-0.03185210612482418,0.04561499316006298,0.6257124951315041,3.018127029367769,0.7159353064566114,0.0,0.0,21.83048250617594,-0.09642290465736414,-0.2919351071593176,0.17189076231246936 +1.62,0.010344357631584901,-0.002015588069609184,1.9988042245350242,0.04341722116894763,0.16880831704644922,0.03134820317332547,0.984193063966474,0.15425741472530255,-0.048997107848545815,0.0861581129691169,0.46400429358676815,2.649426590407973,0.8488694439158168,0.0,0.0,22.264530642361564,-0.09361185737058841,-0.33098390944337797,0.16498223091164343 +1.6400000000000001,0.015379749754567806,-0.003234121940764242,1.9997033349026891,0.04735209666365171,0.19035513069646604,0.041406862494033705,0.9796980018850134,0.2138275717194289,-0.0698129168436788,0.136156944405886,0.30456139558072237,2.2280628475955715,0.9770290681756331,0.0,0.0,22.510336759248982,-0.0852153306272262,-0.3344644062662644,0.1583077372849761 +1.6600000000000001,0.02215086714597756,-0.004782226984238618,2.0008757465191245,0.05023132364867353,0.2074853708442581,0.052730895821322926,0.9755234941486564,0.27912048138907036,-0.09460074387211115,0.19289565634355121,0.1572283512779582,1.7995439841171805,1.1004729523187637,0.0,0.0,22.60776267159686,-0.07513947028812651,-0.31718305561528937,0.15184414195950355 +1.68,0.030813202311342075,-0.00665133462147483,2.0022831622378066,0.05227389839270899,0.2204172081690369,0.06525923619117661,0.9718152735893486,0.3487830643354794,-0.12378620006100555,0.25362506955209546,0.026132339808656035,1.3907113955104233,1.2192144431004788,0.0,0.0,22.59421564446368,-0.06558044031957777,-0.28872050899653334,0.14557902716658427 +1.7,0.041477270300358554,-0.008820986272270027,2.0038797612190904,0.053686035037046616,0.22950664565101714,0.07893166681800802,0.968614629879887,0.421704232029634,-0.15784738983088725,0.31579050839185946,-0.08808645271562086,1.0161764668940894,1.3332693538492686,0.0,0.0,22.50143261200271,-0.057609109701168504,-0.25514507636413675,0.13950027551501365 +1.72,0.05421413854325978,-0.011262323893322747,2.0056186097318136,0.054642557636573,0.23516787308251325,0.0936898557260294,0.9658842960203963,0.4968965242501052,-0.19725601912162521,0.37715547192904525,-0.18672500937415926,0.6827280032146,1.4426727819948337,0.0,0.0,22.354641785310605,-0.0516020065519216,-0.22019154799743967,0.1335952058409851 +1.74,0.06906143490373187,-0.01394110448977473,2.0074555973494674,0.055279793129061755,0.23782567799453694,0.10947777479603517,0.9635339683600965,0.5733983712278709,-0.24243584538276577,0.4358499445520357,-0.27199972083153745,0.39230148312430146,1.5474809403585401,0.0,0.0,22.173016797065053,-0.04753488570386177,-0.18606468174316937,0.1278515537707896 +1.76,0.08602905511215939,-0.016820133267995675,2.009351534434174,0.05569528686516358,0.23788853331796986,0.12624157498872307,0.9614417015509567,0.6502116549023496,-0.29373538132388777,0.49037204185373157,-0.34641433636558183,0.14394386016183874,1.647767237666237,0.0,0.0,21.970670563979134,-0.04517207115899535,-0.15398009114971511,0.1222580623907702 +1.78,0.10510422870308238,-0.019861135438766796,2.0112729972974708,0.0559512282215487,0.23573463385805732,0.143929076882095,0.9594701992705015,0.7262730871635286,-0.351409947618638,0.5395642637748408,-0.412391039894985,-0.06490856978177012,1.7436169153486096,0.0,0.0,21.757747769971385,-0.0441837958143243,-0.12452520027081537,0.1168045475571143 +1.8,0.12625585150714486,-0.023026139373029483,2.0131923857178897,0.0560796261071442,0.23170582466534592,0.16248904437919826,0.9574783531801488,0.8004516416975213,-0.41560899419098046,0.5825777161064625,-0.4720736686196614,-0.23761468890620352,1.8351220357208426,0.0,0.0,21.541406171091396,-0.04421621961088518,-0.09789847179767674,0.11148170898713657 +1.82,0.14943811523968503,-0.026278457077641496,2.0150875286514895,0.05608805286230672,0.22610619836526186,0.18187038413495193,0.9553288861756086,0.8715633186744672,-0.4863661823163585,0.6188319012382629,-0.5272399198473546,-0.377820710817416,1.9223774397586602,0.0,0.0,21.326615438864245,-0.04493119313749805,-0.07406676187701333,0.10628088844635278 +1.84,0.17459351356559655,-0.02958334325070113,2.016941065040282,0.055965276591769975,0.2192033464077939,0.2020213771131345,0.9528930390809579,0.9383958501659577,-0.5635911384285928,0.6479741832567238,-0.5792802343569964,-0.48915491662940286,2.0054777594772166,0.0,0.0,21.11677154402731,-0.046027046199828114,-0.052867778703824975,0.10119386576526901 +1.86,0.2016553149519231,-0.03290840320669837,2.018739744178724,0.055686415852145565,0.21123102275862907,0.22288901280807388,0.9500531385576955,0.9997379089495261,-0.6470627607514706,0.6698411235773369,-0.6292155880594934,-0.5750543259776292,2.084515349868018,0.0,0.0,20.914152784235938,-0.047247706124085465,-0.03407582990411251,0.09621271703146293 +1.8800000000000001,0.2305495867872036,-0.03622380819404319,2.0204737318059,0.055217443248825424,0.20239246468422575,0.24441846593243266,0.946703722244369,1.0544092058690413,-0.7364244801457386,0.6844228892856822,-0.6777362535001098,-0.6386761023274937,2.1595789387727082,0.0,0.0,20.72025060188015,-0.048384828468953645,-0.01744309037004199,0.09132972979037955 +1.9000000000000001,0.26119684382776925,-0.03950236611031335,2.022135968870354,0.05451898062440699,0.19286391720816526,0.2665527314627648,0.9427517337775289,1.1012892867470387,-0.8311820528175327,0.691831402400733,-0.7252501405519753,-0.6828623225360153,2.23075279751247,0.0,0.0,20.53600609063089,-0.049275919248627176,-0.002724605915965239,0.08653736045869602 +1.92,0.2935133822349063,-0.042719486792036354,2.023721604656059,0.05354939682135803,0.18279809430532315,0.2892324168558906,0.9381161590432772,1.1393438396153468,-0.9307044175740529,0.6922725716847845,-0.7719336548893102,-0.7101373620133702,2.2981162672627446,0.0,0.0,20.361977639089986,-0.04980032575789306,0.010307451081012498,0.08182821839709863 +1.94,0.32741235026845816,-0.04585307373363848,2.025227510452367,0.05226725742014184,0.17232742510398372,0.3123956791722255,0.9327273620910514,1.1676479614529864,-1.034227987269286,0.6860227111943278,-0.8177808584673237,-0.7227242474267358,2.361743514433154,0.0,0.0,20.19845932901024,-0.049874260301392526,0.0218565647680109,0.07719506363583456 +1.96,0.3628045985035336,-0.04888336794716167,2.0266518710299803,0.05063319554674714,0.1615670048510245,0.3359782885898483,0.9265262932310289,1.1854062007857662,-1.1408645366560632,0.6734090574677675,-0.862648571917707,-0.7225710717518894,2.42170342308511,0.0,0.0,20.045564613539,-0.049445560327536886,0.03209919490294566,0.07263080860821092 +1.98,0.3995993459862607,-0.05179276442829603,2.0279938465782577,0.04861128080164744,0.15061721501848466,0.359913797133755,0.9194636787560785,1.1919693739475787,-1.2496126432460275,0.6547941401999132,-0.9062962534856027,-0.7113817403412606,2.478059560460135,0.0,0.0,19.90328575066265,-0.04848859216517876,0.041184935770003483,0.06812851743991019 +2.0,0.43770469360589875,-0.05456561715666478,2.029253295885615,0.04616996404409675,0.13956600485802323,0.38413379126076386,0.9114992567850886,1.186848218632537,-1.3593724587500886,0.6305636412280741,-0.9484202430374584,-0.6906474484319238,2.5308701728316096,0.0,0.0,19.771536409813095,-0.0469995178700382,0.049238361482462634,0.06368139905748146 +2.02,0.47702801172354936,-0.0571880446231592,2.0304305513247103,0.04328267206982489,0.1284908440492464,0.4085682083109475,0.9026010926492336,1.1697239510971427,-1.468963446272083,0.6011172963384981,-0.9886824215278178,-0.6616767272587817,2.5801881842112473,0.0,0.0,19.65018263442922,-0.04499202926681056,0.05636177435937184,0.05928279258355197 +2.04,0.5174762254513904,-0.05964774449233677,2.0315262369120015,0.0399281200514965,0.11746036654034765,0.4331456990613365,0.8927449864916781,1.1404557694689839,-1.5771446137379193,0.5668623535009829,-1.0267336075466356,-0.6256228637926649,2.6260611813371195,0.0,0.0,19.539065741147155,-0.042493585683585966,0.06263833053034984,0.05492614522889809 +2.06,0.5589560177182539,-0.061933823147729244,2.032541121868371,0.0360904022135889,0.10653573154864444,0.45779402120456686,0.8819139725170528,1.0990853183604177,-1.6826367035012657,0.528209095913379,-1.0622321622700728,-0.5835081446806505,2.6685313761290033,0.0,0.0,19.43801960159797,-0.03954215497146307,0.06813520015987296,0.05060498422117005 +2.08,0.6013739672746913,-0.06403664351347196,2.033476003406525,0.031758913376224475,0.09577173046988482,0.48244045120435347,0.8700979014488571,1.0458381125687886,-1.784145756345888,0.4855679624295228,-1.0948583484458312,-0.5362447939129511,2.7076355424300376,0.0,0.0,19.34688395463611,-0.03618343870103202,0.07290655257904932,0.04631288524536536 +2.1,0.6446366360345192,-0.06594769267382578,2.0343316137234146,0.026928146204371915,0.08521766915890622,0.5070122044606657,0.8572930935992983,0.9811219160541154,-1.8803874497676722,0.4393478440172762,-1.1243250171935408,-0.48465273021768923,2.7434049280636166,0.0,0.0,19.26551483749173,-0.032468552763394776,0.07699625346475547,0.04204344040324388 +2.12,0.6886506176110424,-0.06765946939103781,2.0351085472759376,0.02159740151741513,0.07491805441742172,0.531436855946959,0.8435020483147714,0.9055220878212734,-1.9701116107071566,0.38995519509834553,-1.1503851950258135,-0.42947440951385785,2.775865146444159,0.0,0.0,19.193792833379874,-0.028452127547503148,0.08044023019532154,0.037790228810578294 +2.14,0.7333225565961099,-0.06916539061060538,2.035807205315655,0.015770442016323546,0.06491311208907383,0.5556427554094134,0.8287331955451983,0.8197939371300909,-2.0521263179729208,0.33779366576423386,-1.172837120160253,-0.37138707449245756,2.8050360543894803,0.0,0.0,19.13162956106831,-0.024190783542597405,0.08326850714767675,0.03354679264083402 +2.16,0.7785591460672869,-0.07045971539307273,2.0364277553398624,0.009455113398653969,0.05523916230712617,0.5795594328235483,0.8130006762626171,0.7248521734931217,-2.1253210364859165,0.28326402880596124,-1.1915272336754368,-0.31101273614087294,2.830931624432916,0.0,0.0,19.078972645305395,-0.019741927269432247,0.08550693754118543,0.029306620725614692 +2.18,0.8242671090095999,-0.0715374843699232,2.036970103591316,0.002662951191561074,0.045928875443886026,0.6031179910854884,0.796324139983472,0.62175758857156,-2.188688261985791,0.22676423946696309,-1.2063515666141755,-0.24892618594647772,2.853559820776741,0.0,0.0,19.03580928443065,-0.015162799938915894,0.08717866433529964,0.025063140815918525 +2.2,0.8703531678142447,-0.07239447273265345,2.037433879028826,-0.004591213090961171,0.03701143035491061,0.6262514838903065,0.778728549438523,0.5117011637586697,-2.2413431994313067,0.1686895225808091,-1.2172558786301548,-0.18566130530854552,2.872922487988015,0.0,0.0,19.002168460443926,-0.010509700356831742,0.08830533178744482,0.02080972041734446 +2.22,0.9167240047703781,-0.07302715487359983,2.0378184273237943,-0.012289636468446377,0.028512594687542036,0.6488952774583459,0.760243984304982,0.3959858556142712,-2.282541050043142,0.10943242957742177,-1.2242348005309496,-0.12171592032350374,2.8890152605607065,0.0,0.0,18.978121810586327,-0.005837299033936927,0.08890804481387116,0.016539674887812335 +2.24,0.9632862154933962,-0.07343267905163599,2.038122813445268,-0.020412031982020445,0.020454745334950925,0.6709873952827357,0.7409054377687729,0.2760063682721834,-2.3116915403325584,0.04938284640364831,-1.2273301209377694,-0.05755546159854681,2.9018274995611977,0.0,0.0,18.963783193744366,-0.0011979674174460442,0.08900804030904019,0.01224628037898412 +2.2600000000000002,1.0099462565241257,-0.07360885080669652,2.0383458313138796,-0.028935912246382374,0.012856845475002146,0.6924688454920477,0.7207526014728023,0.15322727442134051,-2.3283703914470886,-0.011072036585020727,-1.2266282514510687,0.006384259917621288,2.911342259822887,0.0,0.0,18.959307034194502,0.0033589311097426133,0.08862700037193222,0.007922788329691233 +2.2800000000000002,1.0566103878749202,-0.07355412425127836,2.0384860188746434,-0.03783690142225711,0.005734392864123649,0.7132839308861469,0.6998296361035653,0.02915989124575047,-2.33232749873689,-0.0715477590049301,-1.222256825711033,0.06969475758739598,2.9175362877168793,0.0,0.0,18.964885599423166,0.0077878017537799105,0.08778691151007711,0.0035624376374518564 +2.3000000000000003,1.1031846110763892,-0.07326760078932705,2.0385416768089293,-0.04708900577347477,-0.0009006480734884934,0.7333805423619453,0.6781849264420526,-0.09466164865857256,-2.3234916692477423,-0.13166257877476653,-1.2143803581755919,0.1319925270224591,2.920380045606629,0.0,0.0,18.980745451754768,0.012047589169754347,0.0865093672915111,-0.0008415396677877284 +2.32,1.1495746032788137,-0.07274903522736513,2.0385108890338275,-0.056664829624198144,-0.007039920549868956,0.7527104374413166,0.6558708210991048,-0.2167046724481222,-2.301971847659963,-0.19103735308948772,-1.2031949274164044,0.19291599826646239,2.9198377549251133,0.0,0.0,19.007143383115928,0.016102457156686234,0.08481423492429863,-0.005295923491113993 +2.34,1.1956856481739717,-0.0719988496349004,2.038391543184878,-0.06653571954827828,-0.012678749783763966,0.7712295070455307,0.6329433582797288,-0.33546212832164457,-2.2680548464672348,-0.24929524578309886,-1.1889219647179292,0.2521210186899172,2.9158674456387756,0.0,0.0,19.04436217394774,0.019922223729990973,0.08271766530212832,-0.009807525571318108 +2.36,1.2414225649184374,-0.07101815567967254,2.0381813495088594,-0.07667181573065089,-0.017816215321456202,0.7888980355490557,0.6094619796599502,-0.4494761919330351,-2.222199679828662,-0.3060611974706926,-1.1718014124454625,0.3092740693869881,2.9084209959747374,0.0,0.0,19.092706490814983,0.02348237232515471,0.08022951959122576,-0.014383227828291963 +2.38,1.286689636855042,-0.06980878652051192,2.0378778570650717,-0.08704198649419173,-0.022455223816127173,0.8056809613989586,0.5854892347001456,-0.5573609157917748,-2.1650286771293366,-0.3609611074965148,-1.1520847362367506,0.3640424969477742,2.897444143097268,0.0,0.0,19.15249914409517,0.026763477790921142,0.07735040010858701,-0.019030041739526743 +2.4,1.331390542646094,-0.0683733397108963,2.0374784668927557,-0.09761362081723256,-0.026602589171738817,0.8215481479872931,0.5610904773910871,-0.6578235752185962,-2.0973156159729425,-0.41362070198808965,-1.1300284764737687,0.416081260623968,2.882876443533087,0.0,0.0,19.224077773921024,0.029749959526980008,0.07406858252655485,-0.023755189400776595 +2.42,1.3754282934547124,-0.06671523298087743,2.0369804428778946,-0.1083522548647662,-0.03026913019405473,0.8364746766856249,0.5363335565547763,-0.7496844304286061,-2.019971160136488,-0.4636641022735429,-1.105889137834062,0.4650160467098874,2.8646511623688315,0.0,0.0,19.30779183631702,0.03242820537587976,0.07035721371051867,-0.028566203908037213 +2.44,1.418705181046171,-0.06483877525266517,2.0363809224488802,-0.11922101218968466,-0.03346979685377036,0.8504411756105975,0.5112884995548983,-0.8318946910458271,-1.9340259135651021,-0.5107121630450934,-1.0799201679170152,0.5104230378486737,2.8426950734247685,0.0,0.0,19.403999537077862,0.034784286969907985,0.06617212673268302,-0.033471042753333055 +2.46,1.4611227431386187,-0.06274925582018379,2.0356769309193212,-0.13017984262467613,-0.03622383382627942,0.8634341984422005,0.48602718792019795,-0.9035525257305348,-1.8406114085232688,-0.5543807179651554,-1.0523715071508861,0.5518060492195194,2.8169281592925777,0.0,0.0,19.51306411034494,0.03680168420182195,0.06145050526948581,-0.0384782037189768 +2.48,1.5025817540272544,-0.060453055273018315,2.0348654052118897,-0.14118455037494523,-0.03855498314714198,0.875446667269548,0.4606230224126769,-0.9639169955829312,-1.740939341268325,-0.5942789468687285,-1.0234916734863286,0.5885720280736993,2.7872632101525876,0.0,0.0,19.635348554536463,0.03845961441521828,0.05611040149066865,-0.04359682928055733 +2.5,1.5429822504694695,-0.05795778242017516,2.033943234742308,-0.15218560505258524,-0.04049171591712979,0.8864783920519741,0.43515057506777044,-1.0124197996011826,-1.6362793634150659,-0.6300081552889407,-0.9935315956372213,0.6200059269582894,2.753605332575546,0.0,0.0,19.771207618671774,0.0397326718891049,0.050050804968409746,-0.048836783732026406 +2.52,1.5822236051149081,-0.055272442070195014,2.032907329315154,-0.1631267276519154,-0.04206746575625372,0.896536677200239,0.40968522732114543,-1.0486747044040283,-1.5279357455991855,-0.6611613239549987,-0.9627485089651028,0.6452456099313841,2.7158513922144594,0.0,0.0,19.920975478653006,0.04059247288397316,0.04315164009322303,-0.05420868787212786 +2.54,1.6202046625117603,-0.05240763889095527,2.0317547259198325,-0.1739432344763822,-0.04332081455782869,0.9056370235372369,0.38430279702931486,-1.0724844971116323,-1.417223265458484,-0.6873238345367491,-0.9314073293372066,0.6632567196459073,2.673889425118076,0.0,0.0,20.08494710258195,0.041011830164380036,0.03527283639490586,-0.05972390034652485 +2.56,1.6568239561144287,-0.04937582246761754,2.030482748329158,-0.1845601049734384,-0.04429555532605722,0.9138039319245774,0.35907916317955824,-1.0838452645842729,-1.3054427421878354,-0.7080758051444799,-0.8997762315113679,0.6728064279987355,2.6275980585043515,0.0,0.0,20.263350585570322,0.04097164747019244,0.02625160811044963,-0.06539444545996057 +2.58,1.6919800290593854,-0.0461915777747044,2.0290892354794248,-0.1948897161561863,-0.045040529597844145,0.9210718129297238,0.3340899050502105,-1.0829477812249588,-1.1938567423918194,-0.722996482128791,-0.8681128983186813,0.6724339905028098,2.5768459787927047,0.0,0.0,20.456306484631877,0.04047030452643963,0.01589745868772119,-0.07123290795233098 +2.6,1.7255718871952228,-0.04287196316195447,2.0275728568444698,-0.20482916250995453,-0.04560911034359113,0.927486003383553,0.3094099816856925,-1.0701757934934335,-1.0836661003320691,-0.7316711525424959,-0.8366382037481279,0.6604155041039053,2.5214914661554895,0.0,0.0,20.66376856918616,0.03953496855582328,0.003985297148613065,-0.07725234259912853 +2.62,1.7574996204723863,-0.039436894114472563,2.0259335354345844,-0.21425706605865022,-0.04605817624255098,0.9331038815396693,0.2851134867044212,-1.0461010211655115,-0.9759880008431984,-0.7337010874471241,-0.805494858450137,0.6347208440108438,2.4613819777484385,0.0,0.0,20.88543910810273,0.038234265994488315,-0.009751726444523878,-0.08346626714880817 +2.64,1.7876652387921972,-0.035909564862266125,2.0241730014648778,-0.22302978879836127,-0.04644639811423322,0.9379960512529079,0.2612735218975027,-1.0114747338749424,-0.8718364303112488,-0.7287171127182996,-0.774689370289588,0.5929628670960474,2.3963537138657522,0.0,0.0,21.12065231025711,0.03669209483956615,-0.025626490723679896,-0.08988878370403264 +2.66,1.8159737809321332,-0.032316890601445866,2.0222955017587974,-0.23097700436317883,-0.046831628816402085,0.9422475227144771,0.2379622405682166,-0.9672157759403388,-0.7721057822703622,-0.7163975078311863,-0.7440161274580801,0.5323430756530615,2.3262310797072683,0.0,0.0,21.368210799553516,0.03510341728199551,-0.04399542696503944,-0.0965349568686509 +2.68,1.8423347706935134,-0.028689939724392596,2.0203086883027144,-0.23789667101284762,-0.04726714713302481,0.9459587442767757,0.2152511204366089,-0.9143948726643335,-0.677558296007204,-0.6964911103788661,-0.7129615782570472,0.44960291129813906,2.2508258809590833,0.0,0.0,21.626154579105105,0.033752630068603295,-0.0652428608445556,-0.10342149596936245 +2.7,1.8666641127580135,-0.025064306657099363,2.018224701092986,-0.2435496074983671,-0.04779645813968536,0.9492462219960872,0.19321153511850062,-0.8542149759367336,-0.5888158268536412,-0.6688466268235318,-0.680586139081188,0.3409982115464734,2.1699360991166223,0.0,0.0,21.891435117424663,0.03303300962143774,-0.0897501044899478,-0.11056761388451725 +2.72,1.8888865401889312,-0.02148034918041821,2.0160614385983253,-0.24765406159793799,-0.048446348605075434,0.9522423077488523,0.17191568984249364,-0.7879873204819149,-0.5063561951738651,-0.6334491878535405,-0.6453842473274283,0.2023171501355999,2.0833442664254,0.0,0.0,22.159438320080397,0.03346353870062169,-0.11784198205936586,-0.11799607249426292 +2.74,1.9089387450909643,-0.017983178942515835,2.0138439637784833,-0.249880945047328,-0.04921795314534058,0.9550935289929737,0.15143796507542254,-0.7171030203708124,-0.4305140082294483,-0.5904651412106647,-0.605128787381582,0.028971510287616242,1.990815572611395,0.0,0.0,22.42332385133251,0.035692228586269525,-0.14969931045884705,-0.12573380367279904 +2.7600000000000002,1.926773334376618,-0.0146222502173627,2.011605920465755,-0.2498508564748846,-0.05007580133625938,0.9579565698397288,0.13185664162338928,-0.6430005362928132,-0.36148541716238375,-0.5402954442289131,-0.5567219368731042,-0.1837940389141244,1.892096491089437,0.0,0.0,22.67303498430932,0.04047669093924555,-0.18521391728429654,-0.13381347354442244 +2.7800000000000002,1.9423637518003736,-0.011450345963304064,2.009390703036828,-0.24713477040817936,-0.050935170817784536,0.96099069552389,0.11325589055572129,-0.5671303717482913,-0.2993357586532729,-0.48363808084334664,-0.4960882356892689,-0.44049992145139694,1.7869138942681622,0.0,0.0,22.894141314822633,0.04860195880597562,-0.22376925947361143,-0.14227268838628163 +2.8000000000000003,1.9557102425260775,-0.008521719051075724,2.0072519666577193,-0.24126133288261112,-0.05164901510231737,0.9643451465475594,0.09572767011074974,-0.4909199360351296,-0.24400882610976565,-0.4215565531383624,-0.41820159090834064,-0.7447409047877825,1.6749774653788683,0.0,0.0,23.06653654415096,0.0607396075915867,-0.26390612487007187,-0.1511509532879647 +2.82,1.966846788832946,-0.005889147534336558,2.005252846830219,-0.23173529247590458,-0.05199670179295619,0.9681399809983968,0.07937300810845699,-0.41574403644056446,-0.1953364711488981,-0.35554886721281403,-0.3173329552131142,-1.0980605235337122,1.555988227566219,0.0,0.0,23.162939769642296,0.07720623459431342,-0.3028608290746234,-0.16048769828038048 +2.84,1.975848676575367,-0.003599732370921817,2.003462966236383,-0.21807352472478242,-0.0516784918296396,0.9724394720046124,0.06430197962515714,-0.3429092497254102,-0.15304889688648352,-0.28760868912152243,-0.1876617684562123,-1.4982313526308793,1.4296532618046354,0.0,0.0,23.148111049324456,0.09752359929592203,-0.3358574514479608,-0.1703123780099518 +2.86,1.9828398434899595,-0.001689466023065938,2.001952157123667,-0.1998686186198475,-0.05032269069991764,0.9772188182596308,0.050631446068277915,-0.27366307148866587,-0.1167888992376677,-0.2202584377409787,-0.024516818246322514,-1.9366462664314896,1.2957122702276525,0.0,0.0,22.979269407220148,0.11974865306009175,-0.35512684180454523,-0.18063380579475993 +2.88,1.9879984512661804,-0.000176997630954233,2.0007798286621004,-0.17689344687262085,-0.047515938234874044,0.9823289921347144,0.038479803517936485,-0.20923716138338783,-0.08613646136639982,-0.15652824525350423,0.17347083228802476,-2.3945358647151282,1.1539723171205765,0.0,0.0,22.608693036182355,0.13974047895327435,-0.3487143078379126,-0.19144140324563055 +2.9,1.9915582813048527,0.0009423598068465961,1.9999793761366165,-0.14926135293089407,-0.04286803878246876,0.9874723586012881,0.027959269850856613,-0.15092503994538836,-0.060652700323925876,-0.0998462012210484,0.40130392211979793,-2.8379039915789552,1.0043302873459765,0.0,0.0,21.988919941787596,0.15057644754080624,-0.2991296490763341,-0.20275104135140887 +2.92,1.9938029324094697,0.0017006506415428784,1.9995383193344471,-0.11765414830264281,-0.03611955448603639,0.9922124046840809,0.019168285315690866,-0.10017206909381265,-0.03995089743831416,-0.05380518761364229,0.6439926558164626,-3.2113040929325414,0.8467346418796958,0.0,0.0,21.08165906339926,0.14217669649136666,-0.18167397130453686,-0.2147073846613809 +2.94,1.9950499032144968,0.0021571817595063816,1.9993779328305024,-0.08362474657997361,-0.027295975146196978,0.9960488453752359,0.012187252640068142,-0.05861273093353446,-0.023788716168320602,-0.021754910915748445,0.8726262997845966,-3.4306215962395155,0.6810684018114969,0.0,0.0,19.896291716409852,0.09893373389159876,0.03625064896362434,-0.22731449766016648 +2.96,1.9956227048422253,0.0023923716568491006,1.9993476381324697,-0.049945273015515115,-0.01694575966044795,0.998583155409602,0.0070705491398316235,-0.027923409720602862,-0.012144652355120675,-0.0058335605874832545,1.036327449373649,-3.377902944013586,0.5074670562903593,0.0,0.0,18.697940813302925,-0.013445577183156976,0.3767669620700846,-0.23742969144444365 +2.98,1.995813809595637,0.0024954545923018562,1.9992888173063397,-0.020731110965176533,-0.006639047375934434,0.9997558249366919,0.003799288518152399,-0.009264725753505725,-0.005178421440607834,-0.003729169208359941,1.035683468223422,-2.9218631763938996,0.32951662703800544,0.0,0.0,18.174161213549525,-0.23421966791604976,0.8466715146216237,-0.23639221462242882 +3.0,1.9958599205642589,0.0025512469418962733,1.999172728215246,-0.0016837428434832613,0.0006766879495327952,0.9999958101658875,0.002255383365704436,-0.0021580363748036476,-0.0027684259490047174,-0.005871472287899877,0.7356415296752087,-1.9031476159670109,0.15697564448800388,0.0,0.0,18.74430531537498,-0.560702769518466,1.5532105764106605,-0.22064962988222186 +3.02,1.9959231417371093,0.00262408184291302,1.9991808698905666,-0.0016439659322199573,0.0006824797624709441,0.999995879951269,0.0022520386042146467,-0.0031459583496519818,-0.003655409767583661,0.0004015488137856958,0.0005875821915860557,-0.003976153599771104,0.00033815733496064815,0.0,0.0,19.327437243861905,1.440540937842178,0.5846662049315385,-0.22158869647802074 +3.04,1.9959815456998506,0.002678154449175779,1.9993840091159523,-0.0083428248483166,0.019561133810259392,0.9997661909612813,0.003914425676667652,-0.0030055847756676407,-0.0025160979050515434,0.010180867285972468,1.886765058006247,0.6741043838848578,-0.16365436845735964,0.0,0.0,18.807736917421305,0.7551966654377452,0.37116774289734855,-0.23387940372630878 +3.06,1.9959798551528702,0.0028758184391177903,1.9996167047115874,-0.019197562915907913,0.04818010839963193,0.998627177680022,0.007341030685345519,-0.0001375673719190727,-0.009074130705905039,0.01227847445535776,2.8625193142116574,1.0908428348824561,-0.3404369302919102,0.0,0.0,19.213212686827635,0.3090457190372755,0.17663951348177087,-0.23390793239783206 +3.08,1.9958319157881081,0.0034550769686195037,1.999875910608885,-0.03201225817284407,0.08076446312825356,0.9961399162833942,0.012553246958157944,0.0071888130936963186,-0.026889601028777296,0.01694810945558025,3.2672305217668467,1.283562403591262,-0.5216287961607138,0.0,0.0,20.234383013059677,0.005848033573991884,-0.006467591766404435,-0.22299683827954345 +3.1,1.995429900734949,0.004713477805495539,2.0002748133955164,-0.04460094766435352,0.11335772320820921,0.9923605410450252,0.019527893724548773,0.019994917670618256,-0.05709624223956828,0.033211639016691163,3.2844959461542578,1.2509606670595705,-0.696999369169689,0.0,0.0,21.161813433225582,-0.17291482960984483,-0.1180138973765911,-0.2108914446031266 +3.12,1.9946687470207853,0.006967820391286942,2.0009105061173553,-0.05554500055626823,0.14353152182704015,0.9876820791475642,0.028241205462277263,0.038367269803040754,-0.09858609237738236,0.06307282603830486,3.065429800085171,1.0672626236526237,-0.8634543575665186,0.0,0.0,21.836833973269844,-0.26561732826590556,-0.16991989973350818,-0.19951289537760528 +3.14,1.9934658906829748,0.010517110775098894,2.001826051453918,-0.06421282751295937,0.16998758584196963,0.9825917477905123,0.03865734880195791,0.062072813429632806,-0.14931286555767745,0.10521487735905374,2.7198700946981997,0.809713341927912,-1.0207746814418273,0.0,0.0,22.27637964534187,-0.30412730270577804,-0.1847957899986799,-0.18872558509754225 +3.16,1.991769581882639,0.015622225856459011,2.0030205310069906,-0.07048585805944847,0.19215942156914279,0.9775142411954016,0.050718919310390305,0.09100430809286907,-0.20717698374257007,0.15725638659398591,2.3183698067337195,0.5297276214631967,-1.1691872023916328,0.0,0.0,22.520120057857582,-0.3093468615953483,-0.17841211228669807,-0.1784266675752959 +3.18,1.9895596656957741,0.022496855174831503,2.004464260620135,-0.07453990642392112,0.2099347055990227,0.9727438994725762,0.06434693290477182,0.1252866619325582,-0.27036133504488075,0.21634126639746457,1.9048941880140982,0.25821564021490745,-1.3090821490439264,0.0,0.0,22.61087122580693,-0.2948860766823249,-0.1611748327007823,-0.16857054259876475 +3.2,1.986843991509064,0.03130588538293424,2.006112025785828,-0.07669493917845387,0.2234693712349993,0.9684356905343707,0.07944582886132974,0.16522170211104317,-0.3373823039175164,0.2795146815529396,1.5058096690881657,0.011856898419976392,-1.4409165582330585,0.0,0.0,22.58789436630217,-0.269522084605756,-0.13971169838784891,-0.1591269441491662 +3.22,1.9836528687930863,0.04216814067947756,2.0079128663335757,-0.07731872275262583,0.23306705230923494,0.9646257471330021,0.09591002141530491,0.2111816872638898,-0.4070089059922761,0.34398124816687803,1.1360021659054926,-0.20205767042107056,-1.5651485043882387,0.0,0.0,22.484112952216794,-0.23882394263148954,-0.11801595429754003,-0.15006751529785506 +3.24,1.9800331441885293,0.05516123640931081,2.0098165153709178,-0.07676937374820711,0.2391034883516549,0.9612617845143547,0.11362995531397627,0.26351114827313743,-0.4781463298821981,0.4072569050902038,0.8030185774314127,-0.38210275231771007,-1.682199261176238,0.0,0.0,22.325581714425343,-0.20625787269320783,-0.09828927445873631,-0.14136471381211008 +3.2600000000000002,1.9760427537877436,0.07032712948779449,2.0117771485646156,-0.07536439533121587,0.241980470743774,0.9582328947345737,0.13249671370860872,0.32245929881394764,-0.5497347585113429,0.46724084764627105,0.5098107160143414,-0.530255420354942,-1.7924388498405148,0.0,0.0,22.132133090729177,-0.1739268072942699,-0.08155641133243119,-0.13299314662754472 +3.2800000000000002,1.9717461114478252,0.08767758748647567,2.0137552035818747,-0.07336686743416897,0.24209850941790098,0.9553943397199797,0.15240495441930887,0.38814358111494857,-0.6206827207704314,0.5222358338846421,0.25651964307640884,-0.650237928001396,-1.8961862368985232,0.0,0.0,21.918471930351807,-0.14305642470994662,-0.06810170691327727,-0.12493030099847996 +3.3000000000000003,1.9672104105563137,0.10719921422729232,2.0157179347504806,-0.07098222068217513,0.2398411036331386,0.9525863199103063,0.17325435773683864,0.46053606776658956,-0.6898364516801833,0.5709380651573437,0.04162235406210982,-0.7464475713664905,-1.9937163905554485,0.0,0.0,21.695301414424353,-0.11431181324191786,-0.05777049741637169,-0.11715649217608667 +3.3200000000000003,1.9625027792617633,0.1288579055088448,2.0176392095847273,-0.06836117288608486,0.2355661363151283,0.9496472320609552,0.19494994255492187,0.5394632382374756,-0.7559788043947607,0.6124098794027458,-0.13733680449982835,-0.8232993260110596,-2.0852692085670657,0.0,0.0,21.470296140669962,-0.08800336396472952,-0.05017165665169704,-0.10965443183528736 +3.34,1.9576881767385708,0.15260272526800375,2.01949889957862,-0.06560594681153521,0.22960164227738794,0.9464224606297871,0.21740163665885187,0.6246117962698875,-0.8178489809899752,0.6460429034148,-0.28328262896035955,-0.8848502663180438,-2.1710577846906243,0.0,0.0,21.248874301159198,-0.06422077665942072,-0.044809894584818655,-0.10240872446097843 +3.36,1.9528279081560145,0.1783692411708011,2.0212820935410747,-0.06277793825957134,0.2222442795822247,0.9427697944228413,0.24052344040606294,0.715536120289887,-0.8741748161718257,0.6715161656436542,-0.3993179671610074,-0.9346111336431692,-2.2512750519724984,0.0,0.0,21.03478378207714,-0.04292042834901744,-0.04116923666054462,-0.09540543237909892 +3.38,1.9479786441004445,0.20608237323696615,2.022978271726067,-0.05990570020212639,0.21375948306963605,0.9385624139176588,0.2642324462706123,0.8116654421264665,-0.9237108838151624,0.6887521159959775,-0.4885437988280906,-0.9754785134084792,-2.3260986772369825,0.0,0.0,20.830537809672165,-0.023982347034732676,-0.03876315554933368,-0.08863174544137316 +3.4,1.9431918439277052,0.23565880907100462,2.02458051807948,-0.05699256478076409,0.2043826624284112,0.933690198100309,0.2884478962131464,0.9123105450293898,-0.965277453234474,0.6978727412679588,-0.5539513025920698,-1.009741729356815,-2.395694448763764,0.0,0.0,20.637736769872152,-0.007247196859576421,-0.037162272933441765,-0.08207574263215871 +3.42,1.9385134966081814,0.2670090344517553,2.0260848088390215,-0.054023522302810816,0.19432103239813558,0.9280599142232138,0.3130903879356908,1.016670678845864,-0.9977969192515189,0.6991575135287423,-0.5983607041120749,-1.0391332846860113,-2.4602185078627197,0.0,0.0,20.457306607747046,0.00746004994218552,-0.03600722312544152,-0.075726216565426 +3.44,1.9339841053176092,0.30003902274156274,2.0274893918634724,-0.05097117011250198,0.18375579747588708,0.9215947002009933,0.33808128500821255,1.1238416863448335,-1.0203256266781167,0.6930045300182734,-0.6243904845716595,-1.0649019837007743,-2.5198187581225486,0.0,0.0,20.289676730876607,0.02031542936070463,-0.035011903473863515,-0.0695725318824224 +3.46,1.9296388531021778,0.33465162227201534,2.0287942572925717,-0.04780067009874683,0.1728444980271653,0.9142331297952239,0.3633423452127172,1.2328262312649523,-1.0320799830557281,0.6798958206074381,-0.6344460592119701,-1.0878949963577054,-2.5746357199287955,0.0,0.0,20.134913914879405,0.03148742547092098,-0.03396067804289715,-0.06360449617487732 +3.48,1.9255078966905341,0.3707476776921883,2.03000069278996,-0.04447373441569424,0.16172338159481459,0.9059280609250921,0.38879555452855,1.3425467021460367,-1.0324564361059325,0.6603674135739402,-0.6307205763203861,-1.1086400230028637,-2.624803022884201,0.0,0.0,19.992823909901325,0.0411325695399285,-0.03270195467918324,-0.05781223245798471 +3.5,1.921616744269279,0.4082269188909698,2.031110913236396,-0.04095170931404832,0.15050970448007164,0.8966454014392207,0.4143631383695682,1.4518609772094013,-1.0210453397802322,0.6349843905643365,-0.6152028340302363,-1.127422064909922,-2.6704476642198296,0.0,0.0,19.863029131063442,0.04939379380854092,-0.031139776742982572,-0.052186052435846286 +3.52,1.9179866810018977,0.4469886491091476,2.0321277537309963,-0.037197856275395715,0.13930390026133657,0.8863628789974183,0.4399677142653794,1.5595808678214298,-0.9976390050887078,0.6043208561432832,-0.589688946196925,-1.1443516009815113,-2.7116901141456657,0.0,0.0,19.745028624909416,0.05640049640644207,-0.029224552997017872,-0.04671633781192147 +3.54,1.9146352133437947,0.4869322619075963,2.0330544151375483,-0.033178944207022214,0.1281915764653655,0.8750688678425546,0.46553254871961586,1.664492764117775,-0.9622343834606044,0.5689445147680575,-0.5557955855995609,-1.1594225604130486,-2.7486443195255426,0.0,0.0,19.63824506668786,0.06226952658672236,-0.02694371150041608,-0.041393441433727814 +3.56,1.9115765097858126,0.5279576145554308,2.033894252585797,-0.028866270618187237,0.11724531918946934,0.862761301672353,0.4909818834594452,1.7653798080361782,-0.9150309049010494,0.529405393601132,-0.5149735393365373,-1.1725596040102584,-2.781417641920885,0.0,0.0,19.542062544456734,0.0671065006969418,-0.024312842920674926,-0.0362076206848458 +3.58,1.908821821498421,0.5699652830664655,2.0346505989464783,-0.02423622653728094,0.10652629862745051,0.84944668581596,0.5162413011182373,1.861044816909337,-0.8564240219547795,0.4862281670357889,-0.4685209936019384,-1.1836550382690405,-2.8101107601750286,0.0,0.0,19.455858085399242,0.07100704341353303,-0.02136774647025902,-0.031149012635896513 +3.6,1.9063798713963636,0.6128567215494618,2.0353266171135402,-0.01927051168164826,0.09608567677479714,0.8351392113479015,0.541238106351966,1.9503331623562152,-0.7869950133270806,0.4399075143425701,-0.4175964409679598,-1.192596277713374,-2.8348175665501802,0.0,0.0,19.379029121975854,0.07405773236495372,-0.01815767012865782,-0.026207655090571873 +3.62,1.904257204364968,0.6565343458039649,2.0359251767718374,-0.013956094689883868,0.08596582304772328,0.819859966971954,0.565901704579056,2.0321548550342037,-0.7074975888501651,0.390905958566629,-0.3632313675160737,-1.1992851863431295,-2.8556250851044553,0.0,0.0,19.311018314299915,0.07633670757307942,-0.014739918433709087,-0.021373551233223848 +3.64,1.902458494778999,0.7009015582705953,2.0364487530445445,-0.008284999245984564,0.07620134554704516,0.8036362402236945,0.5901639663038046,2.1055051741484787,-0.618841815516928,0.33965367671191343,-0.3063429234139347,-1.2036508786162818,-2.872613438944684,0.0,0.0,19.251336355683577,0.07791407491248525,-0.011175868571130065,-0.016636769558342167 +3.66,1.9009868100267426,0.7458627286244086,2.0368993458512588,-0.002253981537880646,0.06681994692355481,0.7865008968919915,0.6139595698809924,2.1694832908012907,-0.5220758552753012,0.28654982579758903,-0.24774662250234766,-1.2056576335617115,-2.885855888052627,0.0,0.0,19.199582664455008,0.07885235801218116,-0.007528283468320129,-0.011987566359682117 +3.68,1.8998438305770955,0.7913231415776745,2.037278419837563,0.004135851547693326,0.05784311594134308,0.7684918260174878,0.637226319304593,2.2233084491450574,-0.4183659720801786,0.2319649884344935,-0.18816880358012095,-1.2053094553305599,-2.895418951646008,0.0,0.0,19.155463305190562,0.07920730426319311,-0.0038596484487630756,-0.007416516100638813 +3.7,1.8990300282730441,0.8371889209285336,2.03758686527795,0.010879449955633735,0.04928667018631298,0.7496514371721963,0.6599054359953925,2.266333376934042,-0.3089752328162702,0.1762444009416986,-0.12825820973015387,-1.202652493271163,-2.9013626195556754,0.0,0.0,19.118805173494103,0.07902930213767034,-0.00023111066746855924,-0.0029146358362950424 +3.72,1.8985448051057243,0.8833669366423325,2.0378249803914854,0.0179682348049673,0.04116117246089278,0.7300261969760681,0.68194182460698,2.298054691715828,-0.19524130015836968,0.11971168509854987,-0.06859572839324933,-1.197776036589294,-2.9037406475644296,0.0,0.0,19.08956546678048,0.07836550982252832,0.0032984944674357974,0.0015265066546902377 +3.74,1.898386594853773,0.929764699856125,2.037992475103766,0.025390512680099474,0.03347225272887502,0.7096661931041375,0.7032843127272049,2.3181201511473297,-0.0785537013227894,0.06267286086503973,-0.00970122355304883,-1.190812188074706,-2.9026009239877393,0.0,0.0,19.06783573514644,0.0772625460054337,0.006674046698618374,0.005914706051615096 +3.7600000000000002,1.8985529298268984,0.9762902492026247,2.038088495566502,0.03313189098092105,0.026220877372361822,0.6886247165064557,0.723885863383348,2.326332671619155,0.03966904130012888,0.0054204710922715615,0.04796339802453699,-1.1819337113027217,-2.897985890617862,0.0,0.0,19.053840277427597,0.07576930006311355,0.009846365350763886,0.010257053417558285 +3.7800000000000002,1.8990404747163836,1.0228520307746611,2.0381116678741047,0.041175697952036565,0.01940361537456072,0.6669578561707327,0.7437037580251286,2.322651117338548,0.15800201475642076,-0.051762301204859466,0.10399515235074205,-1.1713490862683178,-2.8899330011771336,0.0,0.0,19.047929169199875,0.07393915750404167,0.012775385413633433,0.014559886634473183 +3.8000000000000003,1.8998450294178837,1.0693587733785936,2.0380601585616778,0.049503407213515684,0.013012953748026268,0.644724105205,0.7626997468706914,2.3071879505836606,0.27503582885028655,-0.10859759966156335,0.15805430605423945,-1.1592936581217463,-2.878475203703425,0.0,0.0,19.05056659926992,0.07183080203512737,0.015434048861225236,0.01882875367381997 +3.8200000000000003,1.9009615028160656,1.1157193604138311,2.0379317487569004,0.0580950567222355,0.007037709292112724,0.6219839816922,0.7808401638944278,2.280203936919618,0.38939739595859807,-0.16480898948933556,0.2098702796633018,-1.146016046367364,-2.863641437025039,0.0,0.0,19.062315301721164,0.06950683761703158,0.017812271210164266,0.023068391155451055 +3.84,1.9023838600056004,1.1618426997165798,2.03772391835241,0.06692963784480181,0.0014635690196200797,0.5987996717933837,0.798096005881388,2.2421002129059944,0.49976914607640155,-0.22011770559212618,0.25924631900021206,-1.1317597385850164,-2.8454571315822856,0.0,0.0,19.083817687877776,0.0670298200459789,0.01992000699311225,0.027282731353225752 +3.86,1.9041050462714493,1.2076375929825862,2.0374339362790956,0.07598541506250288,-0.0037262322208520848,0.5752347049597105,0.8144429790360589,2.193408135060934,0.6049063007283525,-0.2742390765187318,0.3060595468523597,-1.1167409429259791,-2.8239446976071476,0.0,0.0,19.115773999450678,0.06445586133338155,0.021788285297301258,0.03147496183148642 +3.88,1.9061168922550897,1.253012606921488,2.0370589529096903,0.0852401254175007,-0.008550118571865127,0.5513536709767297,0.829861522178068,2.1347774164522675,0.7036519372870653,-0.3268791622434826,0.350253164472006,-1.1011251012545136,-2.7991239682448654,0.0,0.0,19.158917767527115,0.0618266265489934,0.02346721487062753,0.035647666405760677 +3.9,1.908410005888542,1.297875949089256,2.036596090878483,0.09467100364059668,-0.013027318066665594,0.5272219853904858,0.8443368215200534,2.0669630968020454,0.7949497309273641,-0.3777316756600955,0.3918185782770094,-1.085005640818781,-2.7710125457010393,0.0,0.0,19.213989318387565,0.05916105928160631,0.02502040896612323,0.039803068034495126 +3.92,1.9109736576263594,1.3421353523658732,2.036042531292643,0.10425458628028908,-0.017177027549654684,0.5029057037102196,0.8578588369538505,1.9908118628991451,0.8778544431435137,-0.4264753016578866,0.43076690258685996,-1.0683891877303158,-2.739625983611573,0.0,0.0,19.281708807845575,0.05644834965056056,0.02651600988537972,0.04394337307499676 +3.94,1.91379566597858,1.385697973214388,2.0353955935093064,0.11396626463388129,-0.021017698728077572,0.47847137641281956,0.8704223623156736,1.907248130000698,0.951540399011849,-0.4727715758837632,0.46709119922519626,-1.0511912473364549,-2.704977737869871,0.0,0.0,19.36275052757119,0.05364344764840956,0.028015364869231063,0.048071189280270016 +3.96,1.9168622900918055,1.4284703100721694,2.0346528082333415,0.12377957698559024,-0.02456655126126312,0.4539859277079347,0.8820271414891732,1.8172601219814053,1.0153083225165402,-0.516263527458002,0.5007224665929652,-1.0332450976335055,-2.6670788355163464,0.0,0.0,19.457719185974,0.050665963728325436,0.029561209140215206,0.05218996753280114 +3.98,1.9201581349177939,1.4703581494457263,2.033811985272804,0.13366524991898082,-0.02783940450814913,0.4295165334352559,0.8926780584435179,1.7218859793251111,1.0685909503371345,-0.5565753155653704,0.5314834837679357,-1.0143244115072858,-2.6259372441649,0.0,0.0,19.567126518427187,0.047402800720109495,0.031167727315579152,0.05630440585911954 +4.0,1.923666072203913,1.5112665486215553,2.032871278410157,0.14359001313246791,-0.03085088285044722,0.4051304698780498,0.9023854129651899,1.6221997275833304,1.1109578045055062,-0.593313092193709,0.5590451297952006,-0.9941772617732566,-2.5815569658464437,0.0,0.0,19.691364172947516,0.04371441132941234,0.03281494484365832,0.060420753610960054 +4.0200000000000005,1.9273671771865055,1.551099865633995,2.031829250284301,0.15351522251281952,-0.03361499034823993,0.38089490801149656,0.9111652857366993,1.5192968092256063,1.142119374464721,-0.6260673028026873,0.5828898112337244,-0.9725661975312462,-2.533936922248691,0.0,0.0,19.830668228603397,0.039444033065552886,0.03444953344168855,0.06454696301701805 +4.04,1.931240676861849,1.5897618495192605,2.030684940168547,0.16339533285900149,-0.03614597401672837,0.3568766382334992,0.9190399876204703,1.414278866163851,1.1619307553793865,-0.6544166001196219,0.6022860016055731,-0.9493065626206572,-2.4830697362174403,0.0,0.0,19.985073246263394,0.034428384957936764,0.03599337012838921,0.0686926556540094 +4.0600000000000005,1.9352639018825224,1.62715580698309,2.029437937732323,0.1731762607288766,-0.03845931204743788,0.33314172895808286,0.9260385804401571,1.3082375854300463,1.1703945443219765,-0.6779334972227959,0.6162761424222124,-0.9242936184774532,-2.4289405385103615,0.0,0.0,20.154359287794982,0.02850708857172579,0.037360069926561934,0.07286889729141169 +4.08,1.9394122316937894,1.6631848650876488,2.0280884671735904,0.18279365912474177,-0.040572589371639295,0.3097551464719248,0.9321974535524243,1.2022376833279897,1.1676625621062922,-0.696191813969687,0.6236767353018539,-0.8975087874962373,-2.371525934606444,0.0,0.0,20.338001906510492,0.021526922927203707,0.038478059142033394,0.07708778861183205 +4.1,1.9436590228540702,1.6977523527823386,2.0266374894144765,0.19217107732129882,-0.042505977153074734,0.28678038808846656,0.9375609462617681,1.097299463942992,1.1540358196386222,-0.7087758577332737,0.623084388948623,-0.868997102218315,-2.310793263272562,0.0,0.0,20.53513888091909,0.013336834052587232,0.03931645895906488,0.08136187007546389 +4.12,1.947975514722397,1.730762326513846,2.0250868360446277,0.2012178987098018,-0.04428203585321441,0.2642791973587515,0.9421820229567749,0.9943817636192345,1.129962161566063,-0.7152911374479216,0.6128761050243834,-0.8388125432602376,-2.2467002852393323,0.0,0.0,20.74456379431066,0.003771432144474285,0.0399075459911471,0.08570333587195349 +4.14,1.9523307152885219,1.7621202669435327,2.023439397359248,0.2098268556697627,-0.04592463018194126,0.24231142837082523,0.9461230314622316,0.8943663597251496,1.0960312395494798,-0.7153763248101771,0.5911888483689683,-0.8069356747732502,-2.1791954520356502,0.0,0.0,20.96474523854291,-0.007374865767768357,0.04035854803268524,0.09012309763841964 +4.16,1.9566912820954347,1.7917339762586602,2.021699397242113,0.2178708567394381,-0.04745687462357882,0.22093509914083026,0.94945659026741,0.7980449388686985,1.0529668493876383,-0.7087162798322166,0.5558659922486013,-0.7731770212960062,-2.1082188811132885,0.0,0.0,21.193851149826457,-0.020375340205063708,0.040847552112474594,0.0946299707750386 +4.18,1.9610214269420398,1.8195147111235936,2.019872796986673,0.225198878684731,-0.0488981882196875,0.20020662584160803,0.9522666324076939,0.7061094011260808,1.001617049395746,-0.6950563886604456,0.5043681838126995,-0.7370862437338086,-2.033703952730188,0.0,0.0,21.429753821895602,-0.03558776131897512,0.04160578868654241,0.09923037548737285 +4.2,1.9652828854031505,1.8453785962942941,2.0179678764189197,0.2316308086367001,-0.050260642509154546,0.18018118314585074,0.9546495574529099,0.6191456687746498,0.9429426367885216,-0.6742190574511671,0.43366251806835854,-0.6978844605522705,-1.9555791258825144,0.0,0.0,21.669982096539798,-0.053449747992589614,0.04289794779221745,0.10392915986511131 +4.22,1.9694350011791537,1.8692483791741086,2.015996038137356,0.2369513379344722,-0.05154475235207299,0.16091312589648607,0.9567153013672608,0.537630523386323,0.8780043226648603,-0.6461238462777087,0.34011834945635844,-0.6544226489542391,-1.8737690645775853,0.0,0.0,21.91159847221541,-0.07444784251423564,0.04501318697905832,0.10873161482663421 +4.24,1.973434982546131,1.8910556040814401,2.013972866541247,0.24090326118690578,-0.05273470315071019,0.14245644176917352,0.9585879365147394,0.46193066448882547,0.8079484022375195,-0.6108129161982507,0.21944502302219004,-0.6051536240070969,-1.7881940638780611,0.0,0.0,22.150936448019994,-0.09906216905998555,0.04827406704976667,0.11364643528756103 +4.26,1.9772383979385042,1.9107433080986653,2.011919434515313,0.24318077155559895,-0.053792812982863016,0.12486526906007789,0.960405180223206,0.3923032852568968,0.7339902092030046,-0.5684835570508692,0.06670496680741914,-0.5480967871253712,-1.698766909722145,0.0,0.0,22.383132089906812,-0.127685271536504,0.05304558630080247,0.11868822446438534 +4.28,1.9807999966082106,1.9282693644917794,2.0098637835171926,0.24342366718960065,-0.054653085845848604,0.10819452326681382,0.9623159063397839,0.32889789507821277,0.6573946226646478,-0.5195291117845705,-0.12356162887689823,-0.4807987503465016,-1.6053873681852662,0.0,0.0,22.60127686658211,-0.16050172843058397,0.059723076010970044,0.12387954140134472 +4.3,1.9840749667864406,1.9436106269157438,2.0078423786458623,0.24121393608407254,-0.05521405139838153,0.09250062880437751,0.9644744056934847,0.2717593411464499,0.579453609323516,-0.46458990282214535,-0.35704548146900206,-0.40032128713068277,-1.5079344946918036,0.0,0.0,22.795187488568473,-0.1972828191381496,0.06865475405598753,0.12925069814801127 +4.32,1.987020770301245,1.9567680260996327,2.0059011761040617,0.23607742766724757,-0.05533197009800324,0.0778421899945068,0.9670296865598017,0.22083208282258465,0.5014622727601918,-0.4046140308356446,-0.6390923549986327,-0.3033456903652655,-1.4062583855315423,0.0,0.0,22.949670110448245,-0.23706894732640815,0.08000308386534283,0.13483923446151755 +4.34,1.989599694040637,1.9677727149071895,2.0040956806294985,0.2274949760032517,-0.05481642731791191,0.06428034960605199,0.9701078454707516,0.1759656707233199,0.42469682960444544,-0.34092701378320556,-0.9736875566740303,-0.1864784874439781,-1.3001716226446132,0.0,0.0,23.042534950394817,-0.2776667181886452,0.09350434910117333,0.14068685176709908 +4.36,1.9917822072499005,1.9766931611430083,2.00248907669432,0.21493020033627466,-0.053431875855886844,0.05187848999521409,0.9737857392166649,0.13692246219978854,0.3504009454483512,-0.2753031945240926,-1.361745842460857,-0.04689449184306793,-1.1894428490654465,0.0,0.0,23.04265475814403,-0.3149270216673352,0.10812656225912642,0.14683408494661948 +4.38,1.9935510611753366,1.9836426745215956,2.0011471988483054,0.19788479457695515,-0.05091042242401162,0.040701004215497945,0.9780558088471716,0.10339027220552924,0.279789771119854,-0.21002505763781454,-1.7984083876422756,0.1165509364884422,-1.0737957096861557,0.0,0.0,22.908821851229863,-0.3417365539827186,0.12159567794003967,0.15331287561413998 +4.4,1.9949057666360457,1.9887861318480826,2.0001289484150515,0.17599685555232916,-0.046983324045729184,0.0308110851072391,0.9827860149248264,0.07500513035468327,0.2140826475033013,-0.14790493344379452,-2.2689649438454937,0.3015843631894807,-0.9529182072258399,0.0,0.0,22.591082291203275,-0.34664465487338203,0.12978800559481085,0.1601373711155359 +4.42,1.9958666041099473,1.9923436058807211,1.9994711597857615,0.14920321798310499,-0.04144057908804521,0.022267936611432636,0.9876868011406261,0.051390326188925854,0.15457069963344017,-0.09222577782051387,-2.7429408720525505,0.5000265975387276,-0.8264901799927011,0.0,0.0,22.037263237456564,-0.31195983777972713,0.12606545632937238,0.16730057583939675 +4.44,1.9964767346197212,1.99458746782068,1.9991684786014448,0.11799236680685372,-0.03422789096689701,0.015124592723574064,0.9923091753834694,0.032214043298620654,0.1027063109971041,-0.04653690224574144,-3.1657427834413894,0.6945893846848327,-0.6942348856125349,0.0,0.0,21.20596813357938,-0.21079128950453352,0.10082613632226178,0.1748276669545097 +4.46,1.9968006189464822,1.995829190693021,1.999151864455269,0.08378163152155858,-0.025584971460723232,0.009428582530266424,0.9961110125314395,0.0172538977296628,0.06015683506975396,-0.01424303664842564,-3.446787830938415,0.8553756859119165,-0.5559738851385823,0.0,0.0,20.082682089984353,-0.0014404593330858972,0.041960524850413905,0.18299278404905622 +4.48,1.9969173761351182,1.996393714695754,1.9992741011782653,0.049463394455286545,-0.01621525662382466,0.005227236792058514,0.9986306194269751,0.006428685498570657,0.028687620396424758,0.002053960647023109,-3.442241728676518,0.9371331126641117,-0.4116626750998658,0.0,0.0,18.792002875268306,0.36497555083061706,-0.06357246083304169,0.19106805645819963 +4.5,1.9969088754821915,1.9965816661004083,1.9993415046422565,0.01985857176567676,-0.007467785917368671,0.00255112101835115,0.9997716544703943,-0.0002785022838532134,0.009612954857845607,0.0027195873338694684,-2.9623137591439335,0.8777782492723295,-0.2631467084182476,0.0,0.0,18.208173772876336,0.8398146106579514,-0.22879927545100892,0.18743185730308415 +4.5200000000000005,1.996846489569091,1.9966315954224705,1.999299214479492,0.0008845702548103091,-0.0015218746796181184,0.001306542784104257,0.9999975971865518,-0.003128806408322322,0.0024644968402238385,-0.0021380044434074694,-1.8969536348824176,0.5970325243738921,-0.12213347772566754,0.0,0.0,18.80181071960565,1.4808495555471888,-0.46399910944220824,0.16860912568120295 +4.54,1.9967809272265173,1.9967034557943861,1.999307880101362,0.000829557222930486,-0.0014956232227179688,0.0013016934528792944,0.9999976902674047,-0.0032674205364779823,0.0036023006506212627,0.0004369898286000532,-0.0054986063487407755,0.0026319122802268557,-0.0004788939507678197,0.0,0.0,19.1412382407069,-0.3032822128419834,-1.553573378463879,0.17018674028254116 +4.5600000000000005,1.9967291491099106,1.9967638522080373,1.9994305557607313,-0.0026834682794380374,-0.020425641644945015,0.002518372152075362,0.9997846017833487,-0.002442406877845522,0.0030180608137059055,0.006194923550100361,-0.3535881359942936,-1.8925885302383412,0.1237987760986273,0.0,0.0,18.522681033582508,-0.20222403116638965,-0.8705890510133236,0.18993210402680466 +4.58,1.9965350579091425,1.9968408554790207,1.9994942576033035,-0.00851351620817091,-0.04984836829459684,0.0051320477709610535,0.9987073256494197,-0.009427251962553776,0.0038840426135578095,0.003901724632506832,-0.5853279866149165,-2.943517766756258,0.26563550008553005,0.0,0.0,19.102027571001145,-0.09743425260697769,-0.371484632864333,0.19332285503777064 +4.6000000000000005,1.995955702301429,1.9969795373111787,1.9995322173254113,-0.015507653184835315,-0.08381862822871428,0.0091950722385489,0.9963179215998693,-0.02834868056292046,0.007253008661097198,0.005909835879309119,-0.6978664923486866,-3.405110475417108,0.41303181793389565,0.0,0.0,20.292188183550135,-0.002752855551881539,-0.005828362545412746,0.18592673540073318 +4.62,1.9946765876229324,1.997237153866804,1.9997029266654325,-0.0224411850635175,-0.11777582172067964,0.014683842132131785,0.9926780111452063,-0.06058682754551306,0.013802860414782412,0.021670682165158668,-0.6818040503520293,-3.4165703418279483,0.5557744657255824,0.0,0.0,21.296509913838808,0.0471972965012909,0.2049924237780021,0.17850942049148114 +4.64,1.9923657314327514,1.9976680255151864,2.0001255957233046,-0.028635238036224836,-0.14899178776859529,0.021570209219963884,0.9881883405476577,-0.10488679455821662,0.02369774858195292,0.05199217475441854,-0.5897838514339061,-3.1585451075622917,0.6932105471981281,0.0,0.0,22.02719243960943,0.06833431491839692,0.31240714899156774,0.1715773279525086 +4.66,1.9887133528553913,1.998313903239932,2.0008587303717316,-0.0338381260785308,-0.1760697334842086,0.02982036157925228,0.9833438748523203,-0.15909562520358783,0.0370301669194022,0.09520345930308907,-0.46356858801639894,-2.760783313090531,0.8258177123073767,0.0,0.0,22.502426707960996,0.07380856076567133,0.35550372747414816,0.16493434971366785 +4.68,1.9834537274451127,1.9992002558735722,2.0019118095865087,-0.03804472331410468,-0.19845739503137125,0.03939186838306774,0.9785783270084243,-0.2211039446198442,0.054027355355459036,0.1486777147299758,-0.328049056780377,-2.3054711392654625,0.9538297561143201,0.0,0.0,22.762247829152095,0.07147789877978006,0.3595074631006987,0.15852372385480468 +4.7,1.9763742771851907,2.0003363878099716,2.0032608877975955,-0.04137379577308445,-0.21611307363870993,0.05023507701084708,0.9741969952034706,-0.2891762484213805,0.07507694768441867,0.2093652429325756,-0.19681707001531357,-1.8429772289876136,1.0773316652033895,0.0,0.0,22.853429944451232,0.065973681945387,0.3408071892990614,0.15232140073579958 +4.72,1.967315986044041,2.0017176989206567,2.0048627649784225,-0.043991206989512976,-0.22928404216012035,0.062295993749855734,0.9703673587260857,-0.36196546149583064,0.10067919634690578,0.27419773064784986,-0.07634868932813423,-1.4027068464325072,1.1963493601516735,0.0,0.0,22.820007326427508,0.059916221461945085,0.30996530884930706,0.146309649591493 +4.74,1.956168936184962,2.003328785601236,2.0066658343122215,-0.0460650610450782,-0.23836438573939311,0.07551903832033079,0.9671387204600834,-0.4383921587546352,0.1313818510670862,0.3403469524447976,0.03117462393872708,-1.0004502724888487,1.3108982767409947,0.0,0.0,22.69901148985131,0.0546660180571269,0.2736585079563024,0.1404728087513657 +4.76,1.942865748899982,2.005146574729033,2.0086174318021484,-0.047741960936341206,-0.24380731588659266,0.0898490186628928,0.9644717993448244,-0.5174997758577407,0.1677235715048886,0.40535743612336705,0.1260116846327885,-0.6433503658158448,1.4210046481064338,0.0,0.0,22.519533971438076,0.05080933792099418,0.23598235610761142,0.13479693622132405 +4.78,1.927374631397543,2.0071430879097387,2.010668271942741,-0.04913658200752112,-0.24607352276918973,0.10523219924658211,0.9622679470616834,-0.5983388834994041,0.21019274717794734,0.46719256548683613,0.20967134473962634,-0.33322638681248584,1.526711600322581,0.0,0.0,22.30342526540423,0.04847424715948079,0.19933583029162594,0.12926976706960733 +4.8,1.9096929035181345,2.009287694648079,2.012774748157931,-0.050329221639419754,-0.24560257781134068,0.12161658751183982,0.9603935385369736,-0.679895915136933,0.25919962342264413,0.5242298096128166,0.2841711380078193,-0.06876671758509917,1.6280779878346339,0.0,0.0,22.06658986252172,0.04753403981093629,0.16501990827423163,0.12388045821298588 +4.82,1.8898413552349898,2.0115488527449616,2.014899806378808,-0.05136782168584935,-0.24279862641543665,0.13895165369766788,0.9586983946167383,-0.7610627516025629,0.31505715239049814,0.575229811125249,0.3515946912298581,0.1530495359588072,1.7251744730797058,0.0,0.0,21.820337402195626,0.047735446966365114,0.13363995700708775,0.1186192495961987 +4.84,1.8678595008204886,2.013895394936292,2.017012941588341,-0.052272247588728886,-0.23802466983035375,0.15718770507969385,0.9570286798502712,-0.840636765119334,0.37796661575993573,0.6192940390438317,0.4138443702915695,0.33605063462816276,1.8180791608985647,0.0,0.0,21.57256122720397,0.04877722478932156,0.10537380064265955,0.11347714517625379 +4.86,1.8438016635894392,2.016297438508596,2.01908970617633,-0.05303946946783751,-0.2316018287826931,0.17627510263992208,0.9552353091109893,-0.9173404353406748,0.44800563447621017,0.6558189451643034,0.4725188694246506,0.4843163744772079,1.906873788984657,0.0,0.0,21.328679008610926,0.05035650052452645,0.08014830732659906,0.10844566088440497 +4.88,1.8177337787677845,2.0187269925243885,2.021110987024154,-0.05364884795782621,-0.22381135663746382,0.1961634622003125,0.9531789831141276,-0.9898516862784243,0.5251176422742223,0.6844506755723058,0.5288686643487565,0.6018497688484927,1.991640778088311,0.0,0.0,21.092345375811664,0.052194488220024926,0.057754084056215294,0.10351664743037521 +4.9,1.7897307975587033,2.021158327997722,2.0230622109478476,-0.05406708022696752,-0.21489804615908104,0.21680093633747816,0.9507328408106732,-1.0568386705456096,0.6091028993587907,0.7050423862680705,0.5837981708892332,0.6923961258626226,2.0724611166719624,0.0,0.0,20.86597428440502,0.05404921784617445,0.0379185950648645,0.09868217822134764 +4.92,1.7598745880222078,2.0235681657836664,2.024932570919954,-0.05425258117406928,-0.2050742202744644,0.2381336340570893,0.9477835163926073,-1.1169949942911834,0.6996116443125376,0.717615238194305,0.6378942231143464,0.7593559092016203,2.1494129292454747,0.0,0.0,20.65111377634629,0.05572026094658045,0.020351556199846188,0.09393448704222132 +4.94,1.7282522453786222,2.025935727330237,2.026714321276016,-0.05415920956455149,-0.19452383277466748,0.2601052044286974,0.9442311905095604,-1.1690730761228592,0.7961401363480228,0.7223236569142687,0.6914677192060202,0.8057558331665859,2.222570553502575,0.0,0.0,20.448711176481375,0.05704868464475421,0.004771990774047552,0.08926593999851382 +4.96,1.6949547403102294,2.028242685310419,2.0284021622994732,-0.05373932994676419,-0.18340640909460607,0.28265658825104756,0.9399890566587319,-1.2119144774145012,0.8980302589063541,0.7194251482037175,0.7446001271818616,0.8342548324893665,2.292003970716829,0.0,0.0,20.259299257602713,0.057914322367599746,-0.009076727681925134,0.08466902834242375 +4.98,1.6600758469728463,2.030473044364517,2.0299927179649324,-0.052946248161685625,-0.17186068108146182,0.30572592734735093,0.934982490986553,-1.2444767372094128,1.004473151650003,0.7092547377190569,0.7971897821743501,0.8471697086753869,2.357778466703891,0.0,0.0,20.08312645770313,0.05823170184679349,-0.021419600517034065,0.0801363716983255 +5.0,1.6237113026390892,2.0326129764403666,2.031484101402645,-0.05173608338705181,-0.16000784465478718,0.32924861282076934,0.9291481142854979,-1.2658566171211436,1.1145170913709233,0.69220391079586,0.8489950667412615,0.846510685761854,2.4199544332676814,0.0,0.0,19.920247952545775,0.05794547762687907,-0.032447657741329516,0.07566072416215083 +5.0200000000000005,1.5859581587099643,2.034650630264965,2.032875558166497,-0.05006915072551226,-0.14795441473715187,0.3531574499686031,0.9224328630513369,-1.2753098203181694,1.2270796029755364,0.6687037640609849,0.8996729920447157,0.8340207056393639,2.4785872472846036,0.0,0.0,19.770589506803823,0.0570258861747176,-0.042318648455439534,0.07123497840901291 +5.04,1.5469142889039476,2.0365759301528157,2.0341671758315143,-0.04791093043098995,-0.13579467934290687,0.3773829171092331,0.9147931360048126,-1.2722672800571702,1.340963575214867,0.6392119512165209,0.9488126205144527,0.8112146662377602,2.533727185456412,0.0,0.0,19.633992418910346,0.055464520358510905,-0.05115951285160247,0.06685216513396962 +5.0600000000000005,1.5066780253232657,2.038380375571236,2.03535964860354,-0.04523269638451766,-0.12361277113056109,0.401853497099657,0.9061940481265508,-1.2563480797176203,1.4548769913296904,0.6042029172669303,0.9959633449054013,0.7794163649367644,2.5854193480123806,0.0,0.0,19.510245294509915,0.053270571485966145,-0.059069808923460365,0.06250544682032641 +5.08,1.465347897212652,2.0400568496083156,2.0364540866988037,-0.04201186974111234,-0.11148438415552987,0.426496062870559,0.8966088016778724,-1.227369010149265,1.567455761486095,0.5641608699686582,1.0406583718206968,0.7397919280778936,2.633703575497193,0.0,0.0,19.399106581777485,0.050467589060048446,-0.06612555490451644,0.05818810600217915 +5.1000000000000005,1.4230224508280842,2.0415994417134566,2.0374518617351276,-0.0382321554213294,-0.09947816849008725,0.45123630120982194,0.8860181695406567,-1.18535071791849,1.6772890562081606,0.5195749309985825,1.082433926039936,0.6933791671195286,2.6786143504279507,0.0,0.0,19.300320565873946,0.04709074631652166,-0.07238314187793682,0.05389352892899342 +5.12,1.3798001320708768,2.043003287793272,2.038354480953857,-0.033883510453221966,-0.08765683735390545,0.47599916185304086,0.8744100779952618,-1.1305203646896667,1.7829464811001394,0.47093593565967223,1.1208447505812997,0.641112723309559,2.720180680985433,0.0,0.0,19.213628682643805,0.04318456328687581,-0.07788310065850856,0.04961518593219737 +5.14,1.335779216488717,2.044264428927852,2.0391634845772137,-0.02896198483867846,-0.07607802142359926,0.5007093214516932,0.8617792719147168,-1.063310710309204,1.8830063968701973,0.41873440077062707,1.1554764666028425,0.5838451257937768,2.7584259676941723,0.0,0.0,19.138777449408007,0.03880102115713513,-0.08265359352419888,0.045346609918390075 +5.16,1.2910577739268794,2.045379688593019,2.039880361901701,-0.02346946808759086,-0.06479490374039072,0.5252916540885171,0.8481270439397087,-0.9843555514855786,1.9760846705707928,0.3634592475175918,1.1859553061983155,0.5223640461626728,2.793367856684612,0.0,0.0,19.07552393453889,0.033997996444309564,-0.08671355852602103,0.041081374341834745 +5.18,1.2457336575338112,2.046346567291777,2.0405064827936896,-0.01741336807928201,-0.053856666585963615,0.5496717016929351,0.8334610095465634,-0.8944814920629844,2.060863141422319,0.3055969420693309,1.2119556605848516,0.4574061212622324,2.8250180848876383,0.0,0.0,19.023639435430148,0.028837946683900717,-0.09007547130751725,0.03681307179770284 +5.2,1.1999045089807792,2.0471631528647043,2.041043042084227,-0.01080624355766566,-0.04330877918708477,0.5737761390312308,0.8177949113463048,-0.7946960854285485,2.1361170964558207,0.24563079549663352,1.2332058103101402,0.38966775968674994,2.8533823235400857,0.0,0.0,18.982911864107354,0.023386787641272888,-0.09274771299338297,0.0325352940713073 +5.22,1.1536677736402476,2.0478280444139494,2.041491014975066,-0.003665407318409133,-0.03319315238145191,0.5975332289876945,0.8011484379811966,-0.6861724690812737,2.2007410773466414,0.18404024115349576,1.2494921321288335,0.31981336201907407,2.878460026762646,0.0,0.0,18.95314722253732,0.01771291307755707,-0.09473654710967438,0.028241614111676847 +5.24,1.1071207210807144,2.0483402876890517,2.041851121998734,0.003987486092648987,-0.02354818359778068,0.6208732647010606,0.7835470452904233,-0.5702306998521411,2.253772380479624,0.121299978563568,1.2606620145434424,0.24848138170450662,2.900244291757431,0.0,0.0,18.934170466666593,0.011886320023953956,-0.09604771665805117,0.023925569999496632 +5.26,1.0603604675804157,2.0486993198987298,2.042123802360951,0.012126856711306986,-0.01440871278131149,0.6437289958546221,0.7650217697806311,-0.4483160897092641,2.294411667673853,0.057878935432373324,1.2666256633061697,0.17628864007724315,2.91872173640097,0.0,0.0,18.925825996663626,0.0059778140446064,-0.09668767644568416,0.01958065057889999 +5.28,1.013483998446879,2.048904922189346,2.042309194675129,0.020723609680518913,-0.005805907296197038,0.6660360371031014,0.7456090267172693,-0.32197493083115303,2.322040174833641,-0.005760948083332232,1.267356939998972,0.10383329075533647,2.9338723987295894,0.0,0.0,18.927977962165777,5.827915767830927e-05,-0.09666447701697867,0.015200282053826435 +5.3,0.9665881887657622,2.048957178431498,2.042407124216687,0.029745531144901072,0.0022329086075847378,0.6877332573022992,0.7253503872587561,-0.19282807955799727,2.3362330891332364,-0.06916606737721086,1.2628933501920214,0.031696808946968635,2.9456696611030044,0.0,0.0,18.94051052952844,-0.005801994764208285,-0.09598831607741298,0.010777814554294716 +5.32,0.9197698218053258,2.0488564394496276,2.042417095917238,0.03915766119543105,0.009684466445480782,0.7087631489203708,0.7042923309335763,-0.06254293753953899,2.3367687604687464,-0.1318925585849337,1.2533352805180855,-0.039554637514180244,2.9540801997996513,0.0,0.0,18.963328218719674,-0.01153392872976955,-0.09467177187990228,0.006306507504580654 +5.34,0.8731256046879892,2.048603292388864,2.042338292418571,0.04892263561874492,0.016529797030925202,0.7290721777713,0.6824859713825961,0.06719557864279618,2.3236335173787657,-0.19350766808409914,1.2388445732849278,-0.10936909498993104,2.959063958597485,0.0,0.0,18.99635637716179,-0.017070214605256595,-0.09272973114221678,0.001779512624416744 +5.36,0.8267521811233117,2.0481985355160117,2.0421695766417267,0.05900098788603061,0.022754495513004215,0.7486111140321333,0.6599867546473863,0.19471445346722635,2.2970219684194864,-0.2535909870594804,1.2196425192324023,-0.17720768169688844,2.9605741427226127,0.0,0.0,19.039541813684192,-0.022345818390737016,-0.09017902188333105,-0.002810146392331256 +5.38,0.780746140991301,2.047643159378873,2.0419094985197765,0.0693514021136024,0.02834900012715859,0.7673353463916356,0.6368541303899161,0.3183817334216543,2.257332783689201,-0.3117354561957698,1.1960073393424382,-0.24254307927847424,2.9585572276332006,0.0,0.0,19.09285356542319,-0.027298431650515376,-0.08703775894660881,-0.007469598031441239 +5.4,0.7352040263816885,2.046938335884741,2.041556305811978,0.0799309066225682,0.033308878081007194,0.7852051821332138,0.6131511973066882,0.4366347979606421,2.2051600643317686,-0.36754805943232555,1.1682712102982515,-0.3048573478181833,2.952952975684599,0.0,0.0,19.15628371184284,-0.031868884896376404,-0.08332440630088345,-0.012206150798816146 +5.42,0.6902223333390551,2.0460854174921645,2.0411079592762076,0.09069499617563713,0.03763511270528821,0.8021861369830048,0.588944324696864,0.5480071312440128,2.141280517068467,-0.42065014784556043,1.1368168598838777,-0.36363875570067616,2.9436944530033404,0.0,0.0,19.229848080986734,-0.03600155001638672,-0.07905655432833991,-0.017027323811472678 +5.44,0.6458975080334739,2.045085949328916,2.0405621529389135,0.10159766890653338,0.04133438602136294,0.8182492196584336,0.564302752725308,0.6511530298728894,2.066636753177132,-0.47067736374549296,1.10207371263607,-0.41837731080518237,2.930708039018837,0.0,0.0,19.31358661136595,-0.03964477646768971,-0.07424940060771268,-0.021940878408268846 +5.46,0.6023259353602554,2.043941697641517,2.0399163407804437,0.1125913612819924,0.04441934999037784,0.8333712172360268,0.539298174476461,0.7448697429367869,1.9823171245237405,-0.5172791697055555,1.0645134966986276,-0.468558663654563,2.9139134220779557,0.0,0.0,19.407563035826488,-0.04275142747041494,-0.06891390738907294,-0.0269548484233431 +5.48,0.5596039170470759,2.042654698532062,2.03916777186373,0.12362676100888896,0.04690887765882368,0.847534988720557,0.5140043035361531,0.8281166158160842,1.8895325916496322,-0.5601180264254231,1.024645124883348,-0.5136560199952427,2.893223576259547,0.0,0.0,19.511863437693805,-0.04527960663912245,-0.06305458672262099,-0.03207756649290881 +5.5,0.5178276351740567,2.041227331422269,2.0383135367944663,0.13465247343796666,0.04882828191921183,0.8607297755371673,0.4884964317020066,0.9000308953720358,1.7895911896793546,-0.5988683062849557,0.9830085327479618,-0.5531196418136844,2.868544716609009,0.0,0.0,19.626593089141274,-0.04719368988293626,-0.056666835380565383,-0.03731768401151545 +5.5200000000000005,0.47709309553584245,2.039662422064362,2.0373506294344246,0.14561451151170565,0.05020948437041197,0.8729515390692123,0.4628509826641379,0.9599399482854384,1.683870716753691,-0.6332150757541183,0.9401669936917569,-0.5863634242501878,2.8397762320800006,0.0,0.0,19.751870808548684,-0.04846579775834425,-0.049733710060780034,-0.042684183232928274 +5.54,0.4374960434155236,2.0379633801335526,2.036276029025951,0.15645557247421135,0.051091109510179304,0.884203336781856,0.4371450692786293,1.0073697432703075,1.5737903170384733,-0.6628529294547028,0.8966972411000053,-0.6127479079922,2.8068105969609487,0.0,0.0,19.887819853365862,-0.04907785371935258,-0.042222008457437676,-0.04818638158260186 +5.5600000000000005,0.3991318419920644,2.0361343763963364,2.0350868093614247,0.16711405636502946,0.051518469991736406,0.894495749784625,0.41145606452229844,1.0420495486022239,1.4607816663303925,-0.6874851121361558,0.8531765245204801,-0.6315589309817126,2.7695332619089332,0.0,0.0,20.03455407458581,-0.04902436708091984,-0.03407752165938349,-0.053833930515043815 +5.58,0.36209530062655876,2.034180564019471,2.033780283388139,0.17752277205689182,0.05154339679043123,0.9038473756118505,0.3858611994592244,1.0639128955501846,1.3462604924881794,-0.7068232233748823,0.8101655350599595,-0.6419809704590863,2.727822524393751,0.0,0.0,20.19215766383374,-0.0483160514131632,-0.025219376361509338,-0.05963681412477881 +5.6000000000000005,0.32648043649154707,2.032108347577936,2.032354193713683,0.1876072671989567,0.05122385385134656,0.9122853999405067,0.3604372055520139,1.073094951790294,1.2315991736775835,-0.7205878655595104,0.768185987123694,-0.643064142742276,2.6815493750194768,0.0,0.0,20.36065627544054,-0.04698434226275028,-0.015533529557241965,-0.06560535594754191 +5.62,0.2923801482064158,2.0299257014797214,2.030806961876912,0.19728371080486728,0.050623260250760374,0.9198462587783036,0.3352600232419656,1.0699265309916144,1.1181011511621963,-0.7285106715196844,0.7276915711101783,-0.6336839278570139,2.6305773108615154,0.0,0.0,20.53997652802045,-0.04508682757578775,-0.004865756075071503,-0.07175024548315612 +5.64,0.259885774071692,2.0276425365128827,2.0291380118987257,0.20645625463861308,0.04980942429247553,0.9265763963486032,0.31040460354492866,1.0549250273210877,1.0069778644773684,-0.7303382367356319,0.6890310103902176,-0.6124931373117065,2.574762100300284,0.0,0.0,20.729889733104127,-0.04271357477343107,0.006985082998201442,-0.07808259852774752 +5.66,0.22908649988742938,2.025271108606324,2.0273481862621696,0.21501380691405947,0.04885297344700438,0.9325331102798743,0.28594483393384373,1.0287825957925836,0.8993288582387484,-0.7258385835739649,0.6524020529262844,-0.5778666614954622,2.513951477210349,0.0,0.0,20.92993383644941,-0.03999434706276017,0.02027206731502673,-0.0846140682133701 +5.68,0.20006857192998947,2.022826457150304,2.0254402744616526,0.22282617590719575,0.04782514191351383,0.937785450063387,0.26195362333996897,0.9923518892246176,0.7961256146165512,-0.7148108991944944,0.6177953724648431,-0.527841358812956,2.4479847370386376,0.0,0.0,21.13930443109181,-0.037106734239650496,0.03530130341711679,-0.09135702968842911 +5.7,0.1729142592216093,2.0203268507112195,2.0234196743241775,0.2297395905579262,0.046794754826817955,0.9424150907051176,0.238503182918466,0.9466296013977282,0.6981995288515499,-0.6970994069457337,0.5849275387321019,-0.46005631046299883,2.3766922029418422,0.0,0.0,21.35670090179721,-0.034285223394837194,0.05242588120042152,-0.09832487102361498 +5.72,0.14770049568519264,2.017794204969387,2.0212952021781896,0.23557169051245846,0.04582422749300774,0.9465170333331453,0.2156655383087271,0.8927379567336458,0.6062342662945551,-0.672612333953737,0.5531625112362023,-0.3717027458767676,2.299894527081041,0.0,0.0,21.580108613381093,-0.031830942557951714,0.07202850357898277,-0.10553241143267444 +5.74,0.1244971176025976,2.015254420411483,2.019080056287272,0.2401062117039739,0.04496438758027576,0.9501998802108411,0.19351330341348694,0.8319041560314347,0.5207625231846071,-0.641346974137237,0.5214219401679777,-0.25949831857117966,2.2174018108163938,0.0,0.0,21.80648606234112,-0.030119995904925867,0.0944882076887597,-0.11299655337015832 +5.76,0.10336459593390626,2.012737564889002,2.0167929128304363,0.24308778470613285,0.044247958888987735,0.9535852881773237,0.1721207286546094,0.7654376998521049,0.4421669696040866,-0.6034218087925647,0.48808801339167873,-0.11970627560323568,2.129012497224006,0.0,0.0,22.031451380696335,-0.029601752642732126,0.1201206544737955,-0.1207350405990617 +5.78,0.08435116158221188,2.0102777981465576,2.014459111985072,0.24421759787696332,0.043681692896926444,0.9568060063469387,0.1515649722438736,0.6947055957876909,0.3706848817608944,-0.5591151378704142,0.4509150980053646,0.051766566531574604,2.034513691089236,0.0,0.0,22.248615823258532,-0.03079107779122059,0.14908524438644027,-0.12876871006458587 +5.8,0.06748920111002012,2.0079129081493834,2.0121117862501,0.24315107360769653,0.04323727885882803,0.9600016777057218,0.13192752526485063,0.6211057789116033,0.3064155850570947,-0.5089121832080591,0.40696214491798277,0.2591506886391277,1.933680524098913,0.0,0.0,22.448836730861263,-0.03423509135295966,0.18123955931700125,-0.13712204307848364 +5.82,0.052790828216775235,2.005683301108188,2.0097926955946956,0.23949940113202653,0.04284157316517555,0.9633113415591502,0.11329561196111496,0.5460398643574069,0.2493298131234022,-0.45355849108586394,0.35258529882653,0.5064062413155793,1.8262762742789167,0.0,0.0,22.619229287365073,-0.04044019791902009,0.2159314638189232,-0.14582456518225315 +5.84,0.040242593598036565,2.0036302731956526,2.007552375259223,0.23283763095917911,0.04236736773168452,0.966861438295891,0.09576326486292351,0.47088767967416134,0.19928009685705347,-0.39411722465630067,0.28355104026661004,0.7965476769611324,1.7120530732147425,0.0,0.0,22.742154662068145,-0.04975265658133088,0.2516851377293202,-0.15490998685699478 +5.86,0.029799441316327235,2.0017934142990383,2.005449035755677,0.22272350494666968,0.04162673109018765,0.9707481984664464,0.07943167363945401,0.3969880425540927,0.156011807746212,-0.3320234327232464,0.19534077146274403,1.1305668039948626,1.5907553686406377,0.0,0.0,22.793921948296013,-0.06209225853862099,0.28585656261074643,-0.16442013993978 +5.88,0.021378252238055406,2.00020706487295,2.003545399366091,0.2087319725126824,0.04037133394014528,0.9750142132534833,0.06440809732129225,0.32563198544414795,0.11917596049128952,-0.2691281843787391,0.0838539632184514,1.505916822620113,1.4621226309632516,0.0,0.0,22.743881691273092,-0.07665795623059624,0.3139889396907984,-0.17440615928917957 +5.9,0.014851721534236549,1.9988959514579288,2.001902550413921,0.1905142781113701,0.03830481390490193,0.9796203894979043,0.05080298758329584,0.25807699943856655,0.08834586644893719,-0.2077161064272749,-0.05344378481503345,1.9141853712839498,1.325892537905797,0.0,0.0,22.554604216117035,-0.09123988326481736,0.3290296405191221,-0.18492759010349147 +5.92,0.010043926041649949,1.9978704229605457,2.0005699765898584,0.16789278268655566,0.035118083308032164,0.9844181764617261,0.03872450901308811,0.19558659346488566,0.06304331769822005,-0.15047286773331942,-0.21539295242877923,2.3377985148598364,1.1818031972040086,0.0,0.0,22.1856972372209,-0.10142307682501106,0.31954271064891004,-0.19602529933771368 +5.94,0.006729695614413436,1.99712222736846,1.999571829742075,0.14101419056187517,0.030560011232449405,0.9891318604096482,0.02827094791831651,0.1394919015071433,0.04277760563931368,-0.10035794085197994,-0.39389416370080177,2.744576947809456,1.0296054503231842,0.0,0.0,21.605752202254802,-0.09955461387159992,0.2693906565984273,-0.20764176752623678 +5.96,0.004640712482444695,1.9966223089423412,1.998891589488236,0.1105626164017985,0.02455862529252359,0.993373887368264,0.019521825554842404,0.09124245275105462,0.02709711093192845,-0.06030143304647963,-0.569394682245811,3.082195100171662,0.8691174326288605,0.0,0.0,20.79239299183078,-0.07347083868008131,0.15352449747944769,-0.21979680865097811 +5.98,0.003479828069386919,1.9963222156572584,1.9984572181240863,0.07807202093768191,0.017402983586605924,0.99671700769327,0.01253404499610986,0.0523901787848524,0.015636672987310488,-0.032837641570781756,-0.7053390251328062,3.268320864524301,0.7001326292021245,0.0,0.0,19.767893089586973,-0.006628627618305849,-0.06363908696639775,-0.2323636231095203 +6.0,0.0029454047149399037,1.9961605855431919,1.9981403429141085,0.04630372681097297,0.009947081741077711,0.9988508613061422,0.007346925631106359,0.024377025221913436,0.00811116587081736,-0.019253632177605354,-0.7463139591028505,3.1833228608207564,0.5227025210296768,0.0,0.0,18.859639173855093,0.09940229662978983,-0.39467972505209825,-0.24119712919628844 +6.0200000000000005,0.002765424143353381,1.9960751857704766,1.9978323095861112,0.019167908292807827,0.0034557213502329075,0.9998024200843382,0.0039711553896272485,0.007923309129780159,0.004155669722973686,-0.016014584455310135,-0.6537281875362669,2.713816080364414,0.3409983461056387,0.0,0.0,18.782387156127587,0.21210093808219788,-0.8173661686586461,-0.23690746824789427 +6.04,0.0027187462652102476,1.9960130255143576,1.9975619630208303,0.001709417558514515,-0.0007187356894446683,0.9999955264665803,0.002346989017082791,0.002031656295942921,0.003084830749777505,-0.013572118867902865,-0.4212726201881396,1.744872284057725,0.1644385300357949,0.0,0.0,19.625641231141206,0.3607931153740418,-1.4418920901564056,-0.22164576978643474 +6.0600000000000005,0.0026554328530712648,1.9959377533912808,1.9975247571510324,0.0016602319389490128,-0.0007389451312452054,0.9999956048248881,0.002342631193352069,0.003141702396159369,0.003781111275874072,-0.0018654214367929498,-0.002031728385216758,0.004913497004097822,0.0004427885978891821,0.0,0.0,10.594326710729092,-1.37727847924261,-0.8501082672700033,-0.1711354472147628 +6.08,0.002575933387592718,1.9959057634491715,1.9962142553422206,0.010572640157522305,-0.01755365540745269,0.9997838403007046,0.003515840654989765,0.003160543952008314,0.002819353185108936,-0.06553229069961815,-1.679688003551399,-0.8951786817697706,-0.11524396891225017,0.0,0.0,9.51659064497822,-0.8488065448193299,-0.47830024606567856,-0.18421470053461825 +6.1000000000000005,0.0025092722473512635,1.9958629824284955,1.9933372181480584,0.024716591661040017,-0.044832634980182785,0.9986710389078143,0.005939779809211699,-0.0018023968661845884,0.01111785501653128,-0.14348776065091148,-2.727284052752327,-1.4208271764309885,-0.23875734094594275,0.0,0.0,9.970145331246004,-0.4713539704680362,-0.1946567907327227,-0.19691338248080403 +6.12,0.0025260119646469434,1.9956780759396011,1.9889362556878687,0.04096994881727714,-0.07791462172590469,0.9960700933299481,0.009754188274127893,-0.015575247004524844,0.036003438940809616,-0.2167732817541586,-3.315999374934077,-1.6325288049320852,-0.37366545897266473,0.0,0.0,10.739825128106176,-0.22156298748320247,-0.021952418344009045,-0.20352396797030284 +6.140000000000001,0.002718686413776057,1.9951619789217871,1.983089063110715,0.05731615591429593,-0.11362097116431545,0.9917547390837336,0.015088760314937343,-0.03924695074113438,0.08034160394949857,-0.2797903956483685,-3.59708973004568,-1.6395756142682263,-0.5167790548798193,0.0,0.0,11.600879958867713,-0.04490513213399272,0.09328140886570555,-0.206955518997323 +6.16,0.0031863083880545283,1.9940867829398403,1.975863141456467,0.07240444818210395,-0.14969509060107195,0.9858306766588729,0.022064738649949966,-0.0723575374372421,0.1438806022465055,-0.3287918536737266,-3.663179616837401,-1.5059174172235332,-0.6656352174542737,0.0,0.0,12.527542972875622,0.08709189264195538,0.1706152339314256,-0.2063344849236709 +6.18,0.004022451514262627,1.9921993799790456,1.9673139258113204,0.08536988669312585,-0.18448326536517098,0.978637215714489,0.03077510787632606,-0.11368764792401204,0.22433953988463418,-0.36085254149281115,-3.572563332404235,-1.276946322467716,-0.816976554675675,0.0,0.0,13.526090490902035,0.19553082831633029,0.22134765968575137,-0.2007237425998143 +6.2,0.00530778056989916,1.9892282284535976,1.9574959625890966,0.09570053579094866,-0.21663790843094582,0.9706736337672938,0.041256766584260245,-0.16184162454740558,0.31822786224032484,-0.3739308560686301,-3.352111017753502,-0.9847836393441572,-0.9665731801922017,0.0,0.0,14.414461793886016,0.27316480387538966,0.2466436979140057,-0.1929000796450307 +6.22,0.007099960228508782,1.9849031701634834,1.9464495982123147,0.10319510750596432,-0.24512987710387707,0.9624969392599908,0.05349537412730255,-0.21560866501301162,0.42136099931334237,-0.36868398156715215,-3.029530085654834,-0.6582627331947949,-1.111595877849576,0.0,0.0,15.111246642626774,0.31663020133747555,0.25157455169875526,-0.18436037877042333 +6.24,0.009426531381034525,1.9789786549152277,1.934191868186672,0.10789622371726294,-0.2693272803977547,0.9546057323860846,0.06744713957411855,-0.27425216012217646,0.5295999076725992,-0.3483193769547017,-2.6416369578618846,-0.32140616252082493,-1.2507807305873144,0.0,0.0,15.604616079386984,0.3320014523582986,0.2409789731462627,-0.1757166772524511 +6.26,0.012283440037862085,1.97124877584085,1.9207186797659308,0.11002861175805138,-0.2889507147711626,0.9473671818498283,0.08304584144762847,-0.3375774216024227,0.6394793957921519,-0.31749330519327873,-2.222393818421021,0.006101629043159917,-1.3837426159606854,0.0,0.0,15.924854582131811,0.3275653702134703,0.2203005967936237,-0.16702558123343508 +6.28,0.015638635859189738,1.9615522501551594,1.9060135875726931,0.10993261865511776,-0.30397634929217404,0.940995529076999,0.10020285766249042,-0.405827360119001,0.7483967942216074,-0.2812326724142669,-1.797330091510294,0.3099881952834179,-1.5103557938266579,0.0,0.0,16.13661970264307,0.3075837257015961,0.19018980676847946,-0.15780965217654536 +6.3,0.019438265417655066,1.9497699515906377,1.8900586628803469,0.10803947254014706,-0.3145796622596411,0.9355502966788394,0.11879709950159824,-0.47947484378783034,0.8544799493018166,-0.2441364327291432,-1.3867840128492979,0.5773033694875715,-1.6302939940017098,0.0,0.0,16.226474939946293,0.28125617653014345,0.16193233473870364,-0.14888938936604212 +6.32,0.023612812993445413,1.9358240833709601,1.8728354965861969,0.10476317740292924,-0.32102861817781386,0.9309820193591172,0.13870033381829405,-0.5590485717975952,0.9563679644486135,-0.21049147107814714,-1.0010742126606913,0.806725113201463,-1.743714123819272,0.0,0.0,16.237709449743164,0.2503784937307304,0.13520613674743476,-0.14006353975004326 +6.34,0.02808315815563785,1.9196728779190193,1.8543298073391252,0.10048986585148005,-0.32365685385942033,0.9271565930904436,0.1597769686153008,-0.6449177234752063,1.0529457499709818,-0.18383052326386773,-0.6476198891381584,0.9980083303366256,-1.8506851295799671,0.0,0.0,16.191409017452514,0.21744523863746001,0.11165386837887706,-0.1314304943057376 +6.36,0.03276563127888043,1.9013059297330233,1.8345327870648647,0.0955555415916126,-0.32283195400153275,0.9238854418251964,0.18189106171042993,-0.7372076908823693,1.1431711320447377,-0.16695852658259214,-0.33048418534753965,1.153636050472468,-1.9513454738275868,0.0,0.0,16.106182202064282,0.1840779965848216,0.09190253802266121,-0.1230549290723171 +6.38,0.03757611115893462,1.8807394861162061,1.8134416185308961,0.09023634979124004,-0.31893487581409335,0.9209503016539552,0.20491092711051476,-0.8357557232341175,1.2259621829157212,-0.16194914876384758,-0.05143422764519423,1.2775486043031898,-2.0458761185491547,0.0,0.0,15.998481839330568,0.15131582130777577,0.07592211580728683,-0.11496789129295881 +6.4,0.04243319965275134,1.8580119147476464,1.7910595740815234,0.08474743426484148,-0.3123468913856655,0.9181222042777443,0.22871141169476786,-0.9401090054801222,1.300151418023285,-0.17017139547365306,0.189302418785689,1.3742059971690206,-2.134474583832632,0.0,0.0,15.882480297564882,0.11983183109933904,0.0633298863914287,-0.10717674229970116 +6.42,0.04726056734667027,1.8331795119952,1.767395911413976,0.07924678095571819,-0.3034411336751331,0.9151754354656116,0.2531745414184345,-1.0495478280760138,1.364493342042882,-0.19233720868124546,0.3925379056450891,1.448010147018265,-2.2173379060379026,0.0,0.0,15.769951111908798,0.09007077745653502,0.05359287055195991,-0.09967525143387874 +6.44,0.05198858812439886,1.806312753358189,1.7424656830966585,0.07384156274688068,-0.29257683078064023,0.9118974518580697,0.27818925032469305,-1.1631208289256922,1.4177091972737652,-0.2285645686060699,0.5598000511343091,1.5029973103529048,-2.2946541998628254,0.0,0.0,15.670288674614802,0.06232786653825084,0.04614452497404892,-0.09245072161601756 +6.46,0.0565553832193662,1.7774930312663884,1.7162895202951929,0.06859550488068929,-0.280095151789342,0.9080955966530451,0.3036507039241285,-1.2796844147233422,1.45855343315968,-0.27845241993793457,0.6931143471757581,1.5427007599476625,-2.366599881471663,0.0,0.0,15.5906571982717,0.03679208426971092,0.04044515333442744,-0.08548806303417306 +6.48,0.060907386169347114,1.7468098841398383,1.6888934155232858,0.06353645444234068,-0.26631613527492826,0.9036012725144753,0.3294595200617875,-1.3979424096029618,1.48588851337817,-0.3411641809150079,0.7948467444344854,1.570110314436772,-2.4333397285540825,0.0,0.0,15.536211845791648,0.01357132163500907,0.036010702667027994,-0.07877186388917076 +6.5,0.06499952596912931,1.7143586949755794,1.6603085055683429,0.058663707213379684,-0.2515364645377675,0.8982720986878389,0.35552104464119805,-1.516484449598961,1.4987573830557668,-0.4155164200371329,0.8675827881208772,1.58768448655003,-2.4950280185217704,0.0,0.0,15.510352544397591,-0.007291032884172877,0.03242451557717008,-0.07228730158561208 +6.5200000000000005,0.06879511105492116,1.6802388229275602,1.6305708428079986,0.05395484204970933,-0.23602798699850067,0.8919924941614576,0.3817447507595204,-1.6338229335281382,1.4964458208312443,-0.5000689095109747,0.9140310565654364,1.5973901026059765,-2.5518098366956807,0.0,0.0,15.51498917179968,-0.025803360541772014,0.02933985201938283,-0.06602041212616291 +6.54,0.0722654836421191,1.6445521232769305,1.5997211393058985,0.04937192116158949,-0.2200369221041412,0.8846730694534242,0.40804378012121023,-1.7484288355203608,1.4785297284198733,-0.5932120797856582,0.9369450687341323,1.6007551198587824,-2.6038221569457916,0.0,0.0,15.550802981221882,-0.042014683462958446,0.02647786618074499,-0.05995807346715987 +6.5600000000000005,0.07538950302103724,1.6074018073958234,1.567804470469187,0.04486697552181615,-0.20378370155643458,0.8762491618435855,0.4343346219862013,-1.858766701197733,1.4449049522156556,-0.6932481254032267,0.9390609530990339,1.5989270304511243,-2.651194609762185,0.0,0.0,15.617497068811112,-0.05600452821471802,0.023623343192872015,-0.05408786290687845 +6.58,0.07815290749693016,1.5688915931896812,1.5348699312982474,0.0403867348974717,-0.18746336807806074,0.8666788028136341,0.4605369149445862,-1.9633289159336293,1.3957993251175638,-0.798462627694587,0.9230491909196463,1.5927326227817455,-2.6940499711593535,0.0,0.0,15.714030383905818,-0.06787382858009627,0.020619427172984484,-0.04839787389001608 +6.6000000000000005,0.08054759726588546,1.5291250974190307,1.5009702458966767,0.03587659222458554,-0.17124643953350097,0.8559403592285162,0.48657335369007676,-2.060669012620683,1.3317681812189506,-0.9071844735547925,0.8914789051385368,1.5827367021859324,-2.7325044428135965,0.0,0.0,15.838830555342003,-0.07773715870033548,0.01736193655194518,-0.042876538197464543 +6.62,0.08257087417050578,1.4882054239123383,1.4661613380234628,0.03128381675513632,-0.1552801323124122,0.8440300418665139,0.5123696826951041,-2.1494334834270625,1.2536756237784161,-1.0178329036100155,0.8467929729006433,1.5692982961715565,-2.7666677928719987,0.0,0.0,15.989981467689864,-0.08571658107944091,0.013793503056486467,-0.03751248061342274 +6.640000000000001,0.08422466862483638,1.4462349056981605,1.4305018758021932,0.02656005102812518,-0.13968983483824465,0.8309594283744067,0.5378547593244168,-2.2283913377865336,1.1626643737396212,-1.1289515566822848,0.7912919919976301,1.552623292509954,-2.796643414020315,0.0,0.0,16.16538261833279,-0.0919372518256063,0.009897585535229791,-0.03229442171640172 +6.66,0.08551477882640704,1.4033149641418674,1.3940528066722429,0.021663143358580432,-0.12458072888706792,0.81675310499473,0.5629606697552327,-2.2964605374141525,1.0601171881988987,-1.2392302608559447,0.7271249913702487,1.5328126331427494,-2.822528341648367,0.0,0.0,16.362878872997904,-0.09652472731967476,0.0056923433972813076,-0.027211134446703034 +6.68,0.08645014258720729,1.3595460538202715,1.3568768992815714,0.016558379965278792,-0.11003947090110808,0.8014464944270909,0.5876228820146953,-2.3527304443029093,0.9476127271176271,-1.3475159750964796,0.6562848231989489,1.5099052754125155,-2.844413262903763,0.0,0.0,16.58036105838654,-0.0996037249892,0.0012243651405514089,-0.022251450523020737 +6.7,0.08704215768585734,1.3150276676360322,1.3190383077042007,0.011219188999814604,-0.09613586438243177,0.7850839070494533,0.611780421613502,-2.396479527865723,0.826878467281136,-1.452814683125362,0.5806064232945933,1.483915232383146,-2.8623825364910647,0.0,0.0,16.81583951548521,-0.10129795348338677,-0.0034377015067034167,-0.017404305089740887 +6.72,0.08730406257363038,1.2698583821528664,1.2806021707482593,0.005627392866438887,-0.0829244744141636,0.7677168296459258,0.6353760567341753,-2.4271877695370847,0.6997428995188191,-1.5542862010555956,0.5017665349887067,1.4548621614565131,-2.876514232702265,0.0,0.0,17.067493976354847,-0.10173056973257233,-0.008209510767947736,-0.012658803871849492 +6.74,0.08725038557349722,1.2241359280310067,1.2416342558486742,-0.00022691410757066601,-0.07044615372984606,0.7494024492412767,0.6583564817071632,-2.444543447944236,0.5680888572524863,-1.6512338278564929,0.42128397951042634,1.422795190386669,-2.886880193933437,0.0,0.0,17.3337037766796,-0.10102484199843231,-0.012997215915440563,-0.008004297088631982 +6.76,0.08689646741485195,1.1779572746037485,1.2022006536453913,-0.006345787680399134,-0.05872946492754861,0.7302023986536947,0.6806724894752467,-2.44844425453671,0.4338094465410452,-1.7430905995714545,0.3405200341071961,1.387809931944069,-2.89354610911944,0.0,0.0,17.613062506002905,-0.09930467851864311,-0.017703321918504433,-0.003430447506237564 +6.78,0.08625805906842667,1.1314187210198374,1.1623675262006237,-0.012724084565922838,-0.047791994801959406,0.7101817038024825,0.7022791257676764,-2.4389929502916203,0.29876769729822394,-1.8294036587813893,0.26067887705165294,1.3500589119589108,-2.896571592111609,0.0,0.0,17.904380869596476,-0.09669479115107715,-0.02223201377271783,0.001072715385126313 +6.8,0.08535099440303562,1.0846159890216225,1.1222009091128002,-0.01935027695089022,-0.03764156447803508,0.6894079095455665,0.7231358196504871,-2.416488006186681,0.1647607387947594,-1.9098179652363942,0.18280833771734037,1.3097558900868944,-2.8960102541531807,0.0,0.0,18.20668096590283,-0.09332037525935866,-0.026493802545656808,0.005514758587804365 +6.82,0.08419093518461665,1.037644314416583,1.0817665656199762,-0.026207371978042286,-0.028277343612172587,0.6679503599289105,0.7432064868951849,-2.381409857807629,0.0334890210294945,-1.9840602854743117,0.1078013440630497,1.2671747655831278,-2.891909763479114,0.0,0.0,18.519184524309047,-0.08930628250128059,-0.030409367672337484,0.009904826321709654 +6.84,0.08279318436963826,0.9905985357263255,1.0411298891460643,-0.033273884546902194,-0.01969087905731116,0.6458796094515841,0.7624596041652166,-2.334403539000229,-0.09346914384559643,-2.0519241313428913,0.036398506166521086,1.2226439183511442,-2.8843118893611144,0.0,0.0,18.841297007223968,-0.08477573358660895,-0.033912518059224905,0.014251636363110932 +6.86,0.0811725624835392,0.9435731794573713,1.0003558495751914,-0.040524818948546225,-0.01186704871793144,0.6232669437062985,0.7808682533481069,-2.276258538391478,-0.21467766042718453,-2.11325608335866,-0.030807758782772296,1.1765369339309115,-2.873252532275698,0.0,0.0,19.17258892186386,-0.0798486623207929,-0.036952240215689214,0.018563489309469168 +6.88,0.0793433410799877,0.8966625420177264,0.9595089777662181,-0.04793262183088218,-0.004784950404634388,0.6001839901075744,0.7984101364791117,-2.2078867483240447,-0.32885747253168973,-2.167943741465588,-0.09336723711877327,1.1292607017501735,-2.8587617452502165,0.0,0.0,19.512775242171106,-0.07463980340422571,-0.039493844645834164,0.022848288108916155 +6.9,0.07731922679805435,0.8499607685761158,0.9186533823601216,-0.0554680755136796,0.0015812662185624203,0.5767024020404828,0.8150675626453872,-2.130299354191743,-0.43489640458227563,-2.215905394449681,-0.15096384703841106,1.0812418648847186,-2.8408637532687413,0.0,0.0,19.861693520488526,-0.06925663914224456,-0.041519259308549494,0.027113565626148944 +6.92,0.07511338931766182,0.8035619291885656,0.8778527927143911,-0.06310110650359319,0.00726161866895084,0.5528936024100916,0.8308274090726188,-2.0445834513973966,-0.5318544195326239,-2.2570813877386815,-0.2034053730152132,1.0329125450500334,-2.8195769777892337,0.0,0.0,20.21928107037264,-0.06379730910854908,-0.043026549486603116,0.0313665185380327 +6.94,0.07273852650641552,0.7575600923499782,0.8371706217632977,-0.07080148899983811,0.012289558083867487,0.5288285750722024,0.8456810593271041,-1.9518790862822581,-0.6189647844948932,-2.2914270928547893,-0.25061098672527987,0.9846961778650611,-2.7949140721827486,0.0,0.0,20.58555151770903,-0.05834856597007121,-0.04402876629042651,0.03561404627189422 +6.96,0.07020696020686132,0.7120493958059881,0.7966700427139974,-0.07853942706264966,0.01670128179786731,0.5045776948633608,0.8596243222648811,-1.8533573056959678,-0.695631701492935,-2.318907334364538,-0.2925966021242322,0.9369941798670995,-2.7668819717119586,0.0,0.0,20.96057103434919,-0.052983833370966016,-0.044552241633037615,0.039862793944137206 +6.98,0.06753075639525774,0.6671241140119876,0.7564140737323485,-0.08628600179297588,0.020535092895574054,0.48021058885379153,0.8726573360758778,-1.7501996762773524,-0.7614250015929007,-2.3394921053680027,-0.32945814778724675,0.8901740386033208,-2.735481959048447,0.0,0.0,21.34443467063296,-0.04776139030958748,-0.044634457313956655,0.04411919833735604 +7.0,0.06472186382471481,0.6228787210707816,0.716465665159449,-0.0940134712051027,0.023830809313793704,0.45579602299287497,0.8847844625575743,-1.643579607095941,-0.8160725184160638,-2.3531533943070686,-0.3613528390855522,0.8445592765091747,-2.700709743776182,0.0,0.0,21.737243369932827,-0.042722672006471174,-0.0443216224400812,0.04838953592757202 +7.0200000000000005,0.061792265731676566,0.5794079473259311,0.6768877843631115,-0.10169541031267951,0.026629226270300645,0.4314018095108797,0.8960141776670275,-1.5346456851770303,-0.8594507545108798,-2.359862948914612,-0.38847849302613413,0.8004215908951288,-2.662555552224922,0.0,0.0,22.139082453167195,-0.03789063959822392,-0.04366610031364748,0.052679971908064185 +7.04,0.05875413973000006,0.5368068270296222,0.6377434941199756,-0.10930667709642096,0.028971636259212348,0.40709473132443186,0.9063589655111771,-1.4245071187154768,-0.8915744344494123,-2.359590813418539,-0.41105084566098027,0.7579753143604469,-2.6210042225688897,0.0,0.0,22.550002569272635,-0.03326812816742164,-0.04272383697870408,0.05699660910553876 +7.0600000000000005,0.055620021643760106,0.4951707336199877,0.5990960215104708,-0.11682318619581455,0.030899410286494823,0.38294048034188855,0.915835224331609,-1.314221282702535,-0.912585505356861,-2.3523044899818277,-0.4292786989560142,0.7173741655899749,-2.5760352995320885,0.0,0.0,22.970004274244125,-0.028836031044777055,-0.04155196196421507,0.061345535666913774 +7.08,0.052402969743688646,0.4545953980868925,0.5610088157663261,-0.12422146595040173,0.03245364202734964,0.3590036070881786,0.9244631948401031,-1.2047832807436176,-0.9227421027358493,-2.3379685936331702,-0.4433365288393074,0.6787100590411651,-2.5276231232888975,0.0,0.0,23.399027471606487,-0.024551113492623733,-0.04020675858460909,0.06573287038482856 +7.1000000000000005,0.04911672665258993,0.4151769045943282,0.5235455954188738,-0.1314779652114566,0.033674852649555484,0.3353474796096905,0.9322669236234405,-1.0971173788887612,-0.9224079510651599,-2.3165448920695613,-0.4533339103789979,0.6420135012815221,-2.475736908222483,0.0,0.0,23.836946855597063,-0.02034316696813243,-0.03874223439858393,0.0701648044566145 +7.12,0.045775877053202574,0.3770116558236555,0.48677038753674573,-0.1385680633474109,0.03460274753101152,0.31203425034658633,0.9392742774579196,-0.9920701354333523,-0.9120426191132504,-2.28799264989466,-0.45928073566493255,0.6072548122658596,-2.4203408090995904,0.0,0.0,24.28357416850919,-0.016111116145406168,-0.037209556027590907,0.07464763823964145 +7.140000000000001,0.04239600021132925,0.34019629818511754,0.4507475648938202,-0.14546471908497072,0.03527600630721522,0.28912483074744744,0.9455170284695055,-0.8904050467933093,-0.8921930034934097,-2.2522692352386615,-0.46104669041761487,0.5743450704387203,-2.3613939749711714,0.0,0.0,24.738667425898736,-0.011717583387809372,-0.03565763636810848,0.07918781111002376 +7.16,0.03899381709902338,0.30482759377695534,0.41554189065264413,-0.15213667020440313,0.035732073932638865,0.2666788749987513,0.9510310352985277,-0.792798553680468,-0.8634863740672162,-2.209331001575699,-0.4583128031319693,0.5431353040761085,-2.2988505947500295,0.0,0.0,25.201946171117218,-0.006981323905868919,-0.03413516295135188,0.08379192192022486 +7.18,0.03558733235663311,0.2710022212466584,0.38121858466538244,-0.15854606581366523,0.03600690218115102,0.24475477642617752,0.9558565516995203,-0.699837300879707,-0.8266252821782569,-2.1591345374522124,-0.4505121048801155,0.5134120699308378,-2.2326599428391347,0.0,0.0,25.67311016749108,-0.0016669197635188482,-0.032694318635738906,0.08846673714466219 +7.2,0.032195971131966594,0.23881648079404708,0.3478434308572096,-0.16464537595595827,0.03613456799221372,0.22340968284175855,0.9600387006703855,-0.6120166108953359,-0.782384602831966,-2.1016384922813636,-0.43675562630599746,0.48488722962926456,-2.1627664378063653,0.0,0.0,26.151856577750202,0.004528748149920325,-0.03139635371432847,0.09321918443465882 +7.22,0.02884070948004772,0.2083658684203789,0.3154829514046568,-0.1703733815331016,0.03614666756447568,0.20269954011014654,0.963628157396874,-0.5297402047312813,-0.7316109409402016,-2.0368063486566146,-0.41573932152965476,0.45717953730403793,-2.0891097303238557,0.0,0.0,26.63788746169556,0.011993297220316057,-0.03031900251330788,0.09805633240411316 +7.24,0.02554419379617087,0.17974446977836014,0.2842046804806223,-0.17565000646801104,0.03607135428379985,0.18267917597949085,0.9666820838783423,-0.45332126535104356,-0.675224551429793,-1.9646107299139104,-0.38562746079649457,0.4297857061830629,-2.011624835032235,0.0,0.0,27.130895192777196,0.021217991269687708,-0.029565467402974388,0.10298536497335459 +7.26,0.022330838748098863,0.15304410282805875,0.2540775778452098,-0.18036972563772363,0.035931857221371176,0.1634024379462852,0.9692653439503579,-0.38298497010736204,-0.614223756049982,-1.885040106793974,-0.3439093072857197,0.40203909743266,-1.9302423086236384,0.0,0.0,27.630508004716297,0.03281648260268133,-0.02927426209801176,0.1080135724607052 +7.28,0.019226883171777016,0.12835310883659223,0.22517262949129924,-0.18439328909388578,0.03574429069059168,0.1449223984372455,0.9714519849438579,-0.31887259451453775,-0.5496915171453229,-1.7981090810013638,-0.28722970135871745,0.3730553325249174,-1.8448884438910933,0.0,0.0,28.136171151773418,0.04753717557483195,-0.029628509430914884,0.11314840269919069 +7.3,0.01626036797773351,0.10575465090243959,0.19756368650003,-0.1875375854706562,0.035514554920435815,0.12729163643198624,0.9733268771173981,-0.26104718845802166,-0.4828032635325443,-1.7038737229957293,-0.21120243540709266,0.3416663878654833,-1.7554853840004447,0.0,0.0,28.646930778056067,0.06625793255504964,-0.030862160085625145,0.11839764184669722 +7.32,0.013460977820069645,0.08532432660703418,0.17132858915990945,-0.18956369247582122,0.035234152447580096,0.11056259529149995,0.9749872171052505,-0.20950062909665837,-0.41483416655615696,-1.6024536168080705,-0.11023093820677701,0.30634873968258647,-1.6619509479451808,0.0,0.0,29.161076831940896,0.08994346164126732,-0.0332588366471579,0.12376981927950079 +7.34,0.010859658103909348,0.06712683721683495,0.14655060143898196,-0.1901636392525086,0.034874836261022175,0.09478800115355748,0.9765432765317275,-0.16416155580919428,-0.3471627786907573,-1.4940621294938308,0.02261189445052203,0.26515781168503894,-1.5641977839514087,0.0,0.0,29.675586677724134,0.11953299974155462,-0.037136453213837184,0.1292749377754447 +7.36,0.008487881392965862,0.05121138586477306,0.1233201234554594,-0.18894730774828086,0.03438221304836533,0.080021306321592,0.9781172572116081,-0.12490331100671878,-0.28126631069367614,-1.3790457103529332,0.195547797769294,0.21569144903158932,-1.4621312311664987,0.0,0.0,30.185289277874173,0.1557074235525986,-0.04280700489863343,0.13492557677250863 +7.38,0.006376396374868791,0.037605417995795046,0.10173652759343209,-0.1854324975461197,0.033668822429188755,0.06631710311064945,0.9798383749808187,-0.0915506168563724,-0.2187010593271698,-1.257931438167146,0.41773332414236103,0.15512097300163355,-1.3556450119862669,0.0,0.0,30.681635294214942,0.19845999492870314,-0.05049175101837455,0.14073817041563977 +7.4,0.004553257659095049,0.026306306981120964,0.08190973433459264,-0.1790438717929125,0.03260795850621367,0.05373143607058676,0.9818314243256082,-0.0638834524008726,-0.16106011737281442,-1.1314794091171179,0.6983025747396674,0.08035650759473473,-1.2446137757406075,0.0,0.0,31.15090624772478,0.2463664069450049,-0.060135643331768907,0.14673352888917127 +7.42,0.0030409219366444172,0.017270703327813733,0.06396075387811843,-0.16913082256368803,0.031031119756319173,0.04232186661369148,0.9841954552180843,-0.04163655965025201,-0.10990037742796584,-1.0007331855505253,1.044393715163259,-0.01151094745493321,-1.1288820634971297,0.0,0.0,31.57165681478071,0.29542256648397247,-0.07097111808088441,0.15293467091015833 +7.44,0.0018522436913296879,0.010401651061489606,0.04801983372726073,-0.15502082436941558,0.02873574843663843,0.03214686003404414,0.9869697969864957,-0.024492693591281423,-0.06663321359475635,-0.8670584004841498,1.4575586906199884,-0.12215034310361189,-1.0082509876715249,0.0,0.0,31.911378125076965,0.3372628317864961,-0.0806944993663734,0.15936130702211337 +7.46,0.0009854542324235398,0.005534466020767472,0.034220151114019506,-0.13613441146598584,0.025515702226768022,0.02326383873687466,0.9900884630990856,-0.012065753229312665,-0.03237929532715097,-0.7321574096717962,1.9276987298961525,-0.2497980870979821,-0.8824658299355848,0.0,0.0,32.12348338909857,0.35643157558606364,-0.08458326810668138,0.1660239805821349 +7.48,0.00041891967950733226,0.0024240926235593934,0.022684622590646755,-0.11220214904872222,0.021228444203215242,0.015726362596093946,0.9933341393635152,-0.003869497315732549,-0.0077970861706913065,-0.5980432408688401,2.4232150523822904,-0.3858650316072227,-0.7512094371042786,0.0,0.0,32.14875386121323,0.32634356109616824,-0.07512063240450484,0.17291961038361758 +7.5,0.000107599543358684,0.0007394937803025972,0.013504567661559979,-0.08364266861489082,0.015906427905240394,0.009583067165034334,0.9963227661566232,0.0007197147052641299,0.007097434508565469,-0.46693858693088486,2.8755422492985363,-0.5110588809514813,-0.6141125252964661,0.0,0.0,31.92977526965268,0.2038459682312732,-0.04214149517287891,0.1800308823937404 +7.5200000000000005,-1.5865370229633407e-05,7.418337530198168e-05,0.006713580620871101,-0.05217776311495121,0.009911221052816539,0.004881165354838409,0.9985766985857125,0.0024796531555402106,0.013097491291318689,-0.341029033641626,3.156060457945725,-0.5918302643488129,-0.4707998669600896,0.0,0.0,31.448645474327186,-0.07776553719173873,0.026240217259710485,0.18734332058430725 +7.54,-3.231287106677665e-05,-1.583458595723645e-05,0.002269336442746955,-0.02177314993565547,0.00411190483191998,0.0016742417767014477,0.9997530790625283,0.0023154611040718635,0.011932777921510854,-0.2219622720900245,3.043111480054994,-0.5784736237906646,-0.3209868579517091,0.0,0.0,30.79332731819349,-0.6192137519227473,0.13896838837481607,0.1949308349353997 +7.5600000000000005,-1.7778054681255422e-05,6.276378069349673e-05,6.81847947490953e-05,-2.9101743477082726e-05,5.248989948503081e-06,2.8442939555858607e-05,0.9999999991582679,0.0011878576877215187,0.006327468028326785,-0.10995079009844967,2.1745731959410657,-0.4107564829246999,-0.16459463253847403,0.0,0.0,30.258807463117797,-1.5596038365875737,0.3263175708326902,0.20383953892364842 +7.58,-1.8464380976886724e-05,6.155953276224476e-05,3.178314290691738e-05,-2.145215392413631e-05,-3.2064688274261667e-06,1.285953355003455e-05,0.999999999682078,-3.431508225130802e-05,-6.011896685547205e-05,-0.0018200857038479373,0.0007649430861267348,-0.0008455222844616084,-0.0015583611924547824,0.0,0.0,19.533005621610283,-0.000548900290262555,0.000649674610254003,0.001990017664952188 diff --git a/test/gtest_factor_force_torque.cpp b/test/gtest_factor_force_torque.cpp deleted file mode 100644 index 16bd8d88a91abebcf2c00cdabb7bd163f44e2046..0000000000000000000000000000000000000000 --- a/test/gtest_factor_force_torque.cpp +++ /dev/null @@ -1,856 +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> - -// 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<StateBlock>(c); KF->addStateBlock('C', sbc, problem); - StateBlockPtr sbd = make_shared<StateBlock>(cd); KF->addStateBlock('D', sbd, problem); - StateBlockPtr sbL = make_shared<StateBlock>(Lc); KF->addStateBlock('L', sbL, problem); - StateBlockPtr sbbimu = make_shared<StateBlock>(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<StateBlock>(zero3); KFA_->addStateBlock('C', sbcA, problem_); - StateBlockPtr sbdA = make_shared<StateBlock>(zero3); KFA_->addStateBlock('D', sbdA, problem_); - StateBlockPtr sbLA = make_shared<StateBlock>(zero3); KFA_->addStateBlock('L', sbLA, problem_); - StateBlockPtr sbbimuA = make_shared<StateBlock>(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_factor_inertial_kinematics.cpp b/test/gtest_factor_inertial_kinematics.cpp index 45034d63e1e90ab4c86172a8ba81a88317cd6bb4..0f63fd9eeefb78accd65fb03b42eeffc64d21230 100644 --- a/test/gtest_factor_inertial_kinematics.cpp +++ b/test/gtest_factor_inertial_kinematics.cpp @@ -58,6 +58,7 @@ FactorInertialKinematics_1KF_1v_bfix,ZeroMvt: #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" @@ -72,6 +73,7 @@ FactorInertialKinematics_1KF_1v_bfix,ZeroMvt: #include "bodydynamics/capture/capture_inertial_kinematics.h" #include "bodydynamics/feature/feature_inertial_kinematics.h" #include "bodydynamics/factor/factor_inertial_kinematics.h" +#include <core/state_block/state_block_derived.h> #include <Eigen/Eigenvalues> @@ -126,11 +128,11 @@ class FactorInertialKinematics_1KF : public testing::Test // ADD THE STATEBLOCKS AND SET THEIR DEFAULT VALUES bias_p_ = ZERO3; bias_imu_ = ZERO6; - StateBlockPtr sbc = make_shared<StateBlock>(ZERO3); - StateBlockPtr sbd = make_shared<StateBlock>(ZERO3); - StateBlockPtr sbL = make_shared<StateBlock>(ZERO3); - StateBlockPtr sbb = make_shared<StateBlock>(bias_p_); - sbbimu_ = make_shared<StateBlock>(bias_imu_); + StateBlockPtr sbc = make_shared<StatePoint3d>(ZERO3); + StateBlockPtr sbL = make_shared<StateVector3d>(ZERO3); + StateBlockPtr sbd = make_shared<StateVector3d>(ZERO3); + StateBlockPtr sbb = make_shared<StateParams3>(bias_p_); + sbbimu_ = make_shared<StateParams6>(bias_imu_); KF0_->addStateBlock('C', sbc, problem_); KF0_->addStateBlock('D', sbd, problem_); @@ -328,7 +330,7 @@ class FactorInertialKinematics_1KF_1v_bfix : public FactorInertialKinematics_1KF // Vector6d ZERO6; ZERO6 << ZERO3, ZERO3; // Vector3d bias_p_ = ZERO3; // Vector6d bias_imu_ = ZERO6; -// StateBlockPtr sbc = make_shared<StateBlock>(ZERO3); +// StateBlockPtr sbc = make_shared<StatePoint3d>(ZERO3); // StateBlockPtr sbd = make_shared<StateBlock>(ZERO3); // StateBlockPtr sbL = make_shared<StateBlock>(ZERO3); // StateBlockPtr sbb = make_shared<StateBlock>(bias_p_); 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..6e3ee1a73fdbcc8c4f8d39b79175ffd677b3ab31 --- /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().front()->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().front()->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_simulation_problem_force_torque_inertial_dynamics.cpp b/test/gtest_simulation_problem_force_torque_inertial_dynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64ab43627674118f196b43bccc4b05c15fed4ba8 --- /dev/null +++ b/test/gtest_simulation_problem_force_torque_inertial_dynamics.cpp @@ -0,0 +1,560 @@ +//--------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/factor/factor_angular_momentum.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 <core/math/rotations.h> +#include <core/tree_manager/tree_manager_sliding_window.h> +#include <core/capture/capture_pose.h> + + +#include <Eigen/Dense> +#include <Eigen/Geometry> + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> + +using namespace wolf; +using namespace bodydynamics::fti; + +WOLF_REGISTER_STATEBLOCK_WITH_KEY(L, StateVector3d); + +class Test_SimulationProblemForceTorqueInertialDynamics_yaml : public testing::Test +{ + public: + ProblemPtr P; + SolverCeresPtr solver; + + // FTI sensor + SensorForceTorqueInertialPtr S; + ProcessorForceTorqueInertialDynamicsPtr p; + Vector6d bias_true; + Vector3d cdm_true; + Vector3d inertia_true; + double mass_true; + Matrix<double, 12, 1> data_fti; + + // Pose sensor + SensorBasePtr SP; + Vector7d data_pose; + + double dt; + + // Reading csv data_fti + std::vector<double> time_stamp; + std::vector<Vector3d> position, vlin, vang, force, torque, a_meas; + std::vector<Quaterniond> quaternion; + + protected: + void extractAndCompleteData(const std::string& file_path_name) + { + Vector3d position_i, vlin_i, vang_i, force_i, torque_i, a_meas_i; + std::ifstream data_simulation; + string data_file = file_path_name; + data_simulation.open(data_file, std::ios::in); + string line_data; + char delimiter = ','; + std::getline(data_simulation, line_data); + + int counter = 0; + + while (std::getline(data_simulation, line_data)) + { + std::stringstream data_str(line_data); + + // time stamp + string string_time_stamp; + std::getline(data_str, string_time_stamp, delimiter); + time_stamp.push_back(std::stod(string_time_stamp)); + + // position + string string_pos_x; + string string_pos_y; + string string_pos_z; + + std::getline(data_str, string_pos_x, delimiter); + std::getline(data_str, string_pos_y, delimiter); + std::getline(data_str, string_pos_z, delimiter); + + position_i(0) = std::stod(string_pos_x); + position_i(1) = std::stod(string_pos_y); + position_i(2) = std::stod(string_pos_z); + + position.push_back(position_i); + + // quaternion + string string_quat_x; + string string_quat_y; + string string_quat_z; + string string_quat_w; + + std::getline(data_str, string_quat_x, delimiter); + std::getline(data_str, string_quat_y, delimiter); + std::getline(data_str, string_quat_z, delimiter); + std::getline(data_str, string_quat_w, delimiter); + + Quaterniond quaternion_i(std::stod(string_quat_w), + std::stod(string_quat_x), + std::stod(string_quat_y), + std::stod(string_quat_z)); + + quaternion.push_back(quaternion_i); + + // linear velocity + string string_vlin_x; + string string_vlin_y; + string string_vlin_z; + + std::getline(data_str, string_vlin_x, delimiter); + std::getline(data_str, string_vlin_y, delimiter); + std::getline(data_str, string_vlin_z, delimiter); + + vlin_i(0) = std::stod(string_vlin_x); + vlin_i(1) = std::stod(string_vlin_y); + vlin_i(2) = std::stod(string_vlin_z); + + vlin.push_back(vlin_i); + + // angular velocity + string string_vang_x; + string string_vang_y; + string string_vang_z; + + std::getline(data_str, string_vang_x, delimiter); + std::getline(data_str, string_vang_y, delimiter); + std::getline(data_str, string_vang_z, delimiter); + + vang_i(0) = std::stod(string_vang_x); + vang_i(1) = std::stod(string_vang_y); + vang_i(2) = std::stod(string_vang_z); + + vang.push_back(vang_i); + + // force + string string_force_x; + string string_force_y; + string string_force_z; + + std::getline(data_str, string_force_x, delimiter); + std::getline(data_str, string_force_y, delimiter); + std::getline(data_str, string_force_z, delimiter); + + force_i(0) = std::stod(string_force_x); + force_i(1) = std::stod(string_force_y); + force_i(2) = std::stod(string_force_z); + + force.push_back(force_i); + + // torque + string string_torque_x; + string string_torque_y; + string string_torque_z; + + std::getline(data_str, string_torque_x, delimiter); + std::getline(data_str, string_torque_y, delimiter); + std::getline(data_str, string_torque_z, delimiter); + + torque_i(0) = std::stod(string_torque_x); + torque_i(1) = std::stod(string_torque_y); + torque_i(2) = std::stod(string_torque_z); + + torque.push_back(torque_i); + + // acceleration -- need to compute from other data + a_meas_i = force_i/mass_true; + + a_meas.push_back(a_meas_i); + + counter++; + } + } + + void SetUp() override + { + bias_true = Vector6d::Zero(); + cdm_true = {0, 0, 0}; + inertia_true = {0.0134943, 0.0141622, 0.0237319}; + mass_true = 1.9828; + + std::string wolf_bodydynamics_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + + extractAndCompleteData(wolf_bodydynamics_root + "/test/dades_simulacio_pep.csv"); + + + ParserYaml parser = ParserYaml("problem_force_torque_inertial_dynamics_simulation_test.yaml", + wolf_bodydynamics_root + "/test/yaml/"); + ParamsServer server = ParamsServer(parser.getParams()); + P = Problem::autoSetup(server); + + solver = std::make_shared<SolverCeres>(P); + auto options = solver->getSolverOptions(); + // solver->getSolverOptions().max_num_iterations = 1e4; + // solver->getSolverOptions().function_tolerance = 1e-15; + // solver->getSolverOptions().gradient_tolerance = 1e-15; + // solver->getSolverOptions().parameter_tolerance = 1e-15; + + // FTI sensor and processor + S = std::static_pointer_cast<SensorForceTorqueInertial>(P->findSensor("sensor FTI")); + p = std::static_pointer_cast<ProcessorForceTorqueInertialDynamics>(S->getProcessorList().front()); + + // Pose sensor + SP = P->findSensor("sensor Pose"); + } +}; + +TEST_F(Test_SimulationProblemForceTorqueInertialDynamics_yaml, force_registration) +{ + FactorySensor::registerCreator("SensorForceTorqueInertial", SensorForceTorqueInertial::create); +} + +//this test checks the pre-integration evolution along the time +TEST_F(Test_SimulationProblemForceTorqueInertialDynamics_yaml, preintegration_and_csv) +{ + std::string wolf_bodydynamics_root = _WOLF_BODYDYNAMICS_ROOT_DIR; + // creem un nou arxiu CSV per imprimir els estats que estem rebent ara + std::fstream fout; + fout.open(wolf_bodydynamics_root + "/test/dades_simulacio_pep_estimador.csv", + std::fstream::out | std::fstream::trunc); + + S->getStateBlock('I')->setState(bias_true); + S->getStateBlock('C')->setState(cdm_true); + S->getStateBlock('i')->setState(inertia_true); + S->getStateBlock('m')->setState(Vector1d(mass_true)); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->fix(); + S->getStateBlock('C')->fix(); + S->getStateBlock('i')->fix(); + S->getStateBlock('m')->fix(); + S->setStateBlockDynamic('I', false); + + //Writing the first lines to know how the data will be distributed in the csv + fout << "time stamp" + << "," + << "position_x_true" + << "," + << "position_y_true" + << "," + << "position_z_true" + << "," + << "quaternion_x_true" + << "," + << "quaternion_y_true" + << "," + << "quaternion_z_true" + << "," + << "quaternion_w_true" + << "," + << "position_x_est" + << "," + << "position_y_est" + << "," + << "position_z_est" + << "," + << "quaternion_x_est" + << "," + << "quaternion_y_est" + << "," + << "quaternion_z_est" + << "," + << "quaternion_w_est" + << "\n"; + + // Force processor to make a KF at t=0 + CaptureMotionPtr C0 = + std::make_shared<CaptureForceTorqueInertial>(0, S, VectorXd::Zero(12), MatrixXd::Identity(12, 12), nullptr); + C0->process(); + + int i_init = 0; + + // fix measured position and orientation + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('P')->setState(position[i_init]); + p->getOrigin()->getFrame()->getStateBlock('O')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->setState(quaternion[i_init].coeffs()); + p->getOrigin()->getFrame()->getStateBlock('V')->fix(); + p->getOrigin()->getFrame()->getStateBlock('V')->setState(quaternion[i_init].conjugate() * vlin[i_init]); + Vector3d ang_mom_init( + vang[i_init](0) * inertia_true(0), vang[i_init](1) * inertia_true(1), vang[i_init](2) * inertia_true(2)); + p->getOrigin()->getFrame()->getStateBlock('L')->fix(); + p->getOrigin()->getFrame()->getStateBlock('L')->setState(ang_mom_init); + + + for (int i = i_init + 1; i < time_stamp.size(); i++) // start with second data_fti + { + // SIMULATOR + + TimeStamp t = time_stamp[i]; + + // Data + data_fti << a_meas[i], vang[i], force[i], torque[i]; + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data_fti, S->getNoiseCov(), nullptr); + C->process(); + + // results printed in the csv in the order established before + Vector3d position_est(p->getLast()->getFrame()->getStateBlock('P')->getState()); + Vector4d quaternion_coeffs_est(p->getLast()->getFrame()->getStateBlock('O')->getState()); + + fout << time_stamp[i] << "," << position[i](0) << "," << position[i](1) << "," << position[i](2) << "," + << quaternion[i].coeffs()(0) << "," << quaternion[i].coeffs()(1) << "," << quaternion[i].coeffs()(2) + << "," << quaternion[i].coeffs()(3) << "," << position_est(0) << "," << position_est(1) << "," + << position_est(2) << "," << quaternion_coeffs_est(0) << "," << quaternion_coeffs_est(1) << "," + << quaternion_coeffs_est(2) << "," << quaternion_coeffs_est(3) + << "\n"; + + } + fout.close(); +} + +TEST_F(Test_SimulationProblemForceTorqueInertialDynamics_yaml, simulation_online) +{ + // Calibration params + Vector6d bias_guess = S->getStateBlock('I')->getState(); + Vector3d cdm_guess = S->getStateBlock('C')->getState(); + Vector3d inertia_guess = S->getStateBlock('i')->getState(); + double mass_guess = S->getStateBlock('m')->getState()(0); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->unfix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->unfix(); + S->setStateBlockDynamic('I', false); + + + // add regularization, uncomment if the parameter is not fixed + + S->addPriorParameter('I', // cdm + bias_guess, // cdm + 0.1*0.1*Matrix6d::Identity()); // cov + + S->addPriorParameter('C', // cdm + cdm_guess, // cdm + 0.1*0.1*Matrix3d::Identity()); // cov + + S->addPriorParameter('i', // inertia + inertia_guess, // inertia + 0.01*0.01*Matrix3d::Identity()); // cov + + S->addPriorParameter('m', // mass + Vector1d(mass_guess), // mass + 0.1 * 0.1 * Matrix1d::Identity()); // cov + + std::string report; + + // Force processor to make a KF at t=0 + CaptureMotionPtr C0 = + std::make_shared<CaptureForceTorqueInertial>(0, S, VectorXd::Zero(12), MatrixXd::Identity(12, 12), nullptr); + C0->process(); + + int i_init = 0; + + // Pose + data_pose << position[i_init] , quaternion[i_init].coeffs(); + CaptureBasePtr CP = std::make_shared<CapturePose>(time_stamp[i_init], SP, data_pose, SP->getNoiseCov()); + CP->process(); + + unsigned int last_kf_id = p->getOrigin()->getFrame()->id(); + + for (int i = i_init + 1; i < time_stamp.size(); i++) // start with second data + { + // SIMULATOR + + TimeStamp t = time_stamp[i]; + + // Data + data_fti << a_meas[i], vang[i], force[i], torque[i]; + data_pose << position[i] , quaternion[i].coeffs(); + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data_fti, S->getNoiseCov(), nullptr); + C->process(); + + CaptureBasePtr CP = std::make_shared<CapturePose>(t, SP, data_pose, SP->getNoiseCov()); + CP->process(); + + // check if new KF and solve + if (last_kf_id != p->getOrigin()->getFrame()->id()) + { + last_kf_id = p->getOrigin()->getFrame()->id(); + + // solve! + report = solver->solve(wolf::SolverManager::ReportVerbosity::BRIEF); + + // results of this iteration + WOLF_INFO("Time : ", t, " s."); + WOLF_INFO(report); + WOLF_INFO("Estimated bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 - rad/s."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + } + } + + // FINAL RESULTS + WOLF_INFO("True bias * : ", bias_true.transpose(), " m/s2 - rad/s."); + WOLF_INFO("Guess bias : ", bias_guess.transpose(), " m/s2 - rad/s."); + WOLF_INFO("Estimated bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 - rad/s."); + WOLF_INFO("True center of mass * : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess center of mass : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("True inertia * : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("True mass * : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + ASSERT_MATRIX_APPROX(bias_true , S->getStateBlock('I')->getState(), 0.11); + ASSERT_MATRIX_APPROX(cdm_true , S->getStateBlock('C')->getState(), 1e-2); + ASSERT_MATRIX_APPROX(inertia_true, S->getStateBlock('i')->getState(), 1e-2); + ASSERT_NEAR (mass_true , S->getStateBlock('m')->getState()(0), 2e-2); +} + +TEST_F(Test_SimulationProblemForceTorqueInertialDynamics_yaml, simulation_batch) +{ + // Calibration params + Vector6d bias_guess = S->getStateBlock('I')->getState(); + Vector3d cdm_guess = S->getStateBlock('C')->getState(); + Vector3d inertia_guess = S->getStateBlock('i')->getState(); + double mass_guess = S->getStateBlock('m')->getState()(0); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->unfix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->unfix(); + S->setStateBlockDynamic('I', false); + + + // // add regularization, uncomment if the parameter is not fixed + + // S->addPriorParameter('I', // bias + // bias_guess, // bias + // 0.1 * 0.1 * Matrix3d::Identity()); // cov + + // S->addPriorParameter('C', // cdm + // cdm_guess, // cdm + // 0.01 * 0.01 * Matrix3d::Identity()); // cov + + // S->addPriorParameter('i', // inertia + // inertia_guess, // inertia + // 0.01 * 0.01 * Matrix3d::Identity()); // cov + + // S->addPriorParameter('m', // mass + // Vector1d(mass_guess), // mass + // 0.01 * 0.01 * Matrix1d::Identity()); // cov + + int i_init = 0; + + // Force FTI processor to make a KF at t=0 + CaptureMotionPtr C0 = + std::make_shared<CaptureForceTorqueInertial>(0, S, VectorXd::Zero(12), S->getNoiseCov(), nullptr); + C0->process(); + + // Pose + data_pose << position[i_init] , quaternion[i_init].coeffs(); + CaptureBasePtr CP = std::make_shared<CapturePose>(time_stamp[i_init], SP, data_pose, SP->getNoiseCov()); + CP->process(); + + for (int i = i_init + 1; i < time_stamp.size(); i++) // start with second data + { + // SIMULATOR + TimeStamp t = time_stamp[i]; + + // Data + data_fti << a_meas[i], vang[i], force[i], torque[i]; + data_pose << position[i] , quaternion[i].coeffs(); + + // ESTIMATOR + + // fti + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data_fti, S->getNoiseCov(), nullptr); + C->process(); + + // Pose + CaptureBasePtr CP = std::make_shared<CapturePose>(t, SP, data_pose, SP->getNoiseCov()); + CP->process(); + } + + std::string report = solver->solve(wolf::SolverManager::ReportVerbosity::BRIEF); + + + // FINAL RESULTS + WOLF_INFO("-----------------------------"); + WOLF_INFO(report); + WOLF_INFO("True bias * : ", bias_true.transpose(), " m/s2 - rad/s."); + WOLF_INFO("Guess bias : ", bias_guess.transpose(), " m/s2 - rad/s."); + WOLF_INFO("Estimated bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 - rad/s."); + WOLF_INFO("True center of mass * : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess center of mass : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("True inertia * : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("True mass * : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + ASSERT_MATRIX_APPROX(bias_true , S->getStateBlock('I')->getState(), 0.21); + ASSERT_MATRIX_APPROX(cdm_true , S->getStateBlock('C')->getState(), 2e-3); + ASSERT_MATRIX_APPROX(inertia_true, S->getStateBlock('i')->getState(), 1e-2); + ASSERT_NEAR (mass_true , S->getStateBlock('m')->getState()(0), 4e-2); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + // ::testing::GTEST_FLAG(filter) = + // "Test_SimulationProblemForceTorqueInertialDynamics_yaml.preintegration_and_csv"; + ::testing::GTEST_FLAG(filter) = + "Test_SimulationProblemForceTorqueInertialDynamics_yaml.simulation*"; + + return RUN_ALL_TESTS(); +} 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..43629f400a4f763490ea6bd6210deaae1104ae80 --- /dev/null +++ b/test/gtest_solve_problem_force_torque_inertial_dynamics.cpp @@ -0,0 +1,1166 @@ +//--------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/factor/factor_angular_momentum.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 <core/math/rotations.h> +#include <core/tree_manager/tree_manager_sliding_window.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; + Vector6d bias_true; + 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()); + + bias_true = Vector6d::Zero(); + cdm_true = {0, 0, 0.0341}; + inertia_true = {0.0176, 0.0180, 0.0296}; // rounded {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) +{ + Vector3d cdm_guess(0.01, 0.01, 0.033); + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('m')->setState(Vector1d(mass_true)); + + // 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("-----------------------------"); + + double dt = 0.2; + double time_last = 5.0; + + for (TimeStamp t = p->getLast()->getTimeStamp() + dt; t <= time_last; t += dt) + { + 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) +{ + Vector3d cdm_guess(0.005, 0.005, 0.05); + double mass_guess = 1.9; + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + // 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. + + double dt = 0.2; + double time_last = 5.0; + + for (TimeStamp t = p->getLast()->getTimeStamp() + dt; t <= time_last; t += dt) + { + 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 +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, cdm_and_ang_mom_hovering) +{ + Vector3d cdm_guess(0.02, 0.02, 0.033); + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('m')->setState(Vector1d(mass_true)); + + // 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_KF0->getFrame()->getStateBlock('L')->unfix(); + C_KF1->getFrame()->fix(); + C_KF1->getFrame()->getStateBlock('L')->unfix(); + + 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("-----------------------------"); + + double dt = 0.2; + double time_last = 10.0; + + for (TimeStamp t = p->getLast()->getTimeStamp() + dt; t <= time_last; t += dt) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->process(); + p->getOrigin()->getFrame()->fix(); + p->getOrigin()->getFrame()->getStateBlock('L')->unfix(); + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO("Estimated cdm : ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("Estimated ang mom : ", + p->getOrigin()->getFrame()->getStateBlock('L')->getState().transpose(), + " m^2 kg/s."); + 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_and_ang_mom_hovering) +{ + Vector3d cdm_guess(0.05, 0.05, 0.05); + double mass_guess = 1.50; + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + // 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', false); + + 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_KF0->getFrame()->getStateBlock('L')->unfix(); + C_KF1->getFrame()->fix(); + C_KF1->getFrame()->getStateBlock('L')->unfix(); + + 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. + + double dt = 0.2; + double time_last = 10.0; + + for (TimeStamp t = p->getLast()->getTimeStamp() + dt; t <= time_last; t += dt) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->setTimeStamp(t); + C->process(); + p->getOrigin()->getFrame()->fix(); + p->getOrigin()->getFrame()->getStateBlock('L')->unfix(); + 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("Estimated ang mom : ", + p->getOrigin()->getFrame()->getStateBlock('L')->getState().transpose(), + " m^2 kg/s."); + + 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 +} + +// Here we only fix P and O from each key frame +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, hovering_test_without_fixing) +{ + Vector3d cdm_guess(0.05, 0.05, 0.05); + double mass_guess = 1.50; + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + // 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', false); + + // add regularization to unobservable states + S->addPriorParameter('C', // cdm + Vector1d(cdm_true(2)), // cdm Z + Matrix1d::Identity() * 1, // cov Z + 2, // start: Z coordinate + 1); // size + S->addPriorParameter('i', // inertia + Vector2d(inertia_true.segment<2>(0)), // inertia XY + Matrix2d::Identity() * 0.01, // cov XY + 0, // start: X coordinate + 2); // size: 2 + + 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()->unfix(); + C_KF0->getFrame()->getStateBlock('P')->fix(); + C_KF0->getFrame()->getStateBlock('O')->fix(); + C_KF1->getFrame()->unfix(); + C_KF1->getFrame()->getStateBlock('P')->fix(); + C_KF1->getFrame()->getStateBlock('O')->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("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. + + double dt = 0.2; + double time_last = 10.0; + + for (TimeStamp t = p->getLast()->getTimeStamp() + dt; t <= time_last; t += dt) + { + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->setTimeStamp(t); + C->process(); // En aquesta lÃnia el programa s'aborta + // p->getOrigin()->getFrame()->unfix(); + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->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("Estimated ang mom : ", + p->getOrigin()->getFrame()->getStateBlock('L')->getState().transpose(), + " m^2 kg/s."); + + 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 +} + +// Here we only fix P and O from each key frame +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, torque_yaw_constant) +{ + Vector3d inertia_guess(0.01, 0.01, 0.02); + S->getStateBlock('i')->setState(inertia_guess); + + // Data + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + Vector3d ang_vel_measure_initial(0, 0, 0); + data.segment<3>(3) = ang_vel_measure_initial; + data.segment<3>(6) = -mass_true * gravity(); + Vector3d torque_measure_constant(0, 0, 1); + data.segment<3>(9) = torque_measure_constant; + MatrixXd data_cov = MatrixXd::Identity(12, 12); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->fix(); + S->getStateBlock('C')->fix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->fix(); + // S->setStateBlockDynamic('I'); + + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + C_KF0->getFrame()->unfix(); + C_KF0->getFrame()->getStateBlock('P')->fix(); + C_KF0->getFrame()->getStateBlock('P')->setState(Vector3d(0, 0, 0)); + C_KF0->getFrame()->getStateBlock('O')->fix(); + C_KF0->getFrame()->getStateBlock('O')->setState((Quaterniond(1, 0, 0, 0)).coeffs()); + + // initialiation of all the variables needed in the following iteration process + Vector3d torque_true(0, 0, 1); + Vector3d dL; + Vector3d L_true(0, 0, 0); + Vector3d ang_vel_true(0, 0, 0); + Vector3d ang_acc_true; + Vector3d angle_true; + Quaterniond quat_true(1, 0, 0, 0); + double dt = 0.2; // time increment + Vector3d position_data(0, 0, 0); + Quaterniond q_data; + std::string report; + + for (TimeStamp t = dt; t <= 5.0; t += dt) + { + // SIMULATOR + + // calculate dL = torque*dt + dL = torque_true * dt; + // actualize L = L + dt + L_true = L_true + dL; // L = L + dt + // w = L*i_inv we actualize the data + ang_vel_true(0) = L_true(0) / inertia_true(0); + ang_vel_true(1) = L_true(1) / inertia_true(1); + ang_vel_true(2) = L_true(2) / inertia_true(2); + data.segment<3>(3) = ang_vel_true; + + ang_acc_true(0) = torque_true(0) / inertia_true(0); + ang_acc_true(1) = torque_true(1) / inertia_true(1); + ang_acc_true(2) = torque_true(2) / inertia_true(2); + + angle_true = ang_vel_true * dt + 0.5 * ang_acc_true * dt * dt; + quat_true = quat_true * exp_q(angle_true); + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->setTimeStamp(t); + C->process(); + + p->getOrigin()->getFrame()->unfix(); + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('P')->setState(position_data); + q_data = quat_true; + p->getOrigin()->getFrame()->getStateBlock('O')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->setState(q_data.coeffs()); + + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + WOLF_INFO("Estimated inertia: ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("-----------------------------"); + } + + WOLF_INFO("True inertia : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia: ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("-----------------------------"); + auto inertia_estimated = S->getStateBlock('i')->getState(); + + ASSERT_NEAR(inertia_estimated(2), inertia_true(2), 1e-6); +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, torque_yaw_constant__unfix_all__zero_bias) +{ + // Simulation data -- hovering and constant torque in yaw + Vector3d acc_true = -gravity(); + Vector3d ang_vel_true(0, 0, 0); + Vector3d force_true = -mass_true * gravity(); + Vector3d torque_true(0, 0, 0.01); + Vector3d L_true(0, 0, 0); + Vector3d position_true(0, 0, 0); + Quaterniond quat_true(1, 0, 0, 0); + Vector3d + ang_acc_true; // = inertia_true.asDiagonal().inverse() * torque_true; // alpha = I.inv * torque is constant + // Vector3d ang_acc_true = (torque_true.array() / inertia_true.array()).matrix(); + ang_acc_true(0) = torque_true(0) / inertia_true(0); + ang_acc_true(1) = torque_true(1) / inertia_true(1); + ang_acc_true(2) = torque_true(2) / inertia_true(2); + Vector3d angle_true(0, 0, 0); + + // Data -- hovering and constant torque in yaw + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity(); + data.segment<3>(3) = ang_vel_true; + data.segment<3>(6) = -mass_true * gravity(); + data.segment<3>(9) = torque_true; + MatrixXd data_cov = S->getNoiseCov(); + + Vector3d position_data = position_true; + Vector4d orient_data = quat_true.coeffs(); + + // Calibration params + Vector6d bias_guess; + bias_guess << 0, 0, 0, 0, 0, 0; + S->getStateBlock('I')->setState(bias_guess); + Vector3d cdm_guess(0.005, 0.005, 0.1); + S->getStateBlock('C')->setState(cdm_guess); + Vector3d inertia_guess(0.01, 0.01, 0.02); + S->getStateBlock('i')->setState(inertia_guess); + double mass_guess = 2.0; + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->unfix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->unfix(); + S->setStateBlockDynamic('I', false); + + // Process origin + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + // UAV states + C_KF0->getFrame()->unfix(); // JS: this is unnecessary, all states are unfixed by default + C_KF0->getFrame()->getStateBlock('P')->fix(); + C_KF0->getFrame()->getStateBlock('P')->setState(position_data); + C_KF0->getFrame()->getStateBlock('O')->fix(); + C_KF0->getFrame()->getStateBlock('O')->setState(orient_data); + + // initialiation of all the variables needed in the following iteration process + // Vector3d dL; + double dt = 0.1; // time increment + unsigned int last_kf_id = p->getOrigin()->getFrame()->id(); + std::string report; + + for (TimeStamp t = dt; t <= 10.0; t += dt) + { + // SIMULATOR + + // angular momentum + // L_true += torque_true * dt; // FIXME JS: it seems this is not needed! + + // ang velocity + ang_vel_true += ang_acc_true * dt; + + // orientation (quaternion and total angle) + // FIXME JS: this should be on top but it works better here, weird... + quat_true *= exp_q(ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/); + angle_true += ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/; + + // // w = L*i_inv we actualize the data + // ang_vel_true(0) = L_true(0) / inertia_true(0); + // ang_vel_true(1) = L_true(1) / inertia_true(1); + // ang_vel_true(2) = L_true(2) / inertia_true(2); + + // FTI measurements + data.segment<3>(0) = acc_true; // TODO: add acc bias + data.segment<3>(3) = ang_vel_true; // TODO: add gyro bias + data.segment<3>(6) = force_true; + data.segment<3>(9) = torque_true; + + // Pose measurements (simulate motion capture) + position_data = position_true; + orient_data = quat_true.coeffs(); + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->process(); + + // check if new KF + if (last_kf_id != p->getOrigin()->getFrame()->id()) + { + last_kf_id = p->getOrigin()->getFrame()->id(); + + // fix measured position and orientation + p->getOrigin()->getFrame()->unfix(); + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('P')->setState(position_data); + p->getOrigin()->getFrame()->getStateBlock('O')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->setState(orient_data); + + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + + WOLF_INFO("Total angle turned : ", angle_true.transpose(), " rad."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + } + } + + WOLF_INFO("True IMU bias : ", bias_true.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Guess IMU bias : ", bias_guess.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("True center of mass : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess center of mass : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("True inertia : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("True mass : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + auto bias_estimated = S->getStateBlock('I')->getState(); + auto cdm_estimated = S->getStateBlock('C')->getState(); + auto inertia_estimated = S->getStateBlock('i')->getState(); + auto mass_estimated = S->getStateBlock('m')->getState()(0); + + ASSERT_MATRIX_APPROX(bias_estimated, bias_true, 1e-4); + ASSERT_MATRIX_APPROX(cdm_estimated.head(2), cdm_true.head(2), 1e-6); // cdm_z is not observable while hovering + ASSERT_NEAR(inertia_estimated(2), inertia_true(2), 1e-6); // Ix and Iy not observable in this test + ASSERT_NEAR(mass_estimated, mass_true, 1e-5); +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, torque_yaw_constant__unfix_all__nonzero_bias) +{ + // Simulation data -- hovering and constant torque in yaw + Vector3d acc_true = -gravity(); + Vector3d ang_vel_true = Vector3d(0, 0, 0); + Vector3d force_true = -mass_true * gravity(); + Vector3d torque_true = Vector3d(0, 0, 0.01); + Vector3d position_true = Vector3d(0, 0, 0); + Quaterniond quat_true = Quaterniond(1, 0, 0, 0); + Vector3d ang_acc_true = inertia_true.asDiagonal().inverse() * torque_true; + Vector3d angle_true = Vector3d(0, 0, 0); + Vector3d acc_bias_true = 0.001 * Vector3d(1, 1, 1); + Vector3d gyro_bias_true = 0.001 * Vector3d(1, 1, 1); + bias_true << acc_bias_true, gyro_bias_true; + // bias_true.setZero(); + + // Data -- hovering and constant torque in yaw + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity() + acc_bias_true; + data.segment<3>(3) = ang_vel_true + gyro_bias_true; + data.segment<3>(6) = -mass_true * gravity(); + data.segment<3>(9) = torque_true; + MatrixXd data_cov = S->getNoiseCov(); + + Vector3d position_data = position_true; + Vector4d orient_data = quat_true.coeffs(); + + // Calibration params + Vector6d bias_guess = Vector6d::Zero(); + // Vector6d bias_guess = bias_true; + Vector3d cdm_guess = Vector3d(0.005, 0.005, 0.1); + Vector3d inertia_guess = Vector3d(0.01, 0.01, 0.02); + double mass_guess = 2.0; + S->getStateBlock('I')->setState(bias_guess); + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('i')->setState(inertia_guess); + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->unfix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->unfix(); + S->setStateBlockDynamic('I', false); + + // add regularization to unobservable states + S->addPriorParameter('C', // cdm + Vector1d(cdm_true(2)), // cdm Z + Matrix1d::Identity() * 1, // cov Z + 2, // start: Z coordinate + 1); // size + S->addPriorParameter('i', // inertia + Vector2d(inertia_true.segment<2>(0)), // inertia XY + Matrix2d::Identity() * 0.01, // cov XY + 0, // start: X coordinate + 2); // size: 2 + + // Process origin + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + // UAV states + // C_KF0->getFrame()->unfix(); // JS: this is unnecessary, all states are unfixed by default + C_KF0->getFrame()->getStateBlock('P')->fix(); + C_KF0->getFrame()->getStateBlock('P')->setState(position_data); + C_KF0->getFrame()->getStateBlock('O')->fix(); + C_KF0->getFrame()->getStateBlock('O')->setState(orient_data); + + // initialiation of all the variables needed in the following iteration process + double t_final = 8.0; // total run time + double dt = 0.1; // time increment + unsigned int last_kf_id = p->getOrigin()->getFrame()->id(); + std::string report; + + for (TimeStamp t = dt; t <= t_final; t += dt) + { + // SIMULATOR + + // ang velocity + ang_vel_true += ang_acc_true * dt; + + // orientation (quaternion and total angle) + quat_true *= exp_q(ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/); + angle_true += ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/; + + // FTI measurements + data.segment<3>(0) = acc_true + acc_bias_true; + data.segment<3>(3) = ang_vel_true + gyro_bias_true; + data.segment<3>(6) = force_true; + data.segment<3>(9) = torque_true; + + // Pose measurements (simulate motion capture) + position_data = position_true; + orient_data = quat_true.coeffs(); + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->process(); + + // check if new KF + if (last_kf_id != p->getOrigin()->getFrame()->id()) + { + last_kf_id = p->getOrigin()->getFrame()->id(); + + // fix measured position and orientation + // p->getOrigin()->getFrame()->unfix(); + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('P')->setState(position_data); + p->getOrigin()->getFrame()->getStateBlock('O')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->setState(orient_data); + + // solve! + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + + // results of this iteration + WOLF_INFO("Total angle turned : ", angle_true.transpose(), " rad."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + } + } + + // Final results + WOLF_INFO("True IMU bias * : ", bias_true.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Guess IMU bias : ", bias_guess.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("True center of mass * : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess center of mass : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("True inertia * : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("True mass * : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + auto bias_estimated = S->getStateBlock('I')->getState(); + auto cdm_estimated = S->getStateBlock('C')->getState(); + auto inertia_estimated = S->getStateBlock('i')->getState(); + auto mass_estimated = S->getStateBlock('m')->getState()(0); + + ASSERT_MATRIX_APPROX(bias_estimated, bias_true, 1e-4); + ASSERT_MATRIX_APPROX(cdm_estimated.head(2), cdm_true.head(2), 1e-6); // cdm_z is not observable while hovering + ASSERT_NEAR(inertia_estimated(2), inertia_true(2), 1e-6); // Ix and Iy not observable in this test + ASSERT_NEAR(mass_estimated, mass_true, 1e-5); +} + +TEST_F(Test_SolveProblemForceTorqueInertialDynamics_yaml, + torque_yaw_constant__unfix_all__nonzero_bias_and_nonzero_noise) +{ + // Simulation data -- hovering and constant torque in yaw + Vector3d acc_true = -gravity(); + Vector3d ang_vel_true = Vector3d(0, 0, 0); + Vector3d force_true = -mass_true * gravity(); + Vector3d torque_true = Vector3d(0, 0, 0.1); + Vector3d position_true = Vector3d(0, 0, 0); + Quaterniond quat_true = Quaterniond(1, 0, 0, 0); + Vector3d ang_acc_true = inertia_true.asDiagonal().inverse() * torque_true; + Vector3d angle_true = Vector3d(0, 0, 0); + Vector3d acc_bias_true = 0.001 * Vector3d(1, 1, 1); + Vector3d gyro_bias_true = 0.001 * Vector3d(1, 1, 1); + bias_true << acc_bias_true, gyro_bias_true; + // bias_true.setZero(); + + // Noise + Vector3d acc_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->acc_noise_std; + Vector3d gyro_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->gyro_noise_std; + Vector3d force_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->force_noise_std; + Vector3d torque_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->torque_noise_std; + + // Data -- hovering and constant torque in yaw + VectorXd data = VectorXd::Zero(12); // [ a, w, f, t ] + data.segment<3>(0) = -gravity() + acc_bias_true + acc_noise; + data.segment<3>(3) = ang_vel_true + gyro_bias_true + gyro_noise; + data.segment<3>(6) = -mass_true * gravity() + force_noise; + data.segment<3>(9) = torque_true + torque_noise; + MatrixXd data_cov = S->getNoiseCov(); + + Vector3d position_data = position_true; + Vector4d orient_data = quat_true.coeffs(); + + // Calibration params + Vector6d bias_guess = Vector6d::Zero(); + // Vector6d bias_guess = bias_true; + Vector3d cdm_guess = Vector3d(0.005, 0.005, 0.1); + Vector3d inertia_guess = Vector3d(0.01, 0.01, 0.02); + double mass_guess = 2.0; + S->getStateBlock('I')->setState(bias_guess); + S->getStateBlock('C')->setState(cdm_guess); + S->getStateBlock('i')->setState(inertia_guess); + S->getStateBlock('m')->setState(Vector1d(mass_guess)); + + S->getStateBlock('P')->fix(); + S->getStateBlock('O')->fix(); + S->getStateBlock('I')->unfix(); + S->getStateBlock('C')->unfix(); + S->getStateBlock('i')->unfix(); + S->getStateBlock('m')->unfix(); + S->setStateBlockDynamic('I', false); + + // add regularization to unobservable states + S->addPriorParameter('C', // cdm + Vector1d(cdm_true(2)), // cdm Z + Matrix1d::Identity() * 1, // cov Z + 2, // start: Z coordinate + 1); // size + S->addPriorParameter('i', // inertia + Vector2d(inertia_true.segment<2>(0)), // inertia XY + Matrix2d::Identity() * .001, // cov XY + 0, // start: X coordinate + 2); // size: 2 + + // Process origin + CaptureMotionPtr C0_0 = std::make_shared<CaptureForceTorqueInertial>(0.0, S, data, data_cov, nullptr); + C0_0->process(); + CaptureMotionPtr C_KF0 = std::static_pointer_cast<CaptureMotion>(p->getOrigin()); + + // UAV states + // C_KF0->getFrame()->unfix(); // JS: this is unnecessary, all states are unfixed by default + C_KF0->getFrame()->getStateBlock('P')->fix(); + C_KF0->getFrame()->getStateBlock('P')->setState(position_data); + C_KF0->getFrame()->getStateBlock('O')->fix(); + C_KF0->getFrame()->getStateBlock('O')->setState(orient_data); + + // initialiation of all the variables needed in the following iteration process + double t_final = 2.40; // total run time + double dt = 0.01; // time increment + unsigned int last_kf_id = p->getOrigin()->getFrame()->id(); + std::string report; + + for (TimeStamp t = dt; t <= t_final; t += dt) + { + // SIMULATOR + + // ang velocity + ang_vel_true += ang_acc_true * dt; + + // orientation (quaternion and total angle) + quat_true *= exp_q(ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/); + angle_true += ang_vel_true * dt /*+ 0.5 * ang_acc_true * dt * dt*/; + + // noises + acc_noise = Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->acc_noise_std; + gyro_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->gyro_noise_std; + force_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->force_noise_std; + torque_noise = 0 * Vector3d::Random() * S->getParamsSensorForceTorqueInertial()->torque_noise_std; + + // FTI measurements + data.segment<3>(0) = acc_true + acc_bias_true + acc_noise; + data.segment<3>(3) = ang_vel_true + gyro_bias_true + gyro_noise; + data.segment<3>(6) = force_true + force_noise; + data.segment<3>(9) = torque_true + torque_noise; + data_cov = S->getNoiseCov(); + + // Pose measurements (simulate motion capture) + position_data = position_true; + orient_data = quat_true.coeffs(); + + // ESTIMATOR + + CaptureMotionPtr C = std::make_shared<CaptureForceTorqueInertial>(t, S, data, data_cov, nullptr); + C->process(); + + // check if new KF + if (last_kf_id != p->getOrigin()->getFrame()->id()) + { + last_kf_id = p->getOrigin()->getFrame()->id(); + + // fix measured position and orientation + // p->getOrigin()->getFrame()->unfix(); + p->getOrigin()->getFrame()->getStateBlock('P')->fix(); + p->getOrigin()->getFrame()->getStateBlock('P')->setState(position_data); + p->getOrigin()->getFrame()->getStateBlock('O')->fix(); + p->getOrigin()->getFrame()->getStateBlock('O')->setState(orient_data); + + // solve! + report = solver->solve(wolf::SolverManager::ReportVerbosity::FULL); + + // WOLF_INFO(report); + // P->print(4,1,1,1); + + // results of this iteration + WOLF_INFO("Total angle turned : ", angle_true.transpose(), " rad."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + } + } + + // Final results + WOLF_INFO("True IMU bias * : ", bias_true.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Guess IMU bias : ", bias_guess.transpose(), " m/s2 | rad/s."); + WOLF_INFO("Estimated IMU bias : ", S->getStateBlock('I')->getState().transpose(), " m/s2 | rad/s."); + WOLF_INFO("True center of mass * : ", cdm_true.transpose(), " m."); + WOLF_INFO("Guess center of mass : ", cdm_guess.transpose(), " m."); + WOLF_INFO("Estimated center of mass: ", S->getStateBlock('C')->getState().transpose(), " m."); + WOLF_INFO("True inertia * : ", inertia_true.transpose(), " m^2 Kg."); + WOLF_INFO("Guess inertia : ", inertia_guess.transpose(), " m^2 Kg."); + WOLF_INFO("Estimated inertia : ", S->getStateBlock('i')->getState().transpose(), " m^2 Kg."); + WOLF_INFO("True mass * : ", mass_true, " Kg."); + WOLF_INFO("Guess mass : ", mass_guess, " Kg."); + WOLF_INFO("Estimated mass : ", S->getStateBlock('m')->getState()(0), " Kg."); + WOLF_INFO("-----------------------------"); + + auto bias_estimated = S->getStateBlock('I')->getState(); + auto cdm_estimated = S->getStateBlock('C')->getState(); + auto inertia_estimated = S->getStateBlock('i')->getState(); + auto mass_estimated = S->getStateBlock('m')->getState()(0); + + EXPECT_MATRIX_APPROX(bias_estimated, bias_true, 1e-3); + EXPECT_MATRIX_APPROX(cdm_estimated.head(2), cdm_true.head(2), 1e-3); // cdm_z is not observable while hovering + EXPECT_NEAR(inertia_estimated(2), inertia_true(2), 1e-3); // Ix and Iy not observable in this test + EXPECT_NEAR(mass_estimated, mass_true, 1e-3); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + // ::testing::GTEST_FLAG(filter) = + // "Test_SolveProblemForceTorqueInertialDynamics_yaml.torque_yaw_constant__unfix_all__nonzero_bias_and_nonzero_noise"; + 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..2df498f9c582fcad2918a0ae5fc714951ebb6c26 --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics_processor_test.yaml @@ -0,0 +1,64 @@ +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 + accb_initial_std: 0.01 # m/s2 - initial bias + gyrob_initial_std: 0.01 # rad/sec - initial bias + 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] + imu_bias_fix: true + com_fix: true + inertia_fix: true + mass_fix: true + + 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_simulation_test.yaml b/test/yaml/problem_force_torque_inertial_dynamics_simulation_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f3e25c03075b12b8df8c8f0026df1a88aa055e9e --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics_simulation_test.yaml @@ -0,0 +1,97 @@ +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: [100,100,100] + L: [100,100,100] + time_tolerance: 0.01 + tree_manager: + type: "TreeManagerSlidingWindow" + plugin: "core" + n_frames: 1000 + n_fix_first_frames: 0 + 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 + acc_noise_std: 0.001 # std dev of acc noise in m/s2 + gyro_noise_std: 0.001 # std dev of gyro noise in rad/s + accb_initial_std: 0.01 # m/s2 - initial bias + gyrob_initial_std: 0.01 # rad/sec - initial bias + acc_drift_std: 0.0001 # std dev of acc drift m/s2/sqrt(s) + gyro_drift_std: 0.0001 # std dev of gyro drift rad/s/sqrt(s) + + #FT + force_noise_std: 0.001 # std dev of force noise in N + torque_noise_std: 0.0001 # std dev of torque noise in N/m + + # Dynamics + com: [0.005,0.005,0.01] # center of mass [m] + inertia: [0.015,0.015,0.030] # moments of inertia i_xx, i_yy, i_zz [kg m2] + mass: 2.00 # mass [kg] + imu_bias_fix: true + com_fix: true + inertia_fix: true + mass_fix: true + + - + name: sensor Pose + type: SensorPose + plugin: core + extrinsic: + pose: [0,0,0, 0,0,0,1] + std_px: 0.001 + std_py: 0.001 + std_pz: 0.001 + std_ox: 0.001 + std_oy: 0.001 + std_oz: 0.001 + + processors: + - + name: "proc FTID" + type: "ProcessorForceTorqueInertialDynamics" + sensor_name: "sensor FTI" + plugin: "bodydynamics" + time_tolerance: 0.01 + apply_loss_function: false + unmeasured_perturbation_std: 0.001 + state_getter: true + state_priority: 1 + # n_propellers: 6 + keyframe_vote: + voting_active: true + max_time_span: 0.0995 # De moment el que funciona millor és 1 s o 2s + max_buff_length: 999 # motion deltas + dist_traveled: 999 # meters + angle_turned: 999 # radians (1 rad approx 57 deg, approx 60 deg) + + - + name: prc Pose + type: ProcessorPose + plugin: core + sensor_name: sensor Pose + time_tolerance: 0.01 + keyframe_vote: + voting_active: true + apply_loss_function: false + 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..79aaa00d0706000e43bd4641d1658d8ab1b77236 --- /dev/null +++ b/test/yaml/problem_force_torque_inertial_dynamics_solve_test.yaml @@ -0,0 +1,73 @@ +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: 6 + n_fix_first_frames: 0 + 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 + acc_noise_std: 0.001 # std dev of acc noise in m/s2 + gyro_noise_std: 0.001 # std dev of gyro noise in rad/s + accb_initial_std: 0.01 # m/s2 - initial bias + gyrob_initial_std: 0.01 # rad/sec - initial bias + acc_drift_std: 0.0001 # std dev of acc drift m/s2/sqrt(s) + gyro_drift_std: 0.0001 # std dev of gyro drift rad/s/sqrt(s) + + #FT + force_noise_std: 0.001 # std dev of force noise in N + torque_noise_std: 0.0001 # std dev of torque noise in N/m + + # 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.952 # mass [kg] + imu_bias_fix: true + com_fix: true + inertia_fix: true + mass_fix: true + + 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.0995 + 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..84aca5a4b193784435a6c073d95770f1a255deea --- /dev/null +++ b/test/yaml/processor_force_torque_inertial.yaml @@ -0,0 +1,14 @@ + 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..21d169c79bfe81bc609e966bf3dfbd57275e7e97 --- /dev/null +++ b/test/yaml/sensor_force_torque_inertial.yaml @@ -0,0 +1,16 @@ +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 +accb_initial_std: 0 # m/s2 - initial bias +gyrob_initial_std: 0 # rad/sec - initial bias +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] +imu_bias_fix: true +com_fix: true +inertia_fix: true +mass_fix: true \ 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