00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00219
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
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
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
00272
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
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())