【NextPilot日志移植】logged_topics.cpp解析
📘 PX4 Logger 模块注册 uORB 主题、实际订阅与数据采集流程
🧭 目的与背景
在 PX4 飞控中,日志记录模块 logger
需要记录多个 uORB 主题的数据(如 IMU、GPS、姿态等)。为了系统统一管理这些记录需求,logger 提供了 add_topic()
接口用于“登记想记录的 uORB 主题”,实现模块内部集中管理与订阅。
🧩 logger::add_topic() 的本质作用
topics.logger::add_topic("sensor_accel", 100, TopicSubscriber::IntervalMeasureMode::Exact, 0);
✅ 实际作用
- 向 logger 模块登记想要记录的 uORB 主题;
- 记录这些主题的名称、记录频率、测量模式等信息;
- 后续由 logger 内部统一处理订阅与记录。
❌ 不包括的操作
- 不等同于调用
orb_subscribe()
,不会立即订阅; - 不等同于调用
orb_advertise()
,不会注册/创建新主题; - 主题必须已经存在于系统中并由其他模块发布。
🗂️ 参数详解
参数 | 含义说明 |
---|---|
name | uORB 主题名(如 "sensor_accel" ) |
interval_ms | 记录频率,单位 ms(如 100 表示每 100 ms 记录一次) |
measure_mode | 测量模式,如 Exact 表示严格按周期记录 |
instance | 主题实例号(用于处理多个 IMU 等多实例情况) |
🧱 logger 模块的内部结构
-
调用
add_topic()
把主题信息写入TopicSubscriber
对象的队列或映射表中。 -
启动 logger 时统一订阅
logger 在初始化或任务启动时遍历所有已登记主题,调用orb_subscribe()
实际订阅。 -
定时记录数据
logger 根据每个主题的interval_ms
周期读取数据并写入日志文件(通常是ulog
格式)。
🧮 简要流程图(逻辑)
代码中 add_topic()↓
记录待订阅主题信息↓
logger 启动时统一 orb_subscribe()↓
定时读取并写入日志文件
🧾 示例:logger_main.cpp 中注册主题
// 典型注册方式
topics.logger::add_topic("vehicle_attitude", 50); // 每 50 ms 记录一次姿态
topics.logger::add_topic("sensor_gyro", 100); // 每 100 ms 记录一次陀螺仪
🧠 延伸理解:为什么不直接订阅?
如果每个地方都调用 orb_subscribe()
:
- 会造成重复订阅、资源浪费;
- 不好统一管理日志频率;
- 无法自动匹配多实例等情况。
logger 统一订阅后可:
- 动态调整频率;
- 控制记录启停;
- 集中写入磁盘。
🧩 回顾:add_topic() 只是登记
调用 logger::add_topic("topic_name", ...)
后,主题信息被保存在 logger 的内部容器中(如 TopicSubscriber
列表),此时并未真正执行订阅与数据读取操作。
🚀 真正订阅:在 logger::initialize_topics()
中统一完成
✅ 发生时机
logger 启动后,在主循环启动前调用:
bool Logger::initialize_topics()
✅ 核心代码流程
for (TopicSubscriber &sub : _subscriptions) {sub.subscribe(); // 真正调用 orb_subscribe()
}
这里 TopicSubscriber
是对每个 add_topic() 的封装对象,subscribe()
方法内部包含:
_orb_sub_fd = orb_subscribe_multi(_topic_name, _instance);
✅ 此时才真正使用
orb_subscribe
完成对每个 uORB 主题的订阅!
📦 数据采集:在 logger 主循环中周期读取(调用 orb_copy()
)
核心代码位置
文件路径:
src/modules/logger/logger.cpp
函数位置:
void Logger::run()
主循环中采集逻辑:
while (!should_exit()) {for (TopicSubscriber &sub : _subscriptions) {if (sub.should_copy()) {sub.copy();}}
}
🧠 详细解释:sub.copy() 内部做了什么?
bool TopicSubscriber::copy()
{orb_copy(_topic_id, _orb_sub_fd, &_data); // 读取最新数据_log_writer.write(_data); // 写入日志缓冲区return true;
}
✅ 每次循环判断是否到了记录周期(通过
should_copy()
),如果到了,就执行orb_copy()
把数据读出来,并调用ulog_stream
写入磁盘日志文件中。
🗂️ 总结:logger 记录数据的完整生命周期
add_topic() → 只是登记主题信息
initialize_topics() → 统一 orb_subscribe() 实际订阅
Logger::run() → 周期判断是否采样
TopicSubscriber::copy() → orb_copy() + 写入日志
🧪 补充调试建议
可通过日志或断点验证以下内容:
logger::initialize_topics()
中的订阅是否成功(检查_orb_sub_fd
是否有效);logger::run()
中是否触发copy()
;orb_copy()
返回是否正常;ulog
日志中是否能看到该主题。