apollo record包解析工具

举报
yd_215912107 发表于 2022/11/06 15:00:02 2022/11/06
【摘要】 当前解析cyber record包需要在docker中进行,并且依赖cyber和编译好的proto文件。实际上cyber_record录制好之后就是按照一定的格式保存的文件,对用户来说,拿到录制好的文件之后,用更少的依赖进行解析是最好的。下面你可以通过纯Python来解析Apollo record文件,只需要1行安装命令。pip3 install cyber_record record_ms...
当前解析cyber record包需要在docker中进行,并且依赖cyber和编译好的proto文件。实际上cyber_record录制好之后就是按照一定的格式保存的文件,对用户来说,拿到录制好的文件之后,用更少的依赖进行解析是最好的。
下面你可以通过纯Python来解析Apollo record文件,只需要1行安装命令。
pip3 install cyber_record record_msg
项目地址:
https://cyber-record.readthedocs.io/en/latest/#cyber-record.readthedocs.io/en/latest/#
原理
由于cyber record本身能够进行自解析,因此从理论上来说也是可行的,首先是分析cyber record的格式,然后调用protobuf进行反序列化。cyber record的格式在上面几篇文章中已经介绍过了,我们这里重点介绍下如何进行自解析。
protobuf的原理实际上是根据用户定义好的proto文件,来对消息进行解析,实际使用的时候,protobuf采用了descriptor来描述proto文件,而descriptor_pb则是proto文件的proto,这里有点绕口,你可以理解为“描述文件”的“描述文件”,通过上述2个定义,我们就可以知道消息的结构,有哪些字段,也就能够解析消息了。
但问题没那么简单,我们知道一个proto文件中可能会引入另外的proto文件,也就是说一个proto文件的descriptor_pb只能描述它自己,但并没有包含它引用的消息的格式,这有点类似c++的头文件。但解析整个消息,我们必须知道所有消息的格式才行,包括它们的引用。好在cyber record中都保存了这些信息。
把自身的消息描述descriptor和它依赖的所有消息的descriptor,都放入descriptor_pool,之后就可以根据消息类型来创建消息了。
以上就是具体的实现原理,简单来说,由于每个bag包中的消息种类有限,例如有5种,那么把这5种消息的descriptor都存入bag包中,体积也不会占用太大,而最后我们可以根据这些描述信息,去解析保存在bag中对应类型的消息
安装
现在你只需要通过pip安装cyber_record库,就可以在本地解析record文件了,再也不需要其它任何依赖。
pip3 install cyber_record
1. 命令行
Info
通过cyber_record info 来获取record文件的统计信息
$ cyber_record info -f example.record.00000 record_file: example.record.00000 version: 1.0 begin_time: 2021-07-23 17:12:15.114944 end_time: 2021-07-23 17:12:15.253911 duration: 0.14 s size: 477.55 KByte message_number: 34 channel_number: 8 /apollo/planning , apollo.planning.ADCTrajectory , 1 /apollo/routing_request , apollo.routing.RoutingRequest , 0 /apollo/monitor , apollo.common.monitor.MonitorMessage , 0 /apollo/routing_response , apollo.routing.RoutingResponse , 0 /apollo/routing_response_history , apollo.routing.RoutingResponse , 1 /apollo/localization/pose , apollo.localization.LocalizationEstimate, 15 /apollo/canbus/chassis , apollo.canbus.Chassis , 15 /apollo/prediction , apollo.prediction.PredictionObstacles , 2
Echo
通过cyber_record echo 打印指定topic的消息到命令行,打印"/apollo/canbus/chassis"下的所有消息到命令行
$ cyber_record echo -f example.record.00000 -t /apollo/canbus/chassis engine_started: true speed_mps: 0.0 throttle_percentage: 0.0 brake_percentage: 0.0 driving_mode: COMPLETE_AUTO_DRIVE gear_location: GEAR_DRIVE header { timestamp_sec: 1627031535.112813 module_name: "SimControl" sequence_num: 76636 }
你也可以用以下命令输出到文件
cyber_record echo -f example.record.00000 -t /apollo/canbus/chassis > 1.txt
2. 读取消息
安装之后,可以参考以下例子读取消息
读取所有消息
from cyber_record.record import Record file_name = "20210521122747.record.00000" record = Record(file_name) for topic, message, t in record.read_messages(): print("{}, {}, {}".format(topic, message, t))
运行的结果
编辑切换为居中
添加图片注释,不超过 140 字(可选)
根据topic过滤
def read_filter_by_topic(): record = Record(file_name) for topic, message, t in record.read_messages('/apollo/sensor/rs32/Scan'): print("{}, {}, {}".format(topic, type(message), t))
根据时间过滤
def read_filter_by_time(): record = Record(file_name) for topic, message, t in record.read_messages(start_time=1627031535164278940,\ end_time=1627031535215164773): print("{}, {}, {}".format(topic, type(message), t))
根据Topic和时间过滤
def read_filter_by_both(): record = Record(file_name) for topic, message, t in record.read_messages('/apollo/canbus/chassis', \ start_time=1627031535164278940, end_time=1627031535215164773): print("{}, {}, {}".format(topic, type(message), t))
3. 保存消息
获取到消息类型之后,你可以根据不同的topic来处理和保存消息,你只需要知道消息的结构就行了,也不需要添加编译好的proto文件依赖。
为了方便处理消息,通过安装record_msg ,可以帮助我们快速的读取和保存消息。
pip3 install record_msg
record_msg 提供了3种不同类型的接口
csv格式
通过to_csv 可以把消息的属性展开为数组,减轻了手动一个个枚举属性的问题。下面的例子中to_csv会把pose的所有属性都输出为一个数组。
import csv from record_msg.parser import to_csv f = open("message.csv", 'w') writer = csv.writer(f) def parse_pose(pose): ''' save pose to csv file ''' line = to_csv([pose.header.timestamp_sec, pose.pose]) writer.writerow(line) f.close()
如果你要有选择的输出属性,可以自己构建数组然后传递给to_csv展开属性 ,例如pose.position实际上包含x,y,z 3个属性,我们不必一个个构造,通过to_csv(pose.position)会自动输出
[pose.position.x, pose.position.y, pose.position.z]
保存图像
你可以通过ImageParser来解析和保存record包中的图像。
from record_msg.parser import ImageParser image_parser = ImageParser(output_path='../test') for topic, message, t in record.read_messages(): if topic == "/apollo/sensor/camera/front_6mm/image": image_parser.parse(image) # or use timestamp as image file name # image_parser.parse(image, t)
通过output_path指定图像输出的路径,默认情况下图像编号从0-n,也可以自定义图像名称,例如输出时间戳为图像的名称。
保存点云
你可以通过PointCloudParser来解析和保存record包中的点云。
pointcloud_parser = PointCloudParser('../test') for topic, message, t in record.read_messages(): if topic == "/apollo/sensor/lidar32/compensator/PointCloud2": pointcloud_parser.parse(message) # other modes, default is 'ascii' # pointcloud_parser.parse(message, mode='binary') # pointcloud_parser.parse(message, mode='binary_compressed')
通过output_path指定点云文件的保存路径,默认情况下图像编号从0-n,也可以自定义点云名称,例如输出时间戳为图像的名称,根据不同的点云消息格式,你需要传入不同的mode,默认点云为ascii格式。

3. 写入消息
当你缺少数据包,或者想通过数据集来自己构造数据包的时候,cyber_record也提供了写入消息,并且保存为Record包的能力。
import time from cyber_record.record import Record from modules.map.proto import map_pb2 write_file_name = "example_w.record.00000" def write_message(): pb_map = map_pb2.Map() pb_map.header.version = 'hello'.encode() with Record(write_file_name, mode='w') as record: record.write('/apollo/map', pb_map, int(time.time() * 1e9))
构造好的record文件保存为write_file_name
示例
更多的例子可以参考




ROS Bag分析
ROS 发布订阅模式
ROS导航模块
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。