用rviz深度解析KITTI数据集:从bag文件提取点云数据的实战进阶指南

如果你正在自动驾驶或机器人感知领域深耕,手头大概率已经积累了不少KITTI数据集。这个被誉为行业“基准测试集”的宝库,包含了丰富的激光雷达点云、图像和IMU数据。但很多时候,我们拿到的可能只是一堆原始的.bin文件或转换后的ROS bag文件,如何高效地“打开”并深入分析这些数据,就成了第一个拦路虎。尤其是当你需要针对特定传感器(比如Livox雷达)进行定制化可视化分析时,仅仅会播放bag文件是远远不够的。

这篇文章,就是为你准备的。我们不打算复述那些基础的“三步打开法”,而是聚焦于一个更深入、更具实操性的目标:如何利用rviz,对KITTI数据集的bag文件进行深度解析与点云数据提取,并针对不同传感器特性进行高级配置与可视化分析。 我会结合自己处理多个自动驾驶项目数据流的经验,分享从环境准备、数据探查、可视化配置到数据提取与后处理的完整工作流,其中会特别涵盖Livox雷达topic的配置技巧,以及一些能极大提升效率的“骚操作”。

1. 环境搭建与KITTI数据准备:超越基础安装

在开始任何可视化之前,一个稳定且功能完备的ROS环境是基石。对于KITTI数据集,我们通常需要在ROS中处理sensor_msgs/PointCloud2类型的点云消息。假设你已经在Ubuntu系统上安装了ROS(Melodic或Noetic版本),那么还需要一些额外的工具包。

首先,确保安装了ros-<distro>-velodyne-pointcloudros-<distro>-pcl-ros这类点云处理相关的包。对于KITTI数据,一个非常实用的工具是kitti2bag,它能够将原始的KITTI数据格式转换为ROS bag文件。

# 安装kitti2bag(假设使用Python3和pip)
pip install kitti2bag
# 或者从源码安装
git clone https://github.com/ethz-asl/kitti_to_rosbag.git
cd kitti_to_rosbag
pip install -e .

使用kitti2bag转换数据非常简单。你需要下载官方的KITTI原始数据集(例如2011_09_26_drive_0001_sync),其中包含image_00, velodyne_points, oxts等文件夹。

# 进入包含`calib`、`image_00`、`velodyne_points`等文件夹的父目录
kitti2bag -t 2011_09_26 -r 0001 raw_synced

这条命令会生成一个名为kitti_2011_09_26_drive_0001_sync.bag的文件。转换过程中,有几个关键点需要注意:

  • 时间戳同步raw_synced参数确保图像、点云和IMU数据的时间戳已经过同步处理。这是后续进行多传感器融合分析的前提。
  • Topic映射:转换后,点云数据通常会发布在/kitti/velo/pointcloud这个topic上,图像数据则在/kitti/camera_gray_left/image_raw等topic。使用rosbag info命令可以精确查看。
  • 坐标系:KITTI数据有自己的坐标系定义(相机坐标系、Velodyne坐标系),转换工具会将其映射到ROS中,例如velodyne坐标系。理解这一点对后续在rviz中正确显示至关重要。

注意:不同版本的kitti2bag或其它转换工具(如kitti2rosbag)生成的topic名称和坐标系可能略有不同。第一步永远是先用rosbag inforostopic list命令摸清bag文件的“家底”。

2. 深度探索bag文件:信息挖掘与预处理

拿到一个bag文件,别急着用rviz打开。先花几分钟进行深度探索,能避免后续很多莫名其妙的显示问题。

使用rosbag info进行宏观分析: 在终端中运行rosbag info your_kitti.bag,你会得到类似下面的信息摘要:

path:        your_kitti.bag
version:     2.0
duration:    1:02s (62s)
start:       Sep 26 2011 09:00:00.00 (1317020400.00)
end:         Sep 26 2011 09:01:02.00 (1317020462.00)
size:        4.2 GB
messages:    12401
compression: none [11/11 chunks]
types:       sensor_msgs/Image      [060021388200f6f0f447d0fcd9c64743]
             sensor_msgs/PointCloud2 [1158d486dd51d683ce2f1be655c3c181]
topics:      /kitti/camera_color_left/image_raw   3721 msgs    : sensor_msgs/Image
             /kitti/camera_color_right/image_raw  3721 msgs    : sensor_msgs/Image
             /kitti/velo/pointcloud               1240 msgs    : sensor_msgs/PointCloud2

这张“体检表”告诉我们:bag文件时长、大小、包含哪些topic、每种消息的类型和数量。例如,点云topic是/kitti/velo/pointcloud,共1240帧,这意味着帧率大约在20Hz左右。

使用rqt_bag进行微观探查rosbag info给了我们骨架,rqt_bag则能让我们看到血肉。它是一个图形化工具,可以按时间轴查看所有topic的消息流。

# 在一个终端启动roscore后,另一个终端运行
rqt_bag your_kitti.bag

rqt_bag界面中,你可以:

  1. 观察消息时序:查看点云、图像、IMU消息的发布是否对齐,是否存在丢帧或时间戳跳跃。
  2. 预览图像:直接点击图像topic的消息,可以在下方预览图像内容,快速检查数据质量。
  3. 查看消息内容:点击点云消息,可以查看其header(包含时间戳和坐标系信息)、height、width、fields(点云的字段,如x, y, z, intensity)等原始数据。这对于理解数据结构非常有帮助。

针对Livox雷达bag文件的特殊探查: 如果你拿到的是Livox雷达录制的bag文件(例如通过Livox SDK和ROS驱动录制),其topic命名和消息结构与标准的Velodyne KITTI数据有所不同。典型的Livox ROS驱动会发布如下topic:

Topic名称消息类型说明
/livox/lidarlivox_ros_driver/CustomMsgLivox自定义格式的点云消息,包含更多信息如tag、line_id等。
/livox/imusensor_msgs/ImuIMU数据。
可能还有 /livox/lidar_pointcloudsensor_msgs/PointCloud2驱动转换后的标准ROS点云消息。

对于Livox的CustomMsg,你需要使用rosmsg show livox_ros_driver/CustomMsg来查看其详细结构,或者使用rostopic echo /livox/lidar | head -n 50来查看实际数据。关键点在于:rviz默认支持的是sensor_msgs/PointCloud2。如果bag里只有CustomMsg,你需要确保有节点(通常是Livox驱动本身)将其转换为PointCloud2,或者你需要在播放bag时同时启动一个转换节点。

3. rviz高级配置与点云可视化实战

终于到了核心环节——用rviz让点云“活”过来。这里我们分通用配置和针对Livox的特定配置来讲解。

基础配置与坐标系校正

  1. 启动roscore和rviz:
    # 终端1
    roscore
    # 终端2
    rviz
    
  2. 添加PointCloud2显示类型:点击左下角Add,选择By topicBy display type,找到sensor_msgs/PointCloud2并添加。
  3. 解决最常见的“No transform”问题:添加点云后,如果rviz窗口一片空白,并提示“No transform from [frame_id] to [Fixed Frame]”,这说明坐标系没对上。你需要:
    • 在rviz左侧Global Options中,将Fixed Frame修改为点云消息的header.frame_id。对于KITTI转换的bag,通常是velodynebase_link。对于Livox,可能是livox_framelaser
    • 如果修改后仍不显示,可能是tf静态变换缺失。对于简单的可视化,你可以直接添加一个TF显示类型,查看坐标系树,或者使用static_transform_publisher发布一个静态变换(例如,假设雷达坐标系就是世界坐标系):
      rosrun tf static_transform_publisher 0 0 0 0 0 0 1 map velodyne 100
      
      这条命令每100ms发布一次从mapvelodyne的静态变换(位置和旋转均为0)。在rviz中,将Fixed Frame设为map,点云就能显示了。

点云显示样式与属性调节: 在PointCloud2的属性面板中,有几个关键设置能极大影响可视化效果:

  • StylePoints(点)、Squares(方形)、Flat Squares(扁平方形)。对于高密度点云,Points最常用。
  • Size (m):每个点在屏幕上显示的大小。KITTI点云通常设置为0.05-0.1。
  • Color Transformer:这是分析点云的关键。你可以根据不同的字段为点云着色:
    • Axis Color:固定颜色。
    • Intensity:根据点云的强度(反射率)着色,KITTI的Velodyne数据包含此信息。这能帮助你区分不同材质的物体(如车道线反射强度高)。
    • RGB:如果点云类型包含RGB字段(如某些彩色激光雷达)。
    • Z(高度):根据点的Z坐标(高度)着色,非常适合观察地形起伏和物体高度。 通过切换Color Transformer并观察Color Map(如Rainbow)的映射,可以从不同维度理解点云数据。

针对Livox雷达的配置技巧: Livox雷达(如Mid-40, Horizon)因其非重复扫描模式而闻名,这带来了更快的点云填充率,但在可视化时也可能需要特殊处理。

  1. Topic选择:在rviz的PointCloud2显示属性中,将Topic设置为Livox驱动发布的PointCloud2格式的topic,例如/livox/lidar_pointcloud避免直接使用/livox/lidar(CustomMsg),除非你安装了支持该消息的rviz插件。
  2. 去畸变显示(可选):对于运动平台采集的数据,点云可能存在运动畸变。一些高级的Livox驱动或后处理节点会发布经过去畸变处理的点云topic,例如/livox/undistort_pointcloud。在分析定位建图效果时,优先订阅这个topic。
  3. 利用CustomMsg中的额外信息(进阶):如果你想利用CustomMsg中的tag(点类型,如正常点、噪声点)或line_id(激光线号)来过滤或着色点云,你需要编写一个简单的ROS节点,订阅/livox/lidar,将这些信息转换为PointCloud2fields(例如,将tag存入intensity字段),然后发布一个新的topic供rviz使用。

下面是一个简化的Python节点示例,将Livox CustomMsg的tag信息映射到点云强度:

#!/usr/bin/env python
import rospy
from livox_ros_driver.msg import CustomMsg
from sensor_msgs.msg import PointCloud2, PointField
import sensor_msgs.point_cloud2 as pc2

def livox_callback(custom_msg):
    points = []
    for point in custom_msg.points:
        # 将x, y, z, intensity(这里用tag代替), tag 组成一个点
        # tag通常为uint8,可以缩放后放入float32的intensity字段
        points.append([point.x, point.y, point.z, float(point.tag)])
    # 创建PointCloud2消息
    header = custom_msg.header
    fields = [
        PointField('x', 0, PointField.FLOAT32, 1),
        PointField('y', 4, PointField.FLOAT32, 1),
        PointField('z', 8, PointField.FLOAT32, 1),
        PointField('intensity', 12, PointField.FLOAT32, 1),
    ]
    pc2_msg = pc2.create_cloud(header, fields, points)
    pub.publish(pc2_msg)

if __name__ == '__main__':
    rospy.init_node('livox_tag_to_intensity')
    sub = rospy.Subscriber('/livox/lidar', CustomMsg, livox_callback)
    pub = rospy.Publisher('/livox/pointcloud_with_tag', PointCloud2, queue_size=10)
    rospy.spin()

运行此节点后,在rviz中订阅/livox/pointcloud_with_tag,并将Color Transformer设为Intensity,就能根据点的tag(如区分正常点和噪声点)进行着色分析了。

4. 从bag文件中提取与保存点云数据

可视化是为了分析和调试,而提取数据是为了后续的算法处理、训练或存档。这里介绍几种从bag文件中提取点云数据的方法。

方法一:使用rosbag record录制特定topic 这是最简单直接的方法,尤其适用于从一个大bag文件中提取部分感兴趣时间段的数据。

# 提取特定topic的点云数据到新的bag文件
rosbag record -O extracted_points.bag /kitti/velo/pointcloud
# 在另一个终端播放原bag文件
rosbag play original_kitti.bag
# 录制完成后,在第一个终端按Ctrl+C停止录制

方法二:使用Python脚本编程提取 这种方法最灵活,可以将点云数据保存为各种格式(如PCD, PLY, CSV),并方便地进行过滤、降采样等处理。以下是一个将bag中点云保存为PCD文件的完整脚本示例:

#!/usr/bin/env python
import rosbag
import sensor_msgs.point_cloud2 as pc2
import pcl  # 需要安装python-pcl或open3d,这里以pcl为例
import os
import argparse

def extract_pointclouds(bag_file, topic, output_dir, save_interval=1):
    """
    从bag文件中提取指定topic的点云,并保存为PCD文件。
    :param bag_file: 输入的bag文件路径
    :param topic: 点云topic名称
    :param output_dir: 输出PCD文件的目录
    :param save_interval: 保存间隔(帧数),1表示每帧都保存
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    bag = rosbag.Bag(bag_file, 'r')
    count = 0
    saved_count = 0

    for topic, msg, t in bag.read_messages(topics=[topic]):
        if count % save_interval != 0:
            count += 1
            continue

        # 从PointCloud2消息中生成点列表
        points = list(pc2.read_points(msg, field_names=("x", "y", "z", "intensity"), skip_nans=True))

        if not points:
            print(f"Frame {count} has no points.")
            count += 1
            continue

        # 转换为pcl点云对象 (这里假设使用python-pcl)
        # 注意:python-pcl的安装可能较麻烦,也可使用numpy保存为txt或使用open3d
        cloud = pcl.PointCloud_PointXYZI()
        cloud.from_list(points)  # 需要将points转换为适合的格式

        # 生成文件名,使用时间戳保证唯一性
        timestamp = msg.header.stamp.to_nsec()
        output_path = os.path.join(output_dir, f"frame_{saved_count:06d}_{timestamp}.pcd")
        pcl.save(cloud, output_path, format='pcd')

        print(f"Saved frame {count} to {output_path}")
        saved_count += 1
        count += 1

    bag.close()
    print(f"Extraction complete. Total frames processed: {count}, saved: {saved_count}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Extract point clouds from ROS bag.')
    parser.add_argument('bag_file', help='Path to the input ROS bag file.')
    parser.add_argument('--topic', default='/kitti/velo/pointcloud', help='PointCloud topic name.')
    parser.add_argument('--output_dir', default='./extracted_pcds', help='Output directory for PCD files.')
    parser.add_argument('--interval', type=int, default=1, help='Save every N frames.')
    args = parser.parse_args()

    extract_pointclouds(args.bag_file, args.topic, args.output_dir, args.interval)

方法三:使用rosrun pcl_ros bag_to_pcd工具 如果你的系统安装了pcl_ros,有一个现成的命令行工具可以使用:

# 将bag中的点云topic转换为PCD文件序列
rosrun pcl_ros bag_to_pcd <input_file.bag> <pointcloud_topic> <output_directory>
# 示例
rosrun pcl_ros bag_to_pcd kitti_2011_09_26_drive_0001_sync.bag /kitti/velo/pointcloud ./pcd_output

这个工具会自动按时间戳生成一系列PCD文件。不过,它的自定义选项较少,比如无法方便地过滤字段或进行降采样。

提取后的数据处理建议: 提取出的点云文件(尤其是PCD序列)可以方便地用于:

  • 离线算法测试:在不需要ROS环境的情况下,用Open3D、PCL库直接读取和处理。
  • 数据标注:导入到标注工具中,进行3D bounding box标注。
  • 深度学习:转换为KITTI的.bin格式或自定义格式,用于训练点云检测、分割网络。
  • 统计分析:计算点云密度、分布、强度直方图等。

5. 进阶技巧与故障排查

在实际操作中,你可能会遇到一些棘手的情况。这里分享几个我踩过坑后总结的进阶技巧。

高效播放与循环播放

  • rosbag play -r 2.0 your_bag.bag:以2倍速播放,快速跳过不关心的段落。
  • rosbag play -l your_bag.bag:循环播放,非常适合反复观察某一段场景。
  • rosbag play -s 15 your_bag.bag:从第15秒开始播放。
  • rosbag play -u 30 your_bag.bag:只播放前30秒。

rviz显示性能优化: 当点云数据量极大(如128线激光雷达)时,rviz可能会卡顿。

  1. 降低显示频率:在PointCloud2属性中,调整Decay Time。设置为一个较小的值(如0.1秒),rviz不会累积显示历史点云,能显著提升性能。
  2. 使用VoxelGrid过滤器:在rviz中添加一个VoxelGrid类型的过滤器(在PointCloud2属性的Filters列表中添加)。设置一个合理的叶子大小(Leaf Size),例如0.1m,可以在保持场景形状的同时大幅减少显示的点数。
  3. 按距离裁剪:添加PassThrough过滤器,设置Z轴(或XY轴)的范围,只显示特定距离内的点云。

处理时间戳问题: 有时播放bag时,rviz会提示时间戳在未来的警告,或者/clock topic有问题。可以尝试:

  • rosbag play --clock your_bag.bag:使用bag文件内部的时间戳来模拟ROS时间。
  • 在rviz的Global Options中,将Fixed Frame设置为一个不依赖于tf变换的坐标系(如mapodom),并确保有对应的静态变换发布。

多传感器数据同步可视化: KITTI bag通常包含图像和点云。你可以在rviz中同时显示:

  1. 添加Image显示类型,订阅对应的相机topic(如/kitti/camera_color_left/image_raw)。
  2. 调整ImageImage TopicTransport(通常为raw)。
  3. 为了便于对比,你可以将Image显示的面板拖拽到主窗口,与点云并列显示。通过同步播放bag,可以直观地分析相机与激光雷达的感知结果。

当点云显示为“扁平的墙”时: 如果点云看起来像一堵垂直的墙,而不是立体的场景,很可能是坐标系设置错误。检查并确保:

  1. Fixed Frame设置正确。
  2. 点云消息的frame_idFixed Frame一致,或者存在正确的tf变换。
  3. PointCloud2属性中,检查PositionOrientation是否被意外修改(应均为0)。

处理KITTI或各类激光雷达bag文件,核心在于理解数据流、坐标系和工具链。从基础的播放查看,到深入的数据提取与定制化分析,每一步都藏着提升效率的细节。rviz不仅仅是一个查看器,结合ROS丰富的命令行工具和脚本能力,它能成为一个强大的数据分析和算法调试环境。尤其是在处理像Livox这样有自己特点的传感器数据时,多花点时间在前期探索和配置上,后期算法开发阶段就能省下大量排查数据问题的时间。

Logo

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

更多推荐