3. AMR路网抽象与地图建模

AMR交通管制的第一步不是写调度算法,而是把物理场地抽象成一张“可计算的路网”。同一片仓库地面,用不同抽象方式表达,会直接决定后续能否做死锁检测、能否算最短路径、能否评估拥堵。本节围绕四种常用抽象——栅格图、拓扑图、车道图,以及单向/双向通道、交叉口节点、容量与阻抗属性——说明它们各自适合什么场景、如何建模,以及属性字段应如何定义。

一、四种抽象的分工

栅格图(Grid Map)把场地切成等尺寸方格,每格标记为可通行或障碍。它最贴近激光雷达/视觉的原始感知,适合做局部避障、代价地图和路径平滑。缺点是格数随面积平方增长,且不表达“车道”语义,AMR容易在开阔区走出无意义的锯齿路径。

拓扑图(Topological Map)把可通行区域压缩成“节点+边”。节点通常放在交叉口、装卸位、充电桩、电梯口等关键位置,边表示两节点间可直达的路段。它规模小、搜索快,适合全局路径规划和交通管制决策,但丢失了车道宽度、方向等细节。

车道图(Lane Graph)是拓扑图的细化:每条边对应一条有方向、有宽度的车道中心线,节点对应车道之间的连接点(交叉口)。它既保留拓扑的简洁,又能表达单向、双向、变道、让行等交通规则,是AMR交通管制最常用的核心模型。

工程上常见做法是三者共存:栅格图用于底层运动控制,车道图用于交通管制与路径规划,拓扑图作为车道图的简化视图用于快速查询。

二、单向与双向通道的建模

单向通道用一条有向边表示,方向即允许行驶方向;双向通道用两条方向相反的有向边表示,而不是一条无向边。这样做的原因是:双向通道的两个方向往往具有不同的容量和阻抗(例如一侧有装卸口、一侧靠墙),用两条独立边才能分别赋属性。

窄巷道(宽度仅容一台AMR通过)应建模为单向边,并在交叉口节点上设置“互斥区”,同一时刻只允许一个方向占用。宽巷道可建模为双向边,但若两车对向相遇需要避让,则应在边上标注避让点或会车区。

三、交叉口节点的定义

交叉口是车道图的节点,也是交通管制最关键的资源。一个交叉口节点至少应包含:

  • 节点ID:全局唯一,如 J012。
  • 坐标:世界坐标 (x, y),用于计算距离。
  • 类型:十字、丁字、环岛、装卸位入口、充电位入口等。
  • 容量 capacity:允许同时进入的AMR数量,通常为1。
  • 占用状态:空闲/被占用/预留,供调度器读写。
  • 连接边列表:入边与出边,决定转向关系。

交叉口的容量默认取1,意味着它是一把“互斥锁”。若交叉口面积较大、可容纳两台AMR错身,可把容量设为2,但必须同时定义内部避让规则,否则会出现“都以为能进、结果卡死”的情况。

四、容量与阻抗属性

容量(Capacity)描述一段路或一个节点同时能容纳多少台AMR。边的容量通常按“长度/车长”估算,例如一条12米长的单向车道、AMR车长1.2米,理论容量约10台,但考虑安全间距,实际取6~8台。

阻抗(Impedance / Cost)是路径规划中边的权重,AMR会选择总阻抗最小的路径。阻抗不应只用距离,而应综合:

  • 基础行驶时间 = 路段长度 / 额定速度;
  • 转向代价:直行0,转弯加0.5~1.5秒;
  • 拥堵惩罚:当前排队AMR数量 × 单位等待时间;
  • 风险惩罚:窄道、人车混行区、装卸口附近加权。

下面这张图给出一个典型车道图片段:节点J1~J4为交叉口,边E1~E6为车道,其中E2、E5为单向,E1、E3、E4、E6为双向(用两条有向边表示),并标注了容量与基础阻抗。

车道图片段:节点、单向/双向边、容量与阻抗 J1 cap=1 J2 cap=1 J3 cap=2 J4 cap=1 J5 装卸位 cap=1 停靠 J6 充电位 cap=1 停靠 E1 双向 L=18m cap=6 cost=9.0 E2 单向↓ L=20m cap=4 cost=11.5 E3 双向 L=20m cap=5 cost=10.0 E4 双向 L=18m cap=6 cost=9.0 E5 单向→ L=12m cap=3 cost=7.0 E6 双向 L=12m cap=4 cost=6.5 双向边(两条有向边) 单向边 交叉口节点(cap=容量) 停靠位

五、属性字段的落地定义

为了让调度器、路径规划器和仿真器共用同一份地图,建议用统一的数据结构描述车道图。下面给出一个可直接参考的JSON片段(示例参数,非实测值):

{
  "nodes": [
    {"id":"J1","x":10.0,"y":8.0,"type":"cross","capacity":1,"state":"idle"},
    {"id":"J2","x":28.0,"y":8.0,"type":"cross","capacity":1,"state":"idle"},
    {"id":"J5","x":46.0,"y":8.0,"type":"dock","capacity":1,"state":"idle"}
  ],
  "edges": [
    {"id":"E1","from":"J1","to":"J2","dir":"both","length":18.0,
     "capacity":6,"base_cost":9.0,"speed_limit":1.2,"width":2.4},
    {"id":"E2","from":"J2","to":"J4","dir":"single","length":20.0,
     "capacity":4,"base_cost":11.5,"speed_limit":1.0,"width":1.6},
    {"id":"E5","from":"J2","to":"J5","dir":"single","length":12.0,
     "capacity":3,"base_cost":7.0,"speed_limit":1.0,"width":1.8}
  ]
}

其中 dir 取 single 或 both;base_cost 是不含拥堵的基础阻抗;speed_limit 用于计算行驶时间;width 用于判断能否会车。运行时,调度器在 base_cost 上叠加实时拥堵惩罚,得到动态阻抗。

六、建模时的常见判断

第一,节点粒度:交叉口、装卸位、充电位、电梯口必须建为节点;长直通道中间若无需决策,不必打散成多个节点,否则搜索空间膨胀。第二,单向判定:宽度小于“车宽+安全余量×2”的通道应设为单向;若必须双向,则需在通道两端设置互斥信号。第三,容量取值:节点容量默认1,只有物理上确实能并行通过且规则明确时才取2以上。第四,阻抗标定:基础阻抗可用“长度/速度+转向时间”估算,拥堵惩罚建议用“排队数×平均等待时间”,并定期用实际运行数据回归修正。

建模完成后,建议做一次“可达性自检”:从每个装卸位出发,检查是否存在到达每个充电位的有向路径;若某条单向边导致回程无路,说明方向定义有误,需要补一条反向边或调整节点连接。

把路网抽象、方向、节点容量和阻抗四件事定义清楚,后续的交通管制、死锁预防和拥堵治理才有统一的数据基础。地图不是画得越细越好,而是要让调度器“看得懂、算得动、判得准”。