<< All versions

Skill v1.0.0

currentAutomated scan100/100
dimensionalos/dimos/localize
──Details
PublishedOctober 22, 2026 at 03:27 PM
Content Hashsha256:d59677431f7c388c...
Git SHAeb73dadee54e
──Files
Files (1 file, 10.5 KB)
SKILL.md10.5 KBactive
SKILL.md · 215 lines · 10.5 KB

version: "1.0.0" name: perception-localize description: Locate objects with a running PerceptionLocalize stack over MCP (state, localize) - probe the machine, build the time window, watch labels on a schedule, narrow an object's position, change params per call, and tell where a failure comes from. Use whenever a blueprint with PerceptionLocalize and McpServer runs, such as go2-localize or xarm-localize-live.


Localize over MCP

localize answers from memory: the colour frames the module has embedded, lifted to 3D through depth or lidar. It does not look at the current frame. Every answer is a verified 3D object with a score and a view count, and an empty answer is an answer.

Before the first call

nvidia-smi --query-gpu=name,memory.used,memory.total --format=csv,noheader uv run dimos mcp call state

  • Probe the GPU first. How many labels a call can carry depends on it, see

"Labels and GPU".

  • state() returns ready: N frames embedded, localize will answer or

not ready: <stage>. Until it reads ready, localize returns its stage instead of an answer.

  • Error: no running MCP server (is DimOS running?) means the stack is down.

Check uv run dimos status and report it. Do not stop or restart the stack.

Calling localize

uv run dimos mcp call localize -j '{"objects": "chair,door", "start": -30, "duration": 30}' --timeout 300

  • localize(objects, start=-10, duration=10, params="") returns once the answer

is built. Send the next call after it returns.

  • objects is one label, or a few separated by commas. The labels of one call

share one detector pass per frame.

  • The client waits 30 s. A window with many unread frames takes longer, so pass

--timeout 300 or set MCP_TIMEOUT=300.

Building the window

start and duration are seconds over the embedded frames the module holds.

argumentswindow
start=-10, duration=10 (default)the last 10 s
start=-60, duration=60the last minute
start=-60, duration=20from 60 s ago to 40 s ago
start=30, duration=2020 s, from 30 s after the oldest frame held
  • A negative start counts back from the newest embedded frame, a positive one

forward from the oldest frame still held. The module holds horizon_s of frames, 600 s by default, so once memory is full the oldest frame moves.

  • Frames are embedded at 1 per second, 3 on a walking robot (mobile=True),

each the sharpest of its slot.

  • The first answer line is the window as resolved:

window A s to B s of C s of feed, N frames. With N = 0 nothing was read and the answer is memory only.

  • A frame is read once per label and params, accept_score and min_views

aside. A window of frames never read for a label costs detector passes; asking the same window again is nearly free.

Reading the answer

window 0.0s to 30.0s of 28.3s of feed, 76 frames 'chair' at (-0.97, 7.31, 0.40) in world score=0.49 views=6 id=ed6e5cde seen 24s ago no verified detection of 'bag'

  • One line per verified object, newest sighting first. The position is the

centroid of the object's newest sighting, in the frame the line names.

  • id names the same object for as long as the module runs.
  • score is the detector's best score for the object. Compare scores within one

label only; the scale changes with the wording.

  • views counts the distinct camera positions, 1 cm apart, that saw the object.

An object needs 2, and a camera that does not move adds none. A camera that never moves, such as a fixed top view, stays silent until min_views is 1: pass '{"min_views": 1}', which reads no frame again.

  • Objects found by earlier calls are answered again, also outside this window.

seen Ns ago is the age of the newest sighting.

  • no verified detection means nothing passed the gates at these params. It is

not proof that the object is absent.

  • Labels are not compared: one object can be answered under two labels.

Watching labels on a schedule

Four labels on a rolling 5 s window, one call after another:

uv run dimos mcp call localize -j '{"objects": "chair,door,bag,plant", "start": -5, "duration": 5}' --timeout 60

  • A laptop RTX 5070 with 8 GB sustained this, calling continuously.
  • A call reads only the frames not yet read for its labels, so each call costs

the new seconds, not its whole window.

  • The window must span the time between the starts of two calls: the call's own

run time plus any wait. If calls take longer than the window, widen it. The overlap is free, because read frames are skipped; a gap loses frames.

  • Keep the label strings and params the same on every call. Memory is per

exact label string, and frames read under other params are read again, unless only accept_score or min_views changed.

  • An object that left view stays in the answer at its last position, with a

growing seen Ns ago. An object that moved farther than cluster_radius_m gets a new id at its new position, and the old id stays at the old one.

Narrowing a position

The position is the newest sighting's, so a closer sighting refines it.

  1. Bring the camera close to the object and move it a little: every new camera

position adds a view.

  1. Ask a short recent window, {"objects": "red mug", "start": -5, "duration": 5}.

A sighting within cluster_radius_m of the object's centre joins it under the same id, and the position moves to the new sighting.

  1. Check that views grew and seen is near 0 s.

At a table, a walking robot's room-scale params are coarse for small objects:

fieldroom (go2)desk (xarm)desk (dual_yam)
cluster_radius_m: sightings this close are one object0.300.080.08
fuse_voxel_m: object cloud resolution0.030.010.01
min_camera_range_m: closer sightings are dropped0.50.280.07
surface_patch_max_rise_m: a sighting this flat on the support is surface0.080.0030.003

Pass desk-scale field overrides on every call for that label while working at the table. Objects the label already has from room-scale calls keep how they were built; a label first asked at the table is built at desk scale.

Changing params per call

Each blueprint sets its rig's LocalizeParams (types.py) at construction (desk-scale defaults in LocalizeParams(), room-scale GO2_PARAMS in go2_localize.py).

params is a JSON object of LocalizeParams field overrides for that call only. The next call without it is back on the module's params, the ones its blueprint set.

params valuemeaning
""the module's params
'{"accept_score": 0.45}'these fields on the module's params

uv run dimos mcp call localize -j '{"objects": "apple", "params": "{\"accept_score\": 0.45}"}' --timeout 300 uv run dimos mcp call localize -j '{"objects": "apple", "params": "{\"min_views\": 3}"}' --timeout 300

fieldacts onwhat it gates
accept_scoreevery object of the label, at oncebest score an object needs to be answered
min_viewsevery object of the label, at oncecamera positions an object needs
candidate_floorframes read by this calldetector score that forms a box; lower means more boxes and more GPU memory
cluster_radius_mframes read by this callsightings this close are one object
max_object_extent_m, min_points, min_camera_range_mframes read by this callsize, points and camera distance a sighting needs
fuse_voxel_mobjects a sighting of this call joinsresolution of the object's cloud
verify_radius_m, peak_prominenceframes read by this callhow many frames retrieval hands to the detector
  • accept_score and min_views act only when the answer is built: changing

them reads no frame again and applies at once to everything remembered for the label. Try them first.

  • The other fields shape only what happens from now on. Objects already built

keep how they were built.

  • A misspelled field fails the call with unexpected keyword argument.
  • Every field and its desk-scale default is defined on LocalizeParams in

types.py.

Labels and GPU

  • Do not pass many labels at once. Every detector box is segmented on the GPU,

so each label adds memory and time.

  • Reference: a laptop RTX 5070 with 8 GB sustained 4 labels per call on a

rolling 5 s window, calling continuously. With less free GPU memory, or with Rerun or other models on the same GPU, start with fewer.

  • Use concrete nouns with one attribute that sets the object apart: red mug,

wooden crate. Broad or overlapping labels make many boxes.

  • On Remote torch.OutOfMemoryError, halve the labels per call.

Where a failure comes from

Embedding, detection and segmentation work from the image and find almost any object. Lifting a detection to 3D needs depth at the object, and that depends on the rig. Most failures to get a position come from the lift or from the rig, not from the image models.

Room: walking robots

  • No depth at the object. The stack lifts through the rig's depth camera or

lidar, so a detected object gets no 3D answer until depth reaches it. Go2's L1 lidar is sparse and covers a small radius: when the robot starts moving, localize can lift only what is close. Bring the robot nearer and ask a recent window again.

  • Odometry drift. A static object seen at two times with drifted poses lands

in two places, as two ids. The one seen most recently matches the current odometry.

Desk: arms

  • Depth is rarely the problem.
  • Look-alike objects. A group of objects of the same colour and shape, with

few unique features, can be missed by the detector from some angles.

Any rig

  • Calibration. A poorly calibrated rig shows every object twice, the copies

shifted by the same offset for every object. That points at the rig, not at the params.

  • Light and environment. Every rig depends heavily on them. Tune params on

the fly with the call's params, the score fields candidate_floor and accept_score first.

  • Clouds for grasping. fuse_voxel_m is 0.01 in LocalizeParams() and 0.03

in go2. For GraspGenX, go as low as 0.003, because it can reject clouds fused at larger voxels. The voxel applies every time a sighting joins an object, so pass it on every call for that label, for example '{"fuse_voxel_m": 0.003}'. A cloud already fused coarser does not regain detail.

  • Simulation. Sim images and MuJoCo workflows need their own params; the

presets were fit on images from real rigs.

Tuning params offline on a recording, before deploying them, is in README.md.

All versions