View Javadoc
1   package org.opentrafficsim.demo.strategies;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.Dimension;
6   import java.util.ArrayList;
7   import java.util.Hashtable;
8   import java.util.Iterator;
9   import java.util.List;
10  import java.util.Map;
11  
12  import javax.naming.NamingException;
13  import javax.swing.Box;
14  import javax.swing.BoxLayout;
15  import javax.swing.JLabel;
16  import javax.swing.JSlider;
17  import javax.swing.SwingConstants;
18  import javax.swing.border.EmptyBorder;
19  import javax.swing.event.ChangeEvent;
20  import javax.swing.event.ChangeListener;
21  
22  import org.djunits.unit.DirectionUnit;
23  import org.djunits.unit.SpeedUnit;
24  import org.djunits.value.vdouble.scalar.Acceleration;
25  import org.djunits.value.vdouble.scalar.Direction;
26  import org.djunits.value.vdouble.scalar.Duration;
27  import org.djunits.value.vdouble.scalar.Length;
28  import org.djunits.value.vdouble.scalar.Speed;
29  import org.djutils.cli.CliException;
30  import org.djutils.cli.CliUtil;
31  import org.djutils.draw.curve.Arc2d;
32  import org.djutils.draw.curve.OffsetCurve2d;
33  import org.djutils.draw.point.DirectedPoint2d;
34  import org.djutils.draw.point.Point2d;
35  import org.djutils.event.Event;
36  import org.djutils.event.EventListener;
37  import org.opentrafficsim.animation.colorer.Colorer;
38  import org.opentrafficsim.animation.colorer.FixedColorer;
39  import org.opentrafficsim.animation.data.gtu.AccelerationGtuColorer;
40  import org.opentrafficsim.animation.data.gtu.DesiredHeadwayGtuColorer;
41  import org.opentrafficsim.animation.data.gtu.IncentiveGtuColorer;
42  import org.opentrafficsim.animation.data.gtu.SocialPressureGtuColorer;
43  import org.opentrafficsim.animation.data.gtu.SpeedGtuColorer;
44  import org.opentrafficsim.base.OtsRuntimeException;
45  import org.opentrafficsim.base.logger.Logger;
46  import org.opentrafficsim.base.parameters.ParameterException;
47  import org.opentrafficsim.base.parameters.ParameterTypes;
48  import org.opentrafficsim.core.definitions.Defaults;
49  import org.opentrafficsim.core.definitions.DefaultsNl;
50  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
51  import org.opentrafficsim.core.gtu.Gtu;
52  import org.opentrafficsim.core.gtu.GtuCharacteristics;
53  import org.opentrafficsim.core.gtu.GtuException;
54  import org.opentrafficsim.core.gtu.GtuType;
55  import org.opentrafficsim.core.network.Link;
56  import org.opentrafficsim.core.network.Network;
57  import org.opentrafficsim.core.network.NetworkException;
58  import org.opentrafficsim.core.network.Node;
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.strategical.LaneBasedStrategicalPlanner;
63  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
64  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlanner;
65  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
66  import org.opentrafficsim.road.gtu.tactical.lmrs.IncentiveSocioSpeed;
67  import org.opentrafficsim.road.gtu.tactical.lmrs.Lmrs;
68  import org.opentrafficsim.road.gtu.tactical.lmrs.LmrsFactory;
69  import org.opentrafficsim.road.gtu.tactical.lmrs.LmrsFactory.Setting;
70  import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsParameters;
71  import org.opentrafficsim.road.network.CrossSectionLink;
72  import org.opentrafficsim.road.network.Lane;
73  import org.opentrafficsim.road.network.LaneKeepingPolicy;
74  import org.opentrafficsim.road.network.LanePosition;
75  import org.opentrafficsim.road.network.RoadNetwork;
76  import org.opentrafficsim.road.network.factory.LaneFactory;
77  import org.opentrafficsim.road.network.speed.LaneSpeedLimits;
78  import org.opentrafficsim.swing.gui.OtsSimulationPanel;
79  import org.opentrafficsim.swing.gui.OtsSimulationPanel.DemoPanelPosition;
80  import org.opentrafficsim.swing.gui.OtsSimulationPanelDecorator;
81  import org.opentrafficsim.swing.script.AbstractSimulationScript;
82  
83  import nl.tudelft.simulation.dsol.SimRuntimeException;
84  import nl.tudelft.simulation.jstats.distributions.DistNormal;
85  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
86  import nl.tudelft.simulation.jstats.streams.StreamInterface;
87  import picocli.CommandLine.Option;
88  
89  /**
90   * Demo of lane change strategies.
91   * <p>
92   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
93   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
94   * </p>
95   * @author Wouter Schakel
96   */
97  public class StrategiesDemo extends AbstractSimulationScript
98  {
99      /** Factory. */
100     private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalRoutePlanner> factory;
101 
102     /** GTU id number. */
103     private int gtuIdNum = 0;
104 
105     /** Number of GTUs. */
106     private int gtuNum = 60;
107 
108     /** Random stream. */
109     private final StreamInterface stream = new MersenneTwister(1L);
110 
111     /** Queue of cumulative odometers. */
112     private final List<Double> queue = new ArrayList<>();
113 
114     /** Lane change listener. */
115     private KmplcListener kmplcListener;
116 
117     /** Truck fraction. */
118     private double truckFraction = 0.1;
119 
120     /** Next GTU type. */
121     private GtuType nextGtuType;
122 
123     /** Truck length. */
124     private Length truckLength;
125 
126     /** Truck mid. */
127     private Length truckMid;
128 
129     /** Car length. */
130     @Option(names = "--length", description = "Length", defaultValue = "2m")
131     private Length carLength;
132 
133     /** Car mid. */
134     private Length carMid;
135 
136     /**
137      * Constructor.
138      */
139     protected StrategiesDemo()
140     {
141         super("Strategies demo", "Demo of driving strategies in LMRS.");
142         try
143         {
144             CliUtil.changeOptionDefault(this, "simulationTime", "3600000s");
145         }
146         catch (NoSuchFieldException | IllegalStateException | IllegalArgumentException | CliException exception)
147         {
148             throw new OtsRuntimeException(exception);
149         }
150     }
151 
152     @Override
153     protected OtsSimulationPanelDecorator getDecorator()
154     {
155         return new OtsSimulationPanelDecorator()
156         {
157             @Override
158             public void setupDemo(final OtsSimulationPanel simulationPanel, final Network network)
159             {
160                 StrategiesDemo.this.setupDemo(simulationPanel, network);
161             }
162 
163             @Override
164             public List<Colorer<? super Gtu>> getGtuColorers()
165             {
166                 return List.of(new FixedColorer<>(Color.BLUE, "Blue"), new SpeedGtuColorer(), new AccelerationGtuColorer(),
167                         new SocialPressureGtuColorer(), new DesiredHeadwayGtuColorer(Duration.ofSI(0.5), Duration.ofSI(1.6)),
168                         new IncentiveGtuColorer(IncentiveSocioSpeed.class));
169             }
170         };
171     }
172 
173     /**
174      * Main method.
175      * @param args arguments
176      */
177     public static void main(final String[] args)
178     {
179         StrategiesDemo demo = new StrategiesDemo();
180         CliUtil.execute(demo, args);
181         try
182         {
183             demo.start();
184         }
185         catch (Exception ex)
186         {
187             ex.printStackTrace();
188         }
189     }
190 
191     /**
192      * Setup a demo panel within the simulation panel. The default implementation does nothing.
193      * @param simulationPanel simulation panel
194      * @param network network
195      */
196     private void setupDemo(final OtsSimulationPanel simulationPanel, final Network network)
197     {
198         // demo panel
199         simulationPanel.createDemoPanel(DemoPanelPosition.RIGHT);
200         simulationPanel.getDemoPanel().setBorder(new EmptyBorder(10, 10, 10, 10));
201         simulationPanel.getDemoPanel().setLayout(new BoxLayout(simulationPanel.getDemoPanel(), BoxLayout.Y_AXIS));
202         simulationPanel.getDemoPanel().setPreferredSize(new Dimension(300, 300));
203 
204         // text
205         JLabel textLabel = new JLabel("<html><p align=\"justify\">"
206                 + "Adjust the sliders below to change the ego-speed sensitivity and socio-speed sensitivity of the drivers, "
207                 + "and observe how traffic is affected." + "</html>"); // Detailed instructions are in the attached read-me.
208         textLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
209         simulationPanel.getDemoPanel().add(textLabel);
210 
211         // spacer
212         simulationPanel.getDemoPanel().add(Box.createVerticalStrut(20));
213 
214         // number of vehicles
215         JLabel gtuLabel = new JLabel("<html>Number of vehicles</html>");
216         gtuLabel.setHorizontalAlignment(SwingConstants.CENTER);
217         gtuLabel.setPreferredSize(new Dimension(200, 0));
218         gtuLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
219         simulationPanel.getDemoPanel().add(gtuLabel);
220         JSlider gtuSlider = new JSlider(0, 120, this.gtuNum);
221         gtuSlider.setMinorTickSpacing(10);
222         gtuSlider.setMajorTickSpacing(30);
223         gtuSlider.setPaintTicks(true);
224         gtuSlider.setPaintLabels(true);
225         gtuSlider.setToolTipText("<html>Number of vehicles</html>");
226         gtuSlider.addChangeListener(new ChangeListener()
227         {
228             @SuppressWarnings("synthetic-access")
229             @Override
230             public void stateChanged(final ChangeEvent e)
231             {
232                 StrategiesDemo.this.gtuNum = ((JSlider) e.getSource()).getValue();
233                 if (!StrategiesDemo.this.getSimulator().isStartingOrRunning())
234                 {
235                     // StrategiesDemo.this.checkVehicleNumber();
236                     simulationPanel.getDemoPanel().getParent().repaint();
237                 }
238             }
239         });
240         simulationPanel.getDemoPanel().add(gtuSlider);
241 
242         // spacer
243         simulationPanel.getDemoPanel().add(Box.createVerticalStrut(20));
244 
245         // ego
246         JLabel egoLabel = new JLabel("<html>Ego-speed sensitivity<sup>-1</sup> [km/h]</html>");
247         egoLabel.setHorizontalAlignment(SwingConstants.CENTER);
248         egoLabel.setPreferredSize(new Dimension(200, 0));
249         egoLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
250         simulationPanel.getDemoPanel().add(egoLabel);
251         int egoSteps = 70;
252         JSlider egoSlider = new JSlider(0, egoSteps, egoSteps / 2);
253         egoSlider.setMinorTickSpacing(2);
254         egoSlider.setMajorTickSpacing(egoSteps / 5);
255         egoSlider.setPaintTicks(true);
256         egoSlider.setPaintLabels(true);
257         Hashtable<Integer, JLabel> egoTable = new Hashtable<>();
258         for (int i = 0; i <= egoSteps; i += egoSteps / 5)
259         {
260             egoTable.put(i, new JLabel(String.format("%d", i)));
261         }
262         egoSlider.setLabelTable(egoTable);
263         egoSlider.setToolTipText("<html>Ego-speed sensitivity as 1/<i>v<sub>gain</sub></i></html>");
264         egoSlider.addChangeListener(new ChangeListener()
265         {
266             /** Whether exception was logged. */
267             private boolean loggedException = false;
268 
269             @Override
270             public void stateChanged(final ChangeEvent e)
271             {
272                 double v = Math.max(((JSlider) e.getSource()).getValue(), 0.01);
273                 Speed vGain = new Speed(v, SpeedUnit.KM_PER_HOUR);
274                 for (Gtu gtu : getNetwork().getGTUs())
275                 {
276                     if (gtu.getType().isOfType(DefaultsNl.CAR))
277                     {
278                         try
279                         {
280                             gtu.getParameters().setClaimedParameter(LmrsParameters.VGAIN, vGain, egoSlider);
281                         }
282                         catch (ParameterException exception)
283                         {
284                             if (!this.loggedException)
285                             {
286                                 Logger.ots().error("Unable to set VGAIN on GTU.");
287                                 this.loggedException = true;
288                             }
289                         }
290                     }
291                 }
292             }
293         });
294         simulationPanel.getDemoPanel().add(egoSlider);
295 
296         // spacer
297         simulationPanel.getDemoPanel().add(Box.createVerticalStrut(20));
298 
299         // socio
300         JLabel socioLabel = new JLabel("Socio-speed sensitivity [-]");
301         socioLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
302         simulationPanel.getDemoPanel().add(socioLabel);
303         int socioSteps = 20;
304         JSlider socioSlider = new JSlider(0, socioSteps, socioSteps / 2);
305         socioSlider.setMinorTickSpacing(1);
306         socioSlider.setMajorTickSpacing(socioSteps / 5);
307         socioSlider.setPaintTicks(true);
308         socioSlider.setPaintLabels(true);
309         Hashtable<Integer, JLabel> socioTable = new Hashtable<>();
310         for (int i = 0; i <= socioSteps; i += socioSteps / 5)
311         {
312             double val = (double) i / socioSteps;
313             socioTable.put(i, new JLabel(String.format("%.2f", val)));
314         }
315         socioSlider.setLabelTable(socioTable);
316         socioSlider.setToolTipText("Socio-speed sensitivity between 0 and 1");
317         socioSlider.addChangeListener(new ChangeListener()
318         {
319             /** Whether exception was logged. */
320             private boolean loggedException = false;
321 
322             @Override
323             public void stateChanged(final ChangeEvent e)
324             {
325                 JSlider slider = (JSlider) e.getSource();
326                 double sigma = ((double) slider.getValue()) / slider.getMaximum();
327                 for (Gtu gtu : getNetwork().getGTUs())
328                 {
329                     if (gtu.getType().isOfType(DefaultsNl.CAR))
330                     {
331                         try
332                         {
333                             gtu.getParameters().setClaimedParameter(LmrsParameters.SOCIO, sigma, socioSlider);
334                         }
335                         catch (ParameterException exception)
336                         {
337                             if (!this.loggedException)
338                             {
339                                 Logger.ots().error("Unable to set SOCIO on GTU.");
340                             }
341                             this.loggedException = true;
342                         }
343                     }
344                 }
345             }
346         });
347         simulationPanel.getDemoPanel().add(socioSlider);
348 
349         // spacer
350         simulationPanel.getDemoPanel().add(Box.createVerticalStrut(20));
351 
352         // km/lc
353         JLabel kmplcLabel = new JLabel("Km between lane changes (last 0): -");
354         this.kmplcListener = new KmplcListener(kmplcLabel, (RoadNetwork) network);
355         for (Gtu gtu : network.getGTUs())
356         {
357             gtu.addListener(this.kmplcListener, LaneBasedGtu.LANE_CHANGE_EVENT);
358         }
359         kmplcLabel.setHorizontalAlignment(SwingConstants.LEFT);
360         kmplcLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
361         simulationPanel.getDemoPanel().add(kmplcLabel);
362     }
363 
364     /**
365      * Create or destroy vehicles.
366      */
367     @SuppressWarnings("unused")
368     private void checkVehicleNumber()
369     {
370         while (getNetwork().getGTUs().size() > this.gtuNum)
371         {
372             int n = getNetwork().getGTUs().size();
373             int i = n <= 1 ? 0 : StrategiesDemo.this.stream.nextInt(0, n - 1);
374             Iterator<Gtu> it = getNetwork().getGTUs().iterator();
375             Gtu gtu = it.next();
376             for (int j = 0; j < i; j++)
377             {
378                 gtu = it.next();
379             }
380             gtu.destroy();
381             this.queue.clear();
382         }
383         // if: add one vehicle per cycle, so we limit the disturbance
384         if (getNetwork().getGTUs().size() < this.gtuNum)
385         {
386             Lane lane = null;
387             Length pos = null;
388             Speed initialSpeed = null;
389             Length gap = Length.ZERO;
390             for (Link link : getNetwork().getLinkMap().values())
391             {
392                 for (Lane l : ((CrossSectionLink) link).getLanes())
393                 {
394                     if (l.numberOfGtus() == 0)
395                     {
396                         lane = l;
397                         pos = lane.getLength().times(0.5);
398                         gap = Length.POSITIVE_INFINITY;
399                         initialSpeed = Speed.ZERO;
400                     }
401                     for (int i = 0; i < l.numberOfGtus(); i++)
402                     {
403                         LaneBasedGtu gtu1 = l.getGtu(i);
404                         Length up = gtu1.getPosition(l, gtu1.getFront());
405                         LaneBasedGtu gtu2;
406                         Length down;
407                         if (i < l.numberOfGtus() - 1)
408                         {
409                             gtu2 = l.getGtu(i + 1);
410                             down = gtu2.getPosition(l, gtu2.getRear());
411                         }
412                         else
413                         {
414                             Lane nextLane = l.nextLanes(DefaultsNl.VEHICLE).iterator().next();
415                             if (nextLane.numberOfGtus() == 0)
416                             {
417                                 continue;
418                             }
419                             gtu2 = nextLane.getGtu(0);
420                             down = l.getLength().plus(gtu2.getPosition(nextLane, gtu2.getRear()));
421                         }
422                         Length tentativeGap = down.minus(up)
423                                 .minus(this.nextGtuType.isOfType(DefaultsNl.TRUCK) ? this.truckLength : this.carLength);
424                         if (tentativeGap.gt(gap))
425                         {
426                             // check reasonable gap (0.3s)
427                             Speed maxSpeed = Speed.max(gtu1.getSpeed(), gtu2.getSpeed());
428                             if (maxSpeed.eq0() || tentativeGap.divide(maxSpeed).si * .5 > .3)
429                             {
430                                 gap = tentativeGap;
431                                 initialSpeed = Speed.interpolate(gtu1.getSpeed(), gtu2.getSpeed(), 0.5);
432                                 pos = up.plus(tentativeGap.times(0.5))
433                                         .minus(this.nextGtuType.isOfType(DefaultsNl.TRUCK) ? this.truckMid : this.carMid);
434                                 if (pos.gt(l.getLength()))
435                                 {
436                                     pos = pos.minus(l.getLength());
437                                     lane = l.nextLanes(DefaultsNl.VEHICLE).iterator().next();
438                                 }
439                                 else
440                                 {
441                                     lane = l;
442                                 }
443                             }
444                         }
445                     }
446                 }
447             }
448             if (lane != null)
449             {
450                 try
451                 {
452                     createGtu(lane, pos, this.nextGtuType, initialSpeed, getNetwork());
453                 }
454                 catch (NamingException | GtuException | NetworkException | SimRuntimeException exception)
455                 {
456                     throw new OtsRuntimeException(exception);
457                 }
458                 this.nextGtuType = this.stream.nextDouble() < this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
459                 this.queue.clear();
460             }
461         }
462         getSimulator().scheduleEventRel(Duration.ofSI(0.5), () -> checkVehicleNumber());
463     }
464 
465     /** Lane change listener. */
466     private class KmplcListener implements EventListener
467     {
468         /** Label to show statistic. */
469         private final JLabel label;
470 
471         /** Network. */
472         private final RoadNetwork network;
473 
474         /**
475          * Constructor.
476          * @param label label
477          * @param network network
478          */
479         @SuppressWarnings("synthetic-access")
480         KmplcListener(final JLabel label, final RoadNetwork network)
481         {
482             this.label = label;
483             this.network = network;
484             StrategiesDemo.this.queue.add(0.0);
485         }
486 
487         @SuppressWarnings("synthetic-access")
488         @Override
489         public void notify(final Event event)
490         {
491             if (event.getType().equals(LaneBasedGtu.LANE_CHANGE_EVENT))
492             {
493                 double cumul = 0.0;
494                 for (Gtu gtu : this.network.getGTUs())
495                 {
496                     cumul += gtu.getOdometer().si;
497                 }
498                 cumul /= 1000;
499                 StrategiesDemo.this.queue.add(cumul);
500                 while (StrategiesDemo.this.queue.size() > 51)
501                 {
502                     StrategiesDemo.this.queue.remove(0);
503                 }
504                 double val =
505                         (StrategiesDemo.this.queue.get(StrategiesDemo.this.queue.size() - 1) - StrategiesDemo.this.queue.get(0))
506                                 / (StrategiesDemo.this.queue.size() - 1.0);
507                 this.label.setText(
508                         String.format("Lane change rate (last %d): %.1f km/lc", StrategiesDemo.this.queue.size() - 1, val));
509             }
510         }
511     }
512 
513     @Override
514     protected RoadNetwork setupSimulation(final OtsSimulatorInterface sim) throws Exception
515     {
516         RoadNetwork network = new RoadNetwork("Strategies demo", getSimulator());
517 
518         GtuCharacteristics truck = Defaults.NL.apply(DefaultsNl.TRUCK, this.stream)
519                 .orElseThrow(() -> new GtuException("No characteristics for NL.TRUCK could be generated.")).get();
520         GtuCharacteristics car = Defaults.NL.apply(DefaultsNl.CAR, this.stream)
521                 .orElseThrow(() -> new GtuException("No characteristics for NL.CAR could be generated.")).get();
522         this.truckLength = truck.getLength();
523         this.truckMid = truck.getLength().times(0.5).minus(truck.getFront());
524         this.carLength = car.getLength();
525         this.carMid = car.getLength().times(0.5).minus(car.getFront());
526 
527         double radius = 150;
528         LaneSpeedLimits speedLimits = new LaneSpeedLimits(new Speed(120, SpeedUnit.KM_PER_HOUR),
529                 Map.of(DefaultsNl.TRUCK, new Speed(80, SpeedUnit.KM_PER_HOUR)));
530         Node nodeA = new Node(network, "A", new Point2d(-radius, 0), new Direction(270, DirectionUnit.EAST_DEGREE));
531         Node nodeB = new Node(network, "B", new Point2d(radius, 0), new Direction(90, DirectionUnit.EAST_DEGREE));
532 
533         OffsetCurve2d half1 = new Arc2d(new DirectedPoint2d(radius, 0.0, Math.PI / 2), radius, true, Math.PI);
534         List<Lane> lanes1 = new LaneFactory(network, nodeB, nodeA, DefaultsNl.FREEWAY, sim, LaneKeepingPolicy.KEEPRIGHT, half1)
535                 .leftToRight(0.0, Length.ofSI(3.5), DefaultsRoadNl.FREEWAY, speedLimits).addLanes(DefaultsRoadNl.DASHED)
536                 .getLanes();
537         OffsetCurve2d half2 = new Arc2d(new DirectedPoint2d(-radius, 0.0, -Math.PI / 2), radius, true, Math.PI);
538         List<Lane> lanes2 = new LaneFactory(network, nodeA, nodeB, DefaultsNl.FREEWAY, sim, LaneKeepingPolicy.KEEPRIGHT, half2)
539                 .leftToRight(0.0, Length.ofSI(3.5), DefaultsRoadNl.FREEWAY, speedLimits).addLanes(DefaultsRoadNl.DASHED)
540                 .getLanes();
541 
542         LmrsFactory<Lmrs> lmrsFactory = new LmrsFactory<>(Lmrs::new).set(Setting.SOCIO_TAILGATING, true)
543                 .set(Setting.SOCIO_LANE_CHANGE, true).set(Setting.SOCIO_SPEED, true)
544                 .set(Setting.INCENTIVE_STAY_ON_SLOW_LANES, true, DefaultsNl.TRUCK).setStream(this.stream);
545         lmrsFactory.addParameter(DefaultsNl.CAR, LmrsParameters.SOCIO, 0.5);
546         lmrsFactory.addParameter(DefaultsNl.TRUCK, LmrsParameters.SOCIO, 1.0);
547         lmrsFactory.addParameter(DefaultsNl.CAR, LmrsParameters.VGAIN, new Speed(35.0, SpeedUnit.KM_PER_HOUR));
548         lmrsFactory.addParameter(DefaultsNl.TRUCK, LmrsParameters.VGAIN, new Speed(50.0, SpeedUnit.KM_PER_HOUR));
549         lmrsFactory.addParameter(ParameterTypes.TMAX, Duration.ofSI(1.6));
550         lmrsFactory.addParameter(DefaultsNl.TRUCK, ParameterTypes.A, Acceleration.ofSI(0.4));
551         lmrsFactory.addParameter(DefaultsNl.TRUCK, ParameterTypes.FSPEED, 1.0);
552         lmrsFactory.addParameter(DefaultsNl.TRUCK, ParameterTypes.FSPEED_GTU,
553                 new DistNormal(this.stream, 85.0 / 80.0, 2.5 / 80.0));
554         this.factory = new LaneBasedStrategicalRoutePlannerFactory(lmrsFactory, lmrsFactory);
555 
556         for (int i = 0; i < lanes1.size(); i++)
557         {
558             Length pos = Length.ofSI(10.0);
559             Length gap = lanes1.get(i).getLength().plus(lanes2.get(i).getLength()).divide(this.gtuNum / 2);
560             for (int j = 0; j < 2; j++)
561             {
562                 Lane lane = j == 0 ? lanes1.get(i) : lanes2.get(i);
563                 while (true)
564                 {
565                     GtuType gtuType;
566                     if (i == 0)
567                     {
568                         gtuType = DefaultsNl.CAR;
569                     }
570                     else
571                     {
572                         gtuType = this.stream.nextDouble() < 2 * this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
573                     }
574 
575                     Speed initialSpeed = Speed.ZERO;
576 
577                     createGtu(lane, pos, gtuType, initialSpeed, network);
578 
579                     pos = pos.plus(gap);
580                     if (pos.si > lane.getLength().si)
581                     {
582                         pos = pos.minus(lane.getLength());
583                         break;
584                     }
585                 }
586             }
587         }
588 
589         this.nextGtuType = this.stream.nextDouble() < this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
590         sim.scheduleEventNow(() -> checkVehicleNumber());
591 
592         return network;
593     }
594 
595     /**
596      * Creates a GTU.
597      * @param lane lane
598      * @param pos position
599      * @param gtuType GTU type
600      * @param initialSpeed initial speed
601      * @param net network
602      * @throws NamingException on exception
603      * @throws GtuException on exception
604      * @throws NetworkException on exception
605      * @throws SimRuntimeException on exception
606      */
607     public void createGtu(final Lane lane, final Length pos, final GtuType gtuType, final Speed initialSpeed,
608             final RoadNetwork net) throws NamingException, GtuException, NetworkException, SimRuntimeException
609     {
610         GtuCharacteristics gtuCharacteristics = Defaults.NL.apply(gtuType, this.stream)
611                 .orElseThrow(() -> new GtuException("No characteristics for GTU type " + gtuType + " could be generated."))
612                 .get();
613 
614         LaneBasedGtu gtu = new LaneBasedGtu("" + (++this.gtuIdNum), gtuType, gtuCharacteristics.getLength(),
615                 gtuCharacteristics.getWidth(), gtuCharacteristics.getMaximumSpeed(), gtuCharacteristics.getFront(), net);
616         gtu.setMaximumAcceleration(gtuCharacteristics.getMaximumAcceleration());
617         gtu.setMaximumDeceleration(gtuCharacteristics.getMaximumDeceleration());
618         gtu.setNoLaneChangeDistance(Length.ofSI(50));
619         gtu.setBookkeeping(LaneBookkeeping.INSTANT);
620 
621         // strategical planner
622         LaneBasedStrategicalPlanner strategicalPlanner = this.factory.create(gtu, null, null, null);
623         // this.factories.get(gtuType).create(gtu, null, null, null);
624 
625         // init
626         gtu.init(strategicalPlanner, new LanePosition(lane, pos).getLocation(), initialSpeed);
627 
628         if (this.kmplcListener != null)
629         {
630             gtu.addListener(this.kmplcListener, LaneBasedGtu.LANE_CHANGE_EVENT);
631         }
632     }
633 
634 }