提供针对 ROS 2 bag 的实用工具,包含 frame_id 重写、topic 重命名、tf_static 写入等功能。
- rewrite_frame_id:根据配置,将已录制 rosbag2 中指定 topic 的
header.frame_id批量替换,并生成新的 bag。 - rewrite_tf_static_frame_id:对
/tf_statictopic 中的 frame 名称进行批量改写,并生成新的 bag。 - rename_topic:根据配置,将已录制 rosbag2 中的 topic 名称进行重命名,并生成新的 bag。
- write_tf_static:根据外参配置文件,向 rosbag2 中写入 tf_static 变换信息。
- filter_pointcloud_xyzi:将指定 PointCloud2 topic 的点云字段裁剪为常见的
x/y/z/intensity四个字段。 - export_assets:解析 rosbag2 中的点云、图像、/tf_static,并导出为 PCD/PNG/YAML 等通用格式
- fuse_pointclouds: 将多个激光雷达 PCD 点云融合到统一坐标系。
colcon build --packages-select ros2_bag_utils
source install/setup.bash依赖说明:请确保环境已安装
python-lzf(例如pip install python-lzf或sudo apt install python3-lzf),以便导出的 PCD 使用 LZF 压缩格式。
示例:config/fuse_pointclouds.example.yaml
input_root: /path/to/exported/data
base_frame: base_link
reference_lidar: lidar_name
time_tolerance_ms: 10.0
lidar_subdir: lidar
calib_subdir: calib
overwrite: falseinput_root: 包含点云和外参的根目录。base_frame: 目标基准坐标系。reference_lidar: 参考时间轴的激光雷达名称。time_tolerance_ms: 时间匹配容差(毫秒)。lidar_subdir: 点云子目录(默认lidar)。calib_subdir: 外参子目录(默认calib)。overwrite: 是否覆盖现有输出目录。
ros2 run ros2_bag_utils fuse_pointclouds --config /path/to/config.yaml命令行参数可覆盖配置文件:
--input-root:输入根目录--base-frame:基准坐标系--reference-lidar:参考激光雷达名称--time-tolerance-ms:时间容差(毫秒)--lidar-subdir:点云子目录--calib-subdir:外参子目录--overwrite:覆盖输出目录
ros2 launch ros2_bag_utils fuse_pointclouds.launch.py \
config_file:=/path/to/config.yaml- 读取指定目录下的 PCD 文件,根据时间戳匹配多个激光雷达的点云。
- 使用外参文件中的四元数和平移,将点云变换到统一坐标系。
- 输出融合后的点云到
input_root/lidar/base_frame/目录。 - 支持 ASCII、二进制和压缩 PCD 格式。
- 依赖 wind-pypcd、scipy、numpy、PyYAML。
示例:config/rewrite_frame_id.example.yaml
input_bag: /path/to/original_bag
output_bag: /path/to/output_bag # 可选,缺省时自动追加后缀
mappings:
/example/topic: base_link
/another/topic: camera_linkros2 run ros2_bag_utils rewrite_frame_id --config /path/to/config.yaml命令行参数可覆盖配置文件:
--input-bag:输入 bag 目录--output-bag:输出 bag 目录(可选)--topic-map topic:=frame:可重复指定,覆盖原配置
ros2 launch ros2_bag_utils rewrite_frame_id.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/override/input/bag \
topic_map:="/topic:=new_frame;/topic2:=map"- 如配置中的 topic 不存在或没有消息,将直接报错退出。
- 如消息缺少
header.frame_id字段,同样报错。 - 如果 bag 中存在
/tf_static,且变换中的 parent/child frame 命中被替换的旧 frame,会自动同步改写,保持 tf 树一致。 - 当检测到同一 topic 的消息使用不同的
frame_id,或同一旧 frame 被要求映射到多个新 frame 时会报错提示。 - 输出 bag 与输入 bag 采用相同的 storage/serialization 插件,并保存到新的目录中。
示例:config/rewrite_tf_static_frame_id.example.yaml
input_bag: /path/to/original_bag
output_bag: /path/to/output_bag # 可选,缺省时自动追加后缀
frames:
old_frame_a: new_frame_a
old_frame_b: new_frame_bros2 run ros2_bag_utils rewrite_tf_static_frame_id --config /path/to/config.yaml命令行参数可覆盖配置文件:
--input-bag:输入 bag 目录--output-bag:输出 bag 目录(可选)--frame-map old:=new:可重复指定,覆盖原配置
ros2 launch ros2_bag_utils rewrite_tf_static_frame_id.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/override/input/bag \
frame_map:="old:=new;foo:=bar"- 仅对
/tf_statictopic 进行处理,其余 topic 原样拷贝。 - 同时改写
TransformStamped的header.frame_id(parent)与child_frame_id。 - 当旧 frame 在 bag 中未出现时给出警告但不会中断,仍会输出新 bag。
- 改写成功后输出改写统计,包括映射列表及修改前后的 tf 连线摘要。
示例:config/rename_topic.example.yaml
input_bag: /path/to/original_bag
output_bag: /path/to/output_bag # 可选,缺省时自动追加后缀
mappings:
/old/topic: /new/topic
/another/old: /another/newros2 run ros2_bag_utils rename_topic --config /path/to/config.yaml命令行参数可覆盖配置文件:
--input-bag:输入 bag 目录--output-bag:输出 bag 目录(可选)--topic-map old:=new:可重复指定,覆盖原配置
ros2 launch ros2_bag_utils rename_topic.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/override/input/bag \
topic_map:="/old:=/new;/foo:=/bar"- 若旧 topic 不存在或无消息,将直接报错终止。
- 若新 topic 已存在于原 bag 或多个旧 topic 指向同一新 topic,同样报错。
- 输出 bag 使用与输入相同的 storage/serialization 插件并写入新目录,metadata 中的 topic 名称会同步更新。
示例:config/write_tf_static.example.yaml
input_bag: /path/to/original_bag
output_bag: /path/to/output_bag # 可选,缺省时自动追加后缀
lidars: # 可选,存在则处理
lidar0:
topic: "/sensing/lidar/lidar0/pointcloud_raw"
extrinsics: "/absolute/path/to/lidar0_extrinsics.yaml"
lidar1:
topic: "/sensing/lidar/lidar1/pointcloud_raw"
extrinsics: "/absolute/path/to/lidar1_extrinsics.yaml"
cameras: # 可选,存在则处理
cam0:
topic: "/sensing/camera/cam0/image_ptr"
extrinsics: "/absolute/path/to/cam0_extrinsics.yaml"外参文件格式:
parent_frame: base_link
child_frame: lidar_lidar0
transform:
translation:
x: 1.250
y: 0.000
z: 1.600
rotation_quaternion:
x: 0.000
y: 0.000
z: 0.000
w: 1.000ros2 run ros2_bag_utils write_tf_static --config /path/to/config.yamlros2 launch ros2_bag_utils write_tf_static.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/override/input/bag- 外参文件不存在或格式错误时发出警告并跳过该设备,不会终止程序。
- 检查 frame 名称是否符合 ROS 规范,不规范时跳过。
- 若原 bag 已存在
/tf_statictopic,会发出警告并合并变换。 - 使用原 bag 第一条消息的时间戳作为 tf_static 的时间戳。
示例:config/filter_pointcloud_xyzi.example.yaml
input_bag: /path/to/original_bag
output_bag: /path/to/output_bag # 可选,缺省时自动追加后缀
topics:
- /sensing/lidar/front/pointcloud_raw
- /sensing/lidar/rear/pointcloud_rawros2 run ros2_bag_utils filter_pointcloud_xyzi --config /path/to/config.yaml命令行参数可覆盖配置文件:
--input-bag:输入 bag 目录--output-bag:输出 bag 目录(可选)--topic /foo/points:可重复指定,覆盖原配置
ros2 launch ros2_bag_utils filter_pointcloud_xyzi.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/override/input/bag \
topics:="/topic_a;/topic_b"- 仅当目标 topic 的所有消息同时包含
x、y、z、intensity且类型为float32时会执行裁剪。 - 如任意目标 topic 的任意消息缺失上述字段或字段类型不符合要求,将立即报错并终止。
- 输出 bag 与输入 bag 使用相同的 storage/serialization 插件,并写入新的目录,目标 topic 的消息仅保留
xyzi字段。
示例:config/export_assets.example.yaml
input_bag: /path/to/rosbag2_directory
output_root: /path/to/export_root # 可选,缺省时自动生成 *_exported
base_frame: base_link # 可选,外参统一到的基准坐标系
filename_style: ns # 默认纳秒命名,可设置为 datetime
filename_time_source: header # 默认使用 header.stamp,可改为 bag
# filename_time_format: "%Y%m%d_%H%M%S" # 仅当 filename_style=datetime 时有效
lidar:
topics:
- /sensing/lidar/front/points_raw
output_subdir: lidar
camera:
topics:
- /sensing/camera/front/image_raw
output_subdir: camera
tf_static:
enabled: true
output_subdir: calib
filename_time_format: "%Y%m%dT%H%M%S_%f"
overwrite: falseros2 run ros2_bag_utils export_assets --config /path/to/config.yaml常用覆盖参数:
--input-bag:输入 bag 目录--output-root:导出根目录--base-frame:外参统一到的基准 frame--lidar-topic /topic:可重复指定点云 topic--camera-topic /topic:可重复指定图像 topic--disable-lidar:跳过点云导出(等同于在配置中将lidar.enabled: false)--disable-tf-static:跳过外参导出--overwrite:若输出目录已存在则清空后重建--filename-style {ns,datetime}:选择文件命名样式;datetime 可结合 24 小时制格式--filename-time-source {header,bag}:选择时间来源,默认 header
ros2 launch ros2_bag_utils export_assets.launch.py \
config_file:=/path/to/config.yaml \
input_bag:=/path/to/bag \
output_root:=/path/to/export \
lidar_topics:="/lidar_a;/lidar_b" \
camera_topics:="/cam/image" \
overwrite:=true- 自动识别
sensor_msgs/msg/PointCloud2、sensor_msgs/msg/Image、sensor_msgs/msg/CompressedImage以及/tf_static。 - 点云导出为
.pcd,采用 PCD v0.7 格式。输出的 PCD 存储格式可通过pcd_format配置控制(取值:uncompressed、ascii或compressed,默认uncompressed)。点云字段保留由pointcloud_format控制(取值:auto/xyz/xyzi/xyzit,默认为 auto)。 - 图像统一保存为
.png,原始图像根据编码解码,压缩图像先解压再保存。 tf_static将尝试构建到base_frame的变换链,缺失时给出警告;如 bag 中不存在base_frame,会报错并停止写入外参。- 点云、图像分别按
frame_id归档到lidar/、camera/子目录,每帧以采样时间命名;外参写入calib/下frame_extrinsics.yaml文件。 - 可通过配置项
lidar.enabled: false或 CLI--disable-lidar显式禁用点云导出,避免解析 PointCloud2(加速仅导出相机数据的流程)。 - 默认使用消息
header.stamp的纳秒值作为文件名;可通过配置切换为 24 小时制时间字符串。 - 处理过程中使用
rich进度条展示点云与图像的处理进度;若环境缺少rich会降级为日志提示。
colcon test --packages-select ros2_bag_utils