Plotjuggler之ulog格式分析

Plotjuggler是一个强大的画图工具,绘图极其方便,对于分析log绝对是个神器。
在这里插入图片描述

在其主页上,看到它支持ulog模式,而ulog格式又是一个飞行器开源库PX4里所定义的,官方文档写的不是很好,主要是只讲了定义,没有实际的例子,看了几遍都没看懂。并且PX4的源码里对Ulog封装的也不好,根本看不出是怎么写的,没法从中把它给抽取出来。

网上搜了个遍,没看到有如何写ulog格式的教程或代码,只能啃官方代码和文档。

记得某个晚上,看了两个小时的PX4源码和格式,一行代码都没写出来,当晚心情特别沉重,这玩意儿真的就这么难吗?

第二天找到了Pyulog,是一个Python解析ulog的库,令人惊喜的是它里面竟然有写ulog的函数。

单步调试看源码,看了大概一天半,才终于看明白了原理,恍然大悟。

然后自己重新建了C++工程,自己造些数据写成ulog,并且下载PlotJuggler源码调试自己写的ulog格式,一步一步用Plotjuggler画出来。等框架写的差不多了,再移植到我们的代码框架里。

以下是Ulog格式分析,其中的类别定义可取Plotjuggler和PX4源码里找到。如下,分为三个部分,其中Header和Definitions是初始化时预定义的。

----------------------
|       Header       |
----------------------
|    Definitions     |
----------------------
|        Data        |
----------------------

1 Header

固定16个字节,没什么好说的。

----------------------------------------------------------------------
| 0x55 0x4c 0x6f 0x67 0x01 0x12 0x35 | 0x01         | uint64_t       |
| File magic (7B)                    | Version (1B) | Timestamp (8B) |
----------------------------------------------------------------------

2 Definitions

Format

struct message_format_s {
	struct message_header_s header;
	char format[header.msg_size];
};

format[header.msg_size]的格式如下:':‘前面是格式(可理解为结构体)名称,’:‘后面是类型,以’;'个该,PlotJugger会按照这个格式解析format。如

"pose:uint64_t timestamp;double x;double y;double z;"

variable

struct message_add_logged_s {
	struct message_header_s header;
	uint8_t multi_id;
	uint16_t msg_id;
	char message_name[header.msg_size-3];
};

message_name:即上面注册的Format “pose”

msg_id:每个message_name都要有一个独一无二的msg_id,作为后续记录的key

multi_id: 每个message_name可能会有多个类型,比如同一个数据类型有多个sensor,此时msg_id相同,而multi_id不同

3 Data

struct message_data_s {
	struct message_header_s header;
	uint16_t msg_id;
	uint8_t data[header.msg_size-2];
};

msg_id: 就是注册变量的id,对应格式为msg_id:{data},注意timestamp单位是毫秒,且必须单调递增。

整体log文本格式如下(实际是二进制存储)

--------------------------//header
"ULog"
--------------------------// format
"pose:uint64_t timestamp;double x;double y;double z;"
"radar:uint64_t timestamp;double x;double y;"
//------------------------variable  format:msg_id
"pose":0
"radar":1
//------------------------data   msg_id:timestamp,data  
0:100000000,{Pose}
1:100000050,{Radar}
0:100001000,{Pose}
1:100001050,{Radar}
0:100002000,{Pose}
1:100002050,{Radar}
...................

Pose和Radar对应的结构体如下:

struct Pose {
    uint64_t timestamp = 0;
    double x = 0;
    double y = 0;
    double z = 0;
}
struct Radar {
    uint64_t timestamp = 0;
    double x = 0;
    double y = 0;
}

后记

格式搞明白花了时间跨度为两周,开发时间跨度为一周。果然还是会者不难,难者不会。

回过头看,卡脖子的几个点,Ulog格式是怎样的,sprintf怎么用,如何写二进制,如何调用ulog,记录如下:

1 定义结构体时,pack push 1,没有pop,导致程序挂了,而且挂的还是别的地方,耗时2小时

#pragma pack(push, 1)
#pragma pack(pop)

2 ULog类名的L写成了小写Ulog没注意,编译提示找不到定义,一直以为是CMAKELIST自己没有添加对,耗时2小时
3 debug调试问题,clion debug没有,bin下面没有lib,vscode怎么调试C++,怎么设置debug模式?

4 CMakeList.txt 编译,全局变量多变量重复定义。
5 sprintf怎么用?

int format_len = snprintf(msg.format, sizeof(msg.format), "%s:", format_name.c_str());
format_len += snprintf(msg.format + format_len, sizeof(msg.format) - format_len,
                                   "%s", format[i].c_str());

6 二进制文件怎么写?

std::ofstream file(”test.ulg“, std::ios::out|std::ios::binary);
file.write((char *) &msg, msg_size);

7 vector怎么使用字符串初始化? 使用({})

std::vector<std::string> format_names({"format1","format2"});

8 怎么调用ulog?

使用单例模式,这样可以在全局都使用ulog,只需要初始化一个对象,不必在每个类中都定义一个ulog的对象,而且可以把写log到一个文件内。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值