Skip to the content.

第15章 导航案例研究:从语义地图到自监督可穿越性

原课程: Robots That Learn, UC Berkeley CS294-291, Spring 2026 主讲: Jitendra Malik; 嘉宾: Jonas Frey (ETH Zurich) 本章基于 Lecture 10: Navigation Case Studies 精读论文: Chang et al., “GOAT: GO to Any Thing,” RSS 2024; Frey et al., “Fast Traversability Estimation for Wild Visual Navigation,” RSS 2023


一、本章概要

核心问题: 移动机器人在真实世界(家庭、仓库、森林、草地)中自主导航,面临两个根本性挑战:(1)如何理解人类以多种模态(类别标签、图像、自然语言)表述的目标并规划到达路径?(2)如何在没有预标注数据的情况下,让机器人快速判断前方地形的可穿越性(traversability),而非仅依赖不可靠的几何障碍检测?

为什么这个问题现在变得紧迫: 2023-2024年,两个趋势交汇了。一方面,大语言模型(LLM)和视觉-语言模型(VLM)使机器人具备了”听懂人话”和”看懂图片”的能力;另一方面,四足机器人(如 Boston Dynamics Spot、ANYbotics ANYmal)的移动能力已超出了传统几何导航范式的假设——它们可以穿越高草丛、碎石、树枝,而这些地形在激光雷达的点云中看起来像”障碍物”。传统 SLAM + 占据栅格地图的方法无法在语义层面区分”一棵不能穿过的树”和”一片可以穿过的高草”。导航问题已经从”在哪里”升级为”去做什么”和”能不能走”。

本章路线图: 我们精读两篇 RSS 旗舰会议的导航案例研究论文。第一篇是 Chang et al. (2024) 的 GOAT(GO to Any Thing)——一个模块化的、终身学习的、多模态室内导航系统,核心创新是”对象实例记忆”(Object Instance Memory)和基于多模态匹配的全局策略。第二篇是 Frey et al. (2023) 的 Wild Visual Navigation (WVN)——一个纯视觉的、在线自监督的室外可穿越性估计系统,核心创新是用 DINO-ViT 视觉特征 + 本体感觉的自监督信号在数分钟内教会机器人哪些地形可以走。两篇论文分别代表了导航研究的两个前沿方向:基于地图的语义导航(map-based semantic navigation)和无地图的自适应可穿越性(map-less adaptive traversability)。

与前后章节的逻辑关系: 第7章(强化学习)提供了端到端策略学习的基础,但 GOAT 和 WVN 都展示了模块化方法在真实世界部署中的实用优势——将感知(视觉模型)、记忆(语义地图/图结构)、规划(经典的寻路算法)和学习(在线微调)解耦。第14章(运动控制案例)解决了”怎么走”的问题,本章解决的是”往哪走”和”能不能走”的问题。两章共同构成完整的自主移动能力栈。


二、核心概念与数学基础

2.1 导航问题的形式化定义

我们首先建立导航问题的统一数学框架,使其能同时涵盖 GOAT 的室内目标导航和 WVN 的野外可穿越性导航。

定义 1(具身导航问题,Embodied Navigation Problem):在未知环境 \(\mathcal{E}\) 中,机器人从初始位姿 \(x_0 \in SE(3)\) 出发。在每个时间步 \(t\),机器人接收传感器观测 \(o_t = (I_t^{\text{RGB}}, I_t^{\text{depth}}, x_t^{\text{pose}})\),其中 \(I_t^{\text{RGB}} \in \mathbb{R}^{H \times W \times 3}\) 是 RGB 图像,\(I_t^{\text{depth}} \in \mathbb{R}^{H \times W}\) 是深度图像,\(x_t^{\text{pose}} \in SE(3)\) 是由机载 SLAM 系统估计的位姿。导航目标是找到一个策略 \(\pi\),使其能够输出动作序列 \(\{a_t\}\),令机器人到达目标 \(g\),其中 \(g\) 的规范方式因任务而异。

多模态目标空间: GOAT 的核心洞察之一是目标 \(g\) 可以来自不同的模态空间: \(g \in \mathcal{G} = \mathcal{G}_{\text{cat}} \cup \mathcal{G}_{\text{img}} \cup \mathcal{G}_{\text{lang}}\)

其中:

终身学习范式: GOAT 将一个 episode 定义为连续 \(N\) 个目标的序列 \(\{g_1, g_2, \ldots, g_N\}\)。关键约束是:当机器人搜索 \(g_{k+1}\) 时,它已经拥有了搜索 \(g_1, \ldots, g_k\) 过程中积累的对环境的知识。这使得机器人性能应从首个目标的 60% 成功率提升到环境充分探索后的 90%。

2.2 语义地图表示:从占据栅格到实例感知记忆

GOAT 的核心数据结构是语义地图 \(m_t \in \mathbb{Z}^{K \times M \times M}\),其中 \(M \times M\) 是俯视栅格地图的尺寸(每个栅格对应物理世界 5cm \(\times\) 5cm),\(K = C + 4\) 是通道数:

语义地图通过投影操作从 RGB-D 观测中增量构建。在时刻 \(t\),首先在 RGB 图像 \(I_t\) 上运行 Mask R-CNN [He et al., 2017] 进行实例分割,得到每个物体的检测框 \(b_i\)、语义类别 \(c_i\)、实例掩码 \(\text{mask}_i\)。然后利用深度图像 \(I_t^{\text{depth}}\) 将每个像素反投影为 3D 点云:

\[\mathbf{p}^{\text{world}} = T_t^{\text{cam} \to \text{world}} \cdot \pi^{-1}(u, v, d)\]

其中 \(\pi^{-1}\) 是相机内参的反投影函数,\(T_t^{\text{cam} \to \text{world}}\) 是将相机坐标系变换到世界坐标系的齐次变换矩阵。投影后的点云被离散化到俯视栅格中。

实例关联(关键创新): 当新的检测物体 \(o_i\) 被投影到地图上时,GOAT 不是简单地覆盖栅格,而是检查其投影位置是否与已有物体 \(o_j\) 的空间位置重叠。如果重叠,则判定为同一物体实例,并将包含该物体的新视角图像追加到该实例的图像列表中。这种对象实例记忆(Object Instance Memory)是 GOAT 区别于以往语义导航方法(如 [Gervet et al., 2023])的本质特征。

2.3 多模态目标编码与匹配

当新目标 \(g_{k+1}\) 被指定时,GOAT 的全局策略 \(\pi_G\)(全局策略)在对象实例记忆中搜索匹配的物体。匹配算法是模态特定的:

类别目标匹配(\(\mathcal{G}_{\text{cat}}\)): 直接在语义地图中搜索最近的目标类别物体实例:

\[i^* = \arg\min_{i: \text{cat}(o_i) = g} \text{dist}(x_t^{\text{robot}}, \text{loc}(o_i))\]

这里 \(\text{dist}(\cdot, \cdot)\) 是地图上经过快速行进法(Fast Marching Method)计算的测地距离,而非欧几里得距离。

语言目标匹配(\(\mathcal{G}_{\text{lang}}\)): 首先用大语言模型(GOAT 使用 Mistral 7B [Jiang et al., 2023])从语言描述中提取目标类别:\(c = \text{LLM}(g_{\text{lang}})\)。然后将语言描述的 CLIP 特征 [Radford et al., 2021] 与对象实例记忆中每个该类物体的所有视角图像的 CLIP 特征逐一比较:

\[s_{\text{lang}}(g, o_i) = \max_{I \in \text{views}(o_i)} \cos \left( \mathbf{f}_{\text{CLIP}}(g), \mathbf{f}_{\text{CLIP}}(I) \right)\]

其中 \(\mathbf{f}_{\text{CLIP}}(\cdot) \in \mathbb{R}^{512}\) 是 CLIP ViT-B/32 的图像/文本编码器的输出嵌入。

图像目标匹配(\(\mathcal{G}_{\text{img}}\)): 与语言目标类似,先用 Mask R-CNN 从目标图像中提取类别 \(c\)。然后使用 SuperGlue 网络 [Sarlin et al., 2020] 进行图像关键点匹配:

\[s_{\text{img}}(g, o_i) = \max_{I \in \text{views}(o_i)} \vert \text{SuperGlue}(g, I)\vert\]

SuperGlue 是一个基于图神经网络的特征匹配网络,它学习从两幅图像的关键点集合中建立对应关系。匹配点数量越多,说明同一物体实例在不同视角下被观察到的证据越强。

匹配阈值与探索决策: 在环境探索阶段,GOAT 使用预定义的相似度阈值(CLIP: 0.28,SuperGlue: 6.0)来判断匹配是否足够可靠。如果没有任何物体实例的匹配分数超过阈值,全局策略输出探索目标——使用基于前沿的探索(frontier-based exploration)选择最近的未探索区域作为导航子目标。当环境被充分探索后,策略简单地选择匹配分数最高的物体实例。这种”探索 vs 利用”的自动切换机制是 GOAT 终身学习能力的核心。

2.4 可穿越性估计:从语义到异常检测

与 GOAT 的室内语义导航形成鲜明对比,WVN(Wild Visual Navigation)处理的是室外自然环境中没有任何语义标签的可穿越性估计问题。

可穿越性得分的形式化: WVN 将可穿越性建模为连续值 \(\tau \in [0, 1]\),其中 \(\tau = 0\) 表示完全不可穿越,\(\tau = 1\) 表示完全可穿越。其核心监督信号来自本体感觉——具体来说,是机器人当前估计速度 \(\mathbf{v} = (v_x, v_y)\) 与外部参考速度指令 \(\bar{\mathbf{v}} = (\bar{v}_x, \bar{v}_y)\) 之间的差异。直觉是:当机器人在容易穿越的地形上运动时,它应当紧密跟踪参考指令;当地形变得困难时,跟踪误差会增大。

\[\begin{aligned} v_{\text{error}} &= \frac{1}{2}\left[(v̄_x - v_x)^2 + (v̄_y - v_y)^2\right] \in \mathbb{R} \quad &\text{(公式 1)} \\ \tau &= \sigma\big(-k(v_{\text{error}} - v_{\text{thr}})\big) \quad &\text{(公式 2)} \end{aligned}\]

其中 \(\sigma(\cdot)\) 是 sigmoid 函数,\(k\) 控制陡峭度,\(v_{\text{thr}}\) 是 sigmoid 的中点(\(v_{\text{thr}}\) 处的可穿越性得分为 0.5)。这两个参数需要根据每个机器人平台的运动规格进行标定。

为什么速度跟踪误差是好的监督信号: (1)它不需要任何外部传感器——完全来自机器人的本体感觉状态估计器;(2)它自然地编码了机器人平台的运动能力——不同机器人(四足、六足、轮式、履带式)在同一地形上会有不同的速度跟踪表现;(3)它是连续的而非二值的,能够表达可穿越性的灰度。

2.5 几何特征提取:从超像素到 DINO-ViT 嵌入

WVN 的感知管线体现了 2023 年视觉学习领域的一个重要范式转变:从监督学习的 CNN 特征转向自监督学习的 Vision Transformer (ViT) 特征。

给定输入 RGB 图像 \(I \in \mathbb{R}^{H \times W \times 3}\),WVN 使用 DINO-ViT [Caron et al., 2021] 提取密集的特征图 \(\mathbf{F} \in \mathbb{R}^{E \times H \times W}\),其中 \(E = 90\) 是特征维度。DINO-ViT 的关键性质是:在没有使用任何人工标注的情况下,其自注意力机制学到的特征天然地编码了语义上有意义的分割信息——这被称为”涌现的分割能力”(emerging segmentation)。

计算压缩: 全分辨率密集特征的存储和处理对于机载计算(如 Jetson Orin)来说成本过高(每帧约 \(90 \times 480 \times 640 \approx 27.6M\) 个浮点数)。WVN 使用 SLIC 超像素分割 [Achanta et al., 2012] 将图像压缩为 \(n = 100\) 个超像素段,然后对每个段内的特征取均值:

\[\mathbf{f}_n = \frac{1}{\vert \mathcal{S}_n\vert } \sum_{(h,w) \in \mathcal{S}_n} \mathbf{F}_{:,h,w}\]

其中 \(\mathcal{S}_n\) 是第 \(n\) 个超像素段内的像素坐标集合。每个段得到一个 \(E\) 维的特征嵌入 \(\mathbf{f}_n\)。这种”视觉词汇袋”式的压缩使得每帧的处理时间降至约 17ms(在 Jetson Orin 上),实现了实时性。

2.6 异常检测与置信度加权的可穿越性学习

与 GOAT 拥有预训练的物体检测器不同,WVN 面对的是一个开放世界——机器人永远无法预先知道所有可能的地形类别。WVN 通过同时学习一个特征重建器(用于异常检测)和一个可穿越性回归器(用于穿越性评分)来解决这个问题。

特征重建器 \(f_{\text{reco}}^{\theta_r}\): 这是一个编码器-解码器网络,被训练来重建被穿越过的段(即 \(\tau_n \neq 0\) 的段)的 DINO-ViT 特征:

\[\mathcal{L}_{\text{reco}}(\mathbf{f}_n) = \delta_{\tau_n \neq 0} \cdot \frac{1}{E}\sum_{e=1}^{E} \|f_{\text{reco}}^{\theta_r}(\mathbf{f}_n)_e - \mathbf{f}_{n,e}\|^2 \quad \text{(公式 3)}\]

\(\delta_{\tau_n \neq 0}\) 确保网络只学习重建穿越过的正样本。因此,网路从未学习过如何重建异常段(如天空、树木)的特征——当这些段被输入时,会产生高重建误差。

置信度函数: 重建误差被映射为置信度 \(c(\mathcal{L}_{\text{reco}}) \in [0, 1]\)。WVN 通过对正样本的重建损失拟合高斯分布 \(\mathcal{N}(\mu_{\text{pos}}, \sigma_{\text{pos}})\) 来确定决策边界:

\[\mu_{\text{pos}} = \frac{1}{n_{\text{trav}}} \sum_{\mathbf{f}_n : \tau_n \neq 0} \mathcal{L}_{\text{reco}}(\mathbf{f}_n), \quad \sigma_{\text{pos}} = \sqrt{\frac{1}{n_{\text{trav}}} \sum_{\mathbf{f}_n : \tau_n \neq 0} (\mathcal{L}_{\text{reco}}(\mathbf{f}_n) - \mu_{\text{pos}})^2} \quad \text{(公式 4-6)}\] \[c(\mathcal{L}_{\text{reco}}(\mathbf{f}_n)) = \begin{cases} 1, & \text{if } \mathcal{L}_{\text{reco}} \leq \mu_{\text{pos}} \\ \exp\left(-\frac{(\mathcal{L}_{\text{reco}} - \mu_{\text{pos}})^2}{2(\sigma_{\text{pos}} \cdot k_\sigma)^2}\right), & \text{otherwise} \end{cases} \quad \text{(公式 7)}\]

可穿越性回归器 \(f_{\text{trav}}^{\theta_t}\): 一个单通道输出网络,被训练来回归段的连续可穿越性得分 \(\tau\)。关键创新是置信度加权 MSE 损失:

\[\mathcal{L}_{\text{trav}}(\mathbf{f}) = \underbrace{\sum_{n: \tau_n = 0} (1 - c(\mathbf{f}_n)) \cdot \|f_{\text{trav}}^{\theta_t}(\mathbf{f}_n) - 0\|^2}_{\text{未穿越段: 假设不可穿越,由置信度加权}} + \underbrace{\sum_{n: \tau_n \neq 0} \|f_{\text{trav}}^{\theta_t}(\mathbf{f}_n) - \tau_n\|^2}_{\text{已穿越段: 精确回归}} \quad \text{(损失函数)}\]

这个损失函数的设计精巧之处在于其不对称性:

总损失: \(\mathcal{L}_{\text{total}}(\mathbf{f}) = w_{\text{trav}}\mathcal{L}_{\text{trav}}(\mathbf{f}) + w_{\text{reco}}\mathcal{L}_{\text{reco}}(\mathbf{f})\),典型权重 \(w_{\text{trav}} = 0.03, w_{\text{reco}} = 0.5\)。

2.7 两种范式的对比

维度 GOAT (Chang et al. 2024) WVN (Frey et al. 2023)
场景 室内(家庭、仓库) 室外(森林、草地、公园)
地图表示 俯视语义实例地图(显式) 局部 2.5D 高程地图 + 投影穿越性(隐式)
感知输入 RGB-D + 机载位姿 RGB 单目 + 本体感觉 (IMU, 速度)
学习范式 模块化:大规模预训练模型 + 零样本泛化 在线自监督:演示数分钟即可适应新环境
目标定义 多模态(类别、图像、语言) 隐式(人类演示的偏好)
泛化机制 组件级泛化(Mask R-CNN + CLIP + SuperGlue) 在线自适应(持续学习)
终身学习 是:对象实例记忆跨目标累积 部分:单次任务内在线学习
部署平台 平台无关(Spot 四足 + Stretch 轮式) ANYmal C 四足

三、论文精读

3.1 GOAT: GO to Any Thing (Chang et al. RSS 2024)

3.1.1 系统架构

GOAT 采用模块化设计管线,由四个核心模块组成(图 2, Section IV):

(1) 感知系统(Perception): RGB 图像 → Mask R-CNN (ResNet-50 骨干,MS-COCO 预训练) → 实例分割。深度图像 → MiDaS 单目深度估计(填充反射物体导致的深度空洞)→ 3D 点云投影。两者联合构建俯视语义实例地图。

(2) 对象实例记忆(Object Instance Memory): 语义地图的每个栅格存储的是实例 ID 而非类别标签。当新的检测与已有实例空间重叠时,新视角图像被追加到该实例的图像列表中。这使得 GOAT 能够区分同一类别的不同实例(”黑色的那把椅子” vs “棕色的那把椅子”)。

(3) 全局策略(Global Policy): 对于每个新目标 \(g_k\),首先在记忆中搜索匹配的物体实例。如果找到(相似度超过阈值),将其实例的存储位置作为长距离点导航目标。如果找不到,输出探索目标(基于前沿探索,选择最近的未探索区域)。

(4) 局部策略(Local Policy): 使用快速行进法(Fast Marching Method)在语义地图上规划从当前位置到长距离目标的路径。在 Spot 机器人上,使用内置的点导航控制器到达路径上的各个航点;在 Stretch 机器人上,规划第一步的低级动作。

动态记忆更新: GOAT 的一个常被忽视但至关重要的设计是其动态更新协议(Section IV-e)。当新的传感器观测到达时,相关栅格被直接覆盖。如果之前被标记为”有物体”的区域现在显示为空,它被标记为空;如果之前被标记为”未探索/遮挡”的区域现在显示为可穿越,它被标记为可穿越。这种朴素但有效的策略使得 GOAT 能够处理环境中的动态变化(如人类移动物体)。

3.1.2 实验结果分析

GOAT 在 9 个视觉各异的真实家庭中进行了总计 90 小时的实验,覆盖了 200+ 个不同的物体实例,总共 675 个导航目标。关键数据(Table I):

方法 类别目标 SR 图像目标 SR 语言目标 SR 平均 SR 平均 SPL
GOAT 94.3% 86.4% 68.2% 83.0% 0.642
CLIP on Wheels 65.3% 46.1% 40.8% 50.7% 0.418
GOAT w/o Instances 94.1% 28.6% 27.6% 49.4% 0.398
GOAT w/o Memory 76.4% 59.4% 45.3% 60.3% 0.188

注:SR = Success Rate (成功率),SPL = Success weighted by Path Length (路径归一化成功率) [Anderson et al., 2018]

三个关键消融发现:

  1. 实例感知的记忆至关重要: GOAT w/o Instances(将目标退化为”最近的目标类别物体”,类似 [Gervet et al., 2023])在类别目标上与 GOAT 持平(94.1%),但在图像和语言目标上分别暴跌至 28.6% 和 27.6%。这说明:如果目标是”那把特定的椅子”而非”任意一把椅子”,必须跟踪实例级别的信息。

  2. 终身记忆带来性能持续提升: GOAT w/o Memory(每个目标后重置地图和记忆)的成功率从首个目标的 60% 到第 5-10 个目标仍无改善,而完整 GOAT 的成功率从 60% 单调上升至 90%(Figure 6)。SPL 从 0.20 提升至 0.80,意味着在充分探索后,机器人能以近乎最优的路径到达目标。

  3. 图像匹配优于语言匹配: 图像目标的成功率(86.4%)显著高于语言目标(68.2%)。作者归因于 SuperGlue 基于几何关键点的匹配能够利用预测实例与目标图像的几何一致性,而 CLIP 的语义特征编码无法捕捉细粒度的实例内属性(如花纹、形状、颜色分布)。

3.1.3 局限性讨论

论文的 Discussion (Section VII) 坦诚地分析了若干局限性:

3.2 Fast Traversability Estimation for Wild Visual Navigation (Frey et al. RSS 2023)

3.2.1 系统架构

WVN 的架构(Fig. 2, Section III)由五个并发运行的模块组成:

(1) 特征提取器(Feature Extractor): DINO-ViT 骨干 + SLIC 超像素分割 → 每帧 100 个段嵌入。

(2) 可穿越性得分生成(Traversability Score Generation): 本体感觉的速度跟踪误差 → Kalman 平滑 → sigmoid 映射 → \(\tau \in [0,1]\)。

(3) 监督图与任务图(Supervision & Mission Graphs): 这是 WVN 最精巧的工程设计之一。监督图(Supervision Graph)是一个固定长度的环形缓冲区,存储机器人的足迹轨迹和可穿越性得分,最大长度由 \(N_{\text{sup}} \times d_{\text{sup}}\) 决定。任务图(Mission Graph)存储整个任务中用于训练的所有数据。每当新的任务节点被添加时,系统触发图间交互:将监督图中的足迹轨迹和相应可穿越性得分反投影到任务图中所有在范围内的节点图像上,为每个段生成监督标签。

(4) 在线学习线程(Learning Thread): 联合训练特征重建器 \(f_{\text{reco}}\) 和可穿越性回归器 \(f_{\text{trav}}\),两者共享一个两层 MLP 的特征提取层([256, 32] 神经元,ReLU 激活),使用 Adam 优化器(学习率 0.001)。每步训练从 8 个有效的任务节点中随机采样构成一个批次。

(5) 闭环集成(Closed-Loop Integration): 预测的可穿越性通过光线投影(raycasting)融合到局部 2.5D 高程地图中,然后经过中值滤波、距离变换生成 SDF(Signed Distance Field),最后通过局部规划器生成 SE(2) 扭转指令驱动四足机器人。

3.2.2 消融研究的关键结论

WVN 在三个地理环境(Hilly, Forest, Grass)上进行了系统的消融研究(Section V-C)。关键结论:

学习方法的比较(Table III): 基于 DINO-ViT 特征 + MLP 的 WVN 在三个场景的 GT 准确率(81.05%, 82.45%, 78.21%)上全面优于传统方法(Random Forest, SVM-RBF, SVM-Poly)。这验证了 Vision Transformer 特征在可穿越性估计中的表征能力。

特征提取器的比较(Table V): 自监督预训练的 DINO-ViT(21M 参数, 17.0ms 推理)显著优于 ImageNet 预训练的 ResNet-50 和 EfficientNet-B4。即使使用相同的 ResNet-50 架构,DINO 自监督训练(DINO-ReN)也优于 ImageNet 预训练(74.14% vs 72.71% 在 Forest 场景的 GT)。这印证了自监督视觉特征在机器人感知中的优势。

场景泛化实验(Table VI): WVN 在一个场景上训练后在所有场景上测试——虽然在同一场景上表现最好(如 Hilly→Hilly: 81.05%),但跨场景泛化的性能会出现显著下降(如 Forest→Grass: 75.80%, Grass→Forest: 73.22%)。即使这些场景在地理位置、季节、天气上都相隔不远,泛化仍然受限。这有力地论证了在线自监督学习的必要性——与其在大规模离线数据上训练一个试图覆盖所有场景的模型,不如让机器人在每个新环境中花几分钟进行在线自适应。

适应速度(Figure 13): WVN 从随机初始化网络开始,在约 200 个训练步内即可达到较好的性能。由于每帧图像提供约 100 个超像素段作为训练样本,这 200 步只需要少量的图像帧。在实际硬件实验中(Section V-B1),机器人在 9 步训练后(21 秒)的分割还很差,但在 800 步后(约 2 分钟)已经能正确区分泥土(可穿越)和树木(不可穿越)。

3.2.3 真实世界部署

WVN 最令人印象深刻的实验是公里级自主路径跟随(Section V-B4)。在一个公园环境中,人类操作员沿碎石步道遥控机器人不到 2 分钟作为演示。随后关闭学习线程(冻结模型权重),机器人自主沿步道行进了 3 次,总计约 2.45 公里。在没有使用任何全局规划、没有预先标注步道语义类别、仅依赖视觉穿越性估计和一个简单的”智能胡萝卜”探索策略的情况下,机器人基本上保持在路径中央,避开了草地、灌木丛、长椅和行人。

这个实验的关键洞察在于:区区两分钟的遥控演示,WMV 就学到了一套语义线索来识别”应该走的路”,而不需要训练一个语义分割模型来学习公园中所有可能的可穿越类别(柏油路、碎石路、水泥路、草地⋯⋯)。这是一种极其高效的偏好编码(preference encoding)形式。

3.2.4 局限性

Section V-D 详细列举了 WVN 的局限性:


四、算法与代码实现

本节实现一个基于 WVN 核心思想的可穿越性估计器。我们选择实现 WVN 而非 GOAT,因为 GOAT 的实现依赖于大量外部组件(Mask R-CNN、CLIP、SuperGlue、Mistral 7B、SLAM),这些不适合作为单章代码示例;而 WVN 的核心创新——在线自监督可穿越性学习——可以作为一个自包含的 PyTorch 模块实现,且其数学公式在本文中已完整推导。

4.1 核心网络架构

import torch
import torch.nn as nn
import torch.nn.functional as F

class TraversabilityNetwork(nn.Module):
    """
    WVN 可穿越性估计网络 (Section III-E3)

    架构: 共享的两层 MLP 隐藏层 + 两个独立输出头
    - 特征重建头 (reconstruction head): 用于异常检测
    - 可穿越性头 (traversability head): 用于回归可穿越性得分

    输入: DINO-ViT 段特征 f_n ∈ R^E  (E=90 in the paper)
    输出:
      - f_reco: 重建的特征 ∈ R^E
      - tau_pred: 预测的可穿越性得分 ∈ [0,1]
    """

    def __init__(self, input_dim=90, hidden_dims=[256, 32]):
        super().__init__()

        # 共享隐藏层 (Section III-E3: [256, 32] neurons, ReLU)
        layers = []
        in_dim = input_dim
        for h_dim in hidden_dims:
            layers.append(nn.Linear(in_dim, h_dim))
            layers.append(nn.ReLU())
            in_dim = h_dim
        self.shared = nn.Sequential(*layers)

        # 编码器-解码器的瓶颈层维度
        self.bottleneck_dim = hidden_dims[-1]  # 32

        # 特征重建头: 重建输入 DINO-ViT 特征
        # 仅对正样本 (traversed segments) 训练 → 对异常段产生高误差
        self.reco_head = nn.Linear(self.bottleneck_dim, input_dim)

        # 可穿越性回归头: 输出连续穿越性得分 τ ∈ [0,1]
        # 后接 sigmoid 确保输出在 [0,1] 区间
        self.trav_head = nn.Sequential(
            nn.Linear(self.bottleneck_dim, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        """
        Args:
            x: (B, E) — batch of segment embeddings
        Returns:
            f_reco:     (B, E) — reconstructed features
            tau_pred:   (B, 1) — predicted traversability scores
        """
        shared_feat = self.shared(x)       # (B, 32) bottleneck
        f_reco = self.reco_head(shared_feat)  # (B, E)
        tau_pred = self.trav_head(shared_feat)  # (B, 1)
        return f_reco, tau_pred


def compute_velocity_error(v_ref, v_curr):
    """
    计算速度跟踪误差 → 用于生成可穿越性监督信号

    公式 1 (Section III-C):
      v_error = 0.5 * [(v̄_x - v_x)^2 + (v̄_y - v_y)^2]

    Args:
        v_ref:  (B, 2) — reference velocity command [vx, vy]
        v_curr: (B, 2) — current estimated robot velocity [vx, vy]
    Returns:
        v_error: (B,) — mean squared velocity error per sample
    """
    return 0.5 * torch.sum((v_ref - v_curr) ** 2, dim=1)


def velocity_error_to_traversability(v_error, k=5.0, v_thr=0.5):
    """
    将速度误差映射为可穿越性得分 τ ∈ [0,1]

    公式 2 (Section III-C):
      τ = sigmoid(-k * (v_error - v_thr))

    物理直觉:
      - v_error << v_thr → τ ≈ 1 (紧密跟踪 → 地形容易穿越)
      - v_error >> v_thr → τ ≈ 0 (跟踪误差大 → 地形困难)
      - v_error == v_thr → τ = 0.5

    Args:
        v_error: (B,) — velocity tracking errors
        k: sigmoid steepness (default: 5.0)
        v_thr: sigmoid midpoint (default: 0.5 m/s error → score 0.5)
    Returns:
        tau: (B,) — continuous traversability scores ∈ [0,1]
    """
    return torch.sigmoid(-k * (v_error - v_thr))

4.2 训练损失函数

def fit_gaussian_to_positive_losses(losses_reco, tau_labels):
    """
    对正样本 (τ ≠ 0) 的重建损失拟合高斯分布

    公式 4-6 (Section III-E1):
      n_trav = sum(δ_{τ_n ≠ 0})                                   公式 4
      μ_pos  = (1/n_trav) * sum(L_reco(f_n))  for τ_n ≠ 0        公式 5
      σ_pos  = sqrt((1/n_trav) * sum((L_reco(f_n) - μ_pos)^2))    公式 6

    Args:
        losses_reco: (B,) — per-segment reconstruction losses
        tau_labels:  (B,) — per-segment traversability labels
    Returns:
        mu_pos:  scalar — mean of positive-sample losses
        sigma_pos: scalar — std  of positive-sample losses
    """
    mask_pos = (tau_labels != 0)
    n_trav = mask_pos.sum()

    if n_trav == 0:
        # 如果没有正样本,回退到默认值
        return torch.tensor(0.0), torch.tensor(1.0)

    losses_pos = losses_reco[mask_pos]
    mu_pos = losses_pos.mean()
    sigma_pos = losses_pos.std(unbiased=False)

    # 处理只有一个正样本的边界情况
    if sigma_pos == 0:
        sigma_pos = torch.tensor(1e-6)

    return mu_pos, sigma_pos


def traversability_confidence(loss_reco, mu_pos, sigma_pos, k_sigma=2.0):
    """
    将重建损失映射为置信度 c(L_reco) ∈ [0,1]

    公式 7 (Section III-E1):
      c(L_reco) = 1                            if L_reco ≤ μ_pos
                = exp(-(L_reco - μ_pos)^2 / 2(σ_pos * k_σ)^2)   otherwise

    物理含义:
      - L_reco 小 → 段与穿越过的地形视觉相似 → c ≈ 1 (高置信度)
      - L_reco 大 → 段是网络从未见过的异常    → c ≈ 0 (低置信度)

    Args:
        loss_reco: (B,) — per-segment reconstruction losses
        mu_pos:    scalar — mean of positive-sample losses
        sigma_pos: scalar — std  of positive-sample losses
        k_sigma:   tuning parameter to scale confidence (default: 2.0)
    Returns:
        confidence: (B,) — confidence scores ∈ [0,1]
    """
    # 重建损失 ≤ 正样本均值 → 完全置信 (c=1)
    confident_mask = (loss_reco <= mu_pos)

    # 重建损失 > 正样本均值 → 高斯尾部似然
    sigma_scaled = sigma_pos * k_sigma + 1e-8  # 数值稳定性
    anomaly_confidence = torch.exp(
        -((loss_reco - mu_pos) ** 2) / (2 * sigma_scaled ** 2)
    )

    confidence = torch.where(confident_mask,
                             torch.ones_like(loss_reco),
                             anomaly_confidence)

    return confidence


def total_loss(batch_features, batch_tau_labels, model, wtrav=0.03, wreco=0.5):
    """
    计算 WVN 总损失 (公式 3, 损失函数, 公式 8)

    公式 3 (重建损失, Section III-E1):
      L_reco(f_n) = δ_{τ_n≠0} * (1/E) * Σ_e ||f_reco(f_n)_e - f_n,e||^2

    损失函数 (置信度加权穿越性回归, Section III-E2):
      L_trav = Σ_{τ_n=0} (1-c_n) * ||f_trav(f_n) - 0||^2   (未穿越段: 保守)
             + Σ_{τ_n≠0}       ||f_trav(f_n) - τ_n||^2     (已穿越段: 回归)

    公式 8 (总损失):
      L_total = w_trav * L_trav + w_reco * L_reco

    Args:
        batch_features:  (B, E) — segment embeddings
        batch_tau_labels: (B,)  — traversability labels (0 or >0)
        model:            TraversabilityNetwork instance
        wtrav:            weight for traversability loss (default: 0.03)
        wreco:            weight for reconstruction loss (default: 0.5)
    Returns:
        loss_total: scalar — combined loss
        loss_trav:  scalar — traversability regression loss
        loss_reco:  scalar — reconstruction loss
    """
    B, E = batch_features.shape
    f_reco, tau_pred = model(batch_features)

    # ---- 1. 特征重建损失 (公式 3) ----
    # 仅对正样本 (τ ≠ 0) 计算重建损失
    mask_pos = (batch_tau_labels != 0).float().unsqueeze(1)  # (B, 1)
    reco_errors = (f_reco - batch_features) ** 2              # (B, E)
    loss_reco = (mask_pos * reco_errors).sum() / (mask_pos.sum() * E + 1e-8)

    # ---- 2. 计算每个段的重建损失和置信度 ----
    # 每个段的重建 MSE
    losses_reco_per_sample = reco_errors.mean(dim=1).detach()  # (B,)
    mu_pos, sigma_pos = fit_gaussian_to_positive_losses(
        losses_reco_per_sample, batch_tau_labels
    )
    confidence = traversability_confidence(
        losses_reco_per_sample, mu_pos, sigma_pos, k_sigma=2.0
    )  # (B,)

    # ---- 3. 置信度加权的穿越性回归损失 (Section III-E2) ----
    tau_pred_flat = tau_pred.squeeze(-1)  # (B,)

    # 无标签段 (τ=0): 保守惩罚,权重 = (1 - confidence)
    mask_unlabeled = (batch_tau_labels == 0).float()
    loss_unlabeled = (mask_unlabeled * (1 - confidence) *
                      (tau_pred_flat - 0.0) ** 2).sum()

    # 有标签段 (τ≠0): 精确回归
    loss_labeled = (mask_pos.squeeze(-1) *
                    (tau_pred_flat - batch_tau_labels) ** 2).sum()

    n_unlabeled = mask_unlabeled.sum() + 1e-8
    n_labeled = mask_pos.sum() + 1e-8
    loss_trav = loss_unlabeled / n_unlabeled + loss_labeled / n_labeled

    # ---- 4. 总损失 (公式 8) ----
    loss_total = wtrav * loss_trav + wreco * loss_reco

    return loss_total, loss_trav, loss_reco

4.3 在线训练循环

class OnlineTraversabilityLearner:
    """
    在线可穿越性学习器

    模拟 WVN 的训练循环 (Section III-E3):
    - 每个训练步: 从任务图中随机采样 8 个有效任务节点
    - 使用 Adam 优化器 (lr=0.001) 联合训练
    - 持续更新模型权重 → 新环境无冷启动

    典型用法:
        learner = OnlineTraversabilityLearner(input_dim=90)
        for frame in robot_camera_stream:
            segments = extract_dino_features_and_slic(frame)
            tau_labels = velocity_error_to_traversability(vel_error)
            learner.update(segments, tau_labels)
            traversability_map = learner.predict(segments)
    """

    def __init__(self, input_dim=90, wtrav=0.03, wreco=0.5, lr=1e-3):
        self.model = TraversabilityNetwork(input_dim=input_dim)
        self.optimizer = torch.optim.Adam(self.model.parameters(), lr=lr)
        self.wtrav = wtrav
        self.wreco = wreco
        # 任务节点缓冲: 每个节点 = (segment_features, tau_labels)
        self.mission_buffer = []
        self.train_step = 0

    def add_mission_node(self, segment_features, tau_labels):
        """
        向任务图中添加节点 (Section III-D2)

        仅在满足条件时添加:
        - 至少有一个段的穿越性得分 ≠ 0 (有效监督信号)
        - 与上一个节点的距离 >= d_mis

        Args:
            segment_features: (N_segments, E) — per-segment DINO-ViT embeddings
            tau_labels:       (N_segments,)  — per-segment traversability scores
        """
        if (tau_labels != 0).any():
            self.mission_buffer.append((segment_features.detach().clone(),
                                         tau_labels.detach().clone()))

    def update(self, segment_features, tau_labels, batch_nodes=8):
        """
        在线训练更新 (Section III-E3)

        从任务图中随机采样 batch_nodes 个有效节点构成批次
        执行一步梯度更新

        Args:
            segment_features: (N_segments, E) — current frame's segment features
            tau_labels:       (N_segments,)  — current frame's traversability labels
            batch_nodes:      number of mission nodes to sample
        Returns:
            loss_total.item(): scalar — combined loss value
        """
        # 将当前帧添加到任务图
        self.add_mission_node(segment_features, tau_labels)

        if len(self.mission_buffer) < 2:
            return 0.0

        # 从任务图中随机采样
        n_sample = min(batch_nodes, len(self.mission_buffer))
        indices = torch.randperm(len(self.mission_buffer))[:n_sample]

        batch_feats = []
        batch_labels = []
        for idx in indices:
            feats, labels = self.mission_buffer[idx]
            batch_feats.append(feats)
            batch_labels.append(labels)

        batch_feats = torch.cat(batch_feats, dim=0)    # (total_segments, E)
        batch_labels = torch.cat(batch_labels, dim=0)  # (total_segments,)

        # 梯度下降
        self.optimizer.zero_grad()
        loss_total, loss_trav, loss_reco = total_loss(
            batch_feats, batch_labels, self.model,
            self.wtrav, self.wreco
        )
        loss_total.backward()
        self.optimizer.step()

        self.train_step += 1
        return loss_total.item()

    def predict(self, segment_features):
        """
        对新的视觉帧进行可穿越性预测

        Args:
            segment_features: (N_segments, E) — per-segment embeddings
        Returns:
            tau_pred: (N_segments,) — predicted traversability ∈ [0,1]
        """
        with torch.no_grad():
            _, tau_pred = self.model(segment_features)
        return tau_pred.squeeze(-1)

4.4 训练技巧与工程细节

  1. 特征预处理: DINO-ViT 的输出特征通常需要 L2 归一化以消除幅度变化。在实际部署中,建议对 \(\mathbf{f}_n\) 做 F.normalize(f_n, dim=-1) 后再送入网络。

  2. 正负样本平衡: WVN 的一个核心挑战是正样本(穿越过的地形)和负样本(未穿越的地形)的极端不平衡——机器人穿越的区域远少于未穿越的区域。置信度加权损失的设计就是专门针对这个问题设计的:它利用异常检测的非对称性来自动平衡两者。

  3. Kalman 滤波参数: 论文中未明确给出 Kalman 滤波器的具体参数 [UNSPECIFIED]。在我们的实现中,使用标准的一维恒定速度模型,过程噪声 \(Q = 0.01\),观测噪声 \(R = 0.1\)。

  4. 可穿越性二值化阈值: WVN 通过自监督的 ROC 曲线选择阈值 \(\tau_{\text{thr}}\)(Section III-E2)。在实际部署中,取 \(\tau_{\text{thr}}\) 使得 FPR (False Positive Rate) = 0.15 作为默认值。

  5. 推理速度优化: 论文在 Jetson Orin 上仅达到 2.5Hz(未优化代码)。潜在优化策略包括:(a) 将 DINO-ViT 特征提取移至 TensorRT;(b) 减少每帧的超像素段数量(论文中 \(n=100\),可降至 50 以换取速度);(c) 使用 INT8 量化。


五、核心 Takeaway 与延伸阅读

核心洞察

  1. 模块化 vs 端到端不是二元选择: GOAT 和 WVN 都展示了模块化设计在真实世界机器人部署中的实用优势。GOAT 在感知(Mask R-CNN)、匹配(CLIP + SuperGlue)、规划(Fast Marching)和运动控制(机器人内置控制器)之间保持清晰的接口边界,使得系统可以轻松部署到不同的机器人平台(Spot 四足 + Stretch 轮式),并且可以为下游应用(拣选与放置、社交导航)重新组合模块。相比之下,端到端学习策略(如直接用 RL 训练导航)虽然理论上更优雅,但在真实世界中面临数据稀缺和泛化瓶颈。

  2. 终身记忆是泛在导航的核心能力: GOAT 的实验(Figure 6)最清晰地展示了这一点——从首个目标的 60% 成功率到充分探索后的 90% 成功率,30% 的绝对提升来自记忆而非更好的感知。这种”越用越聪明”的特性是真实部署中不可或缺的能力,但大多数仿真基准(如 Habitat)的 episode 设计只评估单次目标的导航,系统性低估了记忆的价值。

  3. 自监督在开集环境中的独特优势: WVN 实现快速适应的关键不是更大的模型或更多的数据,而是将监督信号的生成嵌入到机器人自身的操作中。这种范式消除了对人工标注数据的依赖——机器人在森林中行走时,它的足迹和速度误差自然地生成了可穿越性标签。WVN 从随机初始化到可用的分割仅需 200 个训练步(几分钟的操作),展示了在线自监督学习的实际可行性。

  4. 视觉 Transformer 特征在机器人感知中的涌现价值: WVN 的消融研究(Table V)提供了令人信服的证据:DINO-ViT 特征在所有场景的穿越性预测中系统性地优于 ImageNet 预训练的 CNN 特征。这不是偶然的——DINO 的自监督训练范式使模型学到了关于物体边界、区域一致性和语义类别的隐式编码,而这些恰好是可穿越性判断所需的视觉线索。WVN 展示了如何以极低的工程成本(一个两层 MLP)将这些涌现的语义能力迁移到机器人导航任务中。

  5. 多模态目标指定的实践权衡: GOAT 的三种目标模态(类别、图像、语言)的成功率差异(94.3% / 86.4% / 68.2%)揭示了当前技术栈的瓶颈所在——语言目标比图像目标低了 18.2 个百分点。这不是因为语言理解本身不重要,而是因为将自然语言描述映射到物理世界中的特定物体实例的技术链条(LLM 提取类别 → CLIP 匹配特征)引入了两个累积误差源。在 HRI(人机交互)场景中,语言目标可能是最自然的交互方式,但它目前也是最不可靠的。

开放问题与研究方向

  1. 如何在有限容量机器人上进行终身实例记忆? GOAT 存储每个物体实例的所有视角图像,在长期部署中不可持续。神经特征场(Neural Feature Fields)将 2D 特征嵌入 3D 空间的连续函数,可能是更紧凑的替代方案,但目前仍需要服务器级 GPU。

  2. WVN 能否从单一任务内的在线学习扩展到跨任务的持续学习? 论文承认其系统可以受益于持续学习范式的进展——当机器人从一个森林移到另一个公园时,如何避免灾难性遗忘,同时利用先前学到的穿越性知识?

  3. 几何穿越性(如 Wermelinger et al. [38] 的高度/坡度启发式)和视觉穿越性(如 WVN)的最佳融合策略是什么? WVN 的光线投影方法虽然直接,但容易引入伪影。一个更有原则的融合框架——可能是概率占用栅格中的贝叶斯更新——值得探索。

  4. GOAT 的多模态目标匹配在未来能否被一个统一的 VLM(视觉-语言模型)所取代? 随着 GPT-4V、Gemini 等多模态模型的进步,用一个统一的视觉-语言嵌入空间同时处理类别、图像和语言目标在概念上是吸引人的,但早期尝试(如 CLIP on Wheels [Gadre et al., 2023])在 GOAT 任务上的表现远不如模态专用的匹配策略(50.7% vs 83.0%)。这是一个活跃的研究前沿。

推荐延伸阅读


六、练习与思考

基础练习

  1. 可穿越性得分校准: 假设一台轮式机器人以 \(v_{\text{ref}} = 0.8\) m/s 的指令速度在草地地形上行进。其 Kalman 滤波后的速度跟踪误差在平坦草地上为 \(v_{\text{error}} \approx 0.05\) m/s,在高草地上为 \(v_{\text{error}} \approx 0.4\) m/s。使用公式 2(\(k=5.0\), \(v_{\text{thr}} = 0.5\)),计算两种地形下的可穿越性得分 \(\tau\)。你得到的结果是否有物理意义上的”合理”差异?如果 \(v_{\text{thr}}\) 被调至 \(0.2\)(更保守),会对高草地的穿越性估计产生什么影响?

  2. CLIP 匹配的余弦相似度: 假设你有三个椅子的 CLIP 嵌入 \(\mathbf{f}_A, \mathbf{f}_B, \mathbf{f}_C \in \mathbb{S}^{511}\)(单位超球面上的点),和一个语言描述嵌入 \(\mathbf{f}_{\text{lang}}\)。已知 \(\cos(\mathbf{f}_A, \mathbf{f}_{\text{lang}}) = 0.31\), \(\cos(\mathbf{f}_B, \mathbf{f}_{\text{lang}}) = 0.24\), \(\cos(\mathbf{f}_C, \mathbf{f}_{\text{lang}}) = 0.19\)。GOAT 在探索阶段使用的 CLIP 阈值为 0.28。哪些椅子会被匹配?如果环境已被充分探索,GOAT 会选择哪个椅子?这个例子揭示了固定阈值策略的什么局限?

综合思考

  1. GOAT + WVN 混合架构设计: 假设你需要为一台四足机器人设计一个同时支持室内和室外导航的系统。请设计一个架构草图,将 GOAT 的语义记忆和 WVN 的自监督可穿越性整合为一个统一的导航栈。你需要明确回答以下问题:(a) 两种方法的地图表示应如何融合?(b) 当目标位于室内但有室外捷径时(例如,从客厅到后院再到另一个房间),系统应如何决策?(c) 终身记忆的哪些组件需要在室内和室外之间共享?

  2. 开放词汇检测 vs 封闭集检测的工程权衡: GOAT 的 Discussion (Section VII-d) 报告了一个反直觉的发现:开放词汇检测器(Detic)在常见室内类别上的表现不如封闭集训练的 Mask R-CNN (2017)。请分析可能的原因。如果让你设计一个实验来验证”互联网规模的弱标注数据不足以代表具身交互数据”这一假说,你会如何设计?(提示:考虑视角分布、遮挡模式、光照条件、物体尺度的差异)

代码实验

  1. 穿越性估计器的环境自适应模拟: 使用第 4 节提供的 OnlineTraversabilityLearner,编写一个模拟实验脚本:定义两个”虚拟环境”——环境 A(”森林”:大量绿色/棕色色调的段嵌入,代表可穿越的泥土和不可穿越的树木)和环境 B(”沙漠”:黄色/橙色色调的段嵌入,代表可穿越的沙地和不可穿越的岩石)。生成两种环境的合成 DINO-ViT 特征(使用随机高斯分布,改变均值和协方差来模拟不同的视觉外观)。在环境 A 上训练学习器,然后:(a) 在环境 A 的新数据上测试;(b) 在环境 B 的数据上测试(零样本泛化);(c) 在环境 B 上继续在线训练 100 步后再测试。观察并分析:(i) 零样本泛化的性能下降幅度;(ii) 在线微调恢复性能所需的最小步数;(iii) 灾难性遗忘(在环境 B 上训练后,在环境 A 上重新测试的性能变化)。

← 第14章 运动控制案例研究
↑ 目录
第16章 灵巧操作案例研究 →