| """Pour from a kettle into a cup: carry it over and rotate the wrist to tip the contents out. |
| |
| The pour goes into a cup, which is small enough that the wrist rotation has to be aimed rather |
| than merely performed. |
| """ |
| from ..registry import register_task |
| from ..envs import YamTaskEnv |
| from ..solvers import pour |
| from .. import conditions as C |
|
|
|
|
| @register_task("pour_kettle") |
| class PourKettleTask(YamTaskEnv): |
| title = "POUR THE KETTLE INTO THE CUP" |
| tags = ["pouring", "trajectory", "wrist-rotation", "robotwin-asset"] |
| instruction = {"default": "Pour water from the kettle into the cup."} |
|
|
| rw_objects = {"rw_kettle": "091_kettle/base0.usd:0.052:0.12"} |
| |
| |
| kettle_xy = (0.0, 0.03) |
| |
| |
| cup = {"usd": "021_cup/base0_mesh.usd", "scale": 0.085, "rpy": (90, 0, 0), "xy": (0.02, -0.22)} |
| |
| |
| |
| |
| |
| pour_deg = 108.0 |
| min_pour_tilt = 75.0 |
| |
| |
| |
| |
| |
| grip_offset = (0.0, 0.045) |
| |
| |
| grip_top = None |
| gripper_effort, gripper_damping = 78, 86 |
|
|
| def _load_scene(self): |
| self.scene.spawn_container({"name": "cup", **self.cup, "xy_jitter": (0.015, 0.015)}) |
| self._placed = [self.scene.place_object( |
| {"name": "rw_kettle", "xy": self.kettle_xy, "xy_jitter": (0.015, 0.010)})] |
|
|
| def solve(self): |
| pos = self.scene.object_pos("rw_kettle") |
| print(f"[task] kettle {self.scene.object_size('rw_kettle').round(3)} at {pos.round(3)}", |
| flush=True) |
| return pour.solve(self, obj="rw_kettle", target="cup", pour_deg=self.pour_deg, |
| max_gap=0.048, jaw="x", grasp_offset=self.grip_offset, |
| grasp_top=self.grip_top) |
|
|
| def evaluate(self): |
| return self.check( |
| C.labelled("kettle lifted", C.object_lifted("rw_kettle", 0.05)), |
| C.labelled("kettle tipped out over the cup", |
| C.object_tipped_over("rw_kettle", "cup", min_tilt_deg=self.min_pour_tilt))) |
|
|