diff --git a/fuse_models/include/fuse_models/omnidirectional_3d_predict.hpp b/fuse_models/include/fuse_models/omnidirectional_3d_predict.hpp index ab64cd352..1afb2ef65 100644 --- a/fuse_models/include/fuse_models/omnidirectional_3d_predict.hpp +++ b/fuse_models/include/fuse_models/omnidirectional_3d_predict.hpp @@ -493,7 +493,16 @@ inline void predict(const fuse_core::Vector3d& position1, const Eigen::Quaternio vel_linear2.y(), vel_linear2.z(), vel_angular2.x(), vel_angular2.y(), vel_angular2.z(), acc_linear2.x(), acc_linear2.y(), acc_linear2.z(), jacobians.data(), jacobian_quat2rpy); - jacobian << J[0], J[1], J[2], J[3], J[4]; + // Convert J[1] from quaternion space (15x4) to RPY space (15x3) using the + // pseudo-inverse of the quaternion-to-RPY Jacobian. + // Chain rule: J[1] = d(state2)/d(quat1) = d(state2)/d(rpy1) * d(rpy)/d(quat) + // So: d(state2)/d(rpy1) = J[1] * pinv(d(rpy)/d(quat)) + Eigen::Map> j_quat2rpy_map(jacobian_quat2rpy); + fuse_core::Matrix j_quat2rpy_pinv = + j_quat2rpy_map.transpose() * (j_quat2rpy_map * j_quat2rpy_map.transpose()).inverse(); + fuse_core::Matrix J1_rpy = J[1] * j_quat2rpy_pinv; + + jacobian << J[0], J1_rpy, J[2], J[3], J[4]; // Convert back to quaternion orientation2 = Eigen::AngleAxisd(rpy[2], Eigen::Vector3d::UnitZ()) *