用rviz分析KITTI数据集实战:从bag文件提取点云数据的完整流程
用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-pointcloud和ros-<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 info和rostopic 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界面中,你可以:
- 观察消息时序:查看点云、图像、IMU消息的发布是否对齐,是否存在丢帧或时间戳跳跃。
- 预览图像:直接点击图像topic的消息,可以在下方预览图像内容,快速检查数据质量。
- 查看消息内容:点击点云消息,可以查看其header(包含时间戳和坐标系信息)、height、width、fields(点云的字段,如x, y, z, intensity)等原始数据。这对于理解数据结构非常有帮助。
针对Livox雷达bag文件的特殊探查: 如果你拿到的是Livox雷达录制的bag文件(例如通过Livox SDK和ROS驱动录制),其topic命名和消息结构与标准的Velodyne KITTI数据有所不同。典型的Livox ROS驱动会发布如下topic:
| Topic名称 | 消息类型 | 说明 |
|---|---|---|
/livox/lidar | livox_ros_driver/CustomMsg | Livox自定义格式的点云消息,包含更多信息如tag、line_id等。 |
/livox/imu | sensor_msgs/Imu | IMU数据。 |
可能还有 /livox/lidar_pointcloud | sensor_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的特定配置来讲解。
基础配置与坐标系校正:
- 启动roscore和rviz:
# 终端1 roscore # 终端2 rviz - 添加
PointCloud2显示类型:点击左下角Add,选择By topic或By display type,找到sensor_msgs/PointCloud2并添加。 - 解决最常见的“No transform”问题:添加点云后,如果rviz窗口一片空白,并提示“No transform from [frame_id] to [Fixed Frame]”,这说明坐标系没对上。你需要:
- 在rviz左侧
Global Options中,将Fixed Frame修改为点云消息的header.frame_id。对于KITTI转换的bag,通常是velodyne或base_link。对于Livox,可能是livox_frame或laser。 - 如果修改后仍不显示,可能是
tf静态变换缺失。对于简单的可视化,你可以直接添加一个TF显示类型,查看坐标系树,或者使用static_transform_publisher发布一个静态变换(例如,假设雷达坐标系就是世界坐标系):
这条命令每100ms发布一次从rosrun tf static_transform_publisher 0 0 0 0 0 0 1 map velodyne 100map到velodyne的静态变换(位置和旋转均为0)。在rviz中,将Fixed Frame设为map,点云就能显示了。
- 在rviz左侧
点云显示样式与属性调节:
在PointCloud2的属性面板中,有几个关键设置能极大影响可视化效果:
- Style:
Points(点)、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)因其非重复扫描模式而闻名,这带来了更快的点云填充率,但在可视化时也可能需要特殊处理。
- Topic选择:在rviz的
PointCloud2显示属性中,将Topic设置为Livox驱动发布的PointCloud2格式的topic,例如/livox/lidar_pointcloud。避免直接使用/livox/lidar(CustomMsg),除非你安装了支持该消息的rviz插件。 - 去畸变显示(可选):对于运动平台采集的数据,点云可能存在运动畸变。一些高级的Livox驱动或后处理节点会发布经过去畸变处理的点云topic,例如
/livox/undistort_pointcloud。在分析定位建图效果时,优先订阅这个topic。 - 利用CustomMsg中的额外信息(进阶):如果你想利用
CustomMsg中的tag(点类型,如正常点、噪声点)或line_id(激光线号)来过滤或着色点云,你需要编写一个简单的ROS节点,订阅/livox/lidar,将这些信息转换为PointCloud2的fields(例如,将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可能会卡顿。
- 降低显示频率:在
PointCloud2属性中,调整Decay Time。设置为一个较小的值(如0.1秒),rviz不会累积显示历史点云,能显著提升性能。 - 使用
VoxelGrid过滤器:在rviz中添加一个VoxelGrid类型的过滤器(在PointCloud2属性的Filters列表中添加)。设置一个合理的叶子大小(Leaf Size),例如0.1m,可以在保持场景形状的同时大幅减少显示的点数。 - 按距离裁剪:添加
PassThrough过滤器,设置Z轴(或X、Y轴)的范围,只显示特定距离内的点云。
处理时间戳问题:
有时播放bag时,rviz会提示时间戳在未来的警告,或者/clock topic有问题。可以尝试:
rosbag play --clock your_bag.bag:使用bag文件内部的时间戳来模拟ROS时间。- 在rviz的
Global Options中,将Fixed Frame设置为一个不依赖于tf变换的坐标系(如map或odom),并确保有对应的静态变换发布。
多传感器数据同步可视化: KITTI bag通常包含图像和点云。你可以在rviz中同时显示:
- 添加
Image显示类型,订阅对应的相机topic(如/kitti/camera_color_left/image_raw)。 - 调整
Image的Image Topic和Transport(通常为raw)。 - 为了便于对比,你可以将
Image显示的面板拖拽到主窗口,与点云并列显示。通过同步播放bag,可以直观地分析相机与激光雷达的感知结果。
当点云显示为“扁平的墙”时: 如果点云看起来像一堵垂直的墙,而不是立体的场景,很可能是坐标系设置错误。检查并确保:
Fixed Frame设置正确。- 点云消息的
frame_id与Fixed Frame一致,或者存在正确的tf变换。 - 在
PointCloud2属性中,检查Position和Orientation是否被意外修改(应均为0)。
处理KITTI或各类激光雷达bag文件,核心在于理解数据流、坐标系和工具链。从基础的播放查看,到深入的数据提取与定制化分析,每一步都藏着提升效率的细节。rviz不仅仅是一个查看器,结合ROS丰富的命令行工具和脚本能力,它能成为一个强大的数据分析和算法调试环境。尤其是在处理像Livox这样有自己特点的传感器数据时,多花点时间在前期探索和配置上,后期算法开发阶段就能省下大量排查数据问题的时间。
更多推荐
所有评论(0)