KITTI 与 Waymo Open Dataset 2024:自动驾驶点云数据格式转换与预处理实战
KITTI 与 Waymo Open Dataset 2024:自动驾驶点云数据格式转换与预处理实战
自动驾驶技术的快速发展离不开高质量的数据集支持。作为该领域的两大标杆,KITTI和Waymo Open Dataset在2024年都进行了重要更新,为研究者提供了更丰富、更精确的感知数据。本文将深入探讨这两大数据集的最新特性,并手把手教你完成从数据下载到预处理的全流程操作。
1. 数据集概览与获取
2024年,KITTI和Waymo Open Dataset都迎来了重大版本更新。KITTI 2024版新增了夜间场景和极端天气条件下的采集数据,点云采样频率提升至20Hz,同时改进了标注精度。Waymo则开放了更多城市道路数据,单个场景的连续采集时长从20秒延长至60秒,显著提升了时序分析的价值。
数据集下载步骤:
- KITTI数据集获取
wget https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2024_kitty_raw_data.zip
unzip 2024_kitty_raw_data.zip -d ./kitti_data
- Waymo数据集获取
import waymo_open_dataset
dataset = waymo_open_dataset.WaymoOpenDataset()
dataset.download_and_prepare('waymo_data/')
关键差异对比:
| 特性 | KITTI 2024 | Waymo Open Dataset 2024 |
|---|---|---|
| 数据采集频率 | 20Hz | 10Hz |
| 单场景时长 | 15-30秒 | 60秒 |
| 标注类型 | 3D框+类别 | 3D框+类别+运动轨迹 |
| 天气条件 | 新增雨雾场景 | 多种光照条件 |
| 数据量 | 50小时 | 2000小时 |
2. 数据解析核心技术
2.1 KITTI数据解析
KITTI采用传统的二进制存储格式,每个点云文件包含约12万点。2024版改进了时间同步机制,使得激光雷达与摄像头数据对齐更精确。
Python解析示例:
import numpy as np
def read_kitti_bin(bin_path):
"""读取KITTI二进制点云文件"""
points = np.fromfile(bin_path, dtype=np.float32).reshape(-1, 4)
# 第4列为反射强度
return points[:, :3], points[:, 3]
# 使用示例
points, intensity = read_kitti_bin('kitti_data/000000.bin')
2.2 Waymo数据解析
Waymo使用TFRecord格式存储数据,单个文件包含完整的场景信息。2024版新增了动态物体轨迹预测标注。
关键解析代码:
import tensorflow as tf
from waymo_open_dataset import dataset_pb2
def parse_waymo_tfrecord(tfrecord_path):
dataset = tf.data.TFRecordDataset(tfrecord_path)
for data in dataset:
frame = dataset_pb2.Frame()
frame.ParseFromString(data.numpy())
yield frame
# 提取点云数据
for frame in parse_waymo_tfrecord('waymo_data/segment-123.tfrecord'):
lidar_data = frame.lidar_data[0] # 获取第一个激光雷达数据
points = np.column_stack([
lidar_data.range_image_returns[0].data,
lidar_data.range_image_returns[0].data
])
3. 格式转换实战
实际项目中经常需要统一数据格式。下面展示将KITTI转换为通用PLY格式,以及Waymo到KITTI格式的转换方法。
3.1 KITTI转PLY格式
def kitti_to_ply(bin_path, ply_path):
points, intensity = read_kitti_bin(bin_path)
header = f"""ply
format ascii 1.0
element vertex {len(points)}
property float x
property float y
property float z
property float intensity
end_header
"""
with open(ply_path, 'w') as f:
f.write(header)
for pt, i in zip(points, intensity):
f.write(f"{pt[0]} {pt[1]} {pt[2]} {i}\n")
# 转换示例
kitti_to_ply('kitti_data/000000.bin', 'output/000000.ply')
3.2 Waymo转KITTI格式
def waymo_to_kitti(frame, output_dir, frame_id):
"""将Waymo帧转换为KITTI格式"""
# 提取点云
lidar = frame.lidar_data[0]
points = np.array([[p.x, p.y, p.z, p.intensity]
for p in lidar.returns])
# 保存点云
points.astype(np.float32).tofile(f'{output_dir}/{frame_id:06d}.bin')
# 保存标注
with open(f'{output_dir}/{frame_id:06d}.txt', 'w') as f:
for label in frame.laser_labels:
f.write(f"{label.type} 0 0 0 "
f"{label.box.center_x} {label.box.center_y} {label.box.center_z} "
f"{label.box.length} {label.box.width} {label.box.height} "
f"{label.box.heading} 1\n")
4. 预处理关键技术与常见问题
4.1 点云滤波与降采样
高质量预处理能显著提升模型性能。以下是基于体素网格的降采样方法:
from sklearn.neighbors import KDTree
def voxel_downsample(points, voxel_size=0.1):
"""体素网格降采样"""
voxel_grid = {}
for point in points:
voxel = tuple((point // voxel_size).astype(int))
if voxel not in voxel_grid:
voxel_grid[voxel] = []
voxel_grid[voxel].append(point)
# 取每个体素中心点
downsampled = []
for voxel in voxel_grid.values():
downsampled.append(np.mean(voxel, axis=0))
return np.array(downsampled)
4.2 数据增强技巧
典型增强操作:
- 随机旋转(±10度)
- 随机平移(±0.5米)
- 随机缩放(0.9-1.1倍)
- 点丢弃(随机丢弃5%点)
def augment_pointcloud(points):
# 随机旋转
angle = np.random.uniform(-10, 10)
rot_mat = np.array([
[np.cos(angle), -np.sin(angle), 0],
[np.sin(angle), np.cos(angle), 0],
[0, 0, 1]
])
points = points @ rot_mat
# 随机平移
points += np.random.uniform(-0.5, 0.5, 3)
# 随机缩放
points *= np.random.uniform(0.9, 1.1)
# 随机丢弃
if np.random.rand() < 0.05:
mask = np.random.rand(len(points)) > 0.05
points = points[mask]
return points
4.3 常见问题排查
问题1:Waymo数据加载缓慢
解决方案:使用TFRecord的并行加载功能
dataset = tf.data.TFRecordDataset(tfrecord_path)
dataset = dataset.prefetch(buffer_size=10) # 预加载10个样本
dataset = dataset.map(parse_function, num_parallel_calls=4)
问题2:KITTI点云坐标不一致
注意:KITTI使用相机坐标系(x向前,y向左,z向上),而Waymo使用激光雷达坐标系。转换关系:
def kitti_to_waymo_coords(points):
"""KITTI坐标转Waymo坐标"""
# x->x, y->-z, z->y
return np.column_stack([points[:,0], points[:,2], -points[:,1]])
问题3:内存不足
处理大型Waymo文件时,建议使用生成器逐帧处理:
def process_large_waymo_file(tfrecord_path):
dataset = tf.data.TFRecordDataset(tfrecord_path)
for data in dataset:
frame = dataset_pb2.Frame()
frame.ParseFromString(data.numpy())
yield process_frame(frame) # 逐帧处理
5. 实战案例:构建统一数据处理流水线
最后,我们整合上述技术构建完整的处理流水线:
class UnifiedDataPipeline:
def __init__(self, config):
self.voxel_size = config.get('voxel_size', 0.1)
self.augment = config.get('augment', False)
def process_kitti(self, bin_path):
points, intensity = read_kitti_bin(bin_path)
points = voxel_downsample(points, self.voxel_size)
if self.augment:
points = augment_pointcloud(points)
return points
def process_waymo(self, frame):
points = extract_waymo_points(frame)
points = voxel_downsample(points, self.voxel_size)
if self.augment:
points = augment_pointcloud(points)
return points
def run(self, input_path, output_path):
if input_path.endswith('.bin'): # KITTI格式
points = self.process_kitti(input_path)
np.save(output_path, points)
elif input_path.endswith('.tfrecord'): # Waymo格式
for i, frame in enumerate(parse_waymo_tfrecord(input_path)):
points = self.process_waymo(frame)
np.save(f'{output_path}_{i:04d}.npy', points)
在实际项目中,这套处理流程可以帮助我们快速统一不同来源的数据,为后续的模型训练打下坚实基础。根据具体任务需求,还可以添加更多定制化的预处理步骤,如地面去除、点云分割等操作。
更多推荐
所有评论(0)