Eigen::Transpose<const Matrix3>的正确使用方法?

Correct way to use Eigen::Transpose<const Matrix3>?

我有一个旋转 class,它可以使用四元数或旋转矩阵来表示旋转。当转置函数定义如下时,我没有问题:

Matrix3 Rot3::transpose() const {
  // quaternion_ is a class variable of type `Quaternion`.
  return quaternion_.toRotationMatrix().transpose();
}

当我使用 Eigen::Transpose 切换到推荐版本时,我的单元测试失败了(我得到 NaN)

Eigen::Transpose<const Matrix3> Rot3::transpose() const {
  // quaternion_ is a class variable of type `Quaternion`.
  return quaternion_.toRotationMatrix().eval().transpose();
}

我需要以那种奇怪的方式使用 .eval() 否则编译器会抱怨一条模糊的错误消息。我的猜测是我对 Eigen::Transpose 声明的使用与我返回的内容不一致。关于此方法为何表现如此奇怪的任何帮助或建议,以及有关正确执行此操作的任何建议?

Eigen::Transpose 只是一个围绕现有矩阵的 wrapper class。它保持对矩阵的引用并在反转索引的同时中继访问元素的调用。这个 class 的目标是能够使用转置矩阵而不必实际复制矩阵本身。

换句话说,这是 Transpose class 定义的一个非常简化的版本:

struct Transpose<const Matrix3> {

   const Matrix3& m_matrix;

   const float& coeffRef(int row, int col) const { 
       return m_matrix.coeffRef(col,row);
   }
}

您可以看到实际来源here

关键部分是 Transpose class 将 reference 存储到给定矩阵。

现在 transpose 函数的第二个版本发生了什么?

  • 您使用 quaternion.toRotationMatrix().eval()
  • 创建一个临时 Matrix3
  • 然后您围绕这个临时矩阵创建一个 Transpose 包装器,存储对该矩阵的引用。

当您从函数 return 中 Transpose 时,您 return 引用了一个超出范围的对象。这称为 ,它会导致未定义的行为(您看到 NaN 的原因很可能是您的临时矩阵所在的内存已被其他数据覆盖)。代码等同于:

Eigen::Transpose<const Matrix3> Rot3::transpose() const {
  // Equivalent to your second version
  Matrix3 temp = quaternion_.toRotationMatrix().eval();
  Transpose<const Matrix3> result = temp.transpose(); // Holds ref to temp -> Leads to UB
  return result; 
}

请注意,这在第一个版本中不会发生,因为您的 return 类型是 Matrix3。在这种情况下,您创建的 Transpose 对象将转换为要进行 returned 的新 Matrix3 对象,它会复制系数。这是您的第一个函数的等效版本。

Matrix3 Rot3::transpose() const {
  // Equivalent to your first version
  Matrix3 temp = quaternion_.toRotationMatrix().eval();
  Matrix3 result = temp.transpose(); // Does a full copy of the transpose of `temp`
  return result; 
}

如果你仍然想继续使用 Eigen::Transpose 作为你的 return 类型(也许你 真的 想避免复制操作),你需要将旋转矩阵存储在永久位置(例如,作为 class 中的缓存 Matrix3 成员变量)以避免悬空引用问题。

另一种选择是传递一个预先存在的矩阵进行填充,例如

void Rot3::setToTranspose() (Matrix3& result)
{
     result = quaternion_.toRotationMatrix().transpose();
}