Grocery Packing#
Note
Requirements
A CUDA GPU (MuJoCo EGL rendering + CuRobo planning + perception weights), the
grocery dependency extra, and a VLM credential for perception and the
completion check (GAP_VLM_PROVIDER / GAP_VLM_MODEL + the providerβs
credentials).
Where the quickstart picks one described object, this example loops: perceive the next object on the table, grasp it with the planner recipe, transport it into the basket, then route back to perception and repeat β until every grocery item is in the basket. It is the executorβs reference example for a graph with a real backward edge.
The example lives at examples/grocery_packing β
a self-contained static v3 graph (packing_graph/workflow.json plus the
scripts/ it references). There is no build step: load and run it like any
other graph.
libero_object_packing/0), 2Γ: one perceive β grasp β transport
pass per item, until the VLM completion check reports the table clear.The loop#
START βββ perceive_next ββfoundβββ grasp ββgraspedβββ transport βββ
β β none β failed β blocked β placed
not_found β β β β β
β done (clean abort βββββββββββββββ β
abort success exit) β
βββββββββββββββββ perceive_next ββββββββββββββββββββββββ
THE BACKWARD EDGE (transport β perceive_next)
transport --placed--> perceive_next is a genuine cycle. The executor treats
a conditional edge that resolves to an already-completed node as a loop: it
resets the loop body (perceive_next, grasp, transport) and re-runs it.
The cross-subgraph store keeps the most-recent producer, so each iteration
grasps the freshly-perceived target_obb and places into the
freshly-perceived container_obb β the loop head re-localizes both the
next item and the basket every pass. GAP_ITERATION_CAP bounds runaway
loops; see Graph patterns for the loop
(subgraph-revisit) semantics.
The subgraphs#
perceive_next_sg is the loop head, and it localizes both ends of the pick on every iteration:
get_observation β exterior_view β perceive("basket") β filter_and_compute_obb β perceive("grocery item") β decide. The basket is re-perceived each pass alongside the next item, so the place pose always reflects the current scene. Two things keep the item pick honest:The perceive node passes an
object_descriptionβ βa packaged grocery product such as a can, box, carton, jar, or bottle; never the wicker basket or storage containerβ β so the VLM pairwise tournament prefers a real item over the basket whenever one is on the table.decide(route_next_object.py) is the loopβs stop signal β a per-pass VLM completion check, not a privileged simulator verdict (see below).
grasp_sg β
open β top_down_grasp_candidates β grasp_move β observe β close.grasp_move.pyis the tuned approach recipe: rise to a hover height, translate in XY over the object, then descend straight down onto it with cuRoboβs axis-constrained linear plan (plan_directed_linear,allowed_axes=["Z"],orientation_mode="LOCK") β a guaranteed vertical drop, not a curved IK path.transport_sg β
transport_move β release.transport_move.pylifts, translates in XY over the basket, then does the same axis-locked straight-Z descend into the basket;place_release.pyopens the gripper and retracts linearly.
Termination β unprivileged, VLM-verified#
route_next_object.py needs no privileged simulator signal β the same policy
runs unchanged on a real robot. Signals are layered:
VLM completion check (primary). Every pass, the exterior frame goes to the VLM: βhave ALL the grocery items been placed inside the basket?β A confident YES β
noneβ done (success). The check only ever forces a STOP β a NO (or an unavailable VLM) never forces the loop to continue, so the guards below still guarantee termination.Env verdict (secondary, sim-only backstop).
sim.check_successis polled, wrapped so a non-sim connector falls through cleanly.No-progress guard. Re-perceiving the same target (cloud centroid within 3 cm) three passes in a row means the last grasp+transport cycle changed nothing β
none. A pass budget backstops pathological alternation.Perception. Otherwise: an item was returned β
found(grasp it); nothing βnone.
none is a normal exit (not on_error), so finishing never looks like a
failure.
Run it#
CUDA_HOME=/usr/local/cuda uv sync --extra grocery
uv run gap skills check --download
# Runs against the VAB pack-all suite; a run video is recorded by default
# to <trace-dir>/run_video.mp4 (pass --no-video to skip it).
MUJOCO_GL=egl uv run gap run examples/grocery_packing/packing_graph \
--sim libero_object_packing/0
uv run gap viz # browse the trace β perceive_next is visited once per object
The default task is the variational-automation pack-all suite
(libero_object_packing): when an object settles inside the basket the env
marks it delivered and removes it from the scene, so the next
perceive_next pass finds the next object. The graph stays a pure policy
(perceive / grasp / transport); the benchmark owns the bookkeeping and the
score. On the default arrangement the loop delivers the items one per pass
and exits cleanly the iteration after the last delivery.
Generate it yourself#
The static graph above is also what gap generate produces from the task
sentence β the perceiving-next-item / grasping-with-planner /
transporting-objects skills carry the same tuned recipes as this exampleβs
scripts, so the generated loop matches this one in structure and behavior:
uv run gap generate "Pick all the objects and place them in the basket" \
--provider vertex --model gemini-3.1-pro-preview
MUJOCO_GL=egl uv run gap run outputs/generated_<timestamp>/task_00 \
--sim libero_object_packing/0
Benchmarking note
gap benchmark applies per-trial safety guards sized for a single pick
(max_perception_calls etc.). A pack-all loop does several times the work β
raise the limits in the benchmark YAML or the workers kill mid-loop episodes:
safety_limits: {max_perception_calls: 400, max_planning_calls: 200, max_sim_steps: 40000}
Next steps#
Build a Graph β the single-object version of this graph, authored step by step.
Grocery Fulfillment β the LLM-generated acceptance benchmark whose grasp/transport recipes this example mirrors.
Graph patterns β the loop (backward-edge) semantics the executor implements.