Skip to content

Configuration

All runtime configuration is done via environment variables. No config files are required — docker-compose sets them for you, but they can be overridden.

Core variables

Variable Default Description
XIAO_HEI_RESPONDER dummy Which responder to use: dummy, perception, or scene_gemini (the submission stack)
XIAO_HEI_VLM_TICK_HZ 1.0 VLM tick rate in Hz

Gemini engine

Variable Default Description
XIAO_HEI_GEMINI_API_KEY (required) API key. GeminiEngine.warmup() validates it at boot, so an invalid key crash-loops the container
XIAO_HEI_GEMINI_MODEL gemini-2.5-flash Model id
XIAO_HEI_GEMINI_TEMPERATURE 0.2 Sampling temperature
XIAO_HEI_GEMINI_MAX_OUTPUT_TOKENS 2048 Response token cap
XIAO_HEI_GEMINI_THINKING_BUDGET 0 Thinking tokens. 0 disables (keeps the JSON answer from being truncated); -1 = dynamic
XIAO_HEI_GEMINI_IMAGE_LONG_EDGE 1280 Downscale long edge before send

Responder loop

Variable Default Description
XIAO_HEI_GEMINI_MAX_EXPLORE_TICKS 120 Ticks without a reachable frontier before committing to an answer (60 s at 2 Hz)
XIAO_HEI_GEMINI_MAX_TICKS 240 Hard cap on ticks per question

Perception sidecar

Variable Default Description
XIAO_HEI_PERCEPTION_BASE_URL http://localhost:8001 Sidecar URL the responder talks to
XIAO_HEI_PERCEPTION_SCORE_THRESHOLD 0.25 YOLO-World detection score gate. Lower → more detections, more noise
XIAO_HEI_PERCEPTION_MIN_INLIERS 10 LiDAR-return count below which a detection mask is dropped
XIAO_HEI_SCAN_KEYFRAMES 10 Sweeps accumulated to densify the sparse single sweep before lifting. One per tick, so this is a window in ticks — 10 ≈ 5 s at the 2 Hz default
XIAO_HEI_SCAN_VOXEL_M 0.05 Voxel size (m) for downsampling the accumulated scan

Exploration

Variable Default Description
XIAO_HEI_EXPLORATION_STRATEGY frontier Exploration algorithm: frontier (cluster the frontier, score by size/distance) or nbv (sample reachable poses, score by unknown-gain/path-cost). Also names the artefact subdirectory. Unknown values disable exploration with an error log — they do not fall back to frontier
XIAO_HEI_EXPLORATION_MAX_WAYPOINTS 500 Waypoint budget. Set to 0 to disable exploration entirely and go straight to question answering. Exploration runs to completion (budget / skips / no frontiers) even if a question arrives first — the answer is deferred until it finishes
XIAO_HEI_EXPLORATION_MAX_SECONDS 480 (8 min) Wall-clock cutoff for the sweep. The clock starts on the first tick with a pose — not at node boot, since /state_estimation takes 90-190 s to arrive and the robot cannot explore before it does. On expiry the loop logs DONE reason=time_limit, saves both PNGs, and hands over to the responder. 0 disables
XIAO_HEI_EXPLORATION_MAX_WAYPOINT_DIST 1.5 Preferred maximum distance (metres) to a frontier target. Closer targets score higher; if all exceed this cap the nearest valid one is used as a fallback
XIAO_HEI_EXPLORATION_LOG_DIR /exploration_logs (GPU compose) Base directory for the run's artefacts. The run lands in <log dir>/<scene>/<strategy>/ — scene from the basename of XIAO_HEI_SCENE_DIR_HOST (default_scene when no scene is mounted), strategy from XIAO_HEI_EXPLORATION_STRATEGY (no_exploration when the waypoint budget is 0). Keying on both means neither a second scene nor a second algorithm clobbers an earlier run. The text log is always written; the two PNGs only when this variable is set
XIAO_HEI_EXPLORATION_SNAPSHOT_S 30 How often (seconds) exploration.png and rviz.png are re-written while the sweep is still running, so a run killed before DONE still leaves images. Each snapshot overwrites the last. Set to 0 to save only at the end
XIAO_HEI_EXPLORATION_MAP_LOG_S 10 How often (seconds) a MAP heartbeat line goes into exploration.log: elapsed, robot pose, path length, and the grid counters (free, occupied, frontier, frontier_open, no_target, free_m2, reachable). This is the coverage curve — WP_* events alone record decisions, not how much of the scene was seen. Watch for reachable falling away from free: that is the strategy walling itself off from its own map. Set to 0 to disable
XIAO_HEI_SCENE_DIR_HOST (unset) Host path of the extracted scene. Mounts the scene into the simulator, and its basename names the log dir — .../chinese_room → exploration_logs/chinese_room/<strategy>/. Passed to ai_module by every compose file
DISPLAY (unset) X display the simulator renders RViz into. When set (and the X socket is mounted), the node screenshots the RViz window to rviz.png as exploration finishes. When unset — a headless host, or the challenge submission — the screenshot is skipped with an info log. Needs xhost +local: on the host, since the containers run as a different user

The exploration phase writes three artefacts into exploration_logs/<scene>/<strategy>/ — the log continuously, the two PNGs every XIAO_HEI_EXPLORATION_SNAPSHOT_S seconds and again on DONE:

File What it is
exploration.log Structured event log (START / WP_SET / WP_ADVANCE / WP_SKIP / DONE)
exploration.png The explorer's own view: occupancy grid + visited waypoints
rviz.png The simulator's view: the traversed path over the scene mesh, as RViz drew it

The screenshot is strictly best-effort. A missing display, a missing python-xlib, or an RViz that never opened all produce a warning and nothing more — exploration has already finished by then, so a lost debug image never fails the run.

Logging

Variable Default Description
XIAO_HEI_VLM_LOG_DIR /vlm_logs (GPU compose) Directory for VLM tick logs. Unset = logging disabled

Docker build-time

Variable Default Description
XIAO_HEI_EXTRA perception pip optional extra to install at build time. The submission image uses perception,gemini,exploration

Configuration in code

All Gemini-related variables are loaded by GeminiConfig.from_env(), which raises if the API key is missing:

from xiao_hei_vln.gemini import GeminiConfig

config = GeminiConfig.from_env()
print(config.model)         # gemini-2.5-flash
print(config.temperature)   # 0.2