📚 ULog 日志系统详解
关键词:结构化日志、飞行数据记录、自描述格式、嵌入式系统、PX4、NextPilot
🧠 一、ULog 是什么?
ULog(Universal Log) 是 PX4/NextPilot 飞控系统中使用的结构化日志格式,用于记录飞行过程中的传感器数据、参数、状态信息、错误日志等。
✅ 主要用途:
- 飞行数据记录(如 IMU、GPS、电池状态)
- 参数变化记录(PID 调整、RC 映射等)
- 调试输出(如 PX4_INFO、PX4_WARN、PX4_ERR)
- 系统状态分析(如 CPU 负载、内存使用)
🧱 二、ULog 的核心特性
特性 | 描述 |
---|
结构化 | 每条日志都有固定格式,便于程序解析 |
自描述 | 日志文件本身包含数据结构定义,无需外部依赖 |
可扩展性强 | 支持多种消息类型,满足不同场景需求 |
高效性 | 使用缓冲机制减少频繁 I/O 操作 |
兼容性 | 支持版本控制,避免日志无法解析 |
跨平台 | 可在 PX4、ROS、Python、MATLAB 等平台解析 |
📦 三、ULog 文件结构
ULog 文件由多个**消息(Messages)**组成,每个消息都包含一个通用头部(ulog_message_header_s
),后续数据根据消息类型不同而变化。
✅ 1. 文件头(ulog_file_header_s
)
struct ulog_file_header_s {uint8_t magic[8]; uint64_t timestamp;
};
- magic:固定值
0x16 0x2d 0x3a
,ASCII 字符为 "LOGxxxxxx"
,用于识别文件类型。 - timestamp:记录日志的起始时间,用于时间戳对齐。
✅ 2. 通用消息头(ulog_message_header_s
)
struct ulog_message_header_s {uint16_t msg_size; uint8_t msg_type;
};
- msg_size:消息体的字节数。
- msg_type:消息类型,用于解析后续数据结构。
✅ 3. 消息类型(ULogMessageType
)
enum class ULogMessageType : uint8_t {FORMAT = 'F',DATA = 'D',INFO = 'I',INFO_MULTIPLE = 'M',PARAMETER = 'P',PARAMETER_DEFAULT = 'Q',ADD_LOGGED_MSG = 'A',REMOVE_LOGGED_MSG = 'R',SYNC = 'S',DROPOUT = 'O',LOGGING = 'L',LOGGING_TAGGED = 'C',FLAG_BITS = 'B',
};
常用消息类型说明:
类型 | 字符 | 含义 |
---|
'F' | FORMAT | 定义数据结构(如 sensor_combined:uint64_t timestamp;float x;float y; ) |
'D' | DATA | 记录实际数据(如传感器数值) |
'I' | INFO | 记录键值对信息(如 sys_toolchain_ver9.4.0 ) |
'P' | PARAMETER | 记录参数值(如 RC_MAP_THROTTLE=1100 ) |
'Q' | PARAMETER_DEFAULT | 记录默认参数值(用于对比是否被修改过) |
'A' | ADD_LOGGED_MSG | 订阅某个日志主题(如 sensor_combined ) |
'R' | REMOVE_LOGGED_MSG | 取消订阅某个日志主题 |
'S' | SYNC | 同步消息,用于日志文件的同步 |
'O' | DROPOUT | 记录数据丢失时间长度(如 duration=100ms ) |
'L' | LOGGING | 记录调试日志(如 PX4_INFO("Debug message") ) |
'B' | FLAG_BITS | 标记日志文件的兼容性标志 |
📝 四、ULog 数据结构详解
✅ 1. ulog_message_format_s
—— 数据格式定义
struct ulog_message_format_s {uint16_t msg_size; uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::FORMAT);char format[1500];
};
✅ 2. ulog_message_add_logged_s
—— 添加日志主题
struct ulog_message_add_logged_s {uint16_t msg_size; uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::ADD_LOGGED_MSG);uint8_t multi_id; uint16_t msg_id; char message_name[255];
};
✅ 3. ulog_message_data_s
—— 数据记录
struct ulog_message_data_s {uint16_t msg_size;uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::DATA);uint16_t msg_id;
};
- 作用:记录实际数据(如传感器数值)。
- 数据结构:
msg_id
:关联到 ADD_LOGGED_MSG
中的 msg_id
- 后续数据:根据
FORMAT
定义的数据结构存储
✅ 4. ulog_message_info_s
和 ulog_message_info_multiple_s
—— 信息记录
struct ulog_message_info_s {uint16_t msg_size;uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::INFO);uint8_t key_len; char key_value_str[255];
};
✅ 5. ulog_message_parameter_s
和 ulog_message_parameter_default_s
—— 参数记录
struct ulog_message_parameter_s {uint16_t msg_size;uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::PARAMETER);uint8_t key_len;char key_value_str[255];
};
- 作用:记录系统参数(如 PID 参数、RC 映射等)。
- 区别:
'P'
:当前参数值。'Q'
:默认参数值(用于对比是否被修改过)。
✅ 6. ulog_message_dropout_s
—— 丢包信息
struct ulog_message_dropout_s {uint16_t msg_size = sizeof(uint16_t); uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::DROPOUT);uint16_t duration;
};
- 作用:记录数据丢失的时间长度。
- 使用场景:当系统无法实时记录所有数据时,插入此消息表示“这段数据可能丢失”。
✅ 7. ulog_message_logging_s
和 ulog_message_logging_tagged_s
—— 日志输出
struct ulog_message_logging_s {uint16_t msg_size;uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::LOGGING);uint8_t log_level; uint64_t timestamp;char message[128];
};
- 作用:记录调试信息(如
PX4_INFO("Debug message")
输出的内容)。 - 使用场景:用于调试飞行控制逻辑,定位问题。
✅ 8. ulog_message_flag_bits_s
—— 兼容性标志
struct ulog_message_flag_bits_s {uint16_t msg_size;uint8_t msg_type = static_cast<uint8_t>(ULogMessageType::FLAG_BITS);uint8_t compat_flags[8]; uint8_t incompat_flags[8]; uint64_t appended_offsets[3];
};
- 作用:用于标记日志文件的兼容性信息。
- 使用场景:新版本飞控写入的文件,旧版本工具可以读取并判断是否兼容。
🔄 五、ULog 的工作流程
✅ 1. 初始化日志文件
ulog_file_header_s header;
header.magic = "LOGxxxxxx";
header.timestamp = hrt_absolute_time();
write(fd, &header, sizeof(header));
✅ 2. 定义数据结构(FORMAT 消息)
ulog_message_format_s format_msg;
format_msg.msg_type = 'F';
strcpy(format_msg.format, "sensor:uint64_t timestamp;float x;float y;float z;");
write(fd, &format_msg, sizeof(format_msg) + strlen(format_msg.format));
✅ 3. 订阅日志主题(ADD_LOGGED_MSG)
ulog_message_add_logged_s add_msg;
add_msg.msg_type = 'A';
add_msg.multi_id = 0;
strcpy(add_msg.message_name, "sensor_combined");
write(fd, &add_msg, sizeof(add_msg) + strlen(add_msg.message_name));
✅ 4. 写入数据(DATA 消息)
ulog_message_data_s data_msg;
data_msg.msg_type = 'D';
data_msg.msg_id = 0x12;
sensor_combined_s data;
data.timestamp = hrt_absolute_time();
data.x = 1.2f;
data.y = 3.4f;
data.z = 5.6f;write(fd, &data_msg, sizeof(data_msg));
write(fd, &data, sizeof(data));
✅ 5. 记录日志信息(INFO / PARAMETER)
ulog_message_info_s info_msg;
info_msg.msg_type = 'I';
strcpy(info_msg.key_value_str, "sys_toolchain_ver9.4.0");write(fd, &info_msg, sizeof(info_msg) + strlen(info_msg.key_value_str));
✅ 6. 记录丢包信息(DROPOUT)
ulog_message_dropout_s dropout;
dropout.msg_type = 'O';
dropout.duration = 100; write(fd, &dropout, sizeof(dropout));
✅ 7. 文件尾部标志(可选)
某些日志系统会写入一个 EOF
标志,表示日志结束。
📊 六、ULog 的典型应用场景
场景 | 使用的消息类型 |
---|
记录传感器数据 | 'D' (DATA) |
记录参数设置 | 'P' (PARAMETER) |
记录日志输出 | 'L' (LOGGING) |
描述数据结构 | 'F' (FORMAT) |
记录丢包信息 | 'O' (DROPOUT) |
标记文件兼容性 | 'B' (FLAG_BITS) |
🧩 七、ULog 的设计亮点
亮点 | 描述 |
---|
模块化设计 | 4个日志主题独立管理,便于扩展 |
异步写入 | 使用后台线程处理 I/O,不影响主线程性能 |
线程安全 | 使用 mutex + condition variable 保证线程安全 |
高性能优化 | 使用缓冲机制减少频繁写磁盘操作 |
加密支持 | 可选编译项,增强安全性 |
性能统计 | 使用 perf_counter 记录写入和 fsync 时间 |
🧠 八、ULog 的优势总结
优势 | 描述 |
---|
结构化存储 | 每条日志都有固定格式,便于解析 |
自描述性 | 每条数据都包含格式信息 |
兼容性设计 | 支持新旧版本兼容,避免日志无法解析 |
跨平台支持 | 可用 QGroundControl、Python、MATLAB 解析 |
加密功能 | 保护敏感飞行数据 |
调试友好 | 支持调试输出、参数变化记录 |
📌 九、ULog 文件示例(简化版)
[File Header] // 文件魔数和起始时间
[F] sensor:uint64_t timestamp;float x;float y;float z;
[A] msg_id=0x12, message_name=sensor_combined
[D] msg_id=0x12, data=0x12345678 0x00000000 0x40000000 0x40800000
[I] sys_toolchain_ver9.4.0
[P] RC_MAP_THROTTLE=1100
[O] duration=100ms
📈 十、ULog 的文件格式示意图
[File Header] // 文件魔数 + 时间戳
[FORMAT Messages] // 描述各个日志主题的结构
[ADD_LOGGED_MSG] // 定义哪些主题被记录
[DATA Messages] // 实际记录的数据(传感器、参数等)
[INFO / PARAMETER Messages] // 元信息和参数记录
[DROPOUT Messages] // 丢包信息
[FLAG_BITS Messages] // 兼容性标志
🧱 十一、ULog 的典型使用流程
Logger logger(LogWriter::BackendFile, 1024 * 1024);
logger.start_log_file(LogType::Full, "/mnt/microsd/log001.ulg");
logger.write_message(LogType::Full, data_ptr, data_size);
logger.stop_log_file(LogType::Full);
📌 十二、移植建议(从 PX4/NextPilot 到其他系统)
✅ 保留核心功能
LogWriterFile
:文件日志写入器ULogMessageType
:消息类型定义LogFileBuffer
:缓冲区管理write_message()
:日志写入接口
✅ 可选功能
- 加密支持(
PX4_CRYPTO
) - 性能统计(
perf_counter
) - 多线程支持(
pthread
)