gear.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Software License Agreement (Apache License)
00004 #
00005 # Copyright 2016 Open Source Robotics Foundation
00006 #
00007 # Licensed under the Apache License, Version 2.0 (the "License");
00008 # you may not use this file except in compliance with the License.
00009 # You may obtain a copy of the License at
00010 #
00011 #     http://www.apache.org/licenses/LICENSE-2.0
00012 #
00013 # Unless required by applicable law or agreed to in writing, software
00014 # distributed under the License is distributed on an "AS IS" BASIS,
00015 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016 # See the License for the specific language governing permissions and
00017 # limitations under the License.
00018 
00019 from __future__ import print_function
00020 
00021 import argparse
00022 import em
00023 import math
00024 import os
00025 import subprocess
00026 import sys
00027 import yaml
00028 
00029 this_dir = os.path.abspath(os.path.dirname(__file__))
00030 template_files = [
00031     os.path.join(this_dir, '..', '..', 'share', 'osrf_gear', 'worlds', 'gear.world.template'),
00032     os.path.join(this_dir, '..', '..', 'share', 'osrf_gear', 'launch', 'gear.launch.template'),
00033     os.path.join(this_dir, '..', '..', 'share', 'osrf_gear', 'launch', 'gear.urdf.xacro.template'),
00034 ]
00035 arm_configs = {
00036     'ur10': {
00037         'default_initial_joint_states': {
00038             'linear_arm_actuator_joint': 0,
00039             'shoulder_pan_joint': 0,
00040             'shoulder_lift_joint': -1.13,
00041             'elbow_joint': 1.51,
00042             'wrist_1_joint': 3.77,
00043             'wrist_2_joint': -1.51,
00044             'wrist_3_joint': 0,
00045         }
00046     },
00047 }
00048 sensor_configs = {
00049     'break_beam': None,
00050     'proximity_sensor': None,
00051     'logical_camera': None,
00052     'laser_profiler': None,
00053 }
00054 default_bin_origins = {
00055     'bin1': [-1.0, -1.33, 0],
00056     'bin2': [-1.0, -0.535, 0],
00057     'bin3': [-1.0, 0.23, 0],
00058     'bin4': [-1.0, 0.995, 0],
00059     'bin5': [-0.3, -1.33, 0],
00060     'bin6': [-0.3, -0.535, 0],
00061     'bin7': [-0.3, 0.23, 0],
00062     'bin8': [-0.3, 0.995, 0],
00063 }
00064 configurable_options = {
00065     'insert_models_over_bins': False,
00066     'disable_shadows': False,
00067     'fill_demo_tray': False,
00068     'spawn_extra_models': False,
00069     'model_type_aliases': {
00070         'belt_model_type1': 'part1',
00071         'belt_model_type2': 'part2',
00072     },
00073 }
00074 
00075 
00076 def prepare_arguments(parser):
00077     add = parser.add_argument
00078     add('-n', '--dry-run', action='store_true', default=False,
00079         help='print generated files to stdout, but do not write them to disk')
00080     add('-o', '--output', default=os.getcwd(),
00081         help='directory in which to output the generated files')
00082     mex_group = parser.add_mutually_exclusive_group(required=False)
00083     add = mex_group.add_argument
00084     add('config', nargs="?", metavar="CONFIG",
00085         help='yaml string that is the configuration')
00086     add('-f', '--file', nargs='+', help='list of paths to yaml files that contain the '
00087     'configuration (contents will be concatenated)')
00088 
00089 
00090 eval_local_vars = {n: getattr(math, n) for n in dir(math) if not n.startswith('__')}
00091 
00092 
00093 def expand_to_float(val):
00094     if isinstance(val, str):
00095         return float(eval(val, {}, eval_local_vars))
00096     return float(val)
00097 
00098 
00099 def expand_yaml_substitutions(yaml_dict):
00100     for k, v in yaml_dict.items():
00101         if isinstance(v, dict):
00102             yaml_dict[k] = expand_yaml_substitutions(v)
00103         if k in ['xyz', 'rpy']:
00104             yaml_dict[k] = [expand_to_float(x) for x in v]
00105         if k in ['initial_joint_states']:
00106             yaml_dict[k] = {kp: expand_to_float(vp) for kp, vp in v.items()}
00107     return yaml_dict
00108 
00109 
00110 class ArmInfo:
00111     def __init__(self, arm_type, initial_joint_states):
00112         self.type = arm_type
00113         self.initial_joint_states = initial_joint_states
00114 
00115 
00116 class ModelInfo:
00117     def __init__(self, model_type, pose, reference_frame):
00118         self.type = model_type
00119         self.pose = pose
00120         self.reference_frame = reference_frame
00121 
00122 class SensorInfo:
00123     def __init__(self, name, sensor_type, pose):
00124         self.name = name
00125         self.type = sensor_type
00126         self.pose = pose
00127 
00128 
00129 class PoseInfo:
00130     def __init__(self, xyz, rpy):
00131         self.xyz = [str(f) for f in xyz]
00132         self.rpy = [str(f) for f in rpy]
00133 
00134 
00135 def get_required_field(entry_name, data_dict, required_entry):
00136     if required_entry not in data_dict:
00137         print("Error: '{0}' entry does not contain a required '{1}' entry"
00138               .format(entry_name, required_entry),
00139               file=sys.stderr)
00140         sys.exit(1)
00141     return data_dict[required_entry]
00142 
00143 
00144 def create_arm_info(arm_dict):
00145     arm_type = get_required_field('arm', arm_dict, 'type')
00146     if arm_type not in arm_configs:
00147         print("Error: arm type '{0}' is not one of the valid arm entries: {1}"
00148               .format(arm_type, arm_configs), file=sys.stderr)
00149         sys.exit(1)
00150     default_joint_states = arm_configs[arm_type]['default_initial_joint_states']
00151     merged_initial_joint_states = dict(default_joint_states)
00152     if 'initial_joint_states' in arm_dict:
00153         initial_joint_states = arm_dict['initial_joint_states']
00154         for k, v in initial_joint_states.items():
00155             if k not in merged_initial_joint_states:
00156                 print("Error: given joint name '{0}' is not one of the known joint "
00157                     "states for the '{1}' type arm: {2}".format(k, arm_type, default_joint_states),
00158                     file=sys.stderr)
00159                 sys.exit(1)
00160             merged_initial_joint_states[k] = v
00161     return ArmInfo(arm_type, merged_initial_joint_states)
00162 
00163 
00164 def create_pose_info(pose_dict):
00165     xyz = get_required_field('pose', pose_dict, 'xyz')
00166     rpy = get_required_field('pose', pose_dict, 'rpy')
00167     for key in pose_dict:
00168         if key not in ['xyz', 'rpy']:
00169             print("Warning: ignoring unknown entry in 'pose': " + key, file=sys.stderr)
00170     return PoseInfo(xyz, rpy)
00171 
00172 
00173 def create_sensor_info(name, sensor_data):
00174     sensor_type = get_required_field(name, sensor_data, 'type')
00175     pose_dict = get_required_field(name, sensor_data, 'pose')
00176     for key in sensor_data:
00177         if key not in ['type', 'pose']:
00178             print("Warning: ignoring unknown entry in '{0}': {1}"
00179                   .format(name, key), file=sys.stderr)
00180     if sensor_type not in sensor_configs:
00181         print("Error: given sensor type '{0}' is not one of the known sensor types: {1}"
00182               .format(sensor_type, sensor_configs.keys()), file=sys.stderr)
00183     pose_info = create_pose_info(pose_dict)
00184     return SensorInfo(name, sensor_type, pose_info)
00185 
00186 
00187 def create_sensor_infos(sensors_dict):
00188     sensor_infos = {}
00189     for name, sensor_data in sensors_dict.items():
00190         sensor_infos[name] = create_sensor_info(name, sensor_data)
00191     return sensor_infos
00192 
00193 
00194 def create_model_info(model_name, model_data):
00195     model_type = get_required_field(model_name, model_data, 'type')
00196     if model_type in configurable_options['model_type_aliases']:
00197         model_type = configurable_options['model_type_aliases'][model_type]
00198     pose_dict = get_required_field(model_name, model_data, 'pose')
00199     reference_frame = ''
00200     if 'reference_frame' in model_data:
00201         reference_frame = model_data['reference_frame']
00202     for key in model_data:
00203         if key not in ['type', 'pose', 'reference_frame']:
00204             print("Warning: ignoring unknown entry in '{0}': {1}"
00205                   .format(model_name, key), file=sys.stderr)
00206     pose_info = create_pose_info(pose_dict)
00207     return ModelInfo(model_type, pose_info, reference_frame)
00208 
00209 
00210 def create_models_to_spawn_infos(models_to_spawn_dict):
00211     models_to_spawn_infos = {}
00212     for reference_frame, reference_frame_data in models_to_spawn_dict.items():
00213         models = get_required_field(reference_frame, reference_frame_data, 'models')
00214         model_count = 0
00215         for model_name, model_to_spawn_data in models.items():
00216             model_to_spawn_data['reference_frame'] = reference_frame
00217             model_info = create_model_info(model_name, model_to_spawn_data)
00218             # assign each model a unique name because gazebo can't do this
00219             # if the models all spawn at the same time
00220             scoped_model_name = reference_frame.replace('::', '|') + '|' + \
00221               model_info.type + '_' + str(model_count)
00222             models_to_spawn_infos[scoped_model_name] = model_info
00223             model_count += 1
00224     return models_to_spawn_infos
00225 
00226 
00227 def create_models_over_bins_infos(models_over_bins_dict):
00228     models_to_spawn_infos = {}
00229     bin_width = 0.6
00230     bin_height = 0.75
00231     for bin_name, bin_dict in models_over_bins_dict.items():
00232         if bin_name in default_bin_origins:
00233             offset_xyz = [
00234                 default_bin_origins[bin_name][0] - bin_width / 2,
00235                 default_bin_origins[bin_name][1] - bin_width / 2,
00236                 bin_height]
00237             # Allow the origin of the bin to be over-written
00238             if 'xyz' in bin_dict:
00239                 offset_xyz = bin_dict['xyz']
00240         else:
00241             offset_xyz = get_required_field(bin_name, bin_dict, 'xyz')
00242 
00243         models = get_required_field(bin_name, bin_dict, 'models') or {}
00244         for model_type, model_to_spawn_dict in models.items():
00245             model_count = 0
00246             model_to_spawn_data = {}
00247             model_to_spawn_data['type'] = model_type
00248             model_to_spawn_data['reference_frame'] = 'world'
00249             xyz_start = get_required_field(
00250                 model_type, model_to_spawn_dict, 'xyz_start')
00251             xyz_end = get_required_field(
00252                 model_type, model_to_spawn_dict, 'xyz_end')
00253             rpy = get_required_field(model_type, model_to_spawn_dict, 'rpy')
00254             num_models_x = get_required_field(
00255                 model_type, model_to_spawn_dict, 'num_models_x')
00256             num_models_y = get_required_field(
00257                 model_type, model_to_spawn_dict, 'num_models_y')
00258             step_size = [
00259                 (xyz_end[0] - xyz_start[0]) / max(1, num_models_x - 1),
00260                 (xyz_end[1] - xyz_start[1]) / max(1, num_models_y - 1)]
00261 
00262             # Create a grid of models
00263             for idx_x in range(num_models_x):
00264                 for idx_y in range(num_models_y):
00265                     xyz = [
00266                         offset_xyz[0] + xyz_start[0] + idx_x * step_size[0],
00267                         offset_xyz[1] + xyz_start[1] + idx_y * step_size[1],
00268                         offset_xyz[2] + xyz_start[2]]
00269                     model_to_spawn_data['pose'] = {'xyz': xyz, 'rpy': rpy}
00270                     model_info = create_model_info(model_type, model_to_spawn_data)
00271                     # assign each model a unique name because gazebo can't do this
00272                     # if the models all spawn at the same time
00273                     scoped_model_name = bin_name + '|' + \
00274                         model_info.type + '_' + str(model_count)
00275                     models_to_spawn_infos[scoped_model_name] = model_info
00276                     model_count += 1
00277     return models_to_spawn_infos
00278 
00279 
00280 def create_belt_part_infos(belt_parts_dict):
00281     belt_part_infos = {}
00282     for spawn_time, belt_part_dict in belt_parts_dict.items():
00283       belt_part_infos[spawn_time] = create_model_info('belt_part', belt_part_dict)
00284     return belt_part_infos
00285 
00286 
00287 def create_drops_info(drops_dict):
00288     drops_info = {}
00289     drop_region = get_required_field('drops', drops_dict, 'drop_region')
00290     drop_region_min = get_required_field('drop_region', drop_region, 'min')
00291     drop_region_min_xyz = get_required_field('min', drop_region_min, 'xyz')
00292     drop_region_max = get_required_field('drop_region', drop_region, 'max')
00293     drop_region_max_xyz = get_required_field('max', drop_region_max, 'xyz')
00294     drops_info['drop_region_min'] = drop_region_min_xyz
00295     drops_info['drop_region_max'] = drop_region_max_xyz
00296 
00297     drops_dict = get_required_field('drops', drops_dict, 'dropped_parts')
00298     dropped_part_infos = {}
00299     for drop_name, dropped_part_dict in drops_dict.items():
00300         part_type = get_required_field(drop_name, dropped_part_dict, 'part_type_to_drop')
00301         destination = get_required_field(drop_name, dropped_part_dict, 'destination')
00302         destination_xyz = get_required_field('destination', destination, 'xyz')
00303         dropped_part_infos[drop_name] = {'part_type': part_type, 'destination': destination_xyz}
00304     drops_info['dropped_parts'] = dropped_part_infos
00305     print(drops_info)
00306     return drops_info
00307 
00308 
00309 def create_order_info(name, order_dict):
00310     announcement_time = get_required_field(name, order_dict, 'announcement_time')
00311     parts_dict = get_required_field(name, order_dict, 'parts')
00312     parts = []
00313     for part_name, part_dict in parts_dict.items():
00314         parts.append(create_model_info(part_name, part_dict))
00315     return {'announcement_time': announcement_time, 'parts': parts}
00316 
00317 
00318 def create_order_infos(orders_dict):
00319     order_infos = {}
00320     for order_name, order_dict in orders_dict.items():
00321         order_infos[order_name] = create_order_info(order_name, order_dict)
00322     return order_infos
00323 
00324 
00325 def create_bin_infos():
00326     bin_infos = {}
00327     for bin_name, xyz in default_bin_origins.items():
00328         bin_infos[bin_name] = PoseInfo(xyz, [0, 0, 1.5708])
00329     return bin_infos
00330 
00331 
00332 def create_options_info(options_dict):
00333     options = configurable_options
00334     for option, val in options_dict.items():
00335         options[option] = val
00336     return options
00337 
00338 
00339 def prepare_template_data(config_dict):
00340     template_data = {
00341         'arm': None,
00342         'sensors': {},
00343         'models_to_insert': {},
00344         'models_to_spawn': {},
00345         'belt_parts': {},
00346         'drops': {},
00347         'orders': {},
00348         'options': {},
00349     }
00350     # Process the options first as they may affect the processing of the rest
00351     options_dict = {}
00352     if 'options' in config_dict:
00353         options_dict = config_dict['options']
00354     template_data['options'].update(create_options_info(options_dict))
00355 
00356     for key, value in config_dict.items():
00357         if key == 'arm':
00358             template_data['arm'] = create_arm_info(value)
00359         elif key == 'sensors':
00360             template_data['sensors'].update(
00361                 create_sensor_infos(value))
00362         elif key == 'models_over_bins':
00363             template_data['models_to_insert'].update(
00364                 create_models_over_bins_infos(value))
00365         elif key == 'belt_parts':
00366             template_data['belt_parts'].update(create_belt_part_infos(value))
00367         elif key == 'drops':
00368             template_data['drops'].update(create_drops_info(value))
00369         elif key == 'orders':
00370             template_data['orders'].update(create_order_infos(value))
00371         elif key == 'options':
00372             pass
00373         elif key == 'models_to_spawn':
00374             template_data['models_to_spawn'].update(
00375                 create_models_to_spawn_infos(value))
00376         else:
00377             print("Error: unknown top level entry '{0}'".format(key), file=sys.stderr)
00378             sys.exit(1)
00379     template_data['bins'] = create_bin_infos()
00380     return template_data
00381 
00382 
00383 def generate_files(template_data):
00384     files = {}
00385     for template_file in template_files:
00386         with open(template_file, 'r') as f:
00387             data = f.read()
00388         files[template_file] = em.expand(data, template_data)
00389     return files
00390 
00391 
00392 def main(sysargv=None):
00393     parser = argparse.ArgumentParser(
00394         description='Prepares and then executes a gazebo simulation based on configurations.')
00395     prepare_arguments(parser)
00396     args = parser.parse_args(sysargv)
00397     config_data = args.config or ''
00398     if args.file is not None:
00399         for file in args.file:
00400             with open(file, 'r') as f:
00401                 comp_config_data = f.read()
00402                 config_data += comp_config_data
00403     dict_config = yaml.load(config_data) or {}
00404     expanded_dict_config = expand_yaml_substitutions(dict_config)
00405     print(yaml.dump({'Using configuration': expanded_dict_config}))
00406     template_data = prepare_template_data(expanded_dict_config)
00407     files = generate_files(template_data)
00408     if not args.dry_run and not os.path.isdir(args.output):
00409         if os.path.exists(args.output) and not os.path.isdir(args.output):
00410             print("Error, given output directory exists but is not a directory.", file=sys.stderr)
00411             sys.exit(1)
00412         print('creating directory: ' + args.output)
00413         os.makedirs(args.output)
00414     for name, content in files.items():
00415         if name.endswith('.template'):
00416             name = name[:-len('.template')]
00417         name = os.path.basename(name)
00418         if args.dry_run:
00419             print('# file: ' + name)
00420             print(content)
00421         else:
00422             file_path = os.path.join(args.output, name)
00423             print('writing file ' + file_path)
00424             with open(file_path, 'w+') as f:
00425                 f.write(content)
00426     cmd = [
00427         'roslaunch',
00428         os.path.join(args.output, 'gear.launch'),
00429         'world_path:=' + os.path.join(args.output, 'gear.world'),
00430         'gear_urdf_xacro:=' + os.path.join(args.output, 'gear.urdf.xacro'),
00431     ]
00432     print("Running command: " + ' '.join(cmd))
00433     if not args.dry_run:
00434         try:
00435             p = subprocess.Popen(cmd)
00436             p.wait()
00437         except KeyboardInterrupt:
00438             pass
00439         finally:
00440             p.wait()
00441         return p.returncode
00442 
00443 if __name__ == '__main__':
00444     sys.exit(main())


osrf_gear
Author(s):
autogenerated on Mon Sep 5 2016 03:41:33