logo

开发者说|Rosbag格式分析

作者:技术小柏2022.04.14 16:11浏览量:782

简介:开发者说|Rosbag格式分析

下面是由社区开发者——王方浩提供的文章,本文主要介绍 ROS发布订阅实现。

关于作者

王方浩,社区布道师,武汉大学电子信息专业,先后在华为和阿里从事固件、驱动、操作系统和后台相关的开发工作,目前主要从事L4级别自动驾驶的开发,喜欢研究技术,分析源码和解答问题,目前活跃于Apollo自动驾驶开源社区,平时喜欢做一些户外运动,对自动驾驶的前景担心的同时又充满激情,“莫愁前路无知己,天下谁人不识君”,希望更多的人一起!

概览

本文主要参考Rosbag Format 2.0,对Rosbag的格式做一个深度分析。

首先打开Rosbag,会在第一行标识出Rosbag的版本号,由于Rosbag有1.0,1.1,1.2,2.0几个版本,通过版本号进行区分,这里只介绍最新的2.0版本。

版本号

首先打开Rosbag,会看到以下格式。也就是说Bag包的第一行是人眼可以识别的版本号,后面紧跟着的是一系列记录序列。

#ROSBAG V2.0
<record 1><record 2>....<record N>

记录

已知Bag包由一系列的记录序列组成,接下来再详细的看每个记录的格式。

<header_len><header><data_len><data>

即每个Record由Header和Data组成,而为了找到Header和Data,还需要保存Header_len和Data_len,结合两者,可以得到Bag包的结构如下:

图片.jpg

信息头(Headers)
接下来,再详细分析上述的信息头(Headers)。每个记录头包含一系列 Name=Value 字段,格式如下:

<field1_len><field1_name>=<field1_value><field2_len><field2_name>=<field2_value>...<fieldN_len><fieldN_name>=<fieldN_value>

和header_len和Header类似,这里的field1_len后面跟着= ,其中field1_len 的长度包含 = 号在内,所有的Field的长度等于 header_len 。

  • field_name 字段名称可以包含任何可打印的 ASCII 字符 (0x20 - 0x7e),但 =(0x3d) 除外。

  • field_value字段值可以包含任何数据(包括嵌入空值、换行符等的二进制数据)。

图片.jpg

Op 码
所有的信息头必须包含Op码字段,也就是说上述field_name必须有一个字段为Op 。这个字段是为了区分Record的类型而准备的,这也是为什么这个字段是必须的原因,因为没有这个字段就没法区分Record的类型。从侧面也可以看出Header信息头可以灵活的用来保存Record的信息,甚至用户可以自定义一些信息。

不同的Op码对应不同的Record类型,一共有6种类型,接下来逐个介绍:

  • Bag Header 主要存放Bag包整体的信息,必须是第一个Record。

  • Chunk 主要的数据结构,可以被压缩,可以理解为把N个消息打包为一个块(Chunk),方便索引节省空间。

  • Connection 块的结构之一,存放信息的格式信息,有了消息的格式,才能解析消息。

  • Message Data 块的结构之一,消息序列化之后以2进制存储,通过Connection获取消息格式后进行反序列化。

  • Index Data 索引数据,因为一个块比较大,索引消息在块中的位置,方便快速查找,缺点在于会占用额外的空间。

  • Chunk Info 块的结构之一,主要描述块的信息,例如消息的起始和结束时间等。

至此Bag包的格式基本上就分析清楚了。接下来分别介绍这6种Record的数据格式,也就是Record Data部分的内容。

图片.jpg

记录类型

Bag header
必须是Bag包的第一条记录。Op码=0x03,包括以下信息,注意这些字段全部保存在header中,而它的data是空白的,可以直接跳过。

  • index_pos Bag Header之后第一条记录的偏移

  • conn_count Connections的数量

  • chunk_count Chunk的数量

Chunk
块,类似一个档案袋,打包了固定大小的多条消息。以下结构保存在Header中。

  • Compression 压缩方式

  • Size 块大小

而Data由Connection和Message Data组成。

Connection
连接,通过连接可以获取消息的定义,用来反序列化消息,以下消息在header结构中

  • Conn 连接的ID

  • Topic 消息的Topic名称

数据段中包含

  • Topic 消息名称

  • Type 消息类型

  • Md5sum md5值

  • Message_definition 消息定义

Message data
消息本身,消息通过序列化保存,通过Conn可以找到对应的连接(Connection),从而获取到消息类型,进行反序列化。

  • Conn 消息连接ID,通过ID可以找到对应的Connection

  • Time 消息发布时间

Index Data
索引消息,主要是为了快速检索信息。

  • Ver 版本号

  • Conn 消息连接ID

  • Count 消息数量

由于有多个消息,因此以下字段也会出现多次。

  • Time 时间

  • Offset 偏移

Chunk Info
块信息,也是为了方便查找。

以下字段保存在Header中

  • Ver

  • Chunk_pos

  • Start_time

  • End_time

  • Count

因为一个块中可能有多种不同的消息,因此以下2个字段会出现多次,类似一个哈希表的结构,保存在Data结构中。

  • Conn

  • Count

读取顺序

上述6种类型的数据结构的读取顺序为:

1、先解析Bag header,获取到index_pos。

2、然后跳到index_pos读取Connection。

3、接着读取Chunk info,上述2个步骤相当于建立起了整个Bag包,块的索引。

4、接着根据Chunk info逐个读取Chunk,先解析Chunk,获取压缩类型和数据大小。

5、接着跳到Chunk尾部解析Index data,这里是一个消息对应一个index。

6、最后根据index data实例化消息(通过接口instantiateBuffer)。

图片.jpg

设计原理

至此Bag包的分析就完成了,其实通过看上述的结构,第一遍可能只能有个大概的印象,为了进一步加深理解,这里对Bag的设计思路进行进一步的分析。

序列化和反序列化
首先知道Bag包就是为了录制消息,而消息的保存和读取就涉及到一个广义上的问题序列化和反序列化,它基本上无处不在,只是大部分人没有注意到,举个简单的例子,程序运行的时候,是直接操作的内存,也就是一个结构体或者一个对象,但内存里面的数据会消失,当要保存内存的数据到磁盘的时候就需要序列化之后保存,常见的序列化方式有XML、Json等等,而Protobuf也就是其中的一种。而当需要读取磁盘中的数据使用的时候,又需要把磁盘中的数据转换为内存中的数据,这个过程叫反序列化。

当然持久化并不是唯一利用到序列化和反序列化的一种,比如两个进程之间通信,由于进程之间的内存映射并不相同,也需要序列化和反序列化,同理还有两台机器之间的通信,例如最常见的网页应用也需要序列化和反序列化,当然还有更厉害的技术,例如跨语言的调用,制作一种通用的对象消息格式,从而实现不同语言之间的数据交换。

回到这个问题本身,即持久化需要序列化消息,然后保存到硬盘,读取消息的时候,又需要反序列化消息为内存的对象。

反序列化
如果说写入消息很简单,但是读取消息的时候就麻烦了,因为不知道消息的类型,就无法解析消息,假设保存了三条消息,包括图片,位置和轨迹消息,读取了一条消息,怎么知道这条消息是什么类型呢?方法很简单,可以在消息头中标识这条消息是什么类型,然后再用这种消息类型去解析消息,这样就解决了。

通过消息类型名称(字符串)来生成对象,这在很多语言中叫做反射(Eflection),这样我们就解决了消息解析的问题,而Bag包中的Connection就是用来解决上述问题,它包含了数据类型和格式,而每个Message Data中可以找到Connection,从而找到消息类型,进行解析。

还有一点设计的比较巧妙的地方在于,每个消息头如果都包含数据的消息定义的话,比较浪费空间,因此通过Connection中包含数据的格式(消息字段的定义),而Message data中只包含ID,从而节省了空间。

索引
消息保存和解析的问题解决了,那么如何快速的查找呢?因为一个包可能比较大,所以把一个包拆分为几个块(Chunk),而Chunk中有消息的时间段,这样查找指定时间段的消息的时候,就可以跳过一些块,从而避免读取整个块之后再进行查找,提高检索效率。

而Index Data则更进一步,直接索引了不同类型的消息在块中的时间戳和偏移,从而方便快速查找。当然索引确实可以加快速度,但是过多的索引也会消耗空间,也就是经典的时间和空间的算法复杂度问题,需要取舍。

以上就是Rosbag的整个分析过程。

本文部分内容参考链接

相关文章推荐

发表评论