diff --git a/imu_complementary_filter/CMakeLists.txt b/imu_complementary_filter/CMakeLists.txt index 8362072..19d1e65 100644 --- a/imu_complementary_filter/CMakeLists.txt +++ b/imu_complementary_filter/CMakeLists.txt @@ -71,6 +71,13 @@ install(DIRECTORY launch config DESTINATION share/${PROJECT_NAME} ) +if(BUILD_TESTING) + find_package(ament_cmake_gtest REQUIRED) + ament_add_gtest(complementary_filter_test + test/complementary_filter_test.cpp) + target_link_libraries(complementary_filter_test complementary_filter) +endif() + ament_export_include_directories(include) ament_export_libraries(complementary_filter) ament_export_targets( diff --git a/imu_complementary_filter/package.xml b/imu_complementary_filter/package.xml index faf7118..d1f9c18 100644 --- a/imu_complementary_filter/package.xml +++ b/imu_complementary_filter/package.xml @@ -18,6 +18,7 @@ std_msgs tf2 tf2_ros + ament_cmake_gtest ament_cmake diff --git a/imu_complementary_filter/src/complementary_filter.cpp b/imu_complementary_filter/src/complementary_filter.cpp index e44525e..3c4a6d8 100644 --- a/imu_complementary_filter/src/complementary_filter.cpp +++ b/imu_complementary_filter/src/complementary_filter.cpp @@ -496,7 +496,8 @@ void invertQuaternion(double q0, double q1, double q2, double q3, void scaleQuaternion(double gain, double& dq0, double& dq1, double& dq2, double& dq3) { - if (dq0 < 0.0) // 0.9 + constexpr double interpolation_threshold = 0.9; + if (dq0 <= interpolation_threshold) { // Slerp (Spherical linear interpolation): double angle = acos(dq0); diff --git a/imu_complementary_filter/test/complementary_filter_test.cpp b/imu_complementary_filter/test/complementary_filter_test.cpp new file mode 100644 index 0000000..fc54f44 --- /dev/null +++ b/imu_complementary_filter/test/complementary_filter_test.cpp @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include "imu_complementary_filter/complementary_filter.h" + +namespace { + +constexpr double kTolerance = 1e-12; +constexpr double kGain = 0.25; + +TEST(ScaleQuaternion, UsesSlerpForLargeCorrections) +{ + double q0 = 0.8; + double q1 = 0.6; + double q2 = 0.0; + double q3 = 0.0; + + const double angle = std::acos(q0); + const double expected_q0 = std::cos(kGain * angle); + const double expected_q1 = std::sin(kGain * angle); + + imu_tools::scaleQuaternion(kGain, q0, q1, q2, q3); + + EXPECT_NEAR(q0, expected_q0, kTolerance); + EXPECT_NEAR(q1, expected_q1, kTolerance); + EXPECT_DOUBLE_EQ(q2, 0.0); + EXPECT_DOUBLE_EQ(q3, 0.0); +} + +TEST(ScaleQuaternion, UsesNormalizedLerpForSmallCorrections) +{ + double q0 = 0.95; + double q1 = std::sqrt(1.0 - q0 * q0); + double q2 = 0.0; + double q3 = 0.0; + + const double lerp_q0 = (1.0 - kGain) + kGain * q0; + const double lerp_q1 = kGain * q1; + const double norm = std::hypot(lerp_q0, lerp_q1); + + imu_tools::scaleQuaternion(kGain, q0, q1, q2, q3); + + EXPECT_NEAR(q0, lerp_q0 / norm, kTolerance); + EXPECT_NEAR(q1, lerp_q1 / norm, kTolerance); + EXPECT_DOUBLE_EQ(q2, 0.0); + EXPECT_DOUBLE_EQ(q3, 0.0); +} + +} // namespace