Support pick and place next to - #30
Conversation
williamshen-nz
left a comment
There was a problem hiding this comment.
Thanks for the changes! Left some comments.
There was a problem hiding this comment.
Pull request overview
This PR extends TipTop’s task translation + TAMP goal construction to support “pick/place next to” instructions by introducing a near(movable, reference) goal predicate and adding logic to ensure near goals still imply a concrete placement surface.
Changes:
- Adds
near(movable, reference)to the perception prompt guidelines and examples to steer the VLM away from misusingon(A, B)for “next to” language. - Updates TAMP goal construction to parse
nearatoms and (when needed) auto-inject anon(movable, table)placement target. - Refactors hand-state goal handling to add
HandEmptyup-front when no holding goal is present.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tiptop/tiptop_run.py |
Adds Near to goal-state construction and injects default On(..., table) for near goals. |
tiptop/perception/prompts/detect_and_translate.txt |
Updates prompt spec + examples to emit near(...) for “next to/beside” instructions and avoid stacking semantics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… near goal - Add experimental.enable_pick_place_next_to flag in tiptop.yml; revert detect_and_translate.txt to the stable prompt and load a separate detect_and_translate_pick_place_next_to.txt when the flag is on (selected via _detect_and_translate_prompt_name in gemini.py). - Restore the loop-based has_holding pattern so HandEmpty is only suppressed by a valid arity-1 holding atom. - Drop the auto-injected On(movable, table) for near goals: it hardcoded the table surface (wrong when the reference isn't on the table) and was redundant with the lateral Near constraint in the common case. Trim the prompt's now-unenforced table-placement promises. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop redundant `enable_` prefix from the experimental flag (`enable_pick_place_next_to` -> `pick_place_next_to`) - Log a warning when Gemini returns an unexpected grounded atom instead of silently ignoring it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
williamshen-nz
left a comment
There was a problem hiding this comment.
Great work!
Let's also bump cuTAMP version here and make sure to test on real robot before approval and merge
williamshen-nz
left a comment
There was a problem hiding this comment.
One last comment!
Thread the near_placement flag through build_tamp_config as a parameter instead of reading tiptop_cfg() inside the function, matching how robot_type and time_dilation_factor are passed. Each call site now supplies cfg.experimental.pick_place_next_to. Addresses PR tiptop-robot#30 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… near goal - Add experimental.enable_pick_place_next_to flag in tiptop.yml; revert detect_and_translate.txt to the stable prompt and load a separate detect_and_translate_pick_place_next_to.txt when the flag is on (selected via _detect_and_translate_prompt_name in gemini.py). - Restore the loop-based has_holding pattern so HandEmpty is only suppressed by a valid arity-1 holding atom. - Drop the auto-injected On(movable, table) for near goals: it hardcoded the table surface (wrong when the reference isn't on the table) and was redundant with the lateral Near constraint in the common case. Trim the prompt's now-unenforced table-placement promises. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop redundant `enable_` prefix from the experimental flag (`enable_pick_place_next_to` -> `pick_place_next_to`) - Log a warning when Gemini returns an unexpected grounded atom instead of silently ignoring it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thread the near_placement flag through build_tamp_config as a parameter instead of reading tiptop_cfg() inside the function, matching how robot_type and time_dilation_factor are passed. Each call site now supplies cfg.experimental.pick_place_next_to. Addresses PR tiptop-robot#30 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dbd4f45 to
62615f1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
tiptop/tiptop_run.py:721
cfg.experimental.pick_place_next_towill crash if a user runs with a config file that hasn’t been updated to include the newexperimentalsection. To keep backward compatibility (and match existing optional-config patterns), read this flag via.get(..., False).
max_planning_time=max_planning_time,
opt_steps=opt_steps_per_skeleton,
robot_type=cfg.robot.type,
time_dilation_factor=cfg.robot.time_dilation_factor,
near_placement=cfg.experimental.pick_place_next_to,
collision_activation_distance=0.0,
enable_visualizer=cutamp_visualize,
tiptop/tiptop_offline.py:86
cfg.experimental.pick_place_next_towill raise if the loaded config file predates the newexperimentalsection. Consider defaulting toFalsewhen the key is absent so older configs continue to work.
config = build_tamp_config(
num_particles=num_particles,
max_planning_time=max_planning_time,
opt_steps=opt_steps_per_skeleton,
robot_type=cfg.robot.type,
time_dilation_factor=cfg.robot.time_dilation_factor,
near_placement=cfg.experimental.pick_place_next_to,
collision_activation_distance=0.0,
enable_visualizer=cutamp_visualize,
)
tiptop/tiptop_websocket_server.py:83
self._cfg.experimental.pick_place_next_towill raise if the server is started with a config file that doesn’t define the newexperimentalsection. Defaulting the flag toFalsewhen missing keeps older configs compatible.
self._config = build_tamp_config(
num_particles=num_particles,
max_planning_time=max_planning_time,
opt_steps=500,
robot_type=self._cfg.robot.type,
time_dilation_factor=self._cfg.robot.time_dilation_factor,
near_placement=self._cfg.experimental.pick_place_next_to,
)
tiptop/perception/gemini.py:26
- Direct attribute access on
tiptop_cfg().experimental.pick_place_next_towill raiseConfigAttributeErrorif a user loads an older config file that doesn’t have the newexperimentalsection. Other parts of the codebase treat optional config keys defensively via.get(...)(e.g. camera config), so it’d be safer to default this flag toFalsewhen missing.
def _detect_and_translate_prompt_name() -> str:
if tiptop_cfg().experimental.pick_place_next_to:
return "detect_and_translate_pick_place_next_to"
return "detect_and_translate"
Both cuRobo and cuTAMP require warp-lang <1.13, so constrain the pypi requirement to keep re-locking off newer, incompatible warp releases. Related: cuRobo is pinned to v0.7.8 because v0.8.0 is effectively a new major version with API changes incompatible with cuTAMP. The existing lock (warp 1.12.0) already satisfies this bound, so no lock changes are needed. Split out from #30.
Both cuRobo and cuTAMP require warp-lang <1.13, so constrain the pypi requirement to keep re-locking off newer, incompatible warp releases. The existing lock (warp 1.12.0) already satisfies this bound, so no lock changes are needed. Split out from #30.
|
Peeled the incidental (non-feature) changes out of this PR into their own focused PRs, so this one can stay purely the pick-and-place-next-to feature:
Note on #34: the tests were rewritten to exercise the generic fill/merge behavior using keys that already exist on Plan: once #32–#34 merge, rebase this branch onto |
Both cuRobo and cuTAMP require warp-lang <1.13, so constrain the pypi requirement to keep re-locking off newer, incompatible warp releases. The existing lock (warp 1.12.0) already satisfies this bound, so no lock changes are needed. Split out from #30.
* Decode git output as UTF-8 in recording metadata The git helpers relied on text=True, which decodes subprocess output using the locale's preferred encoding. Under a non-UTF-8 locale that mangles or crashes on non-ASCII bytes in commit messages, filenames, or diffs. Decode explicitly as UTF-8, and use errors="replace" for the porcelain status and diff, where arbitrary file content can appear. Split out from #30. * Read prompt templates as UTF-8 Path.read_text() decodes with the locale's preferred encoding, which mangles or crashes on non-ASCII prompt text under a non-UTF-8 locale. Decode explicitly as UTF-8, matching the git-output fix.
* Fill missing config keys from packaged defaults on offline replay recording.py snapshots tiptop.yml into each run directory, and tiptop-offline reloads that snapshot to reproduce the run. Because set_tiptop_cfg_from_file did a bare OmegaConf.load, any snapshot predating a later config key crashed on attribute access, e.g. when perception.m2t2.apply_bounds (added in #27) is missing from an older recording but read at perception_wrapper.py. Merge the packaged defaults under the loaded config so omitted keys resolve instead of raising. Recorded configs cannot be updated after the fact, so tiptop-offline opts in via fill_missing; every other caller raises, since a live config the user can edit should be fixed rather than silently patched. Adds tests/test_config.py and a test-unit pixi task, as test-integration filters on -m integration and would not collect them. Split out from #30. * Strengthen config-fill tests - Assert the whole restored section matches defaults, not just one leaf. - Cover the case where a recording carries a key since dropped from the defaults: extra keys are kept and don't raise, since only missing keys are an error.
… near goal - Add experimental.enable_pick_place_next_to flag in tiptop.yml; revert detect_and_translate.txt to the stable prompt and load a separate detect_and_translate_pick_place_next_to.txt when the flag is on (selected via _detect_and_translate_prompt_name in gemini.py). - Restore the loop-based has_holding pattern so HandEmpty is only suppressed by a valid arity-1 holding atom. - Drop the auto-injected On(movable, table) for near goals: it hardcoded the table surface (wrong when the reference isn't on the table) and was redundant with the lateral Near constraint in the common case. Trim the prompt's now-unenforced table-placement promises. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop redundant `enable_` prefix from the experimental flag (`enable_pick_place_next_to` -> `pick_place_next_to`) - Log a warning when Gemini returns an unexpected grounded atom instead of silently ignoring it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pass tiptop's experimental.pick_place_next_to config through to the new TAMPConfiguration.near_placement flag so the PlaceNear operator is enabled in cuTAMP only when the feature is turned on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thread the near_placement flag through build_tamp_config as a parameter instead of reading tiptop_cfg() inside the function, matching how robot_type and time_dilation_factor are passed. Each call site now supplies cfg.experimental.pick_place_next_to. Addresses PR tiptop-robot#30 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42db25c to
a934f26
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YSfQmsMYHih1MuzCmLGjN4
Added comments to clarify the 'pick_place_next_to' experimental feature.
a934f26 to
3e922e4
Compare
State the cuTAMP NearPlacement tolerance value directly and shorten the default_constraint_to_tol pointer to a single line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Adds experimental support for "pick and place next to" tasks (e.g. "place the cup next to the apple") via a new
nearpredicate. Gated behind a config flag and off by default, so the stable pipeline is unaffected.What's included
near(movable, reference)predicate — "place A laterally next to B," where B is a positional reference, not a surface to stack on. Distinct fromon(A, surface).experimental.pick_place_next_tointiptop.yml(defaultfalse). When enabled, perception loads a separate prompt (detect_and_translate_pick_place_next_to.txt) that teaches Gemini thenearpredicate and the on-vs-near distinction. When disabled, the original prompt is used unchanged, so existing behavior is untouched.create_tamp_environmentgroundsnearatoms intoNear(movable, reference)goals, which cuTAMP solves with itsPlaceNearoperator.Enabling
Set
experimental.pick_place_next_to: trueintiptop.yml.Known limitations
nearreference must be a movable, not a surface. A compound goal like "put block X in the bowl and block Y next to the bowl" fails: the bowl is a surface (anOn-target), but cuTAMP'sPlaceNearrequires thenearreference to be a movable, so the goal is rejected with aValueErrorbefore planning. Tracked in tiptop-robot/cuTAMP#26; the fix lives entirely in cuTAMP. Only affects the experimental flag when enabled.