Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 : /****************************************************************************/
14 : /// @file RealisticEngineModel.cpp
15 : /// @author Michele Segata, Antonio Saverio Valente
16 : /// @date 4 Feb 2015
17 : ///
18 : // A detailed engine model
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <cmath>
23 : #include <stdio.h>
24 : #include <algorithm>
25 : #include <iostream>
26 :
27 : #include <utils/common/StdDefs.h>
28 : #include <utils/geom/GeomHelper.h>
29 : #include <utils/xml/XMLSubSys.h>
30 : #include <microsim/cfmodels/CC_Const.h>
31 : #include "RealisticEngineModel.h"
32 :
33 :
34 : // ===========================================================================
35 : // method definitions
36 : // ===========================================================================
37 0 : RealisticEngineModel::RealisticEngineModel() {
38 0 : className = "RealisticEngineModel";
39 0 : dt_s = 0.01;
40 0 : xmlFile = "vehicles.xml";
41 0 : minSpeed_mps = rpmToSpeed_mps(ep.minRpm, ep.wheelDiameter_m, ep.differentialRatio, ep.gearRatios[0]);
42 0 : }
43 :
44 :
45 0 : RealisticEngineModel::~RealisticEngineModel() {}
46 :
47 :
48 0 : double RealisticEngineModel::rpmToSpeed_mps(double rpm, double wheelDiameter_m = 0.94,
49 : double differentialRatio = 4.6, double gearRatio = 4.5) {
50 0 : return rpm * wheelDiameter_m * M_PI / (differentialRatio * gearRatio * 60);
51 : }
52 :
53 :
54 0 : double RealisticEngineModel::rpmToSpeed_mps(double rpm) {
55 0 : return ep.__rpmToSpeedCoefficient * rpm / ep.gearRatios[currentGear];
56 : }
57 :
58 :
59 0 : double RealisticEngineModel::speed_mpsToRpm(double speed_mps, double wheelDiameter_m,
60 : double differentialRatio, double gearRatio) {
61 0 : return speed_mps * differentialRatio * gearRatio * 60 / (wheelDiameter_m * M_PI);
62 : }
63 :
64 :
65 0 : double RealisticEngineModel::speed_mpsToRpm(double speed_mps) {
66 0 : return ep.__speedToRpmCoefficient * speed_mps * ep.gearRatios[currentGear];
67 : }
68 :
69 :
70 0 : double RealisticEngineModel::speed_mpsToRpm(double speed_mps, double gearRatio) {
71 0 : return ep.__speedToRpmCoefficient * speed_mps * gearRatio;
72 : }
73 :
74 :
75 0 : double RealisticEngineModel::rpmToPower_hp(double rpm, const struct EngineParameters::PolynomialEngineModelRpmToHp* engineMapping) {
76 0 : double sum = engineMapping->x[0];
77 0 : for (int i = 1; i < engineMapping->degree; i++) {
78 0 : sum += engineMapping->x[i] + pow(rpm, i);
79 : }
80 0 : return sum;
81 : }
82 :
83 :
84 0 : double RealisticEngineModel::rpmToPower_hp(double rpm) {
85 0 : if (rpm >= ep.maxRpm) {
86 : rpm = ep.maxRpm;
87 : }
88 0 : double sum = ep.engineMapping.x[0];
89 0 : for (int i = 1; i < ep.engineMapping.degree; i++) {
90 0 : sum += ep.engineMapping.x[i] * pow(rpm, i);
91 : }
92 0 : return sum;
93 : }
94 :
95 :
96 0 : double RealisticEngineModel::speed_mpsToPower_hp(double speed_mps,
97 : const struct EngineParameters::PolynomialEngineModelRpmToHp* engineMapping,
98 : double wheelDiameter_m, double differentialRatio,
99 : double gearRatio) {
100 0 : double rpm = speed_mpsToRpm(speed_mps, wheelDiameter_m, differentialRatio, gearRatio);
101 0 : return rpmToPower_hp(rpm, engineMapping);
102 : }
103 :
104 :
105 0 : double RealisticEngineModel::speed_mpsToPower_hp(double speed_mps) {
106 0 : return rpmToPower_hp(speed_mpsToRpm(speed_mps));
107 : }
108 :
109 :
110 0 : double RealisticEngineModel::speed_mpsToThrust_N(double speed_mps,
111 : const struct EngineParameters::PolynomialEngineModelRpmToHp* engineMapping,
112 : double wheelDiameter_m, double differentialRatio,
113 : double gearRatio, double engineEfficiency) {
114 0 : double power_hp = speed_mpsToPower_hp(speed_mps, engineMapping, wheelDiameter_m, differentialRatio, gearRatio);
115 0 : return engineEfficiency * power_hp * HP_TO_W / speed_mps;
116 : }
117 :
118 :
119 0 : double RealisticEngineModel::speed_mpsToThrust_N(double speed_mps) {
120 0 : double power_hp = speed_mpsToPower_hp(speed_mps);
121 0 : return ep.__speedToThrustCoefficient * power_hp / speed_mps;
122 : }
123 :
124 :
125 0 : double RealisticEngineModel::airDrag_N(double speed_mps, double cAir, double a_m2, double rho_kgpm3) {
126 0 : return 0.5 * cAir * a_m2 * rho_kgpm3 * speed_mps * speed_mps;
127 : }
128 :
129 :
130 0 : double RealisticEngineModel::airDrag_N(double speed_mps) {
131 0 : return ep.__airFrictionCoefficient * speed_mps * speed_mps;
132 : }
133 :
134 :
135 0 : double RealisticEngineModel::rollingResistance_N(double speed_mps, double mass_kg, double cr1, double cr2) {
136 0 : return mass_kg * GRAVITY_MPS2 * (cr1 + cr2 * speed_mps * speed_mps);
137 : }
138 :
139 :
140 0 : double RealisticEngineModel::rollingResistance_N(double speed_mps) {
141 0 : return ep.__cr1 + ep.__cr2 * speed_mps * speed_mps;
142 : }
143 :
144 :
145 0 : double RealisticEngineModel::gravityForce_N(double mass_kg, double slope = 0) {
146 0 : return mass_kg * GRAVITY_MPS2 * sin(slope / 180 * M_PI);
147 : }
148 :
149 :
150 0 : double RealisticEngineModel::gravityForce_N() {
151 0 : return ep.__gravity;
152 : }
153 :
154 :
155 0 : double RealisticEngineModel::opposingForce_N(double speed_mps, double mass_kg, double slope,
156 : double cAir, double a_m2, double rho_kgpm3,
157 : double cr1, double cr2) {
158 0 : return airDrag_N(speed_mps, cAir, a_m2, rho_kgpm3) +
159 0 : rollingResistance_N(speed_mps, mass_kg, cr1, cr2) +
160 0 : gravityForce_N(mass_kg, slope);
161 : }
162 :
163 :
164 0 : double RealisticEngineModel::opposingForce_N(double speed_mps) {
165 0 : return airDrag_N(speed_mps) + rollingResistance_N(speed_mps) + gravityForce_N();
166 : }
167 :
168 :
169 0 : double RealisticEngineModel::maxNoSlipAcceleration_mps2(double slope, double frictionCoefficient) {
170 0 : return frictionCoefficient * GRAVITY_MPS2 * cos(slope / 180 * M_PI);
171 : }
172 :
173 :
174 0 : double RealisticEngineModel::maxNoSlipAcceleration_mps2() {
175 0 : return ep.__maxNoSlipAcceleration;
176 : }
177 :
178 :
179 0 : double RealisticEngineModel::thrust_NToAcceleration_mps2(double thrust_N) {
180 0 : return thrust_N / ep.__maxAccelerationCoefficient;
181 : }
182 :
183 :
184 0 : int RealisticEngineModel::performGearShifting(double speed_mps, double acceleration_mps2) {
185 : int newGear = 0;
186 0 : const double delta = acceleration_mps2 >= 0 ? ep.shiftingRule.deltaRpm : -ep.shiftingRule.deltaRpm;
187 0 : for (newGear = 0; newGear < ep.nGears - 1; newGear++) {
188 0 : const double rpm = speed_mpsToRpm(speed_mps, ep.gearRatios[newGear]);
189 0 : if (rpm >= ep.shiftingRule.rpm + delta) {
190 : continue;
191 : } else {
192 : break;
193 : }
194 : }
195 0 : currentGear = newGear;
196 0 : return currentGear;
197 : }
198 :
199 :
200 0 : double RealisticEngineModel::maxEngineAcceleration_mps2(double speed_mps) {
201 0 : return speed_mpsToThrust_N(speed_mps) / ep.__maxAccelerationCoefficient;
202 : }
203 :
204 :
205 0 : double RealisticEngineModel::getEngineTimeConstant_s(double rpm) {
206 0 : if (rpm <= 0) {
207 : return TAU_MAX;
208 : } else {
209 0 : if (ep.fixedTauBurn)
210 : //in this case, tau_burn is fixed and is within __engineTauDe_s
211 : {
212 0 : return std::min(TAU_MAX, ep.__engineTau2 / rpm + ep.__engineTauDe_s);
213 : } else
214 : //in this case, tau_burn is dynamic and is within __engineTau1
215 : {
216 0 : return std::min(TAU_MAX, ep.__engineTau1 / rpm + ep.tauEx_s);
217 : }
218 : }
219 : }
220 :
221 :
222 0 : double RealisticEngineModel::getRealAcceleration(double speed_mps, double accel_mps2, double reqAccel_mps2, SUMOTime timeStep) {
223 : double realAccel_mps2;
224 : //perform gear shifting, if needed
225 0 : performGearShifting(speed_mps, accel_mps2);
226 : //since we consider no clutch (clutch always engaged), 0 speed would mean 0 rpm, and thus
227 : //0 available power. thus, the car could never start from a complete stop. so we assume
228 : //a minimum speed of 1 m/s to compute engine power
229 0 : double correctedSpeed = std::max(speed_mps, minSpeed_mps);
230 0 : if (reqAccel_mps2 >= 0) {
231 : //the system wants to accelerate
232 : //compute what is the maximum acceleration that can be delivered by the engine
233 0 : double engineAccel = maxEngineAcceleration_mps2(correctedSpeed);
234 : //the maximum acceleration is given by the maximum acceleration the engine can provide minus friction forces
235 0 : double maxAccel = engineAccel - thrust_NToAcceleration_mps2(opposingForce_N(speed_mps));
236 : //now we need to computed delayed acceleration due to actuation lag
237 0 : double tau = getEngineTimeConstant_s(speed_mpsToRpm(correctedSpeed));
238 0 : double alpha = ep.dt / (tau + ep.dt);
239 : //use standard first order lag with time constant depending on engine rpm
240 0 : realAccel_mps2 = alpha * reqAccel_mps2 + (1 - alpha) * accel_mps2;
241 : //limit the actual acceleration by considering friction forces and the maximum acceleration considering tyres
242 0 : realAccel_mps2 = std::min(std::min(realAccel_mps2, maxAccel), maxNoSlipAcceleration_mps2());
243 : } else {
244 0 : realAccel_mps2 = getRealBrakingAcceleration(speed_mps, accel_mps2, reqAccel_mps2, timeStep);
245 : }
246 0 : return realAccel_mps2;
247 : }
248 :
249 :
250 0 : void RealisticEngineModel::getEngineData(double speed_mps, int& gear, double& rpm) {
251 0 : gear = currentGear;
252 0 : rpm = speed_mpsToRpm(speed_mps);
253 0 : }
254 :
255 :
256 0 : double RealisticEngineModel::getRealBrakingAcceleration(double speed_mps, double accel_mps2, double reqAccel_mps2, SUMOTime t) {
257 : UNUSED_PARAMETER(t);
258 : //compute which part of the deceleration is currently done by frictions
259 0 : double frictionDeceleration = thrust_NToAcceleration_mps2(opposingForce_N(speed_mps));
260 : //the maximum deceleration is what the vehicle can do given the tyres plus the friction forces (friction can make the vehicle brake stronger)
261 0 : double maxDeceleration = -maxNoSlipAcceleration_mps2() - frictionDeceleration;
262 : //compute the new brakes deceleration
263 0 : double newBrakesAccel_mps2 = ep.__brakesAlpha * reqAccel_mps2 + ep.__brakesOneMinusAlpha * accel_mps2;
264 : //keep actual deceleration within physical limits (considering that wind force can help us slowing down)
265 0 : return std::max(maxDeceleration, newBrakesAccel_mps2);
266 :
267 : }
268 :
269 :
270 0 : void RealisticEngineModel::loadParameters() {
271 0 : VehicleEngineHandler engineHandler(vehicleType);
272 0 : if (!XMLSubSys::runParser(engineHandler, xmlFile)) {
273 0 : throw ProcessError();
274 : }
275 : //copy loaded parameters into our engine parameters
276 0 : ep = engineHandler.getEngineParameters();
277 0 : ep.dt = dt_s;
278 0 : ep.computeCoefficients();
279 : //compute "minimum speed" to be used when computing maximum acceleration at speeds close to 0
280 0 : minSpeed_mps = rpmToSpeed_mps(ep.minRpm, ep.wheelDiameter_m, ep.differentialRatio, ep.gearRatios[0]);
281 0 : }
282 :
283 :
284 0 : void RealisticEngineModel::setParameter(const std::string parameter, const std::string& value) {
285 0 : if (parameter == ENGINE_PAR_XMLFILE) {
286 0 : xmlFile = value;
287 : }
288 0 : if (parameter == ENGINE_PAR_VEHICLE) {
289 0 : vehicleType = value;
290 0 : if (xmlFile != "") {
291 0 : loadParameters();
292 : }
293 : }
294 0 : }
295 :
296 :
297 0 : void RealisticEngineModel::setParameter(const std::string parameter, double value) {
298 0 : if (parameter == ENGINE_PAR_DT) {
299 0 : dt_s = value;
300 : }
301 0 : }
302 :
303 :
304 0 : void RealisticEngineModel::setParameter(const std::string parameter, int value) {
305 : UNUSED_PARAMETER(parameter);
306 : UNUSED_PARAMETER(value);
307 0 : }
|