EvolvingNav:让机器人在不断变化的世界中寻找目标
0. 简介
EvolvingNav 研究长期在同一环境中工作的机器人如何寻找会被人搬动的物品:昨天看到的杯子今天未必还在原处,出发时合理的预测也可能在途中失效。它从带时间戳的三维物体历史中预测位置概率,区分留在最近观测地点、迁移到其他已知地点和进入未知区域,再根据抵达时间与新视觉证据调整路线。配套的 EvoWorld-Bench 包含 54 个场景、803,680 条任务;论文主表报告的搜索成功率为 86.18%。结合论文与公开源码,可以把这套方法理解为一个持续维护不确定性、边走边验证的搜索系统,而不是一次性给出目的地的导航模型。代码就在Github上。
1. 旧地图仍然正确,目标却已经不在那里
1.1 从“记住位置”到“判断记忆是否过期”
假设机器人上午九点看见药盒放在床头柜,下午两点接到取药请求。房间布局、床的位置、床头柜的三维坐标可能都没有变化,因此地图依然准确;问题出在药盒已经被人收进了柜子。直接读取最近一次观测,会把一个过去成立的事实当作现在成立的事实。每次都重新搜索又会浪费长期积累的经验。历史观测是证据,当前状态是需要推断的隐变量,两者之间隔着一段机器人没有看到的时间,这正是 EvolvingNav 要处理的缺口。
如果目标在机器人出发后仍可能移动,问题还要再向前推进一步。机器人需要二十秒才能到达客厅,却只要五秒就能检查厨房;即便此刻客厅的目标概率更高,二十秒后的情况也可能不同。这里的关键是,导航行动本身消耗时间,而时间会改变行动的价值。系统既要利用过去的生活规律,又要接受规律偶尔失效,还必须通过实际视觉检查确认结果,不能把模型预测当成已经看见了目标。
1.2 两类任务对应两种时间假设
论文把任务分成查询后目标固定和执行中持续演化两类。前一类允许目标在请求到来之前搬动,但搜索开始后不再改变位置,对应 N1 至 N3;后一类在导航期间继续推进世界时间,目标可以在视野外再次转移,对应 N4。这一划分不是难度标签那么简单,它决定滤波器应该怎样工作:固定目标任务主要根据新证据修正判断,持续演化任务还要在没有新图像时更新状态概率,并允许先前检查过的地点重新成为候选。
直觉理解:找一本被人拿走的书,和追踪一辆仍在变换停车位置的车,需要不同的搜索策略。前者查过一个清楚可见的空书架后,可以长期降低它的优先级;后者即使刚检查过停车位,也不能认定车辆不会回来。EvolvingNav 用不同的状态转移假设表达这种区别,而不是给两种任务套用同一张固定排序表。
2. 整体框架:记忆、预测与行动分别承担什么
2.1 输入是观测历史,输出是经过验证的搜索结果
机器人在查询时刻 t q t_q tq 能使用的输入,是截至该时刻已经获得的 RGB 图像、深度图、相机位姿和时间戳。深度与位姿把物体投影到三维世界,时间戳说明这条证据有多旧;目标描述则从记忆中筛选相关历史。模型输出的不是一个绝对坐标,而是已知候选位置与未知状态上的概率分布。随后导航器把候选位置转换成可到达的检查视点,高层系统在预算内执行动作,最后以视觉确认和有效停止作为完成条件。文章将可用历史写为:
H ≤ t q = { ( I k , D k , ξ k , t k ) ∣ t k ≤ t q } , C ~ o = C o ∪ { u n k n o w n } . \mathcal H_{\le t_q} =\{(I_k,D_k,\xi_k,t_k)\mid t_k\le t_q\}, \qquad \widetilde{\mathcal C}_o=\mathcal C_o\cup\{\mathrm{unknown}\}. H≤tq={(Ik,Dk,ξk,tk)∣tk≤tq},C o=Co∪{unknown}.
其中 I k I_k Ik 是图像, D k D_k Dk 是深度, ξ k \xi_k ξk 是相机位姿, C o \mathcal C_o Co 是目标 o o o 的已知候选集合。候选通常对应房间中的区域或承载物状态,例如“厨房岛台上的杯子”,并关联一组检查视点;unknown 表示目标可能位于当前候选集以外。这意味着系统对未知的处理有明确的概率入口,地图中没有列出的地点不会因为“不在表里”就被逻辑上排除,后续探索也有了可以比较的行动价值。

图 1 把论文的几个层面放在同一张图里:长期观测提供历史,预测模块对未来检查时刻做判断,具身智能体调用工具完成搜索。阅读时可以先抓住中间的概率状态,而不必把全部导航任务名称一次记住。只要明确“记忆保存什么、预测估计什么、观测更新什么”,后面的时序编码、贝叶斯更新和路线重规划就能落在同一条因果链上,也更容易判断实验究竟验证了其中哪一环。
2.2 高层控制器不负责替代概率计算
系统包含因果 4D 记忆、P4D-Nav 预测器、事件驱动执行器三个主要部分。“4D”在这里指三维空间加时间,强调对象历史随时间展开,并不是生成一段四维视频。论文使用冻结的零样本视觉语言模型调用记忆、预测、导航、检查和探索工具;冻结只说明这个高层控制器没有为导航任务微调,历史预测器、转移模型和检出概率校准器仍然需要训练。把整套系统称为“完全无需训练”,会漏掉真正承担时序推断的模块。
进一步看,当前公开实现比“让大模型自由规划”更受约束。agent.py 的 _choose() 先计算合法动作及效用,再向控制器提供这些选项;控制器返回的选择只有在效用不低于当前最优项时才被接受,否则回退到最优项。几何成本、概率更新和动作边界都由显式程序维护。理解这种分工,有助于把性能变化归因到信念模型和证据机制,而不是笼统归因于一个更大的语言模型。

图 2 可以沿着从左到右的路径读:图像先形成有来源的历史事件,事件编码成目标相关表征,再得到位置概率;执行过程中,时间传播与新图像持续修正这份分布。这里的关键是,概率状态会反过来影响下一次去哪里观察,因此感知与行动形成闭环。机器人不是等走完整条路线才知道第一步判断错了,而是在一段段移动之后重新计算,途中获得的有效证据也能改变后续目的地。
3. 因果 4D 记忆:保存过去,也约束过去能告诉我们什么
3.1 空间对齐、对象身份与版本记录
建立记忆的第一步仍然是普通机器人感知:检测并分割对象,利用相机内参把深度像素反投影,再通过相机位姿变换到世界坐标。难点在于跨视角、跨时间关联同一对象,并把观测来源保留下来。公开实现中的 memory.py 提供反投影、对象版本和证据记录:同一状态的新观测追加证据,状态改变时关闭旧版本并创建新版本,历史不会被最新坐标直接覆盖。这使“现在在哪里”和“昨天在哪里”能够成为不同的查询。
版本的意义需要谨慎理解:机器人九点看见杯子在桌上,十点看见它在厨房,只能确定两个观测时刻的状态,并不知道杯子具体在九点几分被搬走。版本记录描述的是可获得的观测支持,而非全程可见的真实运动轨迹。源码在按时间查询时还会截断未来证据,隐藏当时尚不知道的版本结束时间;否则模型可能仅凭“这个版本将在十点结束”就猜中未来变化,得到一个表面很准、实际不能部署的预测器。
3.2 不规则时间间隔怎样进入 Transformer
日常巡逻不会按整齐的帧率留下物体历史。两次观测可能相隔十秒,也可能相隔两天,因此只用事件序号作为位置编码不够。P4D-Nav 同时编码距离查询的流逝时间、一天中的周期和一周中的周期,再把它们与物体、候选位置、正负观测和质量特征融合。HistoryInputs.forward() 的下面这段源码使用以天计的时间输入;其中乘以 24 把年龄转换成小时,后面的正弦余弦分别表达日周期和周周期。
# code/src/readyagent/p4d_belief/models.py: HistoryInputs.forward
query_days = batch["query_time_days"].float()
event_days = batch["event_time_days"].float()
age_hours = ((query_days[:, None] - event_days) * 24.0).clamp_min(0.0)
time_features = torch.stack(
(
torch.log1p(age_hours),
torch.sin(2 * math.pi * event_days),
torch.cos(2 * math.pi * event_days),
torch.sin(2 * math.pi * event_days / 7.0),
torch.cos(2 * math.pi * event_days / 7.0),
),
dim=-1,
)
log1p 让十分钟到一小时这样的近期差异保持可辨,同时避免跨天间隔在数值上过度支配输入。周期编码则能表达“工作日午后杯子常在办公室”一类规律。同文件的 RelativeTimeEncoderLayer 还用事件两两时间差的符号与对数幅值生成每个注意力头的偏置,因此连续时间不仅出现在输入特征中,也影响事件之间的关联强度。换句话说,模型需要理解两条记录之间经过了多久,以及这些记录处在什么生活节律里。
难点提示:把巡逻历史看成不定时拍下的家庭相册,照片排序只告诉我们先后,时间戳才告诉我们两张照片间隔十分钟还是三天。日历时间又补上另一层含义:每个工作日下午的照片可能比昨晚的一张照片更能解释今天下午的物品去向。连续时间编码把这几种信息分开表达,模型才有机会学到可重复的习惯。
4. 留存与迁移:先回答是否还在原处,再分配其他去向
4.1 一个结构化的概率分解
P4D-Nav 没有直接对全部位置做一次平铺分类,而是先估计目标在查询时是否位于最近一次正观测地点 s l a s t s_{\mathrm{last}} slast,记为留存概率 ρ q \rho_q ρq;剩余质量再分配给其他已知候选和 unknown,形成迁移分布 π q \pi_q πq。这种分解把一个很强的先验写进模型:过去的位置往往有参考价值,但它不应压倒所有替代解释。设 b q − b_q^- bq− 是查询时尚未融合新观测的先验,论文的组合方式如下,两部分概率相加仍为一。
ρ q = σ ( w ρ ⊤ h q ) , b q − ( i ) = { ρ q , i = s l a s t , ( 1 − ρ q ) π q ( i ) , i ≠ s l a s t . \rho_q=\sigma(\mathbf w_\rho^{\top}\mathbf h_q), \qquad b_q^-(i)= \begin{cases} \rho_q,&i=s_{\mathrm{last}},\\ (1-\rho_q)\pi_q(i),&i\ne s_{\mathrm{last}}. \end{cases} ρq=σ(wρ⊤hq),bq−(i)={ρq,(1−ρq)πq(i),i=slast,i=slast.
这里 h q \mathbf h_q hq 是历史编码得到的查询表征, σ \sigma σ 是 sigmoid 函数, π q \pi_q πq 在排除最近位置后的合法候选中归一化。留存描述的是查询时的终态,不保证物体从未离开:杯子上午被拿到书房、下午又放回桌面,仍属于当前位于最近观测地点的情况。没有这个区分,读者很容易把分支误解成“是否发生过搬动”的分类器,而论文训练和评估面对的是目标当前在哪个状态。
4.2 源码中的掩码与概率质量分配
下面节选 P4DBelief.forward() 的核心计算,保留了可选兼容性分支,省去函数声明和返回字典。candidate_mask 排除补齐位置,last_candidate_index 指出最近一次正观测对应的候选;先把该候选从迁移分支中屏蔽,才能避免同一个位置既领取留存质量、又领取迁移质量。候选表中的 unknown 参与合法候选计算,从而给候选集外的可能性留下空间。
# code/src/readyagent/p4d_belief/models.py: P4DBelief.forward
query, candidates = self.backbone(batch)
rho = torch.sigmoid(self.persistence(query)).squeeze(-1)
relocation_mask = batch["candidate_mask"].bool().clone()
relocation_mask.scatter_(
1, batch["last_candidate_index"].long()[:, None], False
)
relocation_logits = self.pointer_logits(query, candidates, relocation_mask)
if self.compatibility is not None:
paired = torch.cat(
(query[:, None, :].expand_as(candidates), candidates), dim=-1
)
relocation_logits = relocation_logits + self.compatibility(paired).squeeze(-1)
relocation_logits = relocation_logits.masked_fill(~relocation_mask, -1e4)
relocation = torch.softmax(relocation_logits, dim=-1)
probabilities = relocation * (1.0 - rho[:, None])
probabilities.scatter_(
1, batch["last_candidate_index"].long()[:, None], rho[:, None]
)
probabilities = probabilities * batch["candidate_mask"].float()
这段代码中最值得关注的是最后几行,而不只是前面的神经网络层。softmax 负责在替代位置之间竞争,乘以
1
−
ρ
q
1-\rho_q
1−ρq 限制它们能分到的总质量,再用 scatter_ 写回最近位置的概率。完整分布保存了第一次判断失败后的替代方案,其价值会在在线搜索时显现。只保存概率最大的地点,即使首选预测相同,也会丢失第二、第三个候选之间的相对可信度,后续只能依赖另一个临时搜索策略补救。

图 3 前两节的时间编码与这一节的概率分解连起来:左侧解释证据从哪里来,右侧解释质量如何分配。
5. 训练监督:不需要变化标签,不等于不需要状态真值
5.1 终态损失怎样同时训练两个分支
查询时预测器使用当前目标状态的负对数似然训练。这里要厘清的是,论文所说“不需要显式变化标签”,指的是不用另行标注每段间隙里物体是否搬动、搬动几次;训练样本仍然需要查询时目标位置作为监督。若真值就是最近观测地点,损失推动 ρ q \rho_q ρq 增大;若真值在其他位置,损失同时推动 ρ q \rho_q ρq 减小、正确迁移候选的概率增大。这样,两个分支通过同一个最终状态目标协同学习,不需要构造一份额外的移动事件分类标签。
L s t a t e = − log b q − ( s t q o ) = { − log ρ q , s t q o = s l a s t , − log ( 1 − ρ q ) − log π q ( s t q o ) , s t q o ≠ s l a s t . \mathcal L_{\mathrm{state}} =-\log b_q^-(s_{t_q}^{o}) =\begin{cases} -\log\rho_q,&s_{t_q}^{o}=s_{\mathrm{last}},\\ -\log(1-\rho_q)-\log\pi_q(s_{t_q}^{o}),&s_{t_q}^{o}\ne s_{\mathrm{last}}. \end{cases} Lstate=−logbq−(stqo)={−logρq,−log(1−ρq)−logπq(stqo),stqo=slast,stqo=slast.
公式里的
s
t
q
o
s_{t_q}^{o}
stqo 是目标在查询时的真实状态,训练时可用、部署时隐藏。state_nll() 只需从概率表取出真值对应的一项,再计算平均负对数概率。数值截断防止对零取对数,它不代表模型已经校准,也不会自动解决数据泄漏。模型最终能否学到有效规律,仍取决于输入是否只包含查询之前的证据,以及训练、验证和测试是否按完整时间序列隔离。
# code/src/readyagent/p4d_belief/models.py: state_nll
def state_nll(probabilities: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
selected = probabilities.gather(1, targets.long()[:, None]).squeeze(1)
return -torch.log(selected.clamp_min(1e-9)).mean()
从损失展开还能看出结构化设计的作用:它要求模型分别判断“最近位置还可信多少”和“其余质量应分给谁”。这是一种归纳偏置,而不是新增了一种监督信号。论文在相同历史 token 与候选编码条件下比较直接 Transformer 和结构化版本,才能更接近回答分解本身是否有效。若同时换了记忆内容、候选集和导航策略,就无法仅凭最终成功率上升判断留存迁移建模究竟贡献了多少。
5.2 持续演化还需要转移头与检出校准
查询时先验只能回答一个时刻的问题,持续演化任务还需要估计时间间隔
Δ
t
\Delta t
Δt 后状态会如何改变。公开实现的 transition_model.py 用上下文、源候选、目的候选和时距构建转移概率,每个源状态对应的一行在目的状态上归一化;训练则根据时序状态对选择相应行和终态,计算交叉熵。状态对按同一对象、同一世界及时间顺序构造,不能把不同家庭或互不相干的世界轨迹拼成一次转移。
另一个需要训练的量是“目标如果在这里,这个视角能否检出它”。calibration.py 使用验证观测拟合标准化特征上的逻辑回归,输入包括覆盖度、距离、角度、投影像素、深度质量、类别召回与图像质量。条件检出概率不等于检测器置信度:一个视角没有任何检测框时,仍需要知道这次未检出有多大解释力,不能因为缺少检测分数,就把一次空观测当成强否定。
6. 抵达时间感知:概率最高的地点不一定最值得先去
6.1 当前滤波状态与未来预测必须分开
在线循环维护当前时刻的信念 b j b_j bj,并通过转移核 K θ ( j ) ( Δ t ) K_\theta^{(j)}(\Delta t) Kθ(j)(Δt) 表达未来变化。上标加号表示已经融合该时刻观测的后验,减号表示尚未融合新观测的先验。真正移动一段时间后,系统把当前后验传播到新的当前时刻;比较路线时,则分别为每个候选计算抵达预测。这里的关键是,当前状态只随实际经过的时间推进,候选抵达预测是临时计算,多个未来方案不能依次写回同一份当前信念。
b j + 1 − = b j + K θ ( j ) ( t j + 1 − t j ) , p j , i a r r = [ b j + K θ ( j ) ( τ ^ j , i ) ] i . b_{j+1}^-=b_j^+K_\theta^{(j)}(t_{j+1}-t_j), \qquad p_{j,i}^{\mathrm{arr}} =\left[b_j^+K_\theta^{(j)}(\widehat\tau_{j,i})\right]_i. bj+1−=bj+Kθ(j)(tj+1−tj),pj,iarr=[bj+Kθ(j)(τ j,i)]i.
…详情请参照古月居
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)