1 package org.opentrafficsim.demo;
2
3 import java.io.BufferedWriter;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import org.djunits.unit.DurationUnit;
13 import org.djunits.unit.FrequencyUnit;
14 import org.djunits.unit.SpeedUnit;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Direction;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.vector.DurationVector;
21 import org.djunits.value.vdouble.vector.FrequencyVector;
22 import org.djutils.cli.CliUtil;
23 import org.djutils.data.csv.CsvData;
24 import org.djutils.data.serialization.TextSerializationException;
25 import org.djutils.draw.point.DirectedPoint2d;
26 import org.djutils.draw.point.Point2d;
27 import org.djutils.event.Event;
28 import org.djutils.exceptions.Throw;
29 import org.djutils.io.CompressedFileWriter;
30 import org.opentrafficsim.animation.colorer.Colorer;
31 import org.opentrafficsim.animation.data.gtu.GtuTypeGtuColorer;
32 import org.opentrafficsim.base.DistancedObject;
33 import org.opentrafficsim.base.OtsRuntimeException;
34 import org.opentrafficsim.base.parameters.ParameterException;
35 import org.opentrafficsim.base.parameters.ParameterSet;
36 import org.opentrafficsim.base.parameters.ParameterTypeDuration;
37 import org.opentrafficsim.base.parameters.ParameterTypes;
38 import org.opentrafficsim.base.parameters.Parameters;
39 import org.opentrafficsim.base.parameters.constraint.NumericConstraint;
40 import org.opentrafficsim.core.definitions.Defaults;
41 import org.opentrafficsim.core.definitions.DefaultsNl;
42 import org.opentrafficsim.core.definitions.Definitions;
43 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
44 import org.opentrafficsim.core.gtu.Gtu;
45 import org.opentrafficsim.core.gtu.GtuException;
46 import org.opentrafficsim.core.gtu.GtuType;
47 import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
48 import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
49 import org.opentrafficsim.core.gtu.perception.Perception;
50 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
51 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
52 import org.opentrafficsim.core.network.LateralDirectionality;
53 import org.opentrafficsim.core.network.LinkType;
54 import org.opentrafficsim.core.network.Network;
55 import org.opentrafficsim.core.network.NetworkException;
56 import org.opentrafficsim.core.network.Node;
57 import org.opentrafficsim.core.network.route.Route;
58 import org.opentrafficsim.core.parameters.ParameterFactoryByType;
59 import org.opentrafficsim.road.definitions.DefaultsRoadNl;
60 import org.opentrafficsim.road.gtu.LaneBasedGtu;
61 import org.opentrafficsim.road.gtu.LaneBookkeeping;
62 import org.opentrafficsim.road.gtu.VehicleModel;
63 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBias;
64 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBiases;
65 import org.opentrafficsim.road.gtu.generator.characteristics.DefaultLaneBasedGtuCharacteristicsGeneratorOd;
66 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
67 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGeneratorOd;
68 import org.opentrafficsim.road.gtu.operational.LaneOperationalPlanBuilder;
69 import org.opentrafficsim.road.gtu.operational.SimpleOperationalPlan;
70 import org.opentrafficsim.road.gtu.perception.CategoricalLanePerception;
71 import org.opentrafficsim.road.gtu.perception.LanePerception;
72 import org.opentrafficsim.road.gtu.perception.PerceptionCollectable;
73 import org.opentrafficsim.road.gtu.perception.RelativeLane;
74 import org.opentrafficsim.road.gtu.perception.categories.AnticipationTrafficPerception;
75 import org.opentrafficsim.road.gtu.perception.categories.DirectInfrastructurePerception;
76 import org.opentrafficsim.road.gtu.perception.categories.DirectIntersectionPerception;
77 import org.opentrafficsim.road.gtu.perception.categories.InfrastructurePerception;
78 import org.opentrafficsim.road.gtu.perception.categories.neighbors.DirectNeighborsPerception;
79 import org.opentrafficsim.road.gtu.perception.categories.neighbors.NeighborsPerception;
80 import org.opentrafficsim.road.gtu.perception.categories.neighbors.PerceivedGtuType;
81 import org.opentrafficsim.road.gtu.perception.object.PerceivedGtu;
82 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
83 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
84 import org.opentrafficsim.road.gtu.tactical.LaneBasedTacticalPlanner;
85 import org.opentrafficsim.road.gtu.tactical.LaneBasedTacticalPlannerFactory;
86 import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
87 import org.opentrafficsim.road.gtu.tactical.following.AbstractIdm;
88 import org.opentrafficsim.road.gtu.tactical.following.CarFollowingModel;
89 import org.opentrafficsim.road.gtu.tactical.following.IdmPlus;
90 import org.opentrafficsim.road.gtu.tactical.lmrs.AbstractIncentivesTacticalPlanner;
91 import org.opentrafficsim.road.gtu.tactical.lmrs.AccelerationTrafficLights;
92 import org.opentrafficsim.road.gtu.tactical.lmrs.IncentiveKeep;
93 import org.opentrafficsim.road.gtu.tactical.lmrs.IncentiveRoute;
94 import org.opentrafficsim.road.gtu.tactical.lmrs.IncentiveSpeedWithCourtesy;
95 import org.opentrafficsim.road.gtu.tactical.lmrs.Lmrs;
96 import org.opentrafficsim.road.gtu.tactical.lmrs.LmrsFactory;
97 import org.opentrafficsim.road.gtu.tactical.lmrs.LmrsFactory.Setting;
98 import org.opentrafficsim.road.gtu.tactical.util.CarFollowingUtil;
99 import org.opentrafficsim.road.gtu.tactical.util.lmrs.Cooperation;
100 import org.opentrafficsim.road.gtu.tactical.util.lmrs.Desire;
101 import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsParameters;
102 import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsUtil;
103 import org.opentrafficsim.road.network.Lane;
104 import org.opentrafficsim.road.network.LaneKeepingPolicy;
105 import org.opentrafficsim.road.network.LanePosition;
106 import org.opentrafficsim.road.network.LaneType;
107 import org.opentrafficsim.road.network.RoadNetwork;
108 import org.opentrafficsim.road.network.Stripe;
109 import org.opentrafficsim.road.network.control.rampmetering.CycleTimeLightController;
110 import org.opentrafficsim.road.network.control.rampmetering.RampMetering;
111 import org.opentrafficsim.road.network.control.rampmetering.RampMeteringLightController;
112 import org.opentrafficsim.road.network.control.rampmetering.RampMeteringSwitch;
113 import org.opentrafficsim.road.network.control.rampmetering.RwsSwitch;
114 import org.opentrafficsim.road.network.factory.LaneFactory;
115 import org.opentrafficsim.road.network.object.detector.LoopDetector;
116 import org.opentrafficsim.road.network.object.trafficlight.TrafficLight;
117 import org.opentrafficsim.road.network.speed.LaneSpeedLimits;
118 import org.opentrafficsim.road.network.speed.SpeedLimits;
119 import org.opentrafficsim.road.od.Categorization;
120 import org.opentrafficsim.road.od.Category;
121 import org.opentrafficsim.road.od.Interpolation;
122 import org.opentrafficsim.road.od.OdApplier;
123 import org.opentrafficsim.road.od.OdMatrix;
124 import org.opentrafficsim.road.od.OdOptions;
125 import org.opentrafficsim.swing.gui.OtsSimulationPanelDecorator;
126 import org.opentrafficsim.swing.script.AbstractSimulationScript;
127
128 import nl.tudelft.simulation.jstats.distributions.DistNormal;
129 import nl.tudelft.simulation.jstats.streams.StreamInterface;
130 import picocli.CommandLine.Option;
131
132
133
134
135
136
137
138
139
140
141 public class RampMeteringDemo extends AbstractSimulationScript
142 {
143
144
145 private static final String CONTROLLED_CAR_ID = "controlledCar";
146
147
148 private ParameterFactoryByType parameterFactory = new ParameterFactoryByType();
149
150
151 @Option(names = {"-r", "--rampMetering"}, description = "Ramp metering on or off", defaultValue = "true")
152 private boolean rampMetering;
153
154
155 @Option(names = "--output", description = "Generate output.", negatable = true, defaultValue = "false")
156 private boolean output;
157
158
159 @Option(names = "--acceptedGap", description = "Accepted gap.")
160 private Duration acceptedGap = Duration.ofSI(0.5);
161
162
163 private FrequencyVector mainDemand;
164
165
166 @Option(names = "--mainDemand", description = "Main demand in veh/h.", defaultValue = "2000,3000,3900,3900,3000")
167 private String mainDemandString;
168
169
170 private FrequencyVector rampDemand;
171
172
173 @Option(names = "--rampDemand", description = "Ramp demand in veh/h.", defaultValue = "500,500,500,500,500")
174 private String rampDemandString;
175
176
177 private DurationVector demandTime;
178
179
180 @Option(names = "--demandTime", description = "Demand time in min.", defaultValue = "0,10,40,50,70")
181 private String demandTimeString;
182
183
184 @Option(names = "--scenario", description = "Scenario name.", defaultValue = "test")
185 private String scenario;
186
187
188 private GtuType car;
189
190
191 private GtuType controlledCar;
192
193
194 private Map<String, Double> gtusInSimulation = new LinkedHashMap<>();
195
196
197 private double totalTravelTime = 0.0;
198
199
200 private double totalTravelTimeDelay = 0.0;
201
202
203 private Definitions definitions = new Definitions();
204
205
206
207
208 protected RampMeteringDemo()
209 {
210 super("Ramp metering", "Ramp metering");
211 }
212
213
214
215
216
217
218 public static void main(final String[] args) throws Exception
219 {
220 RampMeteringDemo demo = new RampMeteringDemo();
221 CliUtil.changeOptionDefault(demo, "simulationTime", "4200s");
222 CliUtil.execute(demo, args);
223 demo.mainDemand = new FrequencyVector(arrayFromString(demo.mainDemandString), FrequencyUnit.PER_HOUR);
224 demo.rampDemand = new FrequencyVector(arrayFromString(demo.rampDemandString), FrequencyUnit.PER_HOUR);
225 demo.demandTime = new DurationVector(arrayFromString(demo.demandTimeString), DurationUnit.MINUTE);
226 demo.start();
227 }
228
229
230
231
232
233
234 private static double[] arrayFromString(final String str)
235 {
236 return Arrays.stream(str.split(",")).mapToDouble(Double::parseDouble).toArray();
237 }
238
239 @Override
240 protected OtsSimulationPanelDecorator getDecorator()
241 {
242 return new OtsSimulationPanelDecorator()
243 {
244 @Override
245 public List<Colorer<? super Gtu>> getGtuColorers()
246 {
247 List<Colorer<? super Gtu>> colorers = new ArrayList<>(DEFAULT_GTU_COLORERS);
248 colorers.add(new GtuTypeGtuColorer(RampMeteringDemo.this.car, RampMeteringDemo.this.controlledCar));
249 return colorers;
250 }
251 };
252 }
253
254 @Override
255 protected RoadNetwork setupSimulation(final OtsSimulatorInterface sim) throws Exception
256 {
257 RoadNetwork network = new RoadNetwork("RampMetering", sim);
258 if (this.output)
259 {
260 network.addListener(this, Network.GTU_ADD_EVENT);
261 network.addListener(this, Network.GTU_REMOVE_EVENT);
262 }
263 this.car = DefaultsNl.CAR;
264 this.controlledCar = new GtuType(CONTROLLED_CAR_ID, this.car);
265 this.definitions.add(GtuType.class, this.car);
266 this.definitions.add(GtuType.class, this.controlledCar);
267
268
269 StreamInterface stream = sim.getModel().getStream("generation");
270 this.parameterFactory.addParameter(ParameterTypes.FSPEED, new DistNormal(stream, 123.7 / 120.0, 12.0 / 120.0));
271
272 Node nodeA = new Node(network, "A", new Point2d(0, 0), Direction.ZERO);
273 Node nodeB = new Node(network, "B", new Point2d(3000, 0), Direction.ZERO);
274 Node nodeC = new Node(network, "C", new Point2d(3250, 0), Direction.ZERO);
275 Node nodeD = new Node(network, "D", new Point2d(6000, 0), Direction.ZERO);
276 Node nodeE = new Node(network, "E", new Point2d(2000, -25), Direction.ZERO);
277 Node nodeF = new Node(network, "F", new Point2d(2750, 0.0), Direction.ZERO);
278
279 LinkType freeway = DefaultsNl.FREEWAY;
280 LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT;
281 Length laneWidth = Length.ofSI(3.6);
282 LaneType freewayLane = DefaultsRoadNl.FREEWAY;
283 LaneSpeedLimits speedLimit = new LaneSpeedLimits(new Speed(120, SpeedUnit.KM_PER_HOUR),
284 Map.of(DefaultsNl.TRUCK, new Speed(80, SpeedUnit.KM_PER_HOUR)));
285 LaneSpeedLimits rampSpeedLimits = new LaneSpeedLimits(new Speed(70, SpeedUnit.KM_PER_HOUR));
286 List<Lane> lanesAB = new LaneFactory(network, nodeA, nodeB, freeway, sim, policy)
287 .leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(DefaultsRoadNl.DASHED).getLanes();
288 List<Stripe> stripes = new ArrayList<>();
289 List<Lane> lanesBC = new LaneFactory(network, nodeB, nodeC, freeway, sim, policy)
290 .leftToRight(1.0, laneWidth, freewayLane, speedLimit)
291 .addLanes(stripes, DefaultsRoadNl.DASHED, DefaultsRoadNl.BLOCK).getLanes();
292 stripes.get(2).addPermeability(this.car, LateralDirectionality.LEFT);
293 List<Lane> lanesCD = new LaneFactory(network, nodeC, nodeD, freeway, sim, policy)
294 .leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(DefaultsRoadNl.DASHED)
295 .addShoulder(laneWidth, LateralDirectionality.RIGHT, new LaneType("SHOULDER")).getLanes();
296 List<Lane> lanesEF =
297 new LaneFactory(network, nodeE, nodeF, freeway, sim, policy).setOffsetEnd(laneWidth.times(1.5).neg())
298 .leftToRight(0.5, laneWidth, freewayLane, rampSpeedLimits).addLanes().getLanes();
299 List<Lane> lanesFB = new LaneFactory(network, nodeF, nodeB, freeway, sim, policy)
300 .setOffsetStart(laneWidth.times(1.5).neg()).setOffsetEnd(laneWidth.times(1.5).neg())
301 .leftToRight(0.5, laneWidth, freewayLane, speedLimit).addLanes().getLanes();
302
303 Duration first = Duration.ofSI(60.0);
304 Duration agg = Duration.ofSI(60.0);
305
306 Length detectorLength = Length.ZERO;
307 LoopDetector det1 = new LoopDetector("1", new LanePosition(lanesAB.get(0), Length.ofSI(2900)), detectorLength,
308 DefaultsNl.LOOP_DETECTOR, first, agg, LoopDetector.MEAN_SPEED, LoopDetector.OCCUPANCY);
309 LoopDetector det2 = new LoopDetector("2", new LanePosition(lanesAB.get(1), Length.ofSI(2900)), detectorLength,
310 DefaultsNl.LOOP_DETECTOR, first, agg, LoopDetector.MEAN_SPEED, LoopDetector.OCCUPANCY);
311 LoopDetector det3 = new LoopDetector("3", new LanePosition(lanesCD.get(0), Length.ofSI(100)), detectorLength,
312 DefaultsNl.LOOP_DETECTOR, first, agg, LoopDetector.MEAN_SPEED, LoopDetector.OCCUPANCY);
313 LoopDetector det4 = new LoopDetector("4", new LanePosition(lanesCD.get(1), Length.ofSI(100)), detectorLength,
314 DefaultsNl.LOOP_DETECTOR, first, agg, LoopDetector.MEAN_SPEED, LoopDetector.OCCUPANCY);
315 List<LoopDetector> detectors12 = new ArrayList<>();
316 detectors12.add(det1);
317 detectors12.add(det2);
318 List<LoopDetector> detectors34 = new ArrayList<>();
319 detectors34.add(det3);
320 detectors34.add(det4);
321 if (this.rampMetering)
322 {
323
324 TrafficLight light = new TrafficLight("light", lanesEF.get(0), lanesEF.get(0).getLength());
325 List<TrafficLight> lightList = new ArrayList<>();
326 lightList.add(light);
327
328 RampMeteringSwitch rampSwitch = new RwsSwitch(detectors12);
329 RampMeteringLightController rampLightController =
330 new CycleTimeLightController(sim, lightList, DefaultsNl.LOOP_DETECTOR);
331 new RampMetering(sim, rampSwitch, rampLightController);
332 }
333
334
335 List<Node> origins = new ArrayList<>();
336 origins.add(nodeA);
337 origins.add(nodeE);
338 List<Node> destinations = new ArrayList<>();
339 destinations.add(nodeD);
340 Categorization categorization = new Categorization("cat", GtuType.class);
341 Interpolation globalInterpolation = Interpolation.LINEAR;
342 OdMatrix od = new OdMatrix("rampMetering", origins, destinations, categorization, this.demandTime, globalInterpolation);
343
344
345 Category carCatRamp = new Category(categorization, this.car);
346 Category controlledCarCat = new Category(categorization, this.controlledCar);
347
348 od.putDemandVector(nodeA, nodeD, carCatRamp, this.mainDemand, 0.6);
349 od.putDemandVector(nodeA, nodeD, controlledCarCat, this.mainDemand, 0.4);
350
351
352 od.putDemandVector(nodeE, nodeD, carCatRamp, this.rampDemand, 0.6);
353 od.putDemandVector(nodeE, nodeD, controlledCarCat, this.rampDemand, 0.4);
354 OdOptions odOptions = new OdOptions();
355 DefaultLaneBasedGtuCharacteristicsGeneratorOd.Factory factory =
356 new DefaultLaneBasedGtuCharacteristicsGeneratorOd.Factory(
357 new LaneBasedStrategicalRoutePlannerFactory(new LmrsFactory<>(Lmrs::new).setStream(stream)
358 .set(Setting.ACCELERATION_TRAFFIC_LIGHTS, true, DefaultsNl.CAR)));
359 odOptions.set(OdOptions.GTU_TYPE, new ControlledStrategicalPlannerGenerator(factory.create()));
360 odOptions.set(OdOptions.BOOKKEEPING, LaneBookkeeping.INSTANT);
361 odOptions.set(OdOptions.LANE_BIAS, new LaneBiases().addBias(this.car, LaneBias.WEAK_LEFT));
362 odOptions.set(OdOptions.NO_LC_DIST, Length.ofSI(300));
363 OdApplier.applyOd(network, od, odOptions, DefaultsNl.ROAD_USERS);
364
365 return network;
366 }
367
368
369
370
371
372 final ParameterFactoryByType getParameterFactory()
373 {
374 return this.parameterFactory;
375 }
376
377 @Override
378 public void notify(final Event event)
379 {
380 if (event.getType().equals(Network.GTU_ADD_EVENT))
381 {
382 this.gtusInSimulation.put((String) event.getContent(), getSimulator().getSimulatorTime().si);
383 }
384 else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
385 {
386 measureTravelTime((String) event.getContent());
387 }
388 else
389 {
390 super.notify(event);
391 }
392 }
393
394
395
396
397
398 private void measureTravelTime(final String id)
399 {
400 double tt = getSimulator().getSimulatorTime().si - this.gtusInSimulation.get(id);
401 double x = getNetwork().getGTU(id).get().getOdometer().si;
402
403 double ttd = tt - (x / (120 / 3.6));
404 this.totalTravelTime += tt;
405 this.totalTravelTimeDelay += ttd;
406 this.gtusInSimulation.remove(id);
407 }
408
409 @Override
410 protected void onSimulationEnd()
411 {
412 System.out.println("onSimulationEnd()");
413 if (this.output)
414 {
415
416 String file = String.format("%s_%02d_detectors.csv", this.scenario, getSeed());
417 try
418 {
419 CsvData.writeData(file, file + ".header", LoopDetector.asTablePeriodicData(getNetwork()));
420 }
421 catch (IOException | TextSerializationException exception)
422 {
423 throw new OtsRuntimeException(exception);
424 }
425
426
427 for (Gtu gtu : getNetwork().getGTUs())
428 {
429 measureTravelTime(gtu.getId());
430 }
431 Throw.when(!this.gtusInSimulation.isEmpty(), OtsRuntimeException.class,
432 "GTUs remain in simulation that are not measured.");
433 file = String.format("%s_%02d_time.txt", this.scenario, getSeed());
434 BufferedWriter bw = null;
435 try
436 {
437 bw = CompressedFileWriter.create(file, false);
438 bw.write(String.format("Total travel time: %.3fs", this.totalTravelTime));
439 bw.newLine();
440 bw.write(String.format("Total travel time delay: %.3fs", this.totalTravelTimeDelay));
441 bw.close();
442 }
443 catch (IOException exception)
444 {
445 throw new OtsRuntimeException(exception);
446 }
447 finally
448 {
449 try
450 {
451 if (bw != null)
452 {
453 bw.close();
454 }
455 }
456 catch (IOException ex)
457 {
458 throw new OtsRuntimeException(ex);
459 }
460 }
461 }
462 }
463
464
465
466
467
468 private class ControlledStrategicalPlannerGenerator implements LaneBasedGtuCharacteristicsGeneratorOd
469 {
470
471
472 private final DefaultLaneBasedGtuCharacteristicsGeneratorOd defaultGenerator;
473
474
475 private LaneBasedStrategicalPlannerFactory<?> controlledPlannerFactory;
476
477
478
479
480
481 ControlledStrategicalPlannerGenerator(final DefaultLaneBasedGtuCharacteristicsGeneratorOd defaultGenerator)
482 {
483 this.defaultGenerator = defaultGenerator;
484
485 LaneBasedTacticalPlannerFactory<?> tacticalPlannerFactory =
486 new LaneBasedTacticalPlannerFactory<LaneBasedTacticalPlanner>()
487 {
488 @Override
489 public Parameters getParameters(final GtuType gtuType) throws ParameterException
490 {
491 ParameterSet set = new ParameterSet();
492 set.setDefaultParameter(ParameterTypes.LC_INFO);
493 set.setDefaultParameter(ParameterTypes.LOOKBACK);
494 set.setDefaultParameter(ParameterTypes.LOOKAHEAD);
495 set.setDefaultParameter(ParameterTypes.S0);
496 set.setDefaultParameter(ParameterTypes.TMIN);
497 set.setDefaultParameter(ParameterTypes.TMAX);
498 set.setDefaultParameter(ParameterTypes.DT);
499 set.setDefaultParameter(ParameterTypes.VCONG);
500 set.setDefaultParameter(ParameterTypes.T0);
501 set.setDefaultParameter(ParameterTypes.BCRIT);
502 set.setDefaultParameter(ParameterTypes.LCDUR);
503 set.setDefaultParameters(LmrsParameters.class);
504 set.setDefaultParameters(AbstractIdm.class);
505 return set;
506 }
507
508 @SuppressWarnings("synthetic-access")
509 @Override
510 public LaneBasedTacticalPlanner create(final LaneBasedGtu gtu) throws GtuException
511 {
512
513 ParameterSet settings = new ParameterSet();
514 try
515 {
516
517 settings.setParameter(SyncAndAccept.SYNCTIME, Duration.ofSI(1.0));
518 settings.setParameter(SyncAndAccept.COOPTIME, Duration.ofSI(2.0));
519
520 settings.setParameter(AbstractIdm.DELTA, 1.0);
521 settings.setParameter(ParameterTypes.S0, Length.ofSI(3.0));
522 settings.setParameter(ParameterTypes.A, Acceleration.ofSI(2.0));
523 settings.setParameter(ParameterTypes.B, Acceleration.ofSI(2.0));
524 settings.setParameter(ParameterTypes.T, RampMeteringDemo.this.acceptedGap);
525 settings.setParameter(ParameterTypes.FSPEED, 1.0);
526 settings.setParameter(ParameterTypes.B0, Acceleration.ofSI(0.5));
527 settings.setParameter(ParameterTypes.VCONG, new Speed(60, SpeedUnit.KM_PER_HOUR));
528 }
529 catch (ParameterException exception)
530 {
531 throw new GtuException(exception);
532 }
533 return new ControlledTacticalPlanner(gtu, new SyncAndAccept(gtu, new IdmPlus(), settings));
534 }
535 };
536
537 this.controlledPlannerFactory = new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory,
538 RampMeteringDemo.this.getParameterFactory());
539 }
540
541 @Override
542 public LaneBasedGtuCharacteristics draw(final Node origin, final Node destination, final Category category,
543 final StreamInterface randomStream) throws GtuException
544 {
545 GtuType gtuType = category.get(GtuType.class);
546
547 if (gtuType.equals(RampMeteringDemo.this.definitions.get(GtuType.class, CONTROLLED_CAR_ID).get()))
548 {
549 Route route = null;
550 VehicleModel vehicleModel = VehicleModel.MINMAX;
551 return new LaneBasedGtuCharacteristics(Defaults.NL.apply(gtuType, randomStream)
552 .orElseThrow(
553 () -> new GtuException("No characteristics for GTU type " + gtuType + " could be generated."))
554 .get(), this.controlledPlannerFactory, route, origin, destination, vehicleModel);
555 }
556
557 return this.defaultGenerator.draw(origin, destination, category, randomStream);
558 }
559
560 }
561
562
563 private static class ControlledTacticalPlanner extends AbstractIncentivesTacticalPlanner
564 {
565
566 private AutomaticLaneChangeSystem laneChangeSystem;
567
568
569
570
571
572
573 ControlledTacticalPlanner(final LaneBasedGtu gtu, final AutomaticLaneChangeSystem laneChangeSystem)
574 {
575 super(new IdmPlus(), gtu, generatePerception(gtu));
576 addMandatoryIncentive(IncentiveRoute.SINGLETON);
577 addVoluntaryIncentive(IncentiveSpeedWithCourtesy.SINGLETON);
578 addVoluntaryIncentive(IncentiveKeep.SINGLETON);
579 addAccelerationIncentive(AccelerationTrafficLights.SINGLETON);
580 this.laneChangeSystem = laneChangeSystem;
581 }
582
583
584
585
586
587
588 private static LanePerception generatePerception(final LaneBasedGtu gtu)
589 {
590 CategoricalLanePerception perception = new CategoricalLanePerception(gtu);
591 perception.addPerceptionCategory(new DirectEgoPerception<LaneBasedGtu, Perception<LaneBasedGtu>>(perception));
592 perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
593
594 perception.addPerceptionCategory(new DirectNeighborsPerception(perception, PerceivedGtuType.WRAP));
595 perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
596 perception.addPerceptionCategory(new DirectIntersectionPerception(perception, PerceivedGtuType.WRAP));
597 return perception;
598 }
599
600 @Override
601 public OperationalPlan generateOperationalPlan(final Duration startTime, final DirectedPoint2d locationAtStartTime)
602 throws GtuException, NetworkException, ParameterException
603 {
604
605 TacticalContextEgo context = new TacticalContextEgo(getGtu());
606
607
608 Desire desire = LmrsUtil.getLaneChangeDesire(context, this);
609
610
611 getGtu().getParameters().setClaimedParameter(LmrsParameters.DLEFT, desire.left(), this);
612 getGtu().getParameters().setClaimedParameter(LmrsParameters.DRIGHT, desire.right(), this);
613
614
615 Acceleration a = context.getCarFollowingAcceleration();
616
617
618 Acceleration aCoop = Cooperation.PASSIVE.cooperate(context, LateralDirectionality.LEFT, null, desire);
619 a = Acceleration.min(a, aCoop);
620 aCoop = Cooperation.PASSIVE.cooperate(context, LateralDirectionality.RIGHT, null, desire);
621 a = Acceleration.min(a, aCoop);
622
623
624 SimpleOperationalPlan simplePlan =
625 new SimpleOperationalPlan(a, getGtu().getParameters().getParameter(ParameterTypes.DT));
626 simplePlan.minimizeAcceleration(getAcceleration(context, RelativeLane.CURRENT, Length.ZERO));
627
628
629 double dFree = getGtu().getParameters().getParameter(LmrsParameters.DFREE);
630 if (this.laneChangeSystem.initiatedLaneChange().isNone())
631 {
632 if (desire.leftIsLargerOrEqual() && desire.left() > dFree)
633 {
634 this.laneChangeSystem.initiateLaneChange(LateralDirectionality.LEFT);
635 }
636 else if (desire.right() > dFree)
637 {
638 this.laneChangeSystem.initiateLaneChange(LateralDirectionality.RIGHT);
639 }
640 }
641 else
642 {
643 if ((this.laneChangeSystem.initiatedLaneChange().isLeft() && desire.left() < dFree)
644 || (this.laneChangeSystem.initiatedLaneChange().isRight() && desire.right() < dFree))
645 {
646 this.laneChangeSystem.initiateLaneChange(LateralDirectionality.NONE);
647 }
648 }
649 simplePlan = this.laneChangeSystem.operate(context, simplePlan, getGtu().getParameters());
650 context.getIntent(TurnIndicatorStatus.class).ifPresent((d) -> getGtu().setTurnIndicatorStatus(d.object()));
651
652
653 return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), simplePlan,
654 getGtu().getParameters().getParameter(ParameterTypes.LCDUR),
655 context.getIntent(Length.class).orElseGet(() -> new DistancedObject<>(Length.ZERO, Length.ZERO)));
656 }
657 }
658
659
660 private interface AutomaticLaneChangeSystem
661 {
662
663
664
665
666
667
668
669
670
671
672 SimpleOperationalPlan operate(TacticalContextEgo context, SimpleOperationalPlan simplePlan, Parameters parameters)
673 throws OperationalPlanException, ParameterException;
674
675
676
677
678
679 LateralDirectionality initiatedLaneChange();
680
681
682
683
684
685 void initiateLaneChange(LateralDirectionality dir);
686
687 }
688
689
690 private static class SyncAndAccept implements AutomaticLaneChangeSystem
691 {
692
693 public static final ParameterTypeDuration SYNCTIME = new ParameterTypeDuration("tSync",
694 "Time after which synchronization starts.", Duration.ofSI(1.0), NumericConstraint.POSITIVE);
695
696
697 public static final ParameterTypeDuration COOPTIME = new ParameterTypeDuration("tCoop",
698 "Time after which cooperation starts (indicator).", Duration.ofSI(2.0), NumericConstraint.POSITIVE);
699
700
701 private final LaneBasedGtu gtu;
702
703
704 private final CarFollowingModel carFollowingModel;
705
706
707 private final Parameters settings;
708
709
710 private LateralDirectionality direction = LateralDirectionality.NONE;
711
712
713 private Duration initiationTime;
714
715
716
717
718
719
720
721 SyncAndAccept(final LaneBasedGtu gtu, final CarFollowingModel carFollowingModel, final Parameters settings)
722 {
723 this.gtu = gtu;
724 this.carFollowingModel = carFollowingModel;
725 this.settings = settings;
726 }
727
728 @Override
729 public SimpleOperationalPlan operate(final TacticalContextEgo context, final SimpleOperationalPlan simplePlan,
730 final Parameters parameters) throws OperationalPlanException, ParameterException
731 {
732
733 if (this.direction.isNone())
734 {
735 return simplePlan;
736 }
737
738
739 InfrastructurePerception infra =
740 this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(InfrastructurePerception.class);
741 SpeedLimits speedLimits = infra.getSpeedLimits(RelativeLane.CURRENT);
742 NeighborsPerception neighbors =
743 this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(NeighborsPerception.class);
744 if (infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, this.direction).gt0()
745 && !neighbors.isGtuAlongside(this.direction)
746 && acceptGap(neighbors.getFirstFollowers(this.direction), speedLimits, false)
747 && acceptGap(neighbors.getFirstLeaders(this.direction), speedLimits, true))
748 {
749
750 SimpleOperationalPlan plan =
751 new SimpleOperationalPlan(simplePlan.getAcceleration(), simplePlan.getDuration(), this.direction);
752 this.direction = LateralDirectionality.NONE;
753 this.initiationTime = null;
754 return plan;
755 }
756
757
758 Duration since = this.gtu.getSimulator().getSimulatorTime().minus(this.initiationTime);
759 if (since.gt(this.settings.getParameter(SYNCTIME))
760 || this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
761 {
762 PerceptionCollectable<PerceivedGtu, LaneBasedGtu> leaders =
763 neighbors.getLeaders(new RelativeLane(this.direction, 1));
764 if (!leaders.isEmpty())
765 {
766 PerceivedGtu leader = leaders.first();
767 Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings,
768 this.gtu.getSpeed(), speedLimits, this.gtu.getMaximumSpeed(), leader);
769 a = Acceleration.max(a, this.settings.getParameter(ParameterTypes.B).neg());
770 simplePlan.minimizeAcceleration(a);
771 }
772 }
773
774
775 if (since.gt(this.settings.getParameter(COOPTIME))
776 || this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
777 {
778 if (this.direction.isLeft())
779 {
780 context.addIntent(TurnIndicatorStatus.LEFT, Length.ZERO);
781 }
782 else
783 {
784 context.addIntent(TurnIndicatorStatus.RIGHT, Length.ZERO);
785 }
786 }
787
788
789 return simplePlan;
790 }
791
792
793
794
795
796
797
798
799
800 private boolean acceptGap(final Set<PerceivedGtu> neighbors, final SpeedLimits speedLimits, final boolean leaders)
801 throws ParameterException
802 {
803 for (PerceivedGtu neighbor : neighbors)
804 {
805 Speed followerSpeed = leaders ? this.gtu.getSpeed() : neighbor.getSpeed();
806 Speed maxSpeed = leaders ? this.gtu.getMaximumSpeed() : neighbor.getMaximumSpeed();
807 Speed leaderSpeed = leaders ? neighbor.getSpeed() : this.gtu.getSpeed();
808 Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings, followerSpeed,
809 speedLimits, maxSpeed, neighbor.getDistance(), leaderSpeed);
810 if (a.lt(this.settings.getParameter(ParameterTypes.B).neg()))
811 {
812 return false;
813 }
814 }
815 return true;
816 }
817
818 @Override
819 public LateralDirectionality initiatedLaneChange()
820 {
821 return this.direction;
822 }
823
824 @Override
825 public void initiateLaneChange(final LateralDirectionality dir)
826 {
827 this.direction = dir;
828 if (!dir.isNone())
829 {
830 this.initiationTime = this.gtu.getSimulator().getSimulatorTime();
831 }
832 else
833 {
834 this.initiationTime = null;
835 }
836 }
837 }
838
839 }