Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2011-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_Wiedemann.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date June 2011
19 : ///
20 : // The psycho-physical model of Wiedemann
21 : // references:
22 : // Andre Stebens - Traffic simulation with the Wiedemann model
23 : // Werner - Integration von Fahrzeugfolge- und Fahrstreifenwechselmodellen in die Nachtfahrsimulation LucidDrive
24 : // Olstam, Tapani - Comparison of Car-following models
25 : // Higgs, B. et.al - Analysis of theWiedemann car following model over different speeds using naturalistic data.
26 : // Ahmed, H.U.; Huang, Y.; Lu, P. - A Review of Car-Following Models and Modeling Tools forHuman and Autonomous-Ready Driving Behaviors in Micro-Simulation
27 :
28 : /****************************************************************************/
29 : #include <config.h>
30 :
31 : #include <cmath>
32 : #include "MSCFModel_Wiedemann.h"
33 : #include <microsim/MSVehicle.h>
34 : #include <microsim/MSLane.h>
35 : #include <utils/common/RandHelper.h>
36 : #include <utils/xml/SUMOSAXAttributes.h>
37 :
38 : //#define DEBUG_V
39 :
40 : // ===========================================================================
41 : // static members
42 : // ===========================================================================
43 :
44 : // magic constant proposed by Wiedemann (based on real world measurements)
45 : const double MSCFModel_Wiedemann::D_MAX = 150;
46 :
47 : #define B_MIN_MULT 0
48 : #define B_MIN_ADD -1
49 : #define PRED_DECEL_MULT 0.5
50 : #define PRED_DECEL_MULT_EMERGENCY 0.55
51 :
52 : // ===========================================================================
53 : // method definitions
54 : // ===========================================================================
55 291 : MSCFModel_Wiedemann::MSCFModel_Wiedemann(const MSVehicleType* vtype) :
56 : MSCFModel(vtype),
57 291 : mySecurity(vtype->getParameter().getCFParam(SUMO_ATTR_CF_WIEDEMANN_SECURITY, 0.5)),
58 291 : myEstimation(vtype->getParameter().getCFParam(SUMO_ATTR_CF_WIEDEMANN_ESTIMATION, 0.5)),
59 291 : myAX(vtype->getLength() + 1. + 2. * mySecurity),
60 291 : myCX(25. *(1. + mySecurity + myEstimation)),
61 291 : myMinAccel(0.2 * myAccel),
62 291 : myMaxApproachingDecel((myDecel + myEmergencyDecel) / 2) {
63 : // Wiedemann does not drive very precise and may violate minGap on occasion
64 291 : myCollisionMinGapFactor = vtype->getParameter().getCFParam(SUMO_ATTR_COLLISION_MINGAP_FACTOR, 0.1);
65 291 : }
66 :
67 :
68 582 : MSCFModel_Wiedemann::~MSCFModel_Wiedemann() {}
69 :
70 :
71 : void
72 10 : MSCFModel_Wiedemann::VehicleVariables::saveState(OutputDevice& out, const MSCFModel& /*cfm*/) const {
73 10 : out.openTag(SUMO_TAG_CFM_VARIABLES);
74 10 : out.writeAttr(SUMO_ATTR_ID, "Wiedemann");
75 10 : std::ostringstream internals;
76 10 : internals << accelSign;
77 10 : out.writeAttr(SUMO_ATTR_STATE, internals.str());
78 10 : out.closeTag();
79 10 : }
80 :
81 :
82 : void
83 10 : MSCFModel_Wiedemann::VehicleVariables::loadState(const SUMOSAXAttributes& attrs) {
84 10 : bool ok = true;
85 10 : const std::string cfmID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
86 10 : if (cfmID != "Wiedemann") {
87 0 : throw ProcessError(TLF("incompatible carFollowModel '%' when loading state for Wiedemann", cfmID));
88 : }
89 10 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
90 10 : bis >> accelSign;
91 20 : }
92 :
93 :
94 : double
95 3367019 : MSCFModel_Wiedemann::finalizeSpeed(MSVehicle* const veh, double vPos) const {
96 3367019 : const double vNext = MSCFModel::finalizeSpeed(veh, vPos);
97 : VehicleVariables* vars = (VehicleVariables*)veh->getCarFollowVariables();
98 3367019 : vars->accelSign = vNext > veh->getSpeed() ? 1. : -1.;
99 3367019 : return vNext;
100 : }
101 :
102 :
103 : double
104 22026540 : MSCFModel_Wiedemann::followSpeed(const MSVehicle* const veh, double /* speed */, double gap2pred, double predSpeed, double /*predMaxDecel*/, const MSVehicle* const pred, const CalcReason /*usage*/) const {
105 22026540 : return _v(veh, predSpeed, gap2pred, pred != nullptr ? pred->getAcceleration() : 0);
106 : }
107 :
108 :
109 : double
110 9457194 : MSCFModel_Wiedemann::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
111 : /* Wiedemann does not handle approaching junctions or stops very well:
112 : * regime approaching() fails when dv = 0 (i.e. a vehicle inserted with speed 0 does not accelerate to reach a stop)
113 : * for dv ~ 0 the standard decision tree will switch to following() which
114 : * does a lousy job of closing in on a stop / junction
115 : * hence we borrow from Krauss here
116 : */
117 9457194 : return MIN2(maximumSafeStopSpeed(gap, decel, speed, false, veh->getActionStepLengthSecs()), maxNextSpeed(speed, veh));
118 : }
119 :
120 :
121 : double
122 0 : MSCFModel_Wiedemann::interactionGap(const MSVehicle* const, double vL) const {
123 : UNUSED_PARAMETER(vL);
124 0 : return D_MAX;
125 : }
126 :
127 :
128 : MSCFModel*
129 0 : MSCFModel_Wiedemann::duplicate(const MSVehicleType* vtype) const {
130 0 : return new MSCFModel_Wiedemann(vtype);
131 : }
132 :
133 :
134 : double
135 19407231 : MSCFModel_Wiedemann::getSecureGap(const MSVehicle* const veh, const MSVehicle* const pred, const double speed, const double leaderSpeed, const double leaderMaxDecel) const {
136 19407231 : const double bx = (1 + 7 * mySecurity) * sqrt(speed);
137 19407231 : const double abx = myAX + bx - myType->getLength(); // abx is the brutto gap
138 19407231 : return MAX2(abx, MSCFModel::getSecureGap(veh, pred, speed, leaderSpeed, leaderMaxDecel));
139 : }
140 :
141 :
142 : double
143 22026540 : MSCFModel_Wiedemann::_v(const MSVehicle* veh, double predSpeed, double gap, double predAccel) const {
144 : const VehicleVariables* vars = (VehicleVariables*)veh->getCarFollowVariables();
145 22026540 : const double dx = gap + myType->getLength(); // wiedemann uses brutto gap
146 22026540 : const double v = veh->getSpeed();
147 22026540 : const double vpref = veh->getMaxSpeed();
148 22026540 : const double dv = v - predSpeed;
149 : // desired minimum following distance at low speed difference
150 22026540 : const double bx = (1 + 7 * mySecurity) * sqrt(v); // Harding propose a factor of *.8 here
151 22026540 : const double abx = myAX + bx; // Harding propose a factor of *.8 here
152 22026540 : const double ex = 2 - myEstimation; // + RandHelper::randNorm(0.5, 0.15)
153 22026540 : const double sdx = myAX + ex * bx; /// the distance at which we drift out of following
154 22026540 : const double sdv_root = (dx - myAX) / myCX;
155 22026540 : const double sdv = sdv_root * sdv_root;
156 22026540 : const double cldv = sdv * ex * ex;
157 22026540 : const double opdv = cldv * (-1 - 2 * RandHelper::randNorm(0.5, 0.15, veh->getRNG()));
158 : // D_MAX is too low to brake safely when driving at speeds above 36m/s
159 22026540 : const double dmax = MAX2(D_MAX, brakeGap(v, myDecel, 0));
160 : // select the regime, get new acceleration, compute new speed based
161 : double accel;
162 : int branch = 0;
163 22026540 : if (dx <= abx) {
164 332921 : accel = emergency(dv, dx, predAccel, v, gap, abx, bx);
165 : branch = 1;
166 21693619 : } else if (dx < sdx) {
167 2189302 : if (dv > cldv) {
168 595007 : accel = approaching(dv, dx, abx, predAccel);
169 : branch = 2;
170 1594295 : } else if (dv > opdv) {
171 851191 : accel = following(vars->accelSign);
172 : branch = 3;
173 : } else {
174 743104 : accel = fullspeed(v, vpref, dx, abx);
175 : branch = 4;
176 : }
177 : } else {
178 19504317 : if (dv > sdv && dx < dmax) { //@note other versions have an disjunction instead of conjunction
179 682884 : accel = approaching(dv, dx, abx, predAccel);
180 682884 : branch = 5;
181 : } else {
182 18821433 : accel = fullspeed(v, vpref, dx, abx);
183 : branch = 6;
184 : }
185 : }
186 : // since we have hard constraints on accel we may as well use them here
187 : #ifdef DEBUG_V
188 : const double rawAccel = accel;
189 : #endif
190 22026540 : accel = MAX2(MIN2(accel, myAccel), -myEmergencyDecel);
191 22026540 : const double vNew = MAX2(0., v + ACCEL2SPEED(accel)); // don't allow negative speeds
192 : #ifdef DEBUG_V
193 : if (veh->isSelected() && !MSGlobals::gComputeLC) {
194 : std::cout << SIMTIME << " Wiedemann::_v veh=" << veh->getID()
195 : << " v=" << v << " pV=" << predSpeed << " pA=" << predAccel << " gap=" << gap
196 : << " dv=" << dv << " dx=" << dx << " ax=" << myAX << " bx=" << bx << " abx=" << abx
197 : << " sdx=" << sdx << " sdv=" << sdv << " cldv=" << cldv << " opdv=" << opdv
198 : << " branch=" << branch << " rawAccel=" << rawAccel
199 : << " accel=" << accel << " vNew=" << vNew << "\n";
200 : }
201 : #else
202 : UNUSED_PARAMETER(branch);
203 : #endif
204 22026540 : return vNew;
205 : }
206 :
207 :
208 : double
209 19564537 : MSCFModel_Wiedemann::fullspeed(double v, double vpref, double dx, double abx) const {
210 : // maximum acceleration is reduced with increasing speed
211 19564537 : double bmax = 0.2 + 0.8 * myAccel * (7 - sqrt(v));
212 : // if veh just drifted out of a 'following' process the acceleration is reduced
213 19564537 : double accel = dx <= 2 * abx ? MIN2(myMinAccel, bmax * (dx - abx) / abx) : bmax;
214 19564537 : if (v > vpref) {
215 0 : accel = - accel;
216 : }
217 19564537 : return accel;
218 : }
219 :
220 :
221 : double
222 851191 : MSCFModel_Wiedemann::following(double sign) const {
223 851191 : return myMinAccel * sign;
224 : }
225 :
226 :
227 : double
228 1277891 : MSCFModel_Wiedemann::approaching(double dv, double dx, double abx, double predAccel) const {
229 : // there is singularity in the formula. we do the sanity check outside
230 : assert(abx < dx);
231 : // @note: the original model does not have a limit on maximum deceleration here.
232 : // We add this to avoid cascading emergency deceleration
233 : // also, the multiplier on predAccel is always 1 in the original model
234 1277891 : return MAX2(0.5 * dv * dv / (abx - dx) + predAccel * PRED_DECEL_MULT, -myMaxApproachingDecel);
235 : }
236 :
237 :
238 : double
239 332921 : MSCFModel_Wiedemann::emergency(double dv, double dx, double predAccel, double v, double gap, double abx, double bx) const {
240 : // wiedemann assumes that dx will always be larger than myAX (sumo may
241 : // violate this assumption when crashing (-:
242 : //
243 : // predAccel is called b_(n-1) in the literature and it's multipleir is always 1
244 :
245 332921 : if (dx > myAX) {
246 252136 : const double bmin = B_MIN_ADD + B_MIN_MULT * v;
247 252136 : const double accel = (0.5 * dv * dv / (myAX - dx)
248 252136 : + predAccel * PRED_DECEL_MULT_EMERGENCY
249 252136 : + bmin * (abx - gap) / bx);
250 252136 : return accel;
251 : } else {
252 80785 : return -myEmergencyDecel;
253 : }
254 :
255 : // emergency according to C.Werner
256 : //return -myEmergencyDecel;
257 : }
|