2. 日志文件结构解析:二进制日志与文本日志的区别、数据帧结构、时间戳解析

好,咱们进入第二个实战环节。日志文件,说白了就是无人机飞控系统留给我们的「黑匣子」。你拿到手的日志,可能是 .bin 文件,也可能是 .log 或 .txt 文件。这两种格式,差别可不小。

2.1 二进制日志 vs 文本日志:一个省空间,一个省脑子

我个人习惯,外场测试时优先保存二进制日志。为什么?因为飞控的存储空间太金贵了。二进制日志,说白了就是一堆 0 和 1 的紧凑排列,一个浮点数只占 4 个字节。文本日志呢?同样一个数字 "3.14159",存成字符串要 7 个字节,加上逗号、换行符,轻松翻倍。

我遇到过一位同事,为了调试方便,把日志全设成了文本格式。结果飞了 20 分钟,存储卡就满了,关键数据没录下来。嗯,这是个教训。

对比项 二进制日志 文本日志
存储效率 极高,压缩比可达 5:1 低,大量冗余字符
可读性 需要专用解析工具 记事本直接打开
解析速度 快,直接内存映射 慢,需要字符串转换
典型场景 长时间飞行、高速采样 调试阶段、小规模测试
我的建议: 外场正式测试用二进制,内场调试用文本。或者像我一样,飞控同时输出两种格式——二进制存完整数据,文本只存关键状态摘要。

2.2 数据帧结构:日志里的「乐高积木」

不管二进制还是文本,日志数据都不是乱堆的。它们遵循固定的帧结构。你想想看,飞控每秒要记录几百个传感器数据,如果没有统一格式,解析时根本不知道哪段是加速度计,哪段是 GPS。

一个典型的数据帧,长这样:

| 帧头 (2字节) | 消息ID (1字节) | 时间戳 (4字节) | 数据负载 (N字节) | 校验和 (1字节) |
|    0xFE 0xEF  |     0x01       |   0x12345678   |   acc_x, acc_y... |    0xAB       |

帧头是固定的,比如 0xFE 0xEF,用来做同步。解析器一读到这个,就知道「新的一帧来了」。消息 ID 告诉你是哪种数据——0x01 是 IMU,0x02 是 GPS,0x03 是电机状态。时间戳是重中之重,我们马上细说。

我曾经在解析一个老版本日志时,发现帧头被改成了 0xEF 0xFE。结果整个解析器全乱套了。从那以后,我养成了一个习惯:先看帧头,再谈解析

避坑指南: 不同飞控固件版本,帧结构可能微调。比如 PX4 从 v1.11 到 v1.12,消息 ID 的编码方式就变了。解析前,一定要确认固件版本。

2.3 时间戳解析:把「时间」对齐了再说

时间戳,是日志分析里最容易出问题的地方。为什么?因为飞控里有好几个「时钟」在同时跑。

  • 系统时钟:飞控上电后开始计数的微秒数,精度高,但会溢出。
  • GPS 时钟:UTC 时间,精确到纳秒,但信号不好时会跳变。
  • 传感器时钟:每个传感器有自己的时间基准,比如 IMU 是 1kHz 的固定间隔。

我遇到过最头疼的一次:GPS 时间戳突然跳了 1 秒,导致整个航迹回放时位置和姿态对不上。排查了半天,才发现是 GPS 卫星切换时的时间校正。

解析时间戳时,我一般这么做:

// 伪代码示例:统一时间基准
uint64_t system_time_us = get_system_time();  // 飞控上电后的微秒数
uint64_t gps_time_us = gps_tow_to_us(gps_week, gps_tow);  // GPS 周内秒转微秒

// 关键:找到两个时钟的偏移量
int64_t time_offset = system_time_us - gps_time_us;

// 之后所有传感器数据,都统一到系统时间基准
sensor.timestamp_sys = sensor.timestamp_raw + time_offset;
注意: 时间戳的精度直接影响故障定位。比如分析「电机响应延迟」,如果时间戳误差超过 1 毫秒,结论就不可靠了。我一般要求时间戳同步精度在 100 微秒以内。

2.4 实战:手撕一段二进制日志

光说不练假把式。咱们拿一段真实的二进制日志来拆解。假设你拿到的是 PX4 的 .ulog 文件,用 hexdump 看前 64 个字节:

00000000  55 4c 6f 67 01 00 00 00  00 00 00 00 00 00 00 00  |ULog............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

看到没?前 4 个字节是魔数 "ULog",标识这是 PX4 的二进制日志格式。后面跟着版本号、时间戳偏移等信息。解析时,程序就是靠这个魔数来判断文件类型的。

再往下翻,你会看到各种消息块:

// 消息类型定义
#define MSG_TYPE_ADD_LOGGED   0x02  // 添加一个日志变量
#define MSG_TYPE_DATA         0x03  // 实际数据
#define MSG_TYPE_INFO         0x04  // 元信息(参数、版本等)

每个消息块,都遵循我们刚才说的帧结构。解析器按顺序读,读到 MSG_TYPE_DATA 时,就知道后面跟着的是传感器数值。

小技巧: 如果你不想自己写解析器,可以用 PX4 官方提供的 pyulog 库。我经常用它把二进制日志转成 CSV,再用 Excel 或 Python 做进一步分析。但记住,理解底层结构才能用好工具。

2.5 总结:日志结构是故障定位的「地图」

二进制和文本日志,各有各的用武之地。数据帧结构,是解析的骨架。时间戳,是串联所有数据的灵魂。这三者搞明白了,你手里的日志就不再是一堆乱码,而是一张清晰的飞行状态地图。

下一章,咱们聊聊怎么从这张地图里,找到故障的蛛丝马迹。