AgentCanvas / Pages / Developer Guide / Nodesets / Model / pySLAM
2026-08-20

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 as the protagonist on a classic RGB-D benchmark. The 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.
PrimitivePurpose
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__trackFeed 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_trajectoryThe accumulated camera trajectory β€” one 4Γ—4 pose per successfully tracked frame, plus the total frame count.
model_pyslam__get_mapExport the sparse map (3-D landmarks) to a host-side .npz handle β€” the path travels the wire, never the geometry.
model_pyslam__get_dense_mapExport 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_featuresRGB β†’ keypoints (NΓ—6) + descriptors (NΓ—D). pySLAM's local-feature front-end as a standalone tool.
model_pyslam__match_featuresTwo descriptor blocks β†’ matched index pairs (Hamming / L2 auto from dtype).
model_pyslam__eval_trajectoryEstimated 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_depthRGB (+ right image) β†’ dense metric depth map. Depth-Anything V2 / Depth Pro / MASt3R (mono) or SGBM / RAFT-Stereo / CREStereo (stereo).
model_pyslam__segment_semanticRGB β†’ per-pixel semantic (+ instance) map. DeepLabV3 / SegFormer / CLIP / Detic / YOLO.
model_pyslam__reconstruct_multiviewN 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.

AGENTCANVAS Β· canvas graph + GraphExecutor env Β· observe rgb Β· depth Β· pose pySLAM nodes reset Β· track Β· get_map viewer nodes trajectory Β· point-cloud rgb Β· depth pose Β· handle the GraphExecutor fires each node per step Β· wires are typed FRAMEWORK Β· agentcanvas env β€” never imports pyslam proxy nodes auto-generated from /manifest ContainerServer docker run --rm --init Β· health Β· stop DOCKER CONTAINER Β· pyslam venv Β· GPL-3.0 stays here Β· repo mounted ro stock auto_host /manifest Β· /call/{fn} nodeset + nodes __init__.py Β· initialize() PySlamSession β†’ Slam _backend.py Β· 1 thread Β· C++ executor fires the proxy node POST /call/{fn} Β· msgpack (numpy native) owns the lifecycle ↑ results β†’ viewers
How pySLAM enters AgentCanvas under Container Launch. Top β€” on the canvas it is ordinary nodes: an env feeds 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.
LayerFileWhereRole
Nodes + NodeSet__init__.pycontainerEleven 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.pycontainerPySlamSession β€” 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 vehicleapp/server/container_server.pyframeworkContainerServer β€” 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.
Wiringapp/components/registry.pyframework_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:

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:

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:


5. Canvas Nodes

model_pyslam__reset

FieldDetail
Inputstrigger, intrinsics (ANY β€” {fx,fy,cx,cy,width,height}), cam_width, cam_height, cam_hfov (all optional)
Outputsepisode_ok (BOOL), info (TEXT β€” session state JSON, or {"error": …})
Configsensor_type (rgbd / mono / stereo) Β· feature_preset (default ORB2) Β· loop_preset (default DBOW3, or off) Β· cam_hfov Β· depth_scale
Backend callfirst fire: configure_camera β†’ start (builds Slam); every fire: reset_session to clear the map

model_pyslam__track

FieldDetail
Inputsrgb (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)
Outputspose (POSE β€” 4Γ—4 world-from-camera, or null if LOST), tracking_state (TEXT), num_map_points (ANY)
Backend calldepth 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

FieldDetail
Inputstrigger (ANY, opt)
Outputsposes (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

FieldDetail
Inputstrigger (ANY, opt)
Outputsmap_handle (TEXT β€” path to a .npz), num_points (ANY), num_keyframes (ANY)
Backend callthe 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.

FieldDetail
Inputstrigger (ANY, opt)
Outputsdense_handle (TEXT β€” path to a .npz, or empty), num_points (ANY β€” fused voxel points), num_vertices (ANY β€” TSDF mesh vertices)
Configvia env: PYSLAM_VOLUMETRIC Β· PYSLAM_VOLUMETRIC_TYPE (VOXEL_GRID / TSDF / VOXEL_SEMANTIC_GRID) Β· PYSLAM_ENV (indoor / outdoor depth-truncation)
Backend callflush 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.

FieldDetail
Inputsrgb (IMAGE β€” HxWx3 uint8 or .npy path)
Outputskeypoints (ANY β€” NΓ—6 [x, y, size, angle, response, octave]), descriptors (ANY β€” NΓ—D; uint8 for ORB, float for SIFT), num_keypoints (ANY)
Configdetector (default ORB β€” OpenCV, no weights; also SIFT / AKAZE / BRISK / SuperPoint / XFeat) Β· descriptor (blank = same as detector) Β· num_features
Backend callfeature_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.

FieldDetail
Inputsdescriptors_a (ANY β€” NΓ—D), descriptors_b (ANY β€” MΓ—D)
Outputsidxs_a (ANY), idxs_b (ANY β€” aligned matched indices), num_matches (ANY)
Configmatcher_type (BF / FLANN) Β· ratio_test (Lowe, default 0.7)
Backend callfeature_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.

FieldDetail
Inputsposes_est (ANY β€” list of 4Γ—4), poses_gt (ANY β€” list of 4Γ—4), is_monocular (ANY, opt β€” overrides config)
Outputsate_rmse (ANY β€” metres), rpe_rmse (ANY β€” metres, Ξ”=1 frame), metrics (TEXT β€” full ATE + RPE stats JSON)
Configis_monocular β€” off = metric SE3 align; on = Sim3 scale align
Backend callutilities.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.

FieldDetail
Inputsrgb (IMAGE), image_right (IMAGE β€” stereo estimators only, opt)
Outputsdepth (DEPTH β€” HxW float32, metres), depth_range (ANY β€” [min, max]), estimator (TEXT)
Configestimator (Depth-Anything V2 / Depth Pro / MASt3R mono Β· SGBM / RAFT-Stereo / CREStereo stereo) Β· environment (indoor / outdoor) Β· min_depth Β· max_depth
Backend calldepth_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.

FieldDetail
Inputsrgb (IMAGE)
Outputssemantics (ANY β€” HxW int labels, or HxWxC / HxWxD volume), instances (ANY β€” HxW, or null), num_classes (ANY)
Configmodel (DeepLabV3 / SegFormer / CLIP / Detic / YOLO / EOV-Seg) Β· feature_type (label / probability / feature vector) Β· dataset (Cityscapes / ADE20K / VOC / NYU40)
Backend callsemantic_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).

FieldDetail
Inputsimages (ANY β€” a list of β‰₯2 overlapping RGB frames or paths)
Outputsscene_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)
Configbackend (MASt3R Β· DUSt3R Β· VGGT Β· VGGT-Robust Β· Fast3r Β· Depth-Anything V3) Β· as_pointcloud (point cloud, or mesh + cloud)
Backend callscene_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

FRAMEWORK Β· agentcanvas env CONTAINER Β· pyslam venv load nodeset Β· ContainerServer docker run --rm --init Β· repo mounted ro auto_host boots initialize() Β· /health polled until ready reset() Β· first episode configure_camera β†’ start Slam built β€” lazily ORB2 + DBoW3 Β· bg threads spin up track(rgb, depth) Β· Γ—N one call per step Slam.track Β· 1 thread pose out Β· map grows get_map / get_trajectory after the loop npz written to /opt/out only the host path rides the graph wire reset() Β· next episode then loop back to track reset_session() clears map Β· keeps loop DB unload / backend exit docker stop Β· --rm reaps shutdown() β†’ Slam.quit() joins bg threads Β· container removed per-episode loop
The session lifecycle: each framework action (left) drives one container effect (right), top to bottom. Loading the nodeset starts the container (and 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

VariableDefaultPurpose
PYSLAM_SENSORrgbdSensor mode β€” mono / stereo / rgbd
PYSLAM_FEATUREORB2A feature_tracker_configs preset name
PYSLAM_LOOPDBOW3A loop_detector_configs preset name, or off
PYSLAM_IMAGEagentcanvas/pyslam:cudaPin the container image (server_image); :cpu-fixed for the leaner build
PYSLAM_ARTIFACT_DIR<repo>/outputs/pyslam_mapsHost dir where map / scene handles land (mounted rw at /opt/out; resolved absolute)
PYSLAM_VOLUMETRIC0Enable the dense volumetric integrator for get_dense_map
PYSLAM_VOLUMETRIC_TYPEVOXEL_GRIDDense-map output β€” VOXEL_GRID / TSDF / VOXEL_SEMANTIC_GRID
PYSLAM_ENVINDOORDense depth-truncation regime β€” INDOOR / OUTDOOR
PYSLAM_WEIGHTS_DIRdata/models/pyslamHost folder of external multi-view weights mounted into the container
PYSLAM_GPU1GPU 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

AgentCanvas docs