Eigen中Eigen::Affine3d和Eigen::Isometry3d详解
1. 背景:仿射变换 vs 等距变换
在三维空间中,一个 齐次变换矩阵(4×4)一般写作:
T=[At01],A∈R3×3,t∈R3 T = \begin{bmatrix} A & \mathbf{t} \\ 0 & 1 \end{bmatrix}, \quad A \in \mathbb{R}^{3\times 3}, \mathbf{t} \in \mathbb{R}^3 T=[A0t1],A∈R3×3,t∈R3
这里的 A
决定了旋转/缩放/剪切,t
是平移。
-
Affine Transform (仿射变换)
T(x)=Ax+t T(\mathbf{x}) = A\mathbf{x} + \mathbf{t} T(x)=Ax+t
A
可以是任意非奇异矩阵,允许旋转、缩放、剪切。 -
Isometry (等距变换)
T(x)=Rx+t,R∈SO(3) T(\mathbf{x}) = R\mathbf{x} + \mathbf{t}, \quad R \in SO(3) T(x)=Rx+t,R∈SO(3)
这里
R
必须是正交矩阵,且 det(R)=+1\det(R)=+1det(R)=+1,即纯旋转,不允许缩放/畸变。
这是 SE(3) 里标准的位姿表示。
所以:
Affine3d
= 一般的仿射变换(包含旋转+平移+缩放+剪切)。Isometry3d
=Affine3d
的子集(只允许旋转+平移)。
2. Eigen 里的实现关系
在 Eigen 里:
Affine3d
继承自Transform<double, 3, Affine>
Isometry3d
继承自Transform<double, 3, Isometry>
它们本质上都是 Eigen::Transform
的模板特化,只是内部对 矩阵 A
的约束不同:
类型 | 模板参数 | 允许的变换 | 典型用途 |
---|---|---|---|
Eigen::Affine3d | Transform<double,3,Affine> | 旋转、平移、缩放、剪切 | 图形学/通用几何变换 |
Eigen::Isometry3d | Transform<double,3,Isometry> | 旋转 + 平移(R ∈ SO(3)) | 机器人学/SLAM 位姿 |
3. 转换关系
3.1 Isometry3d → Affine3d
总是安全的,因为等距变换一定是仿射变换的特例:
Eigen::Isometry3d iso = Eigen::Isometry3d::Identity();
Eigen::Affine3d affine = iso; // OK
3.2 Affine3d → Isometry3d
可能丢失信息:
如果 Affine3d
中包含缩放/剪切,转成 Isometry3d
时会 直接假设线性部分是旋转矩阵,数值不符合 SE(3) 要求的话,结果不可预测。
Eigen::Affine3d affine = Eigen::Affine3d::Identity();
affine.linear() *= 2.0; // 缩放
Eigen::Isometry3d iso = affine; // 结果不是严格的旋转+平移
3.3 构造与赋值
// 直接从旋转+平移构造
Eigen::Isometry3d iso = Eigen::Translation3d(1,2,3) * Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d::UnitZ());
Eigen::Affine3d affine = iso; // 安全
4. 常见接口差异
两者常用接口大部分相同,比如:
.translation()
→ 获取/设置平移.linear()
→ 获取/设置旋转部分(注意:Affine3d 不一定是正交矩阵!)*
运算符 → 变换向量或点.matrix()
→ 获取 4×4 齐次矩阵
唯一区别:
Isometry3d
在数值上 保证旋转矩阵正交,适合表示位姿。Affine3d
不做约束,可能引入畸变。
5. 实际应用建议
-
机器人学 / SLAM / 计算机视觉
用Isometry3d
,保证运算结果在 SE(3),不会意外引入缩放。Eigen::Isometry3d Tcw; // 相机位姿 Eigen::Vector3d Pc = Tcw * Pw;
-
计算机图形学 / CAD / 建模
用Affine3d
,因为可能需要缩放、剪切等操作。
6. 小示例
#include <Eigen/Dense>
#include <iostream>int main() {// 构造 Isometry3d (旋转+平移)Eigen::Isometry3d iso = Eigen::Isometry3d::Identity();iso.translate(Eigen::Vector3d(1,2,3));iso.rotate(Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d::UnitZ()));// 构造 Affine3dEigen::Affine3d affine = Eigen::Affine3d::Identity();affine.linear() *= 2.0; // 加缩放affine.translation() << 1, 2, 3;// 转换Eigen::Affine3d affine_from_iso = iso; // 安全Eigen::Isometry3d iso_from_affine = affine; // 不一定合法std::cout << "Isometry3d matrix:\n" << iso.matrix() << "\n\n";std::cout << "Affine3d matrix:\n" << affine.matrix() << "\n";
}
总结:
Affine3d
= 仿射变换(更通用,允许缩放/剪切)Isometry3d
= 仿射变换的子集(严格 SE(3),只允许旋转+平移)- 实际上
Isometry3d
更适合 位姿 (pose),Affine3d
更适合 通用几何变换 Isometry3d
可以安全转Affine3d
,反之不保证合法
7.综合示例
下面是一个 综合示例,完整演示 Eigen::Affine3d
和 Eigen::Isometry3d
的构造、转换、使用、对比,适合 SLAM / 机器人学场景。
综合示例:位姿运算与差异对比
#include <Eigen/Dense>
#include <iostream>int main() {// -------------------------------// 1. 构造 Isometry3d(严格的位姿:旋转+平移)// -------------------------------Eigen::Isometry3d T1 = Eigen::Isometry3d::Identity();T1.translate(Eigen::Vector3d(1, 0, 0)); // 平移T1.rotate(Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d::UnitZ())); // 绕Z轴旋转45度std::cout << "Isometry3d T1:\n" << T1.matrix() << "\n\n";// -------------------------------// 2. 构造 Affine3d(包含缩放的仿射变换)// -------------------------------Eigen::Affine3d T2 = Eigen::Affine3d::Identity();T2.translate(Eigen::Vector3d(0, 2, 0)); // 平移T2.linear() *= 2.0; // ⚠️ 加缩放,破坏正交性std::cout << "Affine3d T2 (with scaling):\n" << T2.matrix() << "\n\n";// -------------------------------// 3. 类型转换// -------------------------------Eigen::Affine3d affine_from_iso = T1; // ✅ 永远安全Eigen::Isometry3d iso_from_affine = T2; // ⚠️ 丢失缩放信息,不一定合法std::cout << "Affine from Isometry (safe):\n" << affine_from_iso.matrix() << "\n\n";std::cout << "Isometry from Affine (unsafe if scaled):\n" << iso_from_affine.matrix() << "\n\n";// -------------------------------// 4. 应用:变换点// -------------------------------Eigen::Vector3d p(1, 1, 1);Eigen::Vector3d p_T1 = T1 * p; // 严格位姿变换Eigen::Vector3d p_T2 = T2 * p; // 包含缩放的仿射变换std::cout << "Original point: " << p.transpose() << "\n";std::cout << "Transformed by Isometry3d T1: " << p_T1.transpose() << "\n";std::cout << "Transformed by Affine3d T2 (scaled): " << p_T2.transpose() << "\n\n";// -------------------------------// 5. 提取旋转和平移// -------------------------------std::cout << "T1 translation: " << T1.translation().transpose() << "\n";std::cout << "T1 rotation matrix:\n" << T1.rotation() << "\n\n";std::cout << "T2 translation: " << T2.translation().transpose() << "\n";std::cout << "T2 linear part (not guaranteed orthogonal):\n" << T2.linear() << "\n\n";return 0;
}
输出要点(示例)
Isometry3d T1:
0.707 -0.707 0 1
0.707 0.707 0 0
0 0 1 0
0 0 0 1Affine3d T2 (with scaling):
2 0 0 0
0 2 0 2
0 0 2 0
0 0 0 1Affine from Isometry (safe): // 完全一致
Isometry from Affine (unsafe): // 旋转矩阵不是严格正交Original point: 1 1 1
Transformed by Isometry3d T1: 1 1.41421 1
Transformed by Affine3d T2 (scaled): 2 4 2T1 translation: 1 0 0
T1 rotation matrix: 正交矩阵
T2 translation: 0 2 0
T2 linear part: 非正交矩阵(带缩放)
综合结论
Isometry3d
保证 SE(3) 位姿,适合 SLAM/机器人学。Affine3d
更通用,可以引入缩放/剪切(但在位姿优化中通常不合理)。Isometry3d → Affine3d
永远安全。Affine3d → Isometry3d
可能非法。- 处理点云/位姿变换时,建议 优先用
Isometry3d
。