Skip to content

WeberFU23/Multi-Object-Navigation-Benchmark

Repository files navigation

Multi-Object Navigation Benchmark From HM3D Semantic

本项目用于构建和评测基于 HM3D 场景的多目标视觉导航 benchmark。Agent 根据目标指令、当前 RGB 图和深度图,在 Habitat-Sim 场景中寻找一个或多个目标物体。

如果只想评测 agent,不需要重新构建数据集;准备好 HM3D 原始场景和发布的 dataset_semantic/ 后即可运行 run_eval.py。数据生成、属性提取和 episode 构建流程见 DATASET_BUILDING.md

任务模式

支持四种 target_mode

single: 寻找一个指定目标
any:    多个符合描述的目标中找到任意一个
many:   寻找指定数量的符合描述目标
all:    寻找全部符合描述目标

其中 many 对应 MOS, Multi-Object Search;all 对应 MOC, Multi-Object Collection。single/any 主要用于 smoke test、消融和兼容单目标导航设置。

快速开始

1. 准备环境

推荐使用 conda。当前项目已验证版本:

Python 3.9
habitat-sim 0.3.3
habitat-lab 0.3.3

创建环境:

conda create -n habitat python=3.9 cmake=3.14.0 -y
conda activate habitat

安装 Habitat-Sim:

# 有显示器的本地机器
conda install habitat-sim=0.3.3 withbullet -c conda-forge -c aihabitat -y

# 无显示器服务器或集群机器
conda install habitat-sim=0.3.3 withbullet headless -c conda-forge -c aihabitat -y

如果找不到 0.3.3,可以先查看可用版本:

conda search habitat-sim -c aihabitat

安装 Habitat-Lab:

git clone --branch stable https://github.com/facebookresearch/habitat-lab.git
cd habitat-lab
pip install -e habitat-lab

本项目不需要 habitat-baselines。如果后续要训练 Habitat Baselines agent,再额外安装:

pip install -e habitat-baselines

安装本项目依赖:

cd /path/to/habitat_benchmark
pip install numpy pillow tqdm python-dotenv openai requests opencv-python omegaconf

无 GUI 服务器可把 opencv-python 换成:

pip install opencv-python-headless

检查安装:

python -c "import habitat_sim, habitat; print('ok')"

官方安装说明:

2. 准备数据

评测需要两类数据:

  1. HM3D 原始场景和 navmesh,用于 Habitat-Sim 渲染、碰撞和路径长度计算。
  2. 本 benchmark 发布的数据,即 dataset_semantic/<scene_id>/goals.jsonqueries.jsonepisodes.json

推荐目录结构:

/path/to/hm3d/val/
  00800-TEEsavR23oF/
    TEEsavR23oF.basis.glb
    TEEsavR23oF.basis.navmesh

/path/to/habitat_benchmark/dataset_semantic/
  TEEsavR23oF/
    goals.json
    queries.json
    episodes.json

已经生成好的 benchmark 数据,对外发布或提交时通常只需要包含 dataset_semantic/dataset_semantic_attributes/、rendered viewpoint 图片和 VLM 中间结果属于数据构建阶段产物,不是运行测评的必要输入。

如果需要从 HM3D semantic annotations 重新生成数据,请阅读 DATASET_BUILDING.md

3. 运行 smoke test

自然语言模式是默认测评模式,使用 queries.json 中的自然语言 instruction:

python run_eval.py \
  --config hm3d_config.yaml \
  --agent random \
  --goal-type description \
  --dataset-dir dataset_semantic \
  --scene-root /path/to/hm3d/val \
  --limit 1 \
  --episode-limit 5

Object-nav 模式模仿 GOAT 的 object goal 语义,goal_text 是数据集中的类别名:

python run_eval.py \
  --config hm3d_config.yaml \
  --agent random \
  --goal-type object \
  --dataset-dir dataset_semantic \
  --scene-root /path/to/hm3d/val \
  --limit 1 \
  --episode-limit 5

Object-nav 只使用纯类别 query:query_program.where 中唯一条件必须是 category == <类别名>,且该类别必须出现在当前 scene 的 goals.json 中。带 color、material、feature 等属性约束的 query 仍属于自然语言/description 模式。

如果评测能正常启动,说明 Habitat-Sim、HM3D 路径和 benchmark 数据基本配置已经连通。

适配自己的 Agent

本 benchmark 使用 GOAT-style agent API,并为 MOS/MOC 多目标导航扩展了动作语义。测评者通常只需要模仿 agents_to_test/simple_agent.py,把其中的策略逻辑替换成自己的方法。

两种 goal 类型使用同一套 agent 接口:

description: goal_text 是自然语言目标描述
object:      goal_text 是 dataset-defined category,例如 chair / table / bath mat

Object 模式不是任意开放词输入;它只使用当前 benchmark 数据中出现过的类别,并且只从纯类别 query 构造 episode。target_mode 仍然可以是 single / any / many / all,指标和自然语言模式一致。

最小接口:

def act(self, observation) -> int:
    ...

推荐同时实现:

def reset(self) -> None:
    ...

不需要继承基类,不需要注册到 Habitat,也不需要理解评测框架内部结构。

适配步骤

  1. agents_to_test/ 下新建文件,例如 agents_to_test/my_agent.py
  2. 定义一个 agent 类,例如 MyAgent
  3. 实现 act(self, observation),每一步返回一个动作 ID 或 benchmark_api.Action
  4. 如果 agent 有 episode 内历史状态,实现 reset(self)
  5. observation.rgbobservation.depthobservation.goal_textobservation.task_prompt 等字段读取输入。
  6. 到达一个匹配目标附近时返回 Action.TARGET_FOUND 或整数 6
  7. 认为整个 episode 完成时返回 Action.FINISH 或整数 0
  8. --agent agents_to_test.my_agent:MyAgent 指定要评测的 agent。

single/any 下,找到目标后返回 60 都可以结束 episode,推荐返回 6many/all 下,6 只表示“当前位置发现了一个目标”,不会结束 episode;只有 0 表示 agent 主动结束整个任务。

Observation

自定义 agent 每一步收到 benchmark_api.Observation

@dataclass(frozen=True)
class Observation:
    rgb: np.ndarray              # (H, W, 3), 当前 RGB
    depth: Optional[np.ndarray]  # 当前深度图
    goal_text: str               # object 模式为类别名;description 模式为自然语言描述
    goal_embedding: Optional[np.ndarray]
    goal_image: Optional[np.ndarray]
    goal_type: str               # object / description / image,目前主要是 object 或 description
    subtask_type: str            # GOAT-style 别名,同 goal_type
    target_mode: str             # single / any / many / all
    target_count: Optional[int]  # many 模式要求数量,其他模式通常为 None
    episode_id: str
    scene_id: str
    step_count: int
    gps: np.ndarray              # (2,), 当前 x/z 平面位置
    compass: float               # 当前朝向角,单位 rad
    max_steps: int
    task_prompt: Optional[str]   # 完整任务 prompt,给 VLM/LLM agent 使用
    previous_action: Optional[int]
    extras: Optional[dict]

注意:many/all 模式下,goal_texttask_prompt 都不会告诉 agent 还剩几个目标未找到。goal_embeddinggoal_image 预留给后续 object/image/language 多模态目标;当前 object 和 description 模式都主要使用文本目标。

Action

可以直接返回整数,也可以返回 benchmark_api.Action

from benchmark_api import Action

return Action.MOVE_FORWARD

动作定义:

0 = FINISH
1 = MOVE_FORWARD, 0.25m
2 = TURN_LEFT, 30 degrees
3 = TURN_RIGHT, 30 degrees
4 = LOOK_UP, 30 degrees
5 = LOOK_DOWN, 30 degrees
6 = TARGET_FOUND / SUBTASK_STOP
9 = LEGACY_FINISH

Agent 模板

仓库提供了推荐模板:

agents_to_test/simple_agent.py

测评者可以复制该文件,或新建:

agents_to_test/my_agent.py

示例:

from benchmark_api import Action, Observation


class MyAgent:
    def reset(self) -> None:
        self.steps = 0

    def act(self, observation: Observation) -> int:
        self.steps += 1

        # observation.rgb / observation.depth 可以送入自己的 VLM 或导航模型。
        # observation.goal_text 是 GOAT-style 目标文本。
        # observation.task_prompt 是完整任务说明。
        if self.steps < 10:
            return Action.MOVE_FORWARD

        if observation.target_mode in {"many", "all"}:
            return Action.FINISH
        return Action.TARGET_FOUND

运行自定义 agent:

python run_eval.py \
  --config hm3d_config.yaml \
  --agent agents_to_test.my_agent:MyAgent \
  --limit 1 \
  --episode-limit 5

运行仓库自带模板:

python run_eval.py \
  --config hm3d_config.yaml \
  --agent agents_to_test.simple_agent:SimpleAgent \
  --limit 1 \
  --episode-limit 1 \
  --max-steps 2

API / VLM Agent

如果要接入外部 VLM,可以在 act() 中把 observation.rgbobservation.depthobservation.goal_text 发给自己的模型服务,再把模型输出解析成动作 ID:

from benchmark_api import Action, Observation


class MyVLMAgent:
    def __init__(self):
        self.history = []

    def reset(self) -> None:
        self.history = []

    def act(self, observation: Observation) -> int:
        response = call_your_model(
            rgb=observation.rgb,
            depth=observation.depth,
            instruction=observation.goal_text,
            history=self.history,
        )
        action = parse_action(response)
        self.history.append((observation.step_count, action))
        return action

其中 call_your_model()parse_action() 由被测者自己实现。

兼容旧接口

旧版自定义 agent 如果已经实现:

def act(self, rgb=None, depth=None, prompt=None) -> int:
    ...

可以临时用兼容模式运行:

python run_eval.py \
  --config hm3d_config.yaml \
  --agent my_agents.old_agent:OldAgent \
  --legacy-agent-api \
  --action-protocol legacy

新代码建议使用 act(observation) 接口。

动作协议

默认使用 --action-protocol goat,即 GOAT-style API with MOC extension。输入观测尽量沿用 GOAT-Bench 风格,动作语义扩展出 MOC 所需的全局终止判断。

默认动作:

0 = FINISH, agent 认为整个 episode 已完成
1 = MOVE_FORWARD, 0.25m
2 = TURN_LEFT, 30 degrees
3 = TURN_RIGHT, 30 degrees
4 = LOOK_UP, 30 degrees
5 = LOOK_DOWN, 30 degrees
6 = TARGET_FOUND / SUBTASK_STOP, agent 认为当前位置附近有一个匹配目标实例
9 = LEGACY_FINISH, 旧版 finish 别名,兼容保留

和 GOAT 的关键区别:

  • GOAT 中 SUBTASK_STOP=6 通常表示当前子任务完成,benchmark 会进入下一个给定目标。
  • 本 benchmark 中 TARGET_FOUND=6 只登记当前发现的实例,不会告诉 agent 下一个目标是谁。
  • Agent 必须自己决定继续搜索哪里,以及何时 FINISH

single/any

  • Agent 找到目标后输出 60 都可以结束 episode。
  • 推荐输出 6,与 GOAT 的 SUBTASK_STOP 风格保持一致。

many/all

  • Agent 认为当前位置附近有匹配目标时输出 6
  • 6 会尝试确认当前附近的新目标,但不会结束 episode,也不会给出下一个目标。
  • Agent 认为整个任务完成时输出 0
  • 9 作为旧版 finish alias 仍然被接受。
  • 评测时不会告诉模型还剩几个目标没找到。

旧协议中,many/all 下的 0 仍表示确认目标,9 表示结束 episode:

python run_eval.py ... --action-protocol legacy

compat 协议也接受 6,但保留旧版 0 确认语义。

运行评测

自然语言模式

python run_eval.py \
  --config hm3d_config.yaml \
  --agent agents_to_test.simple_agent:SimpleAgent \
  --goal-type description \
  --dataset-dir dataset_semantic \
  --scene-root /path/to/hm3d/val

Object-nav 模式

python run_eval.py \
  --config hm3d_config.yaml \
  --agent agents_to_test.simple_agent:SimpleAgent \
  --goal-type object \
  --dataset-dir dataset_semantic \
  --scene-root /path/to/hm3d/val

两种模式使用同一套动作协议和指标。区别是:description 使用自然语言 instruction;object 只使用纯类别 query,并把 observation.goal_text 设置为类别名。

Human agent

conda activate habitat

python run_eval.py \
  --config hm3d_config.yaml \
  --agent human \
  --limit 1 \
  --episode-limit 1 \
  --render-live \
  --log-prompts \
  --log-positions

Human 控制:

0 = finish episode
1 = move_forward
2 = turn_left
3 = turn_right
4 = look_up
5 = look_down
6 = target_found
9 = legacy_finish
q = quit

Random agent smoke test

conda activate habitat

python run_eval.py \
  --config hm3d_config.yaml \
  --agent random \
  --limit 1 \
  --episode-limit 2 \
  --max-steps 2 \
  --log-episodes

常用调试参数:

--log-positions
--log-prompts
--log-episodes

OER-space 和 FTR 中的“是否仍有未探索空间”使用 navmesh 采样 oracle 近似计算。默认每个 episode 采样 200 个可达点,agent 轨迹进入 1.5m 半径即视为探索该点:

--space-coverage-samples 200
--space-coverage-radius 1.5

快速 smoke test 可以关闭 OER-space

--space-coverage-samples 0

评测指标

当前正式 benchmark 指标与 多目标导航.md 保持一致。主榜单只聚合 many/all 多目标 episode,报告:

sr
f1
spl_multi

同时输出诊断指标:

precision
recall
pe
tse
rtsr
false_termination_rate / ftr
oer_target
oer_space

为兼容已有脚本,输出中仍保留 splraw_splcoverage 等旧字段;其中 splspl_multi 的别名。

single / any:传统 SR / SPL

singleany 是单目标或任一目标导航任务,当前只作为 smoke test、消融和基础导航能力检查。它们的模式级指标只输出:

sr_single, spl_single
sr_any, spl_any

single/any 不单独报告 precision / recall / f1。这两个模式没有多目标部分完成问题,F1 基本会退化成与 SR 同步的 0/1 指标。

多目标结果层:SR / Precision / Recall / F1

required_target_count 按模式定义:

single: 1
any:    1
many:   target_count
all:    len(target_object_ids)

Agent 输出 6 表示确认当前附近目标。评测器会记录 report_events

{
  "step": 12,
  "near_targets": [101],
  "new_targets": [101],
  "duplicate_targets": [],
  "is_true_positive": true,
  "is_false_positive": false
}

每个 episode 统计:

  • TP6 信号覆盖此前未认领过的合法目标实例。
  • FP6 信号附近没有新的合法目标实例,包括误报和重复认领。
  • FN:episode 结束时仍未被覆盖的 required target。

计算方式:

precision = TP / (TP + FP)
recall    = min(1, found_targets / required_target_count)
f1        = 2 * precision * recall / (precision + recall)

如果 TP + FP = 0,则 precision = 0。如果 precision + recall = 0,则 f1 = 0

SR 按任务类型定义:

MOS / many: found_targets >= target_count
MOC / all:  found_targets == len(target_object_ids) 且 agent 主动输出 FINISH(0)

这意味着 all 模式中,即使 agent 找到了所有目标,只要没有主动输出 0,也不算 MOC 成功。9 作为 legacy finish alias 也会被接受。

路径效率层:SPL-multi / PE / TSE

trajectory_length 是 agent 实际走过的路径长度 p_i

optimal_subset_distance 是全局最优多目标开路径长度 l_i。对于 many,它对应从所有合法目标中选择 target_count 个后的最短路径;对于 all,它对应访问全部目标的最短路径。

agent_subset_distance 是针对 agent 实际确认的目标子集重新计算的最优开路径长度。

Episode 级 SPL-multi

spl_multi_i = success_i * optimal_subset_distance_i
              / max(trajectory_length_i, optimal_subset_distance_i)

细粒度拆分:

pe_i  = agent_subset_distance_i / max(trajectory_length_i, agent_subset_distance_i)
tse_i = optimal_subset_distance_i / agent_subset_distance_i

PE 衡量“agent 选定这组目标后走得是否绕路”。TSE 衡量“agent 选的目标子集是否接近全局最优子集”。当前 TSE 主要对 many/MOS 有意义;all/MOC 没有子集选择,默认不作为主要诊断项。

终止与记忆诊断:FTR / OER / RTSR

RTSR 衡量重复目标选择率:

rtsr = (m - unique(S)) / m

其中 S 是 agent 发出的确认信号对应到真实目标实例后的序列。重复确认同一实例会提高 RTSR

FTR 只在 MOC/all 中统计。agent 主动 FINISH 时,如果仍未找全目标,或 navmesh 采样 oracle 认为仍有未探索空间,则记为 false termination:

ftr = mean(agent 输出 FINISH 时仍未找全目标)

OER-target 衡量 agent 已经找齐 required targets 后仍继续探索的比例:

oer_target_i = (finish_step_i - all_required_found_step_i) / max_steps

如果 agent 找齐后一直没有 FINISH,则用 max_steps 作为停止时间。

OER-space 衡量 sampled navmesh 空间已经被探索完后,agent 仍继续探索的比例:

oer_space_i = (finish_step_i - space_exhausted_step_i) / max_steps

只有当 episode 内确实达到 space_exhausted_step 时才计算;否则该 episode 的 oer_spacenull,聚合时跳过。space_exhausted_step 来自 --space-coverage-samples--space-coverage-radius 控制的近似空间覆盖 oracle。

聚合方式

总指标对 episode-level 指标取平均。sr / f1 / spl_multi 默认只聚合 many/all 多目标 episode:

sr        = mean(sr_i)
f1        = mean(f1_i for many/all)
spl_multi = mean(spl_multi_i)

按任务类型额外输出:

sr_mos, f1_mos, spl_multi_mos
sr_moc, f1_moc, spl_multi_moc

按旧模式额外输出:

sr_single, spl_single
sr_any, spl_any
sr_many, f1_many, precision_many, recall_many
sr_all, f1_all, precision_all, recall_all

项目结构

仓库按“评测核心、agent 接口、数据构建、示例数据和文档”组织:

.
├── README.md                         # benchmark 使用说明:接口、运行评测、指标
├── DATASET_BUILDING.md               # 数据构建说明:从 HM3D semantic 生成 episodes
├── 多目标导航.md                     # 任务和正式论文版指标设计文档
├── hm3d_config.yaml                  # Habitat-Sim 配置
│
├── run_eval.py                       # 评测入口,解析参数并启动 evaluator
├── evaluator.py                      # episode 执行、动作协议、目标确认和路径统计
├── metrics.py                        # episode/result 聚合指标
├── dataset.py                        # 读取 goals / queries / episodes 数据格式
├── benchmark_api.py                  # 对外暴露的 Observation / Action 接口
├── policy.py                         # human / random / API / custom agent 适配
├── prompt.py                         # agent prompt 和 VLM prompt
│
├── agents_to_test/
│   └── simple_agent.py               # 推荐给测评者复制/修改的 agent 模板
│
├── dataset_building/
│   ├── extract_from_semantic.py      # 从 HM3D semantic annotations 构建 goals.json
│   ├── attribute_extractor.py        # 渲染 viewpoint 并用 VLM 提取目标属性
│   ├── normalize_goal_attributes.py  # 规范化 color / material 等属性
│   ├── build_queries.py              # 根据 goal 类别和属性生成结构化 queries
│   ├── generate_query_language.py    # 为 query 生成自然语言描述
│   └── build_episodes.py             # 根据 queries 和 navmesh 生成 episodes
│
├── tools/
│   └── show_pic_of_instance.py       # 渲染 goal viewpoint,人工检查图片质量
│
└── dataset_semantic/
    └── <scene_id>/
        ├── goals.json                # 场景中的候选目标实例及几何/属性信息
        ├── queries.json              # 结构化 query 和自然语言指令
        └── episodes.json             # 可直接评测的 episode 列表

数据构建阶段还可能产生:

dataset_semantic_attributes/<scene_id>.json  # 属性提取中间结果
rendered_viewpoints/<scene_id>/<goal_id>/    # viewpoint 检查图片

这些文件不是运行测评的必要输入。

常见问题

all / many 模式会告诉模型还剩几个目标吗?

不会。many/all 模式下,模型需要自己判断是否找全。到达一个目标附近输出 6,认为任务完成输出 0

已经生成的数据只发布 dataset_semantic/ 可以吗?

可以。评测所需 benchmark 数据是 dataset_semantic/<scene_id>/goals.jsonqueries.jsonepisodes.json。运行测评时还需要用户本地准备 HM3D 原始场景和 navmesh。

数据构建问题在哪里看?

HM3D semantic annotations、viewpoint、属性提取、query 生成和 episode 生成相关问题见 DATASET_BUILDING.md

Habitat 报 scene_instance warning 是否一定失败?

不一定。只要场景、navmesh 和渲染能正常加载,类似缺少 *.basis.scene_instance.json 的 warning 可能不影响评测。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages