def load_sensor_from_json(system: 'WASystem', filename: str, **kwargs) -> 'WASensor': """Load a sensor from json Args: system (WASystem): The system for the simulation filename (str): The json specification file that describes the sensor kwargs: Keyworded arguments. Must contain a 'vehicle' or 'body', not both. Returns: WASensor: The created sensor """ j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', allowed_values=['IMU', 'GPS', 'Wheel Encoder']) # Check the template and create a sensor based off what it is if j['Template'] == 'IMU': sensor = WAIMUSensor(system, filename, **kwargs) elif j['Template'] == 'GPS': sensor = WAGPSSensor(system, filename, **kwargs) elif j['Template'] == 'Wheel Encoder': sensor = WAWheelEncoderSensor(system, filename, **kwargs) else: raise TypeError(f"{j['Template']} is not an implemented sensor type") return sensor
def create_tire_from_json(tire_filename: str) -> veh.ChTire: """Creates a ChTire from a tire file .. info: Currently, only TMeasyTires and RigidTires are supported Args: tire_filename (str): the tire json specification file Returns: ChTire: the created tire Raises: TypeError: If the tire type is not recognized """ j = _load_json(tire_filename) # Valide json file _check_field(j, "Type", value="Tire") _check_field(j, "Template", allowed_values=["TMeasyTire", "RigidTire", "Pac02Tire"]) tire_type = j["Template"] if tire_type == "TMeasyTire": return veh.TMeasyTire(tire_filename) elif tire_type == "RigidTire": return veh.RigidTire(tire_filename) elif tire_type == "Pac02Tire": return veh.Pac02Tire(tire_filename) else: raise TypeError(f"'{tire_type} not a recognized tire type")
def ChFrame_from_json(j: dict): """Creates a ChFrame from a json object. Args: j (dict): The json object that will be converted to a ChFrame Returns: ChFrameD: The frame created from the json object """ # Validate the json file _check_field(j, 'Position', field_type=list) _check_field(j, 'Rotation', field_type=list) # Do the conversion pos = j['Position'] rot = j['Rotation'] return chrono.ChFrameD( chrono.ChVectorD(pos[0], pos[1], pos[2]), chrono.ChQuaternionD(rot[0], rot[1], rot[2], rot[3]))
def __init__(self, p: dict): # Validate properties _check_field(p, 'Noise Type', value='Normal') _check_field(p, 'Mean', field_type=list) _check_field(p, 'Standard Deviation', field_type=list) self._load_properties(p)
def create_path_from_json(filename: str) -> 'WAPath': """Creates a WAPath object from json json file options: * Waypoints Input File (str, required): A csv file describing the path waypoints. Loaded using :meth:`~load_waypoints_from_csv`. * Additional keyworded arguments necessary for the path template Args: filename (str): The json specification that describes the path """ j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Path') _check_field(j, 'Template', allowed_values=['WASplinePath']) _check_field(j, 'Waypoints Input File', field_type=str) # Grab the waypoints waypoints_file = get_wa_data_file(j['Waypoints Input File']) waypoints = load_waypoints_from_csv(waypoints_file, delimiter=",") excluded_keys = ['Type', 'Template', 'Waypoints Input File'] kwargs = {x: j[x] for x in j if x not in excluded_keys} # Create the path path = eval(j['Template'])(waypoints, **kwargs) return path
def __init__(self, system: 'WAChronoSystem', filename: str, vehicle: 'WAChronoVehicle' = None, body: 'WABody' = None): if missing_chrono_sensor: sens_import_error('WAChronoSensor.__init__') super().__init__(vehicle, body) j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', allowed_values=['Camera', 'Lidar', 'IMU', 'GPS']) # noqa _check_field(j, 'Offset Pose', field_type=dict) offset_pose = ChFrame_from_json(j['Offset Pose']) if vehicle is not None: body = vehicle._vehicle.GetChassisBody() else: asset = body body = chrono.ChBody() body.SetPos(WAVector_to_ChVector(asset.position)) body.SetBodyFixed(True) system._system.AddBody(body) # Create the chrono sensor through the Sensor chrono class self._sensor = sens.Sensor.CreateFromJSON(filename, body, offset_pose) # noqa
def load_sensor_suite_from_json(manager: 'WASensorManager', filename: str): """Load a sensor suite from json Each loaded sensor will be added to the manager Args: manager (WASensorManager): The sensor manager to store all created objects in filename (str): The json specification file that describes the sensor suite """ j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', value='Sensor Suite') _check_field(j, 'Sensors', field_type=list) # Load the sensors for sensor in j['Sensors']: new_sensor = load_sensor_from_json(sensor, manager._system) manager.add_sensor(new_sensor)
def load_chrono_terrain_from_json(system: 'WAChronoSystem', filename: str): """Load a ChTerrain from a json specification file Args: filename (str): the relative path to a terrain json file system (WAChronoSystem): the chrono system used to handle the terrain Returns: ChTerrain: The loaded terrain """ j = _load_json(filename) # Validate the json file _check_field(j, 'Terrain', field_type=dict) t = j['Terrain'] _check_field(t, 'Input File', field_type=str) _check_field(t, 'Texture', field_type=str, optional=True) terrain = veh.RigidTerrain(system._system, chrono.GetChronoDataFile(t['Input File'])) # noqa # Add texture to the terrain, if desired if 'Texture' in t: texture_filename = chrono.GetChronoDataFile(t['Texture']) vis_mat = chrono.ChVisualMaterial() vis_mat.SetKdTexture(texture_filename) vis_mat.SetSpecularColor(chrono.ChVectorF(0.0, 0.0, 0.0)) vis_mat.SetFresnelMin(0) vis_mat.SetFresnelMax(0.1) patch_asset = terrain.GetPatches()[0].GetGroundBody().GetAssets()[0] patch_visual_asset = chrono.CastToChVisualization(patch_asset) patch_visual_asset.material_list.append(vis_mat) return terrain
def __init__(self, system: 'WASystem', filename: str, vehicle: 'WAVehicle' = None, body: 'WABody' = None): super().__init__(vehicle, body) if body is not None: raise NotImplementedError( "Setting 'body' is currently not supported. Please pass a vehicle instead" ) self._vehicle = vehicle j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', value='Wheel Encoder') _check_field(j, 'Properties', field_type=dict) p = j['Properties'] _check_field(p, 'Update Rate', field_type=int) _check_field(p, 'Axle', field_type=str, allowed_values=['Front', 'Rear', 'Steering']) _check_field(p, 'Noise Model', field_type=dict, optional=True) if 'Noise Model' in p: _check_field(p['Noise Model'], 'Noise Type', allowed_values=["Normal", "Normal Drift"]) self._load_properties(p)
def __init__(self, system: 'WASystem', filename: str, vehicle: 'WAVehicle' = None, body: 'WABody' = None): super().__init__(vehicle, body) if body is not None: raise NotImplementedError( "Setting 'body' is currently not supported. Please pass a vehicle instead" ) self._vehicle = vehicle self._pos_dtdt = None self._rot = None self._rot_dt = None j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', value='IMU') _check_field(j, 'Properties', field_type=dict) p = j['Properties'] _check_field(p, 'Update Rate', field_type=int) _check_field(p, 'Noise Model', field_type=dict, optional=True) if 'Noise Model' in p: _check_field(p['Noise Model'], 'Noise Type', allowed_values=["Normal", "Normal Drift"]) self._load_properties(p) self._acc = WAVector() self._omega = WAVector() self._orientation = WAVector()
def __init__(self, p: dict): # Validate properties _check_field(p, 'Noise Type', value='Normal Drift') _check_field(p, 'Update Rate', field_type=float) _check_field(p, 'Mean', field_type=list) _check_field(p, 'Standard Deviation', field_type=list) _check_field(p, 'Bias Drift', field_type=float) _check_field(p, 'Tau Drift', field_type=float) self._load_properties(p)
def _load_vis(path, path_name): if path_name in v: p = v[path_name] _check_field(p, 'Color', field_type=list, optional=True) _check_field(p, 'Object', field_type=dict, optional=True) if 'Color' in p: path.get_vis_properties()['color'] = WAVector(p['Color']) if 'Object' in p and environment is not None: o = p['Object'] _check_field(o, 'Size', field_type=list) _check_field(o, 'Color', field_type=list, optional=True) _check_field(o, 'Mode', field_type=str, optional=True) kwargs = {} kwargs['size'] = WAVector(o['Size']) if 'Color' in o: kwargs['color'] = WAVector(o['Color']) if 'Color #1' in o or 'Color #2' in o: raise ValueError( "'Color' cannot be used with 'Color #1' or 'Color #2'" ) elif 'Color #1' in o and 'Color #2' in o: kwargs['color1'] = WAVector(o['Color #1']) kwargs['color2'] = WAVector(o['Color #2']) elif 'Color #1' in o or 'Color #2' in o: raise ValueError( "'Color #1' and 'Color #2' must be used together.") s = path.calc_length_cummulative()[-1] size = WAVector(o['Size']) if 'Mode' in o: m = o['Mode'] if m == 'Continuous': n = s / size.y elif m == 'Dashed': n = s / 3 # Spaced as dashed center lines are (3[m] apart) elif m == 'Spread': n = s / 6 else: raise ValueError( f"'{m}' is not a supported road marker type") else: # Defaults to continuous n = s / size.y points = path.get_points() d_points = path.get_points(der=1) s = path.calc_length_cummulative()[-1] size = WAVector(o['Size']) l = len(points) for e, i in enumerate( range(0, l, 1 if l < n else int(l / n))): p = points[i] dp = d_points[i] kwargs['position'] = WAVector(p) kwargs['yaw'] = -np.arctan2(dp[1], dp[0]) if 'color1' in kwargs: if e % 2 == 0: kwargs['color'] = kwargs['color1'] else: kwargs['color'] = kwargs['color2'] environment.create_body(**kwargs)
def load_chrono_sensor_scene_from_json(manager: "WAChronoSensorManager", filename: str): """Load a chrono sensor scene from a json specification file. A scene may consist of "World" attributes (i.e. lights) or sensors Args: manager (WASensorManager): The sensor manager to edit the scene of filename (str): The json specification file describing the scene """ if missing_chrono_sensor: sens_import_error('load_chrono_sensor_scene_from_json') j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Sensor') _check_field(j, 'Template', value='Chrono Sensor Scene') _check_field(j, 'World', field_type=dict, optional=True) _check_field(j, 'Sensors', field_type=list, optional=True) _check_field(j, 'Objects', field_type=list, optional=True) if 'World' in j: w = j['World'] _check_field(w, 'Point Lights', field_type=list) # Create the point lights # NOTE: Might be optional in the future for p in w['Point Lights']: _check_field(p, 'Position', field_type=list) _check_field(p, 'Color', field_type=list) _check_field(p, 'Maximum Range', field_type=float) pos = ChVector_from_list(p['Position'], chrono.ChVectorF) color = ChVector_from_list(p['Color'], chrono.ChVectorF) max_range = p['Maximum Range'] manager._manager.scene.AddPointLight(pos, color, max_range) if 'Sensors' in j: s = j['Sensors'] for sensor in s: new_sensor = load_chrono_sensor_from_json(manager._system, sensor) manager.add_sensor(new_sensor)
def test_check_field(self): """Tests the check_field method""" j = utils._load_json(utils.get_wa_data_file('test/test.json')) try: utils._check_field(j, 'Name') utils._check_field(j, 'Test', optional=True) utils._check_field(j, 'Template', value='GPS') utils._check_field(j, 'Properties', field_type=dict) utils._check_field(j['Properties']['Noise Model'], 'Noise Type', field_type=str, allowed_values=['Normal', 'Test']) except: self.fail("Raise exception unexpectedly!") with self.assertRaises(KeyError): utils._check_field(j, "Test") with self.assertRaises(TypeError): utils._check_field(j, "Name", field_type=bool) with self.assertRaises(ValueError): utils._check_field(j, "Name", value='Noise Model') with self.assertRaises(ValueError): utils._check_field(j, "Name", value='Noise Model', optional=True)
def read_vehicle_model_file(filename: str) -> tuple: """Read a json specification file to get additional file names to be loaded into ChVehicle classes Will find the vehicle, powertrain and tire input files. The input files are other json files that are readable by the Chrono simulator to be used to create bodies attached to the vehicle. Args: filename (str): the json specification file with Vehicle, Powertrain and Tire input models Returns: tuple: returns each json specification file for the vehicle, powertrain and tire """ j = _load_json(filename) # Validate json file _check_field(j, "Vehicle", field_type=dict) _check_field(j, "Powertrain", field_type=dict) _check_field(j, "Tire", field_type=dict) _check_field(j["Vehicle"], "Input File", field_type=str) _check_field(j["Powertrain"], "Input File", field_type=str) _check_field(j["Tire"], "Input File", field_type=str) # Extract the actual files vehicle_filename = veh.GetDataFile(j["Vehicle"]["Input File"]) powertrain_filename = veh.GetDataFile(j["Powertrain"]["Input File"]) tire_filename = veh.GetDataFile(j["Tire"]["Input File"]) return vehicle_filename, powertrain_filename, tire_filename
def load_environment_from_json(environment: 'WAEnvironment', filename: str): j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Environment') _check_field(j, 'Template') _check_field(j, 'World', field_type=dict, optional=True) _check_field(j, 'Objects', field_type=list, optional=True) if 'World' in j: w = j['World'] if 'Objects' in j: objects = j['Objects'] for o in objects: _check_field(o, 'Size', field_type=list) _check_field(o, 'Position', field_type=list, optional=True) _check_field(o, 'Color', field_type=list, optional=True) kwargs = {} kwargs['size'] = WAVector(o['Size']) kwargs['position'] = WAVector(o['Position']) if 'Color' in o: kwargs['color'] = WAVector(o['Color']) if 'Texture' in o: kwargs['texture'] = o['Texture'] if 'Name' in o: kwargs['name'] = o['Name'] environment.add_asset(WABody(**kwargs)) if 'Track' in j: t = j['Track'] # Validate json _check_field(t, 'Track Input File', field_type=str) track_file = t['Track Input File'] track = create_track_from_json(get_wa_data_file(track_file), environment) environment.add_asset(track.center)
def create_track_from_json(filename: str, environment: 'WAEnvironment' = None) -> 'WATrack': """Creates a WATrack object from a json specification file json file options: * Center Input File (``str``, required): A json file describing the centerline. Loaded using :meth:`~create_path_from_json` * Width (``float``, required): The constant width between the left and right boundaries of the track. * Origin(``list``, required): The GPS origin of the first centerline point * Visualization (``dict``, optional): Additional visualization properties. * Center/Right/Left (``dict``, optional): The each paths visualization properties * Color (``list``, optional): Visualization color. * Object (``dict``, optional): An object that is placed along the path. Only parsed if ``environment`` is set. * Size (``list``, optional): Size of the objects. * Color (``list``, optional): Color of the objects. * Color #1 (``list``, optional): Color of an alternating set of objects. Must come with Color #2 and without Color. * Color #2 (``list``, optional): Color of an alternating set of objects. Must come with Color #1 and without Color. * Mode (``str``, optional): The mode for the object placement along the path. Options include 'Solid', 'Dashed' (3[m] separation) and 'Spread' (6[m] separation). .. todo:: Add a variable width loader Args: filename (str): The json specification that describes the track environment (WAEnvironment, optional): Adds objects to the environment, if present. Defaults to None (doesn't parse objects). Returns: WATrack: The created track """ j = _load_json(filename) # Validate the json file _check_field(j, 'Type', value='Track') _check_field(j, 'Template', allowed_values='Constant Width Track') _check_field(j, 'Center Input File', field_type=str) _check_field(j, 'Width', field_type=float) _check_field(j, 'Origin', field_type=list) _check_field(j, 'Visualization', field_type=dict, optional=True) # Create the centerline path center_file = get_wa_data_file(j['Center Input File']) center = create_path_from_json(center_file) width = j['Width'] origin = WAVector(j['Origin']) # Create the track track = create_constant_width_track(center, width) track.origin = origin # Load the visualization, if present if 'Visualization' in j: v = j['Visualization'] _check_field(v, 'Center', field_type=dict, optional=True) _check_field(v, 'Left', field_type=dict, optional=True) _check_field(v, 'Right', field_type=dict, optional=True) def _load_vis(path, path_name): if path_name in v: p = v[path_name] _check_field(p, 'Color', field_type=list, optional=True) _check_field(p, 'Object', field_type=dict, optional=True) if 'Color' in p: path.get_vis_properties()['color'] = WAVector(p['Color']) if 'Object' in p and environment is not None: o = p['Object'] _check_field(o, 'Size', field_type=list) _check_field(o, 'Color', field_type=list, optional=True) _check_field(o, 'Mode', field_type=str, optional=True) kwargs = {} kwargs['size'] = WAVector(o['Size']) if 'Color' in o: kwargs['color'] = WAVector(o['Color']) if 'Color #1' in o or 'Color #2' in o: raise ValueError( "'Color' cannot be used with 'Color #1' or 'Color #2'" ) elif 'Color #1' in o and 'Color #2' in o: kwargs['color1'] = WAVector(o['Color #1']) kwargs['color2'] = WAVector(o['Color #2']) elif 'Color #1' in o or 'Color #2' in o: raise ValueError( "'Color #1' and 'Color #2' must be used together.") s = path.calc_length_cummulative()[-1] size = WAVector(o['Size']) if 'Mode' in o: m = o['Mode'] if m == 'Continuous': n = s / size.y elif m == 'Dashed': n = s / 3 # Spaced as dashed center lines are (3[m] apart) elif m == 'Spread': n = s / 6 else: raise ValueError( f"'{m}' is not a supported road marker type") else: # Defaults to continuous n = s / size.y points = path.get_points() d_points = path.get_points(der=1) s = path.calc_length_cummulative()[-1] size = WAVector(o['Size']) l = len(points) for e, i in enumerate( range(0, l, 1 if l < n else int(l / n))): p = points[i] dp = d_points[i] kwargs['position'] = WAVector(p) kwargs['yaw'] = -np.arctan2(dp[1], dp[0]) if 'color1' in kwargs: if e % 2 == 0: kwargs['color'] = kwargs['color1'] else: kwargs['color'] = kwargs['color2'] environment.create_body(**kwargs) _load_vis(track.center, 'Center') _load_vis(track.right, 'Right') _load_vis(track.left, 'Left') return track