《城市生命力》P9|UE5 交通编排:动态路由、容量门控与冲突区预约

系列《城市生命力》· P9 · UE5 逻辑 Demo

P5 分析成熟开放世界如何维护车道拓扑、流送节点、异步路径、路口灯组和交通槽。P9 转向 UE5 重建工程,在一张独立验收地图中实现道路图、拥堵改道、出口容量、车辆/行人冲突调度、预约超时和确定性恢复。车辆自身的跟车、制动和碰撞属于 P11;Near/Mid/Far/Hidden 表现分配属于 P12。

上一篇:《城市生命力》P8|UE5 车辆调度:车库派发、策略裁决与路线连续性

对应底层逻辑:《城市生命力》P5|开放世界的交通编排

下一篇:《城市生命力》P10|UE5 行人运动:固定步、过街状态与分层更新

导言:交通系统决定谁能使用下一段空间

车辆沿样条前进,只验证了路线播放。交通编排还要处理四类状态:拥堵后的替代路线、改道期间的车辆连续性、有限出口容量的分配,以及信号切换和预约失效后的冲突区清空。

P9 的验收地图 /Game/Cyber2077/Maps/TrafficOrchestration_Demo 将这些问题组织成一个 32 秒循环:

0–8 秒     FREE FLOW
8–16 秒    CONGESTION REROUTE
16–24 秒   DOWNSTREAM CAPACITY
24–32 秒   RESERVATION RECOVERY

地图内有一条主干路径、一条南侧绕行路径、一个共享出口容量门、一个信号冲突区和一个行人等待区。8 辆东行车与 6 辆西行车持续循环,黄色表示保持主路线,红色表示动态改道。HUD 报告路线成本、改道数量、等待中的预约请求、预约授予、容量阻塞、信号阶段、活动预约、行人组、过期租约和冲突不变量。

Demo 阶段 操作与观察 需要证明的合同
Free Flow NETWORK 视角建立道路与双向车流 车道图和车队状态是独立运行时数据
Congestion Reroute 提高主路线拥堵代价,部分车辆走南侧绕行 只替换未来车道,保持当前车道、位置和速度
Downstream Capacity 三个预约请求竞争同一次批处理中的一个出口信用 本轮最多一份请求获得 Granted,其余请求继续排队
Reservation Recovery 行人组等待一个由孤儿车辆持有的冲突区 预约租约过期后,行人按真实信号、类型内排序与等待保护接管
Save / Restore 保存容量或恢复阶段,继续后再恢复 车队、路由、队列、信号、预约和阶段可继续重演

Demo 提供三组固定观察位。CAM: NETWORK 同时看到西侧车库、主干路、南侧绕行和信号连接,用于确认改道前后车辆没有瞬移;CAM: BOTTLENECK 对准共享出口,适合读取三个请求与一个信用之间的关系;CAM: SIGNAL 对准冲突区和行人等待区,用于观察车辆预约、AllStop、租约过期和行人接管。相机只改变观察位置,不写入运行时状态。

HUD 按模块显示运行时证据。路线区包含主路参考成本、选中路线成本、未来车道数和当前车道保留状态;容量区包含 Queued、Granted 与 Capacity Blocked,其中 Granted 表示预约授予,不代表车辆已经通过出口;信号区包含 Phase、Elapsed、Pedestrian Demand 和当前所有者;SOAK 区累计周期、改道、预约授予、过期租约和冲突违规。截图记录离散状态,32 秒录像用于核对状态转移顺序。

P9 交通编排 Demo 的主路、南侧绕行、瓶颈与信号区总览
Demo 实机 1|Free Flow 阶段共有 14 辆车,东向 8 辆、西向 6 辆,尚未发生改道。NETWORK 视角同时保留主路、南侧绕行、出口容量门和冲突区。

一、从原工程到 UE5:路网是持续运行的数据,不是场景装饰

图 1:从原工程职责、运行时合同到 UE5 Demo 模块的对应关系
图 1:从原工程职责、运行时合同到 UE5 Demo 模块的对应关系
原工程先将道路、样条与交叉口离线编译为持久车道数据,运行时再随着区域流入创建车道、碰撞表示和路口对象。持久拓扑可以在局部区域尚未激活时服务全局查询;流送表示则受节点注册、使用计数、槽位占用和安全卸载约束。车辆 AI、玩家导航与行人使用同一空间基底,但采用不同搜索规则。

原工程的交通全局路线采用带令牌、容量、取消和跨帧续算的异步协议。路径请求受理不等于已经找到路线,部分路径也不等于最终成功。信号灯并非直接改写所有车辆速度,而是形成可被局部运动层读取的停止约束;冲突预测、静态/动态占用和交通槽随后决定车辆如何接近路口。

P9 没有复刻离线编译、流送节点和跨帧 A*。它保留五项直接影响连续运行的语义:

  1. 车道由稳定 ID、拓扑端点、长度、限速和容量定义;
  2. 路由成本可以叠加距离、占用、拥堵和关闭状态;
  3. 改道只能替换尚未行驶的未来车道;
  4. 信号许可与冲突区实际占用是不同条件;
  5. 预约必须能够排队、释放、超时和恢复。

当前实现采用项目自有运行时,ZoneGraph、Mass Entity 和 MassTraffic 均不作为权威交通数据源。地图 Actor 负责配置和展示;道路图、车队、动态路由、容量预约和路口调度存放在可脱离场景运行的 C++ 数据结构中。

Demo 的状态所有权

图 2:Demo9 路网、主路与南侧绕行路,以及三台固定观察相机
图 2:Demo9 路网、主路与南侧绕行路,以及三台固定观察相机
`ACyberTrafficOrchestrationDemoActor` 持有场景周期、固定步累加器和检查点,同时组合五组运行时状态:
  • RoadGraph:主路线、绕行路线与对向车道;
  • Fleet / OpposingFleet:东行和西行车辆的路线与运动状态;
  • Reroute:逐车阻塞帧、上次改道帧和统计;
  • CapacityReservations:下游容量竞争使用的等待队列、等待分数与入口授予计数;
  • Scheduling:信号、车辆预约、行人组、租约与冲突区占用。

HUD 和材质不参与状态推导。按钮将权威固定步推进到目标阶段;红车、信号颜色和冲突区高亮仅用于显示运行时结果。

从源码语义到原型模块的对应关系

原版系统将道路数据、全局寻路、交通槽、路口控制器、信号灯和车辆局部运动分配给不同模块。P9 沿用职责分离原则,并以可独立测试的合同重新组合这些模块:

原版可观察语义 P9 中的对应模块 当前保留内容 当前省略内容
持久道路拓扑 Road Graph Runtime 稳定车道 ID、节点连接、方向、长度、限速、容量 离线烘焙、区域流送、道路版本迁移
全局路线查询 Dynamic Routing Runtime 加权代价、关闭拒绝、路径重建、失败原因 跨帧令牌、分层图、缓存与取消协议
交通槽推进 Fleet Runtime 车辆身份、当前车道、alpha、速度、未来路线 原版槽布局、完整车道变换与局部冲突走廊
下游通行资格 Reservation Runtime 请求、WaitFrames 评分、入口授予惩罚、单批出口信用、释放 实时车道占用率、车辆长度与清空时间预测
路口所有权 Intersection Scheduling 信号、全停、车辆租约、行人组、冲突区互斥 多路口网络协调、复杂相位与网络死锁检测

该映射不包含省略模块的能力。红色车辆成功绕行,只能证明当前小型路网中的路线交接成立;它不能证明 World Partition 道路节点流送或跨区路径令牌的取消机制已经实现。

原工程在不同层级分别实现了这些职责。离线交通编译器生成道路、样条、交叉口和持久连接;运行时交通系统处理节点注册、反注册、使用计数、碰撞表示和路口对象。全局交通路径以令牌进入有限队列,可以取消并跨帧续算;局部车辆运动读取灯光形成的停止约束、车道槽和预测冲突。P9 没有把这些模块压缩成一个“原版交通算法”,只抽取它们之间可验证的边界:拓扑先于车辆、路线结果先验证后提交、灯相只提供许可、实际占用必须独立释放。

原工程的持久路网、流送、异步路径和交通槽来自 P5 已完成的源码阅读。P9 使用从这些职责中提取的运行时合同;文中的 RoadGraph、Routing、Reservation 与 Scheduling 类型均为项目自有实现,不对应原工程中的同名类。

UE5 原型据此拆分:RoadGraph 不引用场景道路 Mesh;Fleet 保存 VehicleId 和路线运动状态,不以 Actor 指针表示身份;CapacityReservations 处理路径预约和信用;Scheduling 处理信号、冲突区与租约。ACyberTrafficOrchestrationDemoActor 只承担验收夹具与状态组合职责。生产实现可以替换地图构造、异步搜索或可视化层,同时保持这些运行时合同。

一次固定步中的先后关系

图 3:Demo9 的 32 秒四阶段确定性循环
图 3:Demo9 的 32 秒四阶段确定性循环
交通模块组合运行时,调用顺序本身就是状态合同。P9 的固定步采用以下顺序:
图 4:渲染帧时间进入累加器后,按 0.1 秒固定步推进交通状态
图 4:渲染帧时间进入累加器后,按 0.1 秒固定步推进交通状态
阶段首次初始化与持续推进必须分开。拥堵代价只在进入 Reroute 阶段时写入一次;车辆阻塞帧、冷却和路线运动仍在后续步骤中连续变化。容量请求也只创建一次,随后由等待分数、入口授予计数和本批信用决定何时获准。若每帧重新创建请求,请求帧与等待帧会被反复重置,容量请求内部的排序和快照恢复都会失去意义。

场景 Actor 还保存 bRerouteTriggered、bCapacityScenarioStarted 和 bRecoveryScenarioStarted。这些阶段标记属于场景协议:恢复检查点时,系统据此判断请求是否已经建立,避免仅依据 elapsed time 重复执行初始化副作用。

代码中的实际调用链比 HUD 显示更具体。Tick 只负责把渲染帧时间积累到 FixedStepAccumulator;每取得一个 0.1 秒步长,才调用 AdvanceTrafficOrchestrationDemoFixedStep。该函数先根据循环时间解析 Phase,再按一次性标记调用拥堵改道、容量初始化或恢复初始化。进入恢复阶段后,FCyberTrafficIntersectionSchedulingRuntime::Advance 负责租约倒计时、灯相转换和等待请求仲裁;之后才推进两支 Fleet,回收抵达终点且入口安全的车辆,并把状态汇总给 HUD。图中将这条调用链压缩为“阶段—调度—车队”三段,箭头表示同一逻辑步内的先后关系,不表示三个并行线程。

当前车队始终以 Open Gate 推进,容量与信号结果尚未接入可见车辆的制动控制。容量队列和冲突预约提供独立的调度证据;车辆运动执行留给 P11。P9 验证门控结果的计算、保存和诊断,不覆盖红灯或容量不足驱动的车身停车。


二、动态路由:代价变化只改写车辆的未来

道路图提供两条可竞争路径

Demo 的东行入口从 start 到 junction。主路线随后经过 short.a 与 short.b 到达终点;绕行路线从同一路口进入一组离散车道段,沿南侧曲线回到终点。东行车道拥有稳定 ID、From/To 节点、世界端点、12 m/s 限速和容量 6。另有一条限速 11 m/s、容量 8 的西行车道用于形成对向流。

初始主路线成本只计算 short.a + short.b 的二维长度。进入拥堵阶段后,系统为这两段各写入 1.0 的拥堵值;调试状态同时保留一个增加 500 的主路线参考成本,用于对比拥堵前后的数值。权威选路仍由动态路由器逐段计算,不直接使用 HUD 参考数。

路由结果由三类软代价与一类硬关闭共同决定

图 5:距离、占用、拥堵三类软代价与硬关闭过滤
图 5:距离、占用、拥堵三类软代价与硬关闭过滤
`FCyberTrafficDynamicRoutingRuntime::FindRoute` 从 Source Node 开始,在道路图上进行确定性最短代价搜索。当前 `LaneCost` 只包含距离、逻辑占用和外部拥堵三类软代价:
LaneCost = DistanceMeters × DistanceWeight
         + OccupancyCount × OccupancyWeight
         + Congestion × CongestionPenaltyScale

默认权重为距离 1、每个占用 40、拥堵倍率 250。硬关闭在候选展开阶段直接拒绝,不参与代价相加;结果单独记录评估车道数、受惩罚车道数和关闭拒绝数。查询还包含 MaxLaneSegmentCount,重建路径超过上限时返回失败,避免异常拓扑产生无限回溯。

策略结构中存在 IncidentPenalty,但当前 FindRoute 对关闭车道采用直接拒绝,没有使用该字段为事故车道增加有限代价。可验证输入只有距离、占用、拥堵与硬关闭;事故软惩罚尚未实现。

这些输入的证据范围并不相同。32 秒主循环在进入 Congestion Reroute 时,只向 short.a 和 short.b 写入 1.0 拥堵值,并遍历东行车队,直到成功改道 4 辆或全部 8 辆都已检查。占用计数由 BuildContext 从 Lane Occupancy 条目聚合,关闭集合由调用者传入;这两条路径主要由 TrafficDynamicRouting 自动测试覆盖。主录像能证明拥堵改道和连续交接,不能单独证明占用权重与关闭拒绝都在该画面中发生过。

FindRoute 的距离以世界端点二维距离除以 100,转换为米;所有可调权重在使用前都会钳制到非负范围,距离权重最低为 0.001。双向车道允许从 To Node 反向到 From Node,普通有向车道只沿 From→To 展开。每次松弛都会增加 EvaluatedLaneCount,遇到关闭车道增加 ClosedLaneRejectedCount,使用占用或拥堵输入时增加 PenalizedLaneCount。这些计数属于搜索过程证据,不是车流统计。

触发改道与找到新路是两个阶段

图 6:拥堵、关闭与下游阻塞触发改道请求,冷却到期后再执行路由事务
图 6:拥堵、关闭与下游阻塞触发改道请求,冷却到期后再执行路由事务
逐车改道状态记录连续阻塞帧与上次改道帧。未来路线出现关闭车道、拥堵达到 0.75,或连续阻塞达到默认 3 帧时,才形成改道触发。触发后还要检查最小间隔;默认 30 帧,Demo 覆盖为 20 帧。冷却中的请求只增加抑制统计,不修改路线。

通过触发门后,查询从“当前车道的出口节点”开始,而不是从车辆世界坐标重新投影。若新结果与原未来车道完全相同,返回 reroute_unchanged;找不到路线、目标无效、超出车道上限或拓扑不连续也都有独立失败原因。

当前车道、运动状态和身份必须保留

图 7:改道仅替换未来路线,当前车道与 VehicleId 保持连续
图 7:改道仅替换未来路线,当前车道与 VehicleId 保持连续
P9 Demo 中四辆车采用南侧动态绕行并保留当前车道
Demo 实机 2|主路拥堵代价从 160.0 上升到 660.0 后,4 辆车选择成本 181.8 的南侧绕行;HUD 同时报告 current lane PRESERVED。

ApplyRoutePreservingCurrentLane 先复制车辆当前车道,再按新路线 ID 逐段检查连接关系。新路径的 Source Node 必须等于当前车道的 To Node,每一段 From Node 必须接上前一段 To Node,最终节点必须抵达查询目标。全部成立后才一次替换 RouteLanes。

替换时,当前车道成为新数组第 0 项,CurrentLaneIndex 重置为 0;车辆的 LaneAlpha、位置、速度和 VehicleId 保持不变。车辆完成当前车道后进入新路线,不会瞬移到绕行路线起点。

Demo 在拥堵阶段遍历东行车队,直到成功改道数量达到 4,或所有 8 辆车都已检查。状态记录 Selected Route Cost、未来车道数和 bCurrentLanePreserved,红色只标记已经应用新路线的车辆。路线连续性属于运行时结果,不以颜色变化作为通过依据。

搜索范围:同步小图。

动态路由器在一次函数调用中遍历当前小型道路图,没有跨帧时间预算、优先队列或大图分区。它适合验证权重与路线交接,不代表生产城市可以每帧为大量车辆同步全图搜索。扩展时需要分层图、缓存、查询队列和时间片,并继续保留现有的触发冷却与原子路线替换。

最短路搜索的诊断信息。

当前实现使用一个面向小图的确定性最短路扫描。查询状态包含各节点当前最小代价、前驱车道和待处理节点集合。每次选择累计代价最低的节点,枚举其出车道,再把满足条件的新代价写回目标节点。到达目标后,沿前驱车道逆向重建并反转,得到从 Source 到 Target 的有序车道 ID。

该实现没有使用优先队列,复杂度不适合大规模道路图;测试可以稳定复现哪些车道被评估、被惩罚或因关闭被拒绝。查询结果除成功/失败外,还保存:

  • 是否找到完整路线;
  • 总代价与路线包含的车道段;
  • 已评估车道数;
  • 使用占用或拥堵惩罚的车道数;
  • 因关闭被跳过的车道数;
  • 失败原因和重建是否超过上限。

这些诊断字段用于将路线异常定位到具体输入。无路线结果可能来自目标不可达、入口无出边、全部候选关闭或前驱重建超过最大段数。仅返回空数组时,Demo 只会显示车辆停止,无法区分数据错误与策略选择。

路线替换先验证、后提交。

新路线不应在验证过程中写入车辆状态。若前三段连接正确、第四段指向错误节点,逐段写入会形成部分更新路径。P9 先在临时数组中完成以下检查:

  1. 当前车道存在,并可作为新路线第 0 段;
  2. 查询结果的起点等于当前车道出口;
  3. 每一段的 From Node 等于上一段 To Node;
  4. 所有车道 ID 都能在 Road Graph 中解析;
  5. 最后一段确实到达目标节点;
  6. 路线段数没有超过运行时上限。

全部检查通过后,完整临时数组一次替换未来路线。异步化后仍需保留该原子提交边界:跨帧搜索可以逐步产生候选,权威路线只在完整结果通过验证后切换。

占用、拥堵和关闭不是同一种惩罚

当前公式把 Occupancy 与 Congestion 都转成可比较代价,但两者来源不同。Occupancy 是某段当前包含多少逻辑车辆;Congestion 是 0–1 的外部拥堵尺度,可以来自速度下降、事故影响或区域策略;Closed 则表示道路不能作为合法候选。

软代价允许查询在“绕行太远”与“主路太挤”之间权衡;硬关闭不参与权衡。若事故只封闭部分车道,生产系统必须明确采用整段关闭、容量降低,还是可恢复惩罚。P9 当前使用硬关闭,IncidentPenalty 尚未参与公式,三类语义保持分离。


三、容量门控:绿灯不代表出口一定有空间

图 8:三个请求在单次批处理中竞争一个容量信用
图 8:三个请求在单次批处理中竞争一个容量信用
P9 Demo 中三个下游请求竞争一个容量信用
Demo 实机 3|Downstream Capacity 阶段形成 3 个请求:1 个获得预约授予,2 个保持排队并记为容量阻塞。Granted 不计作物理通行。

交通信号允许某股移动进入路口,只表示方向许可。若下游车道已经满载,继续授予进入资格可能使车辆停在冲突区内。P9 用独立的 FCyberTrafficIntersectionReservationRuntime 验证单次批处理中的下游容量门控。

进入容量阶段时,Demo 构造三个请求,分别竞争三个冲突区,但它们的 Exit Lane 都指向 lane.article3.shared.exit。请求在关闭 Gate 下进入容量请求队列,保存 VehicleId、入口、出口、请求帧和等待帧。随后一次批处理把 Gate 打开,本轮最多授予 3 个请求,却只提供一个出口容量信用:

ExitLaneCapacity[shared.exit] = 1

结果只能授予 1 份预约,其余 2 份请求保持等待并累计 CapacityBlocked。容量按出口消耗,而不是按冲突区分别计算,因此三条互不相同的入口不会在同一批处理中绕过共享下游瓶颈。Granted 只表示预约获得调度资格,不表示车辆已经驶入或通过出口。

三个计数分别表示不同状态:Queued 是容量请求等待队列中的记录;Granted 是已经获得完整路径预约的请求;CapacityBlocked 是本次处理时因出口信用不足而未获准的次数。CapacityBlocked 按事件累计,不等于当前队列长度。

全文按以下术语区分调度资格与实际占用:

运行时术语 本文含义
Queued 等待中的预约请求
Granted 已授予预约,不表示已经通过路口
Active Reservation 活动预约,即未来冲突空间所有权
Crossing Agent 实际进入或仍占据冲突区的对象
Released 预约释放
Cleared 实际冲突区清空

当前 Demo 把容量表示为整数信用,不模拟流量率、排队长度、车辆尺寸或下游清空时间。它验证了预约授予前读取本批下游预算的接口,实际容量模型仍需结合车道占用和车辆包络。

请求从排队到释放的生命周期。

容量预约运行时将请求分成待处理队列、已获准预约和出口信用占用。新请求先验证车辆、入口、出口与冲突区 ID,然后用单调序号进入队列。批处理时按序号稳定选择候选,再依次检查 Gate、冲突区和 Exit Lane 信用。

获准后,请求从等待队列移入 Conflict Reservation,保留 VehicleId、入口、出口、冲突区和剩余租期。当前出口信用只在一次 ProcessFairQueueBatch 调用内通过 GrantedCountByExitLane 消耗,因此可以保证本批三个请求只授予一份预约。Reservation 会持续占住冲突区,但现有实现不会在下一批开始时自动用活动 Reservation 扣减出口预算。

P9 当前验证的是单次批处理中的共享出口信用,不包含跨固定步维护的下游在途信用。生产实现需要依据活动预约、实际占用和已进入但尚未清空的车辆重算每步预算,或将出口信用建模为有生命周期的令牌。否则,连续两次批处理都传入 shared.exit = 1 时,第二次处理可能在首辆车尚未清空时再次授予预约。

若请求因为容量不足未获准,它继续保留在原数组位置,WaitFrames 在下一次批处理时继续增加。当前选择分数为:

Score = WaitFrames × 100 - DirectionGrantCount(EntryLane)

等待越久,分数越高;同一入口此前获得的预约授予越多,分数越低。分数相同时由稳定数组遍历顺序决定。它不是独立的全局序号协议,但能避免失败请求被删除后重新排到队尾,也能对连续获准的入口施加轻量惩罚。

容量预算优先查询 Exit Lane;没有出口预算时才回退到 Entry Lane。多个入口汇入同一出口时由此共享一个信用池。入口和出口同时配置预算时,以出口预算为准,不叠加两者扩大吞吐。

生产系统中的容量信用来源。

当前夹具直接提供 shared.exit = 1。进入生产级城市运行时后,这个数字可以由多种状态派生:

图 9:生产系统从静态容量、实际占用、预约承诺与安全预留推导本步信用
图 9:生产系统从静态容量、实际占用、预约承诺与安全预留推导本步信用
容量不能仅按车道车辆数计算。车型长度、出口至下一停止线的可用距离均会影响容量;已经获得预约但尚未进入的车辆也需要预留空间。P9 将容量设为独立输入,使后续估算方法可以在不改写请求排序和预约生命周期的情况下替换。

四、冲突区:信号阶段和实际占用共同决定通行

车辆与行人共享冲突区,但当前仲裁仍分两轮执行

FCyberTrafficIntersectionSchedulingRuntime 维护信号、待处理车辆、待处理行人组、活动车辆预约、活动行人组与实际 Crossing Agents。车辆请求和行人组请求共用单调递增的 NextRequestSequence,因此两类记录拥有同一时间基准;当前 ProcessFairRequests 并未把它们合并成一条全局优先队列。实现先按等待时间和 Sequence 排序行人请求,再用同样规则处理车辆请求。

行人组请求会验证 GroupId、SignalId、ConflictZoneId 和 AgentId 唯一性,随后增加信号的 Pedestrian Demand。车辆请求保存 LaneId,用于具有主/次方向窗口的路口判断。重复请求不会生成第二份所有权:已活动的请求直接返回 Granted,已排队的请求返回 Queued 和累计等待时间。

信号切换包含全停清空期

图 10:车辆与行人相位之间必须经过 AllStop 清空窗口
图 10:车辆与行人相位之间必须经过 AllStop 清空窗口
P9 Demo 的 AllStop 清空窗口与互斥状态
Demo 实机 4|行人需求到达后,信号先进入 AllStop;此时行人组仍在等待,冲突区所有权保持互斥,清空转换计数增加。

无扩展移动窗口时,信号从 VehicleGreen 开始。存在行人需求、车辆绿灯持续时间已满足且没有该信号下的活动车辆时,才进入 AllStop 或 PedestrianCrossing。若配置了 Clearance Duration,系统先进入 AllStop,时间结束后才开放行人。

行人阶段到期时,如果仍有 Crossing Agent,阶段时间被钳在上限并累计扩展次数;只有冲突区真正清空后,信号才通过下一次 AllStop 回到 VehicleGreen。许可时间结束不会强制清除仍在路口中的对象。

紧急 All Stop 是单独状态。请求会取当前剩余时间和新时长的最大值,并清空车辆预约、车道和租约映射;期间车辆与行人都不能新进入。它用于恢复保护,不是常规灯色之一。

冲突区所有权必须互斥

车辆取得预约前必须满足:信号为 VehicleGreen、冲突区没有活动 Crossing Agent。行人组被激活前则要求行人阶段允许、同区没有车辆预约。SetCrossingAgent 在存在活动车辆时拒绝写入;车辆预约也在存在行人时失败。

Demo 每步检查:

ActiveVehicleReservations 为空
    或 ActivePedestrianGroups 为空

若两者同时非空,ConflictInvariantViolationCount 增加。该汇总针对当前单一验收冲突场景;通用多路口系统应按 ConflictZoneId 逐区检查,而不能把全城任何车辆预约与任何行人组视为冲突。

孤儿预约通过租约恢复

图 11:孤儿车辆预约过期后,等待中的行人组恢复通行
图 11:孤儿车辆预约过期后,等待中的行人组恢复通行
P9 Demo 中孤儿车辆预约过期后行人组恢复通行
Demo 实机 5|孤儿车辆预约过期后的下一固定步,信号进入 Pedestrian,相同行人组由 pending 转为 active;pedestrian resumed YES 且互斥检查仍为 YES。

恢复阶段创建一个车辆预约 vehicle.article3.orphan,租约设为 1 秒,但不再提交 progress heartbeat。同时,一个行人组请求同一冲突区并进入等待。

Advance 每步减少活动车辆剩余租约。VehicleId 只有调用 ReportVehicleProgress 才会把租约刷新为配置时长。孤儿车辆没有心跳,租约归零后从所有冲突区、信号、车道和 Lease Map 中移除,ExpiredVehicleReservationCount 增加。之后信号进入行人许可窗口,行人类型内排序与等待保护将合法的等待组转为活动,HUD 标记 Pedestrian Recovered。

超时不是对所有慢车的强制清除。生产系统必须按网络延迟、车辆速度和路口大小选择租约,并允许有效车辆持续心跳。P9 只验证“失去所有者的预约不会永久锁死路口”。

从绿灯到实际进入之间有四层条件

图 12:信号许可、预约所有权、实际占用与 P11 运动执行的边界
图 12:信号许可、预约所有权、实际占用与 P11 运动执行的边界
P9 将一次车辆进入路口拆成四层判断:
条件 判定内容 非本层职责
信号阶段 当前方向是否处于许可窗口 不保证冲突区已经清空
冲突区所有权 是否已有车辆或行人占用共享空间 不保证出口有容纳空间
出口容量 下游能否接纳本次移动 不决定车辆怎样制动
车辆局部运动 车辆能否按车距和停止线执行许可 不改写信号与预约

车辆进入路口依次受信号许可、冲突区所有权、出口容量和局部运动条件约束。P9 实现前三层调度状态,并保留独立车队推进作为消费接口;这些结果尚未驱动可见车辆在停止线前制动或等待,车身执行属于 P11。本 Demo 验证四层职责边界和调度结果计算,不构成端到端车身通行验证。各层应保留独立等待原因;合并为单一 CanGo 布尔值后,将无法区分红灯、行人、前车和下游空间阻塞。

预约所有权与 Crossing Agent 不是同一个状态

预约表示“对象获得进入资格并占住未来冲突空间”;Crossing Agent 表示对象已经进入或仍实际占据该空间。二者会在一段时间内重叠,但生命周期边界不同。车辆可以刚获得预约尚未进入,也可以已进入后通过进度心跳继续延长租约;行人组则需要全部成员完成清空,才能结束实际占用。

信号状态因此不能只看预约数量。车辆预约已经释放但某个 Crossing Agent 仍在区内时,行人阶段不能立即接管。相反,一个预约持有者从未进入、也不再上报进度时,租约必须能够将其清除。P9 的孤儿夹具专门覆盖后一种失败。

共享序号不等于严格的跨类型全局队列

图 13:车辆与行人共享 Sequence,但当前仲裁按类型分两轮执行
图 13:车辆与行人共享 Sequence,但当前仲裁按类型分两轮执行
车辆和行人组进入系统时都会从 `NextRequestSequence` 取得编号,但这个编号目前只在各自类型内部用于等待时间相同的次级排序。两类请求的先后关系主要由信号窗口、实际占用和 `ProcessFairRequests` 的两轮处理顺序决定,不能据此将当前实现描述成严格的跨类型 FIFO。

暂不满足许可条件的请求不会被删除。行人尚未获得过街窗口时继续留在 PendingPedestrianGroups;车辆受灯相、行人占用或不兼容车道阻塞时继续留在 PendingVehicles。等待超过 MaximumFairWaitSeconds 的行人会触发 StarvationPriorityCount,同一冲突区的新车辆暂缓获准。Pedestrian Demand 同时推动信号在满足车辆绿灯时长和清空条件后进入 AllStop,再切入行人窗口。

该组合覆盖行人等待保护和互斥所有权,但不属于通用全局公平调度器。若生产目标要求车辆、单个行人和行人组接受统一优先级比较,应将两张 Pending Map 投影为同一候选集合,在一次仲裁中比较等待时间、Sequence、紧急等级和相位切换成本。共享 Sequence 可作为统一排序的稳定依据。


五、固定步、场景控制与检查点恢复

Demo 使用 0.1 秒固定步。每步先推进 32 秒循环并判断阶段,再执行首次进入阶段的改道、容量或恢复初始化;恢复阶段继续推进信号与租约;随后刷新冲突统计并推进东、西两支车队。到达路线末端的车辆只有在入口包络空间空闲时才能回到起点。

NEXT PHASE 和五个场景按钮不直接写入 HUD 状态。它们暂时解除暂停,通过真实固定步运行到 8.1、16.1、24.1 或 25.2 秒,然后恢复原暂停状态。若当前时间已经超过目标,场景会先重置再前进。按钮缩短了验收等待,但仍执行同一运行时链。

暂停只冻结权威固定步

PAUSE 使 AdvanceTrafficOrchestrationDemoFixedStep 直接返回,FrameIndex、ElapsedSeconds、车队、队列和租约均不变化。摄像机与 HUD 仍可工作。自动化在第 69 步暂停并保存,确认暂停期间状态保持,再恢复运行进入改道阶段。

快照覆盖整个调度现场

P9 Demo 在 Downstream Capacity 阶段保存固定步 171
Demo 实机 6|在 Downstream Capacity 阶段暂停并保存检查点:时间 17.1 秒、固定步 171、4 辆已改道、容量状态为 2/1/2。
P9 Demo 恢复到 Downstream Capacity 阶段的固定步 171
Demo 实机 7|推进到恢复阶段后执行 Restore,Phase、Time、Step、路线、容量、信号和冲突状态均回到保存点,随后仍可继续固定步推进。

检查点包含道路图、两支车队、逐车改道状态、容量预约状态、路口调度快照、Demo 汇总状态和三个阶段初始化标记。当前恢复前的交叉验证集中在版本为 1 的 Scheduling 快照内部,会检查:

  • 活动车辆预约的 ConflictZone Key 与 VehicleId 非空,且 VehicleId 具有 Lane、有效 Signal 和 Lease 映射;
  • 待处理车辆的 Map Key 与 VehicleId 一致;
  • 活动行人组的全部 AgentId 已写入相同冲突区;
  • 待处理行人组具有有效信号、冲突区和成员。

验证通过后才把调度状态写入 Demo,并清零固定步累加器、重建表现。RoadGraph、Fleet、Reroute、Capacity 和 Demo 汇总仍按同一会话快照直接复制,尚未建立覆盖整个 P9 状态的 schema、资源版本和跨模块引用预检。与 P8 一样,未满一个固定步的渲染时间相位不会保存;它保证恢复从固定步边界继续,不提供逐帧回放级时间精度。

RESET 会重建自由流场景并清空 Completed Cycle、累计改道、容量预约授予、过期租约和冲突违规等 SOAK 计数,但保留 Runtime Checkpoint 以及 capture/restore 次数。重置运行状态与删除检查点是两项独立操作。

恢复前验证内部引用。

调度快照由多张以 ID 为键的表组成。单独看每张表都可能合法,组合起来仍可能出现悬空引用:活动预约引用不存在的信号;Lease Map 有车辆但 Reservation Map 没有;活动行人组声明三个成员,Crossing Agent 表只登记两个;待处理 Map 的键与记录内 VehicleId 不一致。

P9 在应用快照前验证 Scheduling 内部关系,拒绝不完整的调度子状态。验证通过后整体替换 Scheduling,并重建场景表示。该顺序避免逐字段写入形成部分恢复状态;整个 P9 快照的跨模块一致性验证尚未实现。

当前道路图、车队和 Demo 汇总仍直接复制进会话快照,没有磁盘版本迁移。若未来写入 SaveGame,至少还要补:数据版本、Road Graph 资源版本、失效车道 ID 的重映射、已删除信号与冲突区的回退规则,以及旧版本容量预约的清理策略。

可复现按钮也是验收工具

场景按钮将人工走查映射到确定的逻辑时刻,不直接写入显示状态。Capacity 按钮运行到 16.1 秒,确保阶段初始化已经完成;Recovery 按钮运行到 25.2 秒,使租约完成过期并恢复行人组。

确定性跳转便于复测:代码修改前后可以在相同时刻比较 HUD、请求序号和车辆状态。自动化与人工录像调用同一固定步,不维护独立的测试结果生成路径。


六、验收与能力边界

每个阶段都要守住同一组不变量

四个场景阶段覆盖的调度条件不同,但验收不应只检查该阶段最显眼的数字。P9 在整个 32 秒循环中持续维护以下不变量:

不变量 典型破坏方式 诊断证据
VehicleId 唯一 重置或改道时重复生成车辆 两支 Fleet 的身份集合
当前车道连续 新路线直接覆盖当前车道 bCurrentLanePreserved、位置与 alpha
路线拓扑连续 新段 From/To 接不上 Route Apply 失败原因
单次批处理中的出口信用不超发 不同冲突区在同一批处理中分别消费同一出口 Granted 与 CapacityBlocked
冲突区所有权互斥 车辆预约与行人组同时活动 Conflict Invariant Violation
租约可回收 孤儿预约永久占区 Expired Reservation Count
暂停不推进 UI 暂停但租约仍倒计时 Frame、Elapsed 与 Lease Remaining
恢复不重复副作用 阶段初始化再次建请求 初始化标记、请求数量、累计计数

例如在 Free Flow 中,Capacity 与 Pedestrian 计数都可能为零,但路线连续、身份唯一和暂停语义仍然有效;在 Recovery 阶段,租约过期成功也不能掩盖冲突区曾经同时出现两类所有者。Soak 测试用于验证这些不变量能否跨阶段、跨循环保持成立。

HUD 只读取运行时状态。

HUD 不保存自己的业务计数。Selected Route Cost 来自最后一次路径查询;Queued/Granted/Blocked 来自容量预约状态;Signal Phase、Active Reservation 与 Pedestrian Group 来自 Scheduling;车队数量、当前路线和改道标记来自 Fleet/Reroute。

该只读关系允许自动化在没有 HUD 的情况下检查同一状态,也允许更换 UI 而不改变运行时。若 HUD 自行累计“已改道车辆”,Reset、Restore 和循环边界可能与权威状态分叉。

失败分支也应进入人工走查

主视频记录成功改道、单批单信用预约授予和租约恢复。工程验收也包含失败分支:关闭主路与绕行路并确认返回不可达;提供断裂车道并确认路线应用被拒绝;将出口信用设为零并确认请求保留原数组位置和 WaitFrames;重复提交同一 VehicleId 并确认不会生成第二份预约;构造无效 Scheduling 快照并确认 RestoreSnapshot 在写入前失败。

这些用例不必全部进入文章主画面,但应纳入工程验收。失败分支用于确认无效输入不会形成部分写入状态,补足主视频对成功路径的验证。

人工走查

  1. 用 CAM: NETWORK 建立车库、主干、南侧绕行、出口门与信号区;
  2. 观察 Free Flow,确认 14 辆车持续循环;
  3. 点击 CREATE CONGESTION 或 PENALIZE PRIMARY,确认红车保持当前车道后才进入绕行;
  4. 切到 CAM: BOTTLENECK,点击 LIMIT EXIT,核对 3 个请求中仅 1 个获得 Granted;该状态表示预约授予,不表示车辆已经通过出口;
  5. 切到 CAM: SIGNAL,依次触发 PEDESTRIAN 与 STALE LEASE,观察孤儿预约过期和行人组接管;
  6. 在容量阶段 SAVE STATE,运行到恢复阶段,再 RESTORE,确认同一转换再次出现;
  7. 连续运行多个周期,SOAK 中的 conflict violations 必须保持 0。
演示视频|50 秒交通编排走查,依次覆盖双向自由流、拥堵改道、单批容量竞争、AllStop 清空、孤儿预约恢复和 Checkpoint Restore。视频记录调度状态转移;预约授予不计作车辆实际通行。

自动化证据

图 14:Demo 检查点范围与四周期 Soak 计数
图 14:Demo 检查点范围与四周期 Soak 计数
P9 Demo 完成四周期 Soak 并保持冲突不变量
Demo 实机 8|1281 个固定步完成四个循环:累计 16 次改道、4 次容量预约授予、4 次孤儿租约过期,车辆与行人所有权重叠为 0。

TrafficDynamicRouting 验证带拥堵与关闭上下文的加权路径,以及保持当前车道的路线交接。CityFlowSchedulingLifecycle 覆盖容量请求等待分数、单批授予预算和预约释放。TrafficSignalInterlock 覆盖车辆/行人许可、清空期、类型内排序、跨类型 starvation protection 与互斥。

TrafficOrchestrationArticle3Demo 检查暂停、按钮跳转、改道、单批单信用容量、租约过期、行人恢复、检查点和继续运行。TrafficOrchestrationArticle3DemoMap 验证地图、标签、固定摄像机、GameMode 与 HUD 接线。TrafficOrchestrationArticle3Soak 运行 1281 个 0.1 秒固定步,覆盖四个完整 32 秒周期,要求累计 16 次成功改道、4 次容量预约授予、4 次孤儿租约过期和 0 次冲突不变量违规。

统计字段 代码中的计数单位 跨周期累计 四周期 Soak 期望
CumulativeRerouteCount 成功应用动态路线的事件;每周期目标 4 辆 是 16
CumulativeCapacityGrantCount 单次批处理中的预约授予事件;每周期 1 次 是 4
CumulativeExpiredLeaseCount 孤儿车辆租约过期事件;每周期 1 次 是 4
ConflictInvariantViolationCount 车辆预约与行人组同时活动的违规固定步 是 0

证据采集在 Downstream Capacity 阶段的 step 171 保存检查点,推进到 Reservation Recovery 后再恢复;Phase、Time、路线、容量、信号和冲突状态均返回保存点。AllStop 与行人接管分别记录于 step 251 和 step 252,来自相邻权威固定步。运行至 step 1281 后完成 4 个循环,累计 16 次改道、4 次容量预约授予、4 次孤儿租约过期,Conflict Invariant Violation 为 0。

P9 冻结 tag 为 article3-traffic-orchestration-demo-v1/v2。当前候选已通过编译、通用 Smoke、ValidationAll、Standalone 操作检查和多周期观察。综合 ValidationAll 的 4 条已知警告属于全套验收环境,不是 P9 道路数据失败。

主场景覆盖:

  • 拥堵代价触发改道,当前车道、VehicleId、位置与速度保持连续;
  • 同一次 ProcessFairQueueBatch 中,三个请求竞争一个共享出口信用,仅一份预约获得 Granted;
  • 信号阶段包含 AllStop 清空,孤儿车辆预约通过确定性租约过期释放,等待中的行人组随后接管冲突区;
  • 车队、路由、容量、信号、预约和阶段可在会话检查点后恢复并继续;
  • RESET 清空 SOAK 累计状态,同时保留检查点和 capture/restore 次数。

自动化补充覆盖:

  • 小型项目自有车道图的距离、占用、拥堵三类软代价和硬关闭拒绝;
  • 断裂路线、不可达路线和无效 Scheduling 快照拒绝;
  • 重复预约去重、容量请求内部等待分数、类型内排序与行人 starvation protection;
  • 四周期累计 16 次改道、4 次容量预约授予、4 次租约过期和 0 次冲突违规。

它尚未覆盖离线道路烘焙、World Partition 流送、跨帧大图寻路、多车道变换、复杂路口相位、网络级死锁、真实车辆包络通行时间和目标平台路由预算。车体运动与碰撞留给 P11;全城表现成本留给 P12。

迁移至城市路网时必须保留的合同

迁移到异步搜索后,以下状态合同仍需保持:

  • 查询令牌可以取消,但旧路线在新路线提交前仍有效;
  • 路网局部卸载时,稳定车道 ID 与保存数据要有可解释的失效路径;
  • 拥堵输入可以更新,但不能让车辆无冷却地反复改道;
  • 活动预约只有在完成、取消或超时后才释放冲突区所有权;跨固定步的出口信用生命周期仍需另行建立;
  • 信号许可不能覆盖实际冲突占用;
  • 行人与车辆的等待原因必须可分别诊断;
  • Scheduling 快照必须先验证内部跨表引用;生产级全快照恢复还需扩展到 RoadGraph、Fleet、Reroute、Capacity 和阶段状态,再重建可见对象。

下一阶段需要补充规模证据:不同道路图大小下的查询队列长度、每帧展开节点上限、路线缓存命中率、拥堵更新频率、预约等待分布、租约误回收率和跨区恢复时间。上述指标稳定后,P9 的小型路网合同才具备进入城市运行时的条件。

Demo 颜色只承担诊断。

主路线、绕行路线、等待车辆、已预约对象和冲突区使用不同颜色,以便在同一画面中区分状态。颜色不参与选路、容量和调度决策;HUD 只读取运行时结果。将材质统一替换为灰色也不应改变固定步与自动测试统计。

正式表现阶段可以将这些颜色替换为道路标线、信号灯、车辆制动灯和人群动作,同时保留独立 Debug View。车辆停止时,单帧结果无法区分路线失败、出口满载、预约未获准和前车阻塞;P9 的诊断字段可继续用于编辑器可视化与线上日志。

与 P8、P10、P11 的接口边界

P8 的车辆调度决定“哪辆稳定身份的车可以从车库进入系统”,并提供初始路线需求;P9 决定它接下来可以使用哪些道路空间;P11 执行车身间距、制动和风险动作。P10 的行人过街则通过共享 Conflict Zone 和信号需求与 P9 相连。

P8 车辆派发与策略资格
        ↓ VehicleId / Route Need
P9 道路图、路径、容量与通行所有权
        ↓ Route / Gate / Reservation
P11 车辆运动、车距、制动与碰撞

P10 行人 Crosswalk Demand
        ↕ Signal / Conflict Zone
P9 路口调度

P9 不直接移动车辆 Actor,而是输出路线与通行约束,由车辆运动层消费。行人系统提交需求与实际占用,路口调度返回许可。P11 的车身模型和 P10 的角色动画由此可以独立替换,不改变信号状态机与冲突区所有权。


结语:通行权由拓扑、容量与所有权共同产生

P9 把交通编排从“沿路线移动”拆成三个可恢复的决定:动态路由选择未来空间,容量门限制同一次批处理中的预约授予数量,冲突区预约记录车辆与行人的共享空间所有权。信号提供许可窗口,却不能替代实际占用;改道提供新路线,却不能破坏当前车辆连续性;租约提供故障恢复,却不能替代有效对象的进度心跳。调度状态到车身制动和实际通行的执行链仍留给 P11。

下一篇 P10 将回到行人本身:人口身份和路口许可已经建立后,固定步 SoA 怎样推进人行空间、组织群组、处理避让,并把统一 Motion Frame 交给表现层。


AI 协作复盘

  • AI 参与内容:检索原工程交通证据、UE5 道路图、动态路由、容量预约、信号调度、类型内请求排序、行人等待保护、快照和自动化测试。
  • 主要修正:区分 HUD 参考成本与真实路由成本;确认 IncidentPenalty 当前未进入选路公式;将出口信用限定为单次批处理;区分共享 Sequence 与分类型两轮仲裁;区分信号许可、活动预约、实际占用和 P11 车身执行;将跨表预检限定在 Scheduling 子快照;按代码确认四周期 Soak 累计 16 次改道。
  • 人工判断边界:没有把同步小图搜索写成原工程跨帧全局寻路,也没有把整数容量信用扩张为完整交通流模型。
  • 核验方式:正文对应 CyberTrafficOrchestrationDemoActor、CyberTrafficDynamicRoutingRuntime、CyberTrafficIntersectionSchedulingRuntime、容量预约运行时、六项自动化与 Article 3 验收文档。
  • 遗留风险:大图搜索没有时间片,容量未直接绑定实时 Occupancy,租约参数尚未按道路尺度标定,恢复仍是会话内快照,复杂路口与跨区死锁尚未验证。

《《城市生命力》P9|UE5 交通编排:动态路由、容量门控与冲突区预约》有2条评论

发表评论

了解 AI Native Game Development 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读