ROS1 C++ API读取.bag报“No such file or directory”错误,主因是未链接librosbag_storage依赖,CMakeLists需同时find_package rosbag和rosbag_storage并target_link_libraries二者;ROS2则须用rosbag2_cpp+rosbag2_storage,且storage_id必须设为"sqlite3"。

ROS1 bag 文件解析:用 rosbag C++ API 读取消息时为何报错 “No such file or directory”?
直接用 rosbag::Bag 打开 .bag 文件失败,大概率不是路径写错,而是没链接 librosbag 或漏了 rosbag_storage 依赖。ROS1 的 C++ API 实际是两层:上层 rosbag(提供 Bag 类)依赖下层 rosbag_storage(负责底层 I/O 和索引解析),CMakeLists.txt 中必须显式添加:
-
find_package(rosbag REQUIRED)和find_package(rosbag_storage REQUIRED) target_link_libraries(your_node ${rosbag_LIBRARIES} ${rosbag_storage_LIBRARIES})- 头文件需同时包含
#include <rosbag></rosbag>和#include <rosbag></rosbag>(后者用于时间范围过滤)
典型错误是只 link rosbag,导致运行时报 undefined symbol: _ZN7rosbag3Bag4openERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEj 这类符号缺失——本质是 rosbag_storage 的 so 没进链接列表。
ROS2 bag 解析不能用 rosbag 库,必须用 rosbag2_cpp + rosbag2_storage
ROS2 完全重构了 bag 格式(SQLite3 + 自定义序列化),rosbag(ROS1)库完全不兼容 ROS2 的 .db3 文件。正确做法是:
- 依赖
rosbag2_cpp(提供SequentialReader、StorageOptions等核心类)和rosbag2_storage(实现底层读取器插件) - 打开 bag 时必须指定存储 ID:
storage_options.storage_id = "sqlite3";,否则默认用mock插件,读出来全是空数据 - 消息类型需提前注册:调用
converter_options.output_serialization_format = "cdr";并确保rmw_implementation与 bag 写入时一致(常见坑:bag 是 cyclonedds 写的,你用 fastrtps 读会反序列化失败)
示例关键代码段:
立即学习“C++免费学习笔记(深入)”;
rosbag2_storage::StorageOptions storage_options; storage_options.uri = "/path/to/ros2_bag"; storage_options.storage_id = "sqlite3"; rosbag2_cpp::ConverterOptions converter_options; converter_options.output_serialization_format = "cdr"; reader.open(storage_options, converter_options);
跨 ROS 版本解析的关键障碍:消息定义不匹配
即使成功打开 bag,read_next() 返回的消息指针(std::shared_ptr<:serializedbagmessage></:serializedbagmessage>)仍可能解包失败,根本原因是 .msg 定义版本不一致。比如:
- ROS1 bag 中的
sensor_msgs/PointCloud2使用uint8[] data字段,而 ROS2 对应消息改用uint8[] data+ 显式row_step和point_step,字段顺序和对齐方式不同 - 自定义 msg 若在 ROS1 和 ROS2 中未严格保持字段名、类型、顺序一致,
rosidl_runtime_cpp::get_message_type_support_handle会返回 null,导致deserialize崩溃 - 解决方法不是硬编码解析,而是用
ros2 run rosidl_runtime_cpp generate_interfaces导出对应版本的 msg 头文件,并确保编译时链接的rosidl_typesupport_*与 bag 生成环境一致
性能陷阱:逐条 read_next() 在大 bag 上极慢,别忽略 read_until() 和缓存策略
对 10GB 级别的 bag,用循环 while (reader.has_next()) { reader.read_next(); } 可能比 Python 的 rosbag.Bag 还慢——因为每次调用都触发 SQLite 查询+反序列化+内存拷贝。提速要点:
- 用
reader.get_all_topics_and_types()预过滤只关心的 topic,避免加载无关消息 - 对时间连续的数据(如 IMU),用
reader.seek(time_point)跳转起始位置,再配合read_next()批量读取 - 若需随机访问,别自己实现索引;ROS2 的
SequentialReader不支持 seek,应改用RandomAccessReader(ROS2 Humble+ 支持,但需手动构建,文档极少) - 最实际的优化:把消息内容 memcpy 到预分配的 buffer,避免频繁 new/delete,尤其对
sensor_msgs/Image这类大 payload
真正卡住的往往不是格式解析,而是没意识到 bag 的 I/O 模式和内存模型和普通文件完全不同——它本质是个带 schema 的嵌入式数据库,按需索引比暴力扫描重要得多。



















