Eclipse SUMO - Simulation of Urban MObility
MSDevice_GLOSA.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2013-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
18 // A device for Green Light Optimal Speed Advisory
19 /****************************************************************************/
20 #include <config.h>
21 
27 #include <microsim/MSNet.h>
28 #include <microsim/MSLane.h>
29 #include <microsim/MSEdge.h>
30 #include <microsim/MSLink.h>
31 #include <microsim/MSVehicle.h>
32 #include "MSDevice_GLOSA.h"
33 
34 //#define DEBUG_GLOSA
35 #define DEBUG_COND (true)
36 
37 // ===========================================================================
38 // method definitions
39 // ===========================================================================
40 // ---------------------------------------------------------------------------
41 // static initialisation methods
42 // ---------------------------------------------------------------------------
43 void
45  oc.addOptionSubTopic("GLOSA Device");
46  insertDefaultAssignmentOptions("glosa", "GLOSA Device", oc);
47 
48  oc.doRegister("device.glosa.range", new Option_Float(100.0));
49  oc.addDescription("device.glosa.range", "GLOSA Device", TL("The communication range to the traffic light"));
50 
51  oc.doRegister("device.glosa.max-speedfactor", new Option_Float(1.1));
52  oc.addDescription("device.glosa.max-speedfactor", "GLOSA Device", TL("The maximum speed factor when approaching a green light"));
53 
54  oc.doRegister("device.glosa.min-speed", new Option_Float(5.0));
55  oc.addDescription("device.glosa.min-speed", "GLOSA Device", TL("Minimum speed when coasting towards a red light"));
56 }
57 
58 
59 void
60 MSDevice_GLOSA::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
62  if (!MSGlobals::gUseMesoSim && equippedByDefaultAssignmentOptions(oc, "glosa", v, false)) {
63  MSDevice_GLOSA* device = new MSDevice_GLOSA(v, "glosa_" + v.getID(),
64  getFloatParam(v, OptionsCont::getOptions(), "glosa.min-speed", 5, true),
65  getFloatParam(v, OptionsCont::getOptions(), "glosa.range", 100, true),
66  getFloatParam(v, OptionsCont::getOptions(), "glosa.max-speedfactor", 1.1, true));
67  into.push_back(device);
68  }
69 }
70 
71 void
73  // cleaning up global state (if any)
74 }
75 
76 // ---------------------------------------------------------------------------
77 // MSDevice_GLOSA-methods
78 // ---------------------------------------------------------------------------
79 MSDevice_GLOSA::MSDevice_GLOSA(SUMOVehicle& holder, const std::string& id, double minSpeed, double range, double maxSpeedFactor) :
80  MSVehicleDevice(holder, id),
81  myVeh(dynamic_cast<MSVehicle&>(holder)),
82  myNextTLSLink(nullptr),
83  myDistance(0),
84  myMinSpeed(minSpeed),
85  myRange(range),
86  myMaxSpeedFactor(maxSpeedFactor)
87 
88 {
90 }
91 
92 
94 }
95 
96 
97 bool
98 MSDevice_GLOSA::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
99  double newPos, double /*newSpeed*/) {
100  myDistance -= (newPos - oldPos);
101  if (myNextTLSLink != nullptr && myDistance <= myRange) {
102  const double vMax = myVeh.getLane()->getVehicleMaxSpeed(&myVeh);
103  const double timeToJunction = earliest_arrival(myDistance, vMax);
104  const double timeToSwitch = getTimeToSwitch(myNextTLSLink);
105 #ifdef DEBUG_GLOSA
106  if (DEBUG_COND) {
107  std::cout << SIMTIME << " veh=" << myVeh.getID() << " d=" << myDistance << " ttJ=" << timeToJunction << " ttS=" << timeToSwitch << "\n";
108  }
109 #endif
110  if (myNextTLSLink->haveGreen()) {
111  if (timeToJunction > timeToSwitch) {
113  const double vMax2 = vMax / myVeh.getChosenSpeedFactor() * myMaxSpeedFactor;
114  const double timetoJunction2 = earliest_arrival(myDistance, vMax2);
115  // reaching the signal at yellow might be sufficient
117 #ifdef DEBUG_GLOSA
118  if (DEBUG_COND) {
119  std::cout << " vMax2=" << vMax2 << " ttJ2=" << timetoJunction2 << " yellowSlack=" << yellowSlack << "\n";
120  }
121 #endif
122  if (timetoJunction2 <= (timeToSwitch + yellowSlack)) {
123  // increase speed factor up to a maximum if necessary and useful
124  // XXX could compute optimal speed factor here
126  }
127  }
128  }
129  } else if (myNextTLSLink->haveRed()) {
130  adaptSpeed(myDistance, timeToJunction, timeToSwitch);
131  }
132  }
133  return true; // keep the device
134 }
135 
136 
137 bool
138 MSDevice_GLOSA::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
139  const MSLink* prevLink = myNextTLSLink;
140  myNextTLSLink = nullptr;
141  const MSLane* lane = myVeh.getLane();
142  const std::vector<MSLane*>& bestLaneConts = myVeh.getBestLanesContinuation(lane);
143  double seen = lane->getLength() - myVeh.getPositionOnLane();
144  int view = 1;
145  std::vector<MSLink*>::const_iterator linkIt = MSLane::succLinkSec(myVeh, view, *lane, bestLaneConts);
146  while (!lane->isLinkEnd(linkIt)) {
147  if (!lane->getEdge().isInternal()) {
148  if ((*linkIt)->isTLSControlled()) {
149  myNextTLSLink = *linkIt;
150  myDistance = seen;
151  break;
152  }
153  }
154  lane = (*linkIt)->getViaLaneOrLane();
155  if (!lane->getEdge().isInternal()) {
156  view++;
157  }
158  seen += lane->getLength();
159  linkIt = MSLane::succLinkSec(myVeh, view, *lane, bestLaneConts);
160  }
161  if (prevLink != nullptr && myNextTLSLink == nullptr) {
162  // moved passt tls
164  } else if (myNextTLSLink != nullptr && prevLink != myNextTLSLink) {
165  // approaching new tls
166  double tlsRange = 1e10;
167  const std::string val = myNextTLSLink->getTLLogic()->getParameter("device.glosa.range", "1e10");
168  try {
169  tlsRange = StringUtils::toDouble(val);
170  } catch (const NumberFormatException&) {
171  WRITE_WARNINGF(TL("Invalid value '%' for parameter 'device.glosa.range' of traffic light '%'"),
172  val, myNextTLSLink->getTLLogic()->getID());
173  }
174  myRange = MIN2(getFloatParam(myVeh, OptionsCont::getOptions(), "glosa.range", 100, true), tlsRange);
175  }
176 
177 #ifdef DEBUG_GLOSA
178  if (DEBUG_COND) std::cout << SIMTIME << " veh=" << myVeh.getID() << " enter=" << myVeh.getLane()->getID() << " hadTLS=" << hadTLS
179  << " tls=" << (myNextTLSLink == nullptr ? "NULL" : myNextTLSLink->getTLLogic()->getID()) << " dist=" << myDistance << "\n";
180 #endif
181  return true; // keep the device
182 }
183 
184 
185 double
187  assert(tlsLink != nullptr);
188  const MSTrafficLightLogic* const tl = tlsLink->getTLLogic();
189  assert(tl != nullptr);
190  const auto& phases = tl->getPhases();
191  const int n = (int)phases.size();
192  const int cur = tl->getCurrentPhaseIndex();
193  SUMOTime result = tl->getNextSwitchTime() - SIMSTEP;
194  for (int i = 1; i < n; i++) {
195  const auto& phase = phases[(cur + i) % n];
196  const char ls = phase->getState()[tlsLink->getTLIndex()];
197  if ((tlsLink->haveRed() && (ls == 'g' || ls == 'G'))
198  || (tlsLink->haveGreen() && ls != 'g' && ls != 'G')) {
199  break;
200  }
201  result += phase->duration;
202  }
203  return STEPS2TIME(result);
204 }
205 
206 
207 double
208 MSDevice_GLOSA::earliest_arrival(double distance, double vMax) {
209  // assume we keep acceleration until we hit maximum speed
210  const double v = myVeh.getSpeed();
211  const double a = myVeh.getCarFollowModel().getMaxAccel();
212  const double accel_time = MIN2((vMax - v) / a, time_to_junction_at_continuous_accel(distance, v));
213  const double remaining_dist = distance - distance_at_continuous_accel(v, accel_time);
214  const double remaining_time = remaining_dist / vMax;
215  return accel_time + remaining_time;
216 }
217 
218 
219 /*
220 double
221 MSDevice_GLOSA::latest_arrival(speed, distance, earliest) {
222  // assume we keep current speed until within myRange and then decelerate to myMinSpeed
223  speed = max(speed, GLOSA_MIN_SPEED)
224  potential_decel_dist = min(distance, GLOSA_RANGE)
225  decel_time = (speed - GLOSA_MIN_SPEED) / GLOSA_DECEL
226  avg_decel_speed = (speed + GLOSA_MIN_SPEED) / 2.0
227  decel_dist = decel_time * avg_decel_speed
228  if decel_dist > potential_decel_dist:
229  decel_dist = potential_decel_dist
230  # XXX actually avg_decel_speed is higher in this case
231  decel_time = decel_dist / avg_decel_speed
232  slow_dist = potential_decel_dist - decel_dist
233  fast_dist = distance - (decel_dist + slow_dist)
234  result = fast_dist / speed + decel_time + slow_dist / GLOSA_MIN_SPEED
235  if result < earliest:
236  if (distance > 15):
237  print("DEBUG: fixing latest arrival of %s to match earliest of %s" % (result, earliest))
238  result = earliest
239  return result
240  return 0;
241 }
242 */
243 
244 
245 double
247  const double v = speed;
248  const double t = time;
249  const double a = myVeh.getCarFollowModel().getMaxAccel();
250  // integrated area composed of a rectangle and a triangle
251  return v * t + a * t * t / 2;
252 }
253 
254 
255 double
257  // see distance_at_continuous_accel
258  // t^2 + (2v/a)t - 2d/a = 0
259  const double a = myVeh.getCarFollowModel().getMaxAccel();
260  const double p_half = v / a;
261  const double t = -p_half + sqrt(p_half * p_half + 2 * d / a);
262  return t;
263 }
264 
265 
266 void
267 MSDevice_GLOSA::adaptSpeed(double distance, double timeToJunction, double timeToSwitch) {
268  // ensure that myVehicle arrives at the
269  // junction with maximum speed when it switches to green
270  // car performs a slowDown at time z to speed x for duration y
271  // there are two basic strategies
272  // a) maximize z -> this saves road space but leads to low x and thus excessive braking
273  // b) maximize x -> this saves fuel but wastes road
274  // c) compromise: b) but only when distance to junction is below a threshold
275 
276  const double vMax = myVeh.getLane()->getVehicleMaxSpeed(&myVeh);
277  if (timeToJunction < timeToSwitch
278  && myVeh.getSpeed() > myMinSpeed) {
279  // need to start/continue maneuver
280  const double t = timeToSwitch;
281  const double a = myVeh.getCarFollowModel().getMaxAccel();
282  const double d = myVeh.getCarFollowModel().getMaxDecel();
283  const double u = myMinSpeed;
284  const double w = vMax;
285  const double s = distance;
286  const double v = myVeh.getSpeed();
287  // x : target speed
288  // y : slow down duration
289  // s is composed of 1 trapezoid (decel), 1 rectangle (maintain), 1 trapezoid (accel)
290  // s = (v^2-x^2)/2d + x*(y-(v-x)/d) + (w^2-x^2)/2a
291  // y = t - (w-x)/d
292  // solution for x curtesy of mathomatic.org
293  const double sign0 = -1; // XXX hack
294  const double root_argument = a * d * ((2.0 * d * (s - (w * t))) - ((v - w) * (v - w)) + (a * ((d * (t * t)) + (2.0 * (s - (t * v))))));
295  if (root_argument < 0) {
296 #ifdef DEBUG_GLOSA
297  WRITE_WARNINGF("GLOSA error 1 root_argument=% s=% t=% v=%", root_argument, s, t, v);
298 #endif
299  return;
300  }
301  const double x = (((a * (v - (d * t))) + (d * w) - sign0 * sqrt(root_argument)) / (d + a));
302  const double y = t - (w - x) / d;
303  if (!(x >= u && x <= w && y > 0 && y < t)) {
304 #ifdef DEBUG_GLOSA
305  WRITE_WARNINGF("GLOSA error 2 x=% y=% s=% t=% v=%", x, y, s, t, v);
306 #endif
307  return;
308  }
309  const double targetSpeed = x;
310  const double duration = y;
311 #ifdef DEBUG_GLOSA
312  if (DEBUG_COND) {
313  std::cout << " targetSpeed=" << targetSpeed << " duration=" << duration << "\n";
314  }
315 #endif
316  std::vector<std::pair<SUMOTime, double> > speedTimeLine;
317  speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), myVeh.getSpeed()));
318  speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(duration), targetSpeed));
319  myVeh.getInfluencer().setSpeedTimeLine(speedTimeLine);
320  } else {
321  // end maneuver
322  std::vector<std::pair<SUMOTime, double> > speedTimeLine;
323  speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), myVeh.getSpeed()));
324  speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), vMax));
325  myVeh.getInfluencer().setSpeedTimeLine(speedTimeLine);
326  }
327 }
328 
329 
330 void
332  /*
333  if (tripinfoOut != nullptr) {
334  tripinfoOut->openTag("glosa_device");
335  tripinfoOut->closeTag();
336  }
337  */
338 }
339 
340 std::string
341 MSDevice_GLOSA::getParameter(const std::string& key) const {
342  if (key == "minSpeed") {
343  return toString(myMinSpeed);
344  }
345  throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
346 }
347 
348 
349 void
350 MSDevice_GLOSA::setParameter(const std::string& key, const std::string& value) {
351  double doubleValue;
352  try {
353  doubleValue = StringUtils::toDouble(value);
354  } catch (NumberFormatException&) {
355  throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
356  }
357  if (key == "minSpeed") {
358  myMinSpeed = doubleValue;
359  } else {
360  throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
361  }
362 }
363 
364 
365 /****************************************************************************/
long long int SUMOTime
Definition: GUI.h:35
#define DEBUG_COND
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define TL(string)
Definition: MsgHandler.h:315
#define STEPS2TIME(x)
Definition: SUMOTime.h:55
#define SIMSTEP
Definition: SUMOTime.h:61
#define SIMTIME
Definition: SUMOTime.h:62
#define TIME2STEPS(x)
Definition: SUMOTime.h:57
@ SUMO_ATTR_JM_DRIVE_AFTER_YELLOW_TIME
T MIN2(T a, T b)
Definition: StdDefs.h:76
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
void setChosenSpeedFactor(const double factor)
Returns the precomputed factor by which the driver wants to be faster than the speed limit.
double getChosenSpeedFactor() const
Returns the precomputed factor by which the driver wants to be faster than the speed limit.
const MSVehicleType & getVehicleType() const
Returns the vehicle's type definition.
double getMaxAccel() const
Get the vehicle type's maximum acceleration [m/s^2].
Definition: MSCFModel.h:256
double getMaxDecel() const
Get the vehicle type's maximal comfortable deceleration [m/s^2].
Definition: MSCFModel.h:264
A device which collects info on the vehicle trip (mainly on departure and arrival)
void generateOutput(OutputDevice *tripinfoOut) const
Called on writing tripinfo output.
const MSLink * myNextTLSLink
the upcoming traffic light
MSVehicle & myVeh
myHolder cast to needed type
static void buildVehicleDevices(SUMOVehicle &v, std::vector< MSVehicleDevice * > &into)
Build devices for the given vehicle, if needed.
double myMaxSpeedFactor
maximum speed factor when trying to reach green light
double myRange
maximum communication range
void adaptSpeed(double distance, double timeToJunction, double timeToSwitch)
adapt speed to reach junction at green
double myMinSpeed
minimum approach speed towards red light
~MSDevice_GLOSA()
Destructor.
double time_to_junction_at_continuous_accel(double d, double v)
static void insertOptions(OptionsCont &oc)
Inserts MSDevice_GLOSA-options.
double myDistance
the distance to the upcoming traffic light
bool notifyEnter(SUMOTrafficObject &veh, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
updates next tls link
double distance_at_continuous_accel(double speed, double time)
double earliest_arrival(double speed, double distance)
return minimum number of seconds to reach the junction
double myOriginalSpeedFactor
original speed factor
static void cleanup()
resets counters
void setParameter(const std::string &key, const std::string &value)
try to set the given parameter for this device. Throw exception for unsupported key
std::string getParameter(const std::string &key) const
try to retrieve the given parameter from this device. Throw exception for unsupported key
bool notifyMove(SUMOTrafficObject &veh, double oldPos, double newPos, double newSpeed)
updates distance and computes speed advice
static double getTimeToSwitch(const MSLink *tlsLink)
compute time to next (relevant) switch
const std::string deviceName() const
return the name for this type of device
MSDevice_GLOSA(SUMOVehicle &holder, const std::string &id, double minSpeed, double range, double maxSpeedFactor)
Constructor.
static double getFloatParam(const SUMOVehicle &v, const OptionsCont &oc, const std::string &paramName, const double deflt, bool required=false)
Definition: MSDevice.cpp:206
static void insertDefaultAssignmentOptions(const std::string &deviceName, const std::string &optionsTopic, OptionsCont &oc, const bool isPerson=false)
Adds common command options that allow to assign devices to vehicles.
Definition: MSDevice.cpp:155
static bool equippedByDefaultAssignmentOptions(const OptionsCont &oc, const std::string &deviceName, DEVICEHOLDER &v, bool outputOptionSet, const bool isPerson=false)
Determines whether a vehicle should get a certain device.
Definition: MSDevice.h:203
bool isInternal() const
return whether this edge is an internal edge
Definition: MSEdge.h:265
static bool gUseMesoSim
Definition: MSGlobals.h:103
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
static std::vector< MSLink * >::const_iterator succLinkSec(const SUMOVehicle &veh, int nRouteSuccs, const MSLane &succLinkSource, const std::vector< MSLane * > &conts)
Definition: MSLane.cpp:2560
double getLength() const
Returns the lane's length.
Definition: MSLane.h:598
bool isLinkEnd(std::vector< MSLink * >::const_iterator &i) const
Definition: MSLane.h:841
double getVehicleMaxSpeed(const SUMOTrafficObject *const veh) const
Returns the lane's maximum speed, given a vehicle's speed limit adaptation.
Definition: MSLane.h:566
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:752
Notification
Definition of a vehicle state.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:182
The parent class for traffic light logics.
virtual int getCurrentPhaseIndex() const =0
Returns the current index within the program.
SUMOTime getNextSwitchTime() const
Returns the assumed next switch time.
virtual const Phases & getPhases() const =0
Returns the phases of this tls program.
void setSpeedTimeLine(const std::vector< std::pair< SUMOTime, double > > &speedTimeLine)
Sets a new velocity timeline.
Definition: MSVehicle.cpp:398
Abstract in-vehicle device.
Representation of a vehicle in the micro simulation.
Definition: MSVehicle.h:77
const std::vector< MSLane * > & getBestLanesContinuation() const
Returns the best sequence of lanes to continue the route starting at myLane.
Definition: MSVehicle.cpp:6230
Influencer & getInfluencer()
Definition: MSVehicle.cpp:7182
double getSpeed() const
Returns the vehicle's current speed.
Definition: MSVehicle.h:493
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition: MSVehicle.h:377
const MSLane * getLane() const
Returns the lane the vehicle is on.
Definition: MSVehicle.h:584
const MSCFModel & getCarFollowModel() const
Returns the vehicle's car following model definition.
Definition: MSVehicle.h:974
const SUMOVTypeParameter & getParameter() const
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
Definition: OptionsCont.cpp:76
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
Representation of a vehicle, person, or container.
double getJMParam(const SumoXMLAttr attr, const double defaultValue) const
Returns the named value from the map, or the default if it is not contained there.
Representation of a vehicle.
Definition: SUMOVehicle.h:60
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter