• 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1 Header

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

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

    2 Definitions

    Format

    struct message_format_s {
    	struct message_header_s header;
    	char format[header.msg_size];
    };
    
    • 1
    • 2
    • 3
    • 4

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

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

    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];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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}
    ...................
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    后记

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

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

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

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

    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());
    
    • 1
    • 2
    • 3

    6 二进制文件怎么写?

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

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

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

    8 怎么调用ulog?

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

  • 相关阅读:
    libmp4v2不完全指南封装g711a的坑
    【C语言】学生宿舍信息管理系统
    Springboot毕设项目线上影院系统ao90djava+VUE+Mybatis+Maven+Mysql+sprnig)
    泛型内容总结
    UE4-UMG点击播放关卡序列(Level Sequence)
    饮品类公众号引流到企微,搭建私域模型,实现粉丝快速增长
    安卓学习中遇到的问题【bug】
    淘宝/天猫、1688、京东API接口—item_search - 按关键字搜索淘宝商品
    反射读取数组导出
    Linker Script链接脚本说明
  • 原文地址:https://blog.csdn.net/u012686154/article/details/125512547