AgentCanvas / Pages / Developer Guide / Nodesets / Env / HM-EQA
2026-06-11 15:37

The HM-EQA NodeSet (EnvHMEQANodeSet) wraps Habitat-Sim + HM3D semantic meshes for House-Mesh Embodied Question Answering (Ren et al. 2024, explore-eqa). Each episode is a question about a specific scene+floor; the agent explores via free-pose teleports and emits a letter answer (A/B/C/D).

Sibling of Habitat β€” not an extension. The two cannot share an environment: Habitat NodeSet pins habitat-sim 0.1.7 + Python 3.8 (VLN-CE); HM-EQA uses a newer habitat-sim + Python 3.9 + torch 2.2.1. Different conda env (hmeqa), different simulator instance.


1. Overview

Purpose

House-Mesh Embodied Question Answering asks multi-choice questions over HM3D semantic scenes. Each episode = one question + one scene + one floor + one initial pose. The 500-question val split ships in the HM-EQA repo's CSVs (HM3D train-split license gates the training Qs).

Architecture

Three-layer pattern mirroring Habitat:

  1. HMEQAEnvManager (singleton) β€” per-episode simulator rebuild (one .glb per episode), pinned single-thread executor for GL affinity, loads questions.csv + scene_init_poses.csv once.
  2. Canvas tool nodes β€” 5 tools (reset / step / episode_info / cam_intrinsics / evaluate).
  3. EnvHMEQANodeSet β€” server_python points to $HMEQA_PYTHON, so the framework auto-routes the nodeset into a ac-hmeqa subprocess when loaded via ?mode=server.

Server-Mode Only

The nodeset always runs server-mode: habitat-sim isn't installed in the default agentcanvas env. Set HMEQA_PYTHON=/home/$(whoami)/miniforge3/envs/ac-hmeqa/bin/python before loading, or rely on the default.


Interface migrated (2026-06-09). This nodeset now implements the gym-like env interface β€” reset (metadata only), step_* (control signals, no observation), observe_* (pull perception), evaluate. The node list below predates the migration and may be stale; the template's migration map (Β§6) and capability matrix (Β§3) are authoritative.

2. Canvas Nodes

Node Type Display Name Input Ports Output Ports Purpose
env_hmeqa__reset HM-EQA: Reset trigger (ANY, optional) rgb, depth, pose, cam_pose_matrix, pose_normal, angle, floor_height, question, episode_id, num_step Start/restart the episode selected on the env panel; emits initial obs + question
env_hmeqa__step HM-EQA: Step (teleport) action (TEXT JSON) rgb, depth, pose, cam_pose_matrix, pose_normal, angle, floor_height, question, episode_id, step_index, done Free-pose teleport; emits post-step obs; fires done=True when step_index >= num_step
env_hmeqa__episode_info HM-EQA: Episode Info (none) question, raw_question, choices, answer, scene, floor, episode_id, num_step, tsdf_bnds, floor_height Episode metadata incl. 3Γ—2 TSDF voxel-volume bounds for the planner
env_hmeqa__cam_intrinsics HM-EQA: Cam Intrinsics (none) cam_intr (3Γ—3) Pinhole intrinsics β€” episode-constant, derived from hfov + img size
env_hmeqa__evaluate HM-EQA: Evaluate pred_letter (TEXT) success (BOOL), gt (TEXT), metrics (METRICS) Post-hoc success check β€” compare predicted letter to GT

Output-port highlights


3. Action Contract

Free-pose teleport, sent as JSON TEXT:

{"position_normal": [x, y], "angle": 1.5708}

There is no mid-episode "answer" action. Answer emission is post-hoc on the method side β€” see ExploreEQA NodeSet aggregate step. This deliberately sidesteps the action-manifest unification tracked as TODO #46 β€” once a second multi-action env shows up we'll bundle them.

Source reference: workspace/nodesets/_upstream/explore-eqa/upstream/run_vlm_exp.py:313-321.


4. Observation Bundle

Every reset and step returns the same 10/11-port bundle. The bundle is designed so a downstream method nodeset (e.g. ExploreEQA) can run TSDFPlanner.integrate, find_prompt_points_within_view, and find_next_pose without needing any env-side knowledge.

Port Wire Type Meaning
rgb IMAGE HΓ—WΓ—3 uint8 (alpha dropped)
depth DEPTH HΓ—W float32 in meters
pose POSE habitat-frame agent pose (canvas UI only)
cam_pose_matrix ANY 4Γ—4 TSDF-frame camera extrinsic
pose_normal ANY 3-vector normal-frame position
angle ANY yaw (radians)
floor_height ANY episode-constant z (normal frame)
question TEXT formatted multi-choice question (tail: \nA. <choice>\nB. ...)
episode_id TEXT episode index as string
step_index ANY step counter (step only)
done BOOL step_index >= num_step (step only)

Black-image guard

The env tracks is_black in the bundle when the agent falls through the floor / outside the mesh. The HM-EQA method nodeset skips TSDF integration on black frames (matching upstream behaviour).


5. Configuration

Controlled via module-level _DEFAULTS in workspace/nodesets/env/env_hmeqa/__init__.py. Override via env vars if needed:

Key Default Notes
img_width 640 pixels
img_height 480 pixels
hfov 120 degrees
camera_height 1.5 m (above floor) β€” paper cfg/vlm_exp.yaml line 18
camera_tilt_deg -30.0 negative = tilted downward
tsdf_grid_size 0.1 m β€” consumed by the method-side TSDFPlanner
init_clearance 0.5 m β€” initial pose clearance radius
max_step_room_size_ratio 3.0 num_step = int(sqrt(scene_size) * ratio)
black_pixel_ratio 0.5 frame is "black" if #black-pixels > ratio * H * W β€” paper cfg/vlm_exp.yaml line 30
seed 42 pathfinder seed

Paper-baseline values are mirrored from workspace/nodesets/_upstream/explore-eqa/upstream/cfg/vlm_exp.yaml.

Data layout (ADR-platform-005):

data/hm3d/hmeqa/questions.csv           HM-EQA Q&A
data/hm3d/hmeqa/scene_init_poses.csv    per-(scene, floor) init
data/hm3d/hmeqa/Open_Sans/              annotation font (method-side)
data/hm3d/hm3dsem/{scene}/{scene[6:]}.basis.glb       meshes (HuggingFace-gated)
data/hm3d/hm3dsem/{scene}/{scene[6:]}.basis.navmesh   navmesh

Environment variable overrides:


6. Env panel Integration

HMEQAEnvPanel is a two-field cascade:

Field Type Options
split select val (only split shipped in the CSV)
episode_index select 0 .. ~500

Actions: play, pause, stop, reset. The play button triggers run_start; Reset re-applies the currently-selected episode without touching graph state.

The split field is retained as a single-option select to give future train/test splits a zero-migration path.


7. Environment Setup

One-time install:

bash scripts/install/install_ac_hmeqa.sh      # creates the hmeqa conda env
bash scripts/data/fetch_episodes_vln.sh --hmeqa # stages CSVs + fonts

HM3D mesh download is manual β€” gated by HuggingFace auth:

  1. Request access at https://aihabitat.org/datasets/hm3d-semantics
  2. huggingface-cli login with a read token
  3. Download hm3d-train-habitat-v0.2.tar and extract to data/hm3d/hm3dsem/

See also:

AgentCanvas docs