yqi19's picture
YAM bimanual task suite: env, solvers, tasks, converters
7399b6f verified
Raw
History Blame Contribute Delete
2.81 kB
"""Sort: each object goes to ITS OWN region, chosen per object rather than one shared bin."""
import numpy as np
from ..motion.arm import OPEN
from .base import approach_and_grasp, place_at
COMFORT_R = 0.25 # reach at which this arm tracks well; measured from the passing tasks
def _best_arm(env, *points):
"""Pick the arm that is COMFORTABLE for every point, not the one that is nearest.
Nearest is actively wrong: the left pad sat 9 cm from the right arm's own base, so "nearest"
chose the right arm and it folded into a configuration it could not servo out of (0.54 m
tracking error). Scoring on |reach - COMFORT_R| picks the arm that has to neither fold up nor
stretch out.
"""
best, bscore = None, 1e9
for side, arm in env.arms.items():
root = np.asarray(arm.root, float)[:2]
score = max(abs(float(np.linalg.norm(root-np.asarray(p, float)[:2]))-COMFORT_R)
for p in points)
if score < bscore:
best, bscore = side, score
return best or "right"
def solve(env, assignment, arm="auto", jaw="auto", lift=0.16, on_top=False, max_gap=0.045):
"""`assignment` maps object name -> region name.
Unlike multi_pick there is no drop spread: each region holds one object, so the drop point is
the region centre and spreading would only push it toward the rim.
`arm="auto"` picks the near arm per destination, which is what makes a two-sided sort work
at all: one arm cannot serve pads on both sides of the table.
"""
out = {}
items = list(assignment.items())
prev = None
for i, (name, region) in enumerate(items):
pre = f"[{i+1}/{len(items)}] "
side = arm if arm != "auto" else _best_arm(
env, env.scene.object_pos(name), env.scene.regions[region]["xy"])
a = env.arms[side]
print(f"[solver] {pre}{name} -> {region} with the {side} arm", flush=True)
if prev is not None:
# Regroup the arm that JUST finished: it is left stretched out over its pad, and
# planning the next reach from there leaves it stuck. If the next item uses the other
# arm this also clears the previous one out of the workspace.
env.recorder.phase = f"{pre}0. REGROUP"
prev.flow([prev._seg_start()+np.array([0, 0, 0.18], np.float32)], OPEN)
prev = a
res, ext = approach_and_grasp(env, a, name, jaw=jaw, max_gap=max_gap, prefix=pre)
if not res.ok:
out[name] = {"grasped": False, "reason": res.reason}
continue
out[name] = {"grasped": True, "region": region,
"place_err": place_at(env, a, region, float(ext[2])/2.0,
on_top=on_top, lift=lift, prefix=pre)}
return out