def load_chrono_sensor_from_json(system: 'WAChronoSystem', filename: str, **kwargs) -> 'WAChronoSensor': """Load a chrono sensor from json If the passed json file isn't a chrono type, it will call the correct method. Args: system (WAChronoSystem): The system for the simulation filename (str): The json specification file that describes the sensor Returns: WAChronoSensor: The created sensor """ if missing_chrono_sensor: sens_import_error('load_chrono_sensor_from_json') # Check if the file can be found in the chrono portion of the data folder try: j = _load_json(filename) except FileNotFoundError: # File is not chrono specific, try that now j = _load_json(get_wa_data_file(filename)) return load_sensor_from_json(system, filename, **kwargs) return WAChronoSensor(system, filename, **kwargs)
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 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 __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 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 __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_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 test_check_field_allowed_values(self): """Tests the check_field_allowed_values method""" j = utils._load_json(utils.get_wa_data_file('test/test.json')) try: utils._check_field_allowed_values( j, 'Name', ['Test GPS Sensor Model', 'Test', 'Type']) # noqa except: self.fail("Raise exception unexpectedly!") with self.assertRaises(ValueError): utils._check_field_allowed_values(j, 'Name', ['Test', 'Type']) # noqa
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 test_load_json(self): """Tests the load_json method""" try: j = utils._load_json(utils.get_wa_data_file('test/test.json')) except: self.fail("Raise exception unexpectedly!") self.assertTrue('Name' in j) self.assertTrue('Type' in j) self.assertTrue('Template' in j) self.assertTrue('Properties' in j) self.assertTrue(j['Name'] == 'Test GPS Sensor Model') self.assertTrue('Update Rate' in j['Properties']) self.assertTrue(isinstance(j['Properties'], dict))
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 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_properties_from_json(filename: str, prop: str) -> dict: """Load a specified property from a json specification file Will load a json file and extract the passed property field for use by the underyling vehicle object Args: filename (str): the filename location within the set WA data folder for the json file property (str): the property to get. Ex: "Vehicle Properties" Raises: ValueError: The property field isn't found Returns: dict: the property field extracted from the json file """ j = _load_json(filename) if prop not in j: raise ValueError(f"{prop} not found in json.") return j[prop]
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 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 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 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