nuScenes坐标系转换实战:从Lidar到Camera的3D感知数据融合

张开发
2026/4/21 3:09:44 15 分钟阅读

分享文章

nuScenes坐标系转换实战:从Lidar到Camera的3D感知数据融合
1. 理解nuScenes数据集中的坐标系系统在自动驾驶领域多传感器数据融合是构建环境感知系统的关键。nuScenes数据集作为行业标杆提供了丰富的传感器数据但要让这些数据真正发挥作用首先需要理解其中的坐标系系统。就像我们在现实生活中使用地图导航需要明确我在哪里一样处理传感器数据也需要先搞清楚每个数据点所处的坐标系。nuScenes中主要涉及三种坐标系全局坐标系相当于现实世界中的经纬度以车辆在t0时刻的位置为原点所有其他位置都相对于这个固定参考点车体坐标系以车辆自身为中心建立的坐标系原点通常位于车辆后轴中心随着车辆移动而变化传感器坐标系每个传感器激光雷达、摄像头等都有自己的坐标系原点位于传感器安装位置我第一次接触这些概念时常常混淆它们之间的关系。后来发现可以用手机导航来类比全局坐标系就像地图上的经纬度车体坐标系是手机当前的朝向而摄像头坐标系则是手机摄像头本身的视角。只有把这些关系理清楚才能准确地进行坐标转换。2. 解析传感器标定参数标定参数是坐标系转换的基础相当于各个坐标系之间的翻译字典。在nuScenes中每个传感器都有详细的标定数据主要包括位置(translation)和旋转(rotation)参数。激光雷达的标定数据通常包含translation3个浮点数表示激光雷达相对于车体坐标系原点的位置偏移rotation4个浮点数组成的四元数描述激光雷达的安装朝向摄像头的标定则更复杂一些除了上述参数外还包括相机内参3x3矩阵决定如何将3D点投影到2D图像平面畸变参数虽然nuScenes目前不考虑但在实际应用中很重要我在处理这些参数时发现四元数的使用是个难点。相比欧拉角四元数虽然数学上更优雅但直观性较差。这里分享一个实用技巧可以使用pyquaternion库的Quaternion类来方便地进行四元数运算和矩阵转换。from pyquaternion import Quaternion # 从标定数据创建四元数 quat Quaternion(lidar_calibrated_data[rotation]) # 转换为旋转矩阵 rotation_matrix quat.rotation_matrix3. 从激光雷达到全局坐标系的转换激光雷达数据的坐标转换通常分为两步首先转换到车体坐标系然后再转换到全局坐标系。这个过程就像先把房间内的物品位置描述为相对于你的位置再把这个描述转换为相对于整栋楼的位置。具体实现时我们需要构建4x4的齐次变换矩阵。这个矩阵同时包含旋转和平移信息可以一次性完成两种变换。以下是关键代码示例def get_matrix(calibrated_data, inverseFalse): 构建4x4齐次变换矩阵 matrix np.eye(4) matrix[:3, :3] Quaternion(calibrated_data[rotation]).rotation_matrix matrix[:3, 3] calibrated_data[translation] if inverse: matrix np.linalg.inv(matrix) return matrix # 激光雷达到车体的变换矩阵 lidar_to_ego get_matrix(lidar_calibrated_data) # 车体到全局的变换矩阵 ego_to_global get_matrix(ego_pose) # 组合变换激光雷达到全局 lidar_to_global ego_to_global lidar_to_ego实际应用中我遇到过几个常见问题矩阵乘法顺序容易搞反记住变换是从右向左应用的点坐标需要转换为齐次坐标添加一个1作为第四维变换后的点需要去掉齐次坐标的最后一维4. 从全局坐标系到相机图像的投影将全局坐标系下的点投影到相机图像上需要经过三个步骤全局→车体→相机→图像。这相当于把世界地图上的位置先转换为你当前的位置关系再转换为手机摄像头看到的样子最后变成手机屏幕上的像素坐标。关键步骤包括获取相机相对于车体的变换矩阵注意需要取逆组合全局到图像的完整变换链应用相机内参进行投影处理投影后的归一化坐标# 全局到车体的逆变换 global_to_ego get_matrix(camera_ego_pose, inverseTrue) # 车体到相机的逆变换 ego_to_camera get_matrix(camera_calibrated_data, inverseTrue) # 相机内参矩阵补充为4x4 camera_intrinsic np.eye(4) camera_intrinsic[:3, :3] camera_calibrated_data[camera_intrinsic] # 完整的全局到图像变换 global_to_image camera_intrinsic ego_to_camera global_to_ego这里有个重要细节投影后的点需要除以z值进行透视除法即归一化。同时要过滤掉z0的点因为它们位于相机后面不会在图像上形成投影。5. 可视化验证与常见问题排查理论正确不代表实践就没问题。我在项目中总结了一套可视化验证方法帮助快速定位坐标转换中的错误。对于激光雷达点云投影到图像可以随机选择一些特征明显的点如车辆轮廓在图像上绘制这些投影点检查它们是否落在预期的物体上# 在图像上绘制投影点 for x, y in image_points[image_points[:, 2] 0, :2].astype(int): cv2.circle(image, (x, y), 3, (255, 0, 0), -1)常见问题及解决方法点云整体偏移检查标定参数是否正确加载特别是旋转和平移的顺序点云形状扭曲确认四元数到旋转矩阵的转换是否正确投影点集中在图像边缘可能是坐标系定义不一致如左右手坐标系混淆部分点位置正确但大部分错误检查是否遗漏了某些变换步骤6. 实际应用中的优化技巧经过多个项目的实践我总结了一些优化坐标转换的技巧性能优化使用批量矩阵运算代替循环numpy的矩阵操作比Python循环快几个数量级对于固定变换预先计算好组合矩阵使用mask过滤掉不必要的点如距离过远或位于地平面以下的点精度提升定期检查传感器标定特别是经过剧烈震动后考虑时间同步问题nuScenes提供了精确的时间戳对于移动物体考虑运动补偿代码可维护性封装坐标转换操作为独立类或函数为每个变换步骤添加详细注释实现可视化调试工具class CoordinateTransformer: def __init__(self, nuscenes): self.nuscenes nuscenes def lidar_to_camera(self, lidar_points, camera_name): 完整的激光雷达到相机图像坐标转换 # 实现所有变换步骤 ... return image_points7. 处理动态场景中的坐标转换在实际自动驾驶场景中车辆和周围物体都在运动这给坐标转换带来了额外挑战。nuScenes数据集提供了精确的时间戳可以用来处理这类问题。对于动态物体我们需要获取物体在全局坐标系中的运动轨迹根据传感器采集时间进行插值应用运动补偿到坐标转换中# 获取标注框在不同时间戳的位置 annotations [nuscenes.get(sample_annotation, token) for token in sample[anns]] # 根据时间戳进行运动估计 ...这个过程中最容易出错的是时间同步。我的经验是一定要仔细检查每个传感器数据的时间戳确保使用的是同一时刻的位姿估计。有时候几毫秒的差异就会导致明显的投影误差。8. 扩展应用多相机系统的联合标定nuScenes数据集包含6个相机形成了一个360度的感知系统。在实际项目中我们经常需要在这些相机之间进行坐标转换比如将前相机检测到的物体投影到后相机图像上。实现多相机联合标定的关键步骤选择一个主相机作为参考计算其他相机相对于主相机的变换关系建立统一的坐标转换管道# 计算相机间的相对变换 def get_camera_relative_transform(nuscenes, sample, from_cam, to_cam): # 获取两个相机的标定数据 from_calib nuscenes.get(calibrated_sensor, nuscenes.get(sample_data, sample[data][from_cam])[calibrated_sensor_token]) to_calib nuscenes.get(calibrated_sensor, nuscenes.get(sample_data, sample[data][to_cam])[calibrated_sensor_token]) # 计算相对变换 from_to_ego get_matrix(from_calib) ego_to_to get_matrix(to_calib, inverseTrue) return ego_to_to from_to_ego在多相机系统中重叠区域的投影一致性是验证标定质量的重要指标。我通常会选择一些同时在多个相机视野中的特征点检查它们在各个相机中的投影位置是否合理。

更多文章