Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
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-2023 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// ---------------------------------------------------------------------------
43void
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
59void
60MSDevice_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
71void
73 // cleaning up global state (if any)
74}
75
76// ---------------------------------------------------------------------------
77// MSDevice_GLOSA-methods
78// ---------------------------------------------------------------------------
79MSDevice_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
95
96
97bool
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
137bool
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
185double
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
207double
208MSDevice_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/*
220double
221MSDevice_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
245double
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
255double
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
266void
267MSDevice_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
330void
332 /*
333 if (tripinfoOut != nullptr) {
334 tripinfoOut->openTag("glosa_device");
335 tripinfoOut->closeTag();
336 }
337 */
338}
339
340std::string
341MSDevice_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
349void
350MSDevice_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:36
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:271
#define TL(string)
Definition MsgHandler.h:287
#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, std::string paramName, double deflt, bool required=false)
Definition MSDevice.cpp:199
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:148
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:202
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:2511
double getLength() const
Returns the lane's length.
Definition MSLane.h:593
bool isLinkEnd(std::vector< MSLink * >::const_iterator &i) const
Definition MSLane.h:834
double getVehicleMaxSpeed(const SUMOTrafficObject *const veh) const
Returns the lane's maximum speed, given a vehicle's speed limit adaptation.
Definition MSLane.h:565
MSEdge & getEdge() const
Returns the lane's edge.
Definition MSLane.h:745
Notification
Definition of a vehicle state.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:183
The parent class for traffic light logics.
virtual int getCurrentPhaseIndex() const =0
Returns the current index within the program.
virtual const Phases & getPhases() const =0
Returns the phases of this tls program.
SUMOTime getNextSwitchTime() const
Returns the assumed next switch time.
void setSpeedTimeLine(const std::vector< std::pair< SUMOTime, double > > &speedTimeLine)
Sets a new velocity timeline.
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.
const MSLane * getLane() const
Returns the lane the vehicle is on.
Definition MSVehicle.h:584
Influencer & getInfluencer()
double getSpeed() const
Returns the vehicle's current speed.
Definition MSVehicle.h:493
const MSCFModel & getCarFollowModel() const
Returns the vehicle's car following model definition.
Definition MSVehicle.h:978
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition MSVehicle.h:377
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.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
static OptionsCont & getOptions()
Retrieves the options.
Static storage of an output device and its base (abstract) implementation.
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:62
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
#define DEBUG_COND