1 package org.opentrafficsim.demo.lanechange;
2
3 import java.awt.BorderLayout;
4 import java.awt.Frame;
5 import java.awt.geom.Line2D;
6 import java.lang.reflect.InvocationTargetException;
7 import java.rmi.RemoteException;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashSet;
11 import java.util.LinkedHashSet;
12 import java.util.Set;
13
14 import javax.naming.NamingException;
15 import javax.swing.JFrame;
16 import javax.swing.JPanel;
17 import javax.swing.SwingUtilities;
18 import javax.swing.event.EventListenerList;
19
20 import nl.tudelft.simulation.dsol.SimRuntimeException;
21 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
22 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
23
24 import org.djunits.unit.TimeUnit;
25 import org.djunits.unit.UNITS;
26 import org.djunits.value.vdouble.scalar.Acceleration;
27 import org.djunits.value.vdouble.scalar.DoubleScalar;
28 import org.djunits.value.vdouble.scalar.DoubleScalar.Abs;
29 import org.djunits.value.vdouble.scalar.Duration;
30 import org.djunits.value.vdouble.scalar.Length;
31 import org.djunits.value.vdouble.scalar.Speed;
32 import org.djunits.value.vdouble.scalar.Time;
33 import org.jfree.chart.ChartFactory;
34 import org.jfree.chart.ChartPanel;
35 import org.jfree.chart.JFreeChart;
36 import org.jfree.chart.StandardChartTheme;
37 import org.jfree.chart.axis.NumberAxis;
38 import org.jfree.chart.event.PlotChangeEvent;
39 import org.jfree.chart.plot.Plot;
40 import org.jfree.chart.plot.PlotOrientation;
41 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
42 import org.jfree.data.DomainOrder;
43 import org.jfree.data.general.DatasetChangeListener;
44 import org.jfree.data.general.DatasetGroup;
45 import org.jfree.data.xy.XYDataset;
46 import org.opentrafficsim.core.dsol.OTSModelInterface;
47 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
48 import org.opentrafficsim.core.geometry.OTSGeometryException;
49 import org.opentrafficsim.core.geometry.OTSPoint3D;
50 import org.opentrafficsim.core.gtu.GTUDirectionality;
51 import org.opentrafficsim.core.gtu.GTUException;
52 import org.opentrafficsim.core.gtu.GTUType;
53 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
54 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
55 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
56 import org.opentrafficsim.core.network.LongitudinalDirectionality;
57 import org.opentrafficsim.core.network.NetworkException;
58 import org.opentrafficsim.core.network.OTSNetwork;
59 import org.opentrafficsim.core.network.OTSNode;
60 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
61 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
62 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
63 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
64 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
65 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
66 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
67 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
68 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
69 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
70 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
71 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
72 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
73 import org.opentrafficsim.road.network.factory.LaneFactory;
74 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
75 import org.opentrafficsim.road.network.lane.Lane;
76 import org.opentrafficsim.road.network.lane.LaneType;
77 import org.opentrafficsim.simulationengine.SimpleSimulator;
78
79
80
81
82
83
84
85
86
87
88
89 public class LaneChangeGraph extends JFrame implements OTSModelInterface, UNITS
90 {
91
92 private static final long serialVersionUID = 20141118L;
93
94
95 static final double[] STANDARDSPEEDS = {30, 50, 80, 100, 120};
96
97
98 private GTUFollowingModelOld carFollowingModel;
99
100
101 private ChartPanel[][] charts;
102
103
104 private static final Length LOWERBOUND = new Length(-500, METER);
105
106
107 private static final Length MIDPOINT = new Length(0, METER);
108
109
110 private static final Length UPPERBOUND = new Length(500, METER);
111
112
113 private static LaneChangeGraph lcs;
114
115
116 private OTSNetwork network = new OTSNetwork("network");
117
118
119
120
121
122
123 LaneChangeGraph(final String title, final JPanel mainPanel)
124 {
125 super(title);
126 setContentPane(mainPanel);
127 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
128 this.charts = new ChartPanel[2][STANDARDSPEEDS.length];
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 public static void main(final String[] args) throws NamingException, NetworkException, SimRuntimeException,
143 GTUException, OTSGeometryException, ParameterException, OperationalPlanException
144 {
145 try
146 {
147 SwingUtilities.invokeAndWait(new Runnable()
148 {
149
150 @Override
151 public void run()
152 {
153 try
154 {
155 buildGUI(args);
156 }
157 catch (NamingException | NetworkException | SimRuntimeException | GTUException exception)
158 {
159 exception.printStackTrace();
160 }
161 }
162
163 });
164 }
165 catch (InvocationTargetException | InterruptedException exception)
166 {
167 exception.printStackTrace();
168 }
169 for (int row = 0; row < lcs.charts.length; row++)
170 {
171 LaneChangeModel laneChangeModel = 0 == row ? new Egoistic() : new Altruistic();
172 for (int index = 0; index < STANDARDSPEEDS.length; index++)
173 {
174 Speed speed = new Speed(STANDARDSPEEDS[index], KM_PER_HOUR);
175
176 double startSpeedDifference = -30;
177 double endSpeedDifference = startSpeedDifference + 60;
178 ChartData data = (ChartData) lcs.charts[row][index].getChart().getXYPlot().getDataset();
179 int beginRightKey = data.addSeries("Begin of no lane change to right");
180 int endRightKey = data.addSeries("End of no lane change to right");
181 int beginLeftKey = data.addSeries("Begin of no lane change to left");
182 int endLeftKey = data.addSeries("End of no lane change to left");
183 for (double speedDifference = startSpeedDifference; speedDifference <= endSpeedDifference; speedDifference +=
184 1)
185 {
186 Length criticalHeadway =
187 lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed, new Speed(speedDifference,
188 KM_PER_HOUR), laneChangeModel, true);
189 if (null != criticalHeadway)
190 {
191 data.addXYPair(beginRightKey, speedDifference, criticalHeadway.getInUnit(METER));
192 }
193 criticalHeadway =
194 lcs.findDecisionPoint(MIDPOINT, LaneChangeGraph.UPPERBOUND, speed, new Speed(speedDifference,
195 KM_PER_HOUR), laneChangeModel, true);
196 if (null != criticalHeadway)
197 {
198 data.addXYPair(endRightKey, speedDifference, criticalHeadway.getInUnit(METER));
199 }
200 criticalHeadway =
201 lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed, new Speed(speedDifference,
202 KM_PER_HOUR), laneChangeModel, false);
203 if (null != criticalHeadway)
204 {
205 data.addXYPair(beginLeftKey, speedDifference, criticalHeadway.getInUnit(METER));
206 }
207 else
208 {
209 lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed, new Speed(speedDifference,
210 KM_PER_HOUR), laneChangeModel, false);
211 }
212 criticalHeadway =
213 lcs.findDecisionPoint(MIDPOINT, LaneChangeGraph.UPPERBOUND, speed, new Speed(speedDifference,
214 KM_PER_HOUR), laneChangeModel, false);
215 if (null != criticalHeadway)
216 {
217 data.addXYPair(endLeftKey, speedDifference, criticalHeadway.getInUnit(METER));
218 }
219 Plot plot = lcs.charts[row][index].getChart().getPlot();
220 plot.notifyListeners(new PlotChangeEvent(plot));
221 }
222 }
223 }
224
225 }
226
227
228
229
230
231
232
233
234
235 public static void buildGUI(final String[] args) throws NamingException, NetworkException, SimRuntimeException,
236 GTUException
237 {
238 JPanel mainPanel = new JPanel(new BorderLayout());
239 lcs = new LaneChangeGraph("Lane change graphs", mainPanel);
240 TablePanel chartsPanel = new TablePanel(STANDARDSPEEDS.length, 2);
241 mainPanel.add(chartsPanel, BorderLayout.CENTER);
242 for (int index = 0; index < STANDARDSPEEDS.length; index++)
243 {
244 lcs.charts[0][index] =
245 new ChartPanel(lcs.createChart(String.format("Egoistic reference car at %.0fkm/h", STANDARDSPEEDS[index]),
246 STANDARDSPEEDS[index]));
247 chartsPanel.setCell(lcs.charts[0][index], index, 0);
248 }
249 for (int index = 0; index < STANDARDSPEEDS.length; index++)
250 {
251 lcs.charts[1][index] =
252 new ChartPanel(lcs.createChart(String.format("Altruistic reference car at %.0fkm/h", STANDARDSPEEDS[index]),
253 STANDARDSPEEDS[index]));
254 chartsPanel.setCell(lcs.charts[1][index], index, 1);
255 }
256 lcs.pack();
257 lcs.setExtendedState(Frame.MAXIMIZED_BOTH);
258 lcs.setVisible(true);
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 private Length findDecisionPoint(Length low, Length high, final Speed referenceSpeed, final Speed speedDifference,
279 final LaneChangeModel laneChangeModel, final boolean mergeRight) throws NamingException, NetworkException,
280 SimRuntimeException, GTUException, OTSGeometryException, ParameterException, OperationalPlanException
281 {
282
283 GTUType gtuType = new GTUType("car");
284 Set<GTUType> compatibility = new HashSet<>();
285 compatibility.add(gtuType);
286 LaneType laneType = new LaneType("CarLane", compatibility);
287 final Speed speedLimit = new Speed(120, KM_PER_HOUR);
288
289 Lane[] lanes =
290 LaneFactory.makeMultiLane("Road with two lanes", new OTSNode("From", new OTSPoint3D(LOWERBOUND.getSI(), 0, 0)),
291 new OTSNode("To", new OTSPoint3D(UPPERBOUND.getSI(), 0, 0)), null, 2, laneType, speedLimit, null,
292 LongitudinalDirectionality.DIR_PLUS);
293
294 Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
295 initialLongitudinalPositions.add(new DirectedLanePosition(lanes[mergeRight ? 0 : 1], new Length(0, METER),
296 GTUDirectionality.DIR_PLUS));
297
298
299
300 SimpleSimulator simpleSimulator =
301 new SimpleSimulator(new Time(0.0, SECOND), new Duration(0.0, SECOND), new Duration(3600.0, SECOND), this);
302 this.carFollowingModel =
303 new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2), new Length(2,
304 METER), new Duration(1, SECOND), 1d);
305 this.carFollowingModel =
306 new IDMOld(new Acceleration(1, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2), new Length(2,
307 METER), new Duration(1, SECOND), 1d);
308
309 BehavioralCharacteristics behavioralCharacteristics = new BehavioralCharacteristics();
310 LaneBasedIndividualGTU referenceCar =
311 new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER), new Length(2, METER), new Speed(150,
312 KM_PER_HOUR), simpleSimulator, this.network);
313 LaneBasedStrategicalPlanner strategicalPlanner =
314 new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics, new LaneBasedCFLCTacticalPlanner(
315 this.carFollowingModel, laneChangeModel, referenceCar), referenceCar);
316 referenceCar.init(strategicalPlanner, initialLongitudinalPositions, referenceSpeed);
317 Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
318 sameLaneGTUs.add(new HeadwayGTUSimple(referenceCar.getId(), referenceCar.getGTUType(), Length.ZERO, referenceCar
319 .getLength(), referenceCar.getSpeed(), null));
320
321
322 LaneMovementStep lowResult =
323 computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1], speedDifference,
324 mergeRight);
325 LaneMovementStep highResult =
326 computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, high, lanes[1], speedDifference,
327 mergeRight);
328 Length mid = null;
329 if (lowResult.getLaneChange() != highResult.getLaneChange())
330 {
331
332 final double delta = 0.1;
333 final int stepsNeeded = (int) Math.ceil(Math.log(DoubleScalar.minus(high, low).getSI() / delta) / Math.log(2));
334 for (int step = 0; step < stepsNeeded; step++)
335 {
336 Length mutableMid = low.plus(high).divideBy(2);
337 mid = mutableMid;
338 LaneMovementStep midResult =
339 computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, mid, lanes[1],
340 speedDifference, mergeRight);
341
342 if (midResult.getLaneChange() != lowResult.getLaneChange())
343 {
344 high = mid;
345 highResult = midResult;
346 }
347 else
348 {
349 low = mid;
350 lowResult = midResult;
351 }
352 }
353 }
354 else
355 {
356
357 computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1], speedDifference,
358 mergeRight);
359 }
360 return mid;
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 private LaneMovementStep computeLaneChange(final LaneBasedIndividualGTU referenceCar,
382 final Collection<Headway> sameLaneGTUs, final Speed speedLimit, final LaneChangeModel laneChangeModel,
383 final Length otherCarPosition, final Lane otherCarLane, final Speed deltaV, final boolean mergeRight)
384 throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException,
385 ParameterException, OperationalPlanException
386 {
387 Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
388 initialLongitudinalPositions
389 .add(new DirectedLanePosition(otherCarLane, otherCarPosition, GTUDirectionality.DIR_PLUS));
390 BehavioralCharacteristics behavioralCharacteristics = new BehavioralCharacteristics();
391 LaneBasedIndividualGTU otherCar =
392 new LaneBasedIndividualGTU("otherCar", referenceCar.getGTUType(), new Length(4, METER), new Length(2, METER),
393 new Speed(150, KM_PER_HOUR), referenceCar.getSimulator(), this.network);
394 LaneBasedStrategicalPlanner strategicalPlanner =
395 new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics, new LaneBasedCFLCTacticalPlanner(
396 this.carFollowingModel, laneChangeModel, otherCar), otherCar);
397 otherCar.init(strategicalPlanner, initialLongitudinalPositions, referenceCar.getSpeed().plus(deltaV));
398 Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
399 Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
400 Length referenceCarPosition =
401 referenceCar.position(referenceCar.positions(referenceCar.getReference()).keySet().iterator().next(),
402 referenceCar.getReference());
403 Headway otherHeadwayGTU =
404 new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(), otherCarPosition.minus(referenceCarPosition),
405 otherCar.getLength(), otherCar.getSpeed(), null);
406 if (mergeRight)
407 {
408 preferredLaneGTUs.add(otherHeadwayGTU);
409 }
410 else
411 {
412 sameLaneGTUs.add(otherHeadwayGTU);
413 }
414
415
416 LaneMovementStep result =
417 laneChangeModel.computeLaneChangeAndAcceleration(referenceCar, sameLaneGTUs, mergeRight ? preferredLaneGTUs
418 : null, mergeRight ? null : nonPreferredLaneGTUs, speedLimit, new Acceleration(0.3, METER_PER_SECOND_2),
419 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
420
421 sameLaneGTUs.remove(otherHeadwayGTU);
422 otherCar.destroy();
423 return result;
424 }
425
426
427
428
429
430
431 private JFreeChart createChart(final String caption, final double speed)
432 {
433 ChartFactory.setChartTheme(new StandardChartTheme("JFree/Shadow", false));
434 ChartData chartData = new ChartData();
435 JFreeChart chartPanel =
436 ChartFactory.createXYLineChart(caption, "", "", chartData, PlotOrientation.VERTICAL, false, false, false);
437 NumberAxis xAxis = new NumberAxis("\u2192 " + "\u0394v (other car speed minus reference car speed) [km/h]");
438 xAxis.setAutoRangeIncludesZero(true);
439 double minimumDifference = -30;
440 xAxis.setRange(minimumDifference, minimumDifference + 60);
441 xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
442 NumberAxis yAxis = new NumberAxis("\u2192 " + "gross headway (\u0394s) [m]");
443 yAxis.setAutoRangeIncludesZero(true);
444 yAxis.setRange(LOWERBOUND.getSI(), UPPERBOUND.getSI());
445 yAxis.setInverted(true);
446 chartPanel.getXYPlot().setDomainAxis(xAxis);
447 chartPanel.getXYPlot().setRangeAxis(yAxis);
448 final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chartPanel.getXYPlot().getRenderer();
449 renderer.setBaseLinesVisible(true);
450 renderer.setBaseShapesVisible(false);
451 renderer.setBaseShape(new Line2D.Float(0, 0, 0, 0));
452 return chartPanel;
453 }
454
455
456 @Override
457 public void constructModel(
458 final SimulatorInterface<Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> simulator)
459 throws SimRuntimeException, RemoteException
460 {
461
462 }
463
464
465 @Override
466 public final SimulatorInterface<Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
467 throws RemoteException
468 {
469 return null;
470 }
471
472 }
473
474
475 class ChartData implements XYDataset
476 {
477
478
479 private ArrayList<ArrayList<Double>> xValues = new ArrayList<>();
480
481
482 private ArrayList<ArrayList<Double>> yValues = new ArrayList<>();
483
484
485 private ArrayList<String> seriesKeys = new ArrayList<>();
486
487
488 private transient EventListenerList listenerList = new EventListenerList();
489
490
491 private DatasetGroup datasetGroup = null;
492
493
494
495
496
497
498 public final int addSeries(final String seriesName)
499 {
500 this.xValues.add(new ArrayList<Double>());
501 this.yValues.add(new ArrayList<Double>());
502 this.seriesKeys.add(seriesName);
503 return this.xValues.size() - 1;
504 }
505
506
507
508
509
510
511
512 public final void addXYPair(final int seriesKey, final double x, final double y)
513 {
514 this.xValues.get(seriesKey).add(x);
515 this.yValues.get(seriesKey).add(y);
516 }
517
518
519 @Override
520 public final int getSeriesCount()
521 {
522 return this.seriesKeys.size();
523 }
524
525
526 @Override
527 public final Comparable<?> getSeriesKey(final int series)
528 {
529 return this.seriesKeys.get(series);
530 }
531
532
533 @Override
534 public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
535 {
536 return this.seriesKeys.indexOf(seriesKey);
537 }
538
539
540 @Override
541 public final void addChangeListener(final DatasetChangeListener listener)
542 {
543 this.listenerList.add(DatasetChangeListener.class, listener);
544 }
545
546
547 @Override
548 public final void removeChangeListener(final DatasetChangeListener listener)
549 {
550 this.listenerList.remove(DatasetChangeListener.class, listener);
551 }
552
553
554 @Override
555 public final DatasetGroup getGroup()
556 {
557 return this.datasetGroup;
558 }
559
560
561 @Override
562 public final void setGroup(final DatasetGroup group)
563 {
564 this.datasetGroup = group;
565 }
566
567
568 @Override
569 public final DomainOrder getDomainOrder()
570 {
571 return DomainOrder.ASCENDING;
572 }
573
574
575 @Override
576 public final int getItemCount(final int series)
577 {
578 return this.xValues.get(series).size();
579 }
580
581
582 @Override
583 public final Number getX(final int series, final int item)
584 {
585 return this.xValues.get(series).get(item);
586 }
587
588
589 @Override
590 public final double getXValue(final int series, final int item)
591 {
592 return this.xValues.get(series).get(item);
593 }
594
595
596 @Override
597 public final Number getY(final int series, final int item)
598 {
599 return this.yValues.get(series).get(item);
600 }
601
602
603 @Override
604 public final double getYValue(final int series, final int item)
605 {
606 return this.yValues.get(series).get(item);
607 }
608
609 }