菜单

Administrator
发布于 2026-05-19 / 1 阅读
0
0

KITTI 数据集中的坐标变换

KITTI数据集中的坐标变换

一、概述

KITTI数据集是自动驾驶领域最知名的数据集之一,可用于立体图像、光流估计、三维检测、三维跟踪等方面的研究。KITTI数据采集平台装配有 2个灰度摄像机2个彩色摄像机1个Velodyne 64线3D激光雷达、4个光学镜头以及1个GPS/IMU导航系统。

由于存在多个传感器,获得的图像和点云分别处于不同的坐标系,因此当需要同时利用图像和点云时,就涉及到了 坐标转换


二、传感器坐标系约定

KITTI数据集中各传感器的坐标系定义如下:

传感器 坐标系方向
摄像机(Camera) x — 向右,y — 向下,z — 向前
激光雷达(Velodyne/LiDAR) x — 向前,y — 向左,z — 向上
GPS/IMU x — 向前,y — 向左,z — 向上

摄像机索引(xx ∈ {0, 1, 2, 3}):

  • 0 — 左侧灰度相机(参考相机)
  • 1 — 右侧灰度相机
  • 2 — 左侧彩色相机
  • 3 — 右侧彩色相机

相机 0 号(参考相机) 是所有坐标变换的中间桥梁。所有相机中心均对齐,位于同一 x/y 平面上。


三、标定文件解析

KITTI 数据集为每个数据序列提供了标定文件,主要包括以下三个文件:

1. calib_cam_to_cam.txt — 相机到相机的标定

各参数含义如下:

参数 维度 含义
P_rect_xx 3×4 矫正后的投影矩阵(内参矩阵)
R_rect_xx 3×3 矫正旋转矩阵(使图像平面共面)
S_rect_xx 1×2 矫正后的图像尺寸
T_xx 3×1 从相机0到相机xx的平移矢量(外参)
R_xx 3×3 从相机0到相机xx的旋转矩阵(外参)
K_xx 3×3 矫正前摄像机xx的校准矩阵
D_xx 1×5 矫正前摄像头xx的畸变向量
S_xx 1×2 矫正前的图像尺寸

当使用同步和矫正后的数据集时,只有带 rect 下标的变量(P_rect_xxR_rect_xx 等)是实际使用的。

2. calib_velo_to_cam.txt — 激光雷达到相机的标定

包含 Tr_velo_to_cam 矩阵(3×4),由旋转矩阵 R(3×3)和平移向量 T(3×1)组成:

Tr_velo_to_cam = [R | T]

该矩阵用于将激光雷达坐标系中的点变换到参考相机(相机0)坐标系中。

3. calib_imu_to_velo.txt — IMU到激光雷达的标定

描述 GPS/IMU 导航坐标系到激光雷达坐标系的旋转和平移矩阵。


四、核心坐标变换流程

将激光雷达点云投影到图像上的完整流程可分为 三步

点云 (Velodyne)
    ↓  Tr_velo_to_cam (V2C)
参考相机坐标系 (Camera 0)
    ↓  R0_rect (矫正矩阵)
矫正相机坐标系
    ↓  P_rect (投影矩阵)
图像像素坐标

关键变换矩阵

矩阵 含义 维度 来源
Tr_velo_to_cam 激光雷达到参考相机的刚体变换(V2C) 3×4 calib_velo_to_cam.txt
R0_rect 参考相机的矫正旋转矩阵 3×3 calib_cam_to_cam.txt
P_rect_i 第 i 个相机的矫正后投影矩阵 3×4 calib_cam_to_cam.txt

注意:R0_rect 是 3×3 矩阵,使用时需扩展为 4×4 齐次矩阵(右下角补 1,其余补 0)。Tr_velo_to_cam 是 3×4 矩阵(R|t),同样需扩展为 4×4。所有矩阵按 行主序(row-major) 存储。

完整公式

将 Velodyne 坐标系中的点 x 投影到 左侧彩色图像(相机2) 中的点 y

y = P_rect_2 × R0_rect × Tr_velo_to_cam × x

投影到 右侧彩色图像(相机3)

y = P_rect_3 × R0_rect × Tr_velo_to_cam × x

逐步详解

步骤1:Velodyne → 参考相机坐标系(Camera 0)

def project_velo_to_ref(self, pts_3d_velo):
    pts_3d_velo = self.cart2hom(pts_3d_velo)   # 齐次化,n×4
    return np.dot(pts_3d_velo, np.transpose(self.V2C))  # 左乘变换矩阵

其中 V2C = Tr_velo_to_cam

步骤2:参考相机坐标系 → 矫正相机坐标系

def project_ref_to_rect(self, pts_3d_ref):
    ''' Input and Output are nx3 points '''
    return np.transpose(np.dot(self.R0, np.transpose(pts_3d_ref)))

步骤3:矫正相机坐标系 → 图像像素坐标

def project_rect_to_image(self, pts_3d_rect):
    ''' Input: nx3 points in rect camera coord.
        Output: nx2 points in image2 coord. '''
    pts_3d_rect = self.cart2hom(pts_3d_rect)          # 齐次化
    pts_2d = np.dot(pts_3d_rect, np.transpose(self.P)) # n×3
    pts_2d[:, 0] /= pts_2d[:, 2]   # u = x / z 归一化
    pts_2d[:, 1] /= pts_2d[:, 2]   # v = y / z 归一化
    return pts_2d[:, 0:2]

五、3D目标框坐标变换

5.1 相机 → LiDAR 变换

将相机坐标系中的 3D 边界框转换到 LiDAR 坐标系:

  1. 计算 3D 边界框角点(在相机坐标系下,坐标系在盒子底部中心)
  2. 绕 y 轴旋转 rotation_y 角度
  3. 平移 到质心坐标
  4. 投影到 LiDAR 坐标系
    • a. 逆矫正:R0_rect 的逆矩阵
    • b. 逆刚体变换:Tr_velo_to_cam 的逆矩阵

5.2 LiDAR → 相机变换

将 LiDAR 坐标系中的 3D 边界框转换到相机坐标系:

  1. 质心投影:通过 Tr_velo_to_camR0_rect 将质心从 LiDAR 变换到相机坐标系
  2. 航向角变换:LiDAR 中绕 z 轴的偏航角对应相机中绕 -y 轴的旋转
  3. 计算角点旋转平移

5.3 相机 → 图像变换

将 3D 边界框投影到 2D 图像平面:

  1. 使用投影矩阵 P 将 3D 角点从相机坐标系投影到图像平面
  2. 用 z 坐标归一化得到像素坐标 (u, v)

六、数据读取示例

读取点云(.bin 文件)

import numpy as np

def read_lidar_bin(file_path):
    """读取 KITTI .bin 点云文件"""
    points = np.fromfile(file_path, dtype=np.float32).reshape(-1, 4)
    # 每行: [x, y, z, reflectance]
    return points

读取标定文件

import numpy as np

def read_calib_file(filepath):
    """读取 KITTI 标定文件(calib_xxx.txt)"""
    data = {}
    with open(filepath, 'r') as f:
        for line in f.readlines():
            line = line.strip()
            if len(line) == 0 or line.startswith('#'):
                continue
            key, val = line.split(':', 1)
            try:
                data[key] = np.array([float(x) for x in val.split()])
            except ValueError:
                pass
    return data

七、总结

KITTI 数据集的坐标变换核心是 三步流水线

Velodyne → 参考相机(Cam0) → 矫正坐标系 → 图像像素
   ×V2C          ×R0_rect        ×P_rect_i

其中:

  • Tr_velo_to_cam(V2C):激光雷达 → 参考相机的外参(旋转 + 平移)
  • R0_rect:参考相机的立体矫正旋转矩阵
  • P_rect_i:第 i 个相机的投影矩阵(内参)

弄清楚这套变换流程对于理解基于 KITTI 的 3D 目标检测算法(如 PointPillars、VoxelNet、PV-RCNN 等)至关重要,同时也是将算法迁移到自有数据集时不可避免的步骤。


参考文献


评论