Eigen3四元数调用slerp()方法返回NaN异常问题咨询
问题根本原因
- 这是旧版本Eigen3中
slerp()实现的已知数值稳定性问题:当传入的两个单位四元数点积恰好等于1时,实现中会计算sin(theta)(theta为两个四元数的夹角,此时theta=0,sin(theta)=0),后续计算步骤出现除零操作,最终返回全NaN的四元数。 - 独立测试程序表现正常的核心原因通常是编译选项差异:项目中如果开启了
-ffast-math类的激进浮点优化,会破坏Eigen原本的边界判断分支,导致点积等于1时本该触发的线性插值 fallback 分支被优化失效;部分场景下测试程序使用的Eigen版本高于项目依赖版本,也会出现表现不一致的问题。
可靠解决方案
- 优先升级Eigen到3.3.7及以上正式版本:该版本已经官方修复了
slerp()的边界除零问题,是成本最低的彻底解决方案。 - 若无法升级版本,替换原临时校验逻辑:除了用
isApprox()判断两四元数相等的场景,额外校验两个四元数的点积绝对值是否大于0.9995,满足条件时直接使用线性插值后归一化的结果,可以覆盖夹角极小的所有边界场景,避免除零问题。
参考实现逻辑:Eigen::Quaterniond slerp_safe(double t, const Eigen::Quaterniond& q1, const Eigen::Quaterniond& q2) { double dot = q1.dot(q2); // 点积接近±1时走线性插值避免除零 if (fabs(dot) > 0.9995) { return (q1 * (1 - t) + q2 * t).normalized(); } return q1.slerp(t, q2); } - 统一编译选项:项目中关闭
-funsafe-math-optimizations这类会破坏浮点运算逻辑的优化参数,避免Eigen内置的边界处理逻辑被优化失效。 - 前置归一化校验:所有传入
slerp()的四元数先调用normalized()做一次归一化,避免浮点累计误差带来的非单位四元数输入触发额外数值问题。
内容的提问来源于stack exchange,提问作者user888379
相关产品推荐
相关产品推荐

