chore(task): archive 08-08-cesium-lane-centered-route

This commit is contained in:
2026-08-08 15:25:29 +08:00
parent 5658e7337d
commit e89b649256
7 changed files with 273 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,130 @@
# 自动匹配 Cesium 巡航车道中心:技术设计
## 设计目标
巡航路线以 osm2streets internal road topology 与已渲染的 Driving lane polygon 为几何事实源。道路区间直接使用 polygon 的中轴,不再对 OSM way 中心线施加固定米数偏移OSM 只提供原始标签和转向语义,不再作为最终路口拓扑。
## 数据流
```text
network.json ──▶ internal road/intersection directed graph
OSM XML ──▶ turn:lanes semantics
lane_polygons.geojson ──▶ validated Driving lane centerlines
maneuver-aware lane selection
internal road lane sections + surface-constrained junction curves
vehicle-route.json + diagnostics
```
`scripts/build-area.js` 在 preview stage 调用 `buildVehicleRoute(osmPath, lanePolygonsPath)``lane_polygons.geojson` 同时写入 preview manifest inputs`scripts/lib/area-diagnostics.js` 使用既有 SHA-256 freshness 检查自动识别过期路线。
## 模块边界
### `scripts/lib/lane-geometry.js`
新增纯几何共享模块,承载:
- 校验 Driving `Polygon` ring
- 通过 polygon 两侧对应顶点中点提取 lane centerline
- 米制距离、方向对齐、端点排序和 polyline 拼接所需的无副作用 helper。
`scripts/lib/turn-lane-arrows.js` 改为导入共享 `laneCenterline()`,确保箭头和巡航对 osm2streets polygon 顺序使用同一契约。
### `scripts/lib/vehicle-route.js`
保留现有 OSM 路线拓扑搜索,替换 `LANE_OFFSET_METERS` / `offsetClosedRouteRight()` 路径:
1. 加载并严格校验 `lane_polygons.geojson` 为 FeatureCollection。
2.`road`、lane `index``direction=Fwd|Back` 建立 Driving lane 索引;`osm_way_ids` 仅用于追溯 OSM 标签。
3. 对每条 directed edge使用实际横向位置按行驶方向排列同向车道renderer `index` 只作为稳定 tie-breaker不单独决定车道顺序。
4. 将 edge 末端的 maneuver 与 `turn:lanes:forward|backward` 对齐。左转/掉头选最左兼容车道,右转选最右兼容车道,直行选最右兼容车道;无显式 lane restriction 时按相同位置规则选择。
5. 每个 edge 对应一个 osm2streets internal road禁止把同一 OSM way 下多个 internal road 当作无语义 fragment 直接拼接。
6. 车道中轴端点之间使用 tangent Bezier / U-turn 连接,并绑定共同的 internal intersection全部采样点必须落在对应 `intersection_surface` 内或边界容差内。
7. 任一 edge 无法可靠匹配时丢弃该候选 route继续搜索其他候选最多输出 5 条。
## 输出契约
保留顶层 `routes`、兼容别名 `segments``loop``speedMetersPerSecond`,以及每条路线的 `coordinates``centerlineCoordinates``edgeIds``maneuvers``lengthMeters`
每条路线新增 `laneSegments`,每段至少记录:
- `edgeId``osmWayId``direction`
- `laneIndex``widthMeters``centerOffsetMeters`
- `maneuver``source="lane_polygon_centerline"`
- 参与拼接的 polygon/road 标识。
顶层新增 `diagnostics`,按稳定 reason code 汇总被拒绝的 edge/route例如 `missing_lane_polygon``invalid_lane_polygon``ambiguous_lane_order``no_compatible_turn_lane``disconnected_lane_fragments`。固定 `laneOffsetMeters` 不再作为几何输入;为避免伪造单值,不以平均偏移替代逐段事实。
## 错误与降级语义
- 整个 lane polygon 文件缺失、JSON 无法解析或不是 FeatureCollectionpreview stage 在写产物前失败。
- 单个 polygon 无效:记录诊断,该 polygon 不参与匹配。
- directed edge 缺少唯一可信车道:候选 route 被拒绝,生成器继续选择其他 route。
- 所有候选都被拒绝:生成合法的空 `routes` 和完整诊断Cesium 场景仍可加载,但不显示巡航车辆。
- 禁止回退到固定 `1.3 m`、固定 `1.5 m` 或默认 lane width。
## 兼容性
- Blender scene 经纬度投影改为 WGS84 ECEF→ENU与 Cesium
`eastNorthUpToFixedFrame` 的锚点坐标系一致;这是 route 与最终 GLB 道路重合的必要
跨层契约。
- `segments` 继续与 `routes` 引用相同数组。
- Cesium runtime 当前只消费 `coordinates` 等既有字段,无需理解 `laneSegments` 即可运行。
- preview manifest 新增 lane polygon input 后,旧 manifest 会被诊断为缺少记录并要求重建,这是预期迁移行为。
## 验证策略
- 纯几何测试:四边形、曲线 polygon、反向 geometry、坏 ring。
- 路线 fixture不同宽度、双向/单向、多车道和转向车道选择。
- 精度断言:路线道路区间采样点到 polygon 中轴距离不超过 `0.10 m`
- 连续性断言:车道变化和路口连接处没有由数据拼接产生的异常横跳。
- 集成验证:目标区域重跑 preview检查 route JSON diagnostics、manifest freshness、区域质量门和 Cesium 实际显示。
- 端到端视觉验证:重跑 `blender,cesium,preview`,对最终画面做道路横截面检查;不能用
route 与 lane polygon 的厘米级一致性代替 GLB/Entity 对齐验证。
## Bug AnalysisGLB 与巡航路线随距离漂移
### 1. Root Cause Category
- **Category**B - Cross-Layer ContractD - Test Coverage Gap。
- **Specific Cause**Blender 使用固定米/度的平面近似Cesium 使用 WGS84 椭球 ENU
两层没有共享坐标转换契约。
### 2. Why Fixes Failed
1. 固定车道偏移:只处理症状,且假定所有车道宽度相同。
2. 从 Driving polygon 重建中轴:解决了车道宽度与 lane 选择,但只证明 GeoJSON 内部正确。
3. 路口切线连接:改善了 connector却没有解释直线路段整套坐标同向平移。
### 3. Prevention Mechanisms
| Priority | Mechanism | Specific Action | Status |
|---|---|---|---|
| P0 | Architecture | `Projector` 使用 WGS84 ECEF→ENU与 Cesium 锚点一致 | DONE |
| P0 | Test Coverage | 测试 WGS84 局部经纬度比例与 ENU 方向 | DONE |
| P1 | Documentation | 在 Blender、preview 与 cross-layer spec 固化契约 | DONE |
| P1 | Integration | 坐标变更后强制重跑 `blender,cesium,preview` 并视觉核对 | DONE |
### 4. Systematic Expansion
- **Similar Issues**交通信号、语义模型、route polyline 等所有叠加在 GLB 上的 Cesium
Entity 都依赖同一契约。
- **Design Improvement**:坐标转换只有 `Projector` 一个 Blender 事实源。
- **Process Improvement**:跨运行时几何必须验证最终组合画面,不能停在单层数值测试。
### 5. Knowledge Capture
- [x] 更新 Blender asset generation spec。
- [x] 更新 preview vehicle route spec。
- [x] 更新 cross-layer thinking guide。
- [x] 增加 `ProjectorTest` WGS84 断言。
## 回滚
代码回滚只涉及 preview 路线生成和共享 JS helper重跑 preview 即可恢复旧路线产物,不需要重建 Blender/GLB。用户已有区域配置修改保持不动。

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,43 @@
# 自动匹配 Cesium 巡航车道中心:实施计划
## 实施顺序
- [x] 1. 新增 `scripts/lib/lane-geometry.js`,从 `turn-lane-arrows.js` 迁移并导出 Driving polygon 中轴提取与必要的纯几何 helper补充共享几何测试确认箭头行为不变。
- [x] 2. 扩展 `scripts/lib/vehicle-route.js` 输入校验和车道索引,按 OSM way、行驶方向、实际横向位置及 maneuver 选择目标 Driving lane。
- [x] 3. 实现同车道多 fragment 的方向校验、确定性拼接和 lane centerline 路线生成;删除固定偏移路径,加入稳定 reason-code diagnostics 与 `laneSegments` 溯源。
- [x] 4. 更新 `scripts/test-preview-assets.js` fixture覆盖 `3.0 m` / `3.5 m`、双向单车道、同向多车道、oneway、正反方向、左/直/右选择、坏 polygon 和缺失匹配。
- [x] 5. 修改 `scripts/build-area.js`,在任何 preview 写入前校验 `lane_polygons.geojson`,传给路线生成器并记录到 stage manifest inputs。
- [x] 6. 修改 `scripts/lib/area-diagnostics.js` 的 preview expected inputs并补充 manifest freshness 回归测试或等价断言。
- [x] 7. 更新 README 巡航说明,移除固定 `1.3 m` 描述,说明实际车道中轴、跳过语义和诊断字段。
- [x] 8. 重生成目标区域 preview 路线,检查所有 route 的 lane 溯源、空/拒绝诊断和直线路段 `<= 0.10 m` 中心误差。
- [x] 9. 将路线拓扑切换到 `network.json` internal road/intersection并对 connector 执行 `intersection_surface` 越界拒绝;预览仅显示当前选中 route等待用户做最终视觉复核。
## 当前验证状态
- 目标区域只读生成验证通过:`5` 条路线、`56` 个 lane segment、`116` 个中轴顶点最大误差 `0 m`,覆盖 left/right/through/u_turn实际 center offset 范围 `1.4151.588 m`
- 第 8、9 步暂未完成preview stage 在写产物前因既有 `traffic_signal_assemblies.geojson``traffic_signals.json` 缺失而失败。
- `check:area` 的 3 个 failure 均来自既有 intermediates/blender/preview manifest stale修复需要重跑会重建 GeoPackage 的 intermediates未获用户授权前不执行。
## 验证命令
```bash
node --check scripts/lib/lane-geometry.js
node --check scripts/lib/vehicle-route.js
node --check scripts/lib/turn-lane-arrows.js
node --check scripts/build-area.js
npm run test:turn-lane-arrows
npm run test:preview-assets
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages preview
npm run diagnose:area -- --config config/areas/nantaizi-lake-innovation-valley.json
npm run check:area -- --config config/areas/nantaizi-lake-innovation-valley.json
```
若仓库已有覆盖 manifest freshness 的独立测试入口,一并运行;否则在现有最接近的 Node 测试中加入定向断言。
## 风险与检查点
- polygon fragment 拼接是最高风险点:完成第 3 步后先用目标区域做只读匹配统计,确认不会因 osm2streets 分段导致全部路线被拒绝,再继续 stage 集成。
- 多车道 lane order 不得只依赖 renderer `index`;必须用行驶方向下的实际横向位置验证顺序。
- 不把 OSM `width` 或默认宽度作为无声回退;任何覆盖率下降必须能从 diagnostics 定位。
- 不修改用户已有的 `config/areas/nantaizi-lake-innovation-valley.json` Linux 路径变更。
- 不需要 GLB parity本任务不改变 Blender/GLB。但必须重跑 preview manifest 和区域质量门。

View File

@@ -0,0 +1,49 @@
# 自动匹配 Cesium 巡航车道中心
## Goal
Cesium 预览巡航路线应根据实际车道数据自动落在所选行车道中心,消除固定横向偏移带来的位置误差,为后续车辆仿真提供可靠的几何基础。
## Background
- `scripts/lib/vehicle-route.js:9` 当前使用固定 `LANE_OFFSET_METERS = 1.3`,并在 `makeRoute()` 中对整条平滑后的道路中心线统一向右偏移。
- 当前区域的 `lane_polygons.geojson` 中 Driving lane 宽度为 `3.0 m`,单车道中心距道路中心线应为 `1.5 m`,现有路线存在约 `0.2 m` 横向误差。
- `lane_polygons.geojson` 已包含 `direction``width``index``osm_way_ids` 和车道 polygon这些数据与最终渲染道路来自同一 osm2streets 中间产物。
- OSM 输入包含 `lanes``lanes:forward``lanes:backward``turn:lanes:*`,但不保证包含明确的 `width`,不能单独作为所有区域的精确宽度来源。
- 历史提交 `30846b6` 引入连续巡航路线时沿用了实验阶段的 `1.3 m` 固定值,没有建立路线与 osm2streets 车道几何之间的契约。
## Requirements
- R1路线生成以 osm2streets 实际 Driving lane 数据为主事实源,不再使用全局固定偏移常量。
- R2按 OSM way、行驶方向和车道顺序匹配目标车道并根据各路段的真实宽度及横向位置计算车道中心。
- R3不同宽度、不同车道数或不同方向配置的连续道路必须逐路段计算偏移路口连接处保持连续且不产生横向跳变。
- R4多车道路段必须选择一条明确的目标车道车道选择和转向可行性应使用 `direction``index``allowed_turns` / `turn:lanes:*` 数据,而不是只看总车道数。
- R4.1:同向多车道按下一次 maneuver 选择兼容车道;左转/掉头优先最左侧兼容车道,右转优先最右侧兼容车道,直行默认最右侧兼容车道。没有兼容车道时跳过该候选路线并记录诊断。
- R5每条输出路线记录所用车道、宽度/偏移来源及诊断信息,使下游能够识别精确匹配、次级推导和跳过的路段。
- R5.1:缺少、歧义或无法验证车道数据的路段必须跳过并输出结构化诊断;禁止回退到固定偏移或默认车道宽度。
- R6保留现有 `routes`、兼容别名 `segments`、闭环路线、信号灯停车和 Cesium 动画消费契约。
- R7preview stage manifest 将参与路线计算的车道数据列为输入,使车道几何变化能够正确判定预览产物 stale。
- R8不得修改 Blender/GLB 主资产;本任务只修正预览巡航路线及其生成契约。
## Acceptance Criteria
- [x] AC1当前南台子湖区域重新生成后普通直线路段的巡航点位于匹配 Driving lane 的几何中心,允许误差不超过 `0.10 m`
- [x] AC2测试 fixture 覆盖至少 `3.0 m``3.5 m` 两种车道宽度,输出中心偏移分别随实际车道数据变化,不存在 `1.3 m``1.5 m` 全局常量依赖。
- [x] AC3测试 fixture 覆盖双向单车道、同向多车道、`oneway=yes` 和正反方向,验证目标车道选择及偏移方向正确。
- [x] AC4连续路段车道宽度变化或车道数变化时路线在衔接区连续相邻采样点不得出现由偏移切换导致的异常横跳。
- [x] AC5路线 JSON 能追溯每段匹配到的 `osm_way_ids`、方向、lane index、width、center offset 和数据来源。
- [ ] AC6既有 preview asset tests、路线闭环测试和区域质量门通过信号灯停车与车辆朝向行为不回归。
- [x] AC7preview manifest 记录车道数据文件;该文件改变后诊断能够将 preview 标记为 stale。
## Out of Scope
- 车辆在运行时动态换道、超车或避障。
- 交通流量、车辆间距和碰撞模型。
- 修改 osm2streets 生成的车道 polygon 或 Blender 道路网格。
- 将实验性预览巡航升级为通用交通仿真引擎。
## Technical Constraints
- 本任务涉及 OSM、osm2streets GeoJSON、stage manifest 和 Cesium route JSON 的跨层契约,按复杂任务处理;规划收敛后需要 `design.md``implement.md`
- Driving lane polygon 的相对两边中点算法已在 `scripts/lib/turn-lane-arrows.js:265` 验证;巡航路线应复用同一纯几何实现,不维护第二份 polygon 解析逻辑。
- `preview` 保持可独立执行,但现在明确依赖已有 `lane_polygons.geojson`;车道文件整体缺失或格式无效属于 stage 输入错误,单个路段无法可靠匹配则跳过并写诊断。

View File

@@ -0,0 +1,23 @@
# osm2streets 车道中心线研究
## 上游实现
- 调研版本:`osm2streets-js-node 0.1.4`,上游 `osm2streets` commit `fc119c47dac567d030c6ce7c24a48896f58ed906`
- `Road::get_untrimmed_center_line()` 先根据 OSM reference line、`reference_line_placement`、道路总宽度和驾驶方向生成 road full-width centerline。
- `Road::get_lane_center_lines()``lane_specs_ltr` 从左到右累计真实 lane width再调用 `center_line.shift_from_center(total_width, width_from_left_side)` 生成每条 lane 的中心线。
- `to_lane_polygons_geojson()` 先取得上述 lane centerline再调用 `pl.make_polygons(lane.width)` 生成 Driving polygon。因此 lane polygon 是 lane centerline 的派生产物。
- `PolyLine::make_polygons()` 使用两侧等距平移和 miter 交点生成 polygon ringring 前半边与反向后的后半边一一对应,其中点可恢复原始 lane centerline。
## 当前区域审计
- `54` 个 Driving polygon`32` 个四边形,其余包含 `6/8/10/18` 个非闭合顶点。
- 使用 `network.json``road.center_line``lane_specs_ltr` 和 geom miter 算法重建所有 lane centerline。
-`scripts/lib/lane-geometry.js:laneCenterline()` 结果逐点比较,最大误差为 `0.006 m`
- 结论:道路区间的 polygon 中轴提取与 osm2streets 权威 lane centerline 一致,不是肉眼所见大偏移的来源。
## 路口 movement 限制
- osm2streets 的公开 JS API 没有导出可直接用于车辆行驶的 lane-to-lane movement centerline。
- `debugMovementsFromLaneGeojson()` 只是调试箭头:在双向 road centerline 上使用固定 `1.3 m` 偏移,再用直线连接 road endpoints它不是 lane-aware 仿真轨迹,不能复用。
- 当前项目的大偏移排查应限定在selected lane 与 internal road 的映射、junction connector、U-turn以及多条闭环 route 同时显示造成的视觉混淆。
- 后续 connector 必须显式关联 `network.json` 的 internal road/intersection并受 `intersection_surface.geojson` 约束;不得修改已验证的 lane section centerline。

View File

@@ -0,0 +1,26 @@
{
"id": "cesium-lane-centered-route",
"name": "cesium-lane-centered-route",
"title": "自动匹配 Cesium 巡航车道中心",
"description": "",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "que01",
"assignee": "que01",
"createdAt": "2026-08-08",
"completedAt": "2026-08-08",
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}