📚 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
)