Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 : /****************************************************************************/
14 : /// @file MSCFModel_IDM.cpp
15 : /// @author Tobias Mayer
16 : /// @author Daniel Krajzewicz
17 : /// @author Michael Behrisch
18 : /// @date Thu, 03 Sep 2009
19 : ///
20 : // The Intelligent Driver Model (IDM) car-following model
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include "MSCFModel_IDM.h"
25 : #include <microsim/MSVehicle.h>
26 : #include <utils/xml/SUMOSAXAttributes.h>
27 :
28 : //#define DEBUG_V
29 : //#define DEBUG_INSERTION_SPEED
30 :
31 : #define DEBUG_COND (veh->isSelected())
32 : //#define DEBUG_COND true
33 :
34 :
35 : // ===========================================================================
36 : // method definitions
37 : // ===========================================================================
38 8567 : MSCFModel_IDM::MSCFModel_IDM(const MSVehicleType* vtype, bool idmm) :
39 : MSCFModel(vtype),
40 8567 : myIDMM(idmm),
41 8292 : myDelta(idmm ? 4.0 : vtype->getParameter().getCFParam(SUMO_ATTR_CF_IDM_DELTA, 4.)),
42 8567 : myAdaptationFactor(idmm ? vtype->getParameter().getCFParam(SUMO_ATTR_CF_IDMM_ADAPT_FACTOR, 1.8) : 1.0),
43 8567 : myAdaptationTime(idmm ? vtype->getParameter().getCFParam(SUMO_ATTR_CF_IDMM_ADAPT_TIME, 600.0) : 0.0),
44 8567 : myIterations(MAX2(1, int(TS / vtype->getParameter().getCFParam(SUMO_ATTR_CF_IDM_STEPPING, .25) + .5))),
45 8567 : myTwoSqrtAccelDecel(double(2 * sqrt(myAccel * myDecel))) {
46 : // IDM does not drive very precise and may violate minGap on occasion
47 8567 : myCollisionMinGapFactor = vtype->getParameter().getCFParam(SUMO_ATTR_COLLISION_MINGAP_FACTOR, 0.1);
48 8567 : if (TS / myIterations > 0.25) {
49 24 : WRITE_WARNINGF("Stepping duration of % for % model in vType % is unsafe", (TS / myIterations), myIDMM ? "IDMM" : "IDM", vtype->getID());
50 : }
51 8567 : }
52 :
53 16938 : MSCFModel_IDM::~MSCFModel_IDM() {}
54 :
55 :
56 : void
57 10 : MSCFModel_IDM::VehicleVariables::saveState(OutputDevice& out, const MSCFModel& /*cfm*/) const {
58 10 : out.openTag(SUMO_TAG_CFM_VARIABLES);
59 10 : out.writeAttr(SUMO_ATTR_ID, "IDMM");
60 10 : std::ostringstream internals;
61 10 : internals << levelOfService;
62 10 : out.writeAttr(SUMO_ATTR_STATE, internals.str());
63 10 : out.closeTag();
64 10 : }
65 :
66 :
67 : void
68 10 : MSCFModel_IDM::VehicleVariables::loadState(const SUMOSAXAttributes& attrs) {
69 10 : bool ok = true;
70 10 : const std::string cfmID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
71 10 : if (cfmID != "IDMM") {
72 0 : throw ProcessError(TLF("incompatible carFollowModel '%' when loading state for IDMM", cfmID));
73 : }
74 10 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
75 10 : bis >> levelOfService;
76 20 : }
77 :
78 :
79 : double
80 69849931 : MSCFModel_IDM::minNextSpeed(double speed, const MSVehicle* const /*veh*/) const {
81 : // permit exceeding myDecel when approaching stops
82 139694178 : const double decel = MAX2(myDecel, MIN2(myEmergencyDecel, 1.5));
83 69849931 : if (MSGlobals::gSemiImplicitEulerUpdate) {
84 67112578 : return MAX2(speed - ACCEL2SPEED(decel), 0.);
85 : } else {
86 : // NOTE: ballistic update allows for negative speeds to indicate a stop within the next timestep
87 2737353 : return speed - ACCEL2SPEED(decel);
88 : }
89 : }
90 :
91 :
92 :
93 : double
94 34599076 : MSCFModel_IDM::finalizeSpeed(MSVehicle* const veh, double vPos) const {
95 34599076 : const double vNext = MSCFModel::finalizeSpeed(veh, vPos);
96 34599076 : if (myAdaptationFactor != 1.) {
97 : VehicleVariables* vars = (VehicleVariables*)veh->getCarFollowVariables();
98 3651866 : vars->levelOfService += (vNext / veh->getLane()->getVehicleMaxSpeed(veh) - vars->levelOfService) / myAdaptationTime * TS;
99 : }
100 34599076 : return vNext;
101 : }
102 :
103 :
104 : double
105 61120240 : MSCFModel_IDM::freeSpeed(const MSVehicle* const veh, double speed, double seen, double maxSpeed, const bool /*onInsertion*/, const CalcReason /*usage*/) const {
106 61120240 : if (maxSpeed < 0.) {
107 : // can occur for ballistic update (in context of driving at red light)
108 : return maxSpeed;
109 : }
110 61120240 : const double secGap = getSecureGap(veh, nullptr, maxSpeed, 0, myDecel);
111 : double vSafe;
112 61120240 : if (speed <= maxSpeed) {
113 : // accelerate
114 60269532 : vSafe = _v(veh, 1e6, speed, maxSpeed, veh->getLane()->getVehicleMaxSpeed(veh), false);
115 : } else {
116 : // decelerate
117 : // @note relax gap to avoid emergency braking
118 : // @note since the transition point does not move we set the leader speed to 0
119 1701416 : vSafe = _v(veh, MAX2(seen, secGap), speed, 0, veh->getLane()->getVehicleMaxSpeed(veh), false);
120 : }
121 61120240 : if (seen < secGap) {
122 : // avoid overshoot when close to change in speed limit
123 : vSafe = MIN2(vSafe, maxSpeed);
124 : }
125 : //std::cout << SIMTIME << " speed=" << speed << " maxSpeed=" << maxSpeed << " seen=" << seen << " secGap=" << secGap << " vSafe=" << vSafe << "\n";
126 : return vSafe;
127 : }
128 :
129 :
130 : double
131 233004057 : MSCFModel_IDM::followSpeed(const MSVehicle* const veh, double speed, double gap2pred, double predSpeed, double predMaxDecel, const MSVehicle* const pred, const CalcReason /*usage*/) const {
132 233004057 : applyHeadwayAndSpeedDifferencePerceptionErrors(veh, speed, gap2pred, predSpeed, predMaxDecel, pred);
133 233004057 : return _v(veh, gap2pred, speed, predSpeed, veh->getLane()->getVehicleMaxSpeed(veh));
134 : }
135 :
136 :
137 : double
138 1817588 : MSCFModel_IDM::insertionFollowSpeed(const MSVehicle* const v, double speed, double gap2pred, double predSpeed, double predMaxDecel, const MSVehicle* const pred) const {
139 : // see definition of s in _v()
140 1817588 : double s = MAX2(0., speed * myHeadwayTime + speed * (speed - predSpeed) / myTwoSqrtAccelDecel);
141 1817588 : if (gap2pred >= s) {
142 : // followSpeed always stays below speed because s*s / (gap2pred * gap2pred) > 0. This would prevent insertion with maximum speed at all distances
143 : return speed;
144 : } else {
145 : // we cannot call follow speed directly because it assumes that 'speed'
146 : // is the current speed rather than the desired insertion speed.
147 : // If the safe speed is much lower than the desired speed, the
148 : // followSpeed function would still return a new speed that involves
149 : // reasonable braking rather than the actual safe speed (and cause
150 : // emergency braking in a subsequent step)
151 1318735 : const double speed2 = followSpeed(v, speed, gap2pred, predSpeed, predMaxDecel, pred, CalcReason::FUTURE);
152 1318735 : const double speed3 = followSpeed(v, speed2, gap2pred, predSpeed, predMaxDecel, pred, CalcReason::FUTURE);
153 1318735 : if (speed2 - speed3 < ACCEL2SPEED(1)) {
154 : return speed2;
155 : } else {
156 : #ifdef DEBUG_INSERTION_SPEED
157 : std::cout << SIMTIME << " veh=" << v->getID() << " speed=" << speed << " gap2pred=" << gap2pred << " predSpeed=" << predSpeed << " predMaxDecel=" << predMaxDecel << " pred=" << Named::getIDSecure(pred) << " s=" << s << " speed2=" << speed2 << " speed3=" << speed3 << "\n";
158 : #endif
159 1120839 : return insertionFollowSpeed(v, speed2, gap2pred, predSpeed, predMaxDecel, pred);
160 : }
161 : }
162 : }
163 :
164 :
165 : double
166 442 : MSCFModel_IDM::insertionStopSpeed(const MSVehicle* const veh, double speed, double gap) const {
167 : // we want to insert the vehicle in an equilibrium state
168 442 : double result = MSCFModel::insertionStopSpeed(veh, speed, gap);
169 : int i = 0;
170 780 : while (result - speed < -ACCEL2SPEED(myDecel) && ++i < 10) {
171 : speed = result;
172 338 : result = MSCFModel::insertionStopSpeed(veh, speed, gap);
173 : }
174 442 : return result;
175 : }
176 :
177 :
178 : double
179 78219659 : MSCFModel_IDM::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
180 78219659 : applyHeadwayPerceptionError(veh, speed, gap);
181 78219659 : if (gap < 0.01) {
182 : return 0;
183 : }
184 77477188 : double result = _v(veh, gap, speed, 0, veh->getLane()->getVehicleMaxSpeed(veh), false);
185 : //std::cout << SIMTIME << " stopSpeed speed=" << speed << " gap=" << gap << " decel=" << decel << " result=" << result << "\n";
186 77477188 : if (gap > 0 && speed < NUMERICAL_EPS && result < NUMERICAL_EPS) {
187 : // ensure that stops can be reached:
188 : //std::cout << " switching to krauss: " << veh->getID() << " gap=" << gap << " speed=" << speed << " res1=" << result << " res2=" << maximumSafeStopSpeed(gap, speed, false, veh->getActionStepLengthSecs())<< "\n";
189 35582 : result = maximumSafeStopSpeed(gap, decel, speed, false, veh->getActionStepLengthSecs());
190 : }
191 : // avoid overshooting the stop location
192 77477188 : if (gap >= 0) {
193 77477188 : result = MIN2(result, DIST2SPEED(gap));
194 : //if (result * TS > gap) {
195 : // std::cout << "Maximum stop speed exceeded for gap=" << gap << " result=" << result << " veh=" << veh->getID() << " speed=" << speed << " t=" << SIMTIME << "\n";
196 : //}
197 : }
198 :
199 : return result;
200 : }
201 :
202 :
203 : /// @todo update interactionGap logic to IDM
204 : double
205 0 : MSCFModel_IDM::interactionGap(const MSVehicle* const veh, double vL) const {
206 : // Resolve the IDM equation to gap. Assume predecessor has
207 : // speed != 0 and that vsafe will be the current speed plus acceleration,
208 : // i.e that with this gap there will be no interaction.
209 0 : const double acc = myAccel * (1. - pow(veh->getSpeed() / veh->getLane()->getVehicleMaxSpeed(veh), myDelta));
210 0 : const double vNext = veh->getSpeed() + acc;
211 0 : const double gap = (vNext - vL) * (veh->getSpeed() + vL) / (2 * myDecel) + vL;
212 :
213 : // Don't allow timeHeadWay < deltaT situations.
214 0 : return MAX2(gap, SPEED2DIST(vNext));
215 : }
216 :
217 : double
218 387249446 : MSCFModel_IDM::getSecureGap(const MSVehicle* const /*veh*/, const MSVehicle* const /*pred*/, const double speed, const double leaderSpeed, const double /*leaderMaxDecel*/) const {
219 387249446 : const double delta_v = speed - leaderSpeed;
220 387249446 : return MAX2(0.0, speed * myHeadwayTime + speed * delta_v / myTwoSqrtAccelDecel);
221 : }
222 :
223 :
224 : double
225 371601485 : MSCFModel_IDM::_v(const MSVehicle* const veh, const double gap2pred, const double egoSpeed,
226 : const double predSpeed, const double desSpeed, const bool respectMinGap) const {
227 : // this is more or less based on http://www.vwi.tu-dresden.de/~treiber/MicroApplet/IDM.html
228 : // and http://arxiv.org/abs/cond-mat/0304337
229 : // we assume however constant speed for the leader
230 371601485 : double headwayTime = myHeadwayTime;
231 371601485 : if (myAdaptationFactor != 1.) {
232 : const VehicleVariables* vars = (VehicleVariables*)veh->getCarFollowVariables();
233 42579235 : headwayTime *= myAdaptationFactor + vars->levelOfService * (1. - myAdaptationFactor);
234 : }
235 : double newSpeed = egoSpeed;
236 : double gap = gap2pred;
237 371601485 : if (respectMinGap) {
238 : // gap2pred comes with minGap already subtracted so we need to add it here again
239 233004057 : gap += myType->getMinGap();
240 : }
241 : #ifdef DEBUG_V
242 : if (DEBUG_COND) {
243 : std::cout << SIMTIME << " veh=" << veh->getID() << " gap2pred=" << gap2pred << " egoSpeed=" << egoSpeed << " predSpeed=" << predSpeed << " desSpeed=" << desSpeed << " rMG=" << respectMinGap << " hw=" << headwayTime << "\n";
244 : }
245 : #endif
246 1711392976 : for (int i = 0; i < myIterations; i++) {
247 1339791491 : const double delta_v = newSpeed - predSpeed;
248 1339791491 : double s = MAX2(0., newSpeed * headwayTime + newSpeed * delta_v / myTwoSqrtAccelDecel);
249 1339791491 : if (respectMinGap) {
250 835736333 : s += myType->getMinGap();
251 : }
252 : gap = MAX2(NUMERICAL_EPS, gap); // avoid singularity
253 1339791491 : const double acc = myAccel * (1. - pow(newSpeed / MAX2(NUMERICAL_EPS, desSpeed), myDelta) - (s * s) / (gap * gap));
254 : #ifdef DEBUG_V
255 : if (DEBUG_COND) {
256 : std::cout << " i=" << i << " gap=" << gap << " t=" << myHeadwayTime << " t2=" << headwayTime << " s=" << s << " pow=" << pow(newSpeed / desSpeed, myDelta) << " gapDecel=" << (s * s) / (gap * gap) << " a=" << acc;
257 : }
258 : #endif
259 1339791491 : newSpeed = MAX2(0.0, newSpeed + ACCEL2SPEED(acc) / myIterations);
260 : #ifdef DEBUG_V
261 : if (DEBUG_COND) {
262 : std::cout << " v2=" << newSpeed << " gLC=" << MSGlobals::gComputeLC << "\n";
263 : }
264 : #endif
265 : //TODO use more realistic position update which takes accelerated motion into account
266 2160459427 : gap -= MAX2(0., SPEED2DIST(newSpeed - predSpeed) / myIterations);
267 : }
268 371601485 : return MAX2(0., newSpeed);
269 : }
270 :
271 :
272 : MSCFModel*
273 0 : MSCFModel_IDM::duplicate(const MSVehicleType* vtype) const {
274 0 : return new MSCFModel_IDM(vtype, myIDMM);
275 : }
|