Finished manual gearbox

This commit is contained in:
Ivan Grachyov 2021-11-11 22:16:33 +05:00
parent f55c30c4d3
commit d73f424d7f
15 changed files with 588 additions and 62 deletions

View File

@ -1,14 +1,14 @@
Clutch = class("Clutch")
Clutch = class('Clutch')
function Clutch:initialize(options)
options = options or {}
self.stiffness = options.Stiffness or 0
self.capacity = options.Capacity or 0
self.damping = options.Damping or 0
self.maxTorque = options.MaxTorque or 0
self.stiffness = options.Stiffness or 10
self.capacity = options.Capacity or 2
self.damping = options.Damping or 0.3
self.maxTorque = options.MaxTorque or 150
self.press = 1
self.press = 0
self.slip = 0
self.targetTorque = 0
self.torque = 0
@ -25,9 +25,10 @@ function Clutch:update()
local gboxRatio = self._gearbox and self._gearbox.ratio or 0
local gboxRatioNotZero = gboxRatio ~= 0 and 1 or 0
self.slip = (engRPM * someConversionCoeff - gboxRPM * someConversionCoeff) * gboxRatioNotZero / 2
self.slip = ((engRPM - gboxRPM) * someConversionCoeff) * gboxRatioNotZero / 2
self.targetTorque = math.clamp(self.slip * self.stiffness * (1 - self.press), -self.maxTorque, self.maxTorque)
self.torque = math.lerp(self.torque, self.targetTorque, self.damping)
self.torque = math.lerp(self.damping, self.torque, self.targetTorque)
end
function Clutch:linkEngine(engine)
@ -37,3 +38,7 @@ end
function Clutch:linkGearbox(gbox)
self._gearbox = gbox
end
function Clutch:setPress(val)
self.press = math.clamp(val, 0, 1)
end

View File

@ -31,25 +31,30 @@ config = {
Clutch = {
Stiffness = 22,
Capacity = 1.3,
Damping = 0.5
Damping = 0.8,
MaxTorque = 600
},
Gearbox = {
Ratios = {2.93, 1.96, 1.38, 1.03, 1.06, 0.87},
Reverse = 3.26,
FinalDrive = 4.77
Ratios = {13.45, 8.12, 5.51, 4.16, 3.36, 2.83},
Reverse = 14.41,
FinalDrive = 1
},
FrontDiff = {
Power = 0.25,
Coast = 0.15,
Power = 1,
Coast = 1,
Preload = 10,
UsePowerBias = 10,
ViscousCoeff = 0.96
ViscousCoeff = 0.96,
Axle = Vector(1, 0, 0),
DistributionCoeff = 0.4
},
RearDiff = {
Power = 0.25,
Coast = 0.15,
Power = 1,
Coast = 1,
Preload = 10,
UsePowerBias = 10,
ViscousCoeff = 0.96
ViscousCoeff = 0.96,
Axle = Vector(1, 0, 0),
DistributionCoeff = 0.6
}
}

View File

@ -1,18 +1,104 @@
Differential = class("Differential")
-- @include ../libs/constants.txt
-- @include ../libs/table.txt
require('../libs/constants.txt')
require('../libs/table.txt')
Differential = class('Differential')
function Differential:initialize(options)
options = options or {}
options = options or {}
self.power = options.Power or 1
self.coast = options.Coast or 1
self.preload = options.Preload or 10
self.viscousCoeff = options.ViscousCoeff or 0.9
self.power = options.Power or 1
self.coast = options.Coast or 1
self.preload = options.Preload or 10
self.viscousCoeff = options.ViscousCoeff or 0.9
self.usePowerBias = options.UsePowerBias or 10
self.axle = options.Axle or Vector(1, 0, 0)
self.distributionCoeff = options.DistributionCoeff or 1
self._gearbox = options.Gearbox
self._leftWheel = options.LeftWheel
self._rightWheel = options.RightWheel
self.avgRPM = 0
if self._gearbox then
self._gearbox:linkDiff(self)
end
self._leftWheel = nil
self._rightWheel = nil
self.lwInertia = Vector(0)
self.rwInertia = Vector(0)
self.lwav = 0
self.rwav = 0
self.lwtq = 0
self.rwtq = 0
self.limitTorque = 250
end
function Differential:update()
if isValid(self._leftWheel) and isValid(self._rightWheel) then
self.lwav = math.rad(self._leftWheel:getAngleVelocity()[2])
self.rwav = math.rad(self._rightWheel:getAngleVelocity()[2]) * -1
self.avgRPM = (self.lwav + self.rwav) / 2 * RAD_TO_RPM
-- RAV = relative anglar velocity
local lwrav = self.rwav - self.lwav
local rwrav = self.lwav - self.rwav
local lwtq = lwrav * self.lwInertia
local rwtq = rwrav * self.rwInertia
-- Calculating damping torque here
local lwDampingTorque = self.viscousCoeff * lwtq
local rwDampingTorque = self.viscousCoeff * rwtq
-- Whether use diff power or diff coast
local usePower = self._gearbox and self._gearbox.torque > self.usePowerBias or false
local usedCoeff = usePower and self.power or self.coast
local lwPowerCoeff = usedCoeff
local rwPowerCoeff = usedCoeff
-- this could probably be shortened?
-- if lwtq - rwtq > self.preload then
-- lwPowerCoeff = usedCoeff
-- rwPowerCoeff = usedCoeff
-- end
-- if rwtq - lwtq > self.preload then
-- lwPowerCoeff = usedCoeff
-- rwPowerCoeff = usedCoeff
-- end
local lwCorrectingTorque = math.clamp(lwDampingTorque * lwPowerCoeff, -self.limitTorque, self.limitTorque)
local rwCorrectingTorque = math.clamp(rwDampingTorque * rwPowerCoeff, -self.limitTorque, self.limitTorque)
local gboxTorque = self._gearbox and self._gearbox.torque or 0 -- 6289.245
local lwCorrectedTorque = lwCorrectingTorque * 600 + gboxTorque * self.distributionCoeff * 52.49
local rwCorrectedTorque = rwCorrectingTorque * 600 + gboxTorque * self.distributionCoeff * 52.49
self.lwtq = lwCorrectingTorque
self.rwtq = rwCorrectingTorque
self._leftWheel:applyTorque(lwCorrectedTorque * -self._leftWheel:getRight() * TICK_INTERVAL)
self._rightWheel:applyTorque(rwCorrectedTorque * self._rightWheel:getRight() * TICK_INTERVAL)
end
end
function Differential:linkWheels(left, right)
if isValid(left) and isValid(right) then
self._leftWheel = left
self._rightWheel = right
self.lwInertia = left:getInertia():getLength()
self.rwInertia = right:getInertia():getLength()
end
end
function Differential:linkGearbox(gbox)
if gbox ~= nil then
self._gearbox = gbox
gbox:linkDiff(self)
end
end

View File

@ -0,0 +1,283 @@
@name Realistic Engine [Rework] Audi TT
@inputs [Axles]:array [Throttle Clutch GearUp GearDown]:number [FLW FRW RLW RRW]:entity
@outputs [IdleRPM MaxRPM RPM FinalDrive EngineTorque MaxTorque LoadTorque MasterThrottle]:number
@outputs [ClutchRPM ClutchSlip ClutchStiffness ClutchCapacity ClutchMaxTorque ClutchDamping ClutchTorque LastClutchTorque]:number
@outputs [Gear GearRatio GearboxRPM GearboxTorque]:number
@persist [AxlesPowerMap TorqueMap]:array [MasterThrottle FlywheelMass FlywheelRadius FlywheelInertia StartFriction FrictionCoeff LimiterDuration LimiterTime LoadTorque]:number
@persist [ClutchRPM ClutchSlip ClutchStiffness ClutchCapacity ClutchMaxTorque ClutchDamping ClutchTorque LastClutchTorque]:number
@persist [GearRatios]:array [Gear GearRatio]:number
@persist M:number
@inputs [B]:entity
@outputs [LWCorrectingTorque RWCorrectingTorque]
@outputs [AverageRPM]
@persist [LWTq RWTq] [LWAV RWAV]
@persist [LWDampingTq RWDampingTq]
@persist [LWPowerCoeff RWPowerCoeff]
@persist [LWCorrectedTorque RWCorrectedTorque]
@persist UsedCoeff
@persist [Power Coast Preload] LimitTorque UsePowerBias
@persist ViscousCoeff TickInterval
@model models/engines/inline4s.mdl
@trigger none
#include "libs/math"
if (duped()|dupefinished()) { reset() }
if (first()) {
M = 1
# --- ENGINE
IdleRPM = 900
MaxRPM = 7000
FlywheelMass = 4.9
FlywheelRadius = 0.378
StartFriction = -50
FrictionCoeff = 0.02
LimiterDuration = 0.05
TorqueMap = array(
240.29618749947,
251.33093929699,
262.3656910945,
273.40044289202,
284.43519468953,
295.46994648705,
306.50469828456,
317.53945008208,
328.57420187959,
339.60895367711,
350.64370547463,
361.67845727214,
372.71320906966,
383.74796086717,
394.78271266469,
405.8174644622,
416.85221625972,
427.88696805724,
438.92171985475,
449.95647165227,
449.9557104607,
449.95494926913,
449.95418807757,
449.953426886,
449.95266569443,
449.95190450287,
449.9511433113,
449.95038211973,
449.94962092816,
449.9488597366,
449.94809854503,
449.94733735346,
449.9465761619,
449.94581497033,
449.94505377876,
449.9442925872,
449.94353139563,
449.94277020406,
449.94200901249,
449.94124782093,
449.94048662936,
449.93972543779,
449.93896424623,
449.93820305466,
449.93744186309,
449.93668067153,
449.93591947996,
449.93515828839,
449.93439709682,
449.93363590526,
449.93287471369,
449.93211352212,
449.93135233056,
449.93059113899,
449.92982994742,
449.92906875586,
449.92830756429,
449.92754637272,
449.92678518115,
449.92602398959,
449.92526279802,
449.92450160645,
449.92374041489,
449.92297922332,
449.92221803175,
449.92145684019,
449.92069564862,
449.91993445705,
449.91917326548,
449.91841207392,
449.91765088235,
449.91688969078,
449.91612849922,
446.34504740794,
442.77396631665,
439.20288522537,
435.63180413409,
432.06072304281,
428.48964195153,
424.91856086025,
421.34747976897,
417.77639867769,
414.2053175864,
410.63423649512,
407.06315540384,
403.49207431256,
399.92099322128,
396.34991213,
392.77883103872,
389.20774994744,
385.63666885615,
382.06558776487,
378.49450667359,
374.92342558231,
371.35234449103,
367.78126339975,
364.21018230847,
360.63910121719,
357.0680201259,
353.49693903462,
349.92585794334
)
# --- CLUTCH
ClutchStiffness = 22
ClutchCapacity = 1.3
ClutchDamping = 0.5
GearRatios = array(-14.41, 0, 13.45, 8.12, 5.51, 4.16, 3.36, 2.83)
FinalDrive = 1
# --- Differential
Power = 0.1
Coast = 0.05
Preload = 10
UsePowerBias = 10
ViscousCoeff = 0.96
# ---
function void shiftGear(Direction:number) {
local PrevGear = Gear
Gear = clamp(Gear + Direction, -1, GearRatios:count() - 2)
GearRatio = GearRatios[Gear + 2, number]
}
function number remap(Value:number, A1:number, A2:number, B1:number, B2:number) {
return B1 + (B2 - B1) * (Value - A1) / (A2 - A1)
}
function number processAxle(Torque:number, LW:entity, RW:entity) {
local Axle = vec(0,1,0)
# Basic velocity calculations
local LWAV = toRad(LW:angVelVector():y())
local RWAV = toRad(RW:angVelVector():y()) * -1
# RAV = relative angular velocity
local LWRAV = RWAV - LWAV
local RWRAV = LWAV - RWAV
local LWTq = LWRAV * LW:inertia():y()
local RWTq = RWRAV * RW:inertia():y()
# Calculating damping torque
local LWDampingTq = ViscousCoeff * LWTq
local RWDampingTq = ViscousCoeff * RWTq
# Whether use diff power or diff coast
local UsePower = abs(Torque) > UsePowerBias
local UsedCoeff = UsePower ? Power : Coast
local LWPowerCoeff = 0
local RWPowerCoeff = 0
if (LWTq - RWTq > Preload)
{
LWPowerCoeff = UsedCoeff
RWPowerCoeff = 1 + (1 - UsedCoeff)
}
elseif (RWTq - LWTq > Preload)
{
RWPowerCoeff = UsedCoeff
LWPowerCoeff = 1 + (1 - UsedCoeff)
}
else
{
LWPowerCoeff = 1
RWPowerCoeff = 1
}
LWCorrectingTorque = clamp(LWDampingTq * LWPowerCoeff, -LimitTorque, LimitTorque)
RWCorrectingTorque = clamp(RWDampingTq * RWPowerCoeff, -LimitTorque, LimitTorque)
# Calculating final applied torque here
LWCorrectedTorque = LWCorrectingTorque + Torque / 2
RWCorrectedTorque = RWCorrectingTorque + Torque / 2
LW:applyTorque(LWCorrectedTorque * Axle * 52.49)
RW:applyTorque(RWCorrectedTorque * Axle * -1 * 52.49)
return (LW:angVelVector():y() + RW:angVelVector():y() * -1) / 2 / 6
}
MaxTorque = TorqueMap:max() * M
ClutchMaxTorque = 900
GearRatio = GearRatios[Gear + 2, number]
FlywheelInertia = FlywheelMass * FlywheelRadius ^ 2 / 2
EngineAngVel = IdleRPM / 9.5493
LimitTorque = ClutchMaxTorque
RPM = IdleRPM
LimiterTime = 0
runOnTick(1)
}
if ((changed(GearUp) && GearUp) || changed(GearDown) && GearDown) {
shiftGear(GearUp - GearDown)
}
if (tickClk()) {
GearboxTorque = ClutchTorque * GearRatio * FinalDrive
local AxelsRPM = max(processAxle(GearboxTorque / 2, FLW, FRW), processAxle(GearboxTorque / 2, RLW, RRW))
GearboxRPM = AxelsRPM * FinalDrive * GearRatio
ClutchSlip = ((RPM * 0.10472) - (GearboxRPM * 0.10472)) * (GearRatio != 0) / 2
local A = clamp(ClutchSlip * ClutchStiffness * (1 - Clutch), -ClutchMaxTorque, ClutchMaxTorque)
ClutchTorque = interpolate(ClutchTorque, A, ClutchDamping)
local LoadTorque = ClutchTorque
local RPMFraction = clamp((RPM - IdleRPM) / (MaxRPM - IdleRPM), 0, 1)
local Friction = StartFriction - RPM * FrictionCoeff
local MaxInitialTorque = TorqueMap[clamp(floor(RPMFraction * 100), 0, 100) + 1, number] * M - Friction
local IdleFade = clamp(remap(RPM, IdleRPM, IdleRPM + 600, 1, 0), 0, 1)#clamp(0 + (RPM - (IdleRPM + 500)) * (1 - 0) / (IdleRPM - (IdleRPM + 500)), 0, 1)
local IdleFade2 = clamp(remap(RPM, IdleRPM - 300, IdleRPM, 1, 0), 0, 1)
local AdditionalEngineSupply = (IdleFade) * (-Friction / MaxInitialTorque) + IdleFade2
if (RPM >= MaxRPM) {
Throttle = 0
LimiterTime = systime()
} else {
Throttle *= systime() >= LimiterTime + LimiterDuration
}
MasterThrottle = clamp(AdditionalEngineSupply + Throttle, 0, 1)
local RealInitialTorque = MaxInitialTorque * MasterThrottle
EngineTorque = RealInitialTorque + Friction
RPM += (EngineTorque - LoadTorque) / FlywheelInertia * 9.5493 * tickInterval()
RPM = max(RPM, 0)
B:applyTorque(vec(0, -GearboxTorque * 20, 0))
}

View File

@ -1,7 +1,7 @@
-- @include ../libs/constants.txt
require("../libs/constants.txt")
require('../libs/constants.txt')
Engine = class("Engine")
Engine = class('Engine')
function Engine:initialize(options)
options = options or {}

View File

@ -1,4 +1,4 @@
-- @include ./base.txt
require("./base.txt")
require('./base.txt')
AutomaticGearbox = class("AutomaticGearbox")
AutomaticGearbox = class('AutomaticGearbox')

View File

@ -1,4 +1,7 @@
Gearbox = class("Gearbox")
-- @include ../../libs/table.txt
require('../../libs/table.txt')
Gearbox = class('Gearbox')
function Gearbox:initialize(options)
options = options or {}
@ -7,8 +10,13 @@ function Gearbox:initialize(options)
self.finalDrive = options.FinalDrive
self.reverse = options.Reverse
self.rpm = 0
self.gear = 0
self:recalcRatio()
self.torque = 0
self._linkedDiffs = {}
self._clutch = nil
end
@ -18,12 +26,23 @@ function Gearbox:linkDiff(diff)
end
function Gearbox:shift(dir)
self:setGear(self.gear + dir)
end
function Gearbox:setGear(gear)
if gear >= -1 and gear <= #self.ratios then
self.gear = gear
self:recalcRatio()
end
end
function Gearbox:recalcRatio()
if self.gear == -1 then
self.ratio = -self.reverse
elseif self.gear == 0 then
self.ratio = 0
else
self.ratio = self.ratios[self.gear]
end
end
@ -33,5 +52,15 @@ function Gearbox:linkClutch(clutch)
end
function Gearbox:update()
if self._clutch ~= nil then
self.torque = self._clutch.torque * self.ratio * self.finalDrive
end
local axlesRPM = table.map(self._linkedDiffs, function(diff)
return diff.avgRPM
end)
local maxAxlesRPM = math.max(unpack(axlesRPM))
self.rpm = maxAxlesRPM * self.finalDrive * self.ratio
end

View File

@ -1,7 +1,7 @@
-- @include ./base.txt
require("./base.txt")
require('./base.txt')
CVT = class("CVT")
CVT = class('CVT')
function CVT:initialize(options)
options = options or {}

View File

@ -1,17 +1,40 @@
-- @include ./base.txt
require("./base.txt")
require('./base.txt')
ManualGearbox = class("ManualGearbox")
ManualGearbox = class('ManualGearbox')
function ManualGearbox:initialize(options)
options = options or {}
-- might want to segregate construction options for base gearbox
self.base = Gearbox:new(options)
self.torque = 0
self.rpm = 0
self.gear = 0
self.ratio = 0
end
function ManualGearbox:linkClutch(clutch)
return self.base:linkClutch(clutch)
end
-- proxy the methods here
function ManualGearbox:linkDiff(diff)
return self.base:linkDiff(diff)
end
function ManualGearbox:shift(dir)
return self.base:shift(dir)
end
function ManualGearbox:setGear(gear)
return self.base:setGear(gear)
end
function ManualGearbox:update()
self.base:update()
self.rpm = self.base.rpm
self.torque = self.base.torque
self.gear = self.base.gear
self.ratio = self.base.ratio
end

View File

@ -13,53 +13,100 @@
-- @include ./differential.txt
--
-- @include ./configs/audi_tt.txt
require("./engine.txt")
-- @include ../libs/watcher.txt
require('./engine.txt')
--
require("./clutch.txt")
require('./clutch.txt')
--
require("./gearboxes/manual.txt")
require("./gearboxes/cvt.txt")
require("./gearboxes/auto.txt")
require('./gearboxes/manual.txt')
require('./gearboxes/cvt.txt')
require('./gearboxes/auto.txt')
--
require("./differential.txt")
require('./differential.txt')
--
require("./configs/audi_tt.txt")
require('./configs/audi_tt.txt')
require('../libs/watcher.txt')
ECU = class("ECU")
ECU = class('ECU')
function ECU:initialize(options)
options = options or {}
self:adjustPorts()
self.engine = options.Engine
self.clutch = options.Clutch
self.gearbox = options.Gearbox
self.differentials = options.Differentials
self.input = {}
self:adjustPorts()
self.input = wire.ports.Input or nil
hook.add('tick', 'ecu_update', function()
self:update()
end)
self.shifterWatcher = watcher(function()
return self.input.Shifter.Value
end, function(val)
if self.gearbox == nil or val == 0 then
return
end
self.gearbox:shift(val)
end)
end
function ECU:adjustPorts()
wire.adjustPorts({
Input = "table"
Input = 'table',
FL = 'entity',
FR = 'entity',
RL = 'entity',
RR = 'entity'
}, {
RPM = "number",
Torque = "number"
RPM = 'number',
Torque = 'number',
ClutchTorque = 'number',
ClutchPress = 'number',
AvgRPM = 'number',
GearboxTorque = 'number',
GearboxRPM = 'number',
GearboxRatio = 'number',
Gear = 'number',
ClutchSlip = 'number'
})
end
function ECU:update()
wire.ports.RPM = self.engine.rpm
wire.ports.Torque = self.engine.torque
wire.ports.ClutchPress = self.clutch.press
wire.ports.ClutchTorque = self.clutch.torque
wire.ports.ClutchSlip = self.clutch.slip
wire.ports.GearboxTorque = self.gearbox.torque
wire.ports.GearboxRPM = self.gearbox.rpm
wire.ports.AvgRPM = self.differentials[1].avgRPM
wire.ports.Gear = self.gearbox.gear
wire.ports.GearboxRatio = self.gearbox.ratio
self.input = wire.ports.Input
self.engine:setThrottle(self.input.Throttle.Value)
if self.input.Throttle then
self.engine:setThrottle(self.input.Throttle.Value)
end
if self.input.Clutch then
self.clutch:setPress(self.input.Clutch.Value)
end
self.engine:update()
self.clutch:update()
self.gearbox:update()
self.shifterWatcher()
for _, diff in ipairs(self.differentials) do
diff:update()
end
end
local clutch = Clutch:new(config.Clutch)
@ -70,8 +117,19 @@ engine:linkClutch(clutch)
local gearbox = ManualGearbox:new(config.Gearbox)
gearbox:linkClutch(clutch)
local frontDifferential = Differential:new(config.FrontDiff)
frontDifferential:linkWheels(wire.ports.FL, wire.ports.FR)
frontDifferential:linkGearbox(gearbox)
local rearDifferential = Differential:new(config.RearDiff)
rearDifferential:linkWheels(wire.ports.RL, wire.ports.RR)
rearDifferential:linkGearbox(gearbox)
local differentials = {frontDifferential, rearDifferential}
local ecu = ECU:new({
Engine = engine,
Clutch = clutch
-- Gearbox = gearbox
Clutch = clutch,
Differentials = differentials,
Gearbox = gearbox
})

View File

@ -27,8 +27,8 @@ function Input:noKeysHolding(axle)
end
function Input:getAxleValue(axle)
local positiveValue = axle.Positive ~= nil and input.isKeyDown(axle.Positive)
local negativeValue = axle.Negative ~= nil and input.isKeyDown(axle.Negative)
local positiveValue = axle.Positive ~= nil and (input.isKeyDown(axle.Positive) or input.isMouseDown(axle.Positive))
local negativeValue = axle.Negative ~= nil and (input.isKeyDown(axle.Negative) or input.isMouseDown(axle.Negative))
return (positiveValue and 1 or 0) - (negativeValue and 1 or 0) -- (input.isKeyDown(axle.Positive) and 1 or 0) - (input.isKeyDown(axle.Negative) and 1 or 0)
end

View File

@ -9,5 +9,16 @@ Axles = {
Positive = KEY.W,
Gravity = 1,
Sensitivity = 1
}
},
Clutch = {
Positive = KEY.SHIFT,
Gravity = 0.15,
Sensitivity = 0.25
},
Shifter = {
Positive = MOUSE.LEFT,
Negative = MOUSE.RIGHT,
Gravity = 1,
Sensitivity = 1
},
}

View File

@ -93,3 +93,15 @@ function table.find(tbl, predicate)
end
end
end
function table.reduce(tbl, func, init)
init = init or 0
local accum = init
for _, field in ipairs(tbl) do
accum = accum + func(field)
end
return accum
end

View File

@ -0,0 +1,14 @@
function watcher(expr, cbk)
local lastVal = expr()
return function()
local newVal = expr()
if newVal == lastVal then
return
end
cbk(newVal, lastVal)
lastVal = newVal
end
end