专栏算法工具链Datatools简介

Datatools简介

TROS.Assist2024-04-24
88
0

1. 概述

1.1 简介

DataTools 模块提供基本的数据解析、数据封包、数据录制、数据回灌、数据回放、数据分析、数据渲染等功能,打造基于数据的完整链路。通过提供库以及可执行工具的形式,基于不同应用场景,更加方便用户使用

1.2 架构设计

Description

如上图所示:分为四部分:packsdk、data-io、应用、utility,分别功能如下:

packsdk:

packsdk 用于 Pack 格式及 Pack 文件的交互,提供了一套用于操作 Pack 文件的函数接口。通过该SDK,用户不必关心Pack文件的具体结构以及Pack Frame的数据格式,即可读取pack文件,并获取想要的数据。但只对外提供 C 接口,并且不负责数据的序列化和反序列化

data-io:

dataio 也是用户和 Pack 文件的交互,实际上就是对 packsdk 做了一层 C++ 的封装,并且支持数据的序列化和反序列化,使用起来会更简单易用

  • PackReader: pack 文件的读接口

  • JpegReader: jpeg 文件夹的读接口

  • Mp4Reader: mp4 文件的读接口

  • Timline:多文件的同步机制接口

  • PackWriter:文件写接口

  • frame:提供针对于 frame 的增、删、查、改操作,输入输出都是message,会进行数据的序列化和反序列化

  • BufPool:提供内存池功能

  • compress:提供压缩和解压缩功能

应用:

  • Sender:基于 communication 公用的发送接口

  • Receiver:基于 communication 公用的接收接口

  • recorder:数据录制

  • fillback:数据解析

  • pack-report:数据分析

Utility:

  • file_ops:提供针对文件/文件夹的公共操作

  • json_ops:提供针对 json 的公共操作

  • string_ops:提供针对一些字符串的公共操作

2. 特性说明

2.1 pack存储格式

2.1.1 物理存储结构

Description
  • version 只会出现在 pack 文件头部

  • 除去 file version 外,上述图中剩余的部分恰好是 pack 文件中一帧的构成,一个 pack 有很多帧组成
  • 帧构成:一帧中包含一个 meta 和多个data,total_data_len 统计的是所有 data 的总长度。其中帧的 meta 部分存储的结构就是 FrameV2Proto.Frame

2.1.2 逻辑存储结构

Pack 是由很多帧数据组成,每一帧可以视作一连串小消息的集合,每一类消息有一个唯一标识的名称以供区分。如上图所示,是目前涵盖一些典型的小消息。

Pack 的一帧,包含的信息可通过 frame.v2.proto 的 FrameV2Proto.Frame 对数据进行反序列化得到,如下面 protobuf 代码所示:

其中字段含义如下:

字段名称

含义

version

应用软件输出 pack msg 的版本信息

frame_id

该帧的编号(应用软件输出时递增计数)

time

该帧的主时间戳(起始时间戳)

proto_version

区分元信息本身格式的版本信息(目前包含 V0, V1, V2 三个版本,向后兼容)

data_list

该帧所包含的消息集合

frame_type

帧类型,见 FrameType。Bundle:所有消息绑定发出,均为同一个时间戳;FreeSync:消息按固定周期发出,时间戳各有不同

done_time

该帧的结束时间戳

2.1.3 物理结构到逻辑结构的对应

可参考下述 protobuf 代码来解析上述 msg_list 的内容:

字段名称

含义

meta

消息结构序列化之后的字节流了,使用相应的消息 proto 定义对其进行反序列化,即可拿到该消息的内容

gen_ts

消息产生的时间

done_ts

消息处理完的时间

channel

消息产生的通道(如不同的模组、不同的传感器等)

data_index

消息所对应的 pack data 部分的编号,默认的 -1 表示该消息并无 data

字段名称

含义

message_name

消息的名称

data

为了兼容老版本而存在的,可忽略

proto

消息的详细属性

物理结构到逻辑结构的具体对应关系如下:

Description

上述过程中,从 pack 解包到序列化后的消息字节流的过程,可使用 pack-sdk 来简化流程;相应的,pack-sdk 也可以逆向地将序列化后的消息字节流打包成 pack。根据消息名称,直接获取消息的结构化数据的过程,可通过 Datatools 来简化流程。

2.2 pack-sdk

2.2.1 关于一帧

2.2.1.1 创建

Pack 中的一帧必须要填写的是 frame_id 与 timestamp,分别表示当前帧的顺序标识以及帧发出的时间戳。可以使用 CreatePackFrame(frame_id, timestamp) 来创建一帧,也可以基于一段内存(二维数组的形式),使用 CreatePackFrameFromDatas 创建。如果一帧数据是按照网络字节流的方式获取、或来自于文件流中的一段,也可以采用 OpenPackFrame 来读取并创建这一帧。

2.2.1.2 销毁

创建后的一帧,会在 PackFrame 这个 void* 的 handle 中存放,如不再需要该帧数据,需要使用 DestroyPackFrame 来手动进行销毁。   也可以考虑用 std::unique_ptr 来维护这个 handle,避免忘记销毁,代码及说明如下:

2.2.1.3 访问

  • 可以使用 GetFrameID 获取帧 ID
  • GetFrameType 获取帧类型
  • GetFrameTimestamp 获取帧时间戳
一帧中允许存在多个相同名字的消息,它们或许来自不同的数据处理流程,我们会使用 channel 来区分。可以通过 GetChannelCount 来访问该消息名有几个 channel,使用 GetChannelArray 可以拿到这个 channel array。
如果一个消息,名字与 channel 都相同,我们就可以认定它是同一个数据处理流程出来的多份消息,即可以认为它们都属于同一个 topic。topic 默认采用 message_name#channel_id 的形式表达。可以通过 GetTopicsCount 来查询一帧中有多少 topic,以及通过 GetTopicList 拿到这个 topic 列表的二维字符串数组,该数组可以使用 FreeTopicList 进行销毁。
但 topic 在一帧中依然会有重复,譬如在 FreeSync 类型的帧中,一帧是一个周期,一个周期内某 topic 可能出现多次。可以通过 GetTopicBlockCount 来获取该 topic 在一帧内的数量,然后使用 GetMetaWithData 来获取该 topic 的全部数据。数据通过一个 SingleBlock 的数组来表达(长度正是 GetTopicBlockCount 的返回值)。
SingleBlock 的数据结构如下:

基本包含了一个消息的全部信息:生成时间、处理完成时间、meta(protobuf 序列化后的二进制流)、data(未经序列化的内存块)。

如果对 pack 帧格式足够了解,也完全可以将一帧视为一个 bytes 的二维数组(meta, data, data, …)。故也可以使用 GetPackDataCount 拿到这个二维数组的长度,然后使用 GetPackData 来获取二维数组中某一段的内容。
值得注意的是,GetPackData 接口可能会对一帧数据有修改,因为如果拿到的是二维数组的第一段,则为 Pack Frame 的 Meta,需要进行一次序列化。

2.2.1.4 修改

  • 可以使用 SetFrameID 设置帧 ID
  • SetFrameType 设置帧类型
  • SetFrameTimestamp 设置帧时间戳
如果消息只有 protobuf 定义的部分,那么 AddMeta 就可以将消息序列化后的 binary blob 加到一帧中去。如果消息同时还带有不需要进行序列化的 big blob 需要传输,那么就需要使用 AddMetaWithData 来增加。为了更加安全,使用 AddMetaWithData 时会默认将 data 拷贝到 PackFrame 中,这样 data 就不会收到外面逻辑的影响。但如果对效率以及 memcpy 比较敏感,则可以使用 AddMetaWithUserData 来避免这些 copy,但这也意味着 PackFrame 只是借用了这段内存,需要外面能够确保数据使用的安全周期。
相应的,使用 RemoveMetaWithData 可以从一帧内删除某个 message,也可以更细地使用 RemoveMetaWithDataByChannel 只删除某一个 topic

2.2.2 关于读写

PackSDK 为 Pack 文件的读写分别提供了两个相应句柄:PackFileHandleR和PackFileHandleW。

PackFileHandleR 负责读文件,PackFileHandleW 负责写文件。

2.2.2.1 Pack文件读取

使用 OpenPackFileR 就可以获取该文件的读句柄了,同样的,不再需要读该 pack 时,需要使用 ClosePackFileR 进行关闭与释放。
依然可以考虑使用 std::unique_ptr 来管理该句柄,避免遗忘关闭,指令如下:
可以使用 ReadPackFrame 来按序读取下一帧,可以将这个过程写到循环里:
ReadPackFrame 第二个参数表示是否需要读取 data。读取 data 意味着会有一大块内存需要从文件流中 copy 出来,且这一次 copy 无可避免。但通常,我们只需要 Meta 部分就可以了,此时则可以设为 false,提高读取效率。
通过读句柄,可以通过 TellPackFrame 随时获取当前读取的位置。相应的,如果知道特定帧的位置,也可以使用 SeekPackFrame 直接跳到该帧。如果只想获取某一段时间内的帧,可以使用 SeekPackFrameByTs。

2.2.2.2 Pack文件落盘

与读 Pack 类似,写 Pack 是通过 OpenPackFileW 来创建一个文件写句柄。它也需要在写完之后,使用 ClosePackFileW 来关闭。
通过 WritePackFrame 来将 PackFrame 落盘,可搭配 FlushPackFileW 来确保写到了磁盘上。

2.3 dataio

Description

提供公用基础的读写功能,具体功能如下:

  • 数据读

    • Pack 文件读

    • Jpeg 读

    • MP4 读

  • 数据写

    • Pack 文件写

  • Timeline

    • 提供针对多路 pack 时间戳同步的机制

  • Frame

    • 提供针对于帧的所有操作。增、删、查、改

  • SerializeFunction

    • 序列化、反序列化类

  • MsgCenter

    • 维护 msg 和 SerializeFunction 对应关系的类

2.4 Recorder

整体数据流如下所示:

Description

Recorder 整体类图关系如下:

Description

2.4.1 Fillback

整体数据流如下所示:

Description
整体类图关系如下:
Description
算法工具链
杂谈
评论0
0/1000