mcap文件存储ros2消息为原始字节流,需通过channel.id关联schema.id解析idl结构;ros2 bag record默认生成含ros2 metadata的mcap,须调用readmetadata()获取,并用rosidl_typesupport_introspection_cpp反序列化payload为c++消息对象。

MCAP文件结构和ROS2消息序列化的关系必须理清
ROS2的ros2 bag record默认输出MCAP格式(v0.11+),但MCAP本身不理解ROS2消息类型——它只存原始字节流 + schema(用FlatBuffer描述) + channel元信息。解析时若直接读消息数据而不查schema,会得到一堆无法解释的uint8[]。
关键点在于:MCAP中每条消息都关联一个channel.id,而该ID指向一个schema.id,schema里才定义了消息的IDL结构(如std_msgs/msg/String)。ROS2的rosidl_typesupport_cpp生成的C++类,必须靠这个schema动态匹配字段偏移或手动反序列化。
- 不要尝试用
std::ifstream直接读MCAP payload——它经过MessagePack或自定义二进制编码(取决于writer配置) - schema可能被压缩(zstd),MCAP reader需自动解压;ROS2工具链默认启用压缩,C++解析器必须链接
libzstd - 同一topic在不同bag中
channel.id可能不同,不能硬编码映射
用mcap::McapReader读取并提取ROS2消息原始数据
官方C++ SDK mcap库(v0.19+)支持ROS2专用解析辅助,但需主动启用ROS2_SUPPORT编译选项。核心流程是:打开MCAP → 遍历channel获取message_definition → 用rosbag2_storage_mcap::ROS2Metadata辅助解析schema → 按channel.topic分组消息。
#include <mcap>
#include <rosbag2_storage_mcap><p>mcap::McapReader reader;
reader.open("my_bag_000.mcap");
mcap::Status status = reader.readMetadata();
// 必须调用此函数,否则ros2_metadata字段为空
auto ros2_meta = rosbag2_storage_mcap::get_ros2_metadata(reader);</p>
<p>for (const auto& [topic, info] : ros2_meta.topics_with_message_count) {
std::cout </p></rosbag2_storage_mcap></mcap>
-
mcap::McapReader::readMetadata()必须显式调用,否则ros2_metadata为null - 消息payload仍为
std::string_view(未反序列化),长度由record.message.data.size()给出 - 若MCAP由
ros2 bag record -s sqlite3生成(旧模式),则不含ROS2 metadata,需退回到通用MCAP解析+手动IDL匹配
将MCAP payload反序列化为C++ ROS2消息对象
拿到payload后,不能直接memcpy到ROS2消息对象——因为ROS2消息序列化协议(CycloneDDS/RTI Connext/Fast DDS)默认使用CDR(Common Data Representation),而MCAP存储的是DDS wire format(非标准CDR,含额外header)或ROS2自定义序列化(取决于RMW实现)。最稳妥方式是复用ROS2自己的反序列化逻辑。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 推荐路径:用
rosidl_generator_cpp::deserialize+rosidl_typesupport_introspection_cpp,需提前加载对应msg的typesupport库(如libstd_msgs__rosidl_typesupport_introspection_cpp.so) - 简易替代:若已知msg类型且无嵌套/变长字段,可用
rcpputils::fs::path定位.msg文件,调用rosidl_adapter::parse生成临时type support(适合离线脚本) - 避坑:不要用
rmw_deserialize裸调——它依赖当前RMW句柄,而MCAP解析是离线场景,无rclcpp上下文
典型反序列化片段(需链接rosidl_typesupport_introspection_cpp):
#include <rosidl_typesupport_introspection_cpp> #include <std_msgs><p>const rosidl_message_type_support_t<em> ts = ROSIDL_GET_MESSAGE_TYPE_SUPPORT(std_msgs, msg, String); void</em> msg_ptr = malloc(ts->get_message_size()); ts->convert_from_serialized_data(&serialized_data, msg_ptr); // serialized_data来自MCAP payload std_msgs::msg::String& str_msg = <em>static_cast<:msg::string>>(msg_ptr);</:msg::string></em></p></std_msgs></rosidl_typesupport_introspection_cpp>
时间戳、QoS和隐式frame_id处理容易遗漏
MCAP中的log_time和publish_time都是纳秒级整数,但ROS2消息内部的时间戳(如std_msgs::msg::Header::stamp)是rclcpp::Time类型,需手动转换。更隐蔽的问题是:MCAP不存QoS策略(durability、reliability等),而某些消息(如tf2_msgs/TFMessage)依赖QoS重建历史上下文;frame_id字段虽在msg内,但ROS2工具链常从topic name或metadata推导(如/tf隐含frame_id="world")。
-
record.log_time≈ 系统记录时间,record.publish_time≈ 节点发布时的now(),二者差值反映网络/调度延迟 - 若需重建
tf树,必须结合tf_staticchannel的静态transform(其publish_time通常为0,需特殊处理) - QoS信息只能从原始bag的
metadata.yaml(若存在)或录制命令参数中还原,MCAP本身不携带
真正麻烦的不是读取,而是决定哪一列时间该喂给rclcpp::Time构造函数,以及是否要伪造缺失的QoS上下文——这些决策没有标准答案,得看你的下游处理逻辑依赖什么语义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










