package org.opentrafficsim.demo;
import java.io.BufferedWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.djunits.unit.FrequencyUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.unit.TimeUnit;
import org.djunits.value.storage.StorageType;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.Direction;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.Time;
import org.djunits.value.vdouble.vector.FrequencyVector;
import org.djunits.value.vdouble.vector.TimeVector;
import org.djunits.value.vdouble.vector.base.DoubleVector;
import org.djutils.cli.CliUtil;
import org.djutils.event.EventInterface;
import org.djutils.exceptions.Throw;
import org.djutils.exceptions.Try;
import org.opentrafficsim.base.CompressedFileWriter;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.base.parameters.ParameterSet;
import org.opentrafficsim.base.parameters.ParameterTypeDuration;
import org.opentrafficsim.base.parameters.ParameterTypes;
import org.opentrafficsim.base.parameters.Parameters;
import org.opentrafficsim.base.parameters.constraint.NumericConstraint;
import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
import org.opentrafficsim.core.animation.gtu.colorer.IDGTUColorer;
import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
import org.opentrafficsim.core.compatibility.Compatible;
import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
import org.opentrafficsim.core.geometry.OTSPoint3D;
import org.opentrafficsim.core.gtu.GTU;
import org.opentrafficsim.core.gtu.GTUCharacteristics;
import org.opentrafficsim.core.gtu.GTUDirectionality;
import org.opentrafficsim.core.gtu.GTUException;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
import org.opentrafficsim.core.gtu.perception.EgoPerception;
import org.opentrafficsim.core.gtu.perception.Perception;
import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
import org.opentrafficsim.core.network.LateralDirectionality;
import org.opentrafficsim.core.network.LinkType;
import org.opentrafficsim.core.network.Network;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.core.network.Node;
import org.opentrafficsim.core.network.route.Route;
import org.opentrafficsim.core.parameters.ParameterFactoryByType;
import org.opentrafficsim.road.gtu.colorer.GTUTypeColorer;
import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
import org.opentrafficsim.road.gtu.generator.od.GTUCharacteristicsGeneratorOD;
import org.opentrafficsim.road.gtu.generator.od.ODApplier;
import org.opentrafficsim.road.gtu.generator.od.ODOptions;
import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
import org.opentrafficsim.road.gtu.lane.VehicleModel;
import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlus;
import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AbstractIncentivesTacticalPlanner;
import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationIncentive;
import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
import org.opentrafficsim.road.gtu.lane.tactical.util.TrafficLightUtil;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Desire;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Incentive;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsUtil;
import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
import org.opentrafficsim.road.gtu.strategical.od.Categorization;
import org.opentrafficsim.road.gtu.strategical.od.Category;
import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
import org.opentrafficsim.road.network.OTSRoadNetwork;
import org.opentrafficsim.road.network.control.rampmetering.CycleTimeLightController;
import org.opentrafficsim.road.network.control.rampmetering.RampMetering;
import org.opentrafficsim.road.network.control.rampmetering.RampMeteringLightController;
import org.opentrafficsim.road.network.control.rampmetering.RampMeteringSwitch;
import org.opentrafficsim.road.network.control.rampmetering.RwsSwitch;
import org.opentrafficsim.road.network.factory.LaneFactory;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.LaneType;
import org.opentrafficsim.road.network.lane.OTSRoadNode;
import org.opentrafficsim.road.network.lane.Stripe.Permeable;
import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
import org.opentrafficsim.road.network.lane.object.sensor.Detector;
import org.opentrafficsim.road.network.lane.object.sensor.Detector.CompressionMethod;
import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
import org.opentrafficsim.swing.script.AbstractSimulationScript;
import nl.tudelft.simulation.jstats.distributions.DistNormal;
import nl.tudelft.simulation.jstats.streams.StreamInterface;
import nl.tudelft.simulation.language.d3.DirectedPoint;
import picocli.CommandLine.Option;
/**
*
* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jun. 2019
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
*/
public class RampMeteringDemo extends AbstractSimulationScript
{
/** Controlled car GTU type id. */
private static final String CONTROLLED_CAR_ID = "controlledCar";
/** Parameter factory. */
private ParameterFactoryByType parameterFactory = new ParameterFactoryByType();
/** Ramp metering. */
@Option(names = {"-r", "--rampMetering"}, description = "Ramp metering on or off", defaultValue = "true")
private boolean rampMetering;
/** Whether to generate output. */
@Option(names = "--output", description = "Generate output.", negatable = true, defaultValue = "false")
private boolean output;
/** Accepted gap. */
@Option(names = "--acceptedGap", description = "Accepted gap.", defaultValue = "0.5s")
private Duration acceptedGap;
/** Main demand. */
private FrequencyVector mainDemand;
/** Main demand string. */
@Option(names = "--mainDemand", description = "Main demand in veh/h.", defaultValue = "2000,3000,3900,3900,3000")
private String mainDemandString;
/** Ramp demand. */
private FrequencyVector rampDemand;
/** Ramp demand string. */
@Option(names = "--rampDemand", description = "Ramp demand in veh/h.", defaultValue = "500,500,500,500,500")
private String rampDemandString;
/** Demand time. */
private TimeVector demandTime;
/** Demand time string. */
@Option(names = "--demandTime", description = "Demand time in min.", defaultValue = "0,10,40,50,70")
private String demandTimeString;
/** Scenario. */
@Option(names = "--scenario", description = "Scenario name.", defaultValue = "test")
private String scenario;
/** GTUs in simulation. */
private Map gtusInSimulation = new LinkedHashMap<>();
/** Total travel time, accumulated. */
private double totalTravelTime = 0.0;
/** Total travel time delay, accumulated. */
private double totalTravelTimeDelay = 0.0;
/**
* Constructor.
*/
protected RampMeteringDemo()
{
super("Ramp metering 1", "Ramp metering 2");
}
/**
* @param args String[] command line arguments
* @throws Exception any exception
*/
public static void main(final String[] args) throws Exception
{
RampMeteringDemo demo = new RampMeteringDemo();
CliUtil.changeOptionDefault(demo, "simulationTime", "4200s");
CliUtil.execute(demo, args);
demo.mainDemand =
DoubleVector.instantiate(arrayFromString(demo.mainDemandString), FrequencyUnit.PER_HOUR, StorageType.DENSE);
demo.rampDemand =
DoubleVector.instantiate(arrayFromString(demo.rampDemandString), FrequencyUnit.PER_HOUR, StorageType.DENSE);
demo.demandTime =
DoubleVector.instantiate(arrayFromString(demo.demandTimeString), TimeUnit.BASE_MINUTE, StorageType.DENSE);
demo.start();
}
/**
* Returns an array from a String.
* @param str String; string
* @return double[] array
*/
private static double[] arrayFromString(final String str)
{
int n = 0;
for (String part : str.split(","))
{
n++;
}
double[] out = new double[n];
int i = 0;
for (String part : str.split(","))
{
out[i] = Double.valueOf(part);
i++;
}
return out;
}
/** {@inheritDoc} */
@Override
protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
{
OTSRoadNetwork network = new OTSRoadNetwork("RampMetering", true, sim);
if (this.output)
{
network.addListener(this, Network.GTU_ADD_EVENT);
network.addListener(this, Network.GTU_REMOVE_EVENT);
}
GTUType car = network.getGtuType(GTUType.DEFAULTS.CAR);
GTUType controlledCar = new GTUType(CONTROLLED_CAR_ID, car);
GTUColorer[] colorers =
new GTUColorer[] {new IDGTUColorer(), new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)),
new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2)),
new GTUTypeColorer().add(car).add(controlledCar)};
SwitchableGTUColorer colorer = new SwitchableGTUColorer(0, colorers);
setGtuColorer(colorer);
// parameters
StreamInterface stream = sim.getReplication().getStream("generation");
this.parameterFactory.addParameter(ParameterTypes.FSPEED, new DistNormal(stream, 123.7 / 120.0, 12.0 / 1200));
OTSRoadNode nodeA = new OTSRoadNode(network, "A", new OTSPoint3D(0, 0), Direction.ZERO);
OTSRoadNode nodeB = new OTSRoadNode(network, "B", new OTSPoint3D(3000, 0), Direction.ZERO);
OTSRoadNode nodeC = new OTSRoadNode(network, "C", new OTSPoint3D(3250, 0), Direction.ZERO);
OTSRoadNode nodeD = new OTSRoadNode(network, "D", new OTSPoint3D(6000, 0), Direction.ZERO);
OTSRoadNode nodeE = new OTSRoadNode(network, "E", new OTSPoint3D(2000, -25), Direction.ZERO);
OTSRoadNode nodeF = new OTSRoadNode(network, "F", new OTSPoint3D(2750, 0.0), Direction.ZERO);
LinkType freeway = network.getLinkType(LinkType.DEFAULTS.FREEWAY);
LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT;
Length laneWidth = Length.instantiateSI(3.6);
LaneType freewayLane = network.getLaneType(LaneType.DEFAULTS.FREEWAY);
Speed speedLimit = new Speed(120, SpeedUnit.KM_PER_HOUR);
Speed rampSpeedLimit = new Speed(70, SpeedUnit.KM_PER_HOUR);
List lanesAB = new LaneFactory(network, nodeA, nodeB, freeway, sim, policy)
.leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH).getLanes();
List lanesBC = new LaneFactory(network, nodeB, nodeC, freeway, sim, policy)
.leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH, Permeable.LEFT).getLanes();
List lanesCD = new LaneFactory(network, nodeC, nodeD, freeway, sim, policy)
.leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH).getLanes();
List lanesEF =
new LaneFactory(network, nodeE, nodeF, freeway, sim, policy).setOffsetEnd(laneWidth.times(1.5).neg())
.leftToRight(0.5, laneWidth, freewayLane, rampSpeedLimit).addLanes().getLanes();
List lanesFB = new LaneFactory(network, nodeF, nodeB, freeway, sim, policy)
.setOffsetStart(laneWidth.times(1.5).neg()).setOffsetEnd(laneWidth.times(1.5).neg())
.leftToRight(0.5, laneWidth, freewayLane, speedLimit).addLanes().getLanes();
for (Lane lane : lanesCD)
{
new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(50)), GTUDirectionality.DIR_PLUS, sim);
}
// detectors
Duration agg = Duration.instantiateSI(60.0);
// TODO: detector length affects occupancy, which length to use?
Length detectorLength = Length.ZERO;
Detector det1 = new Detector("1", lanesAB.get(0), Length.instantiateSI(2900), detectorLength, sim, agg,
Detector.MEAN_SPEED, Detector.OCCUPANCY);
Detector det2 = new Detector("2", lanesAB.get(1), Length.instantiateSI(2900), detectorLength, sim, agg,
Detector.MEAN_SPEED, Detector.OCCUPANCY);
Detector det3 = new Detector("3", lanesCD.get(0), Length.instantiateSI(100), detectorLength, sim, agg,
Detector.MEAN_SPEED, Detector.OCCUPANCY);
Detector det4 = new Detector("4", lanesCD.get(1), Length.instantiateSI(100), detectorLength, sim, agg,
Detector.MEAN_SPEED, Detector.OCCUPANCY);
List detectors12 = new ArrayList<>();
detectors12.add(det1);
detectors12.add(det2);
List detectors34 = new ArrayList<>();
detectors34.add(det3);
detectors34.add(det4);
if (this.rampMetering)
{
// traffic light
TrafficLight light = new SimpleTrafficLight("light", lanesEF.get(0), lanesEF.get(0).getLength(), sim);
List lightList = new ArrayList<>();
lightList.add(light);
// ramp metering
RampMeteringSwitch rampSwitch = new RwsSwitch(detectors12);
RampMeteringLightController rampLightController =
new CycleTimeLightController(sim, lightList, Compatible.EVERYTHING);
new RampMetering(sim, rampSwitch, rampLightController);
}
// OD
List origins = new ArrayList<>();
origins.add(nodeA);
origins.add(nodeE);
List destinations = new ArrayList<>();
destinations.add(nodeD);
Categorization categorization = new Categorization("cat", GTUType.class);// , Lane.class);
Interpolation globalInterpolation = Interpolation.LINEAR;
ODMatrix od = new ODMatrix("rampMetering", origins, destinations, categorization, this.demandTime, globalInterpolation);
// Category carCatMainLeft = new Category(categorization, car, lanesAB.get(0));
// Category carCatMainRight = new Category(categorization, car, lanesAB.get(1));
Category carCatRamp = new Category(categorization, car);// , lanesEB.get(0));
Category controlledCarCat = new Category(categorization, controlledCar);
// double fLeft = 0.6;
od.putDemandVector(nodeA, nodeD, carCatRamp, this.mainDemand, 0.6);
od.putDemandVector(nodeA, nodeD, controlledCarCat, this.mainDemand, 0.4);
// od.putDemandVector(nodeA, nodeD, carCatMainLeft, mainDemand, fLeft);
// od.putDemandVector(nodeA, nodeD, carCatMainRight, mainDemand, 1.0 - fLeft);
od.putDemandVector(nodeE, nodeD, carCatRamp, this.rampDemand, 0.6);
od.putDemandVector(nodeE, nodeD, controlledCarCat, this.rampDemand, 0.4);
ODOptions odOptions = new ODOptions();
odOptions.set(ODOptions.GTU_TYPE, new ControlledStrategicalPlannerGenerator()).set(ODOptions.INSTANT_LC, true);
ODApplier.applyOD(network, od, odOptions);
return network;
}
/**
* Returns the parameter factory.
* @return ParameterFactoryByType; parameter factory
*/
final ParameterFactoryByType getParameterFactory()
{
return this.parameterFactory;
}
/** {@inheritDoc} */
@Override
public void notify(final EventInterface event) throws RemoteException
{
if (event.getType().equals(Network.GTU_ADD_EVENT))
{
this.gtusInSimulation.put((String) event.getContent(), getSimulator().getSimulatorTime().si);
}
else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
{
measureTravelTime((String) event.getContent());
}
else
{
super.notify(event);
}
}
/**
* Adds travel time and delay for a single GTU.
* @param id String; id of the GTU
*/
private void measureTravelTime(final String id)
{
double tt = getSimulator().getSimulatorTime().si - this.gtusInSimulation.get(id);
double x = getNetwork().getGTU(id).getOdometer().si;
// TODO: we assume 120km/h everywhere, including the slower ramps
double ttd = tt - (x / (120 / 3.6));
this.totalTravelTime += tt;
this.totalTravelTimeDelay += ttd;
this.gtusInSimulation.remove(id);
}
/** {@inheritDoc} */
@Override
protected void onSimulationEnd()
{
if (this.output)
{
// detector data
String file = String.format("%s_%02d_detectors.txt", this.scenario, getSeed());
Detector.writeToFile(getNetwork(), file, true, "%.3f", CompressionMethod.NONE);
// travel time data
for (GTU gtu : getNetwork().getGTUs())
{
measureTravelTime(gtu.getId());
}
Throw.when(!this.gtusInSimulation.isEmpty(), RuntimeException.class,
"GTUs remain in simulation that are not measured.");
file = String.format("%s_%02d_time.txt", this.scenario, getSeed());
BufferedWriter bw = CompressedFileWriter.create(file, false);
try
{
bw.write(String.format("Total travel time: %.3fs", this.totalTravelTime));
bw.newLine();
bw.write(String.format("Total travel time delay: %.3fs", this.totalTravelTimeDelay));
bw.close();
}
catch (IOException exception)
{
throw new RuntimeException(exception);
}
finally
{
try
{
if (bw != null)
{
bw.close();
}
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
}
}
/**
* Strategical planner generator. This class can be used as input in {@code ODOptions} to generate the right models with
* different GTU types.
*/
private class ControlledStrategicalPlannerGenerator implements GTUCharacteristicsGeneratorOD
{
/** Default generator. */
private DefaultGTUCharacteristicsGeneratorOD defaultGenerator = new DefaultGTUCharacteristicsGeneratorOD();
/** Controlled planner factory. */
private LaneBasedStrategicalPlannerFactory controlledPlannerFactory;
/** Constructor. */
ControlledStrategicalPlannerGenerator()
{
// anonymous factory to create tactical planners for controlled GTU's
LaneBasedTacticalPlannerFactory> tacticalPlannerFactory =
new LaneBasedTacticalPlannerFactory()
{
@Override
public Parameters getParameters() throws ParameterException
{
ParameterSet set = new ParameterSet();
set.setDefaultParameter(ParameterTypes.PERCEPTION);
set.setDefaultParameter(ParameterTypes.LOOKBACK);
set.setDefaultParameter(ParameterTypes.LOOKAHEAD);
set.setDefaultParameter(ParameterTypes.S0);
set.setDefaultParameter(ParameterTypes.TMIN);
set.setDefaultParameter(ParameterTypes.TMAX);
set.setDefaultParameter(ParameterTypes.DT);
set.setDefaultParameter(ParameterTypes.VCONG);
set.setDefaultParameter(ParameterTypes.T0);
set.setDefaultParameter(TrafficLightUtil.B_YELLOW);
set.setDefaultParameters(LmrsParameters.class);
set.setDefaultParameters(AbstractIDM.class);
return set;
}
@SuppressWarnings("synthetic-access")
@Override
public LaneBasedTacticalPlanner create(final LaneBasedGTU gtu) throws GTUException
{
// here the lateral control system is initiated
ParameterSet settings = new ParameterSet();
try
{
// system operation settings
settings.setParameter(SyncAndAccept.SYNCTIME, Duration.instantiateSI(1.0));
settings.setParameter(SyncAndAccept.COOPTIME, Duration.instantiateSI(2.0));
// parameters used in car-following model for gap-acceptance
settings.setParameter(AbstractIDM.DELTA, 1.0);
settings.setParameter(ParameterTypes.S0, Length.instantiateSI(3.0));
settings.setParameter(ParameterTypes.A, Acceleration.instantiateSI(2.0));
settings.setParameter(ParameterTypes.B, Acceleration.instantiateSI(2.0));
settings.setParameter(ParameterTypes.T, RampMeteringDemo.this.acceptedGap);
settings.setParameter(ParameterTypes.FSPEED, 1.0);
settings.setParameter(ParameterTypes.B0, Acceleration.instantiateSI(0.5));
settings.setParameter(ParameterTypes.VCONG, new Speed(60, SpeedUnit.KM_PER_HOUR));
}
catch (ParameterException exception)
{
throw new GTUException(exception);
}
return new ControlledTacticalPlanner(gtu, new SyncAndAccept(gtu, new IDMPlus(), settings));
}
};
// standard strategical planner factory using the tactical factory and the simulation-wide parameter factory
this.controlledPlannerFactory = new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory,
RampMeteringDemo.this.getParameterFactory());
}
/** {@inheritDoc} */
@Override
public LaneBasedGTUCharacteristics draw(final Node origin, final Node destination, final Category category,
final StreamInterface randomStream) throws GTUException
{
GTUType gtuType = category.get(GTUType.class);
// if GTU type is a controlled car, create characteristics for a controlled car
if (gtuType.equals(getNetwork().getGtuType(CONTROLLED_CAR_ID)))
{
Route route = null;
VehicleModel vehicleModel = VehicleModel.MINMAX;
GTUCharacteristics gtuCharacteristics =
GTUType.defaultCharacteristics(gtuType, origin.getNetwork(), randomStream);
return new LaneBasedGTUCharacteristics(gtuCharacteristics, this.controlledPlannerFactory, route, origin,
destination, vehicleModel);
}
// otherwise generate default characteristics
return this.defaultGenerator.draw(origin, destination, category, randomStream);
}
}
/** Tactical planner. */
private static class ControlledTacticalPlanner extends AbstractIncentivesTacticalPlanner
{
/** */
private static final long serialVersionUID = 20190731L;
/** Lane change system. */
private AutomaticLaneChangeSystem laneChangeSystem;
/** Lane change status. */
private final LaneChange laneChange;
/** Map that {@code getLaneChangeDesire} writes current desires in. This is not used here. */
private Map, Desire> dummyMap = new LinkedHashMap<>();
/**
* Constructor.
* @param gtu LaneBasedGTU; gtu
* @param laneChangeSystem AutomaticLaneChangeSystem; lane change system
*/
ControlledTacticalPlanner(final LaneBasedGTU gtu, final AutomaticLaneChangeSystem laneChangeSystem)
{
super(new IDMPlus(), gtu, generatePerception(gtu));
setDefaultIncentives();
this.laneChangeSystem = laneChangeSystem;
this.laneChange = Try.assign(() -> new LaneChange(gtu), "Parameter LCDUR is required.", GTUException.class);
}
/**
* Helper method to create perception.
* @param gtu LaneBasedGTU; gtu
* @return LanePerception lane perception
*/
private static LanePerception generatePerception(final LaneBasedGTU gtu)
{
CategoricalLanePerception perception = new CategoricalLanePerception(gtu);
perception.addPerceptionCategory(new DirectEgoPerception>(perception));
perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
// TODO: perceived GTUs as first type
perception.addPerceptionCategory(new DirectNeighborsPerception(perception, HeadwayGtuType.WRAP));
perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
perception.addPerceptionCategory(new DirectIntersectionPerception(perception, HeadwayGtuType.WRAP));
return perception;
}
/** {@inheritDoc} */
@Override
public OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
throws OperationalPlanException, GTUException, NetworkException, ParameterException
{
// get some general input
Speed speed = getPerception().getPerceptionCategory(EgoPerception.class).getSpeed();
SpeedLimitProspect slp = getPerception().getPerceptionCategory(InfrastructurePerception.class)
.getSpeedLimitProspect(RelativeLane.CURRENT);
SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
// LMRS desire
Desire desire = LmrsUtil.getLaneChangeDesire(getGtu().getParameters(), getPerception(), getCarFollowingModel(),
getMandatoryIncentives(), getVoluntaryIncentives(), this.dummyMap);
// other vehicles respond to these 'interpreted' levels of lane change desire
getGtu().getParameters().setParameter(LmrsParameters.DLEFT, desire.getLeft());
getGtu().getParameters().setParameter(LmrsParameters.DRIGHT, desire.getRight());
// car-following
Acceleration a = getGtu().getCarFollowingAcceleration();
// cooperation
Acceleration aCoop = Cooperation.PASSIVE.cooperate(getPerception(), getGtu().getParameters(), sli,
getCarFollowingModel(), LateralDirectionality.LEFT, desire);
a = Acceleration.min(a, aCoop);
aCoop = Cooperation.PASSIVE.cooperate(getPerception(), getGtu().getParameters(), sli, getCarFollowingModel(),
LateralDirectionality.RIGHT, desire);
a = Acceleration.min(a, aCoop);
// compose human plan
SimpleOperationalPlan simplePlan =
new SimpleOperationalPlan(a, getGtu().getParameters().getParameter(ParameterTypes.DT));
for (AccelerationIncentive incentive : getAccelerationIncentives())
{
incentive.accelerate(simplePlan, RelativeLane.CURRENT, Length.ZERO, getGtu(), getPerception(),
getCarFollowingModel(), speed, getGtu().getParameters(), sli);
}
// add lane change control
if (!this.laneChange.isChangingLane())
{
double dFree = getGtu().getParameters().getParameter(LmrsParameters.DFREE);
if (this.laneChangeSystem.initiatedLaneChange().isNone())
{
if (desire.leftIsLargerOrEqual() && desire.getLeft() > dFree)
{
this.laneChangeSystem.initiateLaneChange(LateralDirectionality.LEFT);
}
else if (desire.getRight() > dFree)
{
this.laneChangeSystem.initiateLaneChange(LateralDirectionality.RIGHT);
}
}
else
{
if ((this.laneChangeSystem.initiatedLaneChange().isLeft() && desire.getLeft() < dFree)
|| (this.laneChangeSystem.initiatedLaneChange().isRight() && desire.getRight() < dFree))
{
this.laneChangeSystem.initiateLaneChange(LateralDirectionality.NONE);
}
}
}
simplePlan = this.laneChangeSystem.operate(simplePlan, getGtu().getParameters());
simplePlan.setTurnIndicator(getGtu());
// create plan
return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), startTime, simplePlan, this.laneChange);
}
}
/** Interface allowing tactical planners to use an automatic lane change system. */
private interface AutomaticLaneChangeSystem
{
/**
* Update operational plan with actions to change lane. This method should be called by the tactical planner always.
* @param simplePlan SimpleOperationalPlan; plan
* @param parameters Parameters; parameters
* @return SimpleOperationalPlan; adapted plan
* @throws OperationalPlanException if the system runs in to an error
* @throws ParameterException if a parameter is missing
*/
SimpleOperationalPlan operate(SimpleOperationalPlan simplePlan, Parameters parameters)
throws OperationalPlanException, ParameterException;
/**
* Returns the direction in which the system was initiated to perform a lane change.
* @return LateralDirectionality; direction in which the system was initiated to perform a lane change, {@code NONE} if
* none
*/
LateralDirectionality initiatedLaneChange();
/**
* Initiate a lane change.
* @param dir LateralDirectionality; direction, use {@code NONE} to cancel
*/
void initiateLaneChange(LateralDirectionality dir);
}
/** Implementation of an automatic lane change system. */
private static class SyncAndAccept implements AutomaticLaneChangeSystem
{
/** Parameter of time after lane change command when the system will start synchronization. */
public static final ParameterTypeDuration SYNCTIME = new ParameterTypeDuration("tSync",
"Time after which synchronization starts.", Duration.instantiateSI(1.0), NumericConstraint.POSITIVE);
/** Parameter of time after lane change command when the system will start cooperation (indicator). */
public static final ParameterTypeDuration COOPTIME = new ParameterTypeDuration("tCoop",
"Time after which cooperation starts (indicator).", Duration.instantiateSI(2.0), NumericConstraint.POSITIVE);
/** GTU. */
private final LaneBasedGTU gtu;
/** Car-following model for gap-acceptance. */
private final CarFollowingModel carFollowingModel;
/** Parameters containing the system settings. */
private final Parameters settings;
/** Initiated lane change direction. */
private LateralDirectionality direction = LateralDirectionality.NONE;
/** Time when the lane change was initiated. */
private Time initiationTime;
/**
* Constructor.
* @param gtu LaneBasedGTU; GTU
* @param carFollowingModel CarFollowingModel; car-following model
* @param settings Parameters; system settings
*/
SyncAndAccept(final LaneBasedGTU gtu, final CarFollowingModel carFollowingModel, final Parameters settings)
{
this.gtu = gtu;
this.carFollowingModel = carFollowingModel;
this.settings = settings;
}
/** {@inheritDoc} */
@Override
public SimpleOperationalPlan operate(final SimpleOperationalPlan simplePlan, final Parameters parameters)
throws OperationalPlanException, ParameterException
{
// active?
if (this.direction.isNone())
{
return simplePlan;
}
// check gap
InfrastructurePerception infra =
this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(InfrastructurePerception.class);
SpeedLimitInfo sli = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
NeighborsPerception neighbors =
this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(NeighborsPerception.class);
if (infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, this.direction).gt0()
&& !neighbors.isGtuAlongside(this.direction)
&& acceptGap(neighbors.getFirstFollowers(this.direction), sli, false)
&& acceptGap(neighbors.getFirstLeaders(this.direction), sli, true))
{
// gaps accepted, start lane change
SimpleOperationalPlan plan =
new SimpleOperationalPlan(simplePlan.getAcceleration(), simplePlan.getDuration(), this.direction);
this.direction = LateralDirectionality.NONE;
this.initiationTime = null;
return plan;
}
// synchronization
Duration since = this.gtu.getSimulator().getSimulatorTime().minus(this.initiationTime);
if (since.gt(this.settings.getParameter(SYNCTIME))
|| this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
{
PerceptionCollectable leaders =
neighbors.getLeaders(new RelativeLane(this.direction, 1));
if (!leaders.isEmpty())
{
HeadwayGTU leader = leaders.first();
Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings,
this.gtu.getSpeed(), sli, leader);
a = Acceleration.max(a, this.settings.getParameter(ParameterTypes.B).neg());
simplePlan.minimizeAcceleration(a);
}
}
// cooperation
if (since.gt(this.settings.getParameter(COOPTIME))
|| this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
{
if (this.direction.isLeft())
{
simplePlan.setIndicatorIntentLeft();
}
else
{
simplePlan.setIndicatorIntentRight();
}
}
// return
return simplePlan;
}
/**
* Checks whether a gap can be accepted.
* @param neighbors Set<HeadwayGTU>; neighbors
* @param sli SpeedLimitInfo; speed limit info
* @param leaders boolean; whether we are dealing with leaders, or followers
* @return boolean; whether the gap is accepted
* @throws ParameterException if a parameter is not defined
*/
private boolean acceptGap(final Set neighbors, final SpeedLimitInfo sli, final boolean leaders)
throws ParameterException
{
for (HeadwayGTU neighbor : neighbors)
{
Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings,
leaders ? this.gtu.getSpeed() : neighbor.getSpeed(), sli, neighbor.getDistance(),
leaders ? neighbor.getSpeed() : this.gtu.getSpeed());
if (a.lt(this.settings.getParameter(ParameterTypes.B).neg()))
{
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public LateralDirectionality initiatedLaneChange()
{
return this.direction;
}
/** {@inheritDoc} */
@Override
public void initiateLaneChange(final LateralDirectionality dir)
{
this.direction = dir;
if (!dir.isNone())
{
this.initiationTime = this.gtu.getSimulator().getSimulatorTime();
}
else
{
this.initiationTime = null;
}
}
}
}