mp_yam_code / source /bimanual /yam /tasks /bell_coaster.py
yqi19's picture
ring_post fixed; 18/30 verified; status table generated from run logs; diagnostics
f84c4af verified
Raw
History Blame Contribute Delete
3.37 kB
"""Slide the desk bell onto its coaster, without grasping it.
RoboTwin has "Click Bell"; this is deliberately not that. The bell here is a plain rigid mesh
with no plunger joint, so a "press" cannot register anything -- a rigid body on a rigid table
does not move under a downward push, and the check would be measuring nothing (it read 0.0 mm).
What IS measurable is sliding it: the bell is dome-shaped and top-heavy, so it tips easily, and
the task is to get it onto the coaster while leaving it standing.
"""
from ..registry import register_task
from ..envs import YamTaskEnv
from ..solvers import push
from .. import conditions as C
@register_task("bell_coaster")
class BellCoasterTask(YamTaskEnv):
title = "SLIDE THE BELL ONTO ITS COASTER"
tags = ["push", "no-grasp", "contact-rich", "robotwin-asset"]
instruction = {"default": "Push the bell onto the coaster."}
# 0.075 made a 14 cm desk bell; this is a believable 8.5 cm one. Mass 0.40 kg because a
# desk bell IS heavy metal, and at 0.12 kg the dome tipped instead of sliding.
rw_objects = {"rw_bell": "050_bell/base0.usd:0.045:0.40"}
# Pushing toward -y means standing on the bell's +y side, and that stance sat 0.43 m from
# the right shoulder -- out of reach, so nothing moved. The arm can only push AWAY from its
# own shoulder at (-0.2,-0.2), so the route runs +x/+y instead.
bell_spawn = (-0.02, -0.02)
spawn_jitter = (0.018, 0.015)
coaster_xy = (0.10, 0.02)
coaster_size = 0.12
def _load_scene(self):
self.scene.build_marker({"name": "coaster", "xy": self.coaster_xy,
"size": self.coaster_size, "color": (0.30, 0.26, 0.22),
"xy_jitter": (0.015, 0.015)})
self._placed = [self.scene.place_object(
{"name": "rw_bell", "xy": self.bell_spawn, "xy_jitter": self.spawn_jitter})]
def solve(self):
e = self.scene.object_size("rw_bell")
print(f"[task] bell {e.round(3)} m", flush=True)
# a dome tips if shoved high up, so push low and slowly
# push LOW on the dome: at 45% of its height it tipped over and rolled 15 cm past
return push.solve(self, obj="rw_bell", target="coaster", advance=0.005,
steps=300, push_z_frac=0.10,
stop_dist=self.coaster_size*0.5)
# NOT YET PASSING, and the remaining cause is the object, not the parameters. Three changes
# each helped and none fixed it:
# push_z_frac 0.45 -> 0.18 -> 0.10 (contact at the skirt) overshoot 0.154 -> 0.079 m
# stop the push early and let it coast in overshoot -> 0.055 m
# mass 0.12 -> 0.40 kg (a desk bell is heavy metal) overshoot 0.059 m
# Position is now inside tolerance, but "still standing" keeps failing: a DOME rotates under
# any horizontal contact, so it rolls rather than slides. The honest options are to push it
# with a flat tool (a swept plate rather than a fingertip), or to replace the bell with an
# object that has a flat side. Do not simply relax object_level to make it pass.
def evaluate(self):
return self.check(
C.labelled("bell on the coaster", C.object_moved_to("rw_bell", "coaster", tol=0.06)),
C.labelled("bell still standing", C.object_level("rw_bell", 25.0)))