From e05ba330fd3793456c21b156b7d16c7e7170d19a Mon Sep 17 00:00:00 2001 From: Arman Aydin <86849247+goldenJester@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:55:36 -0800 Subject: [PATCH 1/5] Initial implementation --- .../examples/object_states/slicing_demo.py | 1 + omnigibson/transition_rules.py | 49 ++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/omnigibson/examples/object_states/slicing_demo.py b/omnigibson/examples/object_states/slicing_demo.py index 500db990b..1d3b63a9a 100644 --- a/omnigibson/examples/object_states/slicing_demo.py +++ b/omnigibson/examples/object_states/slicing_demo.py @@ -89,6 +89,7 @@ def main(random_selection=False, headless=False, short_exec=False): knife.set_position_orientation( position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), orientation=T.euler2quat([-np.pi / 2, 0, 0]), + # orientation=T.euler2quat([np.pi / 2, 0, 0]) # The back of the knife cuts the apple as well ) input("The knife will fall on the apple and slice it. Press [ENTER] to continue.") diff --git a/omnigibson/transition_rules.py b/omnigibson/transition_rules.py index 0b80ab590..18f716913 100644 --- a/omnigibson/transition_rules.py +++ b/omnigibson/transition_rules.py @@ -18,6 +18,7 @@ import omnigibson.utils.transform_utils as T from omnigibson.utils.ui_utils import disclaimer, create_module_logger from omnigibson.utils.usd_utils import RigidContactAPI +from omnigibson.utils.geometry_utils import generate_points_in_volume_checker_function # Create module logger log = create_module_logger(module_name=__name__) @@ -411,6 +412,49 @@ def modifies_filter_names(self): return {self._filter_1_name} +class TouchingVolumeCheckCondition(TouchingAnyCondition): + def __init__(self, filter_1_name, filter_2_name): + ''' + Checks if filter_2_bodies have a contact point within the collision volume generated from each obj in filter_1 + ''' + # super().__init__(filter_2_name, filter_1_name) + super().__init__(filter_1_name, filter_2_name) + + def __call__(self, object_candidates): + # Keep any object that has non-zero impulses between itself and any of the @filter_2_name's objects + objs = [] + + # Manually check contact + filter_2_bodies = set.union(*(self._filter_2_bodies[obj] for obj in object_candidates[self._filter_2_name])) + for obj in object_candidates[self._filter_1_name]: + slicer_volume = None + for name, link in self.obj.links.items(): + if 'slicer' in name: + slicer_volume = link + break + + if slicer_volume is None: + continue + + check_in_volume, _ = generate_points_in_volume_checker_function( + obj=obj, + volume_link=slicer_volume + ) + + # Check if the contact point is within the check_in_volume function + for contact in obj.contact_list(): + # Get the collision point from contact bodies and check if it happens + # within the slicer volume + if contact.body1 in filter_2_bodies and check_in_volume(contact.position): + objs.append(obj) + + # Update candidates + object_candidates[self._filter_1_name] = objs + + # If objs is empty, return False, otherwise, True + return len(objs) > 0 + + class StateCondition(RuleCondition): """ Rule condition that checks all objects from @filter_name whether a state condition is equal to @val for @@ -723,7 +767,10 @@ def candidate_filters(cls): @classmethod def _generate_conditions(cls): # sliceables should be touching any slicer - return [TouchingAnyCondition(filter_1_name="sliceable", filter_2_name="slicer"), + # return [TouchingAnyCondition(filter_1_name="sliceable", filter_2_name="slicer"), + # StateCondition(filter_name="slicer", state=SlicerActive, val=True, op=operator.eq)] + + return [TouchingVolumeCheckCondition(filter_1_name="slicer", filter_2_name="sliceable"), StateCondition(filter_name="slicer", state=SlicerActive, val=True, op=operator.eq)] @classmethod From 2d659770df3d72ae8d7e9c3dee185f675752d04f Mon Sep 17 00:00:00 2001 From: Arman Aydin <86849247+goldenJester@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:43:47 -0800 Subject: [PATCH 2/5] check slicer volume overlap implementation --- omnigibson/transition_rules.py | 74 +++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/omnigibson/transition_rules.py b/omnigibson/transition_rules.py index 18f716913..02673ce9d 100644 --- a/omnigibson/transition_rules.py +++ b/omnigibson/transition_rules.py @@ -7,6 +7,7 @@ import itertools import os import omnigibson as og +import omnigibson.lazy as lazy from omnigibson.macros import gm, create_module_macros from omnigibson.systems import get_system, is_system_active, PhysicalParticleSystem, VisualParticleSystem, REGISTERED_SYSTEMS from omnigibson.objects.dataset_object import DatasetObject @@ -341,13 +342,14 @@ class TouchingAnyCondition(RuleCondition): Rule condition that prunes object candidates from @filter_1_name, only keeping any that are touching any object from @filter_2_name """ - def __init__(self, filter_1_name, filter_2_name): + def __init__(self, filter_1_name, filter_2_name, allow_optimized=True): """ Args: filter_1_name (str): Name of the filter whose object candidates will be pruned based on whether or not they are touching any object from @filter_2_name filter_2_name (str): Name of the filter whose object candidates will be used to prune the candidates from @filter_1_name + allow_optimized (bool): Whether optimization using RigidContactAPI should be allowed """ self._filter_1_name = filter_1_name self._filter_2_name = filter_2_name @@ -364,11 +366,12 @@ def __init__(self, filter_1_name, filter_2_name): # Flag whether optimized call can be used self._optimized = None + self._allow_optimized = allow_optimized def refresh(self, object_candidates): # Check whether we can use optimized computation or not -- this is determined by whether or not any objects # in our collision set are kinematic only - self._optimized = not np.any([obj.kinematic_only or obj.prim_type == PrimType.CLOTH + self._optimized = self._allow_optimized and not np.any([obj.kinematic_only or obj.prim_type == PrimType.CLOTH for f in (self._filter_1_name, self._filter_2_name) for obj in object_candidates[f]]) if self._optimized: @@ -412,41 +415,69 @@ def modifies_filter_names(self): return {self._filter_1_name} -class TouchingVolumeCheckCondition(TouchingAnyCondition): +class TouchingSlicerVolumeCondition(TouchingAnyCondition): def __init__(self, filter_1_name, filter_2_name): ''' - Checks if filter_2_bodies have a contact point within the collision volume generated from each obj in filter_1 + Checks if filter_2_bodies have a contact point within the collision volume which is a slicer meta link generated by objs in filter_1_bodies. ''' - # super().__init__(filter_2_name, filter_1_name) - super().__init__(filter_1_name, filter_2_name) - + super().__init__(filter_1_name, filter_2_name, allow_optimized=False) + + def _touching_volume(self, obj_paths, volume): + volume_mesh_ids = lazy.pxr.PhysicsSchemaTools.encodeSdfPath(volume.prim_path) + + # Define function for checking overlap + valid_hit = False + + def overlap_callback(hit): + nonlocal valid_hit + valid_hit = hit.rigid_body in obj_paths + # Continue traversal only if we don't have a valid hit yet + return not valid_hit + + def check_overlap(): + nonlocal valid_hit + valid_hit = False + if volume.prim.GetTypeName() == "Mesh": + og.sim.psqi.overlap_mesh(*volume_mesh_ids, reportFn=overlap_callback) + else: + og.sim.psqi.overlap_shape(*volume_mesh_ids, reportFn=overlap_callback) + return valid_hit + + return check_overlap() + def __call__(self, object_candidates): # Keep any object that has non-zero impulses between itself and any of the @filter_2_name's objects objs = [] # Manually check contact - filter_2_bodies = set.union(*(self._filter_2_bodies[obj] for obj in object_candidates[self._filter_2_name])) + filter_2_bodies = {x.prim_path for obj in object_candidates[self._filter_2_name] for x in self._filter_2_bodies[obj] } + for obj in object_candidates[self._filter_1_name]: + # Find contact points with eligible objects + for contact in obj.contact_list(): + if contact.body1 in filter_2_bodies: + # contact_points.append(contact.position.tolist()) + break + else: + continue + + # Find slicer volume slicer_volume = None - for name, link in self.obj.links.items(): + for name, link in obj.links.items(): if 'slicer' in name: slicer_volume = link break + # Skip non-slicer objects if slicer_volume is None: continue - check_in_volume, _ = generate_points_in_volume_checker_function( - obj=obj, - volume_link=slicer_volume - ) - # Check if the contact point is within the check_in_volume function - for contact in obj.contact_list(): - # Get the collision point from contact bodies and check if it happens - # within the slicer volume - if contact.body1 in filter_2_bodies and check_in_volume(contact.position): - objs.append(obj) + if not any(self._touching_volume(filter_2_bodies, v) for v in slicer_volume.visual_meshes.values()): + continue + + # If we make it here, this is a legit slicer contact. + objs.append(obj) # Update candidates object_candidates[self._filter_1_name] = objs @@ -767,10 +798,7 @@ def candidate_filters(cls): @classmethod def _generate_conditions(cls): # sliceables should be touching any slicer - # return [TouchingAnyCondition(filter_1_name="sliceable", filter_2_name="slicer"), - # StateCondition(filter_name="slicer", state=SlicerActive, val=True, op=operator.eq)] - - return [TouchingVolumeCheckCondition(filter_1_name="slicer", filter_2_name="sliceable"), + return [TouchingSlicerVolumeCondition(filter_1_name="slicer", filter_2_name="sliceable"), StateCondition(filter_name="slicer", state=SlicerActive, val=True, op=operator.eq)] @classmethod From 508215bf6b29564485b73f481c25b033f47812fc Mon Sep 17 00:00:00 2001 From: Arman Aydin <86849247+goldenJester@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:57:42 -0800 Subject: [PATCH 3/5] Updates --- omnigibson/envs/env_base.py | 2 +- .../examples/object_states/slicing_demo.py | 7 +- omnigibson/transition_rules.py | 41 ++++------ tests/test_transition_rules.py | 77 +++++++++++++++++++ tests/utils.py | 62 ++++++++------- 5 files changed, 128 insertions(+), 61 deletions(-) diff --git a/omnigibson/envs/env_base.py b/omnigibson/envs/env_base.py index 4644a84ca..7d1bd4647 100644 --- a/omnigibson/envs/env_base.py +++ b/omnigibson/envs/env_base.py @@ -525,7 +525,7 @@ def step(self, action): self._current_step += 1 return obs, reward, done, info - except: + except KeyError: raise ValueError(f"Failed to execute environment step {self._current_step} in episode {self._current_episode}") def _reset_variables(self): diff --git a/omnigibson/examples/object_states/slicing_demo.py b/omnigibson/examples/object_states/slicing_demo.py index 1d3b63a9a..db7e1bcb1 100644 --- a/omnigibson/examples/object_states/slicing_demo.py +++ b/omnigibson/examples/object_states/slicing_demo.py @@ -37,9 +37,8 @@ def main(random_selection=False, headless=False, short_exec=False): knife_cfg = dict( type="DatasetObject", name="knife", - category="table_knife", - model="lrdmpf", - bounding_box=[0.401, 0.044, 0.009], + category="carving_knife", + model="usqmjc", position=[0, 0, 10.0], ) @@ -89,13 +88,13 @@ def main(random_selection=False, headless=False, short_exec=False): knife.set_position_orientation( position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), orientation=T.euler2quat([-np.pi / 2, 0, 0]), - # orientation=T.euler2quat([np.pi / 2, 0, 0]) # The back of the knife cuts the apple as well ) input("The knife will fall on the apple and slice it. Press [ENTER] to continue.") # Step simulation for a bit so that apple is sliced for i in range(1000): + # while True: env.step(np.array([])) input("Apple has been sliced! Press [ENTER] to terminate the demo.") diff --git a/omnigibson/transition_rules.py b/omnigibson/transition_rules.py index 02673ce9d..4ffd48383 100644 --- a/omnigibson/transition_rules.py +++ b/omnigibson/transition_rules.py @@ -7,7 +7,6 @@ import itertools import os import omnigibson as og -import omnigibson.lazy as lazy from omnigibson.macros import gm, create_module_macros from omnigibson.systems import get_system, is_system_active, PhysicalParticleSystem, VisualParticleSystem, REGISTERED_SYSTEMS from omnigibson.objects.dataset_object import DatasetObject @@ -422,29 +421,7 @@ def __init__(self, filter_1_name, filter_2_name): ''' super().__init__(filter_1_name, filter_2_name, allow_optimized=False) - def _touching_volume(self, obj_paths, volume): - volume_mesh_ids = lazy.pxr.PhysicsSchemaTools.encodeSdfPath(volume.prim_path) - - # Define function for checking overlap - valid_hit = False - - def overlap_callback(hit): - nonlocal valid_hit - valid_hit = hit.rigid_body in obj_paths - # Continue traversal only if we don't have a valid hit yet - return not valid_hit - - def check_overlap(): - nonlocal valid_hit - valid_hit = False - if volume.prim.GetTypeName() == "Mesh": - og.sim.psqi.overlap_mesh(*volume_mesh_ids, reportFn=overlap_callback) - else: - og.sim.psqi.overlap_shape(*volume_mesh_ids, reportFn=overlap_callback) - return valid_hit - - return check_overlap() - + def __call__(self, object_candidates): # Keep any object that has non-zero impulses between itself and any of the @filter_2_name's objects objs = [] @@ -454,11 +431,13 @@ def __call__(self, object_candidates): for obj in object_candidates[self._filter_1_name]: # Find contact points with eligible objects + contact_points = [] for contact in obj.contact_list(): if contact.body1 in filter_2_bodies: - # contact_points.append(contact.position.tolist()) - break - else: + contact_points.append(contact.position.tolist()) + + # Skip non-contacted objects + if not contact_points: continue # Find slicer volume @@ -472,8 +451,14 @@ def __call__(self, object_candidates): if slicer_volume is None: continue + # TODO: Consider moving this to the SlicerActive state for performance. + check_in_volume, _ = generate_points_in_volume_checker_function( + obj=obj, + volume_link=slicer_volume + ) + # Check if the contact point is within the check_in_volume function - if not any(self._touching_volume(filter_2_bodies, v) for v in slicer_volume.visual_meshes.values()): + if not np.any(check_in_volume(contact_points)): continue # If we make it here, this is a legit slicer contact. diff --git a/tests/test_transition_rules.py b/tests/test_transition_rules.py index 76a8348d3..a78082362 100644 --- a/tests/test_transition_rules.py +++ b/tests/test_transition_rules.py @@ -102,3 +102,80 @@ def test_cooking_rule(): if dough_exists: og.sim.remove_object(dough) og.sim.remove_object(sheet) + +@og_test +def test_slicer_volume_sharp_end(): + """ + Demo of slicing an apple into two apple slices + """ + + # Create the scene config to load -- empty scene with knife and apple + + # Grab reference to apple and knife + apple = og.sim.scene.object_registry("name", "apple") + knife = og.sim.scene.object_registry("name", "knife") + place_obj_on_floor_plane(apple) + place_obj_on_floor_plane(knife) + + + # Let apple settle + for _ in range(50): + og.sim.step() + + # knife.keep_still() + knife.set_position_orientation( + position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), + orientation=T.euler2quat([-np.pi / 4, 0, 0]), + ) + + # Step simulation for a bit so that apple is sliced + for i in range(10000): + og.sim.step() + + # Check if apple is sliced + is_empty = len(og.sim.scene.object_registry("category", "half_apple", default_val=[])) == 0 + assert not is_empty + + # Remove objects + og.sim.remove_object(apple) + og.sim.remove_object(knife) + +@og_test +def test_slicer_volume_blunt_end(): + """ + Demo of slicing an apple into two apple slices + """ + + # Create the scene config to load -- empty scene with knife and apple + + # Grab reference to apple and knife + apple = og.sim.scene.object_registry("name", "apple") + knife = og.sim.scene.object_registry("name", "knife") + place_obj_on_floor_plane(apple) + place_obj_on_floor_plane(knife) + + # Let apple settle + for _ in range(50): + og.sim.step() + + knife.keep_still() + knife.set_position_orientation( + position=apple.get_position() + np.array([-0.15, 0.0, 0.15]), + orientation=T.euler2quat([np.pi / 2, 0, 0]), + ) + + # Step simulation for a bit so that apple is sliced + for i in range(100): + og.sim.step() + + + # Check if apple is not sliced + is_sliced = og.sim.scene.object_registry("name", "apple") is None + assert not is_sliced + + # Remove objects + og.sim.remove_object(apple) + og.sim.remove_object(knife) + +if __name__ == "__main__": + test_slicer_volume_sharp_end() \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index ed006d791..20c7bbe09 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,6 +2,7 @@ from omnigibson.macros import gm from omnigibson.object_states import * +from omnigibson.simulator import launch_simulator from omnigibson.utils.constants import PrimType, ParticleModifyCondition, ParticleModifyMethod import omnigibson.utils.transform_utils as T import numpy as np @@ -12,10 +13,11 @@ def og_test(func): def wrapper(): assert_test_scene() - try: - func() - finally: - og.sim.scene.reset() + func() + # try: + # func() + # finally: + # og.sim.scene.reset() return wrapper num_objs = 0 @@ -38,36 +40,40 @@ def get_obj_cfg(name, category, model, prim_type=PrimType.RIGID, scale=None, bou } def assert_test_scene(): + if og.sim is None: + launch_simulator() if og.sim.scene is None: cfg = { "scene": { "type": "Scene", }, "objects": [ - get_obj_cfg("breakfast_table", "breakfast_table", "skczfi"), - get_obj_cfg("bottom_cabinet", "bottom_cabinet", "immwzb"), - get_obj_cfg("dishtowel", "dishtowel", "dtfspn", prim_type=PrimType.CLOTH, abilities={"cloth": {}}), - get_obj_cfg("carpet", "carpet", "ctclvd", prim_type=PrimType.CLOTH, abilities={"cloth": {}}), - get_obj_cfg("bowl", "bowl", "ajzltc"), - get_obj_cfg("bagel", "bagel", "zlxkry", abilities=TEMP_RELATED_ABILITIES), - get_obj_cfg("cookable_dishtowel", "dishtowel", "dtfspn", prim_type=PrimType.CLOTH, abilities={**TEMP_RELATED_ABILITIES, **{"cloth": {}}}), - get_obj_cfg("microwave", "microwave", "hjjxmi"), - get_obj_cfg("stove", "stove", "yhjzwg"), - get_obj_cfg("fridge", "fridge", "dszchb"), - get_obj_cfg("plywood", "plywood", "fkmkqa", abilities={"flammable": {}}), - get_obj_cfg("shelf_back_panel", "shelf_back_panel", "gjsnrt", abilities={"attachable": {}}), - get_obj_cfg("shelf_shelf", "shelf_shelf", "ymtnqa", abilities={"attachable": {}}), - get_obj_cfg("shelf_baseboard", "shelf_baseboard", "hlhneo", abilities={"attachable": {}}), - get_obj_cfg("bracelet", "bracelet", "thqqmo"), - get_obj_cfg("oyster", "oyster", "enzocs"), - get_obj_cfg("sink", "sink", "egwapq", scale=np.ones(3)), - get_obj_cfg("stockpot", "stockpot", "dcleem", abilities={"fillable": {}}), - get_obj_cfg("applier_dishtowel", "dishtowel", "dtfspn", abilities={"particleApplier": {"method": ParticleModifyMethod.ADJACENCY, "conditions": {"water": []}}}), - get_obj_cfg("remover_dishtowel", "dishtowel", "dtfspn", abilities={"particleRemover": {"method": ParticleModifyMethod.ADJACENCY, "conditions": {"water": []}}}), - get_obj_cfg("spray_bottle", "spray_bottle", "asztxi", visual_only=True, abilities={"toggleable": {}, "particleApplier": {"method": ParticleModifyMethod.PROJECTION, "conditions": {"water": [(ParticleModifyCondition.TOGGLEDON, True)]}}}), - get_obj_cfg("vacuum", "vacuum", "bdmsbr", visual_only=True, abilities={"toggleable": {}, "particleRemover": {"method": ParticleModifyMethod.PROJECTION, "conditions": {"water": [(ParticleModifyCondition.TOGGLEDON, True)]}}}), - get_obj_cfg("blender", "blender", "cwkvib", bounding_box=[0.316, 0.318, 0.649], abilities={"fillable": {}, "blender": {}, "toggleable": {}}), - get_obj_cfg("oven", "oven", "cgtaer", bounding_box=[0.943, 0.837, 1.297]), + # get_obj_cfg("breakfast_table", "breakfast_table", "skczfi"), + # get_obj_cfg("bottom_cabinet", "bottom_cabinet", "immwzb"), + # get_obj_cfg("dishtowel", "dishtowel", "dtfspn", prim_type=PrimType.CLOTH, abilities={"cloth": {}}), + # get_obj_cfg("carpet", "carpet", "ctclvd", prim_type=PrimType.CLOTH, abilities={"cloth": {}}), + # get_obj_cfg("bowl", "bowl", "ajzltc"), + # get_obj_cfg("bagel", "bagel", "zlxkry", abilities=TEMP_RELATED_ABILITIES), + # get_obj_cfg("cookable_dishtowel", "dishtowel", "dtfspn", prim_type=PrimType.CLOTH, abilities={**TEMP_RELATED_ABILITIES, **{"cloth": {}}}), + # get_obj_cfg("microwave", "microwave", "hjjxmi"), + # get_obj_cfg("stove", "stove", "yhjzwg"), + # get_obj_cfg("fridge", "fridge", "dszchb"), + # get_obj_cfg("plywood", "plywood", "fkmkqa", abilities={"flammable": {}}), + # get_obj_cfg("shelf_back_panel", "shelf_back_panel", "gjsnrt", abilities={"attachable": {}}), + # get_obj_cfg("shelf_shelf", "shelf_shelf", "ymtnqa", abilities={"attachable": {}}), + # get_obj_cfg("shelf_baseboard", "shelf_baseboard", "hlhneo", abilities={"attachable": {}}), + # get_obj_cfg("bracelet", "bracelet", "thqqmo"), + # get_obj_cfg("oyster", "oyster", "enzocs"), + # get_obj_cfg("sink", "sink", "egwapq", scale=np.ones(3)), + # get_obj_cfg("stockpot", "stockpot", "dcleem", abilities={"fillable": {}}), + # get_obj_cfg("applier_dishtowel", "dishtowel", "dtfspn", abilities={"particleApplier": {"method": ParticleModifyMethod.ADJACENCY, "conditions": {"water": []}}}), + # get_obj_cfg("remover_dishtowel", "dishtowel", "dtfspn", abilities={"particleRemover": {"method": ParticleModifyMethod.ADJACENCY, "conditions": {"water": []}}}), + # get_obj_cfg("spray_bottle", "spray_bottle", "asztxi", visual_only=True, abilities={"toggleable": {}, "particleApplier": {"method": ParticleModifyMethod.PROJECTION, "conditions": {"water": [(ParticleModifyCondition.TOGGLEDON, True)]}}}), + # get_obj_cfg("vacuum", "vacuum", "bdmsbr", visual_only=True, abilities={"toggleable": {}, "particleRemover": {"method": ParticleModifyMethod.PROJECTION, "conditions": {"water": [(ParticleModifyCondition.TOGGLEDON, True)]}}}), + # get_obj_cfg("blender", "blender", "cwkvib", bounding_box=[0.316, 0.318, 0.649], abilities={"fillable": {}, "blender": {}, "toggleable": {}}), + # get_obj_cfg("oven", "oven", "cgtaer", bounding_box=[0.943, 0.837, 1.297]), + get_obj_cfg("knife", "carving_knife", "usqmjc"), + get_obj_cfg("apple", "apple", "agveuv", bounding_box=[0.098, 0.098, 0.115]), ], "robots": [ { From 5b11605e2d24f9c42471c41577da10682a7d45e6 Mon Sep 17 00:00:00 2001 From: Arman Aydin <86849247+goldenJester@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:03:08 -0800 Subject: [PATCH 4/5] Readd slicing tests --- tests/test_transition_rules.py | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/test_transition_rules.py b/tests/test_transition_rules.py index 22c4f1156..20101d772 100644 --- a/tests/test_transition_rules.py +++ b/tests/test_transition_rules.py @@ -1546,3 +1546,80 @@ def test_single_toggleable_machine_rule_output_object_success(): obj = DatasetObject(**obj_cfg) og.sim.import_object(obj) og.sim.step() + +@og_test +def test_slicer_volume_sharp_end(): + """ + Demo of slicing an apple into two apple slices + """ + + # Create the scene config to load -- empty scene with knife and apple + + # Grab reference to apple and knife + apple = og.sim.scene.object_registry("name", "apple") + knife = og.sim.scene.object_registry("name", "knife") + place_obj_on_floor_plane(apple) + place_obj_on_floor_plane(knife) + + + # Let apple settle + for _ in range(50): + og.sim.step() + + # knife.keep_still() + knife.set_position_orientation( + position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), + orientation=T.euler2quat([-np.pi / 4, 0, 0]), + ) + + # Step simulation for a bit so that apple is sliced + for i in range(10000): + og.sim.step() + + # Check if apple is sliced + is_empty = len(og.sim.scene.object_registry("category", "half_apple", default_val=[])) == 0 + assert not is_empty + + # Remove objects + og.sim.remove_object(apple) + og.sim.remove_object(knife) + +@og_test +def test_slicer_volume_blunt_end(): + """ + Demo of slicing an apple into two apple slices + """ + + # Create the scene config to load -- empty scene with knife and apple + + # Grab reference to apple and knife + apple = og.sim.scene.object_registry("name", "apple") + knife = og.sim.scene.object_registry("name", "knife") + place_obj_on_floor_plane(apple) + place_obj_on_floor_plane(knife) + + # Let apple settle + for _ in range(50): + og.sim.step() + + knife.keep_still() + knife.set_position_orientation( + position=apple.get_position() + np.array([-0.15, 0.0, 0.15]), + orientation=T.euler2quat([np.pi / 2, 0, 0]), + ) + + # Step simulation for a bit so that apple is sliced + for i in range(100): + og.sim.step() + + + # Check if apple is not sliced + is_sliced = og.sim.scene.object_registry("name", "apple") is None + assert not is_sliced + + # Remove objects + og.sim.remove_object(apple) + og.sim.remove_object(knife) + +if __name__ == "__main__": + test_slicer_volume_sharp_end() \ No newline at end of file From bf3c0c1d5ddd6d7bb65694e24fcf544563413e96 Mon Sep 17 00:00:00 2001 From: Arman Aydin <86849247+goldenJester@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:31:01 -0700 Subject: [PATCH 5/5] Update test_transition_rules.py Final changes to the transition test --- tests/test_transition_rules.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_transition_rules.py b/tests/test_transition_rules.py index 20101d772..e881ce2d9 100644 --- a/tests/test_transition_rules.py +++ b/tests/test_transition_rules.py @@ -1563,17 +1563,17 @@ def test_slicer_volume_sharp_end(): # Let apple settle - for _ in range(50): + for _ in range(100): og.sim.step() # knife.keep_still() knife.set_position_orientation( position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), - orientation=T.euler2quat([-np.pi / 4, 0, 0]), + orientation=T.euler2quat([-np.pi, 0, 0]), ) # Step simulation for a bit so that apple is sliced - for i in range(10000): + for i in range(100): og.sim.step() # Check if apple is sliced @@ -1599,17 +1599,17 @@ def test_slicer_volume_blunt_end(): place_obj_on_floor_plane(knife) # Let apple settle - for _ in range(50): + for _ in range(100): og.sim.step() knife.keep_still() knife.set_position_orientation( - position=apple.get_position() + np.array([-0.15, 0.0, 0.15]), - orientation=T.euler2quat([np.pi / 2, 0, 0]), + position=apple.get_position() + np.array([-0.15, 0.0, 0.2]), + orientation=T.euler2quat([np.pi, 0, 0]), ) # Step simulation for a bit so that apple is sliced - for i in range(100): + for i in range(1000): og.sim.step() @@ -1622,4 +1622,5 @@ def test_slicer_volume_blunt_end(): og.sim.remove_object(knife) if __name__ == "__main__": + test_slicer_volume_blunt_end() test_slicer_volume_sharp_end() \ No newline at end of file