Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- VehicleMotor |
3 | -- |
4 | -- @author Stefan Geiger |
5 | -- @date 08/03/08 |
6 | -- |
7 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
8 | |
9 | VehicleMotor = {}; |
10 | |
11 | VehicleMotor_mt = Class(VehicleMotor); |
12 | |
13 | function VehicleMotor:new(vehicle, minRpm, maxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatio, backwardGearRatio, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, rpmFadeOutRange, maxTorque) |
14 | |
15 | local self = {}; |
16 | setmetatable(self, VehicleMotor_mt); |
17 | |
18 | self.vehicle = vehicle; |
19 | self.minRpm = minRpm; |
20 | self.maxRpm = maxRpm; |
21 | self.maxForwardSpeed = maxForwardSpeed; -- speed in m/s |
22 | self.maxBackwardSpeed = maxBackwardSpeed; |
23 | |
24 | self.maxClutchTorque = 5; -- amount of torque that can be transferred from motor to clutch/wheels [t m s^-2] |
25 | |
26 | self.torqueCurve = torqueCurve; |
27 | self.brakeForce = brakeForce; |
28 | |
29 | self.gear = 0; |
30 | self.gearRatio = 0; |
31 | |
32 | self.forwardGearRatios = {forwardGearRatio}; |
33 | self.backwardGearRatios = {backwardGearRatio}; |
34 | self.minForwardGearRatio = minForwardGearRatio; |
35 | self.maxForwardGearRatio = maxForwardGearRatio; |
36 | self.minBackwardGearRatio = minBackwardGearRatio; |
37 | self.maxBackwardGearRatio = maxBackwardGearRatio; |
38 | |
39 | self.lastMotorRpm = 0; |
40 | |
41 | self.rpmFadeOutRange = rpmFadeOutRange; |
42 | self.rpmLimit = math.huge; |
43 | self.speedLimit = math.huge; -- Speed limit in km/h |
44 | self.speedLimitAcc = math.huge; |
45 | |
46 | -- this is not clamped by minRpm |
47 | self.nonClampedMotorRpm = 0; |
48 | self.clutchRpm = 0; |
49 | self.motorLoad = 0; |
50 | self.requiredWheelTorque = 0; |
51 | |
52 | if self.maxForwardSpeed == nil then |
53 | self.maxForwardSpeed = self:calculatePhysicalMaximumForwardSpeed(); |
54 | end |
55 | if self.maxBackwardSpeed == nil then |
56 | self.maxBackwardSpeed = self:calculatePhysicalMaximumBackwardSpeed(); |
57 | end |
58 | |
59 | self.maxMotorTorque = self.torqueCurve:getMaximum(); |
60 | |
61 | self.ptoMotorRpmRatio = ptoMotorRpmRatio; |
62 | |
63 | self.maxTorque = maxTorque; |
64 | |
65 | return self; |
66 | end; |
67 | |
68 | function VehicleMotor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit) |
69 | self.lowBrakeForceScale = lowBrakeForceScale; |
70 | self.lowBrakeForceSpeedLimit = lowBrakeForceSpeedLimit; |
71 | end; |
72 | |
73 | function VehicleMotor:getTorque(acceleration, limitRpm) |
74 | -- Note: the torque curve is undefined outside the min/max rpm range. Clamping makes the curve flat at the outside range |
75 | local torque = self.torqueCurve:get(Utils.clamp(self.nonClampedMotorRpm, self.minRpm, self.maxRpm)); |
76 | local brakePedal = 0; |
77 | |
78 | if limitRpm then |
79 | local maxRpm = self:getCurMaxRpm(); |
80 | |
81 | local rpmFadeOutRange = self.rpmFadeOutRange*self:getGearRatio(); |
82 | |
83 | local fadeStartRpm = maxRpm - rpmFadeOutRange; |
84 | if self.nonClampedMotorRpm > fadeStartRpm then |
85 | if self.nonClampedMotorRpm > maxRpm then |
86 | brakePedal = math.min((self.nonClampedMotorRpm-maxRpm)/rpmFadeOutRange, 1); |
87 | torque = 0; |
88 | else |
89 | torque = torque * math.max((fadeStartRpm-self.nonClampedMotorRpm)/rpmFadeOutRange, 0); |
90 | end; |
91 | end; |
92 | end |
93 | torque = torque * math.abs(acceleration); |
94 | |
95 | local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self.vehicle); |
96 | if neededPtoTorque > 0 then |
97 | torque = math.max(torque - neededPtoTorque/self.ptoMotorRpmRatio, torque*0.1); |
98 | end |
99 | |
100 | return torque, brakePedal; |
101 | end; |
102 | |
103 | function VehicleMotor:getMaximumForwardSpeed() |
104 | return self.maxForwardSpeed; |
105 | end |
106 | |
107 | function VehicleMotor:getMaximumBackwardSpeed() |
108 | return self.maxBackwardSpeed; |
109 | end |
110 | |
111 | |
112 | function VehicleMotor:calculatePhysicalMaximumForwardSpeed() |
113 | local minRatio; |
114 | if self.minForwardGearRatio ~= nil then |
115 | minRatio = self.minForwardGearRatio |
116 | else |
117 | minRatio = math.huge; |
118 | for _, ratio in pairs(self.forwardGearRatios) do |
119 | minRatio = math.min(minRatio, ratio); |
120 | end |
121 | end |
122 | return self.maxRpm * math.pi / (30 * minRatio); |
123 | end |
124 | |
125 | function VehicleMotor:calculatePhysicalMaximumBackwardSpeed() |
126 | local minRatio; |
127 | if self.minBackwardGearRatio ~= nil then |
128 | minRatio = self.minBackwardGearRatio |
129 | else |
130 | minRatio = math.huge; |
131 | for _, ratio in pairs(self.backwardGearRatios) do |
132 | minRatio = math.min(minRatio, ratio); |
133 | end |
134 | end |
135 | return self.maxRpm * math.pi / (30 * minRatio); |
136 | end |
137 | |
138 | function VehicleMotor:updateMotorRpm(dt) |
139 | local vehicle = self.vehicle; |
140 | if next(vehicle.differentials) ~= nil and vehicle.motorizedNode ~= nil then |
141 | self.nonClampedMotorRpm, self.clutchRpm, self.motorLoad = getMotorRotationSpeed(vehicle.motorizedNode) |
142 | self.nonClampedMotorRpm = self.nonClampedMotorRpm * 30 / math.pi; |
143 | self.clutchRpm = self.clutchRpm * 30 / math.pi; |
144 | |
145 | --[[ |
146 | local motorLoad = math.max(self.motorLoad, 0); |
147 | -- We always require 30% of max torque extra |
148 | |
149 | local requiredWheelTorque = (motorLoad + 0.3*self.maxMotorTorque) * math.abs(self.gearRatio); |
150 | |
151 | local requiredWheelTorqueSmoothing = 0.3; |
152 | if requiredWheelTorque > self.requiredWheelTorque then |
153 | requiredWheelTorqueSmoothing = 0.05; |
154 | end |
155 | requiredWheelTorqueSmoothing = math.pow(requiredWheelTorqueSmoothing, dt*0.001); |
156 | |
157 | self.requiredWheelTorque = requiredWheelTorque + (self.requiredWheelTorque-requiredWheelTorque)*requiredWheelTorqueSmoothing; |
158 | ]]-- |
159 | |
160 | self.requiredWheelTorque = self.maxMotorTorque*math.abs(self.gearRatio); |
161 | |
162 | else |
163 | local gearRatio = self:getGearRatio(); |
164 | if vehicle.isServer then |
165 | self.nonClampedMotorRpm = math.max(WheelsUtil.computeRpmFromWheels(vehicle) * gearRatio, 0); |
166 | else |
167 | self.nonClampedMotorRpm = math.max(WheelsUtil.computeRpmFromSpeed(vehicle) * gearRatio, 0); |
168 | end |
169 | end |
170 | self.lastMotorRpm = math.max(self.nonClampedMotorRpm, self.minRpm); |
171 | -- the clamped motor rpm always is higher-equal than the required rpm by the pto |
172 | --local ptoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm); |
173 | -- smoothing for raise/fall of ptoRpm |
174 | if self.lastPtoRpm == nil then |
175 | self.lastPtoRpm = self.minRpm; |
176 | end; |
177 | local ptoRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio; |
178 | if ptoRpm > self.lastPtoRpm then |
179 | self.lastPtoRpm = math.min(ptoRpm, self.lastPtoRpm + self.maxRpm*dt/2000); |
180 | elseif ptoRpm < self.lastPtoRpm then |
181 | self.lastPtoRpm = math.max(self.minRpm, self.lastPtoRpm - self.maxRpm*dt/1000); |
182 | end; |
183 | local ptoRpm = math.min(self.lastPtoRpm, self.maxRpm); |
184 | self.lastMotorRpm = math.max(self.lastMotorRpm, ptoRpm); |
185 | end; |
186 | |
187 | -- accSafeMotorRpm: Select gear ratio so that motor RPM will at least accSafeMotorRpm lower than max rpm, so that we are able to accelerate |
188 | function VehicleMotor:getBestGearRatio(wheelSpeedRpm, minRatio, maxRatio, accSafeMotorRpm, requiredWheelTorque, requiredMotorRpm) |
189 | |
190 | if requiredMotorRpm ~= 0 then |
191 | local gearRatio = math.max(requiredMotorRpm-accSafeMotorRpm, requiredMotorRpm*0.8) / math.max(wheelSpeedRpm, 0.001); |
192 | gearRatio = Utils.clamp(gearRatio, minRatio, maxRatio); |
193 | return gearRatio; |
194 | end |
195 | |
196 | local bestWheelTorque = 0; |
197 | local bestGearRatio = minRatio; |
198 | -- TODO make this more efficient |
199 | for gearRatio = minRatio, maxRatio, 0.5 do |
200 | local rpm = wheelSpeedRpm * gearRatio; -- * (30/math.pi); |
201 | if rpm > self.maxRpm - accSafeMotorRpm then |
202 | break; |
203 | end |
204 | -- Note: optimizing the wheel torque is the same as optimizing the motor power |
205 | local wheelTorque = self.torqueCurve:get(rpm) * gearRatio; |
206 | if wheelTorque > bestWheelTorque then |
207 | bestWheelTorque = wheelTorque; |
208 | bestGearRatio = gearRatio; |
209 | end |
210 | |
211 | if wheelTorque >= requiredWheelTorque then |
212 | break; |
213 | end |
214 | end |
215 | |
216 | return bestGearRatio; |
217 | end |
218 | |
219 | function VehicleMotor:getBestGear(acceleration, wheelSpeedRpm, accSafeMotorRpm, requiredWheelTorque, requiredMotorRpm) |
220 | if acceleration > 0.001 then |
221 | if self.minForwardGearRatio ~= nil then |
222 | local wheelSpeedRpm = math.max(wheelSpeedRpm, 0); |
223 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minForwardGearRatio, self.maxForwardGearRatio, accSafeMotorRpm, requiredWheelTorque/acceleration, requiredMotorRpm); |
224 | return 1, bestGearRatio; |
225 | else |
226 | return 1, self.forwardGearRatios[1]; |
227 | end |
228 | elseif acceleration < -0.001 then |
229 | if self.minBackwardGearRatio ~= nil then |
230 | local wheelSpeedRpm = math.max(-wheelSpeedRpm, 0) |
231 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minBackwardGearRatio, self.maxBackwardGearRatio, accSafeMotorRpm, requiredWheelTorque/(-acceleration), requiredMotorRpm); |
232 | return -1, -bestGearRatio; |
233 | else |
234 | return -1, -self.backwardGearRatios[1]; |
235 | end |
236 | else |
237 | -- unchanged |
238 | return self.gear, self.gearRatio; |
239 | end |
240 | end |
241 | |
242 | function VehicleMotor:updateGear(acceleration) |
243 | local requiredWheelTorque = math.huge; |
244 | if (acceleration > 0) == (self.gearRatio > 0) then |
245 | requiredWheelTorque = self.requiredWheelTorque; |
246 | self.speedLimitAcc = self.maxForwardSpeed * 3.6 * math.abs(acceleration); |
247 | end |
248 | local requiredMotorRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio; |
249 | -- 1) safe rpm for acceleration is 10% of the motor max rpm, so it will take at least 10 frames (=0.16s) until a vehicle has fully accelerated |
250 | -- 2) replaced lastSpeedReal with clutchRPM/gearRatio => better acceleration when using high values for differentials and steeringAngle is at max/min |
251 | local wheelSpeedRpm; |
252 | if math.abs(self.gearRatio) < 0.001 then |
253 | wheelSpeedRpm = self.vehicle.lastSpeedReal*self.vehicle.movingDirection * 30000/ math.pi; |
254 | else |
255 | wheelSpeedRpm = (self.clutchRpm/self.gearRatio); -- * math.pi/30; |
256 | end |
257 | self.gear, self.gearRatio = self:getBestGear(acceleration, wheelSpeedRpm, self.maxRpm*0.1, requiredWheelTorque, requiredMotorRpm); |
258 | end |
259 | |
260 | function VehicleMotor:getGearRatio() |
261 | return self.gearRatio; |
262 | end; |
263 | |
264 | function VehicleMotor:getCurMaxRpm() |
265 | local maxRpm = self.maxRpm; |
266 | |
267 | local gearRatio = self:getGearRatio(); |
268 | if gearRatio ~= 0 then |
269 | --local speedLimit = self.speedLimit * 0.277778; |
270 | local speedLimit = math.min(self.speedLimit, math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600)) * 0.277778; |
271 | if gearRatio > 0 then |
272 | speedLimit = math.min(speedLimit, self.maxForwardSpeed); |
273 | else |
274 | speedLimit = math.min(speedLimit, self.maxBackwardSpeed); |
275 | end |
276 | |
277 | maxRpm = math.min(maxRpm, speedLimit * 30 / math.pi * math.abs(gearRatio)); |
278 | end |
279 | |
280 | maxRpm = math.min(maxRpm, self.rpmLimit); |
281 | return maxRpm; |
282 | end; |
283 | |
284 | function VehicleMotor:setSpeedLimit(limit) |
285 | self.speedLimit = math.max(limit, 1); |
286 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de