def test_parse_scalar_sdf_strings(self): def generate_scalar_test_obj(name, value=None): if value is None: value = random.random() sdf_str = '<{}>{}</{}>'.format(name, value, name) expected_sdf = create_sdf_element(name) self.assertIsNotNone(expected_sdf, '{} returned None'.format(name)) expected_sdf.value = value return sdf_str, expected_sdf scalar_test_cases = list() for c in XMLScalar.__subclasses__(): if c._TYPE == 'sdf': scalar_test_cases.append(c._NAME) # Exclude special cases exclude = ['friction', 'range', 'poissons_ratio'] for tag in scalar_test_cases: if tag in exclude: continue sdf_str, expected_sdf = generate_scalar_test_obj(tag) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # TODO Add tests for friction and range sdf_str, expected_sdf = generate_scalar_test_obj( 'poissons_ratio', random.uniform(-1, 0.5)) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf)
def test_parse_integer_sdf_strings(self): def generate_integer_test_obj(name, value=None): if value is None: value = random.randint(1, 10) sdf_str = '<{}>{}</{}>'.format(name, value, name) expected_sdf = create_sdf_element(name) self.assertIsNotNone(expected_sdf, '{} returned None'.format(name)) expected_sdf.value = value return sdf_str, expected_sdf int_test_cases = list() for c in XMLInteger.__subclasses__(): if c._TYPE == 'sdf': int_test_cases.append(c._NAME) # Exclude special cases exclude = [] for tag in int_test_cases: if tag in exclude: continue sdf_str, expected_sdf = generate_integer_test_obj(tag) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf)
def test_add_light(self): sdf = """ <light type="point" name="point_light"> <pose>0 2 2 0 0 0</pose> <diffuse>1 0 0 1</diffuse> <specular>.1 .1 .1 1</specular> <attenuation> <range>20</range> <linear>0.2</linear> <constant>0.8</constant> <quadratic>0.01</quadratic> </attenuation> <cast_shadows>false</cast_shadows> </light> """ light = Light.from_sdf(parse_sdf(sdf)) group = ModelGroup() group.add_light('light', light) self.assertEqual(group.n_lights, 1) self.assertIsNotNone(group.get_light('light')) group_light = group.get_light('light') self.assertIsNotNone(group_light)
def test_heightmap_generator_to_gazebo_model(self): hg = HeightmapGenerator() hg.biome = WhittakerBiome() model_name = generate_random_string(10) output_dir = os.path.join(os.path.expanduser('~'), '.gazebo', 'models') if not os.path.isdir(output_dir): os.makedirs(output_dir) model = hg.as_model() model.name = model_name model.to_gazebo_model(output_dir=output_dir, copy_resources=True) self.assertTrue(os.path.isdir(os.path.join(output_dir, model_name))) sdf = parse_sdf(os.path.join(output_dir, model_name, 'model.sdf')) self.assertIsNotNone(sdf) self.assertIsNotNone(sdf.models[0]) heightmap = \ sdf.models[0].links[0].visuals[0].geometry.heightmap self.assertIsNotNone(heightmap.uri) self.assertIn('model://{}'.format(model_name), heightmap.uri.value) self.assertIn('/materials/textures/', heightmap.uri.value) self.assertTrue(heightmap.uri.value.endswith('.png')) heightmap = \ sdf.models[0].links[0].collisions[0].geometry.heightmap self.assertIsNotNone(heightmap.uri) self.assertIn('model://{}'.format(model_name), heightmap.uri.value) self.assertIn('/materials/textures/', heightmap.uri.value) self.assertTrue(heightmap.uri.value.endswith('.png')) shutil.rmtree(os.path.join(output_dir, model_name))
def test_actor_walking(self): add_custom_gazebo_resource_path(os.path.join(CUR_DIR, 'gazebo_models')) sdf = parse_sdf( os.path.join(CUR_DIR, 'gazebo_models', 'test_actor_walking', 'model.sdf')) self.assertIsNotNone(sdf) self.assertIsNotNone(sdf.actors) self.assertEqual(len(sdf.actors), 1) actor = Actor.from_sdf(sdf.actors[0]) self.assertIsNotNone(actor) self.assertEqual(actor.name, 'actor') self.assertEqual(actor.pose.position[0], 0) self.assertEqual(actor.pose.position[1], 0) self.assertEqual(actor.pose.position[2], 0) self.assertEqual(len(actor.animations), 1) self.assertEqual(actor.animations[0].name, 'walking') self.assertEqual(actor.animations[0].filename, 'model://test_actor_walking/meshes/walk.dae') self.assertEqual(actor.animations[0].scale, 1) self.assertEqual(actor.animations[0].interpolate_x, True) self.assertEqual(len(actor.script.trajectories), 1) self.assertEqual(actor.script.trajectories[0].id, 0) self.assertEqual(actor.script.trajectories[0].type, 'walking') self.assertEqual(len(actor.script.trajectories[0].waypoints), 32)
def test_parse_gazebo_models(self): gazebo_models_dir = \ os.path.join(CUR_DIR, 'gazebo_models') for item in os.listdir(gazebo_models_dir): sdf = parse_sdf( os.path.join(gazebo_models_dir, item, 'model.sdf')) self.assertIsNotNone(sdf) if '_actor_' in item: self.assertIsNotNone(sdf.actors) else: self.assertIsNotNone(sdf.models)
def test_parse_actor_relative_paths(self): sdf = parse_sdf(os.path.join( CUR_DIR, 'gazebo_models', 'test_actor_relative_paths', 'model.sdf')) self.assertIsNotNone(sdf) self.assertIsNotNone(sdf.actors) self.assertEqual(len(sdf.actors), 1) self.assertEqual(len(sdf.actors[0].animations), 8) self.assertEqual(len(sdf.actors[0].script.trajectories), 13)
def test_template_world_gen(self): root_path = os.path.join(CWD, 'templates', 'world_generator') for template_file in os.listdir(root_path): if not os.path.isfile(os.path.join(root_path, template_file)): continue output_xml = process_jinja_template( os.path.join(root_path, template_file)) sdf = parse_sdf(output_xml) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.world)
def test_export_model_to_sdf(self): box = self.create_random_box() sdf = box.to_sdf('model') filename = '/tmp/box.sdf' sdf.export_xml(filename, version='1.6') self.assertTrue(os.path.isfile(filename)) # Parse the exported file parsed_sdf = parse_sdf(filename) self.assertIsNotNone(parsed_sdf) self.assertEqual(sdf, parsed_sdf)
def test_kobuki_robot_description(self): template_dir = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'examples', 'robot_description', 'kobuki', 'sdf')) template = os.path.join(template_dir, 'kobuki.sdf.jinja') xml = process_jinja_template(template) sdf = parse_sdf(xml) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models), 1) self.assertEqual(sdf.models[0].name, 'kobuki')
def test_parse_world_files(self): world_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'worlds') for item in os.listdir(world_dir): world_file = os.path.join(world_dir, item) if not os.path.isfile(world_file) or \ not world_file.endswith('.world'): continue sdf = parse_sdf(world_file) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone( sdf.world, 'No world element was parsed from file {}'.format(world_file)) if sdf.world.includes is None: world = World.from_sdf(sdf) self.assertIsNotNone(world) w_sdf = world.to_sdf(type='sdf') self.assertIsNotNone(w_sdf.world) for tag in sdf.world.children: if sdf.world.children[tag] is None: continue if isinstance(sdf.world.children[tag], list): if tag == 'model': for model in sdf.world.children[tag]: self.assertTrue(world.model_exists(model.name)) if tag == 'light': for light in sdf.world.children[tag]: self.assertTrue(world.light_exists(light.name)) elif tag == 'physics': self.assertEqual(sdf.world.physics.type, w_sdf.world.physics.type) for physics_tag in sdf.world.physics.children: obj = getattr(sdf.world.physics, physics_tag) self.assertIn(physics_tag, w_sdf.world.physics.children) if obj.has_value(): self.assertEqual( obj, getattr(w_sdf.world.physics, physics_tag)) else: obj = getattr(sdf.world, tag) if obj.has_value(): self.assertEqual(obj, getattr(w_sdf.world, tag))
def test_parse_world_files(self): world_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'worlds' ) for item in os.listdir(world_dir): world_file = os.path.join(world_dir, item) if not os.path.isfile(world_file) or \ not world_file.endswith('.world'): continue sdf = parse_sdf(world_file) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone( sdf.world, 'No world element was parsed from file {}'.format(world_file))
def test_parse_vector_sdf_strings(self): def generate_array_test_obj(name, vec=None): expected_sdf = create_sdf_element(name) self.assertIsNotNone(expected_sdf, '{} returned None'.format(name)) if vec is None: vec = [random.random() for _ in range(expected_sdf._size)] sdf_str = '<{}>{}</{}>'.format(name, ' '.join(str(x) for x in vec), name) expected_sdf.value = vec return sdf_str, expected_sdf array_test_cases = list() for c in XMLVector.__subclasses__(): if c._TYPE == 'sdf': array_test_cases.append(c._NAME) exclude = ['xyz'] for tag in array_test_cases: if tag in exclude: continue sdf_str, expected_sdf = generate_array_test_obj(tag) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Test for XYZ vectors sdf_str, expected_sdf = generate_array_test_obj('xyz', [1, 0, 0]) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) sdf_str, expected_sdf = generate_array_test_obj('xyz', [0, 1, 0]) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) sdf_str, expected_sdf = generate_array_test_obj('xyz', [0, 0, 1]) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf)
def test_actor_relative_paths(self): add_custom_gazebo_resource_path(os.path.join(CUR_DIR, 'gazebo_models')) sdf = parse_sdf( os.path.join(CUR_DIR, 'gazebo_models', 'test_actor_relative_paths', 'model.sdf')) self.assertIsNotNone(sdf) self.assertIsNotNone(sdf.actors) self.assertEqual(len(sdf.actors), 1) actor = Actor.from_sdf(sdf.actors[0]) self.assertIsNotNone(actor) self.assertEqual(actor.name, 'actor_test') self.assertEqual(actor.pose.position[0], 0) self.assertEqual(actor.pose.position[1], 0) self.assertEqual(actor.pose.position[2], 1) self.assertEqual(len(actor.animations), 8) self.assertEqual(len(actor.script.trajectories), 13)
def test_xml_input_random(self): urdf_elements = [ 'mass', 'child', 'parent', 'origin', 'box', 'cylinder', 'sphere', 'mesh', 'limit', 'inertial', 'inertia', 'dynamics' ] for urdf_name in urdf_elements: obj = create_urdf_element(urdf_name) assert obj is not None obj.random() output = subprocess.check_output( ['pcg-urdf2sdf', '--xml', obj.to_xml_as_str(), '--print']) urdf = parse_sdf(output.decode('utf-8')) assert urdf is not None response_urdf = sdf2urdf(urdf) assert obj == response_urdf
def test_parse_biome_from_heightmap(self): add_custom_gazebo_resource_path( os.path.join(CUR_DIR, '..', 'examples', 'models')) # Parse the SDF file sdf = parse_sdf( os.path.join(CUR_DIR, '..', 'examples', 'models', 'pcg_winding_valley_heightmap', 'model.sdf')) self.assertIsNotNone(sdf) sdf_heightmap = sdf.models[0].links[0].visuals[0].geometry.heightmap biome = Biome.from_sdf(sdf_heightmap) self.assertIsNotNone(biome) self.assertEqual(biome.n_elevation_zones, 3) self.assertEqual(biome.n_moisture_zones, 1) for i in range(biome.n_elevation_zones): tag = biome.get_biome(i, 0) self.assertIsNotNone(tag) self.assertIsNotNone(biome.get_diffuse(tag)) self.assertIsNotNone(biome.get_normal(tag))
def randomize_configured_model(self, model_path, min_scale=0.05, max_scale=0.25, min_mass=0.1, max_mass=3.0, min_friction=0.75, max_friction=1.5): # Get path to the configured SDF file configured_sdf_path = self.get_configured_sdf_path(model_path) # Parse the configured SDF that needs to be randomized sdf = parse_sdf(configured_sdf_path) # Process the model(s) contained in the SDF for model in sdf.models: # Process the link(s) of each model for link in model.links: # Randomize scale of the link self.randomize_scale(model_path, link, min_scale=min_scale, max_scale=max_scale) # Randomize inertial properties of the link self.randomize_inertial(link, min_mass=min_mass, max_mass=max_mass) # Randomize friction of the link self.randomize_friction(link, min_friction=min_friction, max_friction=max_friction) # Overwrite the configured SDF file with randomized values sdf.export_xml(configured_sdf_path)
def test_parse_heightmap_model(self): add_custom_gazebo_resource_path( os.path.join(CUR_DIR, '..', 'examples', 'models')) # Parse the SDF file sdf = parse_sdf( os.path.join(CUR_DIR, '..', 'examples', 'models', 'pcg_winding_valley_heightmap', 'model.sdf')) self.assertIsNotNone(sdf) self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models[0].links[0].visuals), 1) self.assertEqual(len(sdf.models[0].links[0].collisions), 1) sdf_heightmap = sdf.models[0].links[0].visuals[0].geometry.heightmap self.assertIsNotNone(sdf_heightmap) uri_path_prefix = \ 'model://pcg_winding_valley_heightmap/materials/textures' self.assertIn(uri_path_prefix, sdf_heightmap.uri.value) self.assertEqual(len(sdf_heightmap.textures), 3) for texture in sdf_heightmap.textures: self.assertIn(uri_path_prefix, texture.diffuse.value) self.assertIn(uri_path_prefix, texture.normal.value) self.assertEqual(texture.size.value, [10]) self.assertEqual(len(sdf_heightmap.blends), 2) for blend in sdf_heightmap.blends: self.assertGreater(blend.min_height.value, 0) self.assertGreater(blend.fade_dist.value, 0) self.assertEqual(sdf_heightmap.size.value, [1000, 1000, 25]) self.assertEqual(sdf_heightmap.pos.value, [0, 0, -4]) sdf_heightmap = \ sdf.models[0].links[0].collisions[0].geometry.heightmap self.assertIsNotNone(sdf_heightmap) self.assertIn(uri_path_prefix, sdf_heightmap.uri.value) self.assertEqual(sdf_heightmap.size.value, [1000, 1000, 25]) self.assertEqual(sdf_heightmap.pos.value, [0, 0, -4])
def test_xml_input_visual_collision(self): for urdf_tag in ['visual', 'collision']: obj = create_urdf_element(urdf_tag) assert obj is not None obj.origin = create_urdf_element('origin') obj.origin.random() obj.geometry = create_urdf_element('geometry') geometries = ['box', 'cylinder', 'sphere', 'mesh'] for geo_name in geometries: obj.geometry.reset(mode=geo_name) obj.geometry.random() output = subprocess.check_output( ['pcg-urdf2sdf', '--xml', obj.to_xml_as_str(), '--print']) urdf = parse_sdf(output.decode('utf-8')) assert urdf is not None response_urdf = sdf2urdf(urdf) assert obj == response_urdf
def test_parse_string_sdf_strings(self): def generate_string_test_obj(name, value=None): if value is None: value = generate_random_string(5) sdf_str = '<{}>{}</{}>'.format(name, value, name) expected_sdf = create_sdf_element(name) self.assertIsNotNone(expected_sdf, '{} returned None'.format(name)) expected_sdf.value = value return sdf_str, expected_sdf string_test_cases = list() for c in XMLString.__subclasses__(): if c._TYPE == 'sdf': string_test_cases.append(c._NAME) # Exclude special cases exclude = ['empty', 'format', 'friction_model', 'collision', 'measure_direction', 'localization', 'view_controller', 'projection_type', 'surface_model', 'world_frame_orientation'] for tag in string_test_cases: if tag in exclude: continue sdf_str, expected_sdf = generate_string_test_obj(tag) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Add tests for measure_direction values = ['child_to_parent', 'parent_to_link'] for value in values: sdf_str, expected_sdf = generate_string_test_obj( 'measure_direction', value) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Add tests for format values = ['L8', 'R8G8B8', 'B8G8R8', 'BAYER_RGGB8', 'BAYER_BGGR8', 'BAYER_GBRG8', 'BAYER_GRBG8'] for value in values: sdf_str, expected_sdf = generate_string_test_obj('format', value) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Add tests for friction_model values = ['pyramid_model', 'box_model', 'cone_model'] for value in values: sdf_str, expected_sdf = generate_string_test_obj( 'friction_model', value) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Add tests for localization values = ['CUSTOM', 'NED', 'ENU', 'NWU', 'GRAV_UP', 'GRAV_DOWN'] for value in values: sdf_str, expected_sdf = generate_string_test_obj( 'localization', value) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf) # Add tests for projection_type values = ['perspective', 'orthographic'] for value in values: sdf_str, expected_sdf = generate_string_test_obj( 'projection_type', value) sdf = parse_sdf(sdf_str) self.assertIsNotNone(sdf) self.assertEqual(parse_sdf(sdf_str), expected_sdf)
def test_model_group_to_sdf(self): sdf_light = """ <light type="point" name="point_light"> <pose>0 2 2 0 0 0</pose> <diffuse>1 0 0 1</diffuse> <specular>.1 .1 .1 1</specular> <attenuation> <range>20</range> <linear>0.2</linear> <constant>0.8</constant> <quadratic>0.01</quadratic> </attenuation> <cast_shadows>false</cast_shadows> </light> """ light = Light.from_sdf(parse_sdf(sdf_light)) box = box_factory(size=[[1, 1, 1]], mass=1, use_permutation=True, name='box')[0] group = ModelGroup(name='test') group.add_light('light', light) group.add_model('box', box) sdf_models, sdf_lights, sdf_includes = group.to_sdf(use_include=False) self.assertEqual(len(sdf_models), 1) self.assertEqual(len(sdf_lights), 1) self.assertEqual(len(sdf_includes), 0) group_box = group.get_model('box', with_group_prefix=False) self.assertIsNotNone(group_box) self.assertEqual(group_box.name, 'box') group_box = group.get_model('box', with_group_prefix=True) self.assertIsNotNone(group_box) self.assertEqual(group_box.name, 'test/box') group_models = group.get_models(with_group_prefix=False) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 1) self.assertIn('box', group_models) group_models = group.get_models(with_group_prefix=True) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 1) self.assertIn('test/box', group_models) group_light = group.get_light('light', with_group_prefix=False) self.assertIsNotNone(group_light) self.assertEqual(group_light.name, 'light') group_light = group.get_light('light', with_group_prefix=True) self.assertIsNotNone(group_light) self.assertEqual(group_light.name, 'test/light') group_lights = group.get_lights(with_group_prefix=False) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('light', group_lights) group_lights = group.get_lights(with_group_prefix=True) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('test/light', group_lights)
def test_nested_model_groups(self): sdf_light = """ <light type="point" name="point_light"> <pose>0 2 2 0 0 0</pose> <diffuse>1 0 0 1</diffuse> <specular>.1 .1 .1 1</specular> <attenuation> <range>20</range> <linear>0.2</linear> <constant>0.8</constant> <quadratic>0.01</quadratic> </attenuation> <cast_shadows>false</cast_shadows> </light> """ light = Light.from_sdf(parse_sdf(sdf_light)) box_1 = box_factory(size=[[1, 1, 1]], mass=1, use_permutation=True, name='box')[0] box_2 = box_factory(size=[[2, 2, 2]], mass=2, use_permutation=True, name='box')[0] nested_group = ModelGroup(name='nested') root_group = ModelGroup(name='root') nested_group.add_light('light', light) nested_group.add_model('box', box_1) root_group.add_model('box', box_2) root_group.add_model('nested', nested_group) # Trying to get light from the root group, should return None gl = root_group.get_light('light') self.assertIsNone(gl) gl = root_group.get_light('nested/light') self.assertIsNotNone(gl) # Trying to get boxes from both groups gb = root_group.get_model('box') self.assertIsNotNone(gb) self.assertEqual(gb.links[gb.link_names[0]].inertial.mass, 2) gb = root_group.get_model('nested/box') self.assertIsNotNone(gb) self.assertEqual(gb.links[gb.link_names[0]].inertial.mass, 1) # Request all models from nested group (with and without prefix) group_models = nested_group.get_models(with_group_prefix=False) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 1) self.assertIn('box', group_models) group_models = nested_group.get_models(with_group_prefix=True) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 1) self.assertIn('nested/box', group_models) # Request all lights from nested group (with and without prefix) group_lights = nested_group.get_lights(with_group_prefix=False) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('light', group_lights) group_lights = nested_group.get_lights(with_group_prefix=True) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('nested/light', group_lights) # Request all models from root group (with and without prefix) group_models = root_group.get_models(with_group_prefix=False) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 2) self.assertIn('box', group_models) self.assertIn('nested/box', group_models) group_models = root_group.get_models(with_group_prefix=True) self.assertIsInstance(group_models, dict) self.assertEqual(len(group_models), 2) self.assertIn('root/box', group_models) self.assertIn('root/nested/box', group_models) # Request all lights from root group (with and without prefix) group_lights = root_group.get_lights(with_group_prefix=False) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('nested/light', group_lights) group_lights = root_group.get_lights(with_group_prefix=True) self.assertIsInstance(group_lights, dict) self.assertEqual(len(group_lights), 1) self.assertIn('root/nested/light', group_lights)
def test_export_models_to_gazebo_model(self): models = [ SimulationModel(name=generate_random_string(5)) for _ in range(2) ] model_names = [obj.name for obj in models] sdf_elements = [model.to_sdf() for model in models] group = ModelGroup.from_sdf(sdf_elements) group.name = generate_random_string(5) group.pose = Pose.random() self.assertIsNotNone(group) self.assertEqual(group.n_models, len(models)) self.assertEqual(group.n_lights, 0) # Convert to separate SDF elements sdf_models, sdf_lights, sdf_includes = group.to_sdf() self.assertEqual(len(sdf_models), len(models)) for tag in sdf_models: self.assertIn( sdf_models[tag].name.replace('{}/'.format(group.name), ''), model_names) self.assertEqual(len(sdf_lights), 0) self.assertEqual(len(sdf_includes), 0) # Convert to default Gazebo model self.assertIsNotNone(group.to_gazebo_model()) default_dir = os.path.join(os.path.expanduser('~'), '.gazebo', 'models') model_dir = os.path.join(default_dir, group.name) # Check if all model files were created self.assertTrue(os.path.isdir(model_dir)) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.config'))) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.sdf'))) # Parse model config file sdf_config = parse_sdf_config(os.path.join(model_dir, 'model.config')) self.assertIsNotNone(sdf_config) # Get name of current user, used in default model metadata input username = getpass.getuser() self.assertEqual(sdf_config.name.value, group.name) self.assertEqual(len(sdf_config.authors), 1) self.assertEqual(sdf_config.authors[0].name.value, username) self.assertEqual(sdf_config.authors[0].email.value, '{}@email.com'.format(username)) self.assertEqual(sdf_config.description.value, '') self.assertEqual(sdf_config.version.value, '1.6') self.assertEqual(len(sdf_config.sdfs), 1) self.assertEqual(sdf_config.sdfs[0].value, 'model.sdf') self.assertEqual(sdf_config.sdfs[0].version, '1.6') # Parse the SDF file sdf = parse_sdf(os.path.join(model_dir, 'model.sdf')) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models), 1) for i, j in zip(sdf.models[0].pose.value, group.pose.to_sdf().value): self.assertTrue(numpy.isclose(i, j)) self.assertEqual(len(sdf.models[0].models), len(models)) for i in range(len(sdf.models[0].models)): self.assertEqual(sdf.models[0].models[i].pose.value, [0 for _ in range(6)]) self.assertIn(sdf.models[0].models[i].name, model_names) # Delete generated Gazebo model directory shutil.rmtree(model_dir) # Rename group group.name = generate_random_string(5) # Export group with individually exported models self.assertIsNotNone(group.to_gazebo_model(nested=False)) # Check if model folders exist self.assertTrue(os.path.isdir(os.path.join(default_dir, group.name))) for name in model_names: self.assertTrue(os.path.isdir(os.path.join(default_dir, name))) # Parse the SDF file for the main model sdf = parse_sdf(os.path.join(default_dir, group.name, 'model.sdf')) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models), 1) self.assertIsNone(sdf.models[0].models) self.assertIsNotNone(sdf.models[0].includes) self.assertEqual(len(sdf.models[0].includes), len(models)) for i, j in zip(sdf.models[0].pose.value, group.pose.to_sdf().value): self.assertTrue(numpy.isclose(i, j)) for i in range(len(sdf.models[0].includes)): self.assertEqual(sdf.models[0].includes[i].pose.value, [0 for _ in range(6)]) self.assertIn(sdf.models[0].includes[i].name.value, model_names) self.assertEqual( sdf.models[0].includes[i].uri.value, 'model://{}'.format(sdf.models[0].includes[i].name.value)) shutil.rmtree(os.path.join(default_dir, group.name)) for name in model_names: shutil.rmtree(os.path.join(default_dir, name))
def generate_sdf(test_case, params): xml = process_jinja_template( os.path.join(CUR_DIR, 'jinja_sdf', '{}.jinja'.format(test_case)), params) return parse_sdf(xml)
def process_model(self, model_path, decimation_fraction_of_visual=0.25, decimation_min_faces=40, decimation_max_faces=200, max_faces=40000, max_vertices=None, component_min_faces_fraction=0.05, component_max_volume_fraction=0.1, fix_mtl_texture_paths=True) -> bool: # Parse the SDF of the model sdf = parse_sdf(self.get_sdf_path(model_path)) # Process the model(s) contained in the SDF for model in sdf.models: # Process the link(s) of each model for link in model.links: # Get rid of the existing collisions prior to simplifying it link.collisions.clear() # Values for the total inertial properties of current link # These values will be updated for each body that the link contains total_mass = 0.0 total_inertia = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] common_centre_of_mass = [0.0, 0.0, 0.0] # Go through the visuals and process them for visual in link.visuals: # Get path to the mesh of the link's visual mesh_path = self.get_mesh_path(model_path, visual) # If desired, fix texture path in 'mtl' files for '.obj' mesh format if fix_mtl_texture_paths: self.fix_mtl_texture_paths(model_path, mesh_path, model.attributes['name']) # Load the mesh (without materials) mesh = trimesh.load(mesh_path, force='mesh', skip_materials=True) # Check if model has too much geometry (blacklist if needed) if not self.check_excessive_geometry( mesh, model_path, max_faces=max_faces, max_vertices=max_vertices): return False # Check if model has disconnected geometry/components (blacklist if needed) if not self.check_disconnected_components( mesh, model_path, component_min_faces_fraction= component_min_faces_fraction, component_max_volume_fraction= component_max_volume_fraction): return False # Compute inertial properties for this mesh total_mass, total_inertia, common_centre_of_mass = \ self.sum_inertial_properties(mesh, total_mass, total_inertia, common_centre_of_mass) # Add decimated collision geometry to the SDF self.add_collision( mesh, link, model_path, fraction_of_visual=decimation_fraction_of_visual, min_faces=decimation_min_faces, max_faces=decimation_max_faces) # Write original scale (size) into the SDF # This is used for later reference during randomization (for scale limits) self.write_original_scale(mesh, model_path) # Make sure the link has valid inertial properties (blacklist if needed) if not self.check_inertial_properties(model_path, total_mass, total_inertia): return False # Write inertial properties to the SDF of the link self.write_inertial_properties(link, total_mass, total_inertia, common_centre_of_mass) # Write the configured SDF into a file sdf.export_xml(self.get_configured_sdf_path(model_path)) return True
def test_export_model_to_gazebo_model(self): box = self.create_random_box() model = SimulationModel.from_sdf(box.to_sdf()) self.assertIsNotNone(model) # Export Gazebo model with default parameters model.to_gazebo_model() default_dir = os.path.join(os.path.expanduser('~'), '.gazebo', 'models') model_dir = os.path.join(default_dir, box.name) # Check if all model files were created self.assertTrue(os.path.isdir(model_dir)) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.config'))) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.sdf'))) # Parse model config file sdf_config = parse_sdf_config(os.path.join(model_dir, 'model.config')) self.assertIsNotNone(sdf_config) # Get name of current user, used in default model metadata input username = getpass.getuser() self.assertEqual(sdf_config.name.value, box.name) self.assertEqual(len(sdf_config.authors), 1) self.assertEqual(sdf_config.authors[0].name.value, username) self.assertEqual(sdf_config.authors[0].email.value, '{}@email.com'.format(username)) self.assertEqual(sdf_config.description.value, '') self.assertEqual(sdf_config.version.value, '1.6') self.assertEqual(len(sdf_config.sdfs), 1) self.assertEqual(sdf_config.sdfs[0].value, 'model.sdf') self.assertEqual(sdf_config.sdfs[0].version, '1.6') sdf = parse_sdf(os.path.join(model_dir, 'model.sdf')) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models), 1) # Export Gazebo model with default parameters author = generate_random_string(5) email = generate_random_string(5) description = generate_random_string(10) model_metaname = generate_random_string(10) model.to_gazebo_model(author=author, email=email, description=description, model_metaname=model_metaname, overwrite=True) # Check if all model files were created self.assertTrue(os.path.isdir(model_dir)) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.config'))) self.assertTrue(os.path.isfile(os.path.join(model_dir, 'model.sdf'))) # Parse model config file sdf_config = parse_sdf_config(os.path.join(model_dir, 'model.config')) self.assertIsNotNone(sdf_config) self.assertEqual(sdf_config.name.value, model_metaname) self.assertEqual(len(sdf_config.authors), 1) self.assertEqual(sdf_config.authors[0].name.value, author) self.assertEqual(sdf_config.authors[0].email.value, email) self.assertEqual(sdf_config.description.value, description) self.assertEqual(sdf_config.version.value, '1.6') self.assertEqual(len(sdf_config.sdfs), 1) self.assertEqual(sdf_config.sdfs[0].value, 'model.sdf') self.assertEqual(sdf_config.sdfs[0].version, '1.6') # Parse the SDF file sdf = parse_sdf(os.path.join(model_dir, 'model.sdf')) self.assertIsNotNone(sdf) self.assertEqual(sdf.xml_element_name, 'sdf') self.assertIsNotNone(sdf.models) self.assertEqual(len(sdf.models), 1) # Delete generated Gazebo model directory shutil.rmtree(model_dir)
def test_from_sdf(self): add_custom_gazebo_resource_path( os.path.join(CUR_DIR, '..', 'examples', 'models')) # Parse the SDF file sdf = parse_sdf( os.path.join(CUR_DIR, '..', 'examples', 'models', 'pcg_winding_valley_heightmap', 'model.sdf')) self.assertIsNotNone(sdf) # Convert SDF heightmap element to simulation heightmap element sdf_heightmap = sdf.models[0].links[0].visuals[0].geometry.heightmap heightmap = Heightmap.from_sdf(sdf_heightmap) self.assertIsNotNone(heightmap) output_sdf = heightmap.to_sdf() self.assertIsNotNone(output_sdf) sdf_heightmap = \ sdf.models[0].links[0].collisions[0].geometry.heightmap heightmap = Heightmap.from_sdf(sdf_heightmap) self.assertIsNotNone(heightmap) output_sdf = heightmap.to_sdf() self.assertIsNotNone(output_sdf) self.assertEqual(sdf_heightmap.size.value, output_sdf.size.value) self.assertEqual(sdf_heightmap.pos.value, output_sdf.pos.value) # Convert model to SimulationModel element model = SimulationModel.from_sdf(sdf.models[0]) self.assertIsNotNone(model) output_sdf = model.to_sdf() self.assertIsNotNone(output_sdf) self.assertIsNotNone(output_sdf) self.assertEqual(len(output_sdf.links[0].visuals), 1) self.assertEqual(len(output_sdf.links[0].collisions), 1) sdf_heightmap = output_sdf.links[0].visuals[0].geometry.heightmap self.assertIsNotNone(sdf_heightmap) uri_path_prefix = \ 'model://pcg_winding_valley_heightmap/materials/textures' self.assertIn(uri_path_prefix, sdf_heightmap.uri.value) self.assertEqual(len(sdf_heightmap.textures), 3) for texture in sdf_heightmap.textures: self.assertIn(uri_path_prefix, texture.diffuse.value) self.assertIn(uri_path_prefix, texture.normal.value) self.assertEqual(texture.size.value, [10]) self.assertEqual(len(sdf_heightmap.blends), 2) for blend in sdf_heightmap.blends: self.assertGreater(blend.min_height.value, 0) self.assertGreater(blend.fade_dist.value, 0) self.assertEqual(sdf_heightmap.size.value, [1000, 1000, 25]) self.assertEqual(sdf_heightmap.pos.value, [0, 0, -4]) sdf_heightmap = \ output_sdf.links[0].collisions[0].geometry.heightmap self.assertIsNotNone(sdf_heightmap) self.assertIn(uri_path_prefix, sdf_heightmap.uri.value) self.assertEqual(sdf_heightmap.size.value, [1000, 1000, 25]) self.assertEqual(sdf_heightmap.pos.value, [0, 0, -4])