Wiki
实时循环与异步通信
通信线程负责等设备,控制线程负责按周期处理最新数据。本节先把线程与数据所有权划清楚,再接串口。
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 总览 · 上一篇:管理器与资源管理源码 · 下一篇:虚拟串口与模拟下位机