【NextPilot日志移植】logged_topics.cpp解析

发布于:2025-05-10 ⋅ 阅读:(19) ⋅ 点赞:(0)

📘 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 模块的内部结构

  1. 调用 add_topic()
    把主题信息写入 TopicSubscriber 对象的队列或映射表中。

  2. 启动 logger 时统一订阅
    logger 在初始化或任务启动时遍历所有已登记主题,调用 orb_subscribe() 实际订阅。

  3. 定时记录数据
    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 日志中是否能看到该主题。

网站公告

今日签到

点亮在社区的每一天
去签到