1. 三维坐标系转换的核心原理
在自动驾驶和机器人定位领域,坐标系转换是最基础也是最关键的算法之一。DR(Dead Reckoning,航位推算)坐标系到自车坐标系的转换,本质上是一个三维空间中的刚体变换问题。刚体变换包含两个核心部分:旋转和平移。
刚体变换的数学表达通常使用齐次坐标和变换矩阵来表示。在三维空间中,一个完整的刚体变换可以表示为4x4的齐次变换矩阵:
code复制[R | t]
[0 | 1]
其中R是3x3的旋转矩阵,t是3x1的平移向量。这种表示方法可以同时描述旋转和平移,并且可以通过矩阵乘法来组合多个变换。
在Eigen库中,这种变换用Affine3d类来表示。Affine3d内部实际上就是存储了一个4x4的变换矩阵,但提供了更友好的接口来构造和操作变换。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 代码实现详解
2.1 数据结构定义
首先我们需要理解代码中使用的几个关键数据结构:
Eigen::Vector3d:Eigen库中的三维双精度浮点向量,用于表示三维空间中的点、向量等。Eigen::Quaterniond:四元数,用于表示三维旋转。Eigen::Affine3d:仿射变换,可以表示旋转和平移的组合。Eigen::Matrix3d:3x3的双精度浮点矩阵,常用于表示旋转矩阵。
2.2 坐标转换实现
坐标转换的核心代码如下:
cpp复制Eigen::Affine3d pose = Eigen::Translation3d(drInfo.position) * drInfo.quat;
Eigen::Affine3d invPose = pose.inverse();
Eigen::Vector3d point_vehicle = invPose * point_global;
这段代码完成了以下几个步骤:
-
构造从自车坐标系到DR坐标系的变换
pose:Eigen::Translation3d(drInfo.position)创建平移变换drInfo.quat是旋转部分(四元数)- 使用
*运算符组合变换,注意顺序是先旋转后平移
-
计算逆变换
invPose:- 对于刚体变换,逆变换可以通过数学公式精确计算
- 对于齐次变换矩阵,逆矩阵的计算公式为:
code复制其中R^T是旋转矩阵的转置(对于正交矩阵,转置等于逆)[R^T | -R^T*t] [ 0 | 1 ]
-
应用逆变换将全局坐标转换到自车坐标系:
- 通过矩阵乘法实现坐标变换
- 注意这里使用的是齐次坐标的变换规则
2.3 速度与加速度的转换
速度和加速度的转换与位置坐标有所不同,因为它们都是向量(有大小和方向,但没有位置属性),所以只需要旋转不需要平移:
cpp复制Eigen::Matrix3d rotation = drInfo.quat.toRotationMatrix();
Eigen::Matrix3d invRotation = rotation.transpose();
Eigen::Vector3d vel_vehicle = invRotation * vel_global;
这里的关键点:
- 将四元数转换为旋转矩阵
- 利用正交矩阵的性质(转置等于逆)得到逆旋转
- 只应用旋转部分来变换向量
3. 四元数与旋转矩阵
3.1 四元数基础
四元数是表示三维旋转的紧凑方式,由一个实部和三个虚部组成:q = w + xi + yj + zk。在代码中,drInfo.quat就是表示自车姿态的四元数。
四元数与旋转矩阵可以相互转换:
-
四元数转旋转矩阵:
cpp复制Eigen::Matrix3d rotation = drInfo.quat.toRotationMatrix(); -
旋转矩阵转四元数:
cpp复制Eigen::Quaterniond quat(rotation_matrix);
3.2 旋转的逆变换
对于旋转矩阵,因为它是正交矩阵,所以它的逆等于它的转置:
cpp复制Eigen::Matrix3d invRotation = rotation.transpose();
对于四元数,逆四元数是共轭四元数(实部不变,虚部取反)除以模长的平方。对于单位四元数(通常用于表示旋转),逆就是共轭:
cpp复制Eigen::Quaterniond invQuat = quat.conjugate();
4. 实际应用中的注意事项
4.1 坐标系定义一致性
在实际项目中,必须确保所有模块对坐标系的定义一致,包括:
- 坐标轴方向(前、左、上)
- 角度定义(偏航角、俯仰角、横滚角的正方向)
- 单位统一(米、弧度等)
4.2 数值稳定性
在连续进行坐标变换时,需要注意:
- 四元数需要定期归一化,防止数值误差累积
cpp复制quat.normalize(); - 对于接近奇异值的旋转,需要有特殊处理
- 浮点数比较需要设置合理的容差
4.3 性能优化
对于实时性要求高的应用:
- 避免在循环中频繁创建临时对象
- 对于固定变换,可以预先计算并缓存逆变换
- 使用Eigen的Map功能避免数据拷贝
5. 扩展应用
5.1 多坐标系转换
在实际自动驾驶系统中,通常涉及多个坐标系:
- 世界坐标系(如DR坐标系)
- 自车坐标系
- 传感器坐标系(雷达、相机等)
- 地图坐标系
需要建立完整的坐标变换链,并确保转换的一致性。
5.2 不确定性的传播
当坐标变换的输入带有不确定性(如定位误差)时,需要考虑不确定性的传播:
- 使用协方差矩阵表示位置和方向的不确定性
- 通过雅可比矩阵传播协方差
- 对于非线性变换(如四元数旋转),可能需要使用一阶近似
6. 调试与验证技巧
6.1 单元测试设计
对于坐标转换算法,建议设计以下测试用例:
- 恒等变换(零旋转和零平移)
- 纯平移变换
- 纯旋转变换
- 组合变换
- 逆变换验证(应用变换再应用逆变换应得到原始值)
6.2 可视化验证
使用可视化工具验证坐标转换的正确性:
- 绘制原始点和变换后的点
- 可视化坐标轴方向
- 对于运动物体,绘制速度和加速度向量
6.3 数值精度检查
检查变换后的数值是否合理:
- 变换后的坐标是否在预期范围内
- 向量长度是否保持不变(对于纯旋转)
- 角度关系是否正确
7. 常见问题排查
7.1 坐标转换结果异常
可能原因:
- 坐标系定义不一致
- 旋转顺序错误(偏航、俯仰、横滚的顺序)
- 四元数未归一化
解决方案:
- 检查所有模块的坐标系文档
- 验证旋转顺序是否符合约定
- 添加四元数归一化检查
7.2 性能瓶颈
可能原因:
- 频繁的矩阵内存分配
- 未使用优化的数学库
- 冗余的变换计算
解决方案:
- 重用矩阵对象
- 启用Eigen的向量化优化
- 缓存常用变换结果
7.3 数值不稳定
可能原因:
- 连续变换导致的误差累积
- 接近奇异的旋转
- 浮点数精度问题
解决方案:
- 定期重新初始化参考坐标系
- 对特殊情况进行特殊处理
- 使用更高精度的数据类型(如long double)
