状态:契约 v2。观测结构已定(沿用公开工作并已在本机实现), 尺寸参数为本项目冻结值,文末单独列出仍待定项。 所有代码行号引自本机仓库
td25a_e2e_coverage,非论文转述。
一、先回答三个问题
| 问题 | 答案 |
|---|---|
| 格式定下来了么 | **结构定了,参数大部分冻结。**三层结构(记账 → 观测 → 可视化)沿用 ICML 2024 的公开设计且本机已有实现;分辨率 / 尺寸 / 通道顺序按 TD25A 冻结为 v2(第三节),仅两项待定 |
| 能在 DrivoR 框架里被解析利用么 | 能,且不存在"解析"步骤——它不是文件格式,是运行时 float32 栅格;DrivoR 的激光支路本来就把非自然图像栅格(2 通道 BEV 直方图)喂进 DINOv2 ViT,我们只是把 2 通道扩成 8 通道(第四节) |
| 现在的覆盖清扫每轮能获取么 | **待实机确认。**记账只依赖位姿流 + 机构 footprint,与规划器是谁无关;逻辑已存在于 shadow_node.py L193–207。是否补旁挂节点、以及历史 rosbag 能否离线回放染色,以 Jetson 上的实机检查结果为准 |
二、格式逐层解析
2.1 记账层(真身)
| 字段 | 定义 |
|---|---|
| 类型 | numpy uint16,二维数组 (H, W) |
| 栅格 | 与地图同分辨率、同原点、同尺寸(map 坐标系) |
| 每格语义 | 清扫机构扫过该格的次数(不是 0/1 布尔) |
| 染色包络 | 清扫机构幅宽 0.70 m,不是 1.23 m 全车包络——车过 ≠ 扫过 |
| 上限 | 65535 饱和保护(np.iinfo(np.uint16).max) |
| 落盘 | .npy / 16 位 PNG;ROS 侧可发 OccupancyGrid |
产生逻辑就三步(shadow_node.py L193–207):
mask_yx = np.zeros(self._visits_xy.T.shape, dtype=np.uint8)
# ← 把机构 footprint 多边形按当前位姿 fillPoly 进 mask
increment = np.logical_and(
self._visits_xy < np.iinfo(np.uint16).max, mask_yx.T > 0)
self._visits_xy[increment] += 1
输入只有两样:位姿流 + footprint 多边形。没有一行依赖策略或规划器。
2.2 观测层(网络实际吃的)
① 软覆盖:tanh(0.2 × visits)(observation.py L155)
| 扫过次数 | 值 |
|---|---|
| 0 | 0.000 |
| 1 | 0.197 |
| 2 | 0.380 |
| 3 | 0.537 |
| 5 | 0.762 |
| 10 | 0.964 |
好处两条:值域有界 [0,1),且重扫程度可分辨——二值图里"扫 1 遍"和"扫 5 遍"
长一样,重扫惩罚就没了监督信号。
② 覆盖前沿 frontier(observation.py L20–39)
frontier = dilate(已扫区, 3×3) ∩ 自由区 ∩ ¬已扫区
即"贴着已扫边界的下一圈未扫格"。障碍先做 7×7 膨胀并把图像四边置为障碍, 防止前沿贴墙。这是策略的显式"该去哪"信号,公开工作验证过有效。
③ ego 对齐(observation.py L42–91)
一个 warpAffine 复合矩阵完成三步:平移(机器人到原点)→ 旋转(90° − heading,
车头朝上)→ 平移(图心)。两个 pad_value 语义值得注意:
- coverage 图外补
0(未扫); - obstacle 图外补
1——地图之外一律视为障碍,天然防出界。
原版输出是 4 级 32×32 多尺度(每级视野 ×4);TD25A 契约改为 单级 224×224 @ 0.05 m(±5.6 m 视野),warp 机器复用,只改输出尺寸。
2.3 可视化层(人看的)

用真实 test_map(11.7×8.6 m)合成:第 2 条带扫了两遍(更亮),
第 7 条带走到一半停下(模拟被动态障碍打断)。配色约定:
绿 = 已扫(亮度 = 重扫)· 紫 = 覆盖前沿 · 黄 = 中心轨迹 · 红 = 机器人 · 青 = 可达域。
三、契约冻结表(v2)
| 项 | 冻结值 | 依据 |
|---|---|---|
| 分辨率 | 0.05 m/格 | 对齐现有 local costmap |
| 观测尺寸 | 224×224(±5.6 m) | 被 patch-14 整除 → 16×16=256 patch;覆盖约 3.7 条清扫带 |
| 坐标系 | ego 对齐,车头朝上 | 沿用公开实现 |
| 通道顺序 | 本支路 3 个:[静态图, tanh 覆盖, frontier] 与雷达 5 通道合并成 8 |
固定,训练推理一致 |
| 值域 | float32 ∈ [0,1] | 不套 ImageNet 归一化 |
| 染色包络 | 机构幅宽 0.70 m | 车过 ≠ 扫过 |
| 更新率 | 记账 10 Hz,观测随策略 2–3 Hz | 记账便宜,观测按需 |
| 覆盖输出 | 永不由网络输出,几何记账回写 | 防幻觉覆盖 |
2026-08-26 变更:原契约的第二通道「实时占据」已砍。它取自 local costmap, 带
inflation_radius: 0.55(= 11 格)的膨胀,会把桌腿间隙这类细结构抹平—— 而这正是雷达高度分档要暴露的东西。实时占据由occ_a..c承担(无膨胀、带高度分辨)。 详见雷达篇 4.5。
仍待定(仅两项):多尺度一期砍还是留作消融;第 5 通道"意图层" (目标带高亮栅格)是否并入本支路。
四、在 DrivoR 框架里怎么接
DrivoR 自己就有"非自然图像栅格 → 预训练 ViT"的先例——它的激光支路:
# drivoR.yaml(原样)
lidar_backbone:
model_name: timm/vit_small_patch14_reg4_dinov2.lvd142m
in_chans: 2 # 2 通道 BEV 直方图,不是 RGB
use_lora: true
我们的全部改动:
lidar_pc: [0] # 打开支路(原默认关闭)
lidar_min_x: -5.6 # ±35 m → ±5.6 m(原为高速尺度,13.5 cm/格连带边都分不清)
lidar_max_x: 5.6
lidar_min_y: -5.6
lidar_max_y: 5.6
lidar_image_size: [224, 224]
lidar_backbone:
in_chans: 8 # 本支路 3:[静态图, tanh 覆盖, frontier]
# 雷达 5:[occ_a, occ_b, occ_c, z_min, z_max]
配三条工程注意:
patch_embed 通道手术:不用自己做。实测timm.create_model(..., in_chans=8)会自动适配预训练卷积核,DrivoR 代码里也已经在in_chans != 3时把patch_embed/pos_embed置为可训练;- 归一化:栅格通道本来就在 [0,1],不要套相机支路的 ImageNet mean/std;
- 诚实的折扣:DINOv2 的预训练收益(+15 PDMS)是在自然图像上量出来的, 栅格通道上会打折,LoRA 补一部分——论文里不要把这个数字原样搬到本支路。
出口与其他支路完全一致:16 个 register token,与相机 token、ego token 拼接后进 AR 解码器交叉注意力。数据流全程无文件、无序列化、无"解析":
visits(uint16) ─tanh→ float32 ─warpAffine→ 224² ego 对齐 ─与雷达 5 通道堆成 8→ ViT ─→ 16 token
↑ 本机现有代码 ↑ 本机现有代码(改输出尺寸) ↑ DrivoR 现有代码(改 in_chans)
参考
- Jonnarth, Zhao & Felsberg. Learning Coverage Paths in Unknown Environments with Deep Reinforcement Learning. ICML 2024, PMLR 235:22491–22508(观测设计出处)
- Jonnarth, Johansson, Zhao & Felsberg. Sim-to-Real Transfer of Deep Reinforcement Learning Agents for Online Coverage Path Planning. IEEE Access 13:106883–106905, 2025(tanh 软覆盖与半虚拟环境)
- Kirby, Boulch, Xu et al. Driving on Registers. CVPR 2026(激光支路先例)
- 本机:
td25a_e2e_coverage/observation.py·shadow_node.py·navsim/agents/drivoR/drivor_features.py