灯已经绿了,来车却没有进入路口。它前面的普通车辆刚刚完成短程任务,仍停在出口附近。对来车来说,目的地没有取消,信号也允许这个方向通行,但它准备经过的空间还没有清空。
每辆车都有自己的任务,也都有能驱动车体的控制器。把它们放进同一条路,问题就变成了:眼前还能走多远,前方哪些空间已经被别人占用,停车以后又该等什么条件解除?
上一篇《NPC 跟车、受限绕行与自主恢复》把目的地、交通限速、换道和恢复接到同一辆物理车上。本篇继续向多车推进:先收集同一轮车身观察,让路口许可、队列距离和已提交的换道动作共同限制每辆车,再把结果交回各车的驾驶控制。底层沿用玩家驾驶与四轮受力,驾驶资格仍由座位关系与驾驶权接口检查。
下面保留六段完整实录,依次看信号排队、无信号交叉、出口受阻、共享出口、施工绕行和清障续行。它们来自独立 PIE 会话,每段只有两辆或三辆受测车,合计十五辆次。这里讨论的是局部普通车辆案例;背景停放车不计入名册,也没有把这些片段当成全城交通、常态人车混流或长期稳定性的证明。
文章目录
目的地保留,当前一步受共享空间约束
一辆车在红灯前停下,不需要业务层再发一次目的地。旁车先通过,也不意味着它自己的任务被别人替换。目的地回答“这趟要去哪里”,共享空间的判断只决定“这一物理步允许怎样继续”。把两者合在一个当前目标字段里,停车、改道和任务结束就很容易互相覆盖。
当前行程保存目的地、巡航速度、意图编号 IntentSerial,以及有向道路条目、车道点、游标 Cursor 和计划编号 PlanSerial。意图编号对应一次有效新任务;即使坐标相同,重新接受 Start 也可能产生新意图。交通停等只保留原意图,绕出、返回或重新接路则可以在同一意图下更新计划。
计划编号在同一宿主生命周期内累计,不保证新任务总从某个固定编号开始。后文三车绕行的 Intent=1、Plan=3 是各自实例中的结果,不能据此跨录像比较同一辆持久车辆。不同 PIE 会话中的实体编号即使重复,也没有这种含义。
| 保存的信息 | 状态所有者 | 等待时怎样处理 |
|---|---|---|
| 目的地、巡航速度、Intent | 行程对象 | 普通交通停等继续保留,接受新任务或取消时单独处理 |
| 道路条目、车道点、Cursor、Plan | 行程中的道路计划 | 按实测位置推进;候选获准后才替换相关计划 |
| 信号组、相位、剩余时间 | 信号运行时 | 按模拟时间推进,提供入口灯色资格 |
| 路口到达顺序、准入与清空持有 | 路口运行时 | 区分尚未进入和已经越线,保留合法的在途清空责任 |
| 前车及允许推进距离 | 队列计算 | 根据当前车身和前车允许量,收紧后车本步距离 |
| 当前换道动作、Intent、Plan | 局部动作占用记录 | 持有已提交动作,按实测车尾或生命周期条件释放 |
| 下一提交帧及对应建议 | 每车控制包 | 只能被匹配的物理步和任务版本消费 |
| 司机资格、命令所有权 | 人物与车辆关系接口 | 每次接受驾驶命令时继续核对 |
| 实际姿态、速度、轮接触 | 同一 Chaos 车体 | 持续参与物理,停等不会直接清零速度 |
这些约束保护的范围不同。路口保留车辆穿过控制区的剩余路径,换道占用保留当前一次横移动作,队列只限制本步还能向前多少。读到“允许通行”时,需要先看是哪一层给出的允许:一个 GO 只解除对应限制,其他约束仍然存在。
UE 侧由一个显式登记车辆的协调宿主取得全队快照,调用原生运行时并发布控制包。各车的执行宿主继续负责路线评估、目标控制、驾驶命令接受、轮力提交和物理回读。决策规则放在运行时里,Actor 的阶段回调负责把它们接到当前场景;这是局部名册的执行入口。
还有一个贯穿全文的区别:任务完成只改变任务状态,实体仍然存在。车停在出口并报告 Completed 后,不能从其他车的观察里消失。反过来,车辆现在停着,也不能推断任务已经完成;它可能只是等灯、等前车或等一个冲突动作清空。
先取得全队观察,再把建议交给各车
单车控制可以只读自己的姿态,多车准入还需要知道别人目前占在哪里。如果第一辆车读取旧车位、第二辆车读取新车位,再各自推导“路口是空的”,即使每辆车的局部算法看起来正确,组合以后也可能互相抢同一段空间。
当前协调器在 PrePhysics 阶段先用模拟时间步推进信号,再读取已加载路网和登记车辆的实际 Body 状态。登记车辆必须已经 Ready,且没有尚未消费的物理提交;某个仍在名册中的参与者暂时不可观察,这一轮就不发布新包。各车拿不到匹配包时请求制动,不把半份观察当成完整队列继续使用。
快照记录实际位置、车头方向、水平速率、车身边界和当前有向道路身份。UE 位置使用厘米,道路计算使用米,位置和车身边界都要在接口处换算。队列中的七米间隔与几百厘米的车身半长,必须先统一单位才能比较。
先观察已有换道动作是否已经结束,再建立路口输入。普通交通车和显式静态障碍分别登记:一辆车速度为零,仍可能是在排队或已经到达,不会因此自动变成允许绕过的施工物。这个类别区分也决定后续空隙和出口判断走哪条路径。
路口更新先在候选状态中计算
整队路口入口会检查帧号是否递增、车辆身份是否重复、每车策略是否齐全、模型尺寸及坐标速度是否有效。通过基本校验后,复制一份路口控制状态,在副本上计算新观察与许可。全部结果有效,才替换正式控制器并发布输出。
这样,计算途中发现结果无效时,不会已经在正式路口状态里消耗部分车辆的到达序号,或给某辆车留下半次准入。这个发布边界只覆盖路口控制器及其输出:信号推进和此前的动作 Observe 各自已经发生,不会随路口拒绝一起回滚。它也没有提供跨线程异步规划、网络分布式一致性或整轮通用回滚。
路口更新接受后,允许推进量再进入队列计算。队列把前车限制向后传播,协调器取最小值合并回每车建议,随后统一发布下一物理提交包。这样,各车使用的是同一轮快照推导出的建议,单车物理回调的先后不会让其他车临时改读它的新位置。这里的统一发布是当前同步调用顺序,不是把信号、路口、动作记录和车体一起提交的全局事务。
全队帧号与单车物理帧号不是同一个计数器
协调器的 FleetFrame 表示全队观察更新次数,各车的 SubmittedFrames 表示自身物理提交次数。二者有不同生命周期,不能假设数字永远相等。发布时,内部观察的 Frame 会重新标为该车的 SubmittedFrames + 1,外层包的 SubmitFrame 使用同一个值。
使用控制包前,要同时核对外层提交帧、Intent、Plan 和内部路口观察的 Frame。任务相同但路线已经换过,旧建议仍会失效;外包装帧号正确,内部观察过期,也同样拒绝。
| 校验对象 | 防止什么情况 | 当前拒绝结果 |
|---|---|---|
| 下一物理提交帧 | 把上一步或尚未对应的建议重复用于当前步 | 建议速度和距离归零,请求制动 |
| Intent | 新任务误用旧目的地的交通结果 | 拒绝旧包,等待新快照 |
| Plan | 路线已变,仍沿用旧移动的准入 | 本步制动,后续按新计划重新计算 |
| 内部 Observation.Frame | 外包装正确却夹带过期原生观察 | 同样拒绝,不能绕过原生入口检查 |
有一种正常失效就发生在本步内:协调器为旧计划生成包后,单车执行入口先评估路线,再运行交通策略。策略可能在这时提交新的换道计划,使 Plan 增加。随后控制包检查发现版本已变,旧包被拒绝,本步请求制动。下一轮观察到新计划,再为它生成匹配包。
所以,换道计划刚提交的这一物理步可能仍在制动,实际横移要等匹配新计划的包。短暂等待保住了版本边界,避免新路线沿用旧路线的通行结果。
有效建议最终交给目标控制器,生成油门、制动和转向,再通过当前司机与命令所有权检查,进入传动和四轮受力。Chaos 完成实际积分后,PostPhysics 发布姿态、速度和测量帧,供下一轮继续判断。全队协调没有获得直接挪动车身的接口。
红灯排队:灯色只打开一层入口条件
第一段在原城的小十字路口安排两组车辆。西侧 A1、A2 同向排队,南侧 B 先行。需要观察的不只是灯头变化,还包括后车能否跟着前车停稳,以及放行后是否继续各自原来的目的地。
视频 1|交叉方向先通过,原队列等绿灯后放行,约 39.27 秒。A1、A2 在红灯前排队,B 先通过。三辆车各有任务,不是三辆同时等待红灯。
片内约 7.83 s,B 中心经过所检查的交汇位置。A1、A2 随后形成稳定等待,低于 0.05 m/s 的共同采样区间约为片内 11.90—17.60 s,跨度 5.70 s。东西组约 17.63 s 首次变绿,A1、A2 约 21.37 s 和 28.10 s 先后经过检查中心线,约 32.90 s 和 37.20 s 确认到达。
B 约 15.00 s 已完成自己的任务,早于西侧队列放行。车体中心经过交汇位置、离开路口保护范围和完成整趟任务,是三个观察时点。影片能看出顺序,精确时间仍来自同轮记录。
本场景两组信号交替运行:一组初始绿灯十八个模拟秒,全红清空一秒,再切到另一组绿灯。片头已裁去初始化,第一张实录对应原生日志约 1.3667 s,所以不能把“原生十九秒变绿”直接写成播放器第十九秒。
实体灯头材质读取实际信号控制器状态,画面中的灯色确实来自运行时。不过这里只配置绿灯和全红清空,模型有黄灯镜片不代表黄灯阶段已经实现;临时灯头也不表示原图所有永久灯位都完成了绑定。
灯绿、具备资格、获得许可、实际越线分别保存
ApproachSignalOpen 是来自信号组的真实灯色输入。内部适配还会把入口距离、前车与出口条件合入请求,形成申请资格。内部字段 Request.SignalOpen 名称像灯色,含义却已经更宽,不能把它直接画成美术灯泡的开关。
| 状态 | 对当前车辆意味着什么 | 尚不能据此推出什么 |
|---|---|---|
| 信号入口开放 | 该方向的灯色条件允许申请 | 冲突空间一定空闲 |
| Eligible | 综合入口条件满足,可以申请 | 已经得到本次移动的持有 |
| Admitted | 路口已授予具体移动 | 车身已经越过停止线 |
| Committed/日志 Inside | 实际越过入口,进入清空阶段 | 中心一过就能释放全部空间 |
| Cleared | 按当前实际位置满足保护范围的离开条件 | 整趟行程已经到达目的地 |
未越线的预准入可以在条件失效时撤回。已经实际进入的车辆,则要继续承担清空责任;横向变绿,也不能直接忽略里面还没通过的车身。这个跨相位保留由冻结源码和合成状态测试支持,第一段没有另行构造“车恰好在区内变红”的专项实车对照。
GO 只解除路口自身的速度建议。队列给出的推进量、当前感知、世界碰撞保护、目的地减速和驾驶资格仍然有效。冻结单测特意检查过:包中的 Command 为 GO,但几何允许推进量为零时,最终速度仍应为零。
后车要继承前车的允许距离,才能稳定排队
红灯首先拦住队首,后车不一定已经靠近同一条停止线。如果后车只检查自己的信号资格,又认为前车接下来可以正常巡航,就会不断接近前车,最后依靠短距离碰撞保护刹一下、松一下。车没撞上,并不代表队列限制已经正确接通。
当前队列输入包含每车实际位置、方向、尺寸、有向链接和本步允许推进量。同一逻辑车道使用统一参考轴排序,再结合横向投影和高度范围确定是否仍共享物理走廊。转弯或换道时,如果每辆车按各自车头投影排序,结果会受世界坐标位置影响;统一坐标轴能避免这种排序歧义。
逻辑节点也不是物理队尾。原路网节点较密,两辆实际相邻的车可能已经属于相邻链接。实现继续跨节点寻找方向相容、位于前方且共享走廊的实际前车,观察范围为 max(25 m, 4 × minimumGap),并进行有限轮次的约束传播。
为了说明关系,先看直线同向、同一参考轴的简化式:
dFollower = min(dFollower,
max(0, centreGap − minimumGap + dLeader))
dLeader 是前车本步已经获准的距离,centreGap 是两车中心间距,minimumGap 是当前跟随中心间隔。后车能够使用的是现有间距超出保留量的部分,再加上前车确实还能向前移动的距离。真实实现另含前后车方向对参考轴的投影,不能直接把这条同轴式用于任意朝向。
本场景的 minimumGap=7 m 指中心间隔。车身简化半长为 2.36 m,同向直线时对应保险杠间余量约为:
bumperGap ≈ 7 − 2 × 2.36 = 2.28 m
车型变化以后,中心间隔和净距的关系也会变。实际前车选择还检查车身宽度、方向和高度,避免两个逻辑上同车道、物理上已分开的对象继续互相限制。跨节点松弛最多执行 min(车辆数, 8) 轮,当前有限传播并不保证任意长队列一帧内完全收敛。
先修正前车“还可以走一百米”的虚假输入
开发中曾给每个队列请求固定填入 100 m。队首已经被红灯限制,后车读到的前车允许量却仍是长距离。对后车而言,前车仿佛马上就能离开,队列无法提供真正的停车约束,只剩下最后的碰撞保护反复制动。
修正以后,传入队列的是每辆车真实的路口允许推进量,先限制在 0—100 m。这里的一百米是局部请求截取上限,不能解释成车辆可以无条件开出一百米。队首得到零,后面的传播才知道队首暂时没有可用前移距离。
非零的小许可也必须合并
另一个问题出在输出接线:早期只在 HeldByLaneQueue=true 时合并队列结果。但 Held 表示接近零的保持状态,小而非零的允许量同样是约束。
例如中心间距 8 m、保留间隔 7 m,前车允许推进 0 m,后车最多只剩 1 m。这时它还没有 Held 标志,却不能把这一米丢掉,重新使用未受队列约束的巡航距离。最终实现对每个匹配车辆的 Grant 都取最小值合并,不再只读停车标志。
这两处修复分别补上输入和输出:前车真实允许量传进队列,后车再使用每一份有效限制。最终红灯队列能形成实际停稳区间,与这条距离约束接线有关;完整尾随人格和跟车上下文仍未全部接通。
速度建议和物理制动分开处理
路口接近还复用了 AI handling 的制动距离尺度。当前计算可写为:
ratio = desiredSpeed / referenceSpeed # referenceSpeed > 0
D = max(minimumDistance,
ratio × (maximumDistance − minimumDistance))
参考速度为零时,不执行除法,比例项取零,再由最小距离保护。当前已审阅构造默认值是 20 m、60 m、40 m/s,对应最小距离、最大距离字段和参考速度。式中没有额外再加最小距离,最大距离字段也不是结果的硬上限。
与上一篇的威胁减速尺度相比,这里的路口辅助还用最小距离作下限,两个消费者不能混成同一公式。这里输入的是建议速度,不是实测速度平方。它给接近策略一个距离尺度,不能直接当成轮胎的物理刹停公式,也没有导入全车型和驾驶人格参数。红灯接近在相应距离范围内按 clamp((distance − extra) / D, 0, 1) 收紧建议速度;让行分支另有接近和最低建议速度,但仍受独立几何推进量限制。
UE 轮力执行边界另外增加速度保护:
speedLimit = sqrt(4 × max(0, permittedDistance − 0.2))
距离使用米,输出使用米每秒。系数四可按 2a 理解为本宿主采用的尺度;在这个单位约定下,它带有 m/s² 量纲。0.2 m 是本地余量,不是所有附着条件下都适用的停车保证。实际水平速度还会用于额外制动需求:
requiredDeceleration = actualSpeedXY² /
(2 × max(0.15, permittedDistance − 0.2))
该需求再按 6 m/s² 归一到制动请求,限制在零到一,制动显著时撤掉油门。感知距离的额外制动另用 0.6 m 余量,两条保护不要混成同一个距离参数。建议速度负责限制请求,实际减速仍由传动、轮力与接触实现。
停等滞回保留“已经决定停住”的历史
只靠比例减速,车辆可能在拒绝线前以很小的建议速度长期爬行;刹车后的轻微回弹,也可能让条件一松就再次给油。最终停等逻辑保存独立 Hold,完整条件为:
enterHold = d < 0.6 m
|| (Stopping && d < 2 m && advisorySpeed < 0.5 m/s)
releaseHold = d > 2 m && (!Stopping || d >= 3 m)
这里 d 是允许推进距离,advisorySpeed 是已经收紧的建议速度,不是车体实测速率。进入条件优先处理;既未进入也未释放时,沿用之前的 Hold。Hold 中建议速度为零,Stopping 保持为真。缺包或版本不匹配也会进入这个保持状态。
单测用不同阶段检查它:拒绝接近的尾段剩 1.5 m、建议已足够低时进入 Hold;回弹到 2.2 m 但仍受拒绝时不释放;前车实际离开,允许量恢复到 8 m 后可以解除。这些是合成输入对状态边界的检查,不是三次实车制动距离测量。
失效控制包请求制动,也不等于车体瞬间静止。一般控制生产者的 Fault 另有中性滑行等政策,不能把所有异常都归成同一种立即停车。协调器退出时保留必需包门、之后缺少匹配包走制动,是源码中的关闭策略;六段录像没有另做一次拔除协调器的实录。
无信号交叉:等对方实际清空,再继续原路线
红灯能够解释入口为什么关闭,没有信号时,两辆车就需要根据各自准备经过的空间判断冲突。第二段关闭信号控制器,也不生成这轮临时灯头,让西向东的 A 与南向北的 B 进入同一个小路口。
视频 2|无信号交叉,一车先行,另一车等清空后继续,约 27.20 秒。看 B 先通过时 A 是否仍停在入口外,以及 A 随后能否沿原目标到达。两车使用不同出口。
B 约在片内 7.83 s 中心经过交汇,A 约 13.53 s 随后经过;B 约 15.00 s 完成,A 约 25.13 s 完成。A 入口低速等待约 3.23 s,这个统计使用 Stopping=1 且实测速率低于 0.1 m/s。若改用低于 0.05 m/s 的筛选,得到约 3.17 s 的更窄样本跨度,差异来自条件,不是两份记录互相矛盾。
A 没有一个“暂停三点二三秒后启动”的专用计时器。这段时间由对方实际运动、已有许可和本车制动共同形成。目的地仍是原目标,空间解除以后继续评估原行程。
路口身份要能说清从哪条车道去哪个出口
只用“在矩形里”描述车辆,不能区分两辆可以同时经过的车与两辆会交叉的车。当前输入保留有向 From/To/Lane 身份,未来车道步骤还带节点语义。经过计划来自原编译路网,控制矩形只界定检查范围,并不创建原来不存在的转弯连接。
适配器沿未来路线求进入与离开位置,形成经过路径。若短路线窗口里没有找到完整出口,就保守处理,不能把当前看见的最后一个点当成“出口一定在这里”。否则一辆车在路口深处的转弯可能落到未检查范围,两辆车各自拿到许可后才在那里相遇。
冲突检查因此保留完整的剩余路口移动,而不是每轮只检查入口附近一小段。当前实现把成对几何冲突编码为帧内冲突区标识,交给原生仲裁;这些标识服务本轮请求,不是从地图中恢复出的永久完整冲突区资产。
已有持有先保留,新请求再比较
仲裁先清理无效、离开或已经清空的记录。对于仍合法的持有,保留其经过路径和已经越线标记;新请求再根据到达序号等规则检查与已有移动的冲突。这能防止已经开进路口的车因为下一轮重新排序,被另一辆新来的车夺走前方空间。
到达序号来自被接受的请求状态。新观察中同时出现的请求先按当前实现的身份顺序登记,之后再按保存的到达关系处理。这里的“先到”是运行时观察和请求序列,不应描述成对现实驾驶法规的完整解释。
等待请求也不是已获准车辆。源码对未决请求保留有限的来源上下文优先规则,未知或不支持的组合采用保守边界;没有把所有 WAIT 计划都当成已经拿到 GO 的空间占用。完整主次路、全部左右转、替代出口、公平性和反饥饿策略仍不在本轮等价范围内。
实际通过的路径逐步释放
已准入路径不会被每轮新目标随意重写。适配器根据实测位置和朝向,在保留路径上找能够证明已经经过的部分,再逐步释放。只因空间上靠近另一段路径,不能跳到后面宣布清空;循环或折返的路线还需要维持原有条目顺序。
控制区周围另加 300 cm 的保守车身清空扩展,避免车中心刚出矩形,转弯车身和车尾仍占着交汇区域。这个数属于当前矩形适配,不是参考引擎固定要求的三米规则。它与换道动作的车尾释放条件也不是同一套状态。
第二段验证了当前两车交叉场景中的等待、清空和续行。它没有测量任意方向混合流的吞吐,也不足以判断某种优先策略在持续来车时是否会饥饿。这些需要更长、更复杂的输入,不能从一次先后通过反推。
绿灯出口被占:已完成的车仍然有车身
第三段回到开头的问题。出口附近的 A 是普通交通车,初始就预置在控制范围内,先完成一个很短的真实任务,然后停住。B 从上游驶来,始终准备去原来的东侧目标;它所在的信号组全片保持绿灯。
视频 3|灯绿,但普通出口车仍占着空间,约 35.33 秒。观察 B 停等时 A 的实际位置,以及 A 离开后 B 怎样继续。只有 A 后来接到新的驶离目标,B 没有重新接单。
A 约在片内 2.83 s 首次 Completed,仍实际停在出口附近。B 约 8.47 s 开始形成稳定停车窗口。约 10.47 s,场景给 A 新的驶离任务;A 动起来以后,B 还要等实际占用清空,才进入。A 第二个任务约 28.87 s 完成,B 约 33.27 s 完成。
A 从出口附近开始这段短程,场景用它构造“任务已经完成,车身仍占空间”的条件。后续 A 从 Intent 1 变为 Intent 2,B 则始终保留 Intent 1、Plan 1 和原目的地。
看一次具体快照,比只看 GO/WAIT 更清楚
片内约 9.47 s,对应原生日志 10.8333 s,关键状态如下:
| 观察项 | 出口车 A | 来车 B |
|---|---|---|
| 行程 | 已完成短程任务 | 仍在执行原行程 |
| 意图与计划 | Intent 1、Plan 1 | Intent 1、Plan 1 |
| Admitted | 1,持有仍在保护范围内 | 0,未获准进入 |
| Inside | 1 | 0 |
| Eligible | 本表重点为已有占位 | 1,具备申请资格 |
| 当前实际结果 | 接近零速,车身未清空 | Stopping 1,允许距离约 0.535 m |
| 冲突来源 | 当前普通车辆 | 冲突阻挡指向 A |
B 的灯色和申请资格已经满足,拒绝发生在共享空间冲突上。A 即使 Completed,也继续提供有向身份和实际 Body 观察;任务结束后不再假定它将主动前移,原有清空保护仍能保留。
同一快照中,StaticExitBlocked=0,DownstreamBlockerRoute=-1,DownstreamDepth=0。它们把原因限定得很清楚:这一刻阻止 B 的是普通车冲突持有,静态出口分支和下游深层阻挡链没有在本例触发。
源码另有出口慢车与真实驾驶阻挡链观察,使用实际速度、前车和驾驶冲突阻挡关系,以及当前和此前路口身份。它要区分真实实体拥堵和两辆车互相等待许可的关系,后者不能仅因为都有 WAIT 就递归当作下游车流堵住。本片实际覆盖的是普通车辆占用造成的冲突保留。
外部给出口车新任务,来车仍等自己的条件解除
场景在 B 稳定等待约两秒后,给 A 新目标,让出口条件发生变化。这个刺激来自脚本;B 的准入仍由原生观察决定。A 接到任务并不等于它已经离开,控制器不能在发令那一刻就把真实车身删掉。
两个脚本事件相隔 2.0000 s,按左闭右开区间筛选的日志样本首尾跨度则是 1.9667 s。物理记录每秒三十个样本,末端释放样本被排除,出现一个采样间隔的差别很正常。这里的“约两秒”描述稳定等待;精确核对时,事件间隔和样本首尾跨度仍要分开。
本例保留原任务的是来车 B。A 明确接到了新的驶离目标,B 则一直等待原行程的通行条件。两个不同的任务变化共同构成了这段场景。
直行和右转共用出口,先保留已经进入的移动
第二段两车交叉后去不同方向,第四段让它们最终进入同一东向出口。B 从西侧直行,A 从南侧右转。即使二者在入口附近看似互不占道,后面的经过路径仍会汇到一起。
视频 4|直行与右转共享出口,约 35.27 秒。B 较早接近并先获得通行,A 在入口外等候,然后沿原曲线路线右转进入同一东向车道。这里不是两条平行车道缩成一条。
片内约 12.60 s 的观察中,B 已在路口或出口移动,Admitted 和 Inside 都为一。A 具备申请资格,却尚未准入,仍在入口外,Stopping 为一,允许推进距离约 0.640 m。A 的冲突阻挡指向本轮 B,而不是一个不明来源的暂停标记。
A 入口低速等待约 2.17 s,统计条件仍是 Stopping 且实测速率低于 0.1 m/s。B 约 29.20 s 完成,A 约 33.17 s 完成。两车目的地特意错开,避免先完成的车又在后车终点前形成另一个堵塞场景。
如果只检查目标车道这一瞬间有没有车,A 可能看到空出口就申请成功,而 B 正沿已获准路径驶向那里。保护完整剩余移动,才能把“现在不在那儿,但这个已提交动作会经过那儿”纳入冲突检查。已有持有保留,新的冲突请求等待,这是本片共享出口的核心。
这次先行来自 B 较早接近并获得的许可。片中没有持续车流,无法据此判断拉链交替、公平等待或反饥饿,也没有提供法定主次路体系的完整验收。当前能核对的是这两条路线的到达关系、冲突和实际清空。
多车施工绕行:目标位置空着,中途也可能冲突
上一篇已经说明单车绕行要检查相邻车道、后车接近、返回几何和碰撞。多车一起到达施工区以后,还需要把别的车已经提交的横移动作算进去。它们未必已经进入相邻车道,但可能正准备使用同一片空间。
视频 5|三车依次绕过施工并返回,约 48.07 秒。观察后车如何保留跟随约束,以及绕出和返回为何分开发生。每车最终保留原意图,经历两次换道提交;计数增加不是车尾已经释放的时刻。
场景沿原有平直走廊放置单车道施工物,保留合法相邻同向车道。A、B、C 依次接近;画面中的绿色停放车属于美术背景,没有第四份受测 NPC 任务。
| 车辆 | 首次观察到绕出提交 | 首次观察到返回提交 | 首次确认到达 |
|---|---|---|---|
| A | 约 1.83 s | 约 10.87 s | 约 28.47 s |
| B | 约 14.33 s | 约 22.63 s | 约 37.53 s |
| C | 约 26.13 s | 约 34.07 s | 约 45.97 s |
各车最终 Intent 都为一,绕出后 Plan 为二,返回后为三,换道提交计数为二。这里只按同一段录像比较这些实例。时点表示采样首次观察到新 Plan 或计数变化,不能拿来给横移完成和内部预约释放打时间戳。
后车仍然要看普通前车。远处登记了施工物,不表示可以看穿近处排队车、提前抢旁边的绕行空间。只有显式静态障碍会提出可选绕行,普通前车即使停止也不改类别。这个约束保留了排队语义,没有把施工区变成每辆车各自寻找最短超车线的竞争。
当前动作从实际车位到本次换道点
当前动作请求由实际 Body 位置起步,终点是这次 lane-waypoint,并补上车身相对道路高度的偏移,使用当前 Body 包围盒。它保护完整的这一段局部动作,绕出和返回各有一次独立请求。
返回几何预览只说明后续路线允许回来,不代表已经得到未来交通许可。真正返回时,仍要检查当前车尾是否越过障碍、当前实体和已提交动作是否冲突,以及世界扫掠是否通过。当前实现还保留已通过障碍的历史约束,防止目的地车道对齐过早把车横回障碍旁。
这与路口的“完整剩余经过路径”需要分开理解。路口保护车辆穿过控制区域的剩余移动;这里保护的是实际起点到当前换道点的动作。两处都要检查完整对象,但“完整”的对象并不是整个余下行程。
为什么不能只检查两个端点
假设起点和目标点都没有其他车,中间横移段却穿过一辆车的车头。端点检查会通过,实际执行仍会接触。已有动作也存在同样问题:另一辆车还没到冲突点,当前身体不在那里,只检查实际车体也会漏掉未来交叉。
准入因此分别检查候选动作对实际实体,以及候选动作对已经提交的动作。候选自身无效、同一车辆已经有未释放动作、中途与实体相交,或与其他持有动作交叉,都会拒绝。没有把“目标车道当前看起来空”直接当作通过条件。
平移段按运动方向构造车身扫过的有向包络。方向变化还要考虑绕路线原点的车角运动,不能假定局部包围盒原点永远等于车身几何中心。实现把转角分成至多五度的小段,分别求区间端点姿态的角点范围。
只包住这些端点,仍可能漏掉端点之间的弧线鼓出。为此按车角半径加入弓高余量:
margin = cornerRadius × (1 − cos(deltaAngle / 2))
角度以弧度参与余弦。该余量扩张区间端点包络,让相应的旋转弧线被包含。随后先比较高度区间,再用平面分离轴判断包络是否相交。高度分离的对象不会仅因俯视图重叠就自动冲突。
这里保护的是计划路径与车身几何。实际车辆仍可能受速度、附着、接触和侧滑影响,所以它不证明任意高速、任意轮胎都能精确落在这个包络内。世界扫掠、物理限速和后续实测反馈仍然需要保留。
从候选到占用,释放要等实际车尾
动作记录不能在刚看见候选时就建立,否则每个失败尝试都可能留下虚假的占位。也不能等实际碰到别人时才登记,那时后来的车辆已经可能拿到了冲突路线。
当前流程先预检查候选完整动作,累积空隙稳定时间;连续通过 0.6 s 后,提交入口再次调用 CanAdmit。复核通过,再检查行程版本和游标,提交正式换道路线、生成新 Plan,然后记录这份动作对应的 Intent 和 Plan。
空隙稳定表达的是过去连续观察通过,提交前复核表达的是现在仍能通过。二者各有作用:没有稳定观察,条件在边缘变化时容易频繁尝试;只保留稳定计时,又可能在最后一轮空间变化后仍提交旧候选。
计划提交接口核对当前 Intent、Plan、Cursor 和候选条目,限定候选能修改哪一版路线。顺序是先接受 RoadJourney 的新计划,再把动作与提交后的 Intent、Plan 记入占用表,后面的 Observe 用这份身份核对车辆。前置复核减少了登记失败的机会,但这两个写入没有被包装成通用回滚:若后面的动作登记返回失败,入口不会自动撤销已经接受的计划。这里说明的是当前同步调用的实际顺序,不把它扩展成任意失败下都原子成功或原子撤销。
中心到达换道点,车尾可能还没过去
如果车辆中心刚到动作终点就释放,后面的车可能立即进入车尾仍占据的空间。道路计划已经消费中间目标,也不等于这段空间完全清空;路线进度和车身占用的结束条件需要分开。
Observe 把实测位置与动作终点之差投影到动作轴上,另查侧向偏差。下面的位置和尺寸统一使用厘米,actionDirection 是水平单位方向:
alongPast = dot(actualPosition − actionEnd, actionDirection)
sideError = abs(cross(actualPosition − actionEnd, actionDirection).z)
release = alongPast > rearExtent + 50 cm && sideError < 150 cm
rearExtent 来自动作记录中的本车局部后向边界,动作方向取起终点的水平单位方向。严格超过后向尺寸加五十厘米,并且侧向误差小于一百五十厘米,才删除这条持有。这里只描述当前局部直段的释放政策,不把中心投影扩展为任意弯曲车辆轮廓的精确清空算法。
冻结单测明确构造过这组差别:动作已提交,中心到终点时记录仍存在;继续观察到车尾越过边界后记录才删除。另有中途实体阻挡、交叉动作拒绝、禁止同车嵌套申请、意图计划替换,以及实体从名册消失后的清理断言。这些是对机制边界的合成测试。
当前没有“等够若干秒就强制释放”的超时通路。车迟迟没走完,记录也不能仅为提高通行率自行消失。相反,Intent 或 Plan 已替换、宿主退出名册时,会清掉已经放弃的动作,以免形成幽灵持有。
清计划并不会清物理车身。旧动作因版本变化释放以后,车若仍停在原位置,下一次实体观察依然会阻挡别的候选。这让“放弃旧计划”与“场地已经空了”保持各自含义。
可以并行的是当前被判定不冲突的动作
第五段的聚合动作占用计数最高为二。它说明当前场景允许两份被几何判断为不冲突的动作同时存在,不是一辆车完成整趟绕行并到达以后,下一辆才获得出发资格。
录像记录只保存聚合计数,没有逐个动作对象的内部释放事件。因此,A 的换道计数变化、B 开始绕出和总计数下降,需要各按自己的含义读取。逐车车尾规则由源码和单测支持;实录能核对整体动作与最大并存数量,无法确定每条持有的精确释放时刻。
这也解释了为什么不需要把所有车辆全局串行化。互相冲突的动作等待,空间分离的动作可以并存;规则只对当前已知实体和计划有效。更大规模中的扫描成本、调度公平与稳定性,还需要下一阶段另测。
清障以后,原队列怎样继续
第六段把任务延续放回一个容易看懂的场景:三车在全路幅障碍前停住,外部移除障碍以后,它们继续各自原来的行程。这里关闭绕行,也不开路口信号,让读者只观察队列和实际前方空间。
视频 6|原队列停稳,清障后继续到达,约 23.73 秒。障碍由场景关闭碰撞并隐藏;车辆没有重建实体,也没有重新接受目的地。观察前方空间解除后,原车怎样恢复。
片内约 7.20 s,三车进入稳定停车区间;约 9.20 s 移除外部路障,形成约两秒全队停稳。随后恢复行驶的仍是片内正式采样期间的同一车辆和同一司机,Goal 与 Intent 保持不变。
障碍解除首先改变当前可用空间,队首获得新的推进量;后车还要结合前车实际位置和允许量重新计算。它们不是在同一时刻被脚本一齐写入非零速度。前车动起来、间距展开、后车继续,仍经过普通驾驶与四轮物理执行。
脚本只控制外部障碍,没有施工人员 AI,也没有 NPC 搬走路障。这个边界让环境刺激和运行时响应可以分开检查:刺激发生一次,后续各车能否继续,由任务、交通约束和物理结果决定。
三车终点不同,到达顺序也与队首顺序不同:C 约 20.97 s 先完成,A 约 21.10 s,B 约 21.67 s。通过同一个障碍,只完成了各自行程中的一段;后续还要走到自己的目的地。
等待保留的是任务,解除的是当前约束
如果停车时直接结束任务,清障后就需要业务层再次 Start。若把限制写回目的地,障碍解除以后也可能只剩一个临时停止点,原来的任务意图已经丢失。当前实现把原目的地留在行程里,停车限制由本步控制包和停等历史保存,因此恢复只需重新得到合法的推进条件。
这段也可以检查清理是否过度。车辆没有重新生成、换司机或瞬移到新起点,轮和传动状态仍属于同一车体。等待结束只恢复正常控制,不重置整个车辆运行时。先前积累的真实位置仍然决定当前游标与前视目标。
本片的两秒是场景观察到的停稳窗口,不是队列约定的统一等待时间。只要障碍还在、前车空间不足或包不匹配,车辆就不能因为时间到了而强行前进。这和第三段“出口车接到新任务,来车仍等实际清空”使用的是同一个分工。
到达必须由物理后的连续观察确认
路口清空、换道动作清空和任务到达分别有结束条件。进入目标附近,前车也许已经完成,但当前车是否停稳还要看自己的物理结果。目标速度为零只是一项请求,不能直接触发下车、对话或下一业务任务。
本轮交通宿主启用实测到达。行程先在末段建立 ArrivalPending,再由 PostPhysics 的实际姿态和完整速度累计保持时间。要求由宿主配置,没有把所有调用行程对象的入口都改成同一种默认模式。
较紧的进入线与稍宽的保持线
水平距离严格小于 1.5 m、完整实测速率严格小于 0.2 m/s,且高度差小于 2.5 m 时,行程进入到达等待,目标速度置零。随后允许车体在水平距离小于 1.8 m 的范围内制动收敛。越过较紧进入线的一点回弹,不会立即再次给油。
保持阶段仍检查高度;超出保持范围或高度不符,就清掉到达等待和时间。它与交通停等 Hold 分开保存:障碍前刹住不代表已经靠近业务目的地,到达等待也不能拿红灯停车历史来充数。
物理后观察还要求实际输入有效、时间步满足 0 < dt <= 0.1 s,行程阶段仍是 JoiningRoad 或 Driving,并且存在本轮待回读提交、NPC 模式正常、宿主没有故障。经过这些门以后,再检查:
horizontalDistance < 1.8 m
heightDifference < 2.5 m
length(actualVelocityMps) < 0.05 m/s
qualifyingPostPhysicsTime >= 0.5 s
任一条件失败,连续保持时间归零。这里累计的是满足条件的物理后更新时间,不是发出制动命令后的墙钟时间,也不是片头已经播放了多久。本轮三十赫兹采样下约半秒对应十五个更新间隔,但浮点累计和观察起点仍会影响首次成立在哪一步,不能承诺固定第十五帧完成。
最后一行 Hold 为零,不代表没有停稳确认
查看证据时需要取第一次 Completed 的样本,该样本中的 ArrivalHold 约为 0.5 s。后续观察已经不再处于待确认阶段,显示保持计数会归零。若只读取最终 result 或最后一行,很容易把正常的完成后清理误认为“没有执行半秒保持”。
和换道计数一样,最终值未必保留过程。核对 ArrivalHold 时,要看它何时建立、在哪个阶段累计,以及完成后怎样清理。影片的“已到达”字幕跟随完成结果,首次完成样本才保留这次保持通过的证据。
全部最终目标的平面误差约为 0.61—1.41 m,处于当前接受范围内。这里测量的是平面误差,不是三维总误差,也不是零误差或厘米级泊车。车身最终朝向、车位几何和任意道路精确停放,还需要单独的停车任务约束。
Completed 也不会使车身失去物理意义。第三段出口车已经给出了例子:任务可以结束,车还在共享空间里。每一种结束结果只处理自己拥有的状态,其他观察者继续按实际车身判断。
源机制、适配策略和本轮证据怎样对应
这轮参考成熟开放世界引擎的车辆智能、路口控制和驾驶辅助机制:交通预更新整理路口与前车观察,入口出口和车道语义决定冲突,接近辅助先限制允许速度,低层驾驶再生成控制。车身清空与任务目标分开,符合条件的驾驶员才能提交命令。
参考代码还包含更丰富的驾驶人格、主次路语义、替代出口、尾随上下文和分帧更新。本轮保留与当前场景有关的职责、输入和阶段,没有声称所有原分支逐行等价。配图整理这些职责和状态之间的关系,内部审稿材料另保留对应源码定位。
| 内容 | 本轮怎样使用 | 来源与范围 |
|---|---|---|
| 路口预更新、前车与任务分工 | 先观察,再给任务允许量,执行后读回实际结果 | 参考机制的职责关系 |
| 有向入口出口与冲突持有 | 使用原路网身份和剩余经过计划,保留已进入车的清空责任 | 原生机制的当前子集,未覆盖全部法规与公平策略 |
| UE 阶段桥接 | 全队 PrePhysics、单车实际物理提交、PostPhysics 回读 | 目标引擎接入 |
| 中心间隔与有限传播 | 当前七米参数和最多八轮跨节点传播 | 项目策略,不能当成所有车型通用常量 |
| 停等与额外制动 | 距离、建议速度和历史共同限制推进 | 当前物理宿主保护政策 |
| 局部换道持有 | 记录完整当前动作,版本校验,实测车尾释放 | 本项目的同步局部占用实现 |
| 控制矩形、临时灯头、场景刺激 | 选择原图局部范围,构造明确可观察条件 | 展示与适配,不是全城永久交通部署 |
合成测试、实车记录和视频检查分别回答问题
历史相关选择回归中,271 项成功,零失败、零未运行、零带警告成功。它已经包含控制包身份、信号与车身清空、完整动作持有这三项专项测试,不能写成二百七十一再加三。选择范围包括有关控制、路口、行程、恢复、感知、轮力和关系的测试,并非全项目测试总量。
这些测试用合成记录检查边界,例如内部观察过期时拒绝、已越线的车不因红灯丢掉清空持有、动作中心到终点仍不释放。它们不经过完整渲染物理场景。六段原图实车补上真实车身、轮力、驾驶接受、连续运动和最终任务完成的检查。
本轮使用的冻结 DLL 与前述历史回归一致。最终补录主要调整选址、实体灯头和镜头,没有因此修改原生代码或重新编译。写作使用冻结源码和当轮记录,没有重新运行 UE;文件哈希核对、全片解码和浏览器播放只能证明材料身份与媒体可读,不能算作又跑了一轮运行时测试。
| 片段 | 时长与受测车数 | 实录核对内容 | 结论边界 |
|---|---|---|---|
| 红绿灯排队 | 39.27 s,三车 | 交叉方向先行,两车停稳排队后按原任务放行 | 没有黄灯、全城配时或跨相位清空专项实录 |
| 无信号交叉 | 27.20 s,两车 | 不同出口的冲突等待与续行 | 没有任意方向持续流公平性结论 |
| 绿灯出口受阻 | 35.33 s,两车 | 普通车已完成仍占位,来车有资格但未获准 | 静态出口标志为零,没有深阻挡链实测 |
| 共享出口 | 35.27 s,两车 | 直行已进入,右转在入口外等待 | 不是缩道、拉链规则或恒定法定优先 |
| 施工绕行 | 48.07 s,三车 | 每车分别绕出返回,当前不冲突动作可以并存 | 没有逐条内部持有的精确释放时间日志 |
| 清障续行 | 23.73 s,三车 | 原队列停稳,移除外部障碍后原意图继续 | 不是施工人员行为或自动业务重新派单 |
六片共 3133 帧,以 15 fps 编码,总时长约 208.8667 s。逐片帧数为 589、408、530、529、721、356,由帧数计算的总时长与各片两位小数相加可能有舍入差别。原生物理采样为 30 Hz,每两个连续源样本取一张真实画面,保持仿真时长,片内没有跳切或加速。
全部受测车辆完成各自最终任务,记录接触计数为零。这只覆盖这些有界输入。十五辆次不是十五车同场,也不是同一批车连续跨越所有场景;车内原生驾驶资格不代表已展示完整司机动画。录制速度和输出帧率都不作为实时性能结论。记录中受测车辆为 NPC 模式;即使报告保留 Replay 标志,也不表示它们在照抄预先编好的油门和转向序列。脚本负责启动任务与改变外部条件,驾驶命令仍由行程和交通策略生成。
所有播放器时间以首录帧映射,近似为原生时间减 1.3667 s。画面粒度约为十五分之一秒,物理记录粒度为三十分之一秒,因此本文时间均按“约”解释;首次观察到状态,也不等于函数内部精确提交的 CPU 时点。
从局部共享空间走向持续街区
当前可以观察到:每车保留自己的目的地,信号和冲突决定入口,队列把前车真实允许量传向后车,局部动作持有保护换道经过,实际车尾与物理停稳分别结束对应阶段。这条链已经能够解释录像里为什么停、保留了什么、条件解除后怎样继续。
接到持续街区以后,还要处理常态人口的生成回收、更多车辆与行人的混合任务、观察者移动、远近表示切换,以及扫描和更新预算。更长时间下的拥堵传播、公平性和性能稳定,也都需要新的输入与记录。不能把局部零接触直接放大成全城无碰撞。
下一阶段会围绕“让城市持续运行”继续展开。这里先把局部共享空间的状态边界收住:任务不因为等待被丢掉,许可不越过实际占位,计划变化不偷用旧包,结束状态也不让仍在场的车身消失。后续规模增加时,才能继续追查每辆车正在等什么,而不只是看到一片停住的模型。
AI 协作复盘
本篇先读取交接、逐车时间线和冻结源码,再检查六段最终录像及关键帧。AI 用于梳理全队与单车更新顺序、整理队列修复的输入输出、核对动作持有和物理清空条件,并绘制机制图。写作没有改工程、扩功能或重录,也没有把历史回归写成这次新跑的测试。
最需要避免的是凭名字猜机制。绿灯并不等于来车已获准,出口被占也不等于静态探针触发;换道计数增加不是车尾已经释放,任务 Completed 更不等于实体退出。第三段的实际字段、第五段的源码和合成断言分别约束这些说法,不能只从画面相似就串成同一种“等待状态”。
时间与数量也单独核对:原生日志先映射到片内时间,事件间隔与采样跨度分开;六个独立会话只合计辆次,不合计并发;三项专项已经包含在历史选择回归中。内部资料保留定位与映射,正文围绕这些状态如何影响实际驾驶展开。
仍需继续验收的内容留在边界里:未逐条记录的动作释放时点不补猜测,没有展示的深阻挡链和公平调度不写成实测,正在开发的持续街区能力也不放进本轮完成清单。图用于解释因果,真实结果仍回到对应录像和同轮物理记录。












