View Javadoc
1   package org.opentrafficsim.demo.carFollowing;
2   
3   import java.awt.Frame;
4   import java.awt.geom.Rectangle2D;
5   import java.rmi.RemoteException;
6   import java.util.ArrayList;
7   import java.util.LinkedHashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Random;
11  
12  import javax.naming.NamingException;
13  import javax.swing.JPanel;
14  import javax.swing.SwingUtilities;
15  
16  import nl.tudelft.simulation.dsol.SimRuntimeException;
17  import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
18  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
19  
20  import org.djunits.unit.TimeUnit;
21  import org.djunits.value.vdouble.scalar.DoubleScalar;
22  import org.opentrafficsim.core.OTS_SCALAR;
23  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
24  import org.opentrafficsim.core.dsol.OTSModelInterface;
25  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
26  import org.opentrafficsim.core.geometry.OTSGeometryException;
27  import org.opentrafficsim.core.geometry.OTSPoint3D;
28  import org.opentrafficsim.core.gtu.GTUException;
29  import org.opentrafficsim.core.gtu.GTUType;
30  import org.opentrafficsim.core.gtu.animation.GTUColorer;
31  import org.opentrafficsim.core.network.LongitudinalDirectionality;
32  import org.opentrafficsim.core.network.NetworkException;
33  import org.opentrafficsim.core.network.OTSNode;
34  import org.opentrafficsim.core.network.route.CompleteRoute;
35  import org.opentrafficsim.graphs.TrajectoryPlot;
36  import org.opentrafficsim.road.car.LaneBasedIndividualCar;
37  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
38  import org.opentrafficsim.road.gtu.following.GTUFollowingModel;
39  import org.opentrafficsim.road.gtu.following.IDM;
40  import org.opentrafficsim.road.gtu.following.IDMPlus;
41  import org.opentrafficsim.road.gtu.lane.changing.AbstractLaneChangeModel;
42  import org.opentrafficsim.road.gtu.lane.changing.Egoistic;
43  import org.opentrafficsim.road.network.factory.LaneFactory;
44  import org.opentrafficsim.road.network.lane.CrossSectionLink;
45  import org.opentrafficsim.road.network.lane.Lane;
46  import org.opentrafficsim.road.network.lane.LaneType;
47  import org.opentrafficsim.road.network.lane.Sensor;
48  import org.opentrafficsim.road.network.lane.SinkSensor;
49  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
50  import org.opentrafficsim.road.network.route.CompleteLaneBasedRouteNavigator;
51  import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
52  import org.opentrafficsim.simulationengine.properties.AbstractProperty;
53  import org.opentrafficsim.simulationengine.properties.ProbabilityDistributionProperty;
54  import org.opentrafficsim.simulationengine.properties.PropertyException;
55  import org.opentrafficsim.simulationengine.properties.SelectionProperty;
56  
57  /**
58   * Demonstrate the Trajectories plot.
59   * <p>
60   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
61   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
62   * <p>
63   * $LastChangedDate: 2015-09-24 19:14:49 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1430 $, by $Author: averbraeck $,
64   * initial version 17 dec. 2014 <br>
65   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
66   */
67  public class Trajectories extends AbstractWrappableAnimation
68  {
69      /** the model. */
70      private TrajectoriesModel model;
71  
72      /** Create a Trajectories simulation. */
73      public Trajectories()
74      {
75          try
76          {
77              this.properties.add(new SelectionProperty("Car following model", "<html>The car following model determines "
78                  + "the acceleration that a vehicle will make taking into account nearby vehicles, "
79                  + "infrastructural restrictions (e.g. speed limit, curvature of the road) "
80                  + "capabilities of the vehicle and personality of the driver.</html>", new String[]{"IDM", "IDM+"}, 1,
81                  false, 10));
82              this.properties.add(new ProbabilityDistributionProperty("Traffic composition",
83                  "<html>Mix of passenger cars and trucks</html>", new String[]{"passenger car", "truck"}, new Double[]{0.8,
84                      0.2}, false, 9));
85          }
86          catch (PropertyException exception)
87          {
88              exception.printStackTrace();
89          }
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public final void stopTimersThreads()
95      {
96          super.stopTimersThreads();
97          this.model = null;
98      }
99  
100     /**
101      * Main program.
102      * @param args String[]; the command line arguments (not used)
103      * @throws SimRuntimeException on ???
104      */
105     public static void main(final String[] args) throws SimRuntimeException
106     {
107         // Create the simulation and wrap its panel in a JFrame. It does not get much easier/shorter than this...
108         SwingUtilities.invokeLater(new Runnable()
109         {
110             @Override
111             public void run()
112             {
113                 try
114                 {
115                     Trajectories trajectories = new Trajectories();
116                     trajectories.buildAnimator(new Time.Abs(0.0, SECOND), new Time.Rel(0.0, SECOND), new Time.Rel(3600.0,
117                         SECOND), trajectories.getProperties(), null, true);
118                 }
119                 catch (SimRuntimeException | NamingException exception)
120                 {
121                     exception.printStackTrace();
122                 }
123             }
124         });
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     protected final Rectangle2D.Double makeAnimationRectangle()
130     {
131         return new Rectangle2D.Double(0, -100, 5000, 200);
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     protected final OTSModelInterface makeModel(final GTUColorer colorer)
137     {
138         this.model = new TrajectoriesModel(this.savedUserModifiedProperties, colorer);
139         return this.model;
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     protected final JPanel makeCharts()
145     {
146         TablePanel charts = new TablePanel(1, 1);
147         Time.Rel sampleInterval = new Time.Rel(0.5, SECOND);
148         List<Lane> path = new ArrayList<Lane>();
149         path.add(this.model.getLane());
150         TrajectoryPlot tp = new TrajectoryPlot("Trajectory Plot", sampleInterval, path);
151         tp.setTitle("Density Contour Graph");
152         tp.setExtendedState(Frame.MAXIMIZED_BOTH);
153         this.model.setTrajectoryPlot(tp);
154         charts.setCell(tp.getContentPane(), 0, 0);
155         return charts;
156     }
157 
158     /** {@inheritDoc} */
159     @Override
160     public final String shortName()
161     {
162         return "Trajectory plot";
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public final String description()
168     {
169         return "<html><H1>Trajectories</H1>"
170             + "Simulation of a single lane road of 5 km length. Vechicles are generated at a constant rate of "
171             + "1500 veh/hour. At time 300s a blockade is inserted at position 4km; this blockade is removed at time "
172             + "420s. This blockade simulates a bridge opening.<br>"
173             + "The blockade causes a traffic jam that slowly dissolves after the blockade is removed.<br />"
174             + "Output is a Trajectory plots.</html>";
175     }
176 
177 }
178 
179 /**
180  * Simulate a single lane road of 5 km length. Vehicles are generated at a constant rate of 1500 veh/hour. At time 300s a
181  * blockade is inserted at position 4 km; this blockade is removed at time 500s. The used car following algorithm is IDM+ <a
182  * href="http://opentrafficsim.org/downloads/MOTUS%20reference.pdf"><i>Integrated Lane Change Model with Relaxation and
183  * Synchronization</i>, by Wouter J. Schakel, Victor L. Knoop and Bart van Arem, 2012</a>. <br>
184  * Output is a trajectory plot with simulation time along the horizontal axis and distance along the road along the vertical
185  * axis.
186  * <p>
187  * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
188  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
189  * <p>
190  * $LastChangedDate: 2015-09-24 19:14:49 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1430 $, by $Author: averbraeck $,
191  * initial version ug 1, 2014 <br>
192  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
193  */
194 class TrajectoriesModel implements OTSModelInterface, OTS_SCALAR
195 {
196     /** */
197     private static final long serialVersionUID = 20140815L;
198 
199     /** the simulator. */
200     private OTSDEVSSimulatorInterface simulator;
201 
202     /** the headway (inter-vehicle time). */
203     private Time.Rel headway;
204 
205     /** number of cars created. */
206     private int carsCreated = 0;
207 
208     /** Type of all GTUs. */
209     private GTUType gtuType = GTUType.makeGTUType("Car");
210 
211     /** the car following model, e.g. IDM Plus for cars. */
212     private GTUFollowingModel carFollowingModelCars;
213 
214     /** the car following model, e.g. IDM Plus for trucks. */
215     private GTUFollowingModel carFollowingModelTrucks;
216 
217     /** The probability that the next generated GTU is a passenger car. */
218     private double carProbability;
219 
220     /** The lane change model. */
221     private AbstractLaneChangeModel laneChangeModel = new Egoistic();
222 
223     /** The blocking car. */
224     private LaneBasedIndividualCar block = null;
225 
226     /** minimum distance. */
227     private Length.Rel minimumDistance = new Length.Rel(0, METER);
228 
229     /** maximum distance. */
230     private Length.Rel maximumDistance = new Length.Rel(5000, METER);
231 
232     /** The Lane containing the simulated Cars. */
233     private Lane lane;
234 
235     /** the speed limit. */
236     private Speed.Abs speedLimit = new Speed.Abs(100, KM_PER_HOUR);
237 
238     /** the trajectory plot. */
239     private TrajectoryPlot trajectoryPlot;
240 
241     /** User settable properties. */
242     private ArrayList<AbstractProperty<?>> properties = null;
243 
244     /** The random number generator used to decide what kind of GTU to generate. */
245     private Random randomGenerator = new Random(12345);
246 
247     /** The GTUColorer for the generated vehicles. */
248     private final GTUColorer gtuColorer;
249 
250     /**
251      * @param properties ArrayList&lt;AbstractProperty&lt;?&gt;&gt;; the properties
252      * @param gtuColorer the default and initial GTUColorer, e.g. a DefaultSwitchableTUColorer.
253      */
254     public TrajectoriesModel(final ArrayList<AbstractProperty<?>> properties, final GTUColorer gtuColorer)
255     {
256         this.properties = properties;
257         this.gtuColorer = gtuColorer;
258     }
259 
260     /** {@inheritDoc} */
261     @Override
262     public final void constructModel(
263         final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
264         throws SimRuntimeException, RemoteException
265     {
266         this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
267         OTSNode from = new OTSNode("From", new OTSPoint3D(getMinimumDistance().getSI(), 0, 0));
268         OTSNode to = new OTSNode("To", new OTSPoint3D(getMaximumDistance().getSI(), 0, 0));
269         OTSNode end = new OTSNode("End", new OTSPoint3D(getMaximumDistance().getSI() + 50.0, 0, 0));
270         LaneType laneType = new LaneType("CarLane");
271         laneType.addCompatibility(this.gtuType);
272         try
273         {
274             this.lane = LaneFactory.makeLane("Lane", from, to, null, laneType, this.speedLimit, this.simulator);
275             CrossSectionLink endLink = LaneFactory.makeLink("endLink", to, end, null);
276             // No overtaking, single (sink) lane
277             Lane sinkLane =
278                 new Lane(endLink, "sinkLane", this.lane.getLateralCenterPosition(1.0), this.lane
279                     .getLateralCenterPosition(1.0), this.lane.getWidth(1.0), this.lane.getWidth(1.0), laneType,
280                     LongitudinalDirectionality.FORWARD, this.speedLimit, new OvertakingConditions.None());
281             Sensor sensor = new SinkSensor(sinkLane, new Length.Rel(10.0, METER), this.simulator);
282             sinkLane.addSensor(sensor, GTUType.ALL);
283         }
284         catch (NamingException | NetworkException | OTSGeometryException exception1)
285         {
286             exception1.printStackTrace();
287         }
288 
289         for (AbstractProperty<?> p : this.properties)
290         {
291             if (p instanceof SelectionProperty)
292             {
293                 SelectionProperty sp = (SelectionProperty) p;
294                 if ("Car following model".equals(sp.getShortName()))
295                 {
296                     String modelName = sp.getValue();
297                     if (modelName.equals("IDM"))
298                     {
299                         this.carFollowingModelCars =
300                             new IDM(new Acceleration.Abs(1, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
301                                 METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
302                         this.carFollowingModelTrucks =
303                             new IDM(new Acceleration.Abs(0.5, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
304                                 METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
305                     }
306                     else if (modelName.equals("IDM+"))
307                     {
308                         this.carFollowingModelCars =
309                             new IDMPlus(new Acceleration.Abs(1, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
310                                 METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
311                         this.carFollowingModelTrucks =
312                             new IDMPlus(new Acceleration.Abs(0.5, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
313                                 METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
314                     }
315                     else
316                     {
317                         throw new Error("Car following model " + modelName + " not implemented");
318                     }
319                 }
320                 else
321                 {
322                     throw new Error("Unhandled SelectionProperty " + p.getShortName());
323                 }
324             }
325             else if (p instanceof ProbabilityDistributionProperty)
326             {
327                 ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) p;
328                 String modelName = p.getShortName();
329                 if (modelName.equals("Traffic composition"))
330                 {
331                     this.carProbability = pdp.getValue()[0];
332                 }
333                 else
334                 {
335                     throw new Error("Unhandled ProbabilityDistributionProperty " + p.getShortName());
336                 }
337             }
338             else
339             {
340                 throw new Error("Unhandled property: " + p);
341             }
342         }
343 
344         // 1500 [vehicles / hour] == 2.4s headway
345         this.headway = new Time.Rel(3600.0 / 1500.0, SECOND);
346 
347         try
348         {
349             // Schedule creation of the first car (this will re-schedule itself one headway later, etc.).
350             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.0, SECOND), this, this, "generateCar", null);
351             // Create a block at t = 5 minutes
352             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(300, SECOND), this, this, "createBlock", null);
353             // Remove the block at t = 7 minutes
354             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(420, SECOND), this, this, "removeBlock", null);
355             // Schedule regular updates of the graph
356             for (int t = 1; t <= 1800; t++)
357             {
358                 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(t - 0.001, SECOND), this, this, "drawGraph",
359                     null);
360             }
361         }
362         catch (SimRuntimeException exception)
363         {
364             exception.printStackTrace();
365         }
366     }
367 
368     /**
369      * Set up the block.
370      * @throws NamingException on error during adding of animation handler
371      * @throws SimRuntimeException on ???
372      * @throws NetworkException on network inconsistency
373      * @throws GTUException if creation of the GTU fails
374      */
375     protected final void createBlock() throws NamingException, SimRuntimeException, NetworkException,
376         GTUException
377     {
378         Length.Rel initialPosition = new Length.Rel(4000, METER);
379         Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
380         initialPositions.put(this.getLane(), initialPosition);
381         this.block =
382             new LaneBasedIndividualCar("999999", this.gtuType, this.carFollowingModelCars, this.laneChangeModel,
383                 initialPositions, new Speed.Abs(0, KM_PER_HOUR), new Length.Rel(4, METER), new Length.Rel(1.8, METER),
384                 new Speed.Abs(0, KM_PER_HOUR), new CompleteLaneBasedRouteNavigator(new CompleteRoute("")), this.simulator,
385                 DefaultCarAnimation.class, this.gtuColorer);
386     }
387 
388     /**
389      * Remove the block.
390      */
391     protected final void removeBlock()
392     {
393         this.block.destroy();
394         this.block = null;
395     }
396 
397     /**
398      * Generate cars at a fixed rate (implemented by re-scheduling this method).
399      */
400     protected final void generateCar()
401     {
402         boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
403         Length.Rel initialPosition = new Length.Rel(0, METER);
404         Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
405         initialPositions.put(this.getLane(), initialPosition);
406         Speed.Abs initialSpeed = new Speed.Abs(100, KM_PER_HOUR);
407         try
408         {
409             Length.Rel vehicleLength = new Length.Rel(generateTruck ? 15 : 4, METER);
410             GTUFollowingModel gtuFollowingModel = generateTruck ? this.carFollowingModelTrucks : this.carFollowingModelCars;
411             if (null == gtuFollowingModel)
412             {
413                 throw new Error("gtuFollowingModel is null");
414             }
415             new LaneBasedIndividualCar("" + (++this.carsCreated), this.gtuType, generateTruck ? this.carFollowingModelTrucks
416                 : this.carFollowingModelCars, this.laneChangeModel, initialPositions, initialSpeed, vehicleLength,
417                 new Length.Rel(1.8, METER), new Speed.Abs(200, KM_PER_HOUR), new CompleteLaneBasedRouteNavigator(
418                     new CompleteRoute("")), this.simulator, DefaultCarAnimation.class, this.gtuColorer);
419             // Re-schedule this method after headway seconds
420             this.simulator.scheduleEventRel(this.headway, this, this, "generateCar", null);
421         }
422         catch (SimRuntimeException | NamingException | NetworkException | GTUException exception)
423         {
424             exception.printStackTrace();
425         }
426     }
427 
428     /** {@inheritDoc} */
429     @Override
430     public final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
431         throws RemoteException
432     {
433         return this.simulator;
434     }
435 
436     /**
437      * Notify the contour plots that the underlying data has changed.
438      */
439     protected final void drawGraph()
440     {
441         this.trajectoryPlot.reGraph();
442     }
443 
444     /**
445      * @return minimum distance of the simulation
446      */
447     public final Length.Rel getMinimumDistance()
448     {
449         return this.minimumDistance;
450     }
451 
452     /**
453      * @return maximum distance of the simulation
454      */
455     public final Length.Rel getMaximumDistance()
456     {
457         return this.maximumDistance;
458     }
459 
460     /**
461      * @param trajectoryPlot TrajectoryPlot
462      */
463     public final void setTrajectoryPlot(final TrajectoryPlot trajectoryPlot)
464     {
465         this.trajectoryPlot = trajectoryPlot;
466     }
467 
468     /**
469      * @return lane.
470      */
471     public Lane getLane()
472     {
473         return this.lane;
474     }
475 
476 }