面向读者:初级工程师 / AGV 与多机器人方向学习者
文章目标:深入理解openTCS,并理清其与常见AGV物流仿真项目区别


文章目录

一、为什么传统 AGV 调度正在遇到瓶颈?

过去很多 AGV 系统,本质上是:

固定路线
+ 中央调度
+ 路径占用
+ 区域锁

这种模式在工厂、仓储、固定站点运输中非常有效。它的核心假设是:

“机器人走固定路线,环境基本不变,冲突可以通过锁来解决。”

在这个假设下,系统设计可以非常简洁:画好路网,定义好资源,谁先申请谁先走。这就是 openTCS 那一代系统的核心逻辑。

但随着 AMR(Autonomous Mobile Robot,自主移动机器人)的普及,这个假设开始崩塌。

1.1 AMR 和传统 AGV 的本质区别

传统 AGV:

  • 沿磁条/二维码/固定轨道行驶

  • 路径在部署时就确定了

  • 不会"自己决定"走哪条路

  • 环境变化时需要人工重新布线

AMR:

  • 自主导航,不依赖固定轨道

  • 可以动态避障

  • 可以自己规划路径

  • 环境变化时可以自适应

这意味着:

特性 传统 AGV AMR
路径 固定 动态
环境 静态 动态
避障 停车等待 绕行
部署 需要改造场地 即插即用
灵活性

1.2 传统调度模式为什么不适合 AMR?

传统 AGV 调度的核心是"资源锁":

路段 A-B 被 AGV-1 占用
→ AGV-2 不能进入
→ 等待释放

这在固定路网里没问题。但 AMR 的路径是动态的:

AMR-1 可能走路线 A
也可能走路线 B
也可能临时绕行路线 C

如果还用传统资源锁:

  • 锁什么?路径都不确定,锁哪段路?

  • 锁多久?AMR 可能随时改路线,锁了的资源可能白锁

  • 怎么释放?AMR 绕行了,原来锁的路段要不要释放?

更严重的问题是:

AMR 数量越来越多,场景越来越复杂。

现代建筑里可能同时有:

  • 配送机器人(送药、送餐)

  • 清洁机器人(扫地、消毒)

  • 巡检机器人(安防、设备检查)

  • 搬运机器人(物流、仓储)

这些机器人:

  • 来自不同厂家

  • 使用不同导航方式

  • 有不同的运动能力

  • 共享同一个物理空间

传统 AGV 调度系统根本没有设计来处理这种复杂度。

1.3 新场景带来的新挑战

挑战 说明
多楼层 机器人需要坐电梯
动态环境 人流、临时障碍物
异构机器人 不同品牌、不同能力
共享设施 电梯、门禁、走廊
实时性 不能等几秒再决策
规模 几十甚至上百台机器人

这些挑战叠加在一起,传统"固定路网 + 资源锁"的模式就力不从心了。

于是:

Open-RMF(Open Robotics Middleware Framework)出现了。

它不是对传统 AGV 调度的"升级",而是一种全新的系统设计哲学。


二、什么是 Open-RMF?

Open-RMF 是由 Open Robotics(ROS 的维护组织)联合多家机器人企业共同推动的开源项目。

它的全称是 Open Robotics Middleware Framework,但这个名字容易让人误解为"只是一个中间件"。实际上它是:

一个完整的多机器人协同基础设施。

2.1 Open-RMF 的定位

它的目标不是:

“控制一台机器人怎么走。”

而是:

“让整个建筑里所有机器人能够和平共处、协同工作。”

你可以把它想象成:

一个城市的交通管理局。

它不管每辆车的发动机怎么工作,不管每辆车的方向盘怎么转。它管的是:

  • 这条路现在谁能走

  • 这个路口谁先过

  • 这个电梯现在归谁用

  • 这扇门什么时候开

  • 如果两辆车要撞上了怎么办

2.2 Open-RMF 管理的对象

对象 说明
机器人 各种品牌、各种类型
电梯 调度、锁定、楼层控制
自动门 开关控制、通行权
走廊/通道 通行调度
充电桩 资源分配
任务 配送、巡检、清洁等

注意:Open-RMF 不只管机器人。它管理的是整个建筑的"可移动资源"和"共享设施"。

2.3 Open-RMF 的技术栈

Open-RMF 构建在 ROS 2 之上:

ROS 2(通信基础)
  ↓
DDS(分布式数据服务)
  ↓
Open-RMF 各模块
  ↓
Fleet Adapter(对接各品牌机器人)

它使用 ROS 2 的通信机制(Topic、Service、Action),但在其上构建了完整的调度逻辑。

2.4 Open-RMF 的核心模块

模块 职责
rmf_traffic 交通调度核心:轨迹管理、冲突检测、协商
rmf_task 任务管理:任务定义、分配、状态跟踪
rmf_fleet_adapter 车队适配器:对接不同品牌机器人
rmf_building_map 建筑地图:多楼层、电梯、门
rmf_lift / rmf_door 设施联动:电梯和门的控制
rmf_visualization 可视化:Web 界面监控

三、Open-RMF 和 openTCS的核心区别

很多人第一次接触 RMF 时,会误以为:

“它只是另一个 AGV 调度系统。”

实际上,它们代表了两种完全不同的系统设计哲学。

3.1 设计哲学对比

维度 openTCS Open-RMF
核心思想 资源锁 + 路径占用 时空轨迹 + 动态协商
路径模型 固定图拓扑 动态轨迹
冲突解决 先到先得(锁) 协商(Negotiation)
时间维度 弱(主要看空间) 强(时空一体)
环境假设 静态 动态
机器人类型 同构为主 异构为主
设施联动 不涉及 电梯、门、充电桩
地图 单层 多楼层
适用场景 工厂/仓库 医院/写字楼/商场

3.2 一个具体例子说明区别

场景:两台机器人要经过同一段走廊。

openTCS 的处理方式:

走廊是一个 Path 资源
Robot-A 先申请 → 获得锁 → 通过
Robot-B 申请 → 被拒绝 → 等待
Robot-A 离开走廊 → 释放锁
Robot-B 获得锁 → 通过

问题:如果走廊很长(比如 50 米),Robot-A 走完需要 30 秒。Robot-B 必须等 30 秒。即使 Robot-B 比 Robot-A 晚 2 秒进入,两者根本不会相遇。

Open-RMF 的处理方式:

Robot-A 的轨迹:走廊入口 @ 10:00 → 走廊出口 @ 10:30
Robot-B 的轨迹:走廊入口 @ 10:02 → 走廊出口 @ 10:32

系统分析:
- 两者方向相同
- Robot-B 始终在 Robot-A 后面
- 安全距离满足
→ 无冲突,两者同时通行

结果:RMF 的方案效率更高,因为它理解"时间"。

3.3 本质区别的一句话总结

openTCS 问的是:

“这个空间现在有没有人?”

Open-RMF 问的是:

“这个空间在未来某个时刻会不会有人?”

这就是从"空间调度"到"时空调度"的跨越。


四、RMF 的核心思想:机器人共享"时空"

Open-RMF 最重要的思想是:

“机器人冲突不仅发生在空间,也发生在时间。”

4.1 什么是"时空冲突"?

两台机器人"冲突"的定义不是"它们在同一个位置",而是"它们在同一时刻出现在同一个位置"。

这个区别看起来微小,但对系统设计的影响是巨大的。

传统系统只考虑空间:

位置 P 被占用 → 其他车不能进

RMF 考虑时空:

位置 P 在 10:05-10:08 被占用 → 其他车在这个时间段不能进
但 10:09 之后可以进

4.2 为什么时空视角更高效?

考虑一个十字路口:

纯空间视角:

路口被 Robot-A 占用
Robot-B、C、D 全部等待
直到 Robot-A 完全离开

时空视角:

Robot-A 在 10:00-10:03 经过路口
Robot-B 计划 10:05 到达路口
→ 不冲突,Robot-B 不需要等待

Robot-C 计划 10:02 到达路口
→ 冲突!Robot-C 需要调整(减速/等待/换路)

时空视角下,只有真正会在同一时刻相遇的机器人才需要协调。其他机器人可以正常通行。

4.3 时空路径规划的数学表示

在 RMF 中,一条路径不是:

[P1, P2, P3, P4]  (纯空间序列)

而是:

[(P1, t1), (P2, t2), (P3, t3), (P4, t4)]  (时空序列)

其中 ti 表示机器人到达 Pi 的时间。

这样,两条路径是否冲突,就变成了一个几何问题:

两条时空轨迹是否在某个时刻距离过近?

4.4 时空规划的优势

优势 说明
更高吞吐量 不需要为"可能的冲突"过度等待
更少停车 只在真正冲突时才协调
更好的预测性 可以提前知道未来会发生什么
更灵活的协调 可以通过调整时间而不是路径来解决冲突

4.5 时空规划的代价

代价 说明
计算复杂度高 维度从 2D 变成 3D(x, y, t)
需要准确的时间估计 如果机器人实际速度和预测不符,计划就会失效
需要持续更新 机器人实际执行和计划有偏差时,需要重新规划
系统复杂度高 比简单的资源锁难实现得多

五、RMF 的整体架构

典型 RMF 架构:

                    +------------------+
                    |   Web Dashboard  |   ← 运维监控界面
                    +------------------+
                            |
                            v
+----------------------------------------------------------+
|                      Open-RMF Core                        |
|----------------------------------------------------------|
| rmf_traffic        | 交通调度:轨迹、冲突、协商          |
| rmf_task           | 任务管理:分配、状态、优先级        |
| rmf_building_map   | 建筑地图:多层、连接、设施          |
+----------------------------------------------------------+
      /          |            |             \
     /           |            |              \
    v            v            v               v
+--------+  +--------+  +---------+  +----------+
| Fleet  |  | Fleet  |  |  Lift   |  |   Door   |
|Adapter |  |Adapter |  | Adapter |  |  Adapter |
|  (A)   |  |  (B)   |  |         |  |          |
+--------+  +--------+  +---------+  +----------+
    |            |            |             |
    v            v            v             v
Robot-A      Robot-B      Elevator      Auto-Door
(品牌X)      (品牌Y)

5.1 架构的关键特点

1. 适配器模式(Adapter Pattern)

RMF 不直接控制任何设备。所有设备都通过 Adapter 接入。这意味着:

  • 添加新品牌机器人 = 写一个新的 Fleet Adapter

  • 添加新电梯 = 写一个新的 Lift Adapter

  • 核心调度逻辑完全不需要改

2. 分布式架构

RMF 基于 ROS 2 的 DDS 通信,天然是分布式的:

  • 各个 Adapter 可以运行在不同机器上

  • 核心调度可以独立部署

  • 没有严格的"中央服务器"概念

3. 建筑级视角

RMF 的设计单位不是"一个房间"或"一个楼层",而是"整栋建筑"。它天然考虑:

  • 多楼层

  • 楼层间的连接(电梯)

  • 区域间的隔离(门)

5.2 信息流

一个典型的任务执行流程:

1. 用户通过 Dashboard 下发任务:"把药品从药房送到 3 楼 302 病房"

2. rmf_task 接收任务,分析任务类型(配送),确定需要:
   - 一台配送机器人
   - 使用电梯从 1 楼到 3 楼
   - 经过 302 病房的门

3. rmf_task 选择合适的机器人(考虑位置、电量、当前任务)

4. rmf_traffic 为该机器人规划时空轨迹:
   - 1楼药房 → 电梯口(避开其他机器人)
   - 等待电梯
   - 乘电梯到 3 楼
   - 3楼电梯口 → 302 病房(避开其他机器人)

5. rmf_traffic 检测轨迹是否与其他机器人冲突
   - 如果冲突 → 触发 Negotiation
   - 协商出双方都可接受的方案

6. Fleet Adapter 将指令翻译为具体机器人能理解的命令

7. Lift Adapter 在适当时机调度电梯

8. Door Adapter 在机器人到达时开门

9. 机器人执行,持续上报状态

10. rmf_traffic 持续监控实际轨迹与计划轨迹的偏差
    - 如果偏差过大 → 重新规划

六、多车协同:RMF 为什么比传统 AGV 更复杂?

传统 AGV 的多车协同相对简单:

任务 → 路径 → 申请资源 → 获得锁 → 执行

而 RMF 的多车协同是一个持续的、动态的过程:

任务
 ↓
规划时空轨迹
 ↓
提交轨迹到全局 Schedule
 ↓
持续检测冲突
 ↓
发现冲突 → 触发 Negotiation
 ↓
协商出新方案
 ↓
更新轨迹
 ↓
继续执行
 ↓
(循环:持续检测、持续调整)

6.1 为什么是"持续"的?

因为 AMR 的世界是动态的:

  • 机器人实际速度可能和预测不同(地面摩擦、电量变化)

  • 新任务随时可能到来

  • 机器人可能突然故障

  • 人可能突然出现在走廊里

  • 电梯可能被人占用

因此,RMF 不是"规划一次就完事",而是"持续规划、持续调整"。

6.2 传统 AGV vs RMF 的协同策略对比

维度 传统 AGV Open-RMF
冲突发现时机 申请资源时 轨迹提交时(提前预测)
冲突解决方式 等待 协商(可能双方都调整)
调整粒度 停/走 减速/加速/换路/等待
动态性 低(一旦获得锁就执行到底) 高(随时可能重新规划)
信息利用 当前状态 未来轨迹预测

6.3 一个具体的多车协同场景

场景:医院走廊,3 台机器人

Robot-A:从药房出发,要去 3 楼(需要坐电梯)
Robot-B:在 1 楼巡检,即将经过电梯口
Robot-C:从 3 楼下来,正在电梯里

RMF 的处理:

1. Robot-A 规划轨迹:药房 → 走廊 → 电梯口 → 等电梯 → 上 3 楼
2. 提交轨迹到 Schedule

3. 系统检测到:
   - Robot-A 和 Robot-B 的轨迹在走廊有时空重叠
   - Robot-A 到达电梯口时,Robot-C 还在电梯里

4. 触发 Negotiation:
   - Robot-A vs Robot-B:协商结果 → Robot-B 减速 2 秒,让 Robot-A 先过
   - Robot-A vs 电梯:Robot-A 到达电梯口后等待 Robot-C 出来

5. 更新所有轨迹
6. 执行

这个过程中没有任何机器人"急刹车"或"碰撞后停下"。所有协调都是提前完成的。


七、rmf_traffic:RMF 最核心的模块

rmf_traffic 是 Open-RMF 的交通调度核心,也是整个系统最有技术含量的部分。

如果说 openTCS 的核心是 Scheduler(资源锁),那么:

RMF 的灵魂就是 rmf_traffic。

7.1 rmf_traffic 的职责

职责 说明
轨迹管理 维护所有机器人的计划轨迹
Schedule 全局时空调度表
冲突检测 检测轨迹之间的时空冲突
Negotiation 冲突协商机制
交通图 维护导航图(多层)

7.2 Schedule:全局时空调度表

Schedule 是 rmf_traffic 的核心数据结构。它维护了:

所有机器人当前和未来的计划轨迹。

你可以把它想象成一个"时空预约表":

Schedule:
  Robot-A: [(P1, 10:00), (P2, 10:05), (P3, 10:10)]
  Robot-B: [(P4, 10:00), (P2, 10:07), (P5, 10:12)]
  Robot-C: [(P6, 10:00), (P7, 10:03), (P8, 10:08)]

任何机器人要移动,都必须先把自己的计划轨迹提交到 Schedule。

系统会检查这条新轨迹是否与已有轨迹冲突。

7.3 为什么需要全局 Schedule?

因为冲突检测需要全局信息。

如果每台机器人只知道自己的轨迹,它无法判断是否会和别人冲突。只有一个全局的 Schedule,才能做到:

  • 任何新轨迹提交时,立即检测与所有已有轨迹的冲突

  • 任何轨迹更新时,重新检测受影响的冲突

  • 任何机器人完成任务时,从 Schedule 中移除其轨迹

7.4 Schedule 和资源锁的区别

维度 资源锁(openTCS) Schedule(RMF)
锁定对象 空间(节点/边) 时空(位置+时间段)
粒度 整个资源 特定时间窗口
释放条件 车辆离开 时间窗口过期
并发性 同一资源同时只能一个车 同一位置不同时间可以多个车
效率 保守(可能过度等待) 精确(只在真正冲突时协调)

八、RMF 为什么强调"时间窗预约"?

传统 AGV 系统的资源锁是"二值"的:

资源状态:空闲 / 占用

这很简单,但也很浪费。

8.1 资源锁的浪费

考虑一条 100 米长的走廊:

传统方式:
Robot-A 进入走廊 → 整条走廊被锁定
Robot-A 走完 100 米需要 60 秒
→ 其他机器人必须等 60 秒

即使 Robot-B 只是想在 Robot-A 后面 30 秒进入走廊
(两者永远不会相遇)
也必须等待

这就是资源锁的根本问题:

它锁定的是"空间",而不是"时空"。

8.2 时间窗预约的思想

RMF 的做法是:

Robot-A 预约:走廊 @ 10:00-10:60
Robot-B 想预约:走廊 @ 10:30-11:30

系统分析:
- Robot-A 在 10:30 时已经走到走廊中段
- Robot-B 在 10:30 从走廊入口进入
- 两者方向相同,Robot-B 在后面
- 安全距离始终满足
→ 批准 Robot-B 的预约

结果:两台机器人可以"同时"使用走廊,只要它们在时空上不冲突。

8.3 时间窗预约的精确含义

“时间窗预约"不是简单的"预约一个时间段”。它是:

预约一条完整的时空轨迹。

即:

不是:"我要在 10:00-10:60 使用走廊"
而是:"我会在 10:00 在走廊入口,10:30 在走廊中点,10:60 在走廊出口"

这样系统可以精确判断:两条轨迹在任意时刻的空间距离是否安全。

8.4 时间窗预约的实现挑战

挑战 说明
时间估计准确性 如果机器人实际比预计慢,预约就不准了
动态更新 机器人速度变化时需要更新预约
计算复杂度 检测两条连续轨迹的冲突比检测两个点的冲突复杂得多
预约失效 机器人故障时,其预约需要被清除

RMF 通过持续监控和动态重规划来应对这些挑战。


九、时间窗(Time Window)思想的深入理解

9.1 轨迹的数学表示

在 RMF 中,一条轨迹(Trajectory)是一系列带时间戳的路径点:

Trajectory = [(x1, y1, t1), (x2, y2, t2), ..., (xn, yn, tn)]

两个路径点之间,机器人被假设为匀速直线运动(或按某种插值方式运动)。

因此,在任意时刻 t,机器人的位置可以通过插值计算出来。

9.2 冲突的数学定义

两条轨迹 A 和 B 冲突,当且仅当:

存在某个时刻 t,使得 distance(A(t), B(t)) < safety_threshold

其中:

  • A(t) 是机器人 A 在时刻 t 的位置

  • B(t) 是机器人 B 在时刻 t 的位置

  • safety_threshold 是安全距离(考虑机器人尺寸)

9.3 为什么这比"同一节点"检测更强大?

传统系统的冲突检测:

if robot_A.current_node == robot_B.current_node:
    conflict!

这只能检测"已经发生"的冲突。

RMF 的冲突检测:

for t in future_time_range:
    if distance(A(t), B(t)) < threshold:
        conflict at time t!

这可以检测"未来将要发生"的冲突。

9.4 时间窗的实际意义

时间窗让系统可以做出更精细的决策:

场景:窄走廊,只能单向通行

传统方式:# 一、为什么传统 AGV 调度正在遇到瓶颈?

过去很多 AGV 系统,本质上是:

固定路线
+ 中央调度
+ 路径占用
+ 区域锁

这种模式在工厂、仓储、固定站点运输中非常有效。它的核心假设是:

“机器人走固定路线,环境基本不变,冲突可以通过锁来解决。”

在这个假设下,系统设计可以非常简洁:画好路网,定义好资源,谁先申请谁先走。这就是 openTCS 那一代系统的核心逻辑。

但随着 AMR(Autonomous Mobile Robot,自主移动机器人)的普及,这个假设开始崩塌。

1.1 AMR 和传统 AGV 的本质区别

传统 AGV:

  • 沿磁条/二维码/固定轨道行驶

  • 路径在部署时就确定了

  • 不会"自己决定"走哪条路

  • 环境变化时需要人工重新布线

AMR:

  • 自主导航,不依赖固定轨道

  • 可以动态避障

  • 可以自己规划路径

  • 环境变化时可以自适应

这意味着:

特性 传统 AGV AMR
路径 固定 动态
环境 静态 动态
避障 停车等待 绕行
部署 需要改造场地 即插即用
灵活性

1.2 传统调度模式为什么不适合 AMR?

传统 AGV 调度的核心是"资源锁":

路段 A-B 被 AGV-1 占用
→ AGV-2 不能进入
→ 等待释放

这在固定路网里没问题。但 AMR 的路径是动态的:

AMR-1 可能走路线 A
也可能走路线 B
也可能临时绕行路线 C

如果还用传统资源锁:

  • 锁什么?路径都不确定,锁哪段路?

  • 锁多久?AMR 可能随时改路线,锁了的资源可能白锁

  • 怎么释放?AMR 绕行了,原来锁的路段要不要释放?

更严重的问题是:

AMR 数量越来越多,场景越来越复杂。

现代建筑里可能同时有:

  • 配送机器人(送药、送餐)

  • 清洁机器人(扫地、消毒)

  • 巡检机器人(安防、设备检查)

  • 搬运机器人(物流、仓储)

这些机器人:

  • 来自不同厂家

  • 使用不同导航方式

  • 有不同的运动能力

  • 共享同一个物理空间

传统 AGV 调度系统根本没有设计来处理这种复杂度。

1.3 新场景带来的新挑战

挑战 说明
多楼层 机器人需要坐电梯
动态环境 人流、临时障碍物
异构机器人 不同品牌、不同能力
共享设施 电梯、门禁、走廊
实时性 不能等几秒再决策
规模 几十甚至上百台机器人

这些挑战叠加在一起,传统"固定路网 + 资源锁"的模式就力不从心了。

于是:

Open-RMF(Open Robotics Middleware Framework)出现了。

它不是对传统 AGV 调度的"升级",而是一种全新的系统设计哲学。


二、什么是 Open-RMF?

Open-RMF 是由 Open Robotics(ROS 的维护组织)联合多家机器人企业共同推动的开源项目。

它的全称是 Open Robotics Middleware Framework,但这个名字容易让人误解为"只是一个中间件"。实际上它是:

一个完整的多机器人协同基础设施。

2.1 Open-RMF 的定位

它的目标不是:

“控制一台机器人怎么走。”

而是:

“让整个建筑里所有机器人能够和平共处、协同工作。”

你可以把它想象成:

一个城市的交通管理局。

它不管每辆车的发动机怎么工作,不管每辆车的方向盘怎么转。它管的是:

  • 这条路现在谁能走

  • 这个路口谁先过

  • 这个电梯现在归谁用

  • 这扇门什么时候开

  • 如果两辆车要撞上了怎么办

2.2 Open-RMF 管理的对象

对象 说明
机器人 各种品牌、各种类型
电梯 调度、锁定、楼层控制
自动门 开关控制、通行权
走廊/通道 通行调度
充电桩 资源分配
任务 配送、巡检、清洁等

注意:Open-RMF 不只管机器人。它管理的是整个建筑的"可移动资源"和"共享设施"。

2.3 Open-RMF 的技术栈

Open-RMF 构建在 ROS 2 之上:

ROS 2(通信基础)
  ↓
DDS(分布式数据服务)
  ↓
Open-RMF 各模块
  ↓
Fleet Adapter(对接各品牌机器人)

它使用 ROS 2 的通信机制(Topic、Service、Action),但在其上构建了完整的调度逻辑。

2.4 Open-RMF 的核心模块

模块 职责
rmf_traffic 交通调度核心:轨迹管理、冲突检测、协商
rmf_task 任务管理:任务定义、分配、状态跟踪
rmf_fleet_adapter 车队适配器:对接不同品牌机器人
rmf_building_map 建筑地图:多楼层、电梯、门
rmf_lift / rmf_door 设施联动:电梯和门的控制
rmf_visualization 可视化:Web 界面监控

三、Open-RMF 和 openTCS 的核心区别

很多人第一次接触 RMF 时,会误以为:

“它只是另一个 AGV 调度系统。”

实际上,它们代表了两种完全不同的系统设计哲学。

3.1 设计哲学对比

维度 openTCS Open-RMF
核心思想 资源锁 + 路径占用 时空轨迹 + 动态协商
路径模型 固定图拓扑 动态轨迹
冲突解决 先到先得(锁) 协商(Negotiation)
时间维度 弱(主要看空间) 强(时空一体)
环境假设 静态 动态
机器人类型 同构为主 异构为主
设施联动 不涉及 电梯、门、充电桩
地图 单层 多楼层
适用场景 工厂/仓库 医院/写字楼/商场

3.2 一个具体例子说明区别

场景:两台机器人要经过同一段走廊。

openTCS 的处理方式:

走廊是一个 Path 资源
Robot-A 先申请 → 获得锁 → 通过
Robot-B 申请 → 被拒绝 → 等待
Robot-A 离开走廊 → 释放锁
Robot-B 获得锁 → 通过

问题:如果走廊很长(比如 50 米),Robot-A 走完需要 30 秒。Robot-B 必须等 30 秒。即使 Robot-B 比 Robot-A 晚 2 秒进入,两者根本不会相遇。

Open-RMF 的处理方式:

Robot-A 的轨迹:走廊入口 @ 10:00 → 走廊出口 @ 10:30
Robot-B 的轨迹:走廊入口 @ 10:02 → 走廊出口 @ 10:32

系统分析:
- 两者方向相同
- Robot-B 始终在 Robot-A 后面
- 安全距离满足
→ 无冲突,两者同时通行

结果:RMF 的方案效率更高,因为它理解"时间"。

3.3 本质区别的一句话总结

openTCS 问的是:

“这个空间现在有没有人?”

Open-RMF 问的是:

“这个空间在未来某个时刻会不会有人?”

这就是从"空间调度"到"时空调度"的跨越。


四、RMF 的核心思想:机器人共享"时空"

Open-RMF 最重要的思想是:

“机器人冲突不仅发生在空间,也发生在时间。”

4.1 什么是"时空冲突"?

两台机器人"冲突"的定义不是"它们在同一个位置",而是"它们在同一时刻出现在同一个位置"。

这个区别看起来微小,但对系统设计的影响是巨大的。

传统系统只考虑空间:

位置 P 被占用 → 其他车不能进

RMF 考虑时空:

位置 P 在 10:05-10:08 被占用 → 其他车在这个时间段不能进
但 10:09 之后可以进

4.2 为什么时空视角更高效?

考虑一个十字路口:

纯空间视角:

路口被 Robot-A 占用
Robot-B、C、D 全部等待
直到 Robot-A 完全离开

时空视角:

Robot-A 在 10:00-10:03 经过路口
Robot-B 计划 10:05 到达路口
→ 不冲突,Robot-B 不需要等待

Robot-C 计划 10:02 到达路口
→ 冲突!Robot-C 需要调整(减速/等待/换路)

时空视角下,只有真正会在同一时刻相遇的机器人才需要协调。其他机器人可以正常通行。

4.3 时空路径规划的数学表示

在 RMF 中,一条路径不是:

[P1, P2, P3, P4]  (纯空间序列)

而是:

[(P1, t1), (P2, t2), (P3, t3), (P4, t4)]  (时空序列)

其中 ti 表示机器人到达 Pi 的时间。

这样,两条路径是否冲突,就变成了一个几何问题:

两条时空轨迹是否在某个时刻距离过近?

4.4 时空规划的优势

优势 说明
更高吞吐量 不需要为"可能的冲突"过度等待
更少停车 只在真正冲突时才协调
更好的预测性 可以提前知道未来会发生什么
更灵活的协调 可以通过调整时间而不是路径来解决冲突

4.5 时空规划的代价

代价 说明
计算复杂度高 维度从 2D 变成 3D(x, y, t)
需要准确的时间估计 如果机器人实际速度和预测不符,计划就会失效
需要持续更新 机器人实际执行和计划有偏差时,需要重新规划
系统复杂度高 比简单的资源锁难实现得多

五、RMF 的整体架构

典型 RMF 架构:

                    +------------------+
                    |   Web Dashboard  |   ← 运维监控界面
                    +------------------+
                            |
                            v
+----------------------------------------------------------+
|                      Open-RMF Core                        |
|----------------------------------------------------------|
| rmf_traffic        | 交通调度:轨迹、冲突、协商          |
| rmf_task           | 任务管理:分配、状态、优先级        |
| rmf_building_map   | 建筑地图:多层、连接、设施          |
+----------------------------------------------------------+
      /          |            |             \
     /           |            |              \
    v            v            v               v
+--------+  +--------+  +---------+  +----------+
| Fleet  |  | Fleet  |  |  Lift   |  |   Door   |
|Adapter |  |Adapter |  | Adapter |  |  Adapter |
|  (A)   |  |  (B)   |  |         |  |          |
+--------+  +--------+  +---------+  +----------+
    |            |            |             |
    v            v            v             v
Robot-A      Robot-B      Elevator      Auto-Door
(品牌X)      (品牌Y)

5.1 架构的关键特点

1. 适配器模式(Adapter Pattern)

RMF 不直接控制任何设备。所有设备都通过 Adapter 接入。这意味着:

  • 添加新品牌机器人 = 写一个新的 Fleet Adapter

  • 添加新电梯 = 写一个新的 Lift Adapter

  • 核心调度逻辑完全不需要改

2. 分布式架构

RMF 基于 ROS 2 的 DDS 通信,天然是分布式的:

  • 各个 Adapter 可以运行在不同机器上

  • 核心调度可以独立部署

  • 没有严格的"中央服务器"概念

3. 建筑级视角

RMF 的设计单位不是"一个房间"或"一个楼层",而是"整栋建筑"。它天然考虑:

  • 多楼层

  • 楼层间的连接(电梯)

  • 区域间的隔离(门)

5.2 信息流

一个典型的任务执行流程:

1. 用户通过 Dashboard 下发任务:"把药品从药房送到 3 楼 302 病房"

2. rmf_task 接收任务,分析任务类型(配送),确定需要:
   - 一台配送机器人
   - 使用电梯从 1 楼到 3 楼
   - 经过 302 病房的门

3. rmf_task 选择合适的机器人(考虑位置、电量、当前任务)

4. rmf_traffic 为该机器人规划时空轨迹:
   - 1楼药房 → 电梯口(避开其他机器人)
   - 等待电梯
   - 乘电梯到 3 楼
   - 3楼电梯口 → 302 病房(避开其他机器人)

5. rmf_traffic 检测轨迹是否与其他机器人冲突
   - 如果冲突 → 触发 Negotiation
   - 协商出双方都可接受的方案

6. Fleet Adapter 将指令翻译为具体机器人能理解的命令

7. Lift Adapter 在适当时机调度电梯

8. Door Adapter 在机器人到达时开门

9. 机器人执行,持续上报状态

10. rmf_traffic 持续监控实际轨迹与计划轨迹的偏差
    - 如果偏差过大 → 重新规划

六、多车协同:RMF 为什么比传统 AGV 更复杂?

传统 AGV 的多车协同相对简单:

任务 → 路径 → 申请资源 → 获得锁 → 执行

而 RMF 的多车协同是一个持续的、动态的过程:

任务
 ↓
规划时空轨迹
 ↓
提交轨迹到全局 Schedule
 ↓
持续检测冲突
 ↓
发现冲突 → 触发 Negotiation
 ↓
协商出新方案
 ↓
更新轨迹
 ↓
继续执行
 ↓
(循环:持续检测、持续调整)

6.1 为什么是"持续"的?

因为 AMR 的世界是动态的:

  • 机器人实际速度可能和预测不同(地面摩擦、电量变化)

  • 新任务随时可能到来

  • 机器人可能突然故障

  • 人可能突然出现在走廊里

  • 电梯可能被人占用

因此,RMF 不是"规划一次就完事",而是"持续规划、持续调整"。

6.2 传统 AGV vs RMF 的协同策略对比

维度 传统 AGV Open-RMF
冲突发现时机 申请资源时 轨迹提交时(提前预测)
冲突解决方式 等待 协商(可能双方都调整)
调整粒度 停/走 减速/加速/换路/等待
动态性 低(一旦获得锁就执行到底) 高(随时可能重新规划)
信息利用 当前状态 未来轨迹预测

6.3 一个具体的多车协同场景

场景:医院走廊,3 台机器人

Robot-A:从药房出发,要去 3 楼(需要坐电梯)
Robot-B:在 1 楼巡检,即将经过电梯口
Robot-C:从 3 楼下来,正在电梯里

RMF 的处理:

1. Robot-A 规划轨迹:药房 → 走廊 → 电梯口 → 等电梯 → 上 3 楼
2. 提交轨迹到 Schedule

3. 系统检测到:
   - Robot-A 和 Robot-B 的轨迹在走廊有时空重叠
   - Robot-A 到达电梯口时,Robot-C 还在电梯里

4. 触发 Negotiation:
   - Robot-A vs Robot-B:协商结果 → Robot-B 减速 2 秒,让 Robot-A 先过
   - Robot-A vs 电梯:Robot-A 到达电梯口后等待 Robot-C 出来

5. 更新所有轨迹
6. 执行

这个过程中没有任何机器人"急刹车"或"碰撞后停下"。所有协调都是提前完成的。


七、rmf_traffic:RMF 最核心的模块

rmf_traffic 是 Open-RMF 的交通调度核心,也是整个系统最有技术含量的部分。

如果说 openTCS 的核心是 Scheduler(资源锁),那么:

RMF 的灵魂就是 rmf_traffic。

7.1 rmf_traffic 的职责

职责 说明
轨迹管理 维护所有机器人的计划轨迹
Schedule 全局时空调度表
冲突检测 检测轨迹之间的时空冲突
Negotiation 冲突协商机制
交通图 维护导航图(多层)

7.2 Schedule:全局时空调度表

Schedule 是 rmf_traffic 的核心数据结构。它维护了:

所有机器人当前和未来的计划轨迹。

你可以把它想象成一个"时空预约表":

Schedule:
  Robot-A: [(P1, 10:00), (P2, 10:05), (P3, 10:10)]
  Robot-B: [(P4, 10:00), (P2, 10:07), (P5, 10:12)]
  Robot-C: [(P6, 10:00), (P7, 10:03), (P8, 10:08)]

任何机器人要移动,都必须先把自己的计划轨迹提交到 Schedule。

系统会检查这条新轨迹是否与已有轨迹冲突。

7.3 为什么需要全局 Schedule?

因为冲突检测需要全局信息。

如果每台机器人只知道自己的轨迹,它无法判断是否会和别人冲突。只有一个全局的 Schedule,才能做到:

  • 任何新轨迹提交时,立即检测与所有已有轨迹的冲突

  • 任何轨迹更新时,重新检测受影响的冲突

  • 任何机器人完成任务时,从 Schedule 中移除其轨迹

7.4 Schedule 和资源锁的区别

维度 资源锁(openTCS) Schedule(RMF)
锁定对象 空间(节点/边) 时空(位置+时间段)
粒度 整个资源 特定时间窗口
释放条件 车辆离开 时间窗口过期
并发性 同一资源同时只能一个车 同一位置不同时间可以多个车
效率 保守(可能过度等待) 精确(只在真正冲突时协调)

八、RMF 为什么强调"时间窗预约"?

传统 AGV 系统的资源锁是"二值"的:

资源状态:空闲 / 占用

这很简单,但也很浪费。

8.1 资源锁的浪费

考虑一条 100 米长的走廊:

传统方式:
Robot-A 进入走廊 → 整条走廊被锁定
Robot-A 走完 100 米需要 60 秒
→ 其他机器人必须等 60 秒

即使 Robot-B 只是想在 Robot-A 后面 30 秒进入走廊
(两者永远不会相遇)
也必须等待

这就是资源锁的根本问题:

它锁定的是"空间",而不是"时空"。

8.2 时间窗预约的思想

RMF 的做法是:

Robot-A 预约:走廊 @ 10:00-10:60
Robot-B 想预约:走廊 @ 10:30-11:30

系统分析:
- Robot-A 在 10:30 时已经走到走廊中段
- Robot-B 在 10:30 从走廊入口进入
- 两者方向相同,Robot-B 在后面
- 安全距离始终满足
→ 批准 Robot-B 的预约

结果:两台机器人可以"同时"使用走廊,只要它们在时空上不冲突。

8.3 时间窗预约的精确含义

“时间窗预约"不是简单的"预约一个时间段”。它是:

预约一条完整的时空轨迹。

即:

不是:"我要在 10:00-10:60 使用走廊"
而是:"我会在 10:00 在走廊入口,10:30 在走廊中点,10:60 在走廊出口"

这样系统可以精确判断:两条轨迹在任意时刻的空间距离是否安全。

8.4 时间窗预约的实现挑战

挑战 说明
时间估计准确性 如果机器人实际比预计慢,预约就不准了
动态更新 机器人速度变化时需要更新预约
计算复杂度 检测两条连续轨迹的冲突比检测两个点的冲突复杂得多
预约失效 机器人故障时,其预约需要被清除

RMF 通过持续监控和动态重规划来应对这些挑战。


九、时间窗(Time Window)思想的深入理解

9.1 轨迹的数学表示

在 RMF 中,一条轨迹(Trajectory)是一系列带时间戳的路径点:

Trajectory = [(x1, y1, t1), (x2, y2, t2), ..., (xn, yn, tn)]

两个路径点之间,机器人被假设为匀速直线运动(或按某种插值方式运动)。

因此,在任意时刻 t,机器人的位置可以通过插值计算出来。

9.2 冲突的数学定义

两条轨迹 A 和 B 冲突,当且仅当:

存在某个时刻 t,使得 distance(A(t), B(t)) < safety_threshold

其中:

  • A(t) 是机器人 A 在时刻 t 的位置

  • B(t) 是机器人 B 在时刻 t 的位置

  • safety_threshold 是安全距离(考虑机器人尺寸)

9.3 为什么这比"同一节点"检测更强大?

传统系统的冲突检测:

if robot_A.current_node == robot_B.current_node:
    conflict!

这只能检测"已经发生"的冲突。

RMF 的冲突检测:

for t in future_time_range:
    if distance(A(t), B(t)) < threshold:
        conflict at time t!

这可以检测"未来将要发生"的冲突。

9.4 时间窗的实际意义

时间窗让系统可以做出更精细的决策:

场景:窄走廊,只能单向通行

传统方式:
Robot-A 进入走廊 → 锁定整条走廊
Robot-B 想从对面进入 → 等待
Robot-A 走完(60秒)→ 释放
Robot-B 进入

总耗时:120秒(串行)


RMF 方式:

```text
Robot-A 预约:走廊正向 @ 10:00-10:60
Robot-B 想预约:走廊反向 @ 10:30

系统分析:
- 如果 Robot-B 在 10:30 从对面进入,会和 Robot-A 在走廊中段相遇
- 冲突!

但系统还可以分析:
- 如果 Robot-B 等到 10:65 再进入(Robot-A 已离开)
- 或者 Robot-B 绕行另一条路,10:30 就能到达目的地

系统选择代价更小的方案。

关键区别:

传统方式只有"等"这一个选项。RMF 可以评估多种方案,选择全局代价最小的。

9.5 时间窗与"弹性"

时间窗还带来一个重要特性:弹性调度。

Robot-A 原计划 10:00 出发
但如果 Robot-A 延迟 5 秒出发(10:05)
就能完全避开 Robot-B 的轨迹
不需要任何人绕路

→ 系统选择让 Robot-A 晚出发 5 秒

这种"通过微调时间来避免空间冲突"的能力,是纯空间调度系统做不到的。

在工业场景里,这种微调往往比绕路更高效:

  • 绕路:多走 50 米,多花 30 秒

  • 延迟出发:等 5 秒,路径不变

时间窗让系统有了这种"时间维度的调整空间"。


十、冲突检测(Conflict Detection)

rmf_traffic 会持续检测所有已提交轨迹之间的冲突。

10.1 冲突检测的输入

输入:
- Schedule 中所有活跃轨迹
- 新提交的轨迹
- 安全距离参数
- 机器人尺寸信息

10.2 冲突检测的类型

RMF 检测的冲突不只是"两车相撞",而是多种类型:

冲突类型 说明 示例
正面冲突 两车迎面相遇 窄走廊对向行驶
追尾冲突 后车追上前车 快车追慢车
交叉冲突 路径在某点交叉 十字路口
资源冲突 同时需要同一设施 两车同时要用电梯
停靠冲突 目标位置被占用 两车要去同一个充电桩

10.3 连续轨迹冲突检测 vs 离散点检测

传统系统通常做离散检测:

每隔 1 秒检查一次:两车是否在同一节点

问题:如果两车在两次检查之间交错通过同一点,就检测不到。

t=10: Robot-A 在 P1,Robot-B 在 P3  → 无冲突
t=11: Robot-A 在 P3,Robot-B 在 P1  → 无冲突

但实际上在 t=10.5 时,两车在 P2 相遇了!

RMF 做的是连续轨迹检测:

将两条轨迹视为连续曲线
计算两条曲线之间的最小距离
如果最小距离 < 安全阈值 → 冲突

这在数学上更复杂,但保证不会遗漏任何冲突。

10.4 冲突检测的计算效率

如果有 N 台机器人,朴素的冲突检测需要 O(N²) 次两两比较。

RMF 通过以下方式优化:

  • 空间索引:只检测空间上可能相遇的轨迹对

  • 时间过滤:时间段不重叠的轨迹不可能冲突

  • 增量检测:新轨迹只需要和已有轨迹比较,不需要全部重新计算

  • 区域划分:不同区域的机器人不可能冲突


十一、RMF 的冲突检测为什么更先进?

11.1 从"被动响应"到"主动预测"

传统 AGV 的冲突处理是被动的:

传感器检测到前方有障碍物
→ 停车
→ 等待
→ 障碍物消失
→ 继续

这种方式的问题:

  1. 已经浪费了时间:车已经走到冲突点才发现问题

  2. 可能导致死锁:两车面对面停下,谁都走不了

  3. 无法全局优化:每辆车只看到自己前方的情况

RMF 的冲突处理是主动的:

规划阶段就预测未来所有可能的冲突
→ 在出发前就解决冲突
→ 机器人按照无冲突的轨迹执行
→ 几乎不需要紧急停车

11.2 预测式调度的优势

维度 被动响应 主动预测
停车次数
系统吞吐量
死锁风险
能耗 高(频繁启停) 低(平滑运动)
可预测性
用户体验 差(机器人经常停) 好(机器人流畅运动)

11.3 预测式调度的前提条件

要做到"主动预测",系统需要:

  1. 准确的运动模型:知道机器人的速度、加速度、转弯半径

  2. 全局信息:知道所有机器人的计划

  3. 实时更新:计划变化时立即重新检测

  4. 快速计算:冲突检测必须在毫秒级完成

这些条件在传统 AGV 时代很难满足(通信慢、计算弱)。但在现代系统中(ROS 2 + 高性能计算),已经完全可行。

11.4 RMF 如何处理预测不准的情况?

预测永远不可能 100% 准确。机器人可能:

  • 比预计慢(地面湿滑、电量低)

  • 比预计快(下坡)

  • 临时停下(遇到行人)

  • 完全偏离计划(故障)

RMF 的应对策略:

层级 1:容差设计
- 安全距离设置得比物理最小距离大
- 时间窗留有余量
- 即使有小偏差也不会真正冲突

层级 2:持续监控
- 实时比较实际轨迹和计划轨迹
- 偏差超过阈值 → 更新 Schedule → 重新检测冲突

层级 3:紧急处理
- 如果预测完全失效(机器人突然停下)
- 触发紧急重规划
- 其他机器人立即调整

这是一个"乐观规划 + 持续修正"的策略:

先假设一切按计划进行,但持续监控,一旦偏差过大就立即修正。


十二、Negotiation:RMF 最核心的思想

当冲突检测发现两条轨迹冲突时,系统需要解决这个冲突。

传统 AGV 的解决方式很简单:

优先级高的先走,优先级低的等待。

RMF 的解决方式更复杂也更高效:

Negotiation(协商)

12.1 为什么不能简单用优先级?

优先级方式的问题:

场景:
Robot-A(高优先级)要从东到西
Robot-B(低优先级)要从南到北
两者在十字路口冲突

优先级方式:Robot-B 停下等待

但如果:
- Robot-B 只需要减速 2 秒就能避开 Robot-A
- 而停下等待需要 15 秒(等 Robot-A 完全通过)

优先级方式浪费了 13 秒。

更极端的情况:

Robot-A(高优先级)要经过一条长走廊
Robot-B(低优先级)在走廊中间

优先级方式:Robot-B 必须让路
但 Robot-B 可能需要倒退 30 米才能让出空间
而 Robot-A 只需要等 5 秒,Robot-B 就自然走过去了

优先级方式反而更慢。

12.2 Negotiation 的核心思想

Negotiation 的本质是:

让冲突双方各自提出"如果我让步,代价是多少",然后选择全局代价最小的方案。

过程:

1. 检测到冲突:Robot-A 和 Robot-B 的轨迹冲突

2. 系统向双方发起协商:
   - 问 Robot-A:"如果你让步(等待/绕路),代价是多少?"
   - 问 Robot-B:"如果你让步(等待/绕路),代价是多少?"

3. Robot-A 回复:
   - 方案 A1:等待 5 秒,代价 = 5
   - 方案 A2:绕路,代价 = 20

4. Robot-B 回复:
   - 方案 B1:等待 3 秒,代价 = 3
   - 方案 B2:绕路,代价 = 15

5. 系统选择全局代价最小的组合:
   - Robot-B 等待 3 秒(代价 3)< Robot-A 等待 5 秒(代价 5)
   → 选择让 Robot-B 等待

12.3 Negotiation 的多轮协商

实际情况可能更复杂。一次协商可能涉及多台机器人:

Robot-A 和 Robot-B 冲突
→ Robot-B 决定绕路
→ 但 Robot-B 的新路径和 Robot-C 冲突
→ 需要 Robot-B 和 Robot-C 再协商
→ Robot-C 决定等待
→ 但 Robot-C 等待会导致 Robot-D 追尾
→ ...

这就是为什么 Negotiation 可能需要多轮:

Round 1: A vs B → B 让步
Round 2: B(new path) vs C → C 让步
Round 3: C(waiting) vs D → D 减速
...
直到所有冲突解决

12.4 Negotiation 的终止条件

协商不能无限进行。RMF 设置了终止条件:

条件 处理方式
找到无冲突方案 正常结束,执行方案
达到最大轮数 使用当前最优方案
超时 回退到优先级方式
无解 某台机器人原地等待

12.5 Negotiation vs 中央决策

有人可能会问:

“为什么不直接由中央系统决定谁让步?为什么要’协商’?”

原因是:

  1. 信息分布:每台机器人最了解自己的"让步代价"。中央系统可能不知道某台机器人绕路需要多少额外时间。

  2. 可扩展性:如果所有决策都由中央做,中央会成为瓶颈。协商可以分散计算负载。

  3. 模块化:不同品牌的机器人可能有不同的"让步能力"(有的能倒退,有的不能)。通过协商,每台机器人可以根据自己的能力提出方案。

  4. 公平性:协商机制可以保证不会总是同一台机器人让步。


十三、Negotiation 的具体机制

13.1 协商的参与者

在 RMF 中,Negotiation 的参与者不是"机器人"本身,而是"Fleet Adapter"代表机器人参与协商。

冲突检测 → 识别冲突方
                ↓
Negotiation System
    ↓                    ↓
Fleet Adapter A      Fleet Adapter B
    ↓                    ↓
提出替代方案          提出替代方案
    ↓                    ↓
        评估所有方案
              ↓
        选择最优组合
              ↓
        更新 Schedule

13.2 替代方案的生成

当一台机器人被要求"让步"时,它的 Fleet Adapter 会生成多个替代方案:

原始轨迹:P1 → P2 → P3 → P4(经过冲突点 P3)

替代方案 1:等待
P1 → P2 → [等待 5 秒] → P3 → P4
代价:+5 秒

替代方案 2:绕路
P1 → P2 → P5 → P6 → P4(绕开 P3)
代价:+15 秒

替代方案 3:减速
P1 → P2 → P3 → P4(但速度降低 30%)
代价:+8 秒

13.3 代价的计算

"代价"不只是时间。它可能包括:

代价因素 说明
时间延迟 任务完成时间推迟多少
能耗增加 绕路/启停带来的额外能耗
任务优先级 高优先级任务的延迟代价更大
连锁影响 这次让步是否会导致后续任务延迟
电量风险 绕路后电量是否还够完成任务

13.4 协商的公平性

为了避免"总是同一台机器人让步",RMF 的协商机制考虑了公平性:

  • 历史让步次数

  • 当前任务的紧急程度

  • 机器人的"让步能力"(有的机器人绕路很容易,有的很难)

这保证了长期运行中,负担是均匀分布的。


十四、Negotiation 本质是什么?

从计算机科学的角度看,Negotiation 本质上是:

一个约束满足问题(CSP)的分布式求解过程。

14.1 形式化定义

给定:
- N 台机器人的计划轨迹 T1, T2, ..., TN
- 安全约束:任意两条轨迹不能时空冲突
- 每台机器人的替代轨迹集合 Alt(Ti)

求解:
- 为每台机器人选择一条轨迹 Ti' ∈ {Ti} ∪ Alt(Ti)
- 使得所有轨迹两两不冲突
- 且总代价最小

14.2 与博弈论的关系

Negotiation 也可以从博弈论角度理解:

  • 每台机器人是一个"玩家"

  • 每个玩家的"策略"是选择哪条轨迹

  • "收益"是负的代价(代价越小越好)

  • 目标是找到一个"纳什均衡"或"社会最优"

RMF 更偏向"社会最优"(全局代价最小),而不是"纳什均衡"(每个人都不愿意单方面改变)。

因为在工业系统中:

全局效率比个体"公平"更重要。

14.3 与 MAPF 的关系

Negotiation 和学术界的 MAPF(Multi-Agent Path Finding)高度相关。

MAPF 的经典算法:

算法 思想 与 RMF 的关系
CBS(Conflict-Based Search) 发现冲突 → 分支 → 为冲突方生成约束 → 重新规划 RMF 的 Negotiation 非常接近 CBS 的思想
WHCA*(Windowed Hierarchical Cooperative A*) 在时间窗口内协同规划 RMF 的时间窗预约类似
Priority-Based Planning 按优先级顺序规划 RMF 的回退策略

RMF 的 Negotiation 可以看作是 CBS 的工程化实现:

CBS 学术版:
1. 找到冲突
2. 为冲突双方分别添加约束
3. 在约束下重新规划
4. 递归直到无冲突

RMF 工程版:
1. 找到冲突
2. 让冲突双方各自提出替代方案
3. 选择全局代价最小的组合
4. 如果新方案引入新冲突,继续协商

核心思想一致,但 RMF 更注重:

  • 实时性(不能算太久)

  • 鲁棒性(算不出来要有兜底方案)

  • 可扩展性(机器人数量增加时不能崩溃)


十五、RMF 和 MAPF 的关系

15.1 MAPF 是什么?

MAPF(Multi-Agent Path Finding)是人工智能和机器人学中的一个经典问题:

给定 N 个智能体,每个有起点和终点,在共享环境中为所有智能体找到无冲突路径,使得总代价最小。

这是一个 NP-hard 问题(当追求最优解时)。

15.2 MAPF 的主流算法

算法 核心思想 最优性 计算速度
CBS 冲突树搜索 最优 慢(指数级最坏情况)
ECBS 有界次优 CBS 有界次优 较快
WHCA* 时间窗口内协同 A* 不保证最优
Priority-Based 按优先级顺序规划 不保证最优 很快
ICTS 递增代价树搜索 最优
LaCAM 惰性约束添加 不保证最优 非常快

15.3 RMF 为什么不直接用 CBS?

CBS 是 MAPF 领域最著名的最优算法。但 RMF 没有直接采用它,原因是:

1. 实时性要求

CBS 在最坏情况下是指数级复杂度。工业系统不能接受"有时候算 1 毫秒,有时候算 10 分钟"。

RMF 需要的是:

保证在有限时间内给出一个"足够好"的方案。

2. 动态性

CBS 假设所有智能体同时开始规划。但现实中:

  • 机器人随时加入和离开

  • 新任务随时到来

  • 环境随时变化

RMF 需要的是增量式规划:

新机器人加入时,只需要和已有轨迹协调,不需要全部重新规划。

3. 异构性

CBS 通常假设所有智能体能力相同。但 RMF 中:

  • 不同机器人速度不同

  • 不同机器人尺寸不同

  • 不同机器人能力不同(有的能倒退,有的不能)

4. 鲁棒性

CBS 假设规划结果会被精确执行。但现实中机器人会偏离计划。

RMF 需要的是:

即使执行偏离计划,系统也能快速恢复。

15.4 RMF 的定位

RMF 不是一个"MAPF 求解器"。它是一个"多机器人协同系统"。

MAPF 是其中的一个子问题,但不是全部。

RMF 还需要处理:

  • 任务分配

  • 设施联动

  • 异构机器人管理

  • 故障恢复

  • 人机共存

因此 RMF 选择了一种"工程化的 MAPF 方案":

不追求数学最优,但保证实时、稳定、可扩展。


十六、RMF 为什么不追求"绝对最优"?

这是一个非常重要的工程哲学问题。

16.1 "最优"在工业系统中的含义

学术论文中的"最优"通常指:

所有机器人的总路径长度(或总完成时间)最小。

但工业系统中的"最优"含义完全不同:

学术"最优" 工业"最优"
总路径最短 系统 7×24 不停机
理论最优解 99.9% 的时间能给出方案
全局重规划 局部调整不影响其他机器人
静态场景 动态变化时快速适应

16.2 为什么"足够好"比"最优"更重要?

场景 1:计算时间

最优方案:需要 5 秒计算
次优方案:需要 50 毫秒计算,比最优多走 2 米

在这 5 秒里,机器人在干什么?停着等。
停 5 秒的代价 >> 多走 2 米的代价。

场景 2:鲁棒性

最优方案:所有机器人精确按计划执行,总时间 100 秒
但如果任何一台机器人偏离计划 → 需要全部重新规划

次优方案:总时间 110 秒
但每台机器人有 5 秒的时间余量
即使有小偏差也不需要重新规划

工业系统选择次优方案,因为:

稳定运行 110 秒 > 理论上 100 秒但经常需要重规划。

场景 3:可维护性

最优算法:复杂的数学优化,只有博士能理解
次优算法:简单的启发式规则,任何工程师都能调试

当系统出问题时:
- 最优算法:需要请专家来分析
- 次优算法:运维工程师看日志就能定位问题

16.3 RMF 的设计原则

RMF 的设计原则可以总结为:

  1. 实时性优先于最优性:宁可方案差一点,也不能让机器人等太久

  2. 稳定性优先于效率:宁可慢一点,也不能系统崩溃

  3. 可恢复性优先于完美性:出了问题能快速恢复,比永远不出问题更现实

  4. 增量式优先于全局式:局部调整比全部重来更实用

这些原则贯穿了 RMF 的所有设计决策。


十七、时空路径规划(Spatio-temporal Planning)

17.1 从空间路径到时空轨迹

传统路径规划的输出是:

Path = [P1, P2, P3, P4]

这只告诉你"经过哪些点",不告诉你"什么时候经过"。

RMF 的路径规划输出是:

Trajectory = [(P1, t1), (P2, t2), (P3, t3), (P4, t4)]

这告诉你"什么时候在哪里"。

17.2 为什么时间信息是必要的?

没有时间信息,就无法做冲突检测。

Robot-A 的路径:P1 → P2 → P3
Robot-B 的路径:P4 → P2 → P5

问题:两者都经过 P2,是否冲突?

没有时间信息 → 不知道!
- 如果 A 在 10:00 经过 P2,B 在 10:30 经过 P2 → 不冲突
- 如果 A 在 10:00 经过 P2,B 在 10:01 经过 P2 → 冲突

因此:

没有时间维度的路径规划,无法做精确的多机器人协调。

17.3 时空路径规划的搜索空间

传统 2D 路径规划的搜索空间是:

(x, y) → 二维平面

时空路径规划的搜索空间是:

(x, y, t) → 三维时空

这意味着:

  • 搜索空间增加了一个维度

  • 计算复杂度显著增加

  • 但表达能力也显著增强

17.4 时空图(Time-expanded Graph)

一种常见的时空路径规划方法是"时空图展开":

原始图:
P1 -- P2 -- P3

时空展开(时间步长 = 1):

t=0:  P1(0) -- P2(0) -- P3(0)
       |         |         |
t=1:  P1(1) -- P2(1) -- P3(1)
       |         |         |
t=2:  P1(2) -- P2(2) -- P3(2)
       |         |         |
t=3:  P1(3) -- P2(3) -- P3(3)

在这个展开图上:

  • 水平边 = 移动(从一个位置到相邻位置)

  • 垂直边 = 等待(在同一位置停留一个时间步)

路径规划变成在这个 3D 图上的搜索问题。

17.5 RMF 的时空规划方式

RMF 不完全使用离散的时空图展开(那样内存消耗太大)。它使用的是:

连续时间轨迹 + 约束检查

即:

  1. 先规划一条空间路径(不考虑其他机器人)

  2. 根据机器人速度,计算每个点的到达时间

  3. 检查这条时空轨迹是否与 Schedule 中的已有轨迹冲突

  4. 如果冲突 → 调整(等待/绕路/减速)

  5. 重复直到无冲突

这种方式比完整的时空图搜索更高效,因为它利用了"大多数情况下不冲突"这个事实。只有在真正冲突时才需要调整。

17.6 时空规划的实际效果

场景:5 台机器人同时在一层楼运行

传统方式(纯空间 + 资源锁):
- 走廊同时只能一台车 → 大量等待
- 十字路口锁定 → 排队通过
- 系统吞吐量:每小时完成 30 个任务

RMF 方式(时空规划):
- 走廊可以多台车同向通行(时空不冲突)
- 十字路口通过时间错开(不需要锁定整个路口)
- 系统吞吐量:每小时完成 50 个任务

提升来自于:

减少了"不必要的等待"。

只有真正会在同一时刻相遇的机器人才需要协调,其他情况都可以并行。


十八、RMF 的交通模型

18.1 核心数据结构

RMF 内部维护的交通模型由以下元素组成:

元素 含义 属性
Waypoint 导航点 坐标、楼层、是否可停靠
Lane 通道 连接两个 Waypoint、方向、速度限制
Trajectory 轨迹 带时间戳的 Waypoint 序列
Schedule 调度表 所有活跃轨迹的集合
Participant 参与者 每台机器人在 Schedule 中的身份

18.2 交通图(Navigation Graph)

RMF 的交通图和 openTCS 的图模型有相似之处,但也有关键区别:

相似点:

  • 都是有向图

  • 都有节点和边

  • 都支持单向通行

区别:

维度 openTCS RMF
层数 单层 多层
边的属性 代价/长度 速度限制/宽度/双向性
动态性 静态(部署时确定) 可动态修改
资源语义 每条边是一个"资源" 边本身不是资源,轨迹才是

18.3 多层交通图

RMF 的交通图天然支持多层:

Floor 1 Graph:
  W1 -- W2 -- W3 -- W4(lift)

Floor 2 Graph:
  W5(lift) -- W6 -- W7 -- W8

Cross-floor connection:
  W4(lift, floor1) ←→ W5(lift, floor2)
  transition_time: 30 seconds

楼层之间通过"电梯节点"连接。跨楼层的路径规划需要考虑:

  • 电梯等待时间(不确定)

  • 电梯容量(一次只能一台机器人)

  • 电梯调度(可能需要等其他楼层的请求)

18.4 Trajectory 的详细结构

一条 Trajectory 包含:

Trajectory {
  participant_id: "robot_A"
  profile: {
    footprint: circle(radius=0.3m)  // 机器人占地面积
    vicinity: circle(radius=0.5m)   // 安全区域
  }
  segments: [
    {position: (10.0, 5.0), time: 10:00:00, velocity: (1.0, 0.0)},
    {position: (15.0, 5.0), time: 10:00:05, velocity: (1.0, 0.0)},
    {position: (20.0, 5.0), time: 10:00:10, velocity: (0.0, 0.0)},  // 停靠
  ]
}

注意 profile 字段:它定义了机器人的物理尺寸和安全区域。冲突检测时,不是检测"两个点是否重合",而是检测"两个圆是否重叠"。

这意味着大型机器人(如叉车)和小型机器人(如配送机器人)的冲突检测阈值是不同的。

18.5 Schedule 的工作方式

Schedule 是所有活跃轨迹的集合:

Schedule {
  trajectories: [
    Trajectory(robot_A, ...),
    Trajectory(robot_B, ...),
    Trajectory(robot_C, ...),
  ]
  
  // 操作:
  submit(trajectory)    // 提交新轨迹
  update(trajectory)    // 更新已有轨迹
  remove(participant)   // 移除某个参与者的轨迹
  query(region, time)   // 查询某区域某时间段的所有轨迹
}

每当有新轨迹提交或更新时,系统会自动检测冲突。


十九、多地图(Multi-map)

19.1 为什么需要多地图?

传统 AGV 系统通常运行在单一平面上:一个仓库、一个车间。

但现代机器人的运行环境往往是三维的:

  • 医院:地下室(药房)→ 1楼(门诊)→ 2楼(病房)→ 3楼(手术室)

  • 写字楼:1楼(大堂)→ 各楼层(办公区)

  • 商场:B1(仓库)→ 1楼(店铺)→ 2楼(餐饮)

机器人需要在这些楼层之间移动,完成跨楼层的任务。

19.2 多地图的挑战

多地图不是简单地"把几张地图拼在一起"。它带来了一系列新问题:

挑战 说明
楼层间连接 如何表示电梯、楼梯、坡道?
跨层路径规划 如何计算包含"坐电梯"的路径?
电梯调度 电梯是共享资源,如何分配?
时间不确定性 等电梯的时间无法精确预测
坐标系统 不同楼层的坐标系可能不同
地图更新 某层地图变了,其他层怎么办?

19.3 RMF 的多地图模型

RMF 使用 rmf_building_map 来管理多层建筑:

Building {
  name: "City Hospital"
  levels: [
    Level {
      name: "B1"
      elevation: -3.0m
      nav_graph: Graph(...)
      walls: [...]
      doors: [...]
    },
    Level {
      name: "L1"
      elevation: 0.0m
      nav_graph: Graph(...)
      walls: [...]
      doors: [...]
      lifts: ["elevator_A", "elevator_B"]
    },
    Level {
      name: "L2"
      elevation: 4.0m
      nav_graph: Graph(...)
      walls: [...]
      doors: [...]
      lifts: ["elevator_A", "elevator_B"]
    }
  ]
  lifts: [
    Lift {
      name: "elevator_A"
      levels: ["B1", "L1", "L2"]
      position: (15.0, 20.0)  // 在每层的位置
      capacity: 1  // 一次一台机器人
    }
  ]
}

19.4 跨层路径规划

当机器人需要从 B1 到 L2 时,路径规划分为几个阶段:

阶段 1:B1 层内路径
  当前位置 → B1 电梯口

阶段 2:等待电梯
  在 B1 电梯口等待(时间不确定)

阶段 3:乘坐电梯
  B1 → L2(约 15 秒)

阶段 4:L2 层内路径
  L2 电梯口 → 目标位置

其中阶段 2 的时间是不确定的,这给时空规划带来了挑战。RMF 的处理方式是:

  • 估计一个"期望等待时间"

  • 实际等待时间可能不同

  • 一旦实际进入电梯,更新后续轨迹的时间


二十、RMF 如何管理多地图?

20.1 Graph + Transition 模型

RMF 使用"图 + 转换"的方式连接多层:

每层有自己的导航图(Graph)
层与层之间通过 Transition 连接

Transition 的属性:
- 类型:电梯 / 坡道 / 楼梯
- 连接的楼层
- 转换时间
- 容量限制
- 是否需要预约

20.2 电梯作为"瓶颈资源"

在多层建筑中,电梯是最关键的瓶颈资源:

  • 容量有限(通常一次一台机器人)

  • 速度有限(楼层间移动需要时间)

  • 共享使用(可能和人共用)

  • 调度复杂(多台机器人可能同时需要)

因此电梯调度本身就是一个优化问题:

场景:
Robot-A 在 B1,要去 L3
Robot-B 在 L1,要去 L2
Robot-C 在 L3,要去 B1

电梯当前在 L2

最优调度:
1. 电梯去 L3 接 Robot-C
2. Robot-C 下到 B1
3. 电梯去 B1 接 Robot-A(顺路)
4. Robot-A 上到 L3
5. 电梯去 L1 接 Robot-B
6. Robot-B 上到 L2

vs 先到先得:
1. Robot-A 先请求 → 电梯从 L2 去 B1 → 空跑
2. Robot-A 上到 L3
3. Robot-B 请求 → 电梯从 L3 去 L1 → 空跑
4. Robot-B 上到 L2
5. Robot-C 请求 → 电梯从 L2 去 L3 → 空跑
6. Robot-C 下到 B1

先到先得方式电梯空跑了很多。

20.3 机器人乘电梯的完整流程

这个流程看似简单,但每一步都可能出问题:

1. 机器人到达电梯口
   → 如果电梯口有其他机器人在等?排队。

2. 请求电梯
   → 通过 Lift Adapter 发送请求
   → 电梯可能在其他楼层,需要等待

3. 电梯到达,门打开
   → 如果电梯里有人/其他机器人?等下一趟。

4. 机器人进入电梯
   → 需要精确对准电梯门
   → 进入后确认位置

5. 请求目标楼层
   → 电梯开始移动

6. 到达目标楼层,门打开
   → 如果门口有障碍物?等待。

7. 机器人离开电梯
   → 离开后通知系统释放电梯

8. 切换到目标楼层的地图
   → 使用目标楼层的导航图继续规划

每一步都需要:

  • 状态确认

  • 超时处理

  • 异常恢复

这就是为什么"机器人坐电梯"在工程上比想象中复杂得多。


二十一、电梯联动(Lift Integration)

21.1 为什么电梯联动是工程难题?

电梯联动不只是"按按钮"那么简单。它涉及:

物理层面:

  • 电梯门的开关时间

  • 电梯的加减速

  • 电梯的载重限制

  • 电梯的维护时间

控制层面:

  • 电梯控制系统的接口(不同品牌不同)

  • 通信协议(Modbus / BACnet / 私有协议)

  • 权限管理(机器人是否有权调用电梯)

调度层面:

  • 多台机器人同时需要电梯

  • 电梯可能被人占用

  • 电梯可能故障

  • 高峰期电梯等待时间长

安全层面:

  • 机器人进出电梯时不能夹人

  • 电梯超载保护

  • 紧急情况下的处理

21.2 RMF 的电梯管理架构

rmf_traffic(调度层)
      ↓
  决定哪台机器人什么时候用电梯
      ↓
rmf_lift(控制层)
      ↓
  Lift Adapter(适配层)
      ↓
  实际电梯控制系统

21.3 Lift Adapter 的职责

Lift Adapter 是 RMF 和实际电梯之间的桥梁:

职责 说明
状态上报 电梯当前楼层、门状态、是否可用
指令下发 呼叫电梯、选择楼层、开关门
协议转换 将 RMF 指令转换为电梯控制系统能理解的协议
异常处理 电梯故障时通知 RMF
权限管理 确保机器人有权使用电梯

21.4 电梯调度策略

RMF 对电梯的调度不是简单的"先到先得",而是考虑全局效率:

策略 1:批量调度
  如果多台机器人要去同一楼层
  → 让它们搭同一趟电梯(如果容量允许)

策略 2:顺路调度
  电梯从 L1 去 L5
  如果 L3 有机器人要去 L5
  → 顺路接上

策略 3:优先级调度
  紧急任务的机器人优先使用电梯

策略 4:预约调度
  机器人还没到电梯口就提前预约
  → 减少到达后的等待时间

21.5 电梯联动的实际案例

场景:医院,2 台电梯,5 台机器人

10:00 Robot-A 在 B1,要送药到 L3
10:01 Robot-B 在 L1,要送餐到 L2
10:02 Robot-C 在 L3,要回 B1 充电
10:03 Robot-D 在 L2,要去 L1 取物
10:05 Robot-E 在 B1,要送标本到 L4

RMF 的调度:
- 电梯 1:B1(接A) → L3(放A, 接C) → B1(放C)
- 电梯 2:L2(接D) → L1(放D, 接B) → L2(放B)
- 电梯 1:B1(接E) → L4(放E)

通过合理调度,减少电梯空跑,提高整体效率。

二十二、门禁联动(Door Integration)

22.1 为什么门禁联动重要?

在建筑环境中,门是无处不在的:

  • 防火门

  • 科室门

  • 电梯门

  • 安全门

  • 自动门

  • 手动门

机器人要在建筑中自由移动,必须能够通过这些门。

22.2 门的类型和挑战

门类型 挑战
自动感应门 机器人能否触发感应?
门禁门 机器人有没有权限?
防火门 常闭,需要主动打开
电动门 需要通信接口控制
手动门 机器人无法通过(需要人帮忙)

22.3 RMF 的门管理

RMF 通过 Door Adapter 管理门:

机器人接近门
      ↓
rmf_traffic 检查:机器人是否有权通过这扇门?
      ↓
通过 Door Adapter 发送开门请求
      ↓
等待门打开确认
      ↓
机器人通过
      ↓
发送关门请求(或等待自动关闭)

22.4 门作为"资源"

在 RMF 中,门也是一种资源:

  • 门打开时,只允许一台机器人通过(避免碰撞)

  • 门的开关有时间成本(不能频繁开关)

  • 某些门有通行方向限制

因此门的调度也需要纳入整体交通规划:

场景:一扇防火门,两侧各有一台机器人要通过

不协调:
- 两台机器人同时到达门口
- 门打开
- 两台机器人同时进入 → 卡住

RMF 协调:
- Robot-A 先到达,请求开门
- 门打开,Robot-A 通过
- Robot-A 通过后,Robot-B 再通过
- 或者:Robot-B 稍微减速,让 Robot-A 先过

22.5 门禁联动在不同场景的应用

医院:

  • 手术室门:只有特定机器人有权进入

  • 隔离区门:需要消毒流程

  • 药房门:需要身份验证

写字楼:

  • 楼层门禁:不同楼层不同权限

  • 会议室门:预约时段才能进入

  • 机房门:高安全级别

酒店:

  • 客房门:配送机器人需要临时权限

  • 后厨门:只有送餐机器人能进

  • 消防通道:紧急时才开放


二十三、异构机器人(Heterogeneous Robots)

23.1 什么是异构机器人系统?

异构(Heterogeneous)意味着系统中的机器人不是"一模一样"的。它们可能在以下维度不同:

维度 差异示例
品牌 机器人 A 来自厂商 X,机器人 B 来自厂商 Y
类型 配送机器人 vs 清洁机器人 vs 巡检机器人
尺寸 小型配送车 vs 大型叉车
速度 0.5 m/s vs 2.0 m/s
能力 能倒退 vs 不能倒退,能旋转 vs 不能旋转
导航方式 激光导航 vs 视觉导航 vs 磁条导航
通信协议 ROS 2 vs 私有 API vs VDA5050
载荷 5kg vs 500kg

23.2 异构带来的调度挑战

挑战 1:不同速度

快车(2 m/s)和慢车(0.5 m/s)在同一走廊
快车会追上慢车
→ 需要考虑"追尾冲突"
→ 传统系统(假设所有车速度相同)无法处理

挑战 2:不同尺寸

小型配送机器人(宽 0.5m)
大型清洁机器人(宽 1.2m)

走廊宽 1.5m:
- 两台小机器人可以并排通过
- 一台大机器人 + 一台小机器人不能并排
- 两台大机器人完全不能并排

→ 通行规则取决于机器人组合

挑战 3:不同能力

机器人 A 可以倒退
机器人 B 不能倒退

如果两者在窄走廊相遇:
- 让 A 倒退是可行的
- 让 B 倒退是不可行的

→ 协商时必须考虑各自的能力约束

挑战 4:不同通信协议

机器人 A 使用 ROS 2 话题通信
机器人 B 使用 HTTP REST API
机器人 C 使用 MQTT + VDA5050

→ 调度系统需要统一接口

23.3 RMF 如何处理异构性?

RMF 的核心设计就是为异构而生的。它通过以下机制处理异构性:

1. Fleet Adapter 抽象

每种机器人有自己的 Fleet Adapter,负责将 RMF 的统一指令翻译为具体机器人能理解的命令。

RMF 统一指令:
  "移动到坐标 (10, 5)"

Fleet Adapter A(ROS 2 机器人):
  发布 nav2 goal

Fleet Adapter B(HTTP API 机器人):
  POST /api/navigate {"x": 10, "y": 5}

Fleet Adapter C(VDA5050 机器人):
  发布 MQTT order 消息

2. Robot Profile

每台机器人在 RMF 中有一个 Profile,描述其物理特性:

Profile {
  footprint: 物理占地面积
  vicinity: 安全区域
  max_speed: 最大速度
  max_acceleration: 最大加速度
  can_reverse: 是否能倒退
  rotation_speed: 旋转速度
}

冲突检测和协商时,系统会根据 Profile 做出不同决策。

3. 能力声明

每台机器人声明自己能执行的任务类型:

Robot-A capabilities: [delivery, pickup]
Robot-B capabilities: [cleaning, disinfection]
Robot-C capabilities: [patrol, inspection]

任务分配时,系统只会把任务分配给有对应能力的机器人。

23.4 异构系统的实际价值

为什么要支持异构?因为现实就是异构的。

一家医院可能有:
- 3 台配送机器人(品牌 A,负责送药送餐)
- 2 台消毒机器人(品牌 B,负责紫外线消毒)
- 1 台巡检机器人(品牌 C,负责夜间安防)
- 1 台导引机器人(品牌 D,负责引导患者)

这些机器人:
- 共享走廊、电梯、门
- 需要互相避让
- 需要协调电梯使用
- 需要统一监控

如果没有统一的调度系统:
- 配送机器人和消毒机器人在走廊撞上
- 两台机器人同时抢电梯
- 运维人员需要看 4 个不同的监控界面

RMF 的价值就在于:

让所有这些不同的机器人在同一个"交通系统"下协同工作。


二十四、Fleet Adapter:RMF 的关键设计

24.1 Fleet Adapter 的定位

Fleet Adapter 是 RMF 架构中最重要的"接口层"。它的定位是:

RMF 核心和具体机器人之间的翻译官。

RMF Core(说"RMF 语言")
      ↓
Fleet Adapter(翻译)
      ↓
具体机器人(说"自己的语言")

24.2 Fleet Adapter 的双向职责

Fleet Adapter 不是单向的,它需要双向工作:

下行(RMF → 机器人):

  • 接收 RMF 的导航指令

  • 翻译为机器人能理解的命令

  • 下发给机器人执行

上行(机器人 → RMF):

  • 获取机器人的实时状态(位置、速度、电量、任务进度)

  • 翻译为 RMF 能理解的格式

  • 上报给 RMF 核心

24.3 Fleet Adapter 的具体接口

RMF 定义了 Fleet Adapter 必须实现的接口:

interface FleetAdapter {
  // 导航相关
  navigate(robot_id, destination, path)  // 让机器人去某个位置
  stop(robot_id)                          // 让机器人停下
  
  // 动作相关
  execute_action(robot_id, action)        // 执行动作(取货、放货等)
  
  // 状态上报
  get_position(robot_id) → (x, y, yaw)   // 获取位置
  get_battery(robot_id) → percentage      // 获取电量
  get_state(robot_id) → state             // 获取状态
  
  // 轨迹相关
  get_remaining_path(robot_id) → path     // 获取剩余路径
  estimate_arrival(robot_id) → time       // 估计到达时间
}

24.4 Full Control vs Traffic Light

RMF 提供两种 Fleet Adapter 模式:

Full Control 模式:

RMF 完全控制机器人的路径
- RMF 计算路径
- RMF 下发每一步指令
- 机器人只负责执行

适用于:机器人本身没有导航能力,或者愿意完全听从 RMF 调度

Traffic Light 模式:

机器人自己导航,RMF 只管"交通"
- 机器人自己规划路径
- 但在进入共享区域前,需要向 RMF "请求通行权"
- RMF 批准后才能进入
- 类似于"红绿灯"

适用于:机器人有自己的导航系统,只需要和其他机器人协调

这两种模式的区别:

维度 Full Control Traffic Light
路径规划 RMF 负责 机器人自己负责
控制粒度 精细(每一步) 粗粒度(区域级)
集成难度 高(需要深度对接) 低(只需要简单接口)
协调效果 好(RMF 完全掌控) 一般(只能在区域边界协调)
适用场景 新部署的机器人 已有导航系统的机器人

24.5 为什么 Traffic Light 模式很重要?

因为现实中,很多机器人已经有了成熟的导航系统。让它们完全放弃自己的导航,改用 RMF 的路径规划,成本太高。

Traffic Light 模式提供了一种"最小侵入"的集成方式:

机器人厂商只需要实现:
1. 上报当前位置
2. 在进入共享区域前请求通行权
3. 收到"停止"指令时能停下

不需要:
- 改变导航算法

- 暴露内部路径规划
    
- 修改底层控制逻辑
    

这大大降低了集成门槛,使得 RMF 可以快速接入已有的机器人系统。

24.6 Fleet Adapter 的实现示例

一个典型的 Full Control Fleet Adapter 的工作循环:

while running:
  // 上行:上报状态
  position = robot.get_position()
  battery = robot.get_battery()
  rmf.update_state(robot_id, position, battery)
  
  // 下行:接收并执行指令
  if rmf.has_new_command(robot_id):
    command = rmf.get_command(robot_id)
    if command.type == NAVIGATE:
      robot.navigate_to(command.destination)
    elif command.type == STOP:
      robot.stop()
    elif command.type == ACTION:
      robot.execute(command.action)
  
  // 进度上报
  if robot.is_navigating():
    remaining = robot.get_remaining_path()
    rmf.update_trajectory(robot_id, remaining)
  
  sleep(100ms)  // 10Hz 更新频率

24.7 Fleet Adapter 的错误处理

Fleet Adapter 还需要处理各种异常:

异常 处理方式
机器人失联 上报 UNAVAILABLE 状态,RMF 释放其预约的资源
导航失败 上报 ERROR,RMF 重新规划或重新分配任务
电量过低 上报低电量,RMF 安排充电任务
被人阻挡 上报 WAITING,RMF 更新轨迹预测
指令超时 重试或上报失败

二十五、为什么异构兼容很重要?

25.1 现实世界的"机器人孤岛"问题

目前大多数建筑中的机器人系统是这样的:

配送机器人(厂商 A)
  → 使用厂商 A 的调度系统
  → 只能看到自己的机器人

清洁机器人(厂商 B)
  → 使用厂商 B 的调度系统
  → 只能看到自己的机器人

巡检机器人(厂商 C)
  → 使用厂商 C 的调度系统
  → 只能看到自己的机器人

结果:

  • 三套系统互相不知道对方的存在

  • 机器人在走廊相遇时不知道怎么办

  • 电梯被一台机器人占用时,其他机器人不知道

  • 运维人员需要看三个不同的界面

这就是"机器人孤岛"问题。

25.2 孤岛问题的后果

效率低下:

配送机器人在等电梯
清洁机器人也在等同一部电梯
两者互相不知道
→ 电梯来了,两台机器人同时冲向电梯门
→ 卡住
→ 需要人工干预

安全隐患:

巡检机器人在走廊高速行驶
配送机器人从侧面路口出来
两者互相看不到对方的计划
→ 差点碰撞
→ 紧急刹车
→ 货物洒落

运维困难:

出了问题:
- 看厂商 A 的日志:一切正常
- 看厂商 B 的日志:一切正常
- 但实际上两台机器人互相堵了 10 分钟

因为没有统一的视角,问题很难定位。

25.3 RMF 如何解决孤岛问题

RMF 提供了一个"统一的交通层":

所有机器人(无论品牌)都通过 Fleet Adapter 接入 RMF
→ RMF 知道所有机器人的位置和计划
→ RMF 可以协调所有机器人的交通
→ 运维人员有统一的监控界面

这不要求机器人放弃自己的导航系统,只要求它们:

  1. 上报自己的状态

  2. 在共享区域遵守 RMF 的交通规则

这就像城市交通:

每辆车可以是不同品牌(奔驰、宝马、丰田),但都必须遵守同一套交通规则(红绿灯、让行标志)。

25.4 异构兼容的商业价值

对于建筑运营方来说,异构兼容意味着:

价值 说明
不被锁定 不需要所有机器人都买同一家的
最佳组合 可以选择各品类最好的机器人
渐进部署 可以逐步添加新机器人,不需要一次性替换
统一管理 一个界面管理所有机器人
降低风险 某个厂商倒闭不影响整体系统

二十六、rmf_demos:官方演示系统

26.1 rmf_demos 是什么?

rmf_demos 是 Open-RMF 官方提供的完整仿真演示包。它不是一个"Hello World"级别的 Demo,而是包含了完整场景的系统演示。

26.2 rmf_demos 包含的场景

场景 说明 展示的能力
Office 办公楼 多机器人配送、门禁联动
Hospital 医院 多楼层、电梯、多种机器人
Hotel 酒店 客房配送、电梯调度
Airport 机场 大规模、长距离
Clinic 诊所 小规模、紧凑空间

26.3 rmf_demos 的技术栈

仿真层:
  Gazebo(物理仿真)
  → 模拟机器人运动、传感器、环境

RMF 层:
  rmf_traffic(交通调度)
  rmf_task(任务管理)
  rmf_fleet_adapter(车队适配)
  rmf_building_map(建筑地图)

可视化层:
  rmf_visualization(RViz 插件)
  rmf-web(Web 界面)

通信层:
  ROS 2(DDS)

26.4 运行 rmf_demos 能看到什么?

在一个典型的 Hospital Demo 中,你可以看到:

1. 多台机器人同时在不同楼层运行
2. 机器人自动避让(不是简单停车,而是提前调整路径)
3. 机器人排队等电梯
4. 电梯被自动调度(不是先到先得)
5. 机器人通过自动门(门自动开关)
6. 新任务下发后,系统自动选择最合适的机器人
7. 机器人电量低时自动去充电
8. Web 界面实时显示所有机器人状态

26.5 rmf_demos 的学习价值

rmf_demos 的价值不只是"看个热闹"。通过阅读其源码,可以学到:

学习内容 对应代码
如何定义建筑地图 building_map 配置文件
如何写 Fleet Adapter fleet_adapter 节点
如何配置交通图 nav_graph 文件
如何定义任务 task 配置
如何集成电梯 lift_adapter 节点
如何集成门 door_adapter 节点
如何做可视化 rmf-web 前端

26.6 rmf_demos 的局限性

需要注意的是,rmf_demos 是仿真环境,和真实部署有差距:

仿真 真实
机器人完美执行指令 机器人可能偏离、延迟
网络无延迟 网络可能丢包、延迟
电梯立即响应 电梯可能被人占用
环境完全已知 可能有未知障碍物
不会故障 随时可能故障

二十七、rmf_demos 使用什么仿真?

27.1 Gazebo 仿真平台

rmf_demos 主要基于 Gazebo 进行仿真。Gazebo 是 ROS 生态中最主流的机器人仿真平台。

Gazebo 提供:

能力 说明
物理引擎 模拟重力、碰撞、摩擦
传感器模拟 激光雷达、摄像头、IMU
环境建模 建筑、家具、障碍物
多机器人 同时仿真多台机器人
插件系统 可扩展自定义功能

27.2 RMF 对 Gazebo 的扩展

RMF 为 Gazebo 添加了专门的插件:

rmf_building_map_tools
  → 从建筑地图自动生成 Gazebo 世界文件

rmf_robot_sim_gazebo_plugins
  → 模拟机器人的 Fleet Adapter 行为

rmf_simulation_assets
  → 提供标准化的仿真资源(机器人模型、建筑模型)

27.3 Traffic Editor:地图编辑工具

RMF 提供了一个专门的地图编辑工具 Traffic Editor:

功能:
- 导入建筑平面图(图片)
- 在平面图上绘制导航图(Waypoint + Lane)
- 定义楼层
- 放置电梯、门
- 设置交通规则(单向、限速)
- 导出为 RMF 格式 + Gazebo 世界文件

这个工具大大简化了场景搭建的过程。不需要手写 XML 或 YAML,可以可视化地编辑。

27.4 从 Traffic Editor 到仿真的流程

1. 获取建筑平面图(CAD 或扫描图)
        ↓
2. 在 Traffic Editor 中导入平面图
        ↓
3. 绘制导航图(标记路径点和通道)
        ↓
4. 标记设施(电梯位置、门位置、充电桩)
        ↓
5. 设置交通规则(单向通道、限速区域)
        ↓
6. 导出:
   - nav_graph.yaml(RMF 导航图)
   - building_map.yaml(建筑信息)
   - world.sdf(Gazebo 仿真世界)
        ↓
7. 启动仿真

二十八、rmf_demos 能看到什么?(详细分析)

28.1 多机器人动态避让

在 Demo 中,最直观的就是多台机器人在走廊中的动态避让:

场景:两台机器人在走廊相向而行

传统方式(你可能见过的):
- 两车接近
- 检测到对方
- 一辆停下
- 另一辆通过
- 停下的那辆继续

RMF 方式(Demo 中看到的):
- 两车还没接近时,系统就预测到冲突
- 系统协商:让其中一辆稍微减速
- 或者让其中一辆提前进入旁边的等待区
- 两车"流畅地"错开
- 几乎没有完全停车的情况

这种"流畅感"就是时空规划的直观体现。

28.2 电梯调度的可视化

场景:3 台机器人需要使用电梯

可以看到:
1. 机器人 A 到达电梯口,电梯被调来
2. 机器人 A 进入电梯,上到 3 楼
3. 同时,机器人 B 到达 1 楼电梯口
4. 系统判断:机器人 C 在 3 楼也要下来
5. 电梯先让 A 出去,再接 C 下来
6. C 到 1 楼出去后,B 进入
7. 整个过程没有"抢电梯"的混乱

28.3 任务动态分配

场景:通过 Web 界面下发配送任务

1. 用户点击"从药房送药到 302 病房"
2. 系统显示:选择了 Robot-B(最近且电量充足)
3. Robot-B 开始移动
4. 途中又来了一个紧急任务
5. 系统判断:Robot-B 正在执行中,选择 Robot-A
6. 两台机器人同时执行各自任务
7. 在走廊相遇时自动协调

28.4 故障恢复

场景:模拟机器人故障

1. Robot-A 正在执行任务
2. 手动让 Robot-A "故障"(停止响应)
3. 系统检测到 Robot-A 失联
4. 释放 Robot-A 预约的资源
5. 将 Robot-A 的任务重新分配给 Robot-C
6. Robot-C 接手任务,继续执行
7. 其他机器人绕开 Robot-A 停留的位置

二十九、RMF 的动态任务分配

29.1 任务类型

RMF 支持多种任务类型:

任务类型 说明 示例
Delivery 配送 从 A 取货送到 B
Loop 循环巡逻 沿固定路线反复巡逻
Clean 清洁 清洁指定区域
Charge 充电 去充电桩充电
Custom 自定义 任何用户定义的任务

29.2 任务分配的考虑因素

当一个新任务到来时,系统需要决定派哪台机器人。考虑因素包括:

因素 权重 说明
距离 离任务起点的距离
电量 是否有足够电量完成任务
能力匹配 必须 机器人是否有执行该任务的能力
当前状态 必须 是否空闲
预计完成时间 如果正在执行任务,什么时候能空闲
交通影响 派这台机器人是否会造成交通拥堵
公平性 避免某台机器人过度使用

29.3 任务分配算法

RMF 的任务分配不是简单的"最近优先"。它使用的是基于代价的分配:

对每台候选机器人,计算总代价:

cost(robot, task) = 
    travel_cost(robot.position → task.start)  // 去取货的代价
  + execution_cost(task)                       // 执行任务的代价
  + traffic_impact(robot, task)                // 对交通的影响
  + opportunity_cost(robot)                    // 机会成本

选择总代价最小的机器人。

其中"机会成本"是一个有趣的概念:

如果 Robot-A 现在空闲,但 5 分钟后有一个高优先级任务需要它
那么现在把它派去做低优先级任务,5 分钟后它就不可用了
→ 机会成本高
→ 应该派其他机器人做当前的低优先级任务

29.4 任务队列和优先级

RMF 维护一个任务队列,支持优先级:

Task Queue:
  [Priority 1] 紧急送药到 ICU        → 立即执行
  [Priority 2] 送餐到 302 病房       → 尽快执行
  [Priority 3] 清洁 1 楼大厅         → 空闲时执行
  [Priority 4] 巡检 B1 设备间        → 低优先级

高优先级任务可以:

  • 抢占低优先级任务的机器人

  • 获得交通优先权(协商时代价权重更高)

  • 优先使用电梯

29.5 任务的生命周期

PENDING → ASSIGNED → EXECUTING → COMPLETED
                         ↓
                      FAILED → REASSIGNED → EXECUTING → ...
状态 含义
PENDING 等待分配
ASSIGNED 已分配给某台机器人
EXECUTING 正在执行
COMPLETED 完成
FAILED 失败(机器人故障、路径不可达等)
REASSIGNED 重新分配给其他机器人
CANCELLED 被取消

29.6 复合任务(Composed Tasks)

RMF 还支持复合任务,即由多个子任务组成的任务:

复合任务:"从药房取药送到 3 楼 302 病房"

子任务分解:
1. 移动到药房
2. 执行取药动作(等待药剂师放药)
3. 移动到电梯口
4. 乘电梯到 3 楼
5. 移动到 302 病房
6. 执行送药动作(等待护士确认)
7. 返回待命点

每个子任务有自己的状态,系统可以精确跟踪进度。


三十、RMF 为什么适合医院场景?

30.1 医院的特殊性

医院是最复杂的机器人部署场景之一:

特点 挑战
多楼层 需要电梯联动
人流密集 需要动态避障
24小时运行 不能停机维护
多种机器人 配送、消毒、巡检
严格的区域管理 手术室、隔离区有权限要求
紧急情况 需要快速响应
卫生要求 消毒机器人有特殊路线
噪音限制 夜间需要安静运行

30.2 传统 AGV 在医院的困境

传统 AGV 系统在医院面临的问题:

问题 1:固定路线不适应人流变化
- 白天门诊区人多,机器人走不动
- 夜间人少,但路线不能动态调整
- 结果:白天效率极低

问题 2:不支持多楼层
- 传统 AGV 通常只在一层运行
- 跨楼层需要人工搬运
- 或者每层独立部署一套系统

问题 3:不支持异构
- 配送机器人和消毒机器人来自不同厂商
- 两套系统互相不知道对方
- 在走廊相遇时混乱

问题 4:不支持动态优先级
- 紧急送血任务和普通送餐任务优先级不同
- 传统系统很难动态调整优先级

30.3 RMF 如何解决医院场景的问题

问题 RMF 的解决方案
人流变化 时空规划 + 动态重规划,根据实时情况调整
多楼层 原生支持多层地图 + 电梯联动
异构机器人 Fleet Adapter 统一管理
动态优先级 任务优先级系统 + 抢占机制
区域权限 交通图中标记权限区域
紧急响应 高优先级任务可以抢占资源
24小时运行 自动充电调度 + 故障自动恢复

30.4 医院场景的典型一天

06:00 - 早班开始
  - 配送机器人开始送早餐
  - 消毒机器人完成夜间消毒,回充电桩

07:00 - 门诊开始
  - 导引机器人在大厅引导患者
  - 配送机器人送检验标本到检验科
  - 人流增加,RMF 自动调整机器人路线避开人流密集区

10:00 - 高峰期
  - 多台配送机器人同时运行
  - 电梯使用频繁,RMF 优化电梯调度
  - 紧急送血任务插入,抢占电梯优先权

12:00 - 午餐配送
  - 大量送餐任务
  - RMF 批量调度,优化路线
  - 避免所有机器人同时挤在电梯口

15:00 - 手术室补给
  - 高优先级任务
  - 机器人获得走廊优先通行权
  - 其他机器人自动避让

18:00 - 晚班交接
  - 部分机器人去充电
  - 夜间巡检机器人上线
  - 消毒机器人开始工作

22:00 - 夜间模式
  - 机器人降低速度(噪音控制)
  - 消毒机器人全面消毒
  - 巡检机器人安防巡逻
  - 配送机器人待命(处理紧急需求)

三十一、RMF 的工程挑战

RMF 的设计很先进,但在实际工程中也面临很多挑战。

31.1 系统复杂度

RMF 的技术栈非常深:

底层:
  Linux + DDS + ROS 2

中间层:
  rmf_traffic(C++,高性能计算)
  rmf_task(任务调度逻辑)
  rmf_fleet_adapter(Python/C++ 混合)

上层:
  rmf-web(React + WebSocket)
  Traffic Editor(Qt)

仿真:
  Gazebo + 自定义插件

部署:
  Docker + 多节点分布式

要完整理解和使用 RMF,需要掌握:

技术 用途
C++ 核心算法(rmf_traffic)
Python Fleet Adapter、脚本
ROS 2 通信框架
DDS 底层通信
Gazebo 仿真
React Web 界面
Docker 部署
YAML 配置

学习曲线相当陡峭。

31.2 调试难度

RMF 系统的调试比传统系统难得多:

问题 1:分布式系统的调试

问题现象:机器人在走廊停了 10 秒不动

可能原因:
- rmf_traffic 的协商超时了?
- Fleet Adapter 的状态上报延迟了?
- DDS 通信丢包了?
- Gazebo 仿真卡顿了?
- 机器人的导航模块出问题了?

需要同时查看多个节点的日志才能定位。

问题 2:时序问题

问题现象:两台机器人偶尔会在走廊"僵持"

原因:
- 协商结果下发到两台机器人的时间有微小差异
- 机器人 A 收到"你先走"时,已经停下了
- 机器人 B 收到"你等一下"时,已经走过了冲突点
- 结果:两者都停下等对方

这种时序 bug 很难复现,很难调试。

问题 3:状态不一致

问题现象:RMF 认为机器人在 A 点,但机器人实际在 B 点

原因:
- Fleet Adapter 上报延迟
- 机器人被人推动了
- 定位系统漂移

后果:
- RMF 基于错误位置做出的调度决策全部错误
- 可能导致"幽灵冲突"(RMF 认为有冲突但实际没有)
- 或者"遗漏冲突"(RMF 认为没冲突但实际有)

31.3 调试工具和方法

RMF 提供了一些调试工具:

工具 用途
rmf-web Dashboard 实时查看所有机器人状态和轨迹
ROS 2 日志 查看各节点的详细日志
RViz 可视化 查看导航图、轨迹、冲突
rosbag 录制 录制通信数据,事后回放分析
Schedule 可视化 查看时空调度表的状态

但即使有这些工具,调试 RMF 系统仍然需要:

  • 对整体架构有深入理解

  • 能够同时分析多个节点的行为

  • 理解时序和并发问题

  • 有耐心(有些 bug 很难复现)

31.4 性能挑战

冲突检测的计算量:

N 台机器人 → N*(N-1)/2 对需要检测
每对需要做连续轨迹的时空距离计算
机器人数量增加时,计算量快速增长

20 台机器人:190 对
50 台机器人:1225 对
100 台机器人:4950 对

协商的收敛时间:

简单冲突(2 台机器人):毫秒级
复杂冲突(多台机器人连锁):可能需要多轮协商
极端情况:协商可能不收敛,需要超时回退

实时性要求:

机器人以 1 m/s 移动
100ms 的决策延迟 = 10cm 的位置误差
如果冲突检测太慢,机器人可能已经进入冲突区域

31.5 不适合简单项目

如果你的场景是:

- 2-3 台机器人
- 单层平面
- 固定路线
- 不需要电梯/门
- 不需要异构

那么 RMF 可能过于复杂。用 openTCS 或者自己写一个简单的调度系统就够了。

RMF 的价值在规模和复杂度上体现:

场景规模 推荐方案
2-3 台同构机器人,单层 自己写简单调度
5-10 台同构机器人,单层,固定路线 openTCS
10+ 台机器人,多层,异构,动态环境 Open-RMF
50+ 台机器人,多建筑,复杂设施联动 Open-RMF + 定制优化

引入 RMF 的成本:

  • 学习成本:团队需要 2-3 个月熟悉

  • 集成成本:每种机器人需要开发 Fleet Adapter

  • 运维成本:分布式系统的监控和维护

  • 调试成本:问题定位比传统系统难

如果场景复杂度不够,这些成本就不值得。


三十二、RMF 和 openTCS 到底怎么选?

这是实际项目中最常被问到的问题。

32.1 决策框架

不要从"哪个更先进"的角度选择,而要从"哪个更适合"的角度选择。

问自己以下问题:

Q1: 机器人是否需要跨楼层?
  是 → 倾向 RMF
  否 → 继续看

Q2: 是否有多种品牌/类型的机器人?
  是 → 倾向 RMF
  否 → 继续看

Q3: 路径是否固定?
  固定 → 倾向 openTCS
  动态 → 倾向 RMF

Q4: 是否需要和电梯/门联动?
  是 → 倾向 RMF
  否 → 继续看

Q5: 机器人数量?
  < 10 台,同构 → openTCS 足够
  > 10 台,或异构 → 考虑 RMF

Q6: 团队技术栈?
  Java 为主 → openTCS 更容易上手
  ROS/C++/Python 为主 → RMF 更自然

Q7: 环境动态性?
  基本静态(工厂) → openTCS
  高度动态(医院、商场) → RMF

32.2 详细对比

维度 openTCS Open-RMF
核心语言 Java C++ / Python
通信框架 自有 ROS 2 / DDS
路径模型 固定图 + 资源锁 动态轨迹 + 时空协商
冲突解决 先到先得 / 优先级 Negotiation 协商
地图 单层 多层
设施联动 不支持 电梯、门、充电桩
异构支持 强(Fleet Adapter)
仿真 内置简单仿真 Gazebo 完整仿真
可视化 Java Swing 桌面应用 Web 界面
社区 工业物流社区 ROS 机器人社区
文档 完善 中等(在改善中)
学习曲线 中等 陡峭
部署复杂度 低(单 JVM) 高(分布式多节点)
成熟度 高(10+ 年) 中(3-4 年)
适用规模 中小型 中大型

32.3 可以混合使用吗?

理论上可以。一种可能的架构:

场景:工厂 + 办公楼

工厂区域(固定路线 AGV):
  → 使用 openTCS 管理
  → 稳定、成熟、够用

办公楼区域(动态 AMR):
  → 使用 RMF 管理
  → 灵活、支持电梯

两个系统之间:
  → 通过网关对接
  → 在交界区域做交通协调

但这种混合架构增加了系统复杂度,通常只在大型项目中才值得。

32.4 未来趋势

从长期看:

  • openTCS 会继续在传统工业物流中占据主导地位

  • RMF 会在"建筑级机器人"领域快速发展

  • 两者可能会互相借鉴(openTCS 可能加入时空规划,RMF 可能简化部署)

  • VDA5050 标准可能成为两者共同的机器人接口标准

32.5 一个真实的选型案例

项目:某三甲医院机器人系统

需求:
- 3 台配送机器人(品牌 A)
- 2 台消毒机器人(品牌 B)
- 1 台巡检机器人(品牌 C)
- 5 层楼,2 部电梯
- 需要和门禁系统联动
- 24 小时运行

分析:
- 异构 ✓ → 需要统一调度
- 多楼层 ✓ → 需要电梯联动
- 门禁 ✓ → 需要设施联动
- 6 台机器人 → 规模中等
- 动态环境(医院人流) → 需要动态规划

结论:选择 Open-RMF

如果需求变成:
- 10 台同品牌 AGV
- 单层仓库
- 固定路线
- 无电梯/门

结论:选择 openTCS(更简单、更稳定)

三十三、如果你做比赛,RMF 最值得学什么?

对于参加机器人比赛(如 RoboMaster、RoboCup、各类创新赛)的同学,RMF 最大的价值不是直接使用它,而是学习它的设计思想。

33.1 时空路径规划

这是最值得学习的第一个思想。

传统做法(大多数比赛队伍):

for each robot:
    plan_path(start, goal)  // 独立规划,不考虑其他机器人
    
if collision_detected:
    stop_and_wait()  // 碰到了再说

RMF 思想:

for each robot:
    plan_trajectory(start, goal, time)  // 规划带时间的轨迹
    
check_all_trajectories_for_conflicts()  // 提前检测所有冲突
    
if conflict_found:
    negotiate_and_adjust()  // 提前协调

在比赛中的应用:

场景:3 台机器人需要同时到达不同目标

传统做法:
- 3 台各自规划路径
- 在路口碰到 → 停车 → 等待 → 浪费时间
- 总完成时间:45 秒

时空规划:
- 3 台同时规划,考虑时间
- 提前安排:Robot-A 先过路口(10:05),Robot-B 后过(10:08)
- 没有停车,流畅通过
- 总完成时间:30 秒

比赛中 15 秒的差距可能就是名次的差距。

具体实现建议:

# 简化版时空路径规划
class SpatioTemporalPlanner:
    def __init__(self):
        self.schedule = {}  # robot_id → trajectory
    
    def plan(self, robot_id, start, goal, start_time):
        # 1. 规划空间路径
        path = a_star(start, goal)
        
        # 2. 计算时间(假设匀速)
        trajectory = []
        t = start_time
        for i, point in enumerate(path):
            trajectory.append((point, t))
            if i < len(path) - 1:
                distance = dist(path[i], path[i+1])
                t += distance / robot_speed
        
        # 3. 检测与已有轨迹的冲突
        conflicts = self.detect_conflicts(trajectory)
        
        # 4. 如果有冲突,调整
        if conflicts:
            trajectory = self.resolve_conflicts(trajectory, conflicts)
        
        # 5. 提交到 schedule
        self.schedule[robot_id] = trajectory
        return trajectory
    
    def detect_conflicts(self, new_trajectory):
        conflicts = []
        for robot_id, existing in self.schedule.items():
            for t in time_range:
                pos_new = interpolate(new_trajectory, t)
                pos_existing = interpolate(existing, t)
                if distance(pos_new, pos_existing) < SAFETY_DIST:
                    conflicts.append((robot_id, t))
        return conflicts

33.2 预测式交通调度

传统做法:

机器人 A 在路口发现机器人 B → 停车 → 等 B 过去 → 继续

RMF 思想:

提前 5 秒就知道 A 和 B 会在路口相遇
→ 让 A 减速 0.2 m/s
→ A 到达路口时 B 已经过去了
→ A 不需要停车

在比赛中的应用:

# 预测式避让
def predictive_avoidance(robot_a, robot_b):
    # 预测未来 5 秒的轨迹
    future_a = predict_trajectory(robot_a, horizon=5.0)
    future_b = predict_trajectory(robot_b, horizon=5.0)
    
    # 找到最早的冲突时刻
    conflict_time = find_first_conflict(future_a, future_b)
    
    if conflict_time is None:
        return  # 无冲突,不需要处理
    
    # 计算调整方案
    # 方案 1:A 减速
    cost_a_slow = estimate_delay(robot_a, slow_down=0.2)
    
    # 方案 2:B 减速
    cost_b_slow = estimate_delay(robot_b, slow_down=0.2)
    
    # 方案 3:A 绕路
    cost_a_detour = estimate_detour(robot_a)
    
    # 选择代价最小的方案
    best = min(cost_a_slow, cost_b_slow, cost_a_detour)
    apply(best)

33.3 协商机制

传统做法:

固定优先级:编号小的先走

问题:编号大的机器人可能离目标只有 1 米,但因为优先级低,要等编号小的机器人走完 50 米。

RMF 思想:

谁让步代价小,谁让步。

在比赛中的简化实现:

def simple_negotiation(robot_a, robot_b, conflict_point):
    """
    简化版协商:比较双方让步代价,代价小的让步
    """
    # 计算 A 让步的代价(等待时间)
    cost_a_yield = time_for_b_to_pass(robot_b, conflict_point)
    
    # 计算 B 让步的代价(等待时间)
    cost_b_yield = time_for_a_to_pass(robot_a, conflict_point)
    
    # 考虑任务紧急程度
    cost_a_yield *= robot_a.task_priority  # 高优先级任务让步代价更大
    cost_b_yield *= robot_b.task_priority
    
    if cost_a_yield < cost_b_yield:
        robot_a.yield()  # A 让步(等待或减速)
    else:
        robot_b.yield()  # B 让步

33.4 设施联动思想

即使比赛中没有电梯,这个思想也很有用。

核心思想:

机器人系统不只管机器人,还要管"共享资源"。

在比赛中的应用:

共享资源可能是:
- 充电桩(多台机器人共用)
- 物料站(取放物料的位置)
- 窄通道(一次只能一台通过)
- 工作台(执行任务的位置)

这些都可以用 RMF 的"设施联动"思想来管理:
- 提前预约
- 排队等待
- 使用完释放
- 动态调度
class SharedResource:
    """共享资源管理(类似 RMF 的设施联动)"""
    
    def __init__(self, name, capacity=1):
        self.name = name
        self.capacity = capacity
        self.current_users = []
        self.queue = []
    
    def request(self, robot_id, priority=0):
        """请求使用资源"""
        if len(self.current_users) < self.capacity:
            self.current_users.append(robot_id)
            return True  # 立即获得
        else:
            self.queue.append((priority, robot_id))
            self.queue.sort(reverse=True)  # 高优先级排前面
            return False  # 需要等待
    
    def release(self, robot_id):
        """释放资源"""
        self.current_users.remove(robot_id)
        if self.queue:
            _, next_robot = self.queue.pop(0)
            self.current_users.append(next_robot)
            notify(next_robot, "resource_available")

33.5 比赛中的完整架构建议

结合 RMF 的思想,一个适合比赛的多机器人系统架构:

┌─────────────────────────────────────────────┐
│              Task Manager                    │
│  - 任务队列                                  │
│  - 任务分配(基于代价)                       │
│  - 优先级管理                                │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│          Traffic Scheduler                   │
│  - 时空轨迹管理(Schedule)                   │
│  - 冲突检测(连续轨迹)                       │
│  - 简化版 Negotiation                        │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│          Path Planner                        │
│  - A* / CBS / WHCA*                         │
│  - 时空路径规划                              │
│  - 动态重规划                                │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│          Resource Manager                    │
│  - 共享资源管理                              │
│  - 预约 / 释放                              │
│  - 队列管理                                  │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│          Robot Interface                     │
│  - 统一接口(类似 Fleet Adapter)             │
│  - 状态上报                                  │
│  - 指令下发                                  │
└─────────────────┬───────────────────────────┘
                  │
         ┌───────┼───────┐
         ▼       ▼       ▼
      Robot-1  Robot-2  Robot-3

三十四、一个适合比赛的 RMF 风格架构(详细设计)

34.1 整体设计原则

从 RMF 中提取适合比赛的设计原则:

原则 说明 比赛中的体现
时空一体 路径带时间 避免不必要的停车
预测优先 提前发现冲突 减少紧急制动
协商代替抢占 代价最小的让步 全局效率更高
增量式规划 新任务不影响已有计划 系统稳定
容错设计 偏差时快速恢复 比赛中不怕意外

34.2 推荐技术栈(详细版)

模块 推荐技术 备选 理由
通信 ROS 2 Humble ZeroMQ ROS 2 生态完善,工具链好
核心调度 C++ Python(原型) 性能关键路径用 C++
路径规划 CBS / SIPP A* + 时间窗 CBS 最优但慢,SIPP 快且好
仿真 Gazebo Stage / 自写 Gazebo 最接近真实
可视化 RViz2 + Web 纯 Web RViz2 调试方便
前端 Vue 3 + WebSocket React Vue 上手快
状态机 SMACH / BehaviorTree 自写 FSM 任务执行用行为树更灵活

34.3 核心模块详细设计

模块 1:Schedule(时空调度表)

// C++ 实现的核心 Schedule
class Schedule {
public:
    // 提交轨迹
    bool submit(const std::string& robot_id, const Trajectory& trajectory) {
        auto conflicts = detect_conflicts(trajectory);
        if (conflicts.empty()) {
            trajectories_[robot_id] = trajectory;
            return true;
        }
        return false;  // 有冲突,需要协商
    }
    
    // 更新轨迹
    void update(const std::string& robot_id, const Trajectory& trajectory) {
        trajectories_[robot_id] = trajectory;
        // 重新检测受影响的冲突
        recheck_conflicts(robot_id);
    }
    
    // 检测冲突
    std::vector<Conflict> detect_conflicts(const Trajectory& new_traj) {
        std::vector<Conflict> conflicts;
        for (const auto& [id, existing] : trajectories_) {
            auto conflict = check_pair(new_traj, existing);
            if (conflict.has_value()) {
                conflicts.push_back(conflict.value());
            }
        }
        return conflicts;
    }
    
private:
    // 两条轨迹的冲突检测
    std::optional<Conflict> check_pair(
        const Trajectory& a, const Trajectory& b) {
        
        // 找到时间重叠区间
        double t_start = std::max(a.start_time(), b.start_time());
        double t_end = std::min(a.end_time(), b.end_time());
        
        if (t_start >= t_end) return std::nullopt;  // 时间不重叠
        
        // 在重叠区间内采样检测
        double dt = 0.1;  // 100ms 采样
        for (double t = t_start; t <= t_end; t += dt) {
            auto pos_a = a.position_at(t);
            auto pos_b = b.position_at(t);
            double dist = (pos_a - pos_b).norm();
            
            if (dist < SAFETY_DISTANCE) {
                return Conflict{a.robot_id(), b.robot_id(), t, pos_a};
            }
        }
        return std::nullopt;
    }
    
    std::unordered_map<std::string, Trajectory> trajectories_;
};

模块 2:Negotiation Engine(协商引擎)

# Python 实现的协商引擎(比赛中够用)
class NegotiationEngine:
    def __init__(self, schedule, planner):
        self.schedule = schedule
        self.planner = planner
    
    def resolve(self, conflict):
        """
        解决一个冲突
        返回:调整方案
        """
        robot_a = conflict.robot_a
        robot_b = conflict.robot_b
        
        # 为双方生成替代方案
        alternatives_a = self.generate_alternatives(robot_a, conflict)
        alternatives_b = self.generate_alternatives(robot_b, conflict)
        
        # 评估所有组合,选择全局代价最小的
        best_cost = float('inf')
        best_plan = None
        
        # 方案 1:A 让步
        for alt_a in alternatives_a:
            cost = alt_a.cost + 0  # B 不变,代价为 0
            if cost < best_cost:
                # 检查 A 的新轨迹是否引入新冲突
                new_conflicts = self.schedule.detect_conflicts(alt_a.trajectory)
                if not new_conflicts:
                    best_cost = cost
                    best_plan = ('a_yields', alt_a)
        
        # 方案 2:B 让步
        for alt_b in alternatives_b:
            cost = 0 + alt_b.cost  # A 不变
            if cost < best_cost:
                new_conflicts = self.schedule.detect_conflicts(alt_b.trajectory)
                if not new_conflicts:
                    best_cost = cost
                    best_plan = ('b_yields', alt_b)
        
        # 方案 3:双方都调整
        for alt_a in alternatives_a:
            for alt_b in alternatives_b:
                cost = alt_a.cost + alt_b.cost
                if cost < best_cost:
                    # 检查两条新轨迹之间是否冲突
                    if not trajectories_conflict(alt_a.trajectory, alt_b.trajectory):
                        best_cost = cost
                        best_plan = ('both_adjust', alt_a, alt_b)
        
        return best_plan
    
    def generate_alternatives(self, robot, conflict):
        """为机器人生成替代方案"""
        alternatives = []
        
        # 方案类型 1:在冲突点前等待
        wait_traj = self.planner.plan_with_wait(
            robot, conflict.point, wait_duration=3.0)
        alternatives.append(Alternative(
            trajectory=wait_traj,
            cost=3.0  # 等待 3 秒的代价
        ))
        
        # 方案类型 2:绕路
        detour_traj = self.planner.plan_avoiding(
            robot, conflict.point)
        if detour_traj:
            detour_cost = detour_traj.duration - robot.original_duration
            alternatives.append(Alternative(
                trajectory=detour_traj,
                cost=detour_cost
            ))
        
        # 方案类型 3:减速通过
        slow_traj = self.planner.plan_with_speed(
            robot, speed_factor=0.5)
        slow_cost = slow_traj.duration - robot.original_duration
        alternatives.append(Alternative(
            trajectory=slow_traj,
            cost=slow_cost
        ))
        
        return alternatives

模块 3:Task Manager(任务管理器)

class TaskManager:
    def __init__(self, fleet, schedule):
        self.fleet = fleet  # 所有机器人
        self.schedule = schedule
        self.task_queue = PriorityQueue()
        self.active_tasks = {}
    
    def submit_task(self, task):
        """提交新任务"""
        self.task_queue.put((-task.priority, task))  # 负号使高优先级排前面
        self.try_assign()
    
    def try_assign(self):
        """尝试分配队列中的任务"""
        while not self.task_queue.empty():
            _, task = self.task_queue.get()
            
            # 找到最合适的机器人
            best_robot = None
            best_cost = float('inf')
            
            for robot in self.fleet.available_robots():
                # 检查能力匹配
                if not robot.can_execute(task.type):
                    continue
                
                # 检查电量
                if not robot.has_enough_battery(task):
                    continue
                
                # 计算代价
                cost = self.estimate_cost(robot, task)
                
                if cost < best_cost:
                    best_cost = cost
                    best_robot = robot
            
            if best_robot:
                self.assign(best_robot, task)
            else:
                # 没有可用机器人,放回队列
                self.task_queue.put((-task.priority, task))
                break
    
    def estimate_cost(self, robot, task):
        """估计机器人执行任务的代价"""
        # 到达任务起点的距离
        travel_dist = distance(robot.position, task.start)
        travel_time = travel_dist / robot.max_speed
        
        # 任务执行时间
        exec_time = task.estimated_duration
        
        # 交通影响(路径上是否拥挤)
        traffic_factor = self.schedule.congestion_factor(
            robot.position, task.start)
        
        # 综合代价
        return travel_time * (1 + traffic_factor) + exec_time
    
    def assign(self, robot, task):
        """将任务分配给机器人"""
        # 规划轨迹
        trajectory = self.plan_trajectory(robot, task)
        
        # 提交到 schedule
        success = self.schedule.submit(robot.id, trajectory)
        
        if success:
            robot.execute(task, trajectory)
            self.active_tasks[task.id] = (robot.id, task)
        else:
            # 有冲突,触发协商
            self.negotiate_and_retry(robot, task, trajectory)

34.4 SIPP 算法:比赛中推荐的时空规划算法

SIPP(Safe Interval Path Planning)是一种高效的时空路径规划算法,非常适合比赛使用:

核心思想:
- 对每个节点,计算"安全时间区间"(没有其他机器人占用的时间段)
- 在安全区间内搜索路径
- 类似 A*,但搜索空间是 (节点, 安全区间) 对
class SIPP:
    """Safe Interval Path Planning"""
    
    def __init__(self, graph, schedule):
        self.graph = graph
        self.schedule = schedule
    
    def plan(self, start, goal, start_time):
        """规划时空路径"""
        # 1. 计算所有节点的安全区间
        safe_intervals = self.compute_safe_intervals()
        
        # 2. A* 搜索(在安全区间上)
        open_set = PriorityQueue()
        open_set.put((0, start, start_time, 0))  # (f, node, time, interval_idx)
        came_from = {}
        g_score = {(start, 0): 0}
        
        while not open_set.empty():
            f, current, current_time, interval_idx = open_set.get()
            
            if current == goal:
                return self.reconstruct_path(came_from, current, interval_idx)
            
            # 扩展邻居
            for neighbor in self.graph.neighbors(current):
                travel_time = self.graph.edge_time(current, neighbor)
                arrival_time = current_time + travel_time
                
                # 找到 neighbor 在 arrival_time 时的安全区间
                for idx, interval in enumerate(safe_intervals[neighbor]):
                    if interval.start <= arrival_time <= interval.end:
                        # 可以在这个安全区间到达
                        new_g = g_score[(current, interval_idx)] + travel_time
                        
                        if (neighbor, idx) not in g_score or new_g < g_score[(neighbor, idx)]:
                            g_score[(neighbor, idx)] = new_g
                            h = self.heuristic(neighbor, goal)
                            open_set.put((new_g + h, neighbor, arrival_time, idx))
                            came_from[(neighbor, idx)] = (current, interval_idx, arrival_time)
                
                # 也考虑"等待后再出发"的情况
                # (当前安全区间内等待,直到邻居的下一个安全区间开始)
        
        return None  # 无解
    
    def compute_safe_intervals(self):
        """计算每个节点的安全时间区间"""
        safe_intervals = {}
        
        for node in self.graph.nodes():
            # 收集所有占用这个节点的时间段
            occupied = []
            for robot_id, trajectory in self.schedule.items():
                for segment in trajectory.segments:
                    if segment.passes_through(node):
                        # 加上安全余量
                        occupied.append((
                            segment.enter_time - SAFETY_MARGIN,
                            segment.exit_time + SAFETY_MARGIN
                        ))
            
            # 合并重叠的占用区间
            occupied.sort()
            merged = self.merge_intervals(occupied)
            
            # 安全区间 = 总时间 - 占用区间
            safe = []
            prev_end = 0.0
            for start, end in merged:
                if start > prev_end:
                    safe.append(Interval(prev_end, start))
                prev_end = end
            safe.append(Interval(prev_end, float('inf')))  # 最后一个区间到无穷
            
            safe_intervals[node] = safe
        
        return safe_intervals
    
    def merge_intervals(self, intervals):
        """合并重叠区间"""
        if not intervals:
            return []
        merged = [intervals[0]]
        for start, end in intervals[1:]:
            if start <= merged[-1][1]:
                merged[-1] = (merged[-1][0], max(merged[-1][1], end))
            else:
                merged.append((start, end))
        return merged

34.5 SIPP 的优势

维度 A* + 时间展开 SIPP
搜索空间 节点数 × 时间步数 节点数 × 安全区间数
内存 大(时间步多) 小(安全区间通常少)
速度
最优性 最优 最优(在给定约束下)
适用场景 小规模 中大规模

SIPP 的关键洞察:

大多数节点在大多数时间都是空闲的。只需要关注"被占用的时间段",而不是枚举所有时间步。


三十五、CBS 算法详解(比赛中的高级选择)

如果你的比赛需要"最优"的多机器人路径规划,CBS(Conflict-Based Search)是最值得学习的算法。

35.1 CBS 的核心思想

高层搜索:搜索"约束组合"
低层搜索:在给定约束下,为单个机器人规划路径

高层发现冲突 → 分支 → 为冲突双方分别添加约束 → 低层重新规划

35.2 CBS 的完整流程

Step 1: 为每台机器人独立规划最短路径(不考虑其他机器人)
Step 2: 检测所有路径之间的冲突
Step 3: 如果无冲突 → 完成!
Step 4: 选择第一个冲突 (robot_i, robot_j, node, time)
Step 5: 分支:
  - 分支 A:给 robot_i 添加约束"不能在 time 时刻在 node"
  - 分支 B:给 robot_j 添加约束"不能在 time 时刻在 node"
Step 6: 在每个分支中,为受约束的机器人重新规划路径
Step 7: 回到 Step 2

35.3 CBS 的实现

class CBS:
    """Conflict-Based Search"""
    
    def __init__(self, graph, robots):
        self.graph = graph
        self.robots = robots  # [(start, goal), ...]
    
    def solve(self):
        """求解 MAPF 问题"""
        # 初始节点:每台机器人独立规划
        root = CTNode()
        root.constraints = {i: [] for i in range(len(self.robots))}
        root.solution = {}
        
        for i, (start, goal) in enumerate(self.robots):
            path = self.low_level_search(i, start, goal, [])
            if path is None:
                return None  # 无解
            root.solution[i] = path
        
        root.cost = sum(len(p) for p in root.solution.values())
        
        # 高层搜索(Best-First Search)
        open_set = PriorityQueue()
        open_set.put((root.cost, id(root), root))
        
        while not open_set.empty():
            _, _, node = open_set.get()
            
            # 检测冲突
            conflict = self.find_first_conflict(node.solution)
            
            if conflict is None:
                return node.solution  # 无冲突,找到解!
            
            # 分支
            robot_i, robot_j, position, time = conflict
            
            for robot_id in [robot_i, robot_j]:
                # 创建子节点
                child = CTNode()
                child.constraints = deepcopy(node.constraints)
                child.constraints[robot_id].append(
                    Constraint(position, time))
                
                child.solution = dict(node.solution)
                
                # 为受约束的机器人重新规划
                start, goal = self.robots[robot_id]
                new_path = self.low_level_search(
                    robot_id, start, goal, 
                    child.constraints[robot_id])
                
                if new_path is not None:
                    child.solution[robot_id] = new_path
                    child.cost = sum(len(p) for p in child.solution.values())
                    open_set.put((child.cost, id(child), child))
        
        return None  # 无解
    
    def low_level_search(self, robot_id, start, goal, constraints):
        """
        低层搜索:带约束的时空 A*
        约束格式:不能在某时刻出现在某位置
        """
        open_set = PriorityQueue()
        open_set.put((0, (start, 0)))  # (f_score, (position, time))
        came_from = {}
        g_score = {(start, 0): 0}
        
        while not open_set.empty():
            f, (current, t) = open_set.get()
            
            if current == goal:
                return self.reconstruct(came_from, (current, t))
            
            # 扩展:移动到邻居 或 原地等待
            successors = []
            
            # 移动到邻居
            for neighbor in self.graph.neighbors(current):
                successors.append((neighbor, t + 1))
            
            # 原地等待
            successors.append((current, t + 1))
            
            for (next_pos, next_t) in successors:
                # 检查约束
                if self.violates_constraint(robot_id, next_pos, next_t, constraints):
                    continue
                
                new_g = g_score[(current, t)] + 1
                
                if (next_pos, next_t) not in g_score or new_g < g_score[(next_pos, next_t)]:
                    g_score[(next_pos, next_t)] = new_g
                    h = self.heuristic(next_pos, goal)
                    open_set.put((new_g + h, (next_pos, next_t)))
                    came_from[(next_pos, next_t)] = (current, t)
        
        return None  # 在当前约束下无解
    
    def find_first_conflict(self, solution):
        """找到第一个冲突"""
        max_t = max(len(path) for path in solution.values())
        
        for t in range(max_t):
            positions = {}
            for robot_id, path in solution.items():
                pos = path[min(t, len(path) - 1)]  # 到达后原地等待
                
                if pos in positions:
                    # 顶点冲突:两台机器人同时在同一位置
                    return (positions[pos], robot_id, pos, t)
                positions[pos] = robot_id
            
            # 边冲突:两台机器人交换位置(对穿)
            for robot_i, path_i in solution.items():
                for robot_j, path_j in solution.items():
                    if robot_i >= robot_j:
                        continue
                    if t + 1 < len(path_i) and t + 1 < len(path_j):
                        if (path_i[t] == path_j[t+1] and 
                            path_i[t+1] == path_j[t]):
                            return (robot_i, robot_j, path_i[t], t)
        
        return None  # 无冲突
    
    def violates_constraint(self, robot_id, position, time, constraints):
        """检查是否违反约束"""
        for constraint in constraints:
            if constraint.position == position and constraint.time == time:
                return True
        return False

35.4 CBS 的优化变体

纯 CBS 在大规模问题上可能很慢。以下是常用的优化:

优化 思想 效果
ECBS 使用有界次优的低层搜索 速度提升 10-100 倍,解质量有界
CBS + Disjoint Splitting 更智能的分支策略 减少搜索树大小
CBS + Bypass 跳过不必要的分支 减少节点数
CBS + Heuristic 高层搜索使用启发式 更快找到好解
CBS + Prioritized 先用优先级规划,再用 CBS 优化 快速得到初始解

35.5 比赛中 CBS vs SIPP 的选择

场景 推荐 理由
3-5 台机器人,需要最优解 CBS 规模小,CBS 能快速求解
5-10 台机器人,需要快速解 SIPP + 优先级 CBS 可能太慢
10+ 台机器人 ECBS 或 LaCAM 需要次优但快速的算法
动态环境,频繁重规划 SIPP 增量式规划更适合
静态环境,一次性规划 CBS 可以花时间找最优解

三十六、RMF 的实时性保证

36.1 为什么实时性重要?

机器人以 1 m/s 移动
如果调度系统需要 1 秒才能做出决策
→ 机器人在这 1 秒内已经移动了 1 米
→ 如果这 1 米内有冲突点,就来不及避让了

因此:
- 冲突检测必须在 < 100ms 内完成
- 协商必须在 < 500ms 内完成
- 路径规划必须在 < 200ms 内完成

36.2 RMF 如何保证实时性?

策略 1:分层计算

快速层(< 10ms):
  - 检查新轨迹是否与已有轨迹冲突
  - 简单的时间窗检查

中速层(< 100ms):
  - 精确的连续轨迹冲突检测
  - 单台机器人的路径重规划

慢速层(< 1000ms):
  - 多轮协商
  - 全局优化
  - 任务重分配

策略 2:Anytime 算法

Anytime 算法的特点:
- 快速给出一个"可行解"(可能不是最优)
- 如果还有时间,继续优化
- 随时可以被打断,返回当前最好的解

RMF 的协商就是 Anytime 的:
- 第一轮(50ms):找到一个可行方案
- 第二轮(100ms):尝试优化
- 如果超时(500ms):使用当前最好的方案

策略 3:增量式计算

不是每次都从头计算:
- 新机器人加入 → 只检测新轨迹和已有轨迹的冲突
- 机器人偏离计划 → 只更新该机器人的轨迹,重新检测相关冲突
- 新任务到来 → 只为新任务规划,不影响已有计划

策略 4:空间索引

不需要检测所有机器人对:
- 使用空间索引(R-tree / Grid)
- 只检测空间上可能相遇的机器人对
- 大幅减少计算量

例如:
- 1 楼的机器人和 3 楼的机器人不可能冲突
- 东翼的机器人和西翼的机器人短期内不可能冲突

36.3 实时性 vs 最优性的权衡

时间预算:500ms

如果在 500ms 内:
- 找到最优解 → 使用最优解
- 只找到次优解 → 使用次优解
- 连可行解都没找到 → 使用回退策略(优先级方式)

回退策略保证:
- 系统永远不会"卡住"
- 最坏情况下退化为传统的优先级调度
- 但大多数情况下能找到更好的方案

三十七、RMF 的容错与恢复

37.1 机器人系统的故障类型

故障类型 频率 影响 示例
定位漂移 位置误差 10-30cm
导航失败 路径被阻挡,无法到达
通信中断 WiFi 断连
机械故障 电机故障、轮子卡住
电量耗尽 机器人停在路中间
软件崩溃 导航节点崩溃

37.2 RMF 的容错层级

层级 1:容差吸收
  - 安全距离 > 物理最小距离
  - 时间窗有余量
  - 小偏差不触发任何响应
  
  效果:90% 的小问题自动消化

层级 2:轨迹更新
  - 机器人偏离计划超过阈值
  - Fleet Adapter 上报新的预计轨迹
  - Schedule 更新,重新检测冲突
  - 如果有新冲突,触发协商
  
  效果:处理中等偏差

层级 3:任务重分配
  - 机器人无法完成当前任务
  - 任务被标记为 FAILED
  - Task Manager 将任务重新分配给其他机器人
  
  效果:处理单台机器人故障

层级 4:紧急停车
  - 检测到即将发生碰撞
  - 所有相关机器人紧急停车
  - 系统重新规划所有受影响的轨迹
  
  效果:最后的安全保障

层级 5:人工干预
  - 系统无法自动恢复
  - 通知运维人员
  - 提供故障信息和建议操作
  
  效果:处理系统级故障

37.3 故障恢复的具体流程

场景:Robot-A 在走廊中间突然停下(电机故障)

时间线:
t=0s:   Robot-A 停下
t=0.5s: Fleet Adapter 检测到 Robot-A 不再移动
t=1.0s: Fleet Adapter 上报 Robot-A 状态为 ERROR
t=1.5s: Schedule 将 Robot-A 的轨迹标记为"静止在当前位置"
t=2.0s: 冲突检测发现 Robot-B 的轨迹和 Robot-A 的新位置冲突
t=2.5s: 触发协商:Robot-B 需要绕开 Robot-A
t=3.0s: Robot-B 获得新轨迹(绕路)
t=3.5s: Robot-B 开始执行新轨迹
t=5.0s: Task Manager 将 Robot-A 的任务重新分配给 Robot-C
t=10s:  通知运维人员处理 Robot-A

整个过程:
- Robot-B 只延迟了约 3 秒(而不是一直等待)
- Robot-A 的任务在 5 秒内被接手
- 系统整体继续运行

37.4 死锁检测与恢复

死锁是多机器人系统中最棘手的问题之一:

死锁场景:
Robot-A 在位置 1,要去位置 2
Robot-B 在位置 2,要去位置 1
走廊只能单向通行
→ 两者互相等待,谁都走不了

RMF 的死锁处理:

检测:
- 监控所有机器人的等待状态
- 如果一组机器人互相等待超过阈值 → 判定为死锁

恢复:
- 选择代价最小的机器人"后退"
- 或者选择一台机器人"绕路"
- 极端情况:让一台机器人回到起点重新规划

预防:
- 时空规划本身就能避免大多数死锁
- 单向通道设计
- 等待区设计(让机器人有地方"让路")

37.5 通信故障处理

场景:Fleet Adapter 和机器人之间通信中断

处理流程:
1. Fleet Adapter 检测到心跳丢失
2. 等待 3 秒(可能是临时网络波动)
3. 如果恢复 → 正常继续
4. 如果未恢复:
   - 上报 Robot 状态为 UNAVAILABLE
   - Schedule 中该机器人的轨迹标记为"不确定"
   - 其他机器人绕开该机器人最后已知位置
   - 任务标记为 INTERRUPTED
5. 通信恢复后:
   - 获取机器人当前实际位置
   - 更新 Schedule
   - 决定是继续原任务还是重新分配

三十八、RMF 的可扩展性

38.1 水平扩展

当机器人数量增加时,RMF 如何扩展?

10 台机器人:
  - 单节点运行所有 RMF 组件
  - 冲突检测:45 对,毫秒级

50 台机器人:
  - 冲突检测:1225 对,可能需要优化
  - 考虑区域划分

100+ 台机器人:
  - 需要分布式部署
  - 区域独立调度 + 跨区域协调

38.2 区域划分策略

大型建筑可以划分为多个区域:

区域 A(1楼东翼):20 台机器人,独立调度
区域 B(1楼西翼):15 台机器人,独立调度
区域 C(2楼):10 台机器人,独立调度

跨区域:
- 机器人从区域 A 到区域 B 时
- 需要两个区域的调度器协调
- 类似于"跨国旅行需要签证"

这种分层调度的好处:

好处 说明
计算量降低 每个区域只需要处理自己的机器人
故障隔离 一个区域的问题不影响其他区域
独立部署 每个区域可以独立升级
并行计算 多个区域同时计算

38.3 多建筑扩展

RMF 还支持多建筑场景:

场景:一个园区有 3 栋建筑

Building A(医院主楼):30 台机器人
Building B(门诊楼):10 台机器人
Building C(后勤楼):5 台机器人

建筑之间通过连廊连接

每栋建筑有独立的 RMF 实例
建筑之间通过"跨建筑协调器"协调

三十九、RMF 的未来发展方向

39.1 当前版本的局限

局限 说明
学习曲线陡峭 需要掌握 ROS 2 + C++ + 分布式系统
文档不够完善 部分模块缺乏详细文档
部署复杂 需要多个节点协同
仿真到实际的差距 仿真中完美运行不代表实际部署顺利
大规模验证不足 100+ 台机器人的实际部署案例较少

39.2 发展方向

1. 简化部署

目标:一键部署 RMF
方式:
- Docker Compose 一键启动
- Kubernetes 编排
- 云原生架构
- 配置向导(不需要手写 YAML)

2. AI 增强调度

目标:用机器学习优化调度决策
方式:
- 学习历史数据,预测人流模式
- 强化学习优化协商策略
- 预测性维护(预测机器人故障)
- 自适应参数调整

3. 更好的人机共存

目标:在人流密集环境中更好地运行
方式:
- 行人轨迹预测
- 社会力模型集成
- 动态速度调整
- 礼让行为

4. 标准化接口

目标:降低集成成本
方式:
- 完全支持 VDA5050 标准
- 提供标准化的 Fleet Adapter 模板
- 插件化架构(即插即用)

5. 数字孪生

目标:实时的虚拟镜像
方式:
- 3D 可视化整个建筑
- 实时显示所有机器人位置和轨迹
- 模拟"如果"场景(what-if analysis)
- 历史回放和分析

39.3 RMF 在行业中的定位

2020:RMF 发布,概念验证阶段
2021-2022:早期采用者开始试用
2023-2024:在医院、机场等场景落地
2025+:预计更多商业化部署

竞争对手:
- 各机器人厂商的私有调度系统
- InOrbit(云端机器人管理)
- Freedom Robotics(机器人运维平台)
- 各种 MAPF 商业解决方案

RMF 的独特优势:
- 开源(无供应商锁定)
- ROS 2 生态(最大的机器人开发者社区)
- 完整的设施联动(不只是交通调度)
- 异构支持(不限品牌)

四十、总结:RMF 的核心价值

40.1 一句话总结

RMF 是一个"建筑级的机器人操作系统",它让不同品牌、不同类型的机器人能够在同一个建筑中安全、高效地协同工作。

40.2 RMF 解决的核心问题

问题 1:机器人孤岛
  → 解决方案:Fleet Adapter 统一接入

问题 2:交通混乱
  → 解决方案:时空路径规划 + Negotiation

问题 3:设施争抢
  → 解决方案:电梯/门/充电桩的统一调度

问题 4:多楼层运行
  → 解决方案:多地图 + 跨层路径规划

问题 5:动态环境
  → 解决方案:实时重规划 + 容错恢复

40.3 RMF 的技术创新

创新点 传统方式 RMF 方式
冲突解决 优先级 / 先到先得 Negotiation 协商
路径模型 空间路径 时空轨迹
冲突检测 离散点检测 连续轨迹检测
设施管理 独立系统 统一联动
机器人接入 同构假设 异构 Fleet Adapter
调度策略 静态规则 动态协商

40.4 学习 RMF 的路径建议

阶段 1:理解概念(1-2 周)
  - 读本文档
  - 看 RMF 官方视频和演示
  - 理解核心概念:Schedule、Trajectory、Negotiation

阶段 2:运行 Demo(1-2 周)
  - 安装 ROS 2 + RMF
  - 运行 rmf_demos
  - 观察系统行为
  - 修改参数看效果

阶段 3:理解代码(2-4 周)
  - 阅读 rmf_traffic 核心代码
  - 阅读 Fleet Adapter 示例
  - 理解 Negotiation 的实现

阶段 4:自己实现(2-4 周)
  - 写一个简化版的 Schedule + 冲突检测
  - 实现简化版 Negotiation
  - 在自己的项目中应用

阶段 5:实际部署(4-8 周)
  - 为自己的机器人写 Fleet Adapter
  - 配置建筑地图
  - 集成电梯/门
  - 调试和优化

40.5 最后的思考

RMF 代表了机器人系统从"单机智能"到"系统智能"的转变:

单机智能:每台机器人自己很聪明,但互相不协调
系统智能:每台机器人可能不那么聪明,但系统整体很聪明

类比:
- 单机智能 = 每个人都是天才,但没有交通规则
- 系统智能 = 普通人 + 完善的交通系统 = 高效的城市运转

RMF 的哲学是:

不要求每台机器人都是天才,而是提供一个让所有机器人都能高效协作的"基础设施"。

这和现代城市的设计理念一致:

好的城市不是因为每个市民都是天才,而是因为有好的道路、交通信号、公共交通系统。

RMF 就是机器人世界的"城市基础设施"。


全文完


这份文档覆盖了 Open-RMF 的核心概念、技术原理、工程实践和比赛应用。如果你有具体的问题或者想深入某个方面,随时可以继续讨论。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐