Skip to content

Support pick and place next to - #30

Merged
ryanlindeborg merged 12 commits into
tiptop-robot:mainfrom
ryanlindeborg:ryan/pick-place-next-to-clean
Jul 31, 2026
Merged

Support pick and place next to#30
ryanlindeborg merged 12 commits into
tiptop-robot:mainfrom
ryanlindeborg:ryan/pick-place-next-to-clean

Conversation

@ryanlindeborg

@ryanlindeborg ryanlindeborg commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds experimental support for "pick and place next to" tasks (e.g. "place the cup next to the apple") via a new near predicate. Gated behind a config flag and off by default, so the stable pipeline is unaffected.

What's included

  • New near(movable, reference) predicate — "place A laterally next to B," where B is a positional reference, not a surface to stack on. Distinct from on(A, surface).
  • Experimental flagexperimental.pick_place_next_to in tiptop.yml (default false). When enabled, perception loads a separate prompt (detect_and_translate_pick_place_next_to.txt) that teaches Gemini the near predicate and the on-vs-near distinction. When disabled, the original prompt is used unchanged, so existing behavior is untouched.
  • Goal handlingcreate_tamp_environment grounds near atoms into Near(movable, reference) goals, which cuTAMP solves with its PlaceNear operator.

Enabling

Set experimental.pick_place_next_to: true in tiptop.yml.

Known limitations

  • near reference 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 (an On-target), but cuTAMP's PlaceNear requires the near reference to be a movable, so the goal is rejected with a ValueError before planning. Tracked in tiptop-robot/cuTAMP#26; the fix lives entirely in cuTAMP. Only affects the experimental flag when enabled.

@williamshen-nz williamshen-nz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! Left some comments.

Comment thread tiptop/perception/prompts/detect_and_translate.txt
Comment thread tiptop/tiptop_run.py
Comment thread tiptop/tiptop_run.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 misusing on(A, B) for “next to” language.
  • Updates TAMP goal construction to parse near atoms and (when needed) auto-inject an on(movable, table) placement target.
  • Refactors hand-state goal handling to add HandEmpty up-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.

Comment thread tiptop/tiptop_run.py Outdated
Comment thread tiptop/tiptop_run.py Outdated
ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request May 29, 2026
… 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>
Comment thread tiptop/config/tiptop.yml Outdated
Comment thread tiptop/perception/gemini.py
Comment thread tiptop/perception/gemini.py
Comment thread tiptop/tiptop_run.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request Jun 1, 2026
- 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
williamshen-nz requested a review from Copilot June 1, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread tiptop/perception/gemini.py
Comment thread tiptop/tiptop_run.py

@williamshen-nz williamshen-nz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit: Great work!

Let's also bump cuTAMP version here and make sure to test on real robot before approval and merge

@williamshen-nz williamshen-nz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last comment!

Comment thread tiptop/planning.py Outdated
ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request Jun 19, 2026
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>
ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request Jun 19, 2026
… 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>
ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request Jun 19, 2026
- 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>
ryanlindeborg added a commit to ryanlindeborg/tiptop that referenced this pull request Jun 19, 2026
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>
@ryanlindeborg
ryanlindeborg force-pushed the ryan/pick-place-next-to-clean branch from dbd4f45 to 62615f1 Compare June 19, 2026 19:23
@williamshen-nz
williamshen-nz requested a review from Copilot July 30, 2026 18:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_to will crash if a user runs with a config file that hasn’t been updated to include the new experimental section. 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_to will raise if the loaded config file predates the new experimental section. Consider defaulting to False when 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_to will raise if the server is started with a config file that doesn’t define the new experimental section. Defaulting the flag to False when 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_to will raise ConfigAttributeError if a user loads an older config file that doesn’t have the new experimental section. 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 to False when 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"

williamshen-nz added a commit that referenced this pull request Jul 30, 2026
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.
williamshen-nz added a commit that referenced this pull request Jul 30, 2026
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.
@williamshen-nz

Copy link
Copy Markdown
Collaborator

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 main (perception.m2t2.apply_bounds, perception.depth_trunc_m, cameras), so they don't depend on the experimental block this PR adds.

Plan: once #32#34 merge, rebase this branch onto main. The extracted diffs should drop out, leaving only the feature: the near predicate + goal handling, the experimental.pick_place_next_to flag and its threading through planning.py / tiptop_run.py / tiptop_websocket_server.py, the Gemini prompt-selection logic, the new detect_and_translate_pick_place_next_to.txt prompt, the tiptop.yml experimental block, and the utils.py cuTAMP 0.0.6 bump.

williamshen-nz added a commit that referenced this pull request Jul 30, 2026
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.
williamshen-nz added a commit that referenced this pull request Jul 30, 2026
* 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.
williamshen-nz added a commit that referenced this pull request Jul 30, 2026
* 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.
ryanlindeborg and others added 6 commits July 31, 2026 08:58
… 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>
@ryanlindeborg
ryanlindeborg force-pushed the ryan/pick-place-next-to-clean branch from 42db25c to a934f26 Compare July 31, 2026 13:05
ryanlindeborg and others added 2 commits July 31, 2026 11:47
Added comments to clarify the 'pick_place_next_to' experimental feature.
@williamshen-nz
williamshen-nz force-pushed the ryan/pick-place-next-to-clean branch from a934f26 to 3e922e4 Compare July 31, 2026 15:47
Comment thread tiptop/config/tiptop.yml Outdated
williamshen-nz and others added 2 commits July 31, 2026 12:59
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants