跳转至

关于技术路线的探讨


现有的机器人操作系统如经典的 ROS 系统,在我看来更像是一个通讯中间件+项目包管理的简单工具包,其余部分都是由机器人工程师去补充完善和现有开源社区的封装好的功能库。我的目标在于降低搭建机器人功能的学习成本并提高系统安全性与效率,受到 Rust 语言设计哲学和pytorch神经网络搭建的启发,我将多机器人的规划、控制等行为理解为多层级的节点,节点是系统逻辑的单元,每个节点具备一定的生命周期,并具有一个或多个机器人的只读引用,通过节点间信道:SegQueue 向下一节点传播当前节点的计算结果或信息。最终获得精简的任务约束指令,通过机器人总线 RobotBus 发送给 实物机器人/仿真机器人/动力学建模/工装等等。越接近物理层的节点应该具有更大的生命周期和权限,当某个节点抛出了错误,实际处理方式实际上是作为下一节点的特权态委托。多个节点与机器人共同完成某个功能的配置被称为任务,任务之间存在依赖关系,暂时的任务描述通过一个任务 json 文件进行。该 任务 json 文件作为系统内核的输入,系统内核会持续接受任务文件并解读其描述,并在线程管理器和内存管理器中创建节点并且搭建节点间通讯


通过“节点 + 信道 + 任务描述”的方式来构建一套统一的机器人任务管理与执行框架,并且借助 Rust 的强安全特性来提高系统的健壮性和性能。

从架构层面把“任务描述 → 节点图执行”固化下来

你可以将节点拓扑的管理和调度做成一个“统一的调度中心(或节点管理器)”,所有节点都向它注册依赖和数据流。 错误处理方面可引入Supervisor或监控树,不要让下游节点单方面承担所有异常处理逻辑。 引入更丰富的生命周期与状态机

让节点从 “创建 → 初始化 → 运行 → 错误 → 退出” 有完整的生命周期变迁,特别是硬件相关节点一旦出错,需要更好的重试、断开和恢复流程。 数据流 + 事件流并重

对于周期性数据流使用队列/内存池,对突发事件使用轻量级信号或回调通道,避免关键异常滞留在队列里难以及时处理。 先聚焦场景,逐步完善

在“多平台兼容、实时控制、热加载”这几块都想要做得面面俱到,工程复杂度非常高。可以先明确一个典型场景(例如:Linux + x86_64 + 实物机器人)把架构跑通,再依次抽象到其它平台。 注意API友好度与可维护性

你希望降低学习成本、让用户更快上手,就需要有一套清晰、稳定的API或SDK。Rust本身门槛就不算低,设计好对外接口和文档,对提升可用性很重要。

  1. 关于节点设计
  2. 目前采用 RwLock 作为读写锁的要求,从而实现存在多个节点可读机器人而只有一个节点可写机器人的设计。如果节点读取的过程非常平凡,同时有些节点需要写入机器人的控制状态,肯恶搞需要更加细粒度的锁或者无锁机制。避免在高并发场景下引发阻塞。
  3. 节点权限设计中,越接近物理层的节点应该具有更大的权限,是否考虑对这些节点做显式的生命周期管理?目前是存在的,我们通过节点行为特征(NodeBehavior)来规范节点行为。不同于 ROS2 中的 CL 状态机设计,我在线程管理器中强行约束的节点的的执行顺序(基于节点的行为特征)初始化 -> 循环运行 -> 销毁。线程管理器通过在每次调用节点行为特征之前使用节点行为特征状态函数来判断当前节点状态并自动推进节点状态。我不确定这是否是一个好的设计,但是这样可以有效减少机器人工程师在编写节点时的思考量,所有节点机器人工程师只需要编写 init/update/destory等节点行为特征函数即可,线程、内存、通讯由系统内核自动管理。当然,这也在一定程度上缩减了节点设计过程中的自由度。
  4. 节点内的执行模型
  5. 流量控制是我尚且正在考虑的一大难题,稍后我们将作为一个独立的问题再次讨论。目前的流量设计实际上与节点的执行频率紧密相关而且与很容易出现堵塞或者缺省的情况。
  6. 实时性也是一个重要的问题,目前通讯实时性可能有所欠缺,一方面在一些场合我需要实时内核来满足强实时的要求,另外一方面我也要照顾一些没有实时内核的设备。
  7. 节点的错误处理
  8. 硬件层的错误处理是一个很困难的问题,我计划将这一部分的保证交给机器人总线(Robot Bus)。RobotBus 不单单负责指令的传递、分发和状态的更新。同时还应该是一个内核到机器人之间的双向保证器,一方面向内核保证机器人持续可用且稳定连接(具体的错误处理尚未考虑),另外一方面向机器人保证内核的指令合法安全。
  9. 对于上层逻辑节点的报错,我考虑在节点之间引入环路,当上层节点报错时,该层节点向旁支发送错误信息,通过临时代理节点完成紧急处理并发送处理结果回本节点。但是这样的处理方案当运行时节点通路不可变时会新增大量备用节点,增大了开销,而在运行中管理节点通路是一件困难的事情,同时也不利于线程和权限管理,我不太想给节点加锁,因为这样会增加系统的复杂度。也会导致某些死锁现象。
  10. 任务定义与依赖
  11. 更加正式的任务依赖图或者执行图应该如何描述?目前我是在json中描述一张有向图来描述节点的依赖关系,通过任务入度决定当前任务是否可以被执行
  12. 关于热加载和动态删减节点,稍后我们作为一个独立的问题再次讨论
  13. 线程和内存管理
  14. 很遗憾,我采用的线程管理方式确实是根据json文件确定节点类型和参数之后就直接生成的,没有采用线程池和异步运行时,这或许是可以被精进的点,稍后我们作为一个独立的问题再次讨论
  15. 在单片机平台中,我计划使用系统时钟戳来代替多线程,尽量减少在单片机中对于标准库的使用。但是我此前并未听闻过 RTOS 或者 无操作系统式的调度架构,希望你能向我介绍和推荐
  16. 内存管理我有考虑过使用内存锁或者内存池来避免过多的内存分配,同时我要求在节点建立时应当通过一个结构体来描述在节点执行过程中需要在 update 中保留的参数,这将在节点开辟时被申请完毕,避免在节点循环过程中频繁的内存分配和释放
  17. 数据流 and 事件驱动
  18. 关于事件触发和事件触发,我们稍后作为一个独立的问题再次讨论
  19. 多平台适配
  20. 由于常见 pc 设备与 单片机等设备差异过大,我计划在单片机上使用一个更加轻量级的内核,改内核与本体具有相同的通讯方法和节点描述方法,但是采用更加符合单片机等设备的触发和内存管理方式。
  21. 总线与外设接口
  22. 这里要说明的是,这里指的 RobotBus 更多的是一种对于机器人任务、指令、约束的协议,而非通讯用网络协议。所以 RobotBus 与工业标准的通信协议并不冲突,核心在于通讯内容中包含的约束和指令的描述方式。
  23. 安全性和性能
  24. 安全性和性能将是本系统设计时最关键的要素。
  25. 关于实时性和热加载的问题,我们稍后作为一个独立的问题再次讨论

下面我会针对你目前的设计思路逐条进行审查,并给出一些可参考的技术建议或改进思路。整体来看,你的方案在“节点 + 信道 + 任务描述”的基础上,通过Rust的安全模型来构建高效和稳健的系统,方向是可行的;但在并发细节、错误处理、生命周期管理、嵌入式适配等方面还有可以进一步完善的空间。

  1. RwLock 作为读写锁的方案
  2. 可行性:单个写者+多个读者的并发读写模型,足以应对大多数“上层只读、底层少量写”场景。
  3. 潜在问题:当读操作非常密集,写操作也频繁出现时,阻塞还是难以避免;或者如果读操作本身耗时较长,写操作会被长时间排队。
  4. 可改进方向

    • 更加细粒度的锁(例如针对不同资源区分不同锁,甚至分层/分段锁)。
    • 无锁结构(Lock-Free)的队列或通道,比如使用 crossbeamatomic 原语,但需要仔细评估复杂度与可维护性。
    • 对性能敏感的场景,可以将写入操作集中在“短暂的独占区”,然后快速释放锁,让读者更快进入。
  5. 节点生命周期管理

  6. 强制线程管理器调度节点从 “init → update → destroy” 的思路,与 ROS2 Lifecycle Nodes 的概念类似,只是粒度更简洁。
  7. 优点
    • 让机器人开发者编写节点时只需实现 init/update/destroy 即可,开发门槛更低。
    • 避免工程师去关心线程、内存、锁等低层细节。
  8. 缺点
    • 失去了对节点行为进行更灵活定义的自由度(例如:有些节点需要特定的子状态机或动态重启时机)。
    • 如果工程师要实现复杂状态切换流程,必须在 update 中自行管理更多逻辑,可能造成某些节点内部代码变得臃肿。
  9. 建议

    • 目前做法在初期阶段是很实用的,能大大简化开发复杂度。
    • 如果将来需求变得多样化,可以考虑在 NodeBehavior 中扩展为多种“子态”或“挂起/重置”之类的 hook,以免所有逻辑都堆在 update 里。
  10. 流量控制

  11. 你已注意到:节点的执行频率、数据生产与消费速率若不匹配,会出现阻塞或丢失数据。
  12. 一种常见做法是:
    • 产生数据的上游节点里加入速率控制(限流)或缓存上限;
    • 在下游节点里实现“最新数据覆盖”或“排队策略”,防止无界队列耗尽内存。
  13. 后续你打算单独讨论,完全没问题,但一定要结合节点执行周期数据优先级一起来做综合设计。

  14. 实时性

  15. 在非实时 OS 环境下,无法做到硬实时保证,但可以做最优努力:如设置线程优先级、分配 CPU 亲和、采用 “RT 线程” 调度策略(在Linux + RT-Preempt补丁中)等。
  16. 对于不带RT补丁的通用操作系统,只能在一定程度上提高调度优先级,仍然可能被系统中断。
  17. 多平台情况下,如果底层平台有RTOS(或预留中断+裸机),可以将最关键的控制环放在RT线程里,而非核心的节点或任务可以运行在普通线程中。

  18. 硬件层错误由 RobotBus 负责

  19. 让 RobotBus 提供对硬件健康度、连接状态的保证是个好思路,但要注意
    • 当 RobotBus 自身检测到硬件故障或严重通信中断时,系统应该有一个“全局故障处理”通道来通知所有相关节点,并可及时采取安全动作(如急停或容错)。
    • 可以在 RobotBus 层定义更具体的“故障类型”和“恢复策略”,如“自动重连”、“退化模式”等。
  20. 上层节点的错误处理
  21. 你提到利用“旁支节点”或“代理节点”来完成紧急处理和错误传回,但也担心运行时建立这些节点会增大开销、同时增加锁竞争或死锁的风险。
  22. 常见做法
    • 使用监督者 (Supervisor) / Actor 模型:一个专门的管理节点来监控子节点的错误。子节点一旦报错,可以通知监督者,后者决定是否重启、跳过或转发错误给更上层。
    • 如果希望分布式多线程地处理错误,依然要保证不会出现死锁/重复调用。可以在监督者内使用消息队列异步地派发恢复逻辑,避免“回调套回调”导致的锁嵌套。
  23. 建议:如果你觉得“旁支代理节点”过于复杂,可考虑用一个更轻量的 Supervisor 来集中管理错误处理流程,不必在运行时临时创建大量节点。

  24. 以 JSON 表达有向图

  25. 这是很常见的做法。核心是在系统内部要将 JSON 解析出来后,形成真正的节点依赖拓扑结构。
  26. 建议
    • 使用拓扑排序或有向无环图 (DAG) 来确定执行顺序。若不小心出现环,系统可能无法自动完成依赖;
    • 在大规模场景下,最好能做循环检测冲突检测以及资源竞争分析,避免多个任务争用相同机器人资源。
  27. 关于热加载
  28. 后续你会单独讨论,这里只提示:动态修改JSON引起的节点增减,必须考虑对已在运行的节点安全退出、重启或无缝更新,会影响线程与资源管理。

  29. 直接根据 JSON 创建线程

  30. 这种“一个节点一个线程”的方式简单易懂,但当节点数量大时,线程切换的开销会迅速增长。
  31. 改进方向
    • 考虑引入线程池,将节点调度与线程调度分离;或考虑异步执行模型 (如 tokio/async-std) 来处理大量并发任务。
    • 如果系统规模不大、节点数量有限,现阶段也可以容忍“直接一线程对一节点”。
  32. 在单片机平台上的调度

    • 你可以了解下常见的RTOS(如 FreeRTOSZephyr 等),也可以看看 Rust 社区的嵌入式框架:
    • RTIC (Real-Time Interrupt-driven Concurrency):一个在嵌入式Rust中常用的调度框架,基于事件驱动、优先级互斥,可以简化并发编程,同时提供一定程度的实时保障。
    • 无操作系统(裸机):使用中断+全局循环的方式管理任务,需要自己编写调度器/时钟管理,但可获得最小的开销和最高的可控度。
  33. 预分配内存

    • 你的思路(在节点创建阶段一次性申请好该节点完整的执行期所需内存,避免循环内频繁分配)非常好,也是嵌入式实时系统常用的做法。
    • 如果需要多任务动态创建,还可使用“固定大小的内存池”或“多级内存池”以避免堆内存碎片。
  34. 你计划后面再详细讨论,这里先补充一点:

  35. 在 Rust 里,可以结合异步通道 (futures::channel or tokio::sync::mpsc) 处理事件型通信,结合无锁队列环形缓冲(ring buffer)来处理高频数据流。
  36. 对于紧急事件(如故障、急停),通常需要“抢占”或“直接中断”,不应只放入排队逻辑中,以免高负载下出现延迟。

  37. 你会在单片机上使用一个更轻量的“同构”内核,保留相同的通信方法与节点模型,但根据裸机/RTOS需求来改写底层调度与资源管理,这样的抽象思路是对的。

  38. 关键在于保持上层API和概念一致,让用户在PC端或嵌入式端都能用近似的方式描述任务与节点,减少移植门槛。

  39. 你将“RobotBus”定位为对机器人指令、任务、约束的协议抽象层,并不与具体的网络物理层或工业总线相冲突。

  40. 建议
  41. 尽量定义好指令集数据格式的版本和兼容策略,尤其是后续可能会扩展支持更多硬件/仿真后端。
  42. 如果需要与第三方控制器或现有工业协议交互,可在 RobotBus 底层实现“翻译适配层”,对外仍然保持统一的RobotBus接口。

  43. 安全

  44. Rust在语言层面能帮你消除大部分内存安全问题;剩下的关键点是并发逻辑是否正确,以及硬件交互中的不安全代码块需仔细审计。
  45. 如果有网络互联或远程控制,还要考虑指令身份认证、通信加密等更高级别的安全需求。
  46. 性能
  47. 主要取决于并发模型(线程数、锁争用、队列数量)和调度策略
  48. 如果要在多任务并行、实时性要求高的场合,就需要更精细的调度机制和资源隔离。
  49. 你也可以在后期通过基准测试profiling(如 perf, flamegraph, cargo profiler 等)来找到瓶颈点,再做针对性优化。

总结

  • 整体方案思路清晰、实现上也兼顾了Rust的安全与对机器人开发者的易用性。
  • 节点生命周期错误处理并发调度等方面,你目前的做法非常贴合“工程落地”的实际需求:简化外部开发者的心智负担、由系统内核负责线程与内存管理。
  • 建议在后续迭代时,聚焦在以下几点完善:
  • 并发策略:在数据频繁读写或节点数量较多的场景下,评估如何降低锁争用、是否需要线程池/异步调度。
  • 错误与故障恢复:考虑引入 Supervisor 或更完整的节点状态机,避免因“临时代理节点”而带来的高额开销或死锁风险。
  • 嵌入式RTOS/无操作系统适配:可以探索RTIC等框架,或至少了解 Zephyr/FreeRTOS 的基本调度概念,以支持多样化的硬件平台。
  • 任务依赖图:确保对 JSON 中的有向图做循环检测、拓扑排序,并在资源争用时有冲突检测机制。

如有更细节的需求(如“如何具体实现线程池”或“如何做热加载”),可以在后续发起更聚焦的问题,再进行更深入的设计和技术方案探讨。祝你项目进展顺利!