diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index c68d68c3a3..599afe3d50 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -297,6 +297,19 @@ def get_num_not_departed(self): """ raise NotImplementedError + def get_fuel_consumption(selfself, veh_id, error=-1001): + """Return the mpg / s of the specified vehicle. + Parameters + ---------- + veh_id : str or list of str + vehicle id, or list of vehicle ids + error : any, optional + value that is returned if the vehicle is not found + Returns + ------- + float + """ + def get_speed(self, veh_id, error=-1001): """Return the speed of the specified vehicle. diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index d165dbdea7..134bac49f7 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -335,7 +335,8 @@ def _add_departed(self, veh_id, veh_type): tc.VAR_EDGES, tc.VAR_POSITION, tc.VAR_ANGLE, - tc.VAR_SPEED_WITHOUT_TRACI + tc.VAR_SPEED_WITHOUT_TRACI, + tc.VAR_FUELCONSUMPTION ]) self.kernel_api.vehicle.subscribeLeader(veh_id, 2000) @@ -370,6 +371,8 @@ def _add_departed(self, veh_id, veh_type): self.kernel_api.vehicle.getLaneIndex(veh_id) self.__sumo_obs[veh_id][tc.VAR_SPEED] = \ self.kernel_api.vehicle.getSpeed(veh_id) + self.__sumo_obs[veh_id][tc.VAR_FUELCONSUMPTION] = \ + self.kernel_api.vehicle.getFuelConsumption(veh_id) # make sure that the order of rl_ids is kept sorted self.__rl_ids.sort() @@ -533,6 +536,13 @@ def get_num_not_departed(self): """See parent class.""" return self.num_not_departed + def get_fuel_consumption(self, veh_id, error=-1001): + """Return fuel consumption in gallons/s.""" + ml_to_gallons = 0.000264172 + if isinstance(veh_id, (list, np.ndarray)): + return [self.get_fuel_consumption(vehID, error) for vehID in veh_id] + return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_FUELCONSUMPTION, error) * ml_to_gallons + def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): diff --git a/flow/core/rewards.py b/flow/core/rewards.py index 6de472af2b..abe17a3dae 100755 --- a/flow/core/rewards.py +++ b/flow/core/rewards.py @@ -330,3 +330,94 @@ def energy_consumption(env, gain=.001): power += M * speed * accel + M * g * Cr * speed + 0.5 * rho * A * Ca * speed ** 3 return -gain * power + + +def veh_energy_consumption(env, veh_id, gain=.001): + """Calculate power consumption of a vehicle. + Assumes vehicle is an average sized vehicle. + The power calculated here is the lower bound of the actual power consumed + by a vehicle. + """ + power = 0 + + M = 1200 # mass of average sized vehicle (kg) + g = 9.81 # gravitational acceleration (m/s^2) + Cr = 0.005 # rolling resistance coefficient + Ca = 0.3 # aerodynamic drag coefficient + rho = 1.225 # air density (kg/m^3) + A = 2.6 # vehicle cross sectional area (m^2) + speed = env.k.vehicle.get_speed(veh_id) + prev_speed = env.k.vehicle.get_previous_speed(veh_id) + + accel = abs(speed - prev_speed) / env.sim_step + + power += M * speed * accel + M * g * Cr * speed + 0.5 * rho * A * Ca * speed ** 3 + + return -gain * power + + +def miles_per_megajoule(env, veh_id=None, gain=.001): + """Calculate miles per mega-joule of either a particular vehicle or the total average of all the vehilces. + Assumes vehicle is an average sized vehicle. + The power calculated here is the lower bound of the actual power consumed + by a vehicle. + """ + mpj = 0 + counter = 0 + if not isinstance(veh_id, list): + speed = env.k.vehicle.get_speed(veh_id) + # convert to be positive since the function called is a penalty + power = -veh_energy_consumption(env, veh_id, gain=1.0) + if power > 0 and speed >= 0.0: + # meters / joule is (v * \delta t) / (power * \delta t) + mpj = speed / power + else: + for veh_id in env.k.vehicle.get_ids(): + speed = env.k.vehicle.get_speed(veh_id) + # convert to be positive since the function called is a penalty + power = -veh_energy_consumption(env, veh_id, gain=1.0) + if power > 0 and speed >= 0.0: + counter += 1 + # meters / joule is (v * \delta t) / (power * \delta t) + mpj += speed / power + if counter > 0: + mpj /= counter + + # convert from meters per joule to miles per joule + mpj /= 1609.0 + # convert from miles per joule to miles per megajoule + mpj *= 10**6 + + return mpj * gain + + +def miles_per_gallon(env, veh_id=None, gain=.001): + """Calculate mpg of either a particular vehicle or the total average of all the vehilces. + Assumes vehicle is an average sized vehicle. + The power calculated here is the lower bound of the actual power consumed + by a vehicle. + """ + mpg = 0 + counter = 0 + if not isinstance(veh_id, list): + speed = env.k.vehicle.get_speed(veh_id) + gallons_per_s = env.k.vehicle.get_fuel_consumption(veh_id) + if gallons_per_s > 0 and speed >= 0.0: + # meters / gallon is (v * \delta t) / (gallons_s * \delta t) + mpg = speed / gallons_per_s + else: + for veh_id in env.k.vehicle.get_ids(): + speed = env.k.vehicle.get_speed(veh_id) + gallons_per_s = env.k.vehicle.get_fuel_consumption(veh_id) + if gallons_per_s > 0 and speed >= 0.0: + counter += 1 + # meters / gallon is (v * \delta t) / (gallons_per_s * \delta t) + mpg += speed / gallons_per_s + if counter > 0: + mpg /= counter + + # convert from meters per gallon to miles per gallon + mpg /= 1609.0 + + + return mpg * gain