chore: initial commit — import worldmodel workspace (plans/, research/)
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
# Chapter 18 — Lyra 2.0 启发的设计原则
|
||||
|
||||
> 本章目标:把 Lyra 2.0(Self-Distillation Bootstraps 3D World Models from 2D Video Diffusion, 2025)译读 [`research/lyra2_review.md`](../../research/lyra2_review.md) 第 8.4 节归纳出的 **3 条核心思想** 正式纳入 PRISM 设计哲学——分别约束 **L2 ↔ L3 ↔ L4 的写入语义**、**SpatialMemory 的证据保留策略** 与 **Pipeline D 的巩固训练范式**。本章是 PRISM v1.5.0 的新增内容。
|
||||
|
||||
---
|
||||
|
||||
## 18.1 章节目的
|
||||
|
||||
Lyra 2.0 是 2025 年 NVIDIA 用 2D 视频扩散模型"自蒸馏"出一个 3D 世界模型的工作;它的"3D 缓存"在工程上和 PRISM 的"空间记忆"是**同类问题**:都需要在多帧观测之间保留 3D 一致性、又都必须容忍**几何精度不够好**这一事实。Lyra 没有 SLAM、没有 TSDF,却用三个看似反直觉的工程决策——**几何只做路由、per-frame 缓存绝不融合、巩固期用自己的不完美输出再训练**——把"精度不够"的几何先验稳稳压在了"够用"的工作点上。这些决策与 PRISM 当下的痛点(L2 漂移、L3 写入污染、Pipeline D 缺少自监督信号)高度对应,因此值得作为**独立章节**沉淀下来,避免在后续 v1.x 演进中被遗忘或被工程惯性悄悄抹去。本章按"原则陈述 → 工程对应 → 风险约束"的顺序组织:每条原则先讲 Lyra 怎么做、再讲 PRISM 该怎么对应,最后在 § 18.6 集中说明哪些地方**不**能照搬。更深的技术细节请回看 [`research/lyra2_review.md`](../../research/lyra2_review.md) § 3.2(3D 缓存设计)与 § 3.3(self-augmentation 公式)。
|
||||
|
||||
---
|
||||
|
||||
## 18.2 原则一:几何只做路由,不做合成
|
||||
|
||||
### 18.2.1 问题描述
|
||||
|
||||
PRISM v1.4 之前的写入逻辑里,L2 既要"提供占据栅格供避障",又要"把局部几何投到 L3 节点的子图里当内容"。这意味着 **L2 的几何误差会污染 L3 的内容**——例如 TSDF 在长走廊里漂了 30 cm,L3 中"走廊节点"挂的那块点云也跟着歪 30 cm,后续 L4 的 bbox 估计也会跟着歪。
|
||||
|
||||
Lyra 2.0 § 3.2(a) 的做法给出了一个干净的解耦:**3D 缓存只用来"挑选哪一帧 keyframe 该被注意到",真正的内容(颜色、纹理、语义)由原始 2D 帧的 cross-attention 提供**。换言之,几何只回答"路由问题"(who should write where),不回答"内容问题"(what to write)。
|
||||
|
||||
### 18.2.2 PRISM 旧逻辑 vs PRISM 新逻辑
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph OLD["PRISM v1.4 旧:L2 既路由又写 L3"]
|
||||
direction LR
|
||||
O_L1["L1 keyframe<br/>(rgb+depth)"]
|
||||
O_L2["L2 TSDF/Octomap"]
|
||||
O_L3["L3 节点子图"]
|
||||
O_L1 --> O_L2
|
||||
O_L2 -- "投影点云<br/>(内容)" --> O_L3
|
||||
O_L2 -- "路由<br/>(选哪个节点)" --> O_L3
|
||||
end
|
||||
subgraph NEW["PRISM v1.5 新:L2 只路由,L3 内容来自 L1"]
|
||||
direction LR
|
||||
N_L1["L1 keyframe<br/>(rgb+depth)"]
|
||||
N_L2["L2 TSDF/Octomap<br/>(几何精度允许低)"]
|
||||
N_L3["L3 节点子图"]
|
||||
N_L1 --> N_L2
|
||||
N_L2 -- "路由<br/>(only: select node)" --> N_L3
|
||||
N_L1 -. "内容<br/>(keyframe ref)" .-> N_L3
|
||||
end
|
||||
style OLD fill:#fde2e2,stroke:#a33
|
||||
style NEW fill:#d4f0d4,stroke:#2e7d32
|
||||
```
|
||||
|
||||
关键差异:**新逻辑下 L3 节点存的不是"L2 几何投影出来的点云",而是"指向 L1 keyframe 的引用 + L2 给出的路由决策"**。L2 即便漂 30 cm,只要它能正确挑出"这一帧属于走廊节点而不是卧室节点",下游就不受影响——内容由 L1 原始观测兜底。
|
||||
|
||||
### 18.2.3 伪代码
|
||||
|
||||
```python
|
||||
# prism/write/route_l3.py (v1.5 新增)
|
||||
def route_and_write_l3(
|
||||
keyframe: L1Keyframe, # 原始观测:rgb + depth + pose
|
||||
l2: MetricLayer, # 可能有漂移的 TSDF/Octomap
|
||||
l3: TopologicalLayer, # 节点 + 边
|
||||
) -> None:
|
||||
"""
|
||||
几何只做路由:用 L2 决定 keyframe 该挂到哪个 L3 节点;
|
||||
L3 的"内容"始终是 keyframe 的引用,不复制几何。
|
||||
"""
|
||||
# ---- 步骤 1: 用 L2 做路由(几何精度低也无妨) ----
|
||||
pose_xy = keyframe.pose.translation[:2]
|
||||
candidate_nodes = l2.query_room_id(pose_xy) # 返回若干候选节点
|
||||
node_id = l3.disambiguate(candidate_nodes, keyframe) # 用 keyframe 视觉再确认
|
||||
|
||||
# ---- 步骤 2: L3 只写引用 + 路由元数据,不写几何 ----
|
||||
l3.nodes[node_id].keyframe_refs.append(keyframe.id)
|
||||
l3.nodes[node_id].routing_log.append({
|
||||
"kf_id": keyframe.id,
|
||||
"l2_pose": pose_xy.tolist(),
|
||||
"l2_uncert": l2.local_uncertainty(pose_xy), # 路由置信度
|
||||
"ts": keyframe.ts,
|
||||
})
|
||||
|
||||
# ---- 步骤 3: 内容查询时,按需从 L1 读回 ----
|
||||
# (L3.get_content(node_id) 实现里只做 L1Keyframe 的解引用,
|
||||
# 不缓存任何 L2 投影点云。这是与旧版的根本区别。)
|
||||
```
|
||||
|
||||
设计含义:**L2 漂移不再是 L3 的故障,而只是 L3 路由的噪声**——可以通过 § 18.3 的 keyframe 独立证据来事后翻案,而不必反向修补 L2。另一个常被忽视的好处是:当未来引入 NeRF / 3DGS 之类的精细几何表示时,它们也只是 L3 的"另一种内容引用",**不会与 L2 抢"几何真相代言人"的位置**——L2 始终只代表"路由用的粗几何",L3 内容由谁提供、提供得多精细,是写入策略的自由度而非架构约束。这一点对应 Lyra § 3.2(d) 中 canonical coordinates **只参与 Q/K 不参与 V** 的设计:粗坐标只用于"挑出谁该被注意到(Q/K 路由)",但模型实际看到的内容(V)来自原始 2D 帧。
|
||||
|
||||
详见对 [`plans/PRISM/03_data_schema.md`](03_data_schema.md) 与 [`plans/PRISM/06_pipeline_C_online_perception.md`](06_pipeline_C_online_perception.md) 的影响(§ 18.5 总表)。
|
||||
|
||||
---
|
||||
|
||||
## 18.3 原则二:Per-frame 独立 keyframe 证据,而非全局融合
|
||||
|
||||
### 18.3.1 问题描述
|
||||
|
||||
PRISM 现行的 L2 是**融合表示**:TSDF 把每一帧深度都"加权累计"到体素里、OctoMap 把每一帧 ray 都更新到八叉树占据概率上。融合带来两个好处(紧凑、平滑)和一个致命缺点:**一旦累计了漂移,原始证据就不可逆地丢了**——你拿不回"第 t=12.3 s 那一帧 ZED 看到桌子在哪",因为它已经被融进了 240 万个体素的加权平均里。
|
||||
|
||||
Lyra 2.0 § 3.2(a) 把这点写得很明白:"**3D 缓存绝不融合(never fuse)**"——每个 keyframe 是一条独立的、可追溯的证据,模型在 cross-attention 时挑出最相关的几条 keyframe,而不是查询一个"已经融合过的全局体素"。
|
||||
|
||||
### 18.3.2 三种策略对比
|
||||
|
||||
| 维度 | OctoMap-only(PRISM v1.0) | TSDF-only(PRISM v1.3) | Lyra-style per-frame | **PRISM v1.5 选择** |
|
||||
|------|---|---|---|---|
|
||||
| 表示 | 八叉树占据概率 | 体素 SDF | keyframe 列表(pose + depth + 特征) | **TSDF(路由)+ keyframe 列表(证据)** |
|
||||
| 漂移可逆 | ✗(已融合) | ✗(已融合) | ✓(每帧独立) | ✓(keyframe 兜底) |
|
||||
| 内存 | O(体素数) | O(体素数) | O(帧数 × 单帧大小) | **O(体素 + 关键帧子集)** |
|
||||
| 避障查询 | 快(直接查体素) | 中 | 慢(要做近邻) | **快(用 TSDF)** |
|
||||
| "翻案"能力 | 无 | 无 | 强 | **强(保留 keyframe)** |
|
||||
| 写入语义 | 累加 | 加权融合 | append-only | **append-only on keyframes** |
|
||||
|
||||
PRISM v1.5 不是把 TSDF 扔掉,而是**让 TSDF 和 keyframe 列表并存**:TSDF 负责 § 18.2 的"路由 + 避障"两个对几何精度要求不高的任务;keyframe 列表负责"翻案"——当 L4 发现某个语义节点和 L3 节点对不齐时,可以回退到 keyframe 重新估计 bbox。
|
||||
|
||||
### 18.3.3 SpatialMemory 新增字段 schema
|
||||
|
||||
```python
|
||||
# prism/schema/spatial_memory.py (v1.5 新增字段)
|
||||
@dataclass
|
||||
class KeyframeEvidence:
|
||||
"""
|
||||
每个 L1 keyframe 在巩固后保留的"独立证据"。
|
||||
一律 append-only,绝不被 L2 融合操作覆盖。
|
||||
"""
|
||||
kf_id: str # 全局唯一,匹配 L1.frames[i].id
|
||||
ts: float # 采集时间戳 (epoch sec)
|
||||
pose: SE3 # 该帧位姿 (世界系)
|
||||
pose_uncert: np.ndarray # 6x6 协方差 (路由置信度的来源)
|
||||
depth_ref: str # 指向 L1 存储的深度图 blob (lazy load)
|
||||
feat_ref: str # 指向 L1 存储的视觉特征 blob (DINO/CLIP)
|
||||
routed_l3: Optional[str] # 当时路由到的 L3 节点 id (可空)
|
||||
routed_l4: List[str] # 当时关联的 L4 实体 id 列表
|
||||
fused_into_l2: bool = False # 是否曾参与 L2 TSDF 融合
|
||||
immutable: bool = True # 一律 True;写入后不可被覆盖
|
||||
|
||||
@dataclass
|
||||
class SpatialMemory:
|
||||
l1: L1Buffer
|
||||
l2: MetricLayer
|
||||
l3: TopologicalLayer
|
||||
l4: SemanticGraph
|
||||
# ---- v1.5 新增 ----
|
||||
keyframe_evidence: List[KeyframeEvidence] # append-only,巩固期挑选关键帧入库
|
||||
```
|
||||
|
||||
实现要点:
|
||||
1. **append-only**:`KeyframeEvidence` 一旦写入即 `immutable=True`,禁止任何 pipeline 反向修改;
|
||||
2. **lazy load**:`depth_ref / feat_ref` 只存路径,原始张量留在 L1 的 zarr/parquet 里,避免内存爆掉;
|
||||
3. **不是全部 keyframe 都进**:Pipeline D 巩固期会按"信息增益"挑一个子集(如每 0.5 m 或每 30° 视角变化保留一帧),典型规模 200–2000 帧/场景。
|
||||
|
||||
详见 § 18.5 对 [`plans/PRISM/03_data_schema.md`](03_data_schema.md) 的影响。
|
||||
|
||||
---
|
||||
|
||||
## 18.4 原则三:巩固期的 Self-Augmentation
|
||||
|
||||
### 18.4.1 问题描述
|
||||
|
||||
Pipeline D(巩固期,离线 / 夜间)目前的逻辑是:拿白天采集的 L1 keyframe,运行更慢更准的离线管线(高分辨率 depth、bundle adjustment、全局优化),把结果写回 L3/L4。这个过程**没有自监督信号**——它假设"离线版的输出就是 ground-truth",但实际工程中离线版本身也会犯错,且模型从不学会"识别并纠正自己之前的错"。
|
||||
|
||||
Lyra 2.0 § 3.3(b) 给了一个简洁的 self-augmentation 公式:**用模型自己之前的不完美输出作为输入,用干净的 ground-truth 作为监督**。具体地,对训练样本的潜变量 $z$,以概率 $p_{\text{aug}}=0.7$ 做扰动:
|
||||
|
||||
$$
|
||||
\tilde z_0 = z_t - t \cdot v_\theta(z_t, t, c), \quad t \sim \mathcal{U}(0, 0.5)
|
||||
$$
|
||||
|
||||
其中 $v_\theta$ 是当前正在训练的流匹配模型自己。这相当于让模型把"自己一步去噪后的结果"当成新输入,再去预测真值——模型在训练中**反复见到自己会犯的错**,因此学会了自我纠错。
|
||||
|
||||
### 18.4.2 在 PRISM 巩固期的对应
|
||||
|
||||
把上面的范式翻译到 PRISM Pipeline D:
|
||||
- **z**:当前场景的 L3/L4 表示(拓扑节点 + 场景图 bbox);
|
||||
- **不完美输入 $\tilde z_0$**:用 Pipeline C 在线版(实时但粗)的 L3/L4 输出;
|
||||
- **ground-truth 监督**:用 Pipeline A 离线版(慢但准)的 L3/L4 输出。
|
||||
|
||||
模型(这里指 L3/L4 的精炼网络,未来 v2.0 的研究方向)学会的不是"从零产生 L3/L4",而是"**从一份带错的 L3/L4 + 当时的 keyframe 证据**,产生干净的 L3/L4"——这恰好就是"巩固"的本质。
|
||||
|
||||
### 18.4.3 伪代码
|
||||
|
||||
```python
|
||||
# prism/pipeline_d/self_aug.py (v1.5 新增,实际实现见 v2.0 路线图)
|
||||
def consolidate_with_self_aug(
|
||||
online_l3l4: SceneRepr, # Pipeline C 的不完美输出
|
||||
offline_l3l4: SceneRepr, # Pipeline A 的"较干净"输出 (监督信号)
|
||||
kf_evidence: List[KeyframeEvidence],
|
||||
refiner: RefinerNet, # 待训练的精炼网络
|
||||
p_aug: float = 0.7,
|
||||
t_max: float = 0.5,
|
||||
) -> RefinerNet:
|
||||
"""
|
||||
巩固期 self-augmentation:
|
||||
以 p_aug 概率用模型自己产生的"伪不完美样本"替换 online_l3l4,
|
||||
用 offline_l3l4 作为干净监督。
|
||||
"""
|
||||
for batch in iterate_scenes():
|
||||
# ---- 1. 决定本步是否使用 self-aug ----
|
||||
if np.random.rand() < p_aug:
|
||||
# 用模型自己生成一个"扰动版"的输入
|
||||
t = np.random.uniform(0.0, t_max)
|
||||
z_clean = encode(offline_l3l4) # 干净潜变量
|
||||
z_t = add_flow_noise(z_clean, t) # 沿流方向加噪
|
||||
v_pred = refiner.predict_velocity(z_t, t, kf_evidence)
|
||||
z_tilde = z_t - t * v_pred # 模型一步去噪结果
|
||||
x_in = decode(z_tilde) # 解回 L3/L4 表示
|
||||
else:
|
||||
# 1 - p_aug = 0.3 概率用真实的"在线不完美输出"
|
||||
x_in = online_l3l4
|
||||
|
||||
# ---- 2. 统一的监督:始终向 offline 版对齐 ----
|
||||
x_pred = refiner(x_in, kf_evidence) # keyframe 证据可被检索
|
||||
loss = scene_repr_loss(x_pred, offline_l3l4)
|
||||
loss.backward(); refiner.step()
|
||||
|
||||
return refiner
|
||||
```
|
||||
|
||||
### 18.4.4 关于 $p_{\text{aug}} = 0.7$ 的取值讨论
|
||||
|
||||
Lyra 2.0 实验里取 $p_{\text{aug}}=0.7$ 而非 0.5 或 1.0,有两个工程意涵值得我们沿用:
|
||||
|
||||
1. **0.7 > 0.5 表示模型主要见到的是"自己生成的扰动样本"**——这是它真正会犯的错;如果 $p$ 太低(如 0.3),模型大部分时间在做"干净到干净"的精炼,巩固期就退化成普通监督学习,失去自我纠错能力。
|
||||
2. **0.7 < 1.0 留了 30% 真实的"在线不完美样本"**——这部分确保模型不会沉迷于自己的失败模式,仍然能见到 Pipeline C 在真实工况下产生的、分布与 self-aug 不一样的错(如 ZED 在暗光下的深度毛刺,模型自己生成的样本里不一定包含)。
|
||||
|
||||
经验上:当 Pipeline C 已经相当稳定(错误率低且类型集中)时,可降到 0.5;当 Pipeline C 仍在快速演化、错误模式多样时,可推到 0.8。PRISM v1.5 起步采用 **0.7**,作为后续 ablation 的中点。
|
||||
|
||||
$t \sim \mathcal{U}(0, 0.5)$ 的含义是:只在流匹配的"前半程"加噪——避免把样本扰动到几乎全噪声,那样监督信号会失效。
|
||||
|
||||
详见 § 18.5 对 [`plans/PRISM/07_pipeline_D_consolidation.md`](07_pipeline_D_consolidation.md) 的影响。
|
||||
|
||||
---
|
||||
|
||||
## 18.5 与 PRISM 现有章节的关系
|
||||
|
||||
本章的三条原则并不是"另起炉灶",而是**约束既有章节在 v1.5 及之后的演进方向**。下表列出影响面:
|
||||
|
||||
| 本章原则 | 影响的现有章节 | 具体影响内容 | 回链 |
|
||||
|---|---|---|---|
|
||||
| § 18.2 几何只做路由 | Chapter 02 架构 | L2 ↔ L3 边的语义从"投影内容 + 路由"收窄为"只做路由" | [`plans/PRISM/02_architecture.md`](02_architecture.md) |
|
||||
| § 18.2 几何只做路由 | Chapter 03 数据 schema | `L3Node.content` 从"几何点云"改为"keyframe 引用列表" | [`plans/PRISM/03_data_schema.md`](03_data_schema.md) |
|
||||
| § 18.2 几何只做路由 | Chapter 06 Pipeline C 在线 | `route_and_write_l3()` 替换原"投影 + 写内容"两步 | [`plans/PRISM/06_pipeline_C_online_perception.md`](06_pipeline_C_online_perception.md) |
|
||||
| § 18.3 keyframe 独立证据 | Chapter 03 数据 schema | 新增 `SpatialMemory.keyframe_evidence: List[KeyframeEvidence]` 字段 | [`plans/PRISM/03_data_schema.md`](03_data_schema.md) |
|
||||
| § 18.3 keyframe 独立证据 | Chapter 13 评估 | 新增"翻案能力"指标:被 keyframe 推翻的 L4 bbox 占比 | [`plans/PRISM/13_evaluation.md`](13_evaluation.md) |
|
||||
| § 18.4 self-augmentation | Chapter 07 Pipeline D 巩固 | 巩固期训练范式从"监督学习"升级为"self-aug 监督学习" | [`plans/PRISM/07_pipeline_D_consolidation.md`](07_pipeline_D_consolidation.md) |
|
||||
| § 18.4 self-augmentation | Chapter 13 评估 | 新增"巩固后纠错率"指标:online → offline 之间被纠正的错误数 | [`plans/PRISM/13_evaluation.md`](13_evaluation.md) |
|
||||
|
||||
上述章节本次 v1.5.0 升级**暂不强制改动**——本章先以"设计原则"形式独立存在;后续 v1.6 / v2.0 在演进对应章节时,再逐条把约束落地。
|
||||
|
||||
---
|
||||
|
||||
## 18.6 风险与限制
|
||||
|
||||
本章列出的三条原则源自 Lyra 2.0 的工程实践,但 Lyra 与 PRISM 在**问题设定上有根本差异**,因此必须把"借鉴"与"复制"严格区分:
|
||||
|
||||
1. **Lyra 是生成式,PRISM 是判别式 + 真实传感器**。Lyra 的"3D 缓存"服务于像素生成(视频扩散解码),它的几何粗糙是可以容忍的,因为下游 cross-attention 会用 2D 帧把内容补回;PRISM 的 L2 服务于**机器人避障**,几何粗糙的容忍度有刚性下限(栅格分辨率不能粗于半个机器人宽度)。**§ 18.2"几何只做路由"不能被滥用为"几何精度可以任意低"**——它只是说"L2 精度不要影响 L3 内容",并不是说"L2 精度可以任意差到避障也失败"。
|
||||
2. **Lyra 的训练数据是合成的(视频扩散先验),PRISM 的数据是真实 ZED / iPhone**。Self-augmentation 在 Lyra 里之所以好用,部分是因为合成数据的"分布"和模型 capacity 是匹配的;PRISM 的真实数据带有显著的传感器噪声(ZED 在反光面、低纹理面会大面积失败),把模型自己生成的样本当训练源,**可能把传感器特有的失败模式当成"正常分布"学下来**。§ 18.4 中保留 30% 真实在线样本($1 - p_{\text{aug}} = 0.3$)就是为了对冲这个风险,但仍需要在 Pipeline D 的实施中加 ablation 验证。
|
||||
3. **per-frame 证据保留有存储代价**。Lyra 的 keyframe 数量受限于扩散模型的 context 长度(数十帧);PRISM 在长期部署中一个酒店场景可能积累数万帧。§ 18.3 的"保留子集"策略(每 0.5 m 或每 30° 视角一帧)是经验值,**长期边界尚未被验证**——可能需要 v2.0 引入"keyframe 退役 / 压缩"机制,否则会成为新的内存瓶颈。
|
||||
4. **原则之间也存在张力**。"§ 18.2 只用 L2 路由"要求 L2 几何只承担轻任务;"§ 18.3 保留 keyframe 翻案"要求事后能纠正 L4——但**到底何时触发翻案?由谁判定 L4 出错?** 当前章节未给出闭环规则,留给 [`plans/PRISM/13_evaluation.md`](13_evaluation.md) 在新增"翻案能力"指标时配套定义。
|
||||
|
||||
综上,本章是 PRISM 在 v1.5 节点上对 Lyra 2.0 的**有节制的吸收**:原则被写下来,但落地节奏由后续章节按版本逐步推进,确保每一条都经过实验回归而非仅凭直觉。一个简明的检验标准是:v1.6 在改动 [`plans/PRISM/03_data_schema.md`](03_data_schema.md) 引入 `keyframe_evidence` 字段时,必须同步在 [`plans/PRISM/13_evaluation.md`](13_evaluation.md) 加入"翻案命中率"与"翻案误报率"两项指标——只有这两个数能稳定测出来,§ 18.3 的原则才算真正落地;否则就只是"看上去更优雅"的架构装饰。Self-aug 的 $p_{\text{aug}}$ 与 § 18.2 路由置信度阈值同理,必须以**可被 ablation 推翻的形式**写进评估章节,本章才算完成它的设计哲学使命。
|
||||
|
||||
---
|
||||
|
||||
**章节版本**:v1.0(新增于 PRISM v1.5.0)
|
||||
**估计阅读时间**:8–10 分钟
|
||||
**关键收获**:几何只做路由 / 不做合成;keyframe 独立证据;巩固期 self-aug。
|
||||
Reference in New Issue
Block a user