yqi19's picture
YAM bimanual task suite: env, solvers, tasks, converters
7399b6f verified
Raw
History Blame Contribute Delete
3.66 kB
"""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)
# left arm sits at +y, so its rim is the +y edge; the right arm takes the -y edge later
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)
# object_size() is the AUTHORED bbox and ignores yaw, so a yawed object's world extent is
# not ext[]. The task passes the rotated half when it has yawed the object.
half = float(world_half_y) if world_half_y is not None else float(ext[1])/2.0
# The second hand must close along the SAME axis as the first -- the other axis is the
# object's length and does not fit the jaw at all (11.7 cm against a 9.4 cm opening, which
# is why the right hand kept closing on air). It takes a different SPOT along that length:
# the end nearer itself, so the two hands are never fighting for the same few centimetres.
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)"
# verify_lift=0: the object cannot rise while the left hand still holds it
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}