Skip to content

fix(libero): hold LIBERO end-effector position during wrist and pitch rotation (v0.1) - #240

Merged
Sonorous281 merged 6 commits into
RLinf:mainfrom
mvanhorn:fix/234-libero-rotation-position-hold
Oct 14, 2026
Merged

Sonorous281 merged 6 commits into
RLinf:mainfrom
mvanhorn:fix/234-libero-rotation-position-hold

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Description

Both primitives copy the end-effector xyz on entry and command translation as clip((hold_xyz - current_xyz) / action_scale, -1, 1), with action_scale defaulting to 0.05 m per unit action. Rotation uses clip(clipped_radians / rot_scale, -1, 1) with rot_scale defaulting to 0.5 rad per unit action, and rotate_pitch also writes action[5] from the wrapped yaw error against the yaw at entry. The result adds converged, max_pos_drift_m, and position_hold_ok, which is true when the largest xyz displacement from the entry pose, including the final pose, is within pos_tol (default 0.03 m); a missed hold stays a normal result with no error key. The planner tool descriptions and schemas expose the new arguments and result fields, and tests/unit_tests/robots/libero/test_libero_rotation_hold.py adds an offline fake controller (0.05 m and 0.5 rad per unit action) covering command scale, translation back toward the entry xyz, pitch yaw correction, hold failure, and an exhausted step budget.

rotate_wrist and rotate_pitch can let the grip site walk several centimeters during the rotation, far enough for a grasped object to slip out, and still return as a normal completion. The result only carried the final angle error plus the environment terminated and truncated flags, so a caller had no signal that the position hold had failed. Each step sent a zero translation and set the rotation component to the clipped radian error divided by 0.10, then clipped to [-1, 1]. A zero translation leaves the position goal on the latest observed pose, so drift is not pulled back to the pose from the start of the call, and dividing a 0.10 rad clip by 0.10 requests a full-scale action (0.5 rad on the controller these calls use) instead of the clipped increment. rotate_pitch also left the yaw channel at zero, and the loop stopped on angle error without measuring xyz displacement from the entry pose.

Fixes #234

Testing

The recorded Python test run found no tests to execute for the touched modules.

Manual verification

Covered in the summary above.

Checklist

  • My code follows the code style of this project.
  • I have updated related documentation when needed.
    Left unticked: this change to robots/libero/tools.py touches no documentation.
  • I have added tests to cover my changes when needed.
  • All new and existing tests passed.
    Not claimed: no local test run is recorded for robots/libero/tools.py.

AI was used for assistance.

… rotation

Both primitives copy the end-effector xyz on entry and command
translation as clip((hold_xyz - current_xyz) / action_scale, -1, 1),
with action_scale defaulting to 0.05 m per unit action. Rotation uses
clip(clipped_radians / rot_scale, -1, 1) with rot_scale defaulting to
0.5 rad per unit action, and rotate_pitch also writes action[5] from the
wrapped yaw error against the yaw at entry. The result adds converged,
max_pos_drift_m, and position_hold_ok, which is true when the largest
xyz displacement from the entry pose, including the final pose, is
within pos_tol (default 0.03 m); a missed hold stays a normal result
with no error key. The planner tool descriptions and schemas expose the
new arguments and result fields, and
tests/unit_tests/robots/libero/test_libero_rotation_hold.py adds an
offline fake controller (0.05 m and 0.5 rad per unit action) covering
command scale, translation back toward the entry xyz, pitch yaw
correction, hold failure, and an exhausted step budget.

Fixes RLinf#234
@panhe1818
panhe1818 requested review from jx-qiu, qurakchin and wilburx813 and removed request for jx-qiu and wilburx813 October 12, 2026 03:38
@wilburx813

wilburx813 commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

@mvanhorn Thanks for the contribution! Could you please resolve the conflicts using the new tool interface introduced in #198 ?

Port the rotate_wrist/rotate_pitch position hold onto the @tool
interface: the hold arguments are documented in the method docstrings,
results are returned as ToolResult, and the LIBERO tool schema fixture
records the new arguments.
@read-the-docs-community

read-the-docs-community Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

@mvanhorn

Copy link
Copy Markdown
Contributor Author

@wilburx813 thanks, merged main in 54ddb47 and ported the change onto the new @tool interface. rotate_wrist and rotate_pitch now take pos_tol / action_scale / rot_scale as documented Args and return the hold fields in the ToolResult, and I updated the libero rotate_wrist/rotate_pitch entries in tests/unit_tests/robots/fixtures/tool_schemas.json to match. the rotation hold test now reads result.data and checks the new schema args.

ran tests/unit_tests/robots/libero plus test_tool_schema_contracts.py locally: 77 passed. ruff check and format are clean on the touched files.

Comment thread robots/libero/tools.py Outdated
@qurakchin qurakchin changed the title fix(libero): hold LIBERO end-effector position during wrist and pitch rotation fix(libero): hold LIBERO end-effector position during wrist and pitch rotation (v0.1) Oct 13, 2026
Comment thread robots/libero/tools.py
@Sonorous281
Sonorous281 merged commit 66d9020 into RLinf:main Oct 14, 2026
7 checks passed
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.

[LIBERO] Rotation primitives do not preserve end-effector position and can drop held objects

4 participants