Skill Catalog#
The open-robot-skills registry ships eleven skill bundles
under skills/. Each bundle owns one subgraph in a generated
workflow: its SKILL.md frontmatter declares the tools it may call
(allowed_tools), the exit conditions, the typed inputs and outputs it
exchanges with neighboring subgraphs, and the canonical scripts that are
materialized into the workflow directory. The body of SKILL.md is the
guidance the subgraph-authoring agent follows.
Every bundle declares a pip extra named after itself; meta-extras bundle
common sets (quickstart = sam3 + grounding-dino + geometry,
grocery = quickstart + curobo, and all). See
Skill registries for discovery and gap check, and the
tool catalog for the model-backed tools these skills
call.
Note
Requirements
Skill bundles carry no model weights themselves — their requirements come
from the tool bundles they call. The perception and tracking skills need
the GPU-backed sam3/grounding-dino bundles plus a hosted VLM API key;
the planner-based grasp skills need the curobo bundle (CUDA). Run
gap check to see which skills are runnable in your environment.
Choosing a perception skill#
Three perception tiers trade latency, robustness, and loop semantics:
Skill |
Cost |
Method |
“Not found” behavior |
Choose when |
|---|---|---|---|---|
~2–4 s |
DINO + pairwise VLM crop tournament + SAM3 |
raises → |
Default single-target perception |
|
One VLM call |
DINO + one set-of-marks letter pick + SAM3 |
Clean |
Clean-all-items loops needing a terminator |
|
Two detection passes |
DINO parent crop → DINO + SAM3 subpart |
raises → |
The affordance is a subpart (handle, rim) |
Choosing a grasp strategy#
Three grasp strategies, by planner availability and target shape:
Skill |
Planner |
Grasp pose |
Choose when |
Avoid when |
|---|---|---|---|---|
None |
Candidate fan, align-then-descend |
cuRobo not deployed; uncluttered scene |
Clutter the arm must thread through |
|
cuRobo |
Sampled candidate fan as a goalset |
Default whenever cuRobo is deployed |
Off-centroid affordances (bowl rims, handles) |
|
cuRobo |
One deterministic short-axis pose |
Elongated targets: pan/pot handles, bottles, tools |
Symmetric objects where any yaw works |
The planner skills’ candidate fans are centered on the OBB XY — they slip on hollow centers and miss handles. For elongated targets a sampled pose often holds at close but shears out under the lift; the short-axis pose grips the bar squarely so it survives lift + transport.
Perception skills#
perceiving-objects#
Fast single-path 3D perception: Grounding-DINO broad detect → pairwise
VLM crop tournament → SAM3 box segmentation → depth back-projection →
geometry.filter_and_compute_obb. The pairwise “A or B?” tournament on
upscaled crops took object identification from ~30% to 97% on the
LIBERO-PosVar study versus a one-shot set-of-marks pick.
Bundle |
|
States |
|
Exits |
|
Inputs |
none (captures its own observation) |
Outputs |
|
Tool bundles |
grounding-dino, vlm, sam3, geometry |
Install |
|
Multi-camera rigs get a verified wrist-fallback gate: identification defaults to the exterior view, and the wrist view is consulted only when the exterior pick fails its own close-up verify while the wrist pick passes — the only policy that improved identification without regressing any frame in the 4-suite regression study. Wrist clouds still fuse into the output geometry, so the OBB recovers the faces a single front view misses.
Results are cached per observation. Set GAP_PERCEPTION_CACHE=0 to
disable, or GAP_PERCEPTION_CACHE_DIR to relocate the cache (default:
.llm_cache/perceiving-objects/ inside the registry checkout).
perceiving-objects-oneshot#
Lightweight one-shot perception: DINO broad detect, a single VLM
set-of-marks letter pick over labeled boxes, then SAM3 box segmentation.
When the VLM answers “none” or DINO emits no detections, the script
returns a clean found: False instead of raising — the loop terminator
for clean-all-items tasks (target.not_found → done).
Bundle |
|
States |
|
Exits |
|
Inputs |
none |
Outputs |
|
Tool bundles |
grounding-dino, vlm, sam3, geometry |
Install |
|
Choose it for generic target descriptions (“the next remaining grocery
item”) and multi-item loops. Avoid it for small targets (< 40 px wide) —
the pairwise tournament in perceiving-objects is far more reliable in
that regime.
perceiving-object-parts#
Hierarchical zoom-in perception for subparts. A frypan handle is ~3% of
image pixels — bad signal for text segmentation; cropping to the parent
first brings it to ~30% of the cropped image. Pipeline: DINO detects the
parent → crop with padding_px (default 30) → DINO + SAM3 segment the
subpart inside the crop → uncrop → world cloud →
geometry.filter_noise + geometry.compute_obb with an
unfiltered-cloud fallback.
Bundle |
|
States |
|
Exits |
|
Inputs |
none ( |
Outputs |
|
Tool bundles |
grounding-dino, vlm, sam3, geometry |
Install |
|
The mask output is the parent’s mask (used for collision isolation
downstream); the obb and cloud are the subpart’s, and the subpart’s
own mask rides along as subpart_mask. The parent OBB/cloud feed the
transport skill’s drop_offset node so the parent body — not the grasp
point — lands at the drop XY.
Warning
Do not append a geometry.filter_and_compute_obb node after this skill.
The script already returns a noise-filtered OBB with an unfiltered-cloud
fallback; re-running DBSCAN on a thin handle shell routinely collapses
the OBB. Bind target_obb directly to the script’s obb output.
Grasping skills#
grasping-direct-ik#
No-planner grasp: pre-rotate the gripper to the grasp orientation at a
safe height above the target, descend straight down, close. The
align-then-descend split avoids the corkscrew failure of a single
go_to_pose that blends rotation and descent and drags across the
target.
Bundle |
|
States |
|
Exits |
|
Inputs |
|
Outputs |
none declared |
Tool bundles |
geometry (+ connector |
Install |
|
The align pose sits 0.15 m above the OBB top
(compute_align_pose.py). Grasp success is verified by a
validate=True checkpoint (target_held), not by a verify node — no
verify_gripper_grasp skill exists. Lifting to carry height belongs to
the following transporting-objects subgraph.
grasping-with-planner#
Collision-aware grasping with cuRobo — the default whenever the curobo
tool bundle is deployed. The subgraph builds a per-observation collision
world with the target excluded, plans to one of several candidate
top-down grasps (a goalset), executes, and closes.
Bundle |
|
States |
|
Exits |
|
Inputs |
|
Outputs |
|
Tool bundles |
curobo, geometry |
Install |
|
ee_pose_at_grasp is the live TCP pose at the observe step;
grasp_pose is the computed pose the planner targets. Downstream
transporting-objects consumes ee_pose_at_grasp to compute drop
height, and grasp_pose supports output-anchored checkpoint verifiers.
The canonical plan_grasp.py deliberately sets
use_grasp_approach=False: cuRobo’s built-in approach flag downweights
Z and lets the optimizer “complete” at the pre-grasp without descending,
so the skill pre-rotates in its own approach state instead. An
optional select_short_axis script state reorders the candidate fan for
elongated targets without changing the poses.
Not for off-centroid affordances (bowl rims, mug/pan handles): the
candidates are OBB-XY-centered. Use grasping-short-axis for those.
grasping-short-axis#
Deterministic, geometry-locked grasp for elongated targets. The single grasp pose is computed (not sampled): straight-down approach with the finger-opening axis snapped to the OBB’s shorter horizontal axis, so the jaws close across the narrow dimension of the bar.
Bundle |
|
States |
|
Exits |
|
Inputs |
|
Outputs |
|
Tool bundles |
curobo, geometry |
Install |
|
Key levers:
short_axis_grasp_pose.pydefaults toz_offset=-0.04(fingertip 4 cm into the OBB top), with grasp Z clamped to −0.05 m.offset_from_baseslides the grasp outward along the handle, clear of a heavier attached body, when a second perception subgraph suppliesbase_obb; withoutbase_obbit safely no-ops.finalize_trajectory.pybetweenexecuteandcloseis mandatory: withsubsample=4the PD controller is still driving when control returns, and aclose_gripperfired mid-motion is silently dropped by the simulator — fingers never close, no error.The canonical
plan_grasp.pykeeps the planner deliberately lean (topk=8,retries=1,num_ik_seeds=128): try the 8 best-ranked candidates once each and fail fast. The previously inflated values (25/3/256) made every grasp-poor attempt grind through up to 75 silentplan_to_grasp_posescalls (~6 min) without improving success.Escalation: when the standard plan reports “0/N feasible” on a far/low/occluded handle,
plan_grasp_motion.py(cuRobo v0.8curobo.plan_grasp_motion) decomposes the grasp into approach, grasp, and lift trajectories with the gripper close interleaved between the grasp and lift legs.
Transport#
transporting-objects#
Move the currently held object above a destination container and release. Enters holding; exits with the object placed.
Bundle |
|
States |
|
Exits |
|
Inputs |
|
Outputs |
none declared |
Tool bundles |
geometry; optionally curobo, grounding-dino, vlm, sam3 (zone + carve variants) |
Install |
|
compute_drop_pose.py accepts the held object as held_obb
(defaults: drop_clearance=0.05, approach_height=0.20, panda
hand-to-TCP offset 0.1029 m). Binding it as target_obb is silently
discarded and the drop lands wrong by the held object’s full height.
Optional states and variants:
perceive_zone— VLM-grounded sub-region localization (“the left compartment of the caddy”); returnsplacement_zone_obborNone(automatic bare-container fallback). No other container re-perception states are allowed.drop_offset— required whenever the grasp was on a subpart (upstream isperceiving-object-parts): shifts the drop XY so the parent centroid lands at the zone centroid. The single most common omission in handle-grasp place-on failures.waypoint_move_carve— collision-aware variant ofmove_abovethat routes throughcurobo.plan_with_grasped_object; use when an obstacle sits on the transport path. The defaultwaypoint_movelifts to 0.45 m and translates with no planner — tuned for LIBERO tabletops, unsafe under shelves or overhead obstacles.descend_release_linear— straight-line Cartesian descent viacurobo.plan_linearwith arobot.go_to_posefallback; the cleanest release dynamics for subpart-grasp place-on tasks.
Policy and tracking skills#
These are class-based, long-running skills: a workflow invokes them
as a single stateful node fed by the graph-scoped observation_stream,
rather than as a multi-state subgraph. Each is also exposed as a flat
tool (pi05-libero.run, molmoact-libero.run, tracking-objects.track)
for callers that want a one-shot invocation.
The two policy skills are per-checkpoint learned VLA policies: a
learned policy is a first-class skill, not a generic runner. The
coordinator chooses between them by their SKILL.md descriptions
(same task family, different checkpoint), and the chosen skill owns its
serving preset — a policy node names the skill’s .run tool and carries
no policy_id.
pi05-libero#
Run the openpi π0.5 LIBERO checkpoint (pi05_libero) in closed loop
until a termination signal fires. Each window the loop reads
observation_stream.latest(), asks the skill’s preset policy server for
an action chunk, and forwards the first replan_every rows to the robot
via sim.apply_policy_action.
Bundle |
|
Form |
Class-based skill + flat tool |
Checkpoint |
openpi π0.5 LIBERO ( |
Task family |
LIBERO Franka pick-and-place (robosuite |
Key inputs |
|
Returns |
|
Install |
|
Env vars |
|
molmoact-libero#
The MolmoAct alternative to pi05-libero for the same task family —
the two are the policy A/B axis the benchmark ablates. Identical loop and
capability envelope; it serves the MolmoAct checkpoint instead.
Bundle |
|
Form |
Class-based skill + flat tool |
Checkpoint |
MolmoAct LIBERO ( |
Task family |
LIBERO Franka pick-and-place (robosuite |
Key inputs |
|
Returns |
|
Install |
|
Env vars |
|
Both skills share three additive terminators: the max_windows
backstop, a VLM yes/no termination_prompt checked every term_period
windows, and gripper-cycle termination (one open → close → open cycle,
with the close debounced to ≥ 3 windows) — the per-item terminator for
clean-all-items loops. Whether the task succeeded is a postcondition
checkpoint, never an exit value. The launcher auto-boots the matching
preset server when a workflow references the skill — no policies: block
is needed for the common case; an entry keyed by the skill name overrides
the serving recipe (e.g. an external url:). gap policy serve pi05-libero (or molmoact-libero) runs the preset by hand. The
websocket client is cached per preset via the executor’s
PolicyExecutor. Connectors without a VLA passthrough do not register
sim.apply_policy_action, so the call fails loudly rather than driving
the robot with the wrong action space. See
Benchmark policies for the policy-serving
workflow.
tracking-objects#
The registry’s only streaming: true skill: a SAM3 tracker loop over
the observation stream. Seeds the tracker with a text prompt on the
first frame, then polls at update_hz and advances via
sam3.tracker_update, publishing a per-tick snapshot
{mask, box, confidence, object_present, n_updates} via ctx.publish
so downstream consumers see live state while the loop runs.
Bundle |
|
Form |
Class-based streaming skill + flat tool |
Key inputs |
|
Returns |
|
Tool bundles |
sam3 |
Install |
|
The loop checks ctx.cancel_token each tick (so it can be wrapped under
a parallel state with a join_policy) and closes the tracker session
in a finally block. Pass close_on_exit=False in the skill form to
resume the session across repeated visits to the same state; the flat
tracking-objects.track tool always runs one fresh, self-closing
session. A failed seed returns immediately with object_present=False.
See also#
Tool catalog — the model-backed tools these skills call, with exact signatures.
Skill registries — discovery, precedence,
gap check.Authoring bundles — the
SKILL.mdcontract.Generation — how skills become subgraphs in generated workflows.