1. 为什么Waymo数据集是自动驾驶研究的“宝藏”?

如果你刚接触自动驾驶研究,或者想找一个高质量、大规模的数据集来练手,那Waymo Open Dataset绝对是你绕不开的“宝藏”。我刚开始做感知算法那会儿,找数据是个大难题,要么数据量太小,要么场景太单一,模型训出来根本没法用。直到Waymo开放了这个数据集,很多研究才真正跑了起来。

简单来说,Waymo数据集就像是自动驾驶领域的“ImageNet”。它由谷歌旗下的Waymo公司发布,包含了海量的真实道路驾驶数据。这些数据不是模拟出来的,而是他们的自动驾驶车辆在北美多个城市,在各种天气、光照和交通条件下实际采集的。这意味着数据里包含了丰富的、真实的、甚至是“刁钻”的驾驶场景,比如大雨天、夜间、复杂的十字路口等等,这对于训练一个鲁棒的模型至关重要。

这个数据集主要分为几个部分,我们最常用的是感知数据集和运动数据集。感知数据集主要用于目标检测、分割等任务,包含了高分辨率的激光雷达点云和相机图像,以及精确的3D边界框标注。根据官方信息,整个数据集包含了约1200万个激光雷达标注框和约1200万个图像标注框,数据量非常庞大。运动数据集则侧重于行为预测,包含了大量交通参与者的轨迹信息。对于我们今天要重点关注的激光雷达数据可视化来说,主要打交道的就是感知数据集。

拿到这样一份数据,你能做什么呢?如果你是学生,可以用来复现顶会论文,完成自己的毕业设计;如果你是算法工程师,可以基于它开发新的3D检测或分割模型;如果你是研究者,可以利用其丰富的标注和连续帧信息,探索跟踪、预测等更前沿的方向。总之,这是一块极好的“敲门砖”和“试金石”。不过,宝藏虽好,入口却有点隐蔽,接下来我就带你一步步把它挖出来。

2. 手把手教你下载与解压Waymo数据

万事开头难,和Waymo数据集打交道的第一步——下载和解压,就可能会让新手有点懵。别担心,跟着我的步骤走,避开我当年踩过的坑。

首先,你需要访问Waymo Open Dataset的官方网站。这里要注意,访问和下载需要你有一个谷歌账号进行登录,这是官方的要求。登录后,你会看到数据集的下载页面。数据集通常按“片段”组织,每个片段大约20秒,对应一个压缩包(比如 training_0000.tar)。我建议新手先从最小的一个片段开始,比如 training_0000,它大概有20多GB。先别急着全下,把一个文件搞明白了,其他的都是同样的套路。

下载链接通常是通过Google Cloud Storage提供的。如果你在命令行环境下,可以使用 gsutil 工具来下载,速度会比较稳定。例如,你可以用这样的命令:

gsutil cp gs://waymo_open_dataset_v_2_0_0/individual_files/training/training_0000.tar /你的本地路径/

如果是在浏览器中,直接点击下载链接即可,但大文件可能会遇到网络不稳定的问题,准备好一个稳定的网络环境很重要。

下载完成后,你得到一个 .tar 格式的压缩包。在Linux或macOS下,直接用 tar 命令解压:

tar -xvf training_0000.tar

在Windows下,你可以使用像7-Zip这样的工具来解压。解压后,你会发现里面并不是直接可读的图片或文本,而是一个或多个 .tfrecord 文件。以 training_0000 为例,解压后你会得到大约24个 tfrecord 文件。

这里有个关键点:tfrecord 是TensorFlow常用的一种高效数据存储格式,它把多个数据样本(在这里就是每一帧的传感器数据)序列化后打包在一起。所以,你不能直接用图片查看器打开它,必须通过代码来解析。这就像是给你一箱封装好的零件,你需要用专门的工具(代码)才能把它们一个个取出来,组装成你能看懂的样子(比如点云、图像)。这也是为什么处理Waymo数据集必须和编程结合的原因。

3. 搭建你的数据处理环境:Python与关键库安装

工欲善其事,必先利其器。要读取 tfrecord 格式的Waymo数据,你需要配置一个合适的Python环境。官方提供了 waymo-open-dataset 这个Python库来帮助我们。根据我的经验,环境配置是新手遇到的第一个“拦路虎”,版本兼容性问题尤其头疼。

我强烈建议你使用 Anaconda 来创建独立的Python环境,避免污染系统环境,也方便管理不同项目所需的库版本。下面是我在项目中验证过的一个比较稳定的环境配置方案,你可以直接照着做:

  1. 创建并激活Conda环境:

    conda create -n waymo python=3.8 -y
    conda activate waymo
    

    这里选择Python 3.8,是因为它在与TensorFlow等库的兼容性上比较平衡。

  2. 安装TensorFlow和Waymo数据集库: 这是核心的一步。Waymo库有多个版本,对应不同版本的TensorFlow。如果你使用较新的TensorFlow 2.x,可以安装对应的版本。但根据我的实测,为了最大程度兼容官方教程和示例代码,安装针对TF 2.6.0的版本是个稳妥的选择:

    pip install tensorflow==2.6.0
    pip install waymo-open-dataset-tf-2-6-0
    

    这个 waymo-open-dataset-tf-2-6-0 包会自动处理依赖。如果你看到官方文档或其它教程里让你从源码编译,现在基本不需要了,直接用这个pip包最省事。

  3. 安装其他辅助库: 为了后续的数据处理和可视化,我们还需要一些帮手:

    pip install numpy matplotlib opencv-python
    

    numpy 用于数值计算,matplotlib 和 opencv-python 用于画图和图像处理。

注意:如果你在Windows系统上操作,可能会在安装 waymo-open-dataset 库时遇到一些编译依赖的问题。一个可行的解决方案是安装预编译的轮子文件,或者考虑在Windows的WSL2(Windows Subsystem for Linux)子系统中配置环境,这能完美兼容Linux的生态,我很多同事都在用,非常顺畅。

环境配好后,不要急着写代码,先做个简单的测试。在Python交互界面里尝试 import waymo_open_dataset,如果不报错,恭喜你,环境搭建成功了!如果报错,大概率是TensorFlow版本或CUDA驱动(如果你用GPU)的问题,需要根据错误信息去搜索解决。

4. 庖丁解牛:读取tfrecord中的激光雷达数据

环境准备好了,我们终于可以动手“拆解”数据了。前面说到,数据存在 tfrecord 文件里,我们需要用Waymo提供的API把它读出来。这个过程有点像用钥匙打开宝箱。

首先,你需要理解Waymo数据的基本组织单元:帧。一个 tfrecord 文件包含一段连续驾驶记录(约20秒),而这段记录又由许多连续的帧组成,每秒大概有10帧。每一帧,都包含了那个瞬间车辆上所有传感器的“快照”:5个激光雷达的点云、多个相机的图像、以及这些数据对应的标注信息。

让我们写一段最基础的代码,打开一个 tfrecord 文件,并读取第一帧的激光雷达数据:

import tensorflow as tf
from waymo_open_dataset import dataset_pb2 as open_dataset

# 1. 指定你的tfrecord文件路径
FILENAME = './path/to/your/segment-xxx.tfrecord'

# 2. 创建一个TFRecordDataset读取器
dataset = tf.data.TFRecordDataset(FILENAME, compression_type='')

# 3. 遍历数据集(这里我们先取第一帧)
for i, data in enumerate(dataset):
    if i >= 1:  # 只看第一帧
        break
        
    # 4. 解析帧数据
    frame = open_dataset.Frame()
    frame.ParseFromString(data.numpy())  # 这是关键的解码步骤
    
    # 5. 打印一些基本信息
    print(f'场景名称: {frame.context.name}')
    print(f'时间戳: {frame.timestamp_micros}')
    print(f'本帧是这段记录中的第 {frame.images[0].pose_timestamp} 帧?等等,我们需要看正确的属性')
    # 实际上,frame.images[0].pose_timestamp 可能不是帧索引,这里只是示例
    
    # 6. 获取激光雷达数据
    # Waymo车辆有多个激光雷达,我们通常最关心顶部的(TOP)
    for laser in frame.lasers:
        # 激光雷达类型:1是顶部(TOP),2是前(FRONT),等等
        if laser.name == open_dataset.LaserName.TOP:
            # 获取范围图像(Range Image)
            ri = laser.ri_return1  # 第一次回波,通常包含最强的反射信号
            # 范围图像是编码后的数据,需要解码才能得到三维点云
            print(f'顶部激光雷达范围图像形状: {ri.shape}')
            break

运行这段代码,你可能会看到输出了场景名、时间戳,以及一个形状类似 (64, 2650, 4) 的数组。这个 (64, 2650, 4) 就是范围图像。它是Waymo存储激光雷达数据的一种紧凑形式,你可以把它想象成一张特殊的“深度图”:64行代表激光雷达的垂直扫描线(64线激光雷达),2650列代表水平方向上的采样点,每个点有4个值(距离、强度、x方向偏移、y方向偏移)。

仅仅拿到范围图像还不够,我们最终需要的是三维空间中的点云坐标 (x, y, z)。这就需要调用Waymo库提供的解码函数进行转换。这个转换过程涉及激光雷达的内参(如光束仰角、水平角偏移等),官方库已经帮我们封装好了。解码后的点云,才是我们做可视化、做算法输入的标准格式。

5. 从抽象数据到三维世界:激光雷达点云可视化实战

数据读出来了,也解码成点云了,但如果不能“看见”它,一切还是抽象的。可视化是理解数据最直观的方式,也是检查数据处理是否正确的重要手段。这里我给你介绍两种最常用的点云可视化方法:使用 Matplotlib 的简单3D散点图,和使用专业工具 Open3D 的交互式查看。

方法一:用Matplotlib快速预览

Matplotlib适合快速查看点云的概貌和分布,比如从俯视图(鸟瞰图)看车辆和障碍物的位置。它的优点是集成在Python环境中,无需额外安装复杂工具。

import numpy as np
import matplotlib.pyplot as plt
from waymo_open_dataset.utils import frame_utils

# 接续上一节的代码,假设我们已经得到了frame对象
def parse_lidar_data(frame):
    """解析一帧数据,返回所有激光雷达点云和对应的强度"""
    range_images, camera_projections, range_image_top_pose = frame_utils.parse_range_image_and_camera_projection(frame)
    
    # 将范围图像转换为点云
    points, cp_points = frame_utils.convert_range_image_to_point_cloud(
        frame,
        range_images,
        camera_projections,
        range_image_top_pose
    )
    
    #  points是一个列表,包含5个激光雷达的点云(TOP, FRONT, SIDE_LEFT, SIDE_RIGHT, REAR)
    #  我们通常把5个雷达的点云合并起来,得到车辆周围完整的360度视图
    points_all = np.concatenate(points, axis=0)
    #  points_all的形状是 (N, 3), N是点的总数,3是x, y, z坐标
    
    # 同时,我们也可以获取反射强度
    range_images_cartesian = frame_utils.convert_range_image_to_cartesian(
        frame, range_images, range_image_top_pose
    )
    # 这里需要从range_images_cartesian中提取强度信息,稍微复杂一些,我们先关注坐标
    return points_all

points_3d = parse_lidar_data(frame)

# 绘制俯视图(Bird‘s Eye View)
plt.figure(figsize=(10, 10))
# 只取x和y坐标,z坐标是高度
plt.scatter(points_3d[:, 0], points_3d[:, 1], s=0.01, c='black', alpha=0.5)  # s是点大小,调小些避免重叠
plt.gca().set_aspect('equal', adjustable='box')
plt.xlabel('X (米)')
plt.ylabel('Y (米)')
plt.title('激光雷达点云俯视图')
plt.grid(True)
plt.show()

这段代码会生成一张黑白的俯视图,你可以清晰地看到道路的轮廓、旁边的车辆(一个个矩形的点簇)、以及远处的建筑物。这是自动驾驶感知算法中最常用的视角之一。

方法二:用Open3D进行交互式3D查看

如果你想更真实、更沉浸地观察点云,比如看看立体的建筑物、树木,甚至点云的颜色(如果融合了相机信息),那么Open3D是更好的选择。它可以让你用鼠标旋转、缩放三维场景。

import open3d as o3d

# 创建Open3D点云对象
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points_3d)

# 我们可以给点云上色,例如根据高度(z坐标)上色,这样地形起伏一目了然
# 先将z坐标归一化到0-1之间,然后映射到颜色
z_min, z_max = points_3d[:, 2].min(), points_3d[:, 2].max()
colors = plt.cm.viridis((points_3d[:, 2] - z_min) / (z_max - z_min))[:, :3]  # 使用viridis配色,只取RGB
pcd.colors = o3d.utility.Vector3dVector(colors)

# 可视化
o3d.visualization.draw_geometries([pcd],
                                  window_name='Waymo LiDAR Point Cloud',
                                  width=1024,
                                  height=768,
                                  point_show_normal=False)

运行这段代码,会弹出一个独立的窗口,里面就是三维点云。你可以用鼠标左键拖拽旋转场景,用滚轮缩放,用右键拖拽平移。这种直观的观察,能帮你迅速建立对场景的空间理解,检查点云质量(比如是否有异常的噪点),这是静态图片无法比拟的体验。

6. 进阶技巧:融合视觉与激光雷达数据

单独看激光雷达点云,我们知道了物体的形状和位置,但不知道它是什么(是车、是人还是树)。而相机图像提供了丰富的纹理和颜色信息,却缺乏精确的距离。将两者融合,就能得到既有颜色又有深度的“彩色点云”,这对于很多高级任务(如基于视觉的3D检测、场景理解)至关重要。

Waymo数据集的精妙之处在于,它已经为每一帧同步提供了多视角相机图像和激光雷达点云,并且提供了精确的标定参数(包括每个相机和激光雷达之间的相对位置和姿态,以及相机自身的内部参数)。利用这些参数,我们可以把三维激光雷达点投影到二维图像上,看看每个激光点对应到图像的哪个像素。

import cv2

# 假设我们已经从frame中提取了前向相机(CAM_FRONT)的图像
for image in frame.images:
    if image.name == open_dataset.CameraName.FRONT:
        # 解码JPEG图像数据
        img = cv2.imdecode(np.frombuffer(image.image, np.uint8), cv2.IMREAD_COLOR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # OpenCV默认是BGR,转为RGB
        break

# 获取相机标定参数(内参和相对于车体的外参)
# 这里需要用到frame.context.camera_calibrations和frame.pose
# 具体投影计算涉及坐标系变换,Waymo库提供了工具函数,但过程稍复杂

# 一个简化的思路示例(伪代码):
# 1. 获取激光雷达点到车体坐标系的转换。
# 2. 获取车体到全局坐标系的转换(frame.pose)。
# 3. 获取全局坐标系到相机坐标系的转换(通过标定参数)。
# 4. 利用相机内参,将相机坐标系下的3D点投影到2D图像像素坐标。

# 实际上,waymo_open_dataset.utils.camera_segmentation_utils等模块有现成的投影函数
# 例如:project_points_to_image(points, calibration)

完成投影后,你可以做一件很酷的事情:为之前的Open3D点云着色。根据每个激光点在图像上对应像素的颜色,来给该点赋予RGB值。这样,你在Open3D中看到的就不再是单调的根据高度上色的点云,而是真实的彩色点云——绿色的树木、红色的车辆、灰色的路面,场景瞬间变得鲜活起来。这个融合过程是自动驾驶感知系统的核心基础,自己动手实现一遍,对理解传感器标定、坐标系变换等概念有极大的帮助。

7. 避坑指南与高效工作流建议

走完整个流程,你可能会遇到一些我当年也头疼的问题。这里分享几个常见的“坑”和我的解决经验,希望能帮你节省时间。

坑1:内存不足。 Waymo一帧的点云可能就有几十万个点,一个片段有近200帧。如果你一次性把所有数据读入内存,很容易爆掉。我的建议是使用迭代器的方式,一帧一帧地处理。tf.data.TFRecordDataset 本身就是一个高效的流水线,配合 for 循环逐帧处理,而不是用 list(dataset) 全部加载。

坑2:速度慢。 数据解码和点云转换可能是瓶颈。确保你安装的 waymo-open-dataset 库是预编译版本(pip安装的通常是),这比从源码安装快很多。另外,对于可视化预览,可以对点云进行下采样,比如每隔10个点取一个,这样能大幅提升渲染速度,又不影响整体观察。

坑3:坐标系混乱。 Waymo数据涉及多个坐标系:激光雷达自身坐标系、车体坐标系、全局坐标系、各个相机坐标系。在融合或计算时,一定要清楚你当前的点云在哪个坐标系下,需要转换到哪个坐标系。官方proto定义和工具函数里通常会有说明,仔细阅读文档,画个坐标系转换图会清晰很多。

高效工作流建议:

  1. 建立数据索引:写一个小脚本,遍历你的数据集文件夹,记录下每个 tfrecord 文件的路径、包含的帧数等信息,保存成一个JSON或CSV文件。以后想找特定场景或时间段的数据,直接查这个索引文件,不用再遍历所有文件。
  2. 封装工具函数:把读取帧、解析激光雷达、解析图像、坐标投影这些常用操作,封装成你自己的工具函数库。比如 load_frame(file_path, frame_index)、get_lidar_points(frame)、project_to_camera(points, camera_name)。这样后续做任何实验,代码都会非常简洁。
  3. 使用Jupyter Notebook进行探索:对于数据可视化和初步分析,Jupyter Notebook是无敌的。你可以把下载、解析、可视化的代码放在不同的Cell里,逐步执行,即时看到结果和图表,方便调试和记录思路。
  4. 考虑使用MMDetection3D等高级框架:如果你的目标不是从头造轮子,而是快速训练模型,那么可以考虑OpenMMLab的MMDetection3D这类工具箱。它们提供了将Waymo数据集转换为标准格式(如KITTI格式)的脚本,并集成了数据加载、模型训练、评估的全流程。这能让你跳过底层数据处理,直接聚焦在算法模型上。当然,前提是你已经通过本文的实践,理解了数据的本质。

处理Waymo数据集,从下载到看到绚丽的彩色点云,这个过程本身就是一个很好的学习项目。它强迫你去理解数据格式、传感器原理、坐标系变换和可视化工具。当你第一次成功地把激光雷达点云和相机图像完美对齐时,那种成就感会让你觉得前面所有的折腾都是值得的。希望这篇详细的指南能成为你探索自动驾驶世界的一块坚实垫脚石。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐