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;"
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},
整体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 ULogL的类名写成小写Ulog没注意,编译提示找不到定义,一直以为是CMAKELIST我没有加对,花了2个小时 3 debug调试问题,clion debug没有,bin下面没有lib,vscode怎么调试C ,怎么设置debug模式?
4 CMakeList.txt 重复定义全局变量多变量。 5
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);
std::vector<std::string> format_names({"format1","format2"});
8 怎么调用ulog?
单例模式的使用可以全局使用ulog,一个对象只需要初始化,不需要在每个类别中定义一个对象ulog对象,可以写log在一份文件中。