1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.djunits.unit.AccelerationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.unit.TimeUnit;
12 import org.djunits.value.StorageType;
13 import org.djunits.value.ValueException;
14 import org.djunits.value.vdouble.scalar.Acceleration;
15 import org.djunits.value.vdouble.scalar.Duration;
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Speed;
18 import org.djunits.value.vdouble.scalar.Time;
19 import org.djunits.value.vdouble.vector.AccelerationVector;
20 import org.opentrafficsim.core.geometry.OTSLine3D;
21 import org.opentrafficsim.core.gtu.GTUException;
22 import org.opentrafficsim.core.gtu.GTUType;
23 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
24 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
25 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
28 import org.opentrafficsim.core.network.LateralDirectionality;
29 import org.opentrafficsim.core.network.Link;
30 import org.opentrafficsim.core.network.NetworkException;
31 import org.opentrafficsim.core.network.Node;
32 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
33 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
34 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
35 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
36 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
37 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
38 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
39 import org.opentrafficsim.road.network.lane.CrossSectionElement;
40 import org.opentrafficsim.road.network.lane.CrossSectionLink;
41 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
42 import org.opentrafficsim.road.network.lane.Lane;
43 import org.opentrafficsim.road.network.lane.object.sensor.Sensor;
44 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
45
46 import nl.tudelft.simulation.language.d3.DirectedPoint;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class LaneBasedCFLCTacticalPlanner extends AbstractLaneBasedTacticalPlanner
62 {
63
64 private static final long serialVersionUID = 20151125L;
65
66
67 private static final Acceleration STAYINCURRENTLANEINCENTIVE = new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
68
69
70 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
71
72
73 private static final Acceleration NONPREFERREDLANEINCENTIVE = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
74
75
76 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
77
78
79 public static final Length GETOFFTHISLANENOW = Length.ZERO;
80
81
82 private static final Duration TIMEHORIZON = new Duration(90, TimeUnit.SECOND);
83
84
85 private LaneChangeModel laneChangeModel;
86
87
88
89
90
91
92
93 public LaneBasedCFLCTacticalPlanner(final GTUFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
94 final LaneBasedGTU gtu)
95 {
96 super(carFollowingModel, gtu);
97 this.laneChangeModel = laneChangeModel;
98 }
99
100
101 @Override
102 public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
103 throws OperationalPlanException, NetworkException, GTUException, ParameterException
104 {
105 try
106 {
107
108 LaneBasedGTU laneBasedGTU = getGtu();
109 LanePerception perception = getPerception();
110
111
112 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
113 {
114 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
115 }
116
117
118
119 perception.perceive();
120
121 Length maximumForwardHeadway = laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
122 Length maximumReverseHeadway = laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
123 Time now = getGtu().getSimulator().getSimulatorTime().getTime();
124 Speed speedLimit = perception.getPerceptionCategory(DefaultSimplePerception.class).getSpeedLimit();
125
126
127 Headway sameLaneLeader = perception.getPerceptionCategory(DefaultSimplePerception.class).getForwardHeadway();
128 Headway sameLaneFollower = perception.getPerceptionCategory(DefaultSimplePerception.class).getBackwardHeadway();
129 Collection<Headway> sameLaneTraffic = new ArrayList<Headway>();
130 if (sameLaneLeader.getObjectType().isGtu())
131 {
132 sameLaneTraffic.add(sameLaneLeader);
133 }
134 if (sameLaneFollower.getObjectType().isGtu())
135 {
136 sameLaneTraffic.add(sameLaneFollower);
137 }
138
139
140 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
141 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
142 boolean currentLaneFine = nextSplitInfo.getCorrectCurrentLanes().contains(lanePathInfo.getReferenceLane());
143
144
145
146
147
148 Collection<Headway> leftLaneTraffic =
149 perception.getPerceptionCategory(DefaultSimplePerception.class).getNeighboringHeadwaysLeft();
150 Collection<Headway> rightLaneTraffic =
151 perception.getPerceptionCategory(DefaultSimplePerception.class).getNeighboringHeadwaysRight();
152
153
154 final LateralDirectionality preferred = LateralDirectionality.RIGHT;
155 final Acceleration defaultLeftLaneIncentive =
156 preferred.isLeft() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
157 final Acceleration defaultRightLaneIncentive =
158 preferred.isRight() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
159
160 AccelerationVector defaultLaneIncentives =
161 new AccelerationVector(new double[] { defaultLeftLaneIncentive.getSI(), STAYINCURRENTLANEINCENTIVE.getSI(),
162 defaultRightLaneIncentive.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
163 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
164 LaneMovementStep lcmr = this.laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic,
165 rightLaneTraffic, leftLaneTraffic, speedLimit,
166 new Acceleration(laneIncentives.get(preferred.isRight() ? 2 : 0)), new Acceleration(laneIncentives.get(1)),
167 new Acceleration(laneIncentives.get(preferred.isRight() ? 0 : 2)));
168 Duration duration = lcmr.getGfmr().getValidUntil().minus(getGtu().getSimulator().getSimulatorTime().getTime());
169 if (lcmr.getLaneChangeDirection() != null)
170 {
171 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
172
173
174 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
175 }
176
177
178 if (lcmr.getGfmr().getAcceleration().si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
179 {
180 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, duration);
181 }
182
183
184 OTSLine3D path = lanePathInfo.getPath();
185 List<Segment> operationalPlanSegmentList = new ArrayList<>();
186 if (lcmr.getGfmr().getAcceleration().si == 0.0)
187 {
188 Segment segment = new OperationalPlan.SpeedSegment(duration);
189 operationalPlanSegmentList.add(segment);
190 }
191 else
192 {
193 Segment segment = new OperationalPlan.AccelerationSegment(duration, lcmr.getGfmr().getAcceleration());
194 operationalPlanSegmentList.add(segment);
195 }
196 OperationalPlan op =
197 new OperationalPlan(getGtu(), path, startTime, getGtu().getSpeed(), operationalPlanSegmentList);
198 return op;
199 }
200 catch (ValueException exception)
201 {
202 throw new GTUException(exception);
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218 private AccelerationVector laneIncentives(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
219 throws NetworkException, ValueException, GTUException, OperationalPlanException
220 {
221 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
222 Length currentSuitability = suitability(gtu, null);
223 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
224 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
225 && rightSuitability == NOLANECHANGENEEDED)
226 {
227 return checkLaneDrops(gtu, defaultLaneIncentives);
228 }
229 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
230 && currentSuitability == NOLANECHANGENEEDED
231 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
232 {
233 return checkLaneDrops(gtu,
234 new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
235 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
236 StorageType.DENSE));
237 }
238 if (currentSuitability == NOLANECHANGENEEDED)
239 {
240 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
241 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
242 StorageType.DENSE);
243 }
244 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
245 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260 private AccelerationVector checkLaneDrops(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
261 throws NetworkException, ValueException, GTUException, OperationalPlanException
262 {
263
264 Length leftSuitability = Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10
265 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.LEFT);
266 Length currentSuitability = laneDrop(gtu, null);
267 Length rightSuitability = Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10
268 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.RIGHT);
269
270 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
271 && currentSuitability == NOLANECHANGENEEDED
272 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
273 {
274 return defaultLaneIncentives;
275 }
276
277 if (currentSuitability == NOLANECHANGENEEDED)
278 {
279 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
280 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
281 StorageType.DENSE);
282 }
283 if (currentSuitability.le(leftSuitability))
284 {
285 return new AccelerationVector(new double[] { PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
286 GETOFFTHISLANENOW.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
287 }
288 if (currentSuitability.le(rightSuitability))
289 {
290 return new AccelerationVector(new double[] { GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
291 PREFERREDLANEINCENTIVE.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
292 }
293 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
294 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 private Length laneDrop(final LaneBasedGTU gtu, final LateralDirectionality direction)
311 throws NetworkException, GTUException, OperationalPlanException
312 {
313 DirectedLanePosition dlp = gtu.getReferencePosition();
314 Lane lane = dlp.getLane();
315 Length longitudinalPosition = dlp.getPosition();
316 if (null != direction)
317 {
318 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane, direction,
319 longitudinalPosition);
320 }
321 if (null == lane)
322 {
323 return GETOFFTHISLANENOW;
324 }
325 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
326 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getGTUType()).getSI();
327 while (remainingTimeSI >= 0)
328 {
329 for (Sensor s : lane.getSensors())
330 {
331 if (s instanceof SinkSensor)
332 {
333 return NOLANECHANGENEEDED;
334 }
335 }
336 int branching = lane.nextLanes(gtu.getGTUType()).size();
337 if (branching == 0)
338 {
339 return new Length(remainingLength, LengthUnit.SI);
340 }
341 if (branching > 1)
342 {
343 return NOLANECHANGENEEDED;
344 }
345 lane = lane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
346 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getGTUType()).getSI();
347 remainingLength += lane.getLength().getSI();
348 }
349 return NOLANECHANGENEEDED;
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363 private Length suitability(final LaneBasedGTU gtu, final LateralDirectionality direction)
364 throws NetworkException, GTUException, OperationalPlanException
365 {
366 DirectedLanePosition dlp = gtu.getReferencePosition();
367 Lane lane = dlp.getLane();
368 Length longitudinalPosition = dlp.getPosition();
369 if (null != direction)
370 {
371 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane, direction,
372 longitudinalPosition);
373 }
374 if (null == lane)
375 {
376 return GETOFFTHISLANENOW;
377 }
378 try
379 {
380 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
381 }
382 catch (NetworkException ne)
383 {
384 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
385 return NOLANECHANGENEEDED;
386 }
387 }
388
389
390
391
392
393
394
395 private double acceleration(final LaneBasedGTU gtu, final Length stopDistance)
396 {
397
398
399 double v = gtu.getSpeed().getSI();
400 double a = -v * v / 2 / stopDistance.getSI();
401 return a;
402 }
403
404
405
406
407
408
409
410
411
412
413
414 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGTU gtu,
415 final Duration timeHorizon) throws NetworkException
416 {
417 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
418 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
419
420 Node nextNode = lane.getParentLink().getEndNode();
421 Link lastLink = lane.getParentLink();
422 Node nextSplitNode = null;
423 Lane currentLane = lane;
424 CrossSectionLink linkBeforeBranch = lane.getParentLink();
425 while (null != nextNode)
426 {
427 if (spareTime <= 0)
428 {
429 return NOLANECHANGENEEDED;
430 }
431 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType());
432 if (0 == laneCount)
433 {
434 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
435 }
436 if (1 == laneCount)
437 {
438 return NOLANECHANGENEEDED;
439
440 }
441 int branching = nextNode.getLinks().size();
442 if (branching > 2)
443 {
444 nextSplitNode = nextNode;
445 break;
446 }
447 else if (1 == branching)
448 {
449 return NOLANECHANGENEEDED;
450 }
451 else
452 {
453 Link nextLink = gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
454 if (nextLink instanceof CrossSectionLink)
455 {
456 nextNode = nextLink.getEndNode();
457
458 remainingDistance += nextLink.getLength().getSI();
459 linkBeforeBranch = (CrossSectionLink) nextLink;
460
461 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
462 {
463
464
465 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu.getGTUType()).size() > 0)
466 {
467 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
468 gtu.getGTUType()))
469 {
470 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
471 {
472 currentLane = adjacentLane;
473 break;
474 }
475
476
477 }
478 }
479 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT,
480 gtu.getGTUType()))
481 {
482 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
483 {
484 currentLane = adjacentLane;
485 break;
486 }
487
488
489 }
490 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
491 {
492 throw new NetworkException(
493 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
494 }
495 }
496
497 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
498 {
499 if (nextLane.getLaneType().isCompatible(gtu.getGTUType()))
500 {
501 currentLane = currentLane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
502 break;
503 }
504 }
505 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getGTUType()).getSI();
506 }
507 else
508 {
509
510
511 return NOLANECHANGENEEDED;
512 }
513 lastLink = nextLink;
514 }
515 }
516 if (null == nextNode)
517 {
518 throw new NetworkException("Cannot find the next branch or sink node");
519 }
520
521
522 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length>();
523 Link linkAfterBranch =
524 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
525 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
526 {
527 if (cse instanceof Lane)
528 {
529 Lane l = (Lane) cse;
530 if (l.getLaneType().isCompatible(gtu.getGTUType()))
531 {
532 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
533 {
534 if (connectingLane.getParentLink() == linkAfterBranch
535 && connectingLane.getLaneType().isCompatible(gtu.getGTUType()))
536 {
537 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
538
539
540
541 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
542 new Duration(spareTime, TimeUnit.SI));
543
544
545 suitabilityOfLanesBeforeBranch.put(l,
546 null == currentValue || value.le(currentValue) ? value : currentValue);
547 }
548 }
549 }
550 }
551 }
552 if (suitabilityOfLanesBeforeBranch.size() == 0)
553 {
554 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
555 }
556 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
557 if (null != currentLaneSuitability)
558 {
559 return currentLaneSuitability;
560 }
561
562 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType());
563 Length leftSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
564 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.LEFT, gtu.getGTUType());
565 Length rightSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
566 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.RIGHT, gtu.getGTUType());
567 if (leftSuitability.ge(rightSuitability))
568 {
569 return leftSuitability;
570 }
571 else if (rightSuitability.ge(leftSuitability))
572 {
573 return rightSuitability;
574 }
575 if (leftSuitability.le(GETOFFTHISLANENOW))
576 {
577 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
578 }
579 return leftSuitability;
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
595 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
596 final GTUType gtuType)
597 {
598
599
600
601
602
603
604
605
606
607 int laneChangesUsed = 0;
608 Lane currentLane = startLane;
609 Length currentSuitability = null;
610 while (null == currentSuitability)
611 {
612 laneChangesUsed++;
613 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0)
614 {
615 return GETOFFTHISLANENOW;
616 }
617 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next();
618 currentSuitability = suitabilities.get(currentLane);
619 }
620 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
621 int notSuitableLaneCount = totalLanes - suitabilities.size();
622 return new Length(
623 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
624 LengthUnit.SI);
625 }
626
627
628
629
630
631
632
633
634 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType)
635 {
636 int result = 0;
637 for (CrossSectionElement cse : link.getCrossSectionElementList())
638 {
639 if (cse instanceof Lane)
640 {
641 Lane l = (Lane) cse;
642 if (l.getLaneType().isCompatible(gtuType))
643 {
644 result++;
645 }
646 }
647 }
648 return result;
649 }
650
651
652 @Override
653 public final String toString()
654 {
655 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
656 }
657
658 }