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