Skip to content

Wiki

实时循环与异步通信

About 4 min read

Existing English translations were reused. 24 current source blocks remain in Simplified Chinese, without triggering paid translation.

通信线程负责等设备,控制线程负责按周期处理最新数据。本节先把线程与数据所有权划清楚,再接串口。

1.示例 1:把两条执行路径画出来

通信线程
  接收字节 → 拼帧 → 校验 → 保存完整反馈快照
  读取待发目标 → 打包 → 异步写设备

控制线程
  read → 取最新完整反馈,更新状态接口
  update → 控制器处理目标与反馈
  write → 保存待发目标,不等待设备回复

串口可能以 50 Hz 返回,控制循环以 100 Hz 运行。某一轮没有新反馈时,可以使用上一份仍有效的快照;超过明确的超时时间则进入错误处理。

2.示例 2:阅读 realtime buffer

打开 realtime_tools/include/realtime_tools/realtime_buffer.hpp,定位:

  • writeFromNonRT(data):非实时侧写入。
  • readFromRT():实时侧尝试取得最新数据。

当前版本 readFromRT 使用 try-lock;拿不到锁时继续返回现有实时侧数据。它不是“每读一次都等待写线程完成”。writeFromNonRT 则可能等待,所以不能因为类型名字包含 realtime,就把所有成员函数都放进实时循环。

2.1.为什么控制器采用固定大小的 Target

在从零编写控制器中,目标快照只有两个 double 和一个标记。数据验证在接收回调完成,update 只读快照并写接口,减少控制周期中的字符串处理与动态分配。

readFromRT() 返回的指针不应被任意线程长期保存后继续访问。按类的并发约定使用,并在当前 update 中复制需要的数据。

3.示例 3:判断一段 read 是否合适

下面是反例伪代码,不应作为插件实现:

read:
  阻塞读取,直到收到完整一帧
  等待设备回复
  sleep 100 ms
  返回

如果一轮只有约 10 ms 预算,上述等待会拖延所有使用该管理器的控制器。

下一章提供可运行的学习实现:接收与写串口都在独立 Asio 线程;read/write 用 try-lock 交接固定大小数据;取不到锁保留上一份快照,并用反馈时间戳检查过期。

3.1.try-lock 是否就等于硬实时

不是。线程调度、驱动、原子操作实现、内存、日志和算法都影响实时性。下一章代码是可观察的通信教学实现,没有宣称经过硬实时认证。

4.数据交接的四个问题

问题 本教程的处理
一次反馈是否完整 先拼帧、解析、验证,再更新整组快照
数据是否过期 单调时钟记录最近有效反馈时间
消费速度低于产生速度 状态只保留最新值,丢弃过期快照
发包是否堆积 同一时刻只保留一个异步写操作

“只保留最新值”适合连续位置目标,未必适合使能、复位或离散任务。后者往往需要可靠队列、序号与确认机制。

5.两种超时必须区分

上游命令超时意味着目标不再更新;下游反馈超时意味着设备状态不再可信。差速控制器的 cmd_vel_timeout 不能代替硬件反馈看门狗,ROS 节点退出也不能代替下位机独立看门狗。

源码:realtime_tools/include/realtime_tools/realtime_buffer.hpp、ros2_controllers/forward_command_controller/src/forward_controllers_base.cpp。对照 writeFromNonRT 和 readFromRT 的调用线程,而不只看函数名。


返回 Humble 总览 · 上一篇:管理器与资源管理源码 · 下一篇:虚拟串口与模拟下位机