7#include <gtsam/base/numericalDerivative.h>
8#include <gtsam/geometry/Pose3.h>
9#include <gtsam/linear/NoiseModel.h>
15void require(
bool condition,
const std::string& message) {
17 throw std::runtime_error(message);
21const gtsam::SharedNoiseModel kNoise = gtsam::noiseModel::Isotropic::Sigma(1, 0.1);
23void constrainsYawOfUpsideDownImu() {
24 const gtsam::Rot3 R_I_S = gtsam::Rot3::Rx(M_PI);
25 const gtsam::Pose3 T_W_I(gtsam::Rot3::Rz(0.7) * R_I_S.inverse(), gtsam::Point3(1.0, 2.0, 3.0));
29 const gtsam::Vector error = factor.evaluateError(T_W_I, H);
31 require(std::abs(error(0) - 0.2) < 1e-9,
"Yaw error of the measurement frame is wrong: " + std::to_string(error(0)));
32 require(H.norm() > 0.5,
"Upside-down IMU has no yaw Jacobian");
35void jacobianMatchesNumericalDerivative() {
36 const gtsam::Rot3 R_I_S = gtsam::Rot3::RzRyRx(2.9, -0.3, 0.4);
37 const gtsam::Pose3 T_W_I(gtsam::Rot3::RzRyRx(0.2, 0.1, -2.5), gtsam::Point3(0.5, -1.0, 0.2));
41 factor.evaluateError(T_W_I, H);
42 const std::function<gtsam::Vector1(
const gtsam::Pose3&)> yawError = [&factor](
const gtsam::Pose3& pose) {
43 return gtsam::Vector1(factor.evaluateError(pose,
nullptr));
45 const gtsam::Matrix H_numerical = gtsam::numericalDerivative11<gtsam::Vector1, gtsam::Pose3>(yawError, T_W_I);
47 require(H.isApprox(H_numerical, 1e-6),
"Analytic yaw Jacobian differs from the numerical one");
50void skipsGimbalLockOfMeasurementFrame() {
51 const gtsam::Rot3 R_I_S = gtsam::Rot3::Rx(M_PI);
52 const gtsam::Pose3 T_W_I(gtsam::Rot3::Ry(M_PI / 2.0) * R_I_S.inverse(), gtsam::Point3());
56 const gtsam::Vector error = factor.evaluateError(T_W_I, H);
58 require(error.isZero() && H.isZero(),
"Yaw factor acts at gimbal lock of the measurement frame");
65 constrainsYawOfUpsideDownImu();
66 jacobianMatchesNumericalDerivative();
67 skipsGimbalLockOfMeasurementFrame();
68 }
catch (
const std::exception& error) {
69 std::cerr << error.what() <<
'\n';