| """Left arm picks the object, passes it to the right arm, right arm sets it down.""" |
| import numpy as np |
|
|
| from ..motion.arm import OPEN, CLOSE, fillet, grasp_quat |
| from ..envs.scene import TABLE_TOP |
| from .base import approach_and_grasp, place_at |
|
|
|
|
| def solve(env, obj, target, handover_xy=(0.06, 0.0), max_gap=0.045, rim_grasp=False, |
| rim_margin=0.010, grasp_top=None, jaw="auto", world_half_y=None): |
| """`rim_grasp` takes the object by the EDGE nearest each arm instead of across its middle. |
| |
| A basket is 18.9 x 14.3 cm and the jaw opens 9.4 cm, so there is no across-the-middle grasp |
| at all: the jaws stall at full opening and the clamp check (correctly) rejects it. Gripping |
| the near rim closes across a ~1 cm wall, and it is also what a person does. |
| """ |
| rec = env.recorder |
| L, R = env.arms["left"], env.arms["right"] |
| ext0 = env.scene.object_size(obj) |
| |
| off_l = (0.0, float(ext0[1])/2.0-rim_margin) if rim_grasp else (0.0, 0.0) |
| res, ext = approach_and_grasp(env, L, obj, max_gap=max_gap, grasp_offset=off_l, |
| grasp_top=grasp_top, jaw="y" if rim_grasp else jaw, |
| prefix="[L] ") |
| if not res.ok: |
| rec.phase = "ABORT: left arm never grasped it" |
| return {"handed": False, "reason": res.reason} |
|
|
| rec.phase = "[L] 4. CARRY to the handover pose" |
| mid = L.to_root(np.array([handover_xy[0], handover_xy[1], TABLE_TOP+0.18], np.float32)) |
| L.flow(fillet([L._seg_start(), L._seg_start()+np.array([0, 0, 0.14], np.float32), mid])[1:], CLOSE) |
|
|
| live = env.scene.object_pos(obj) |
| |
| |
| half = float(world_half_y) if world_half_y is not None else float(ext[1])/2.0 |
| |
| |
| |
| |
| R.quat = grasp_quat(jaw if jaw in ("x", "y") else "y") |
| grab_y = live[1]-(half*0.55 if not rim_grasp else half-rim_margin) |
| Rg = R.to_root(np.array([live[0], grab_y, live[2]], np.float32)) |
| print(f"[solver] right hand takes the {jaw}-axis grip at y={grab_y:+.3f} " |
| f"(object centre {live[1]:+.3f}, half-length {half:.3f})", flush=True) |
| rec.phase = "[R] 5. RIGHT approaches the far rim" |
| R.move_to(Rg+np.array([0, 0, 0.12], np.float32), OPEN, tol=0.02, max_steps=120) |
| R.move_to(Rg, OPEN, tol=0.01, max_steps=100) |
| rec.phase = "[R] 6. RIGHT closes (both hold)" |
| |
| gr = R.grasp(lambda: env.scene.object_pos(obj)[2], max_gap=max_gap, verify_lift=0.0) |
| rec.phase = "[L] 7. LEFT releases" |
| for _ in range(45): |
| L._drive(L._seg_start(), OPEN) |
| L.flow([L._seg_start()+np.array([0, 0.10, 0.10], np.float32)], OPEN) |
| if not gr.ok: |
| rec.phase = "ABORT: right arm never took it" |
| return {"handed": False, "reason": gr.reason} |
| held = float(env.scene.object_pos(obj)[2]) > TABLE_TOP+0.05 |
| print(f"[solver] handover: right holds it above the table = {held}", flush=True) |
| err = place_at(env, R, target, float(ext[2])/2.0, prefix="[R] ") |
| return {"handed": held, "place_err": err} |
|
|