pySLAM NodeSet
The pySLAM NodeSet (ModelPySlamNodeSet, workspace/nodesets/model/model_pyslam/) wraps pySLAM (Luigi Freda, GPL-3.0) as a streaming visual-SLAM session. Where VGGT reconstructs geometry from N views in one feed-forward pass, pySLAM ingests an RGB-D stream one frame at a time and incrementally tracks the camera pose while accumulating a sparse 3-D map β classic feature SLAM (ORB features + DBoW3 loop closure, C++ core), a moving camera building a map as it goes. Feed it a policy's egocentric frames and it reconstructs the walked scene alongside the run.
The nodeset has eleven nodes in three groups: the streaming session above (five nodes β the four core verbs plus a dense volumetric map); three stateless perception nodes that use pySLAM's standalone classes as pure functions β feature extraction, feature matching, and evo trajectory evaluation (ATE / RPE); and three neural full-surface nodes β dense depth prediction, semantic segmentation, and feed-forward multi-view reconstruction (DUSt3R / MASt3R / VGGT). The last two groups share the same container but never touch the map. The aim is to expose pySLAM's whole capability surface β AgentCanvas supporting pySLAM-the-model, not a curated subset for one downstream graph.
env: Container Launch (ADR-server-005) β server_image = agentcanvas/pyslam:cuda by default (full surface incl. reconstruct_multiview; PYSLAM_IMAGE pins :cpu-fixed for the leaner build), rootless Docker, not a conda env Β· backend: GPL-3.0 pySLAM stays inside the container, never imported in the framework process Β· front-end: ORB2 features + DBOW3 loop closure, RGB-D by default Β· GPU via rootless CDI, on by default
pyslam_tum_slam graph streams TUM fr3/long_office_household frame-by-frame into a live SLAM session β no simulator, no policy, CPU-only: raw RGB + depth, the estimated camera path (blue) SE3-fitted onto ground truth (grey) top-down, and the sparse 3-D map densifying in real time; then an orbit of the finished map.| Primitive | Purpose |
|---|---|
model_pyslam__reset | (Re)start the session. The first call builds the Slam instance from the camera intrinsics + presets; later calls clear the map (reset_session) for a new episode, never rebuild. |
model_pyslam__track | Feed one RGB(-D) frame β pose (4Γ4 world-from-camera) + tracking_state + num_map_points. The map accumulates as a side effect. |
model_pyslam__get_trajectory | The accumulated camera trajectory β one 4Γ4 pose per successfully tracked frame, plus the total frame count. |
model_pyslam__get_map | Export the sparse map (3-D landmarks) to a host-side .npz handle β the path travels the wire, never the geometry. |
model_pyslam__get_dense_map | Export the dense volumetric map (fused voxel cloud / TSDF mesh) from pySLAM's volumetric integrator. Requires PYSLAM_VOLUMETRIC=1; empty handle otherwise. |
| Stateless perception β pure functions over pySLAM's standalone classes, no map state: | |
model_pyslam__extract_features | RGB β keypoints (NΓ6) + descriptors (NΓD). pySLAM's local-feature front-end as a standalone tool. |
model_pyslam__match_features | Two descriptor blocks β matched index pairs (Hamming / L2 auto from dtype). |
model_pyslam__eval_trajectory | Estimated vs ground-truth trajectory β ATE + RPE statistics (evo, Umeyama-aligned). |
| Neural full-surface β pySLAM's dense-perception bank on the GPU, stateless: | |
model_pyslam__predict_depth | RGB (+ right image) β dense metric depth map. Depth-Anything V2 / Depth Pro / MASt3R (mono) or SGBM / RAFT-Stereo / CREStereo (stereo). |
model_pyslam__segment_semantic | RGB β per-pixel semantic (+ instance) map. DeepLabV3 / SegFormer / CLIP / Detic / YOLO. |
model_pyslam__reconstruct_multiview | N overlapping RGB views β fused 3-D scene handle + per-view camera poses. DUSt3R / MASt3R / VGGT family (:cuda image). |
1. Container Launch
pySLAM is GPL-3.0 and carries a native C++ core (g2o, Pangolin, DBoW3) pinned to Python 3.11.9. Rather than vendor that source or build a sibling conda env, the nodeset keeps the whole dependency behind a container boundary β the same treatment habitat_sim gets. Since 2026-08-20 the boundary is the framework's own Container Launch vehicle (design doc, ADR-server-005): the nodeset declares server_image and the registry routes it to server mode with docker run as the launch vehicle β the stock auto_host serves the manifest protocol from inside the container, and the framework talks to auto-generated proxy nodes. The framework side never runs import pyslam; the whole nodeset module (nodes + PySlamSession) executes in-container. This replaced the nodeset's original private bridge (a hand-written _client.py docker driver + an in-container FastAPI shim), which was this mechanism's validated prototype.
rgb/depth into the pySLAM nodes, whose pose and map handle flow on typed wires into the viewer nodes, and the GraphExecutor fires each per step. Middle β those nodes are proxies auto-generated from the container's manifest; the framework's ContainerServer owns the container lifecycle (docker run --rm --init, health wait, docker stop) and never imports pySLAM. Bottom β the stock auto_host dispatches each call to the real nodes, which drive the session and Slam. Results ride back up the wires into the viewers (right), so the container's output re-enters the graph.| Layer | File | Where | Role |
|---|---|---|---|
| Nodes + NodeSet | __init__.py | container | Eleven canvas nodes (five session verbs + three stateless perception + three neural full-surface) plus the server_image/server_mounts/server_env declaration. Imported twice: by the backend during discovery (declaration only) and inside the container by the stock auto_host (execution). |
| Session | _backend.py | container | PySlamSession β drives one pySLAM Slam pinned to a single worker thread (pySLAM's background threads have thread affinity), plus the stateless module-level functions the Tier-2 / neural nodes call. |
| Launch vehicle | app/server/container_server.py | framework | ContainerServer β foreground docker run --rm --init, deterministic container names, stale-namesake reap, health wait, docker stop-first teardown, one CPU-degrade retry. Framework-owned; nothing pySLAM-specific. |
| Wiring | app/components/registry.py | framework | _build_container_server β mounts the repo read-only at /opt/agentcanvas-workspace, rewrites paths/PYTHONPATH/executor URLs, resolves server_mounts, injects server_env. |
No framework or nodeset code is baked into the image β the repo rides the read-only mount, so a framework upgrade never needs an image rebuild. The image carries only pySLAM's own venv (which already includes the requirements-serve.txt set). Container access is forced onto the user's rootless Docker daemon (DOCKER_HOST/XDG_RUNTIME_DIR set to the user socket), so the whole thing needs no sudo.
One call, end to end. On the canvas the pySLAM nodes are proxy nodes the registry generated from the container's /manifest. A single track flows proxy β auto_host β node β session: the proxy POSTs the inputs to /call/model_pyslam__track over the loopback port (numpy arrays ride msgpack natively β no base64, no custom wire protocol); auto_host dispatches to the real track node running in-container, which hands the frame to the PySlamSession, which marshals the call onto its one worker thread and returns the pose. The single thread is not incidental β pySLAM's local-mapping / loop-closing / volumetric background threads are bound to the thread that constructed Slam, so every track must run on that same thread (the same single-thread-affinity discipline the GL/physics env nodesets use).
What rides the wire. Light results (pose, tracking state, point counts) and frames travel on the manifest protocol like any server-mode nodeset's. Heavy geometry β sparse / dense maps and multi-view clouds β never rides a wire at all: the in-container node writes the .npz into /opt/out (the server_mounts rw drop, host side outputs/pyslam_maps/) and passes only the host-resolvable handle path downstream, so a multi-thousand-point map never inflates the executor's in-memory state. The private HTTP endpoint zoo of the old bridge (/track, /get_map npz-over-octet-stream, β¦) is gone β the stock GET /manifest Β· POST /call/{fn} Β· GET /health surface replaced all of it.
The session's shape is fixed at launch, not per call. Sensor mode, feature / loop presets, volumetric integration, indoor/outdoor regime β all chosen when the container starts: the class-level server_env snapshots every PYSLAM_* variable from the host environment at import time and the registry injects them as -e on docker run; the in-container initialize() reads them back when it builds the session. That is why those knobs live as environment variables (Β§7) rather than call arguments β they define the container, while only per-frame data (images, depth) rides the call. The per-node Reset config fields override the env defaults right before the first configure_camera.
2. A streaming session, not a stateless pass
Every other foundation-model nodeset here is a stateless primitive (parallelism = "shared", one server across eval workers). pySLAM is the exception: its map is mutable per-episode state that accumulates across track calls, so it is a session. Consequences:
parallelism = "replicated"β each batch-eval worker needs its own map, hence its own container (server-mode fan-out spawns oneContainerServerper worker).server_imageβ the nodeset runs in server mode with the container as the launch vehicle: the whole module executes inside the container, and the framework only ever talks to its proxy nodes.- The
Slaminstance is built lazily on the firstreset(once camera intrinsics are known), then reused; per-episoderesetonly clears the map.
The three Tier-2 perception nodes (Β§5) are the counterexample within this nodeset: they are stateless pure functions that reuse the same container but never touch the Slam object, so β unlike track / get_map β they do not gate on the session being built.
3. Weaving a passive observer into the dataflow
A SLAM observer riding alongside a navigation policy is easy to wire wrong, because two graph-executor facts fight the naΓ―ve topology:
- Fresh instances. The host executor fires freshly-constructed
node_cls()objects, not theget_tools(self)instances that carry a nodeset back-reference β and in-container the same discipline holds as a safety net. So nodes reach the shared session asself._nodeset or _NODESET, a process-level singleton thatinitialize()sets β safe because one container is one process (replicated: one per worker). - Demand-driven pruning. A node whose outputs feed nothing is pruned and never fires. A passive observer therefore must be woven into the live path:
track'snum_map_pointsis wired to aniterOutport, andreset'sepisode_okto aniterIninit port, so both actually run every step / once per episode.
The shipped example, vln/unverified/pyslam_slam_probe.json, is a pure-policy VLN-CE navigator (CMA policy, no LLM) with pySLAM observing: the policy drives, pySLAM reconstructs the walked scene, and the wiring above keeps the observer alive.
4. Camera intrinsics & depth scale
Two alignment details make an environment's RGB-D stream usable by pySLAM:
- Intrinsics. Wire the environment's
intrinsicsoutput (Habitat's observe port emits{fx, fy, cx, cy, width, height}) intoreset.intrinsicsfor an exact pinhole β no hard-coded HFOV. Absent that,cam_hfov+ width/height derivefxfrom the field of view. - Depth scale. RGB-D SLAM needs depth in metres. Habitat VLN-CE renders depth normalised to
[0,1]over a 10 m range, so setdepth_scale = 10.0on the reset node β the track node multiplies depth back to metric before feeding pySLAM. (pySLAM's ownDepthMapFactorapplies only to file-loaded depth, not an ndarray passed totrack(), so the scaling is node-side.) When RGB and depth grids differ (e.g. CMA's RGB 224 vs depth 256), depth is nearest-neighbour resampled onto the RGB grid first.
5. Canvas Nodes
model_pyslam__reset
| Field | Detail |
|---|---|
| Inputs | trigger, intrinsics (ANY β {fx,fy,cx,cy,width,height}), cam_width, cam_height, cam_hfov (all optional) |
| Outputs | episode_ok (BOOL), info (TEXT β session state JSON, or {"error": β¦}) |
| Config | sensor_type (rgbd / mono / stereo) Β· feature_preset (default ORB2) Β· loop_preset (default DBOW3, or off) Β· cam_hfov Β· depth_scale |
| Backend call | first fire: configure_camera β start (builds Slam); every fire: reset_session to clear the map |
model_pyslam__track
| Field | Detail |
|---|---|
| Inputs | rgb (IMAGE β HxWx3 uint8 or .npy path), depth (DEPTH β metres, RGB-D only, opt), timestamp (ANY, opt), gt_pose (ANY β env observe.pose, captured frame-aligned for eval_trajectory, opt) |
| Outputs | pose (POSE β 4Γ4 world-from-camera, or null if LOST), tracking_state (TEXT), num_map_points (ANY) |
| Backend call | depth resample β depth Γ depth_scale β Slam.track(rgb, None, depth, id, ts); pose from tracking.cur_R / cur_t |
{ "pose": [[1.0, 0.0, 0.0, 0.12], [0.0, 1.0, 0.0, 0.03], [0.0, 0.0, 1.0, 0.58], [0.0, 0.0, 0.0, 1.0]], "tracking_state": "OK", "num_map_points": 1862 }
tracking_state walks NOT_INITIALIZED β OK (β LOST if track drops).
model_pyslam__get_trajectory
| Field | Detail |
|---|---|
| Inputs | trigger (ANY, opt) |
| Outputs | poses (ANY β list of 4Γ4, estimated), num_frames (ANY β total frames fed to track), gt_poses (ANY β frame-aligned ground truth captured by track, for eval_trajectory) |
model_pyslam__get_map
| Field | Detail |
|---|---|
| Inputs | trigger (ANY, opt) |
| Outputs | map_handle (TEXT β path to a .npz), num_points (ANY), num_keyframes (ANY) |
| Backend call | the node writes the .npz in-container into /opt/out (the rw server_mounts drop) and returns the matching host-side path under outputs/pyslam_maps/ |
{ "map_handle": "<repo>/outputs/pyslam_maps/map_000060.npz", "num_points": 1862, "num_keyframes": 12 }
The .npz holds a points array of shape (N, 3) β the sparse landmark cloud in world coordinates.
model_pyslam__get_dense_map
Export the dense map β not the sparse landmark cloud but pySLAM's own volumetric integrator output, a fused voxel point cloud or TSDF mesh built over the tracked keyframes. A session verb: it requires the session to be built with volumetric integration on (PYSLAM_VOLUMETRIC=1) and returns an empty handle otherwise.
| Field | Detail |
|---|---|
| Inputs | trigger (ANY, opt) |
| Outputs | dense_handle (TEXT β path to a .npz, or empty), num_points (ANY β fused voxel points), num_vertices (ANY β TSDF mesh vertices) |
| Config | via env: PYSLAM_VOLUMETRIC Β· PYSLAM_VOLUMETRIC_TYPE (VOXEL_GRID / TSDF / VOXEL_SEMANTIC_GRID) Β· PYSLAM_ENV (indoor / outdoor depth-truncation) |
| Backend call | flush the integrator's keyframe queue and wait for it to drain (the integrator is a separate process), then add_update_output_task + pop_output β point cloud / mesh arrays |
The drain-wait matters: the volumetric integrator consumes keyframes on its own process via multiprocessing queues, so popping an output before the queue drains returns a partial or empty map. The node blocks until the queue is empty, then extracts a complete volume (VOXEL_GRID is the verified default).
The first five nodes are the streaming session; the next three are stateless perception β they reuse the container but hold no map state, so they never gate on the session being built.
model_pyslam__extract_features
pySLAM's local-feature front-end as a standalone tool β detect keypoints and compute descriptors on one image.
| Field | Detail |
|---|---|
| Inputs | rgb (IMAGE β HxWx3 uint8 or .npy path) |
| Outputs | keypoints (ANY β NΓ6 [x, y, size, angle, response, octave]), descriptors (ANY β NΓD; uint8 for ORB, float for SIFT), num_keypoints (ANY) |
| Config | detector (default ORB β OpenCV, no weights; also SIFT / AKAZE / BRISK / SuperPoint / XFeat) Β· descriptor (blank = same as detector) Β· num_features |
| Backend call | feature_manager_factory(...).detectAndCompute |
ORB2 (the ORB-SLAM2 interface) needs a C++ binding the CPU image lacks β use plain ORB; learned detectors auto-download a checkpoint on first use.
model_pyslam__match_features
Match two descriptor blocks (from Extract Features) β matched index pairs. Classical matchers run on the arrays alone β no images, no weights.
| Field | Detail |
|---|---|
| Inputs | descriptors_a (ANY β NΓD), descriptors_b (ANY β MΓD) |
| Outputs | idxs_a (ANY), idxs_b (ANY β aligned matched indices), num_matches (ANY) |
| Config | matcher_type (BF / FLANN) Β· ratio_test (Lowe, default 0.7) |
| Backend call | feature_matcher_factory(...).match; distance auto from dtype (Hamming for binary ORB/AKAZE, L2 for SIFT) |
model_pyslam__eval_trajectory
Score an estimated trajectory against ground truth β ATE + RPE via evo. Pairs naturally with Get Trajectory. Pure math, no weights, no GPU.
| Field | Detail |
|---|---|
| Inputs | poses_est (ANY β list of 4Γ4), poses_gt (ANY β list of 4Γ4), is_monocular (ANY, opt β overrides config) |
| Outputs | ate_rmse (ANY β metres), rpe_rmse (ANY β metres, Ξ=1 frame), metrics (TEXT β full ATE + RPE stats JSON) |
| Config | is_monocular β off = metric SE3 align; on = Sim3 scale align |
| Backend call | utilities.evaluation.evaluate_evo (Umeyama-align β APE) + evo RPE |
A near-straight-line trajectory (a straight-corridor episode) makes Umeyama alignment rank-deficient, so ate_rmse can come back null (evo cannot align) while rpe_rmse still returns via a no-align fallback β an evo limitation, not a fault.
The last three nodes are neural full-surface β pySLAM ships a bank of learned dense-perception backends behind one factory each, and exposing pySLAM's whole surface means these get first-class nodes too. Like the stateless perception nodes they hold no map state, but they run neural nets on the GPU, so the first call loads (or downloads) a checkpoint. (The streaming session's own algorithm variety β feature detector, loop detector, sensor mode β is reached by config passthrough on the Reset node, not by new nodes.)
model_pyslam__predict_depth
Dense metric depth from a single image β pySLAM's depth-estimator bank as a standalone tool.
| Field | Detail |
|---|---|
| Inputs | rgb (IMAGE), image_right (IMAGE β stereo estimators only, opt) |
| Outputs | depth (DEPTH β HxW float32, metres), depth_range (ANY β [min, max]), estimator (TEXT) |
| Config | estimator (Depth-Anything V2 / Depth Pro / MASt3R mono Β· SGBM / RAFT-Stereo / CREStereo stereo) Β· environment (indoor / outdoor) Β· min_depth Β· max_depth |
| Backend call | depth_estimator_factory(...).infer(rgb, right) β depth map; device=None picks CUDA |
Mono estimators use rgb alone; stereo ones also consume image_right. SGBM is pure OpenCV/CPU; the learned estimators run on the GPU and auto-download a checkpoint on first use.
model_pyslam__segment_semantic
Per-pixel semantic segmentation β pySLAM's segmentation bank as a standalone tool.
| Field | Detail |
|---|---|
| Inputs | rgb (IMAGE) |
| Outputs | semantics (ANY β HxW int labels, or HxWxC / HxWxD volume), instances (ANY β HxW, or null), num_classes (ANY) |
| Config | model (DeepLabV3 / SegFormer / CLIP / Detic / YOLO / EOV-Seg) Β· feature_type (label / probability / feature vector) Β· dataset (Cityscapes / ADE20K / VOC / NYU40) |
| Backend call | semantic_segmentation_factory(...).infer(rgb) β SemanticSegmentationOutput; device=None picks CUDA |
model_pyslam__reconstruct_multiview
Feed-forward multi-view 3-D reconstruction β pySLAM's scene-from-views stack (the DUSt3R / MASt3R / VGGT family). Give a list of β₯2 overlapping views; get a fused global point cloud plus per-view camera poses in one pass. Needs the agentcanvas/pyslam:cuda image (the multi-view backends carry compiled CUDA kernels).
| Field | Detail |
|---|---|
| Inputs | images (ANY β a list of β₯2 overlapping RGB frames or paths) |
| Outputs | scene_handle (TEXT β path to a .npz: points, colours, mesh, intrinsics), camera_poses (ANY β per-view 4Γ4 cam-to-world), num_points (ANY), num_views (ANY) |
| Config | backend (MASt3R Β· DUSt3R Β· VGGT Β· VGGT-Robust Β· Fast3r Β· Depth-Anything V3) Β· as_pointcloud (point cloud, or mesh + cloud) |
| Backend call | scene_from_views_factory(...).reconstruct(images, as_pointcloud) β SceneFromViewsResult |
Weights are external, not baked into the image (Β§6): MASt3R / MV-DUSt3R load checkpoints from a mounted data/models/pyslam/ folder; the HuggingFace-runtime backends (VGGT / VGGT-Robust / Depth-Anything V3 / Fast3r / pure DUSt3R) download into a mounted cache on first use. MV-DUSt3R is a known gap β a version drift in pySLAM's bundled copy leaves it unusable; the other backends cover multi-view. MASt3R is the verified default (a 104k-point Chateau reconstruction runs end-to-end).
6. Deployment & lifecycle
auto_host calls initialize() inside it); the Slam instance is built lazily on the first reset; track/get_map/reset repeat per episode (the loop clears the map but reuses the built session and container); unloading calls shutdown() in-container then docker stops the container.Loading the nodeset makes the registry's ContainerServer run a foreground docker run --rm --init (repo bind-mounted read-only at /opt/agentcanvas-workspace, weights and the /opt/out handle drop mounted per server_mounts, a loopback port published), then poll /health until the stock auto_host answers β auto_host has already called the nodeset's initialize() by then. Teardown is framework-owned too: auto_host runs shutdown() (quits Slam, joins its background threads), then ContainerServer issues docker stop and --rm reaps the container β a SIGKILLed backend can no longer orphan it. The image is built once via rootless Docker; pySLAM is cloned and compiled at build time, never entering the repo.
GPU. With server_image_gpu on (the default) the container is launched with --device nvidia.com/gpu=all so the neural backends (learned depth, semantic segmentation, multi-view reconstruction) run on the GPU β the classic SLAM core stays on CPU. Rootless Docker reaches the GPU through CDI (the legacy --gpus cgroup path fails unprivileged). When the GPU device is absent, ContainerServer retries once without it β set PYSLAM_GPU=0 to skip the GPU claim outright.
One default image. server_image defaults to agentcanvas/pyslam:cuda β the full surface out of the box. Its neural layers load lazily, so the same image runs fine CPU-only; the old bridge's per-GPU tag auto-selection is gone. cpu-fixed still exists as the leaner base (it already carries a CUDA torch β "cpu" only means the SLAM C++ core is CPU-built β but lacks the compiled multi-view kernels, so reconstruct_multiview needs :cuda). Pin PYSLAM_IMAGE=agentcanvas/pyslam:cpu-fixed to use it.
Identity alignment, not open perms. The image bakes a non-root slam user, which rootless docker maps to a foreign host subuid that cannot write host-owned rw mounts β so the nodeset declares server_container_user = "0:0": under rootless, container-root is the invoking host user, so the handle drop stays a normal 755 directory and every .npz lands on the host owned by the user (HOME=/home/slam rides server_env because the image's venv/cache paths assume the baked user's home). The registry also creates a missing outputs/pyslam_maps/ automatically β no manual setup.
Weights are external. Neither image bakes in model weights β they carry only code and compiled kernels. The multi-view checkpoints live in a host data/models/pyslam/ folder and mount read-only into the container via server_mounts (PYSLAM_WEIGHTS_DIR overrides the location; missing dirs are skipped, that backend just errors at call time); the HuggingFace-runtime backends download into a mounted rw cache on first use. Fetch the explicit checkpoints with the tracked scripts under docker/weights/:
cd workspace/nodesets/model/model_pyslam/docker/weights bash download_mast3r.sh # MASt3R (~2.5GB) β the verified default bash download_all.sh # mast3r + mvdust3r explicit checkpoints
export DOCKER_HOST="unix:///run/user/$(id -u)/docker.sock" bash workspace/nodesets/model/model_pyslam/docker/build.sh agentcanvas/pyslam:cpu-fixed
Run the TUM showcase graph through the experiment wrapper (profile pyslam-tum, CPU-only):
# One episode of the TUM showcase, submitted to the slot backend. # Container Launch fans out one container per eval worker, so worker_count # is no longer pinned to 1 by the nodeset (the map is per-worker state). AGENTCANVAS_BACKEND_URL=http://127.0.0.1:8001 \ python .claude/commands/experiment/bin/submit.py \ pyslam-tum pyslam_tum_slam \ split=freiburg1 episode_count=1 worker_count=1 step_budget=60
7. Environment Variables
| Variable | Default | Purpose |
|---|---|---|
PYSLAM_SENSOR | rgbd | Sensor mode β mono / stereo / rgbd |
PYSLAM_FEATURE | ORB2 | A feature_tracker_configs preset name |
PYSLAM_LOOP | DBOW3 | A loop_detector_configs preset name, or off |
PYSLAM_IMAGE | agentcanvas/pyslam:cuda | Pin the container image (server_image); :cpu-fixed for the leaner build |
PYSLAM_ARTIFACT_DIR | <repo>/outputs/pyslam_maps | Host dir where map / scene handles land (mounted rw at /opt/out; resolved absolute) |
PYSLAM_VOLUMETRIC | 0 | Enable the dense volumetric integrator for get_dense_map |
PYSLAM_VOLUMETRIC_TYPE | VOXEL_GRID | Dense-map output β VOXEL_GRID / TSDF / VOXEL_SEMANTIC_GRID |
PYSLAM_ENV | INDOOR | Dense depth-truncation regime β INDOOR / OUTDOOR |
PYSLAM_WEIGHTS_DIR | data/models/pyslam | Host folder of external multi-view weights mounted into the container |
PYSLAM_GPU | 1 | GPU device claim (server_image_gpu); 0 skips it |
All PYSLAM_* variables are snapshotted from the host environment when the backend imports the nodeset (the class-level server_env) and injected into the container via -e β set them before starting the backend. Per-node config fields on the Reset node (sensor type, presets, HFOV, depth scale) override these env defaults.
8. Operational notes
- Bootstrap latency. RGB-D SLAM needs a few frames of parallax to initialise. On a validated Habitat run the front-end spent ~16 discrete steps in
NOT_INITIALIZEDbefore locking on, then tracked steadily β reconstructing a metric room-scale cloud (~1.5β1.9k points, core extent β 5 Γ 3 Γ 7 m) matching the walked path. - Coverage is episode-dependent. Episodes dominated by in-place rotation, very short paths, or low-texture views may never accumulate enough parallax to initialise, and legitimately return zero map points β an inherent property of feature SLAM under discrete large-rotation motion, not a fault. The front-end honestly reports "not initialised" rather than emitting spurious geometry.
- Per-episode reset is clean. Across a multi-episode run each episode re-bootstraps from an empty map (
reset_session); maps never carry over, and a failed-to-initialise episode exports an empty cloud rather than the previous episode's points.