Skip to content

设计哲学

本文阐述 Roplat 的核心设计理念,帮助你理解它为什么要这样做,而不仅仅是它能做什么。

核心信条:运行期危险左移

传统机器人操作系统(以 ROS 2 为代表)的根本问题是:太多错误被推迟到运行时才暴露。

  • 话题类型不匹配 → 运行时消息丢弃
  • 节点依赖缺失 → 运行时超时等待
  • 调度顺序不确定 → 间歇性死锁
  • 内存越界 → 运行时 segfault

这些问题在实验室调试时已经很痛苦,在真实机器人上更是灾难性的——你无法让一台搬运重物的机械臂"再试一次"。

Roplat 的解决方案:尽可能将这些检查前移到编译期。

               ROS 2                           Roplat
           ┌─────────────┐                ┌─────────────┐
编译期      │  语法检查   │                │  语法检查   │
           │            │                │  类型匹配   │
           │            │                │  拓扑分析   │
           │            │                │  死锁检测   │
           │            │                │  调度确定   │
           └──────┬──────┘                └──────┬──────┘
                 │                             │
           ┌──────▼──────┐                ┌──────▼──────┐
运行期      │  类型匹配   │                │  纯执行     │
           │  拓扑发现   │                │  (确定性) │
           │  调度决策   │                │            │
           │  竞争检测   │                │            │
           │  崩溃/死锁  │                │            │
           └─────────────┘                └─────────────┘

进程内静态,进程级动态

Roplat 在单个进程内追求完全静态

  • 所有节点连接在编译期确定
  • 调度顺序在编译期确定
  • 类型在编译期匹配

但在进程之间保留动态灵活性

  • 不同进程可以独立部署
  • 进程间通过旁路通讯连接
  • YAML 配置参数可运行时加载

这种"进程内静态 + 进程级动态"的策略,在安全性灵活性之间取得了平衡。

图即调度,规约即编译

Roplat 的 system! 宏将数据流描述转化为有向图,然后通过图规约将其化简为最优执行序列:

原始拓扑:
  A → B → D
  A → C → D

R2 规约(并行融合):
  A → (B ∥ C) → D

最终代码:
  let a_out = a.process(input).await;
  let (b_out, c_out) = tokio::join!(
      b.process(a_out.clone()),
      c.process(a_out),
  );
  d.process((b_out, c_out)).await;

五条规约规则:

规则 含义 效果
R0 单节点直接执行 基础情况
R1 串行链融合 A → B → 顺序 await
R2 并行扇出融合 A → (B ∥ C)tokio::join!
R3 拓扑排序 保证依赖顺序
R4 通道切割 跨域边界自动插入 channel

零开销原则

Roplat 在运行时追求零额外开销

  • Rhythm 零堆分配drive() 方法使用值传递模式,每 tick 不产生任何堆分配。节点元组通过值移入/移出闭包,编译器优化为寄存器传递。

  • 无动态分发:所有 trait 方法通过泛型单态化,没有 vtable 查表。

  • 无运行时调度器:不需要执行器在运行时决定"下一个执行谁"——这个决定已经在编译期做完了。

  • Lock-free 通讯:旁路通讯使用原子操作而非互斥锁,保证实时线程不会被阻塞。

关注点分离

Roplat 严格分离三个正交关注点:

关注点 抽象 用户关心的问题
计算 Node "这个节点做什么计算?"
时间 Rhythm "多快执行一次?什么事件触发?"
连接 System "谁的输出接谁的输入?"

这意味着:

  • 修改控制算法 → 只改 Node,不动 Rhythm 和 System
  • 修改执行频率 → 只改 Rhythm,不动 Node 和 System
  • 修改数据流拓扑 → 只改 System,不动 Node 和 Rhythm

每次变更只影响一个维度。

与 ROS 2 的根本分歧

设计决策 ROS 2 Roplat 理由
拓扑构建 运行时发现 编译期声明 消除启动阶段的不确定性
类型检查 运行时哈希比对 编译期泛型约束 类型错误不应在实验中才发现
调度器 运行时 Executor 编译期图规约 确定性是安全的前提
通讯 DDS 中间件 进程内 lock-free 实时系统不能容忍中间件延迟
多语言 各语言独立实现 编译期绑定生成 消除手写绑定的类型不一致

Roplat 并非要替代 ROS 2 的所有场景。它更适合对安全性、确定性和实时性有高要求的场景——如工业机械臂控制、自动驾驶感知管线、多机器人协同编队。

下一步