# Driving V1：权威世界中的持续驾驶

Status: accepted for bounded local experiments. Action: implement. 2026-09-23.

这是 [AI_DRIVING_INFRA](AI_DRIVING_INFRA.md) 所选实验的实现记录。产品目标仍是 [PRODUCT_GOAL](PRODUCT_GOAL.md)：通过同一 Package 生命周期增加世界能力，客户端看到相同的权威结果。它不是道路交通模拟器或真实车辆控制软件。

## Context and evidence

基线为 Git `e1dc2c0d5f38231054325dba67a9ea3344ea7ea5` 加已有的未提交载具、素材绑定、布局和录制实现。不能把 HEAD 当成候选源码身份。最终文件、二进制及录制身份保存在 [evidence/driving-20260923](evidence/driving-20260923/README.md)。本次范围是本地实现、测试和两个新建隔离世界的录制，没有部署共享世界。

参考仓库的浏览器执行器直接积分车辆位置。Pajama 继续用 Rapier 的真实车身、轮胎射线、悬挂、力和碰撞；未引入参考仓库运行时。摄像机用于检查渲染，模型接收结构化传感器数据。

## Alternatives and selection

| 方案 | 收益与约束 | 结论 |
| --- | --- | --- |
| 外部 bot 每帧输出油门和方向 | 接入快，但网络/推理延迟直接进入控制回路，缺少服务器期限和传感器契约 | 保留人工输入；持续驾驶采用下述接口 |
| 在现有边界增加道路文档、驾驶会话和原生控制器 | 复用 Package、座位授权、Physics、复制、检查点；模型服务可替换 | 本次实现 |
| 立即建设通用 ActionRuntime、完整路网规划或机器人中间件 | 可覆盖更多领域，但当前只有一个持续动作实证，抽象和运维成本大 | 等第二个真实持续动作用例及复杂路口需求再提炼 |

## Ownership and data flow

```mermaid
flowchart LR
  DSL[道路 / 载具 / 布局 DSL] --> Cold[冷阶段校验与编译]
  Cold --> Package[现有 Package admission / CAS / checkpoint / tick activation]
  Package --> World[权威道路及载具实体]
  World --> Sensor[有预算的传感器投影]
  Physics[PhysicsFrame: pose + motion] --> Sensor
  Sensor --> Observation[DrivingObservation V1]
  Observation --> Director[异步 DrivingDirector / Jev]
  Director --> Intent[带期限及观察身份的 DrivingIntent]
  Intent --> Session[服务器会话及前置条件验证]
  Session --> Controller[20 Hz 闭环跟踪及制动]
  Controller --> Rapier[Rapier 250 Hz 固定子步]
  Rapier --> Physics
  Physics --> World
  World --> Clients[复制 / 两个原生客户端 / 录制]
```

所有权具体落在以下文件：

| 层 | 实现与契约 |
| --- | --- |
| `pajama-world` | `driving.rs` 定义道路、观察、意图、回执及精确 Package pin；`proto.rs` 增加三种客户端驾驶消息和两种服务器消息 |
| `pajama-content` | `driving.rs` 同时生成道路语义和可见、可碰撞的路面 prefab；路面按最多 16 m 分段 |
| Package admission | `package_authoring.rs`、`prefabs.rs` 验证道路组件及 feature；仍由普通 Package 发布和 Luau prefab 命令创建世界实体 |
| `pajama-physics` | `VehicleControl` 分开油门、刹车、转向；同一物理发布批次返回车身 pose、线/角速度和实际转向上限；遮挡查询使用碰撞几何 |
| `pajama-server` | `driving.rs` 持有临时驾驶会话、感知记忆、前置条件和控制器；在复制之前作用于物理接口 |
| 外部 agent | `scripts/system-one/driving-director.mjs` 管理单个在途模型请求、预算、取消、新鲜度；`jev.mjs` 使用独立 maneuver 问题，不改变原有 skill 选择语义 |
| 原生客户端 | 渲染服务器复制的车辆结果和精确版本导入模型；没有在客户端决定位置、交通权或刹车 |

这也明确了引擎扩展的两条路径：**内容 DSL 在冷阶段求解并产出文档，持续动作在运行时读取反馈并施加有边界的命令**。布局约束不能代替驾驶控制；驾驶控制也不能绕过内容发布直接修改道路。Luau 仍只读不可变帧并返回类型化命令，本轮没有扩展其 packed ABI，也没有在 tick 内发起模型请求。

后续的真实车轮反馈、越野配方和地形路线见 [DRIVING_TERRAIN](DRIVING_TERRAIN.md)。下文的两个首轮视频仍是平路 V1 的历史证据。

## Current bounds

- 道路 V1 是有界双向右侧通行中心线，ego 沿中心线递增方向行驶；2–64 个点、总长最多 4096 m、车道宽 3–6 m、限速 1–20 m/s。几何编译器支持水平直线和缓弯，拒绝坡道；没有路口拓扑或全局寻路。
- 驾驶者是普通玩家连接，必须坐在自己可控制的 Package 四轮车中。最多 8 个会话。无连接 NPC 的驾驶能力尚未加入。
- 会话绑定 server instance、authority epoch、玩家、车辆 incarnation、车辆和道路的精确 Package pin。每个意图绑定已发送且最多 20 tick 的观察、递增 request sequence 和 1–40 tick 有效期；接受频率最多每 4 tick 一次。
- 服务器每 tick 重查交通前置条件，向驾驶者以 5 Hz 发送观察。扫描最多 4096 个实体、候选最多 256、观察最多 32 个接触，范围最多 300 m；不依赖普通客户端的 60 m AOI。预算截断或遮挡会标记观察不完整，禁止开始超车。
- 支持 cruise、follow、stop、overtake、return_right、emergency_brake。纵向速度反馈、停车减速曲线、转向前视跟踪都在服务器。手动非零输入接管；超时、取消、旧物理帧、碰撞未就绪、越界和近距离对向冲突触发制动。
- 地面检查覆盖前方最多 28 m 的 5 个中心线采样点；不是完整宽度/高速远距碰撞预取系统。感知每 tick 的扫描成本尚未做大世界性能验收。
- Rapier 实现新控制接口；Box3D 明确报告 unsupported。最初直线路演使用 `pajama.physics.rapier-world-v3-driving`。后续[崎岖地形扩展](DRIVING_TERRAIN.md)修复累积转向力矩与停车漂移，当前运行配方为 `pajama.physics.rapier-world-v4-driving`，对应录制使用新建 V4 世界；旧 V2/V3 检查点迁移需要单独验证。
- 驾驶会话和意图不进入检查点。恢复后保留道路及车辆 Package pin，释放驾驶者，旧请求不能续用。

## Execution and acceptance

可执行入口：

```powershell
$env:CARGO_TARGET_DIR='target\driving-validation'
cargo build -p pajama-server --bins
cargo build -p pajama-client --features catalog-v2-process-e2e,retained-presentation-wire-v1
# Copy the three executables into target/driving-binaries before cleaning the validation target.
& .\target\driving-binaries\pajama-package.exe road examples/authoring/driving-road.json target/new-road-source studio.road
node scripts/system-one/drive.mjs --url ws://127.0.0.1:PORT --road ROAD_ENTITY_ID --trace new-driving.jsonl --provider fixture
node scripts/authoring/record-driving-demos.mjs --bins target/driving-binaries --character-cache target/vehicle-visual-demo-20260923-final/host/authoring/characters/meshy-hero --out target/new-driving-recording --seconds 70 --fps 4 --ffmpeg PATH_TO_FFMPEG
```

原生录制要求上面的两个非默认客户端 feature；默认构建会拒绝 `--experimental-catalog-v2`。普通服务器及驾驶协议不依赖这些实验展示开关。

`drive.mjs` 要求玩家出生位置附近有已经发布的车辆。`--provider openrouter` 或 `--provider typesafe` 显式选择现有 Jev 路由和环境变量，默认 fixture 不调用付费服务。单次最多 600 秒，默认最多 100 个模型请求。模型失败不刷新服务器意图的期限。

录制脚本从零启动两个独立场景，经现有发布、检查点及 tick 激活路径创建道路和 Kenney SUV。普通 bot 连接驾驶，两个原生客户端分别放在左右显示器。`basic` 验证车道保持及停止线前停车，`fault` 停止发送决策并验证世界继续运行时的自动刹停。视频决策来源明确为 `deterministic-driving-recording`，不是已完成的真实 Jev 演示。

录制 Package 的道路是抬高 0.5 m、基础嵌入地面的实体铺装，避免与地表或 GPU 草层重叠。GPU 草由 render profile 管理，`TerrainParams.grass_profile` 并不是它的开关；通用道路施工与地形、植被排斥的联动仍是后续基础设施，不能从这个平地夹具推定已经实现。观察中的 `crossing_distance_m` 是车头到斑马线前 3 m 停车位置的距离，画面显示斑马线。

验收不只检查发布回执：读取 presented swapchain PNG/报告，验证同帧编码的车身和四个车轮及 Package digest，检查权威位移、行驶速度、车道误差和停止条件，再验证视频完整解码。测试包含实际 Rapier 的两种车重制动、变道回归/停车闭环、遮挡射线，以及 WebSocket 驾驶、拒绝外来控制/旧观察/非法超车、取消、双客户端复制和恢复。

## Outcome and reuse

具体结果与候选身份以 [验收记录](evidence/driving-20260923/README.md) 为准。测试夹具先在默认世界遇到障碍物，服务器正确制动；后来使用显式的平坦空地初始文档，隔离测试道路与随机场景。真实停止控制测试暴露过约 1 m 的越线，加入目标加速度前馈后通过，不以降低停车断言掩盖误差。

可复用的是文档、期限、身份、新鲜度及权威控制的边界。当前两个视频没有证明拥挤交通、真实行人、复杂路口或自主超车的完整效果。模型真实延迟、更多车辆配方、坡道、道路动态编辑、多车性能和旧检查点迁移仍需各自的实验；不要把 V1 的直路通过结果推广到这些情况。
