# ULog 文件格式
ULog 是用于记录信息的文件格式。该格式具有自描述性,即包含格式和 uORB 记录的消息类型。本文档是 ULog 文件格式规范文档。它特别适用于有兴趣编写 ULog 分析器/序列器并需要对文件进行解码/编码的人员。
PX4 使用 ULog 将 uORB 主题记录为与以下来源相关(但不限于以下来源)的信息:
- 设备输入: 传感器、RC 输入等
- 内部状态: CPU 负载、姿态、EKF 状态等。
- 字符串信息:
printf
发言,包括PX4_INFO()
和PX4_ERR()
.
格式使用 小二进制 (打开新窗口) 所有二进制类型的内存布局(数据类型的最小有效字节 (LSB) 位于最低内存地址)。
# 数据类型
以下二进制类型用于记录日志。它们都与 C 语言中的类型相对应。
类型 | 大小(字节 |
---|---|
int8_t, uint8_t | 1 |
int16_t, uint16_t | 2 |
int32_t, uint32_t | 4 |
int64_t, uint64_t | 8 |
浮动 | 4 |
双人 | 8 |
bool, char | 1 |
此外,这些类型还可以作为数组使用:例如 浮点[5]
.
字符串 (字符[长度]
) 不包含终端 NULL 字符 '\0'
在最后。
备注
字符串比较是区分大小写的,在比较报文名称时应考虑到这一点。 添加订阅.
# ULog 文件结构
ULog 文件有以下三个部分:
---------------------- | 标题 | ---------------------- | 定义 | ---------------------- | 数据 | ----------------------
下文将对每个部分进行说明。
# 页眉部分
标头是固定大小的部分,格式如下(16 字节):
---------------------------------------------------------------------- | 0x55 0x4c 0x6f 0x67 0x01 0x12 0x35 | 0x01 | uint64_t | | 文件魔法 (7B) | 版本 (1B) | 时间戳 (8B) | ----------------------------------------------------------------------
- 文件魔术(7 个字节): 文件类型指示符读作 "ULogXYZ,其中 XYZ 是神奇字节序列
0x01 0x12 0x35
"; - 版本(1 个字节): 文件格式版本(目前为 1)
- 时间戳(8 字节):
uint64_t
整数,表示记录开始的时间(微秒)。
# 定义 & 数据部分 报文头
定义和数据 各节包含许多 信息.每个报文前都有一个报文头:
结构 消息标题 {
uint16_t msg_size;
uint8_t msg_type;
};
msg_size
是不含报文头的报文大小(字节)。msg_type
定义内容,是一个字符。
备注
下面的信息部分均以与其对应的字符作为前缀。 msg_type
.
# 定义部分
定义部分包含软件版本、报文格式、初始参数值等基本信息。
本节中的报文类型有
# 'B':标志位 信息
备注
该信息必须是 第一条信息 紧跟在文件头部分之后,因此它与文件开头的偏移量是固定不变的!
该信息为日志解析器提供日志是否可解析的信息。
结构 ulog_message_flag_bits_s {
结构 消息标题 页眉; // msg_type = 'B'
uint8_t compat_flags[8];
uint8_t 不兼容标记[8];
uint64_t 附加偏置[3]; 如果设置了追加位,则 // 追加数据的文件偏移量
};
compat_flags
:兼容标志位- 这些标志表示日志文件中存在与任何 ULog 分析器兼容的特征。
compat_flags[0]
: 默认参数 (位 0):如果设置,日志将包含 默认参数信息
这些位可用于 ULog 未来与现有解析器兼容的变更。例如,可以通过在标准中定义一个新位来添加新的信息类型,而现有的解析器将忽略新的信息类型。这意味着如果其中一个未知位被设置,解析器就可以忽略该位。
不兼容标记
不兼容标志位。incompat_flags[0]
: DATA_APPENDED (位 0):如果设置,则日志包含附加数据,并且至少有一个附加偏置
为非零。
其余位目前未定义,必须设置为 0。这可用于引入现有解析器无法处理的破坏性变化。例如,当一个旧的 ULog 分析程序没有 DATA_APPENDED 读取较新的 ULog 时,它会停止解析日志,因为日志中会包含不符合规格的信息/概念。如果解析器发现任何未指定的位被设置,就必须拒绝解析日志。
附加偏置
:附加数据的文件偏移量(以 0 为基准)。如果不追加数据,则所有偏移量都必须为 0。这可用于可靠地为可能在信息中间停止的日志添加数据。例如,崩溃转储。附加数据的进程应该这样做:
- 设置相关的
不兼容标记
位掩码 - 设置第一个
附加偏置
为日志文件的长度,因为新数据将从这里开始。 - 附加数据部分有效的任何类型的信息。
- 设置相关的
在未来的 ULog 规范中,可能会有更多字段附加在此报文的末尾。这意味着解析器不能假定此报文的长度是固定的。如果 msg_size
大于预期(目前为 40),任何额外的字节都必须被忽略/丢弃。
# 'F':格式信息
格式化信息以单个字符串定义单个信息名称及其内部字段。
结构 消息格式 {
结构 消息标题 页眉; // msg_type = 'F'
烧焦 格式[页眉.msg_size];
};
格式
是一个纯文本字符串,格式如下:message_name:field0;field1;
- 字段的数量可以任意设置(最少 1 个),字段之间用
;
.
- 字段的数量可以任意设置(最少 1 个),字段之间用
A 领域
格式如下 类型 字段名
或数组: 类型[数组长度] 字段名称
使用(只支持固定大小的数组)。
A 类型
是 基本二进制类型 或 消息名称
的格式定义(嵌套使用)。
- 类型在定义之前就可以使用。
- 例如:信息
信息A:信息B[2] msg_b
可以在MessageB:uint_8[3] 数据
- 例如:信息
- 可以任意嵌套,但 无循环依赖
- 例如
信息A:信息B[2] msg_b
及样品;信息B:信息A[4] msg_a
- 例如
有些字段名称比较特殊:
时戳
:每个 订阅信息 必须包含一个时间戳字段- 其类型可以是
uint64_t
(目前唯一使用的)、uint32_t
,uint16_t
或uint8_t
. - 单位始终是微秒,除非在
uint8_t
其中单位为毫秒。 - 时间戳必须始终是单调递增的。
msg_id
. - 在使用小时间戳数据类型(如
uint8_t
因此,日志编写者必须确保足够频繁地记录信息,以便能够检测到 包装 (当时间戳溢出数据类型并返回 0 时) - 在这种情况下,日志阅读器也必须处理环绕问题,并考虑掉线问题。
- 其类型可以是
_padding{}
:字段名称以_ 填充
例如_padding[3]
) 不应显示,其数据必须被阅读器忽略。- 这些字段可由写入者插入,以确保正确对齐。
- 如果填充字段是最后一个字段,则可以不记录该字段,以避免写入不必要的数据。
- 这意味着
message_data_s.data
将因填充的大小而缩短。 - 不过,在嵌套定义中使用报文时,仍然需要填充。
# 'I':信息留言
信息报文定义了字典类型定义 密钥
: 价值
对任何信息,包括但不限于硬件版本、软件版本、软件的构建工具链等。
结构 ulog_message_info_header_s {
结构 消息标题 页眉; // msg_type = 'I'
uint8_t key_len;
烧焦 密钥[key_len];
烧焦 价值[页眉.msg_size-1-key_len]
};
key_len
:键值长度密钥
:例如char[value_len] sys_toolchain_ver
价值
:包含数据(长度为value_len
)对应的密钥
例如9.4.0
.
备注
信息报文中定义的键值对应该是唯一的。也就是说,不应有多个具有相同键值的定义!
解析器可以字典形式存储信息。
预定义的信息报文有
密钥 | 说明 | 数值示例 |
---|---|---|
char[value_len] sys_name | 系统名称 | "PX4"; |
char[value_len] ver_hw | 硬件版本(板) | "PX4FMU_V4"; |
char[value_len] ver_hw_subtype | 执行局颠覆(变体) | "V2"; |
char[value_len] ver_sw | 软件版本(git 标签) | "7f65e01"; |
char[value_len] ver_sw_branch | git 分支 | "主人"; |
uint32_t ver_sw_release | 软件版本(见下文) | 0x010401ff |
char[value_len] sys_os_name | 操作系统名称 | "Linux"; |
char[value_len] sys_os_ve r | 操作系统版本(git 标签) | "9f82919"; |
uint32_t ver_os_release | 操作系统版本(见下文) | 0x010401ff |
char[value_len] sys_toolchain | 工具链名称 | "GNU GCC"; |
char[value_len] sys_toolchain_ver | 工具链版本 | "6.2.1"; |
char[value_len] sys_mcu | 芯片名称和修订版 | STM32F42x,修订版 A"; |
char[value_len] sys_uuid | 载具的唯一标识符(如 MCU ID) | "392a93e32fa3"... |
char[value_len] log_type | 日志类型(未指定时为完整日志) | 使命"; |
char[value_len] 重放 | 如果在重放模式下,重放日志的文件名 | "log001.ulg"; |
int32_t time_ref_utc | 以秒为单位的 UTC 时间偏移 | -3600 |
备注
value_len
的数据大小。 价值
.这在 密钥
.
- 的格式。
ver_sw_release
和版本号
是:0xAABBCCTT, 其中 AA 为 专业BB 是 辅修CC 是补丁,TT 是 类型.- 类型 定义如下
>= 0
发展、>= 64
:阿尔法版本、>= 128
:测试版、= 192
:RC 版本、== 255
发布版本。 - 例如
0x010402FF
转化为发布版本 v1.4.2。
- 类型 定义如下
该信息也可用于数据部分(但这是首选部分)。
# 'M':多种信息报文
多重信息报文的作用与信息报文相同,但适用于长报文或具有相同密钥的多个报文。
结构 ulog_message_info_multiple_header_s {
结构 消息标题 页眉; // msg_type = 'M'
uint8_t is_continued; // 可用于数组
uint8_t key_len;
烧焦 密钥[key_len];
烧焦 价值[页眉.msg_size-2-key_len]
};
is_continued
可用于分割报文:如果设置为 1,则它是具有相同密钥的上一条报文的一部分。
解析器可将所有信息以二维列表的形式存储,其顺序与日志中出现的信息顺序相同。
# 'P':参数信息
中的参数信息 定义 部分定义了启动日志记录时载具的参数值。其格式与 信息留言.
结构 消息信息 {
结构 消息标题 页眉; // msg_type = 'P'
uint8_t key_len;
烧焦 密钥[key_len];
烧焦 价值[页眉.msg_size-1-key_len]
};
如果参数在运行时发生动态变化,该信息也可以是 用于数据部分 也是如此。
数据类型仅限于 int32_t
和 浮动
.
# 'Q':默认参数信息
默认参数信息定义了特定载具和设置的参数默认值。
结构 ulog_message_parameter_default_header_s {
结构 消息标题 页眉; // msg_type = 'Q'
uint8_t 默认类型;
uint8_t key_len;
烧焦 密钥[key_len];
烧焦 价值[页眉.msg_size-2-key_len]
};
默认类型
是一个位字段,用于定义该值属于哪个组。- 必须至少设置一位:
1<<0
:全系统默认1<<1
:当前配置(例如机身)的默认值
- 必须至少设置一位:
日志可能不包含所有参数的默认值。在这种情况下,缺省值等于参数值,不同的缺省类型会被独立处理。
该信息也可用于数据部分,数据类型仅限于 int32_t
和 浮动
.
本节在第一节开始前结束。 订阅信息 或 记录 信息,以先到者为准。
# 数据部分
中的信息类型 数据 部分是:
# A
:订阅信息
通过名称订阅信息,并为其赋予一个在 记录的数据 信息.这必须在第一个相应的 记录的数据 信息.
结构 添加已登录消息 {
结构 消息标题 页眉; // msg_type = 'A'
uint8_t multi_id;
uint16_t msg_id;
烧焦 消息名称[页眉.msg_size-3];
};
multi_id
信息格式:同一信息格式可以有多个实例,例如系统有两个相同类型的传感器。默认情况下,第一个实例必须为 0。msg_id
:与之匹配的唯一 ID 记录的数据 信息 数据。首次使用时必须将其设置为 0,然后再增加。- 相同
msg_id
不得重复用于不同的订阅。
- 相同
消息名称
:要订阅的信息名称。必须与 信息格式 定义。
# R
:退订信息
退订信息,标记不再记录该信息(目前未使用)。
结构 删除已登录消息 {
结构 消息标题 页眉; // msg_type = 'R'
uint16_t msg_id;
};
# 'D':记录的数据信息
结构 消息数据 {
结构 消息标题 页眉; // msg_type = 'D'
uint16_t msg_id;
uint8_t 数据[页眉.msg_size-2];
};
有关填充字段的特殊处理,请参阅上文。
# 'L':记录的字符串信息
记录的字符串信息,即 printf()
输出。
结构 消息记录 {
结构 消息标题 页眉; // msg_type = 'L'
uint8_t 日志级别;
uint64_t 时戳;
烧焦 信息[页眉.msg_size-9]
};
时戳
单位:微秒日志级别
:与 Linux 内核相同:
名称 | 水平值 | 意义 |
---|---|---|
应急 | '0' | 系统无法使用 |
警报 | '1' | 必须立即采取行动 |
CRIT | '2' | 关键条件 |
ERR | '3' | 错误条件 |
警告 | '4' | 警告条件 |
注意事项 | '5' | 正常但重要的条件 |
信息 | '6' | 信息 |
DEBUG | '7' | 调试级信息 |
# 'C':标记的记录字符串信息
结构 标记的消息记录 {
结构 消息标题 页眉; // msg_type = 'C'
uint8_t 日志级别;
uint16_t 标签;
uint64_t 时戳;
烧焦 信息[页眉.msg_size-9]
};
标签
:代表日志信息字符串来源的 id。它可以代表进程、线程或类,具体取决于系统结构。- 例如,针对运行多个进程以控制不同有效载荷、外部磁盘和串行设备等的机载计算机的参考实现,可以使用一个
uint16_t 枚举
进入标签
属性如下
枚举 类 ulog_tag : uint16_t { 未分配, mavlink_handler, ppk_handler, 相机处理程序, ptp_handler, 串行处理程序, 监察人, io_service, cbuf, ulg };
- 例如,针对运行多个进程以控制不同有效载荷、外部磁盘和串行设备等的机载计算机的参考实现,可以使用一个
时戳
单位:微秒日志级别
:与 Linux 内核相同:
名称 | 水平值 | 意义 |
---|---|---|
应急 | '0' | 系统无法使用 |
警报 | '1' | 必须立即采取行动 |
CRIT | '2' | 关键条件 |
ERR | '3' | 错误条件 |
警告 | '4' | 警告条件 |
注意事项 | '5' | 正常但重要的条件 |
信息 | '6' | 信息 |
DEBUG | '7' | 调试级信息 |
# 'S':同步信息
同步信息,这样阅读器就可以通过搜索下一条同步信息来恢复已损坏的信息。
结构 消息同步 {
结构 消息标题 页眉; // msg_type = 'S'
uint8_t 同步魔法[8];
};
同步魔法
:[0x2F、0x73、0x13、0x20、0x25、0x0C、0xBB、0x12] (0x2F, 0x73, 0x13, 0x20, 0x25, 0x0C, 0xBB, 0x12)
# 'O':辍学信息
标记以毫秒为单位、持续时间为给定值的中断(丢失日志信息)。
例如,如果设备速度不够快,就会出现掉线现象。
结构 消息注销 {
结构 消息标题 页眉; // msg_type = 'O'
uint16_t 会期;
};
# 与定义科共享的信息
由于 "定义 "部分和 "数据 "部分使用相同的报文头格式,因此它们也共享下面列出的相同报文:
# 对解析器的要求
有效的 ULog 解析器必须满足以下要求:
- 必须忽略未知信息(但可以打印警告信息)
- 还能解析未来/未知的文件格式版本(但会打印警告)。
- 必须拒绝解析包含未知不兼容位的日志 (
不兼容标记
的 标志位 信息) 表示日志中包含解析器无法处理的破坏性更改。 - 解析器必须能够正确处理在信息中间突然结束的日志。未完成的信息应直接丢弃。
- 对于附加数据:解析器可以假设数据部分存在,即偏移量指向定义部分之后的位置。
- 附加数据必须被视为常规数据部分的一部分。
# 已知的解析器实现
- PX4-Autopilot:C++
- 记录仪模块 (打开新窗口)
- 重放模块 (打开新窗口)
- 硬故障日志模块 (打开新窗口):附加硬故障碰撞数据。
- pyulog (打开新窗口):带有 CLI 脚本的 python、ULog 读写器库。
- ulog_cpp (打开新窗口):C++、ULog 读写器库。
- 飞行图 (打开新窗口):Java, 日志绘图仪。
- MAVLink (打开新窗口):通过 MAVLink 流式传输 ULog 的信息(注意不支持附加数据,至少不支持截断信息)。
- QGroundControl (打开新窗口):C++、通过 MAVLink 传输的 ULog 流以及用于 GeoTagging 的最低限度解析。
- mavlink-router (打开新窗口):C++、通过 MAVLink 的 ULog 流。
- MAVGA 分析 (打开新窗口):Java、通过 MAVLink 传输的 ULog 流以及用于绘图和分析的解析器。
- 情节魔术师 (打开新窗口):用于绘制日志和时间序列的 C++/Qt 应用程序。自 2.1.3 版起支持 ULog。
- ulogreader (打开新窗口):Javascript、ULog 阅读器和解析器以 JSON 对象格式输出日志。