Guest User

Modified esc file for beamng drive that solves EV an problem

a guest
May 5th, 2018
493
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 35.86 KB | None | 0 0
  1. local M = {}
  2. M.type = "auxilliary"
  3. M.relevantDevice = nil
  4. M.defaultOrder = 50
  5.  
  6. M.pauseESCAction = false
  7. M.calibrationMeasurementReady = false
  8. M.calibrationSettled = false
  9. M.doSettle = false
  10. M.doMeasure = false
  11. M.stiffnessFront = 0
  12. M.stiffnessRear = 0
  13. M.wheelAngleFront = 0
  14. M.wheelAngleRear = 0
  15. M.isExisting = true
  16.  
  17. --shorter functions for increased performance
  18. local pow = math.pow
  19. local abs = math.abs
  20. local min = math.min
  21. local max = math.max
  22. local sqrt = math.sqrt
  23. local cos = math.cos
  24. local pi = math.pi
  25.  
  26. local configData = nil
  27.  
  28. local escPulse = 0
  29. local tcsPulse = 0
  30.  
  31. --variables for ESC functionality
  32. local wheelCache = {} --cache for holding all our wheels
  33. local wheelCacheSize = 0
  34. local wheelNameCache = {}
  35. local otherWheelOnAxle = {}
  36. local crossWheels = {}
  37. local tcsWheelBrakeTorques = {}
  38. local revLimiterEngines = {}
  39. local revLimiterEngineCount = 0
  40.  
  41. local yawSmooth = nil --exponential smoothing for the yaw rate
  42. local desiredYawSmooth = nil --exponential smoothing for the yaw rate
  43. local invSquaredCharacteristicSpeed = 0--pre calculated, used for desired yaw calculation
  44.  
  45. local frontLeftWheelId = nil
  46. local frontRightWheelId = nil
  47. local rearRightWheelId = nil
  48. local rearLeftWheelId = nil
  49.  
  50. local escConfigurations = {}
  51. local currentESCConfiguration = nil
  52. local currentESCConfigurationKey = 1
  53. local lastESCConfigurationKey = -1
  54.  
  55. local wheelBase = 0 --m
  56. local invWheelBase = 0
  57. local distanceCOGFrontAxle = 0 --m
  58. local distanceCOGRearAxle = 0 --m
  59. local trackWidth = 0
  60. local mass = 0 --kg
  61.  
  62. local initialWheelCount = 0
  63. local escFailure = false
  64.  
  65. local desiredYawRateSteering = 0
  66. local desiredYawRateAcceleration = 0
  67. local yawRate = 0
  68. local speed = 0 --m/s
  69. local wheelAngleFront = 0
  70. local wheelAngleRear = 0
  71. local desiredYawRate = 0
  72. local yawDifference = 0
  73. local escEnableThreshold = 6 --m/s
  74.  
  75. local throttleFactor = 1
  76. local throttleFactorIntegral = 0
  77. local allWheelSlip = false
  78. local tempRevLimiterTimer = 0
  79. local tempRevLimiterActive = false
  80.  
  81. local tcsDeactivateThreshold = 0.05
  82. local tcsDeactivateSpeedThreshold = 0.55
  83.  
  84. local escActive = false
  85. local tcsActive = false
  86. local warningLightsDelayTime = 0.15 --s
  87. local warningLightsTimer = 0 --s
  88. local offColor = nil
  89.  
  90. --ESC calibbration
  91. local escMeasuringStepThreshold = 3500
  92. local escSettlingStepThreshold = 1500
  93. local escMeasuringStepCounter = 0
  94. local escSettlingStepCounter = 0
  95. local stiffnessFrontSum = 0
  96. local stiffnessRearSum = 0
  97.  
  98. --toggle debug mode
  99. local isDebugMode = 0
  100. local calibrateESC = nop
  101. local hasRegisteredQuickAccess = false
  102.  
  103. local function generateSteeringCurve(steeringAngle) --generates debug data to visualize the steering part of the desired yaw graph
  104.   local steeringData = {}
  105.   for i=0, 50, 1 do
  106.     steeringData[i+1] = abs((steeringAngle * invWheelBase) * (i / (1 + ((i * i) * invSquaredCharacteristicSpeed))))
  107.   end
  108.  
  109.   return steeringData
  110. end
  111.  
  112. local function generateAccelerationCurve() --generates debug data to visualize the acceleration part of the desired yaw graph
  113.   local accelerationData = {[1]=100000}
  114.   for i=1, 50, 1 do
  115.     accelerationData[i+1] = currentESCConfiguration.maxSideAcceleration / i
  116.   end
  117.  
  118.   return accelerationData
  119. end
  120.  
  121. local function updateGFX(dt)
  122.   warningLightsTimer = warningLightsTimer + dt
  123.  
  124.   if warningLightsTimer >= warningLightsDelayTime then
  125.     escPulse = escActive and bit.bxor(escPulse, 1) or 0
  126.     tcsPulse = tcsActive and bit.bxor(tcsPulse, 1) or 0
  127.     warningLightsTimer = 0
  128.   end
  129.  
  130.   if currentESCConfiguration.overrideESCPulse then
  131.     escPulse = currentESCConfiguration.overrideESCPulse
  132.   end
  133.  
  134.   if currentESCConfiguration.overrideTCSPulse then
  135.     tcsPulse = currentESCConfiguration.overrideTCSPulse
  136.   end
  137.  
  138.   if escFailure then
  139.     escPulse = 1
  140.     tcsPulse = 1
  141.   end
  142.  
  143.   electrics.values.esc = escPulse
  144.   electrics.values.tcs = tcsPulse
  145.  
  146.   if streams.willSend("escData") then
  147.     gui.send('escData', {
  148.         steeringAngle = math.deg(wheelAngleFront),
  149.         yawRate = yawRate,
  150.         desiredYawRate = desiredYawRate,
  151.         difference = yawDifference,
  152.         desiredYawRateAcceleration = desiredYawRateAcceleration * fsign(desiredYawRateSteering),
  153.         desiredYawRateSteering = desiredYawRateSteering,
  154.         steeringCurve = generateSteeringCurve(wheelAngleFront),
  155.         accelerationCurve = generateAccelerationCurve(),
  156.         maxSpeed = 50,
  157.         speed = speed,
  158.       })
  159.   end
  160.  
  161.   if streams.willSend("escInfo") then
  162.     gui.send('escInfo', {
  163.         ledColor = (escPulse > 0 or tcsPulse > 0) and offColor or currentESCConfiguration.activeColor
  164.       })
  165.   end
  166.  
  167.   if streams.willSend("tcsData") then
  168.     local lastSlips = {}
  169.     local wheelBrakeFactors = {}
  170.     for i = 0, wheelCacheSize - 1, 1 do
  171.       local wheel = wheelCache[i]
  172.       lastSlips[wheel.name] = wheel.tractionControlLastSlip
  173.       wheelBrakeFactors[wheel.name] = wheel.tractionControlBrakeFactor
  174.     end
  175.  
  176.     gui.send('tcsData', {
  177.         throttleFactor = throttleFactor,
  178.         wheelBrakeFactors = wheelBrakeFactors,
  179.         allWheelSlip = allWheelSlip and -0.2 or 0,
  180.         wheelSlips = lastSlips,
  181.         slipThreshold = currentESCConfiguration.slipThreshold,
  182.       })
  183.   end
  184. end
  185.  
  186. local function updateWheelsIntermediate(dt)
  187.   if not currentESCConfiguration.escEnabled or (electrics.values.gearIndex or 0) < 0 then
  188.     electrics.values.throttleFactor = 1
  189.     if tempRevLimiterActive then
  190.       tempRevLimiterActive = false
  191.       for i = 1, revLimiterEngineCount, 1 do
  192.         revLimiterEngines[i]:resetTempRevLimiter()
  193.       end
  194.     end
  195.     return
  196.   end
  197.  
  198.   --if we lose a wheel, we want to deactivate the ESC
  199.   if initialWheelCount ~= wheels.wheelCount then
  200.     M.updateWheelsIntermediate = nop
  201.     escFailure = true
  202.     electrics.values.throttleFactor = 1
  203.     tempRevLimiterActive = false
  204.     for i = 1, revLimiterEngineCount, 1 do
  205.       revLimiterEngines[i]:resetTempRevLimiter()
  206.     end
  207.     controller.cacheAllControllerFunctions()
  208.     return
  209.   end
  210.  
  211.   speed = electrics.values.wheelspeed
  212.   escActive = false
  213.   tcsActive = false
  214.  
  215.   local vectorForward = obj:getDirectionVector()
  216.   local vectorUp = obj:getDirectionVectorUp()
  217.   local vectorRight = vectorForward:cross(vectorUp)
  218.   local steeringInput = input.steering
  219.   local wheelFront = steeringInput < 0 and wheelCache[frontRightWheelId] or wheelCache[frontLeftWheelId]
  220.   local wheelRear = steeringInput < 0 and wheelCache[rearRightWheelId] or wheelCache[rearLeftWheelId]
  221.  
  222.   wheelAngleFront = math.acos(obj:nodeVecPlanarCos(wheelFront.node1, wheelFront.node2, vectorRight, vectorForward))
  223.   wheelAngleRear = math.acos(obj:nodeVecPlanarCos(wheelRear.node1, wheelRear.node2, vectorRight, vectorForward))
  224.   if wheelAngleFront > 1.5708 then
  225.     wheelAngleFront = (pi - wheelAngleFront)
  226.   end
  227.   wheelAngleFront = wheelAngleFront * fsign(-steeringInput)
  228.   if wheelAngleFront ~= wheelAngleFront then
  229.     wheelAngleFront = 0
  230.   end
  231.  
  232.   if wheelAngleRear > 1.5708 then
  233.     wheelAngleRear = (pi - wheelAngleRear)
  234.   end
  235.   wheelAngleRear = wheelAngleRear * fsign(-steeringInput)
  236.   if wheelAngleRear ~= wheelAngleRear then
  237.     wheelAngleRear = 0
  238.   end
  239.  
  240.   M.wheelAngleFront = wheelAngleFront
  241.   M.wheelAngleRear = wheelAngleRear
  242.  
  243.   ---------------------
  244.   ---------ESC---------
  245.   ---------------------
  246.  
  247.   yawRate = yawSmooth:get(-obj:getYawAngularVelocity())
  248.  
  249.   --calculate expected yaw rate based on steering angle
  250.   desiredYawRateSteering = ((wheelAngleFront * invWheelBase) * (speed / (1 + ((speed * speed * invSquaredCharacteristicSpeed)))))
  251.   --calculate expected yaw rate based on Gs
  252.   desiredYawRateAcceleration = currentESCConfiguration.maxSideAcceleration / (speed + 1e-30)
  253.  
  254.   --get the resulting desired yaw rate (smallest) and make sure to use the sign from the steering part (acceleration part is always positive)
  255.   desiredYawRate = fsign(desiredYawRateSteering) * min(abs(desiredYawRateSteering), abs(desiredYawRateAcceleration))
  256.   desiredYawRate = desiredYawSmooth:get(desiredYawRate)
  257.  
  258.   local counterSteerFlag = false
  259.   if yawRate * desiredYawRate < 0 then --check if we are counter steering while oversteering.
  260.     desiredYawRate = -desiredYawRate --If we do, we need to adjust our desired yaw rate because its sign is wrong at this point (since we are steering in the "wrong" direction
  261.     counterSteerFlag = true --we need to save this information because we need to alter the wheel that needs to be braked
  262.   end
  263.  
  264.   yawDifference = yawRate - desiredYawRate --calculate the difference between expected yaw and actual yaw, ~0 means we're all good, > 0 means oversteer, < 0 means understeer
  265.   local absYawDifference = abs(yawDifference)
  266.  
  267.   local escWheelToBrake = nil
  268.   local escDesiredBrakeTorque = 0
  269.   local escCorrectionAction = 0 -- 0 = nothing, 1 = oversteer, -1 = understeer
  270.   if speed >= escEnableThreshold and absYawDifference > currentESCConfiguration.escThreshold and not M.pauseESCAction then --only act if we are fast enough and pass the threshold
  271.     yawDifference = yawDifference - (fsign(yawDifference) * currentESCConfiguration.escThreshold)
  272.  
  273.     if abs(yawRate) > abs(desiredYawRate) or counterSteerFlag then --Oversteer
  274.       if yawRate > 0 then --turning left
  275.         escWheelToBrake = frontRightWheelId
  276.       elseif yawRate < 0 then --turning right
  277.         escWheelToBrake = frontLeftWheelId
  278.       end
  279.       escCorrectionAction = 1
  280.     else --Understeer
  281.       escCorrectionAction = -1
  282.       if yawRate > 0 then --turning left
  283.         escWheelToBrake = rearLeftWheelId
  284.  
  285.         if counterSteerFlag then
  286.           escWheelToBrake = frontLeftWheelId --switch to the FRONT wheel when braking, otherwise we would work AGAINST the driver while counter steering
  287.           yawDifference = yawDifference * 0.5 --also reduce the severity of the brake action for a smoother drift
  288.           escCorrectionAction = 1
  289.         end
  290.       elseif yawRate < 0 then --turning right
  291.         escWheelToBrake = rearRightWheelId
  292.  
  293.         if counterSteerFlag then
  294.           escWheelToBrake = frontRightWheelId
  295.           yawDifference = yawDifference * 0.5
  296.           escCorrectionAction = 1
  297.         end
  298.       end
  299.     end
  300.  
  301.     if escWheelToBrake ~= nil then
  302.       escActive = true
  303.       local wheel = wheelCache[escWheelToBrake] --get the wheel we need to brake
  304.       wheel.stabilityControlBrakeIntegral = min(max(wheel.stabilityControlBrakeIntegral + absYawDifference * dt, 0), currentESCConfiguration.maxIntegralPart)
  305.  
  306.       local wheelAntiLockupFactor = min(max((abs(wheel.angularVelocity) - 10) * 0.1, 0), 1)--factor in some coef from the wheelspeed, if the AV drops below 1 we gradually lower the braking torque to prevent wheel lockup
  307.       local brakeCoef = min((absYawDifference * currentESCConfiguration.proportionalFactor + wheel.stabilityControlBrakeIntegral * currentESCConfiguration.integralFactor) * wheelAntiLockupFactor, 1)
  308.       local brakingTorque = min(brakeCoef * currentESCConfiguration.brakeForceMultiplier * wheel.brakeTorque, wheel.brakeTorque) --calculate our actual braking torque based on the brake's maximum torque
  309.  
  310.       escDesiredBrakeTorque = brakingTorque
  311.     end
  312.   else
  313.     yawDifference = 0
  314.     for i = 0, wheelCacheSize - 1, 1 do
  315.       wheelCache[i].stabilityControlBrakeIntegral = 0
  316.     end
  317.   end
  318.  
  319.   ---------------------
  320.   ---------------------
  321.   ---------------------
  322.  
  323.  
  324.  
  325.  
  326.   ---------------------
  327.   ---------TCS---------
  328.   ---------------------
  329.  
  330.   allWheelSlip = true
  331.   local averageSlipError = 0
  332.   local peakSlipError = 0
  333.   local wheelCount = 0
  334.   local tcsWheelBrakeCount = wheelCacheSize
  335.   local throttle = electrics.values.throttle or 0
  336.  
  337.   if speed < tcsDeactivateSpeedThreshold or throttle < tcsDeactivateThreshold or M.pauseESCAction then
  338.     --If we are braking or barely accelerating or below a certain speed, TCS is deactivated and resets all interesting values
  339.     throttleFactor = 1
  340.     for i = 0, wheelCacheSize - 1, 1 do
  341.       local wheel = wheelCache[i]
  342.       wheel.tractionControlBrakeFactor = 0
  343.       wheel.tractionControlBrakeIntegral = 0
  344.       wheel.tractionControlLastSlip = 0
  345.     end
  346.     allWheelSlip = false
  347.     throttleFactorIntegral = 0
  348.   else
  349.     for i = 0, wheelCacheSize - 1, 1 do
  350.       local wheel = wheelCache[i]
  351.       if wheel.isPropulsed then
  352.         wheelCount = wheelCount + 1
  353.         --Look at the AV of each propulsed wheel
  354.         local wheelSpeed = wheel.speedSmoother:get(wheel.angularVelocity * wheel.wheelDir / wheel.tractionControlSpeedCorrectionFactor) --take different turning radius into account for wheelspeed
  355.         if abs(wheelSpeed) < 1 then
  356.           wheelSpeed = 0
  357.         end
  358.         local crossWheelName = crossWheels[i]
  359.         local crossWheel = wheelCache[crossWheelName]
  360.         local crossWheelSpeed = crossWheel.speedSmoother:get(crossWheel.angularVelocity * crossWheel.wheelDir * crossWheel.tractionControlSpeedCorrectionFactor) --take different turning radius into account for expected wheelspeed
  361.         if abs(crossWheelSpeed) < 1 then
  362.           crossWheelSpeed = 0
  363.         end
  364.         if wheelSpeed * crossWheelSpeed < 0 and (abs(wheelSpeed) - abs(crossWheelSpeed) > 10) then
  365.           crossWheelSpeed = fsign(wheelSpeed) * crossWheelSpeed
  366.         end
  367.         --And calculate how much deviation there is compared to the diagonal wheel
  368.         local crossWheelSlip = min(max((wheelSpeed - crossWheelSpeed) / (wheelSpeed + 1e-30), 0), 1) --make sure wheelSpeed can never be exactly 0 so we can divide by it
  369.  
  370.         local slipError = wheelSpeed > currentESCConfiguration.tcsWheelSpeedThreshold and crossWheelSlip - currentESCConfiguration.slipThreshold or 0
  371.         averageSlipError = averageSlipError + slipError
  372.         peakSlipError = max(peakSlipError, slipError)
  373.  
  374.         wheel.tractionControlBrakeIntegral = min(max(wheel.tractionControlBrakeIntegral + slipError * dt, -1), 2)
  375.         wheel.tractionControlBrakeFactor = min(max(slipError * currentESCConfiguration.brakingProportionalFactor + wheel.tractionControlBrakeIntegral * currentESCConfiguration.brakingIntegralFactor, 0), currentESCConfiguration.maxBrakingFactor)
  376.  
  377.         --Check what to do, either tell the next frame that we don't have allWheelSlip (if we are with any wheel below the slip threshold)
  378.         --or go on and counter act the slip which is above the threshold
  379.         if crossWheelSlip <= currentESCConfiguration.slipThreshold then
  380.           --No slip here, let the next frame know that we have at least one good wheel
  381.           allWheelSlip = false
  382.           tcsWheelBrakeTorques[i] = 0
  383.         else
  384.           --try to reduce the slip based on the current slip value and the last frame's information about allWheelSlip
  385.           tcsActive = true --activate the esc light
  386.           tcsWheelBrakeTorques[i] = wheel.brakeTorque * wheel.tractionControlBrakeFactor
  387.         end
  388.  
  389.         wheel.tractionControlLastSlip = crossWheelSlip --save slip for debug app
  390.       end
  391.     end
  392.   end
  393.  
  394.   local absWheelAngle = abs(wheelAngleFront)
  395.   if absWheelAngle > 0.01 then
  396.     --calculate wheel speed correction factors for next frame (different wheel speeds because of turning
  397.     local innerTurningCircleRadius = wheelBase / (sqrt(1 - pow(cos(absWheelAngle), 2)) + 1e-30)
  398.     local outerTurningCircleRadius = innerTurningCircleRadius + trackWidth
  399.  
  400.     local ratio = outerTurningCircleRadius / innerTurningCircleRadius
  401.     if wheelAngleFront > 0 then --turning left
  402.       wheelCache[frontRightWheelId].tractionControlSpeedCorrectionFactor = ratio --outer wheels will turn faster
  403.       wheelCache[rearRightWheelId].tractionControlSpeedCorrectionFactor = ratio --outer wheels will turn faster
  404.       wheelCache[frontLeftWheelId].tractionControlSpeedCorrectionFactor = 1
  405.       wheelCache[rearLeftWheelId].tractionControlSpeedCorrectionFactor = 1
  406.     else --turning right
  407.       wheelCache[frontLeftWheelId].tractionControlSpeedCorrectionFactor = ratio --outer wheels will turn faster
  408.       wheelCache[rearLeftWheelId].tractionControlSpeedCorrectionFactor = ratio --outer wheels will turn faster
  409.       wheelCache[frontRightWheelId].tractionControlSpeedCorrectionFactor = 1
  410.       wheelCache[rearRightWheelId].tractionControlSpeedCorrectionFactor = 1
  411.     end
  412.   else
  413.     --reset all values, we are not turning
  414.     wheelCache[frontLeftWheelId].tractionControlSpeedCorrectionFactor = 1
  415.     wheelCache[rearLeftWheelId].tractionControlSpeedCorrectionFactor = 1
  416.     wheelCache[frontRightWheelId].tractionControlSpeedCorrectionFactor = 1
  417.     wheelCache[rearRightWheelId].tractionControlSpeedCorrectionFactor = 1
  418.   end
  419.  
  420.   ---------------------
  421.   ---------------------
  422.   ---------------------
  423.  
  424.  
  425.  
  426.  
  427.   ---------------------
  428.   ---Decision Block----
  429.   ---------------------
  430.  
  431.   local reduceThrottle = false
  432.   local brakeESCWheel = false
  433.   local brakeTCSWheel = false
  434.   local throttleReduceCoef = 1
  435.   local previousTempRevLimiterTimer = tempRevLimiterTimer
  436.   if escActive and tcsActive then
  437.     tcsWheelBrakeCount = 0
  438.     reduceThrottle = true
  439.     brakeESCWheel = true
  440.     tempRevLimiterTimer = 0.3
  441.   elseif escActive then
  442.     brakeESCWheel = true
  443.     reduceThrottle = true
  444.     tempRevLimiterTimer = 0.2
  445.   elseif tcsActive and not allWheelSlip and speed < currentESCConfiguration.brakeThrottleSwitchThreshold then
  446.     brakeTCSWheel = true
  447.   elseif tcsActive then
  448.     reduceThrottle = true
  449.   end
  450.  
  451.   if brakeESCWheel then
  452.     local wheel = wheelCache[escWheelToBrake]
  453.     local otherWheel = wheelCache[otherWheelOnAxle[escWheelToBrake]] --get the other wheel on the axle from our prepared mapping
  454.     if wheel.desiredBrakingTorque == 0 and otherWheel.desiredBrakingTorque == 0 then --if we are not braking, apply torque normally
  455.       wheel.desiredBrakingTorque = escDesiredBrakeTorque
  456.     else --if we ARE already braking, we need to adjust to that and potentially reduce braking torque on other wheels for the desired effect
  457.       if wheel.absActive or (wheel.desiredBrakingTorque + escDesiredBrakeTorque) > wheel.brakeTorque then
  458.         --in case we are already at the limit of grip (ABS active) or we can't add any additional torque to our target wheel anymore
  459.         --Simply reduce the torque on the other axle wheel (this assumes that both wheel have roughly similar brake torques)
  460.         otherWheel.desiredBrakingTorque = max(otherWheel.desiredBrakingTorque - escDesiredBrakeTorque, 0)
  461.       else
  462.         --in the simply case of additional available grip, add our esc torque on top of the already existing brake torque
  463.         --this might not work prefectly if we are right below the grip threshold
  464.         wheel.desiredBrakingTorque = min(wheel.desiredBrakingTorque + escDesiredBrakeTorque, wheel.brakeTorque)
  465.       end
  466.     end
  467.   end
  468.  
  469.   if brakeTCSWheel then
  470.     for i = 0, tcsWheelBrakeCount - 1, 1 do
  471.       wheelCache[i].desiredBrakingTorque = wheelCache[i].desiredBrakingTorque + tcsWheelBrakeTorques[i]
  472.     end
  473.   end
  474.  
  475.   if reduceThrottle then
  476.     local slipError = peakSlipError
  477.     throttleFactorIntegral = max(min(throttleFactorIntegral + slipError * dt, 1), 0)
  478.     local throttleFactorPI = slipError * currentESCConfiguration.throttleProportionalFactor + throttleFactorIntegral * currentESCConfiguration.throttleIntegralFactor
  479.     throttleFactor = min(max(1 - throttleFactorPI, currentESCConfiguration.minThrottleFactor), 1)
  480.   else
  481.     throttleFactorIntegral = max(min(throttleFactorIntegral - currentESCConfiguration.slipThreshold * dt, 1), 0)
  482.     throttleFactor = min(max(1 - (throttleFactorIntegral * currentESCConfiguration.throttleIntegralFactor), currentESCConfiguration.minThrottleFactor), 1)
  483.   end
  484.  
  485.   if tempRevLimiterTimer > previousTempRevLimiterTimer then
  486.     tempRevLimiterActive = true
  487.     for i = 1, revLimiterEngineCount, 1 do
  488.       local engine = revLimiterEngines[i]
  489.       if engine != nil then
  490.         engine:setTempRevLimiter(engine.outputAV1)
  491.       end
  492.     end
  493.   elseif tempRevLimiterTimer <= 0 and tempRevLimiterActive then
  494.     tempRevLimiterActive = false
  495.     for i = 1, revLimiterEngineCount, 1 do
  496.       if revLimiterEngines[i] != nil then
  497.         revLimiterEngines[i]:resetTempRevLimiter()
  498.       end
  499.     end
  500.   end
  501.  
  502.   tempRevLimiterTimer = tempRevLimiterTimer - dt
  503.  
  504.   --Apply our throttle factor
  505.   electrics.values.throttleFactor = throttleFactor
  506.   ---------------------
  507.   ---------------------
  508.   ---------------------
  509.  
  510.   --use existing ESC data for calibration purposes (nop'ed when not in use)
  511.   calibrateESC(wheelAngleFront, wheelAngleRear, yawRate)
  512. end
  513.  
  514. local function doCalibration(wheelAngleFront, wheelAngleRear, yawRate)
  515.   if M.doMeasure then
  516.     escMeasuringStepCounter = escMeasuringStepCounter + 1
  517.  
  518.     local velocity = vec3(obj:getVelocity())
  519.     local directionVector = vec3(obj:getDirectionVector())
  520.     local actualVelocity = (directionVector:dot(velocity) / (directionVector:length() * directionVector:length()) * directionVector):length()
  521.     local velocityVector = vec3(velocity.x, velocity.y, 0)
  522.     local dot = velocityVector:dot(directionVector)
  523.     local floatAngle = math.acos(dot / (directionVector:length() * velocityVector:length()))
  524.  
  525.     yawRate = abs(yawRate) * -1
  526.     wheelAngleFront = abs(wheelAngleFront) * -1
  527.     wheelAngleRear = abs(wheelAngleRear) * -1
  528.  
  529.     local stiffnessFront = (distanceCOGRearAxle * mass * yawRate * actualVelocity) / ((distanceCOGFrontAxle + distanceCOGRearAxle) * (wheelAngleFront - (distanceCOGFrontAxle * yawRate / actualVelocity) - floatAngle ))
  530.     local stiffnessRear = ((yawRate * mass * actualVelocity) - stiffnessFront * (wheelAngleFront - (distanceCOGFrontAxle * yawRate / actualVelocity) - floatAngle)) / (wheelAngleRear + (distanceCOGRearAxle * yawRate / actualVelocity) - floatAngle)
  531.  
  532.     stiffnessFrontSum = stiffnessFrontSum + stiffnessFront
  533.     stiffnessRearSum = stiffnessRearSum + stiffnessRear
  534.  
  535.  
  536.     if escMeasuringStepCounter >= escMeasuringStepThreshold then
  537.       M.stiffnessFront = stiffnessFrontSum / escMeasuringStepCounter
  538.       M.stiffnessRear = stiffnessRearSum / escMeasuringStepCounter
  539.       stiffnessFrontSum = 0
  540.       stiffnessRearSum = 0
  541.       M.doMeasure = false
  542.       M.calibrationMeasurementReady = true
  543.       escMeasuringStepCounter = 0
  544.     end
  545.   end
  546.  
  547.   if M.doSettle then
  548.     escSettlingStepCounter = escSettlingStepCounter + 1
  549.     if escSettlingStepCounter >= escSettlingStepThreshold then
  550.       M.doSettle = false
  551.       M.calibrationSettled = true
  552.       escSettlingStepCounter = 0
  553.     end
  554.   end
  555. end
  556.  
  557. local function startESCCalibration()
  558.   M.calibrationMeasurementReady = false
  559.   M.doMeasure = false
  560.   M.doSettle = false
  561.   M.calibrationSettled = false
  562.   stiffnessFrontSum = 0
  563.   stiffnessRearSum = 0
  564.   M.stiffnessFront = 0
  565.   M.stiffnessRear = 0
  566.   escMeasuringStepCounter = 0
  567.   escSettlingStepCounter = 0
  568.   calibrateESC = doCalibration
  569. end
  570.  
  571. local function stopESCCalibration()
  572.   calibrateESC = nop
  573. end
  574.  
  575. local function getCarData()
  576.   if mass <= 0 then
  577.     return nil
  578.   end
  579.  
  580.   return {
  581.     wheels = wheelCache,
  582.     frontLeftWheelId = frontLeftWheelId,
  583.     frontRightWheelId = frontRightWheelId,
  584.     rearLeftWheelId = rearLeftWheelId,
  585.     rearRightWheelId = rearRightWheelId,
  586.     wheelBase = wheelBase,
  587.     trackWidth = trackWidth,
  588.     distanceCOGRearAxle = distanceCOGRearAxle,
  589.     distanceCOGFrontAxle = distanceCOGFrontAxle,
  590.     mass = mass,
  591.   }
  592. end
  593.  
  594. local function getCurrentConfigData()
  595.   return currentESCConfiguration
  596. end
  597.  
  598. local function sanitizeConfiguration(config, name)
  599.   config.name = name
  600.   config.escEnabled = config.escEnabled or false
  601.   config.activeColor = config.activeColor or "98FB00"
  602.   config.characteristicSpeed = config.characteristicSpeed or 0
  603.  
  604.   config.proportionalFactor = config.proportionalFactor or 1
  605.   config.integralFactor = config.integralFactor or 0
  606.   config.maxIntegralPart = config.maxIntegralPart or 2
  607.  
  608.   config.maxSideAcceleration = config.maxSideAcceleration or 0
  609.   config.brakeForceMultiplier = config.brakeForceMultiplier or 0
  610.   config.escThreshold = config.escThreshold or 0
  611.   config.skewStiffnessFront = config.skewStiffnessFront or 1
  612.   config.skewStiffnessRear = config.skewStiffnessRear or 1
  613.   config.desiredYawRateSmoothing = config.desiredYawRateSmoothing or 500
  614.  
  615.   config.slipThreshold = config.slipThreshold or 0
  616.   config.tcsWheelSpeedThreshold = config.tcsWheelSpeedThreshold or 10
  617.  
  618.   config.throttleProportionalFactor = config.throttleProportionalFactor or 1.5
  619.   config.throttleIntegralFactor = config.throttleIntegralFactor or 1
  620.  
  621.   config.brakingProportionalFactor = config.brakingProportionalFactor or 1.2
  622.   config.brakingIntegralFactor = config.brakingIntegralFactor or 0
  623.  
  624.   config.maxBrakingFactor = max(min(config.maxBrakingFactor or 0, 1),0)
  625.   config.minThrottleFactor = max(min(config.minThrottleFactor or 1, 1),0)
  626.  
  627.   config.brakeThrottleSwitchThreshold = config.brakeThrottleSwitchThreshold or 10
  628.  
  629.   return config
  630. end
  631.  
  632. local function preCalculate()
  633.   invSquaredCharacteristicSpeed = 1 / (currentESCConfiguration.characteristicSpeed * currentESCConfiguration.characteristicSpeed)
  634. end
  635.  
  636. local function setESCMode(key)
  637.   if currentESCConfiguration == nil then --if we don't have any current config, the vehicle does not have esc at all, abort here
  638.     return
  639.   end
  640.  
  641.   if initialWheelCount ~= wheels.wheelCount then --if we have detached wheels, the esc is disabled anyway, no need to switch modes anymore
  642.     return
  643.   end
  644.  
  645.   currentESCConfigurationKey = key
  646.   if currentESCConfigurationKey > #escConfigurations then
  647.     currentESCConfigurationKey = 1
  648.   end
  649.  
  650.   currentESCConfiguration = escConfigurations[currentESCConfigurationKey] --load new esc config
  651.  
  652.   preCalculate()-- make sure to update our precalculated values with the new config
  653.  
  654.   lastESCConfigurationKey = currentESCConfigurationKey
  655.   gui.message(currentESCConfiguration.name, 5, "vehicle.esc.mode")
  656. end
  657.  
  658. local function toggleESCMode()
  659.   local key = currentESCConfigurationKey + 1
  660.   setESCMode(key)
  661.   if currentESCConfigurationKey > #escConfigurations then
  662.     currentESCConfigurationKey = 1
  663.   end
  664. end
  665.  
  666. local function registerQuickAccess()
  667.   if not hasRegisteredQuickAccess then
  668.     core_quickAccess.addEntry({ level = '/', generator = function(entries)
  669.           table.insert(entries, { title = 'ui.radialmenu2.ESC', priority = 40, ["goto"] = '/esc/', icon = 'radial_ESC' })
  670.         end})
  671.  
  672.     core_quickAccess.addEntry({ level = '/esc/', generator = function(entries)
  673.           for k,v in pairs(escConfigurations) do
  674.             local entry = { title = "ui.radialmenu2.ESC."..v.name:gsub(" ","_"), icon = "radial_"..v.name:gsub(" ","_"), onSelect = function() controller.getController("esc").setESCMode(k) return {'reload'} end}
  675.             if currentESCConfiguration == v then
  676.               entry.color = '#ff6600'
  677.             end
  678.             table.insert(entries, entry)
  679.           end
  680.         end})
  681.     hasRegisteredQuickAccess = true
  682.   end
  683. end
  684.  
  685. local function calculateCharacteristicSpeed(config)
  686.   local eg = (mass * (config.skewStiffnessRear * distanceCOGRearAxle - config.skewStiffnessFront * distanceCOGFrontAxle)) / (config.skewStiffnessFront * config.skewStiffnessRear * wheelBase)
  687.   local characteristicSpeed = sqrt(wheelBase / abs(eg + 1e-30)) --guard against infinity
  688.   if isDebugMode and config.escEnabled then
  689.     log('D', "ESC", string.format("Calculated EG: %s --> %.6f", config.name, eg))
  690.     log('D', "ESC", string.format("Calculated characteristic speed: %s --> %.2f m/s", config.name, characteristicSpeed))
  691.     if eg < 0 then
  692.       log('W', "ESC", string.format("Calculated EG (%s) is lower than 0 (oversteery car setup), ESC might not work perfectly!", config.name))
  693.     end
  694.   end
  695.   return characteristicSpeed
  696. end
  697.  
  698. local function calculateAxleDistances()
  699.   wheelBase = obj:nodeLength(wheelCache[frontRightWheelId].node1, wheelCache[rearRightWheelId].node1) --calculate wheelbase from the distance of the front and rear wheels
  700.   invWheelBase = 1 / wheelBase
  701.  
  702.   local tmp = vec3(0,0,0)
  703.   local totalMass = 0
  704.   for _,v in pairs(v.data.nodes) do
  705.     tmp = tmp + vec3(v.pos) * v.nodeWeight
  706.     totalMass = totalMass + v.nodeWeight
  707.   end
  708.  
  709.   local realCOG = tmp / totalMass
  710.  
  711.   --Find the positions of the front and rear axle
  712.   local frontAxlePos = 0
  713.   local rearAxlePos = 0
  714.   local twLeft = 0
  715.   local twRight = 0
  716.   for _,n in pairs(v.data.nodes) do
  717.     if n.cid == wheelCache[frontRightWheelId].node1 then
  718.       frontAxlePos = n.pos.y
  719.       twRight = n.pos.x
  720.     elseif n.cid == wheelCache[rearRightWheelId].node1 then
  721.       rearAxlePos = n.pos.y
  722.     elseif n.cid == wheelCache[frontLeftWheelId].node1 then
  723.       twLeft = n.pos.x
  724.     end
  725.   end
  726.  
  727.   distanceCOGFrontAxle = abs(realCOG.y - frontAxlePos)
  728.   distanceCOGRearAxle = abs(realCOG.y - rearAxlePos)
  729.  
  730.   trackWidth = abs(twLeft - twRight)
  731.  
  732.   if isDebugMode then
  733.     log('D', "ESC", "Distance COG to Rearaxle: "..distanceCOGRearAxle.." m")
  734.     log('D', "ESC", "Distance COG to Frontaxle: "..distanceCOGFrontAxle.." m")
  735.     log('D', "ESC", "Wheelbase: "..wheelBase.." m")
  736.     log('D', "ESC", "Track width: "..trackWidth.." m")
  737.   end
  738. end
  739.  
  740. local function init(jbeamData)
  741.   configData = jbeamData
  742. end
  743.  
  744. local function initSecondStage()
  745.   escPulse = 0
  746.   tcsPulse = 0
  747.   escFailure = false
  748.   M.pauseESCAction = false
  749.   M.wheelAngleFront = 0
  750.   M.wheelAngleRear = 0
  751.   M.stiffnessFront = 0
  752.   M.stiffnessRear = 0
  753.  
  754.   calibrateESC = nop
  755.   M.updateWheelsIntermediate  = nil
  756.   M.updateGFX = nil
  757.  
  758.   wheelCache = table.new(4, 0)
  759.   wheelCacheSize = 0
  760.   wheelNameCache = {}
  761.   tcsWheelBrakeTorques = {}
  762.   throttleFactor = 1
  763.   allWheelSlip = false
  764.  
  765.   --cache all wheels for easy access
  766.   for id,wd in pairs(wheels.wheels) do
  767.     local wheelCacheEntry = wd
  768.     wheelCacheEntry.speedSmoother =  newExponentialSmoothing(500)
  769.     wheelCacheEntry.stabilityControlBrakeIntegral =  0
  770.     wheelCacheEntry.tractionControlBrakeIntegral =  0
  771.     wheelCacheEntry.tractionControlBrakeFactor =  0
  772.     wheelCacheEntry.tractionControlLastSlip =  0
  773.     wheelCacheEntry.tractionControlSpeedCorrectionFactor =  1
  774.     --table.insert(wheelCache, wheelCacheEntry)
  775.     wheelCache[id] = wheelCacheEntry
  776.     wheelNameCache[wd.name] = id
  777.     wheelCacheSize = wheelCacheSize + 1
  778.     tcsWheelBrakeTorques[id] = 0
  779.   end
  780.  
  781.   yawSmooth = newExponentialSmoothing(50)  --windows of 50 frames
  782.  
  783.   --we need to find the average wheel position (basically the "center" of our vehicle)
  784.   local avgWheelPos = vec3(0,0,0)
  785.   local wheelCount = 0
  786.  
  787.   local escConfigs = configData--shallowcopy(v.data.escConfig)
  788.   if v.userSettings and v.userSettings.escConfig then
  789.     escConfigs = tableMergeRecursive(escConfigs, v.userSettings.escConfig)
  790.   end
  791.  
  792.   for _,wheelName in ipairs(escConfigs.actionedWheels) do
  793.     if wheelNameCache[wheelName] == nil then
  794.       log('W', "ESC", "Could not find wheel: "..wheelName.." defined in escConfig")
  795.       M.update = nop
  796.       M.graphicsStep = nop
  797.       return
  798.     end
  799.     local wheelNodePos = v.data.nodes[wheelCache[wheelNameCache[wheelName]].node1].pos --find the wheel position
  800.     avgWheelPos = avgWheelPos + wheelNodePos --sum up all positions
  801.     wheelCount = wheelCount + 1
  802.   end
  803.  
  804.   avgWheelPos = avgWheelPos / wheelCount --make the average of all positions
  805.  
  806.   local vectorForward = vec3(v.data.nodes[v.data.refNodes[0].ref].pos) - vec3(v.data.nodes[v.data.refNodes[0].back].pos) -- vec3(obj:getDirectionVector()) --vector facing forward
  807.   local vectorUp = vec3(v.data.nodes[v.data.refNodes[0].up].pos) - vec3(v.data.nodes[v.data.refNodes[0].ref].pos)
  808.  
  809.   local vectorRight = vectorForward:cross(vectorUp) --vector facing to the right
  810.  
  811.   offColor = escConfigs.offColor or "343434"
  812.  
  813.   --iterate over all wheels that should be included in the esc
  814.   for _,wheelName in ipairs(escConfigs.actionedWheels) do
  815.     local wheelNodePos = vec3(v.data.nodes[wheelCache[wheelNameCache[wheelName]].node1].pos) --find the wheel position
  816.     local wheelVector = wheelNodePos - avgWheelPos --create a vector from our "center" to the wheel
  817.     local dotForward = vectorForward:dot(wheelVector) --calculate dot product of said vector and forward vector
  818.     local dotLeft = vectorRight:dot(wheelVector) --calculate dot product of said vector and left vector
  819.  
  820.     if dotForward >= 0 then
  821.       if dotLeft >= 0 then
  822.         frontRightWheelId = wheelNameCache[wheelName] --this case can only mean it's our front right wheel
  823.       else
  824.         frontLeftWheelId = wheelNameCache[wheelName] -- ...
  825.       end
  826.     else
  827.       if dotLeft >= 0 then
  828.         rearRightWheelId = wheelNameCache[wheelName]-- ...
  829.       else
  830.         rearLeftWheelId = wheelNameCache[wheelName]-- ...
  831.       end
  832.     end
  833.   end
  834.  
  835.   initialWheelCount = wheels.wheelCount
  836.  
  837.   --create an easy way to access the "other" wheel on an axle
  838.   otherWheelOnAxle[frontLeftWheelId] = frontRightWheelId
  839.   otherWheelOnAxle[frontRightWheelId] = frontLeftWheelId
  840.   otherWheelOnAxle[rearLeftWheelId] = rearRightWheelId
  841.   otherWheelOnAxle[rearRightWheelId] = rearLeftWheelId
  842.  
  843.   local tmpConfigs = {}
  844.   for name,config in pairs(escConfigs.configurations) do
  845.     if type(config) == "table" and config.escConfigurationEnabled then
  846.       table.insert(tmpConfigs, sanitizeConfiguration(shallowcopy(config), name)) --we need to create copies of all our configurations as we are going to use them for saving a few values as well and we want a fresh copy every time we reset the vehicle
  847.     end
  848.   end
  849.  
  850.   local counter = 1
  851.   escConfigurations = {}
  852.   table.sort(tmpConfigs, function(a,b) return b.order > a.order end)
  853.   for _,config in pairs(tmpConfigs) do
  854.     escConfigurations[counter] = config
  855.     counter = counter + 1
  856.   end
  857.  
  858.   if lastESCConfigurationKey ~= -1 then
  859.     currentESCConfigurationKey = lastESCConfigurationKey
  860.   else
  861.     currentESCConfigurationKey = escConfigs.defaultConfig
  862.     lastESCConfigurationKey = currentESCConfigurationKey
  863.   end
  864.   currentESCConfiguration = escConfigurations[currentESCConfigurationKey] --load the default configuration
  865.   gui.message(currentESCConfiguration.name, 5, "vehicle.esc.mode")
  866.  
  867.   isDebugMode = escConfigs.isDebugMode > 0
  868.  
  869.   if isDebugMode then
  870.     log('D', "ESC", "ESC configuration data:")
  871.     log('D', "ESC", dumps(configData))
  872.     log('D', "ESC", "Using ESC configuration: "..currentESCConfiguration.name)
  873.     log('D', "ESC", "Front Left wheel: "..frontLeftWheelId)
  874.     log('D', "ESC", "Front Right wheel: "..frontRightWheelId)
  875.     log('D', "ESC", "Rear Left wheel: "..rearLeftWheelId)
  876.     log('D', "ESC", "Rear Right wheel: "..rearRightWheelId)
  877.   end
  878.  
  879.   calculateAxleDistances()
  880.   local statsObj = obj:calcBeamStats()
  881.   mass = statsObj.total_weight --simply the mass of the car
  882.  
  883.   for _,config in pairs(escConfigurations) do
  884.     if type(config) =="table" and config.escConfigurationEnabled and config.characteristicSpeed <= 0 then --calculate char. speed if no override is provided
  885.       config.characteristicSpeed = calculateCharacteristicSpeed(config)
  886.     end
  887.   end
  888.  
  889.   desiredYawSmooth = newExponentialSmoothing(currentESCConfiguration.desiredYawRateSmoothing)
  890.  
  891.   preCalculate()
  892.  
  893.   --TCS
  894.   crossWheels[frontLeftWheelId] = rearRightWheelId
  895.   crossWheels[rearRightWheelId] = frontLeftWheelId
  896.   crossWheels[frontRightWheelId] = rearLeftWheelId
  897.   crossWheels[rearLeftWheelId] = frontRightWheelId
  898.  
  899.   local engines = arrayConcat(powertrain.getDevicesByType("combustionEngine"), powertrain.getDevicesByType("electricMotor"))
  900.   revLimiterEngines = {}
  901.   for _,v in pairs(engines) do
  902.     table.insert(revLimiterEngines, v)
  903.   end
  904.   revLimiterEngineCount = #revLimiterEngines
  905.   tempRevLimiterActive = false
  906.   tempRevLimiterTimer = 0
  907.  
  908.   registerQuickAccess()
  909.  
  910.   --M.update = update
  911.   M.updateWheelsIntermediate = updateWheelsIntermediate
  912.   M.updateGFX = updateGFX
  913. end
  914.  
  915. local function serialize()
  916.   return {escConfigKey = currentESCConfigurationKey}
  917. end
  918.  
  919. local function deserialize(data)
  920.   if data and data.escConfigKey then
  921.     setESCMode(data.escConfigKey)
  922.   end
  923. end
  924.  
  925. -- public interface
  926. M.init = init
  927. M.initSecondStage   = initSecondStage
  928. M.updateWheelsIntermediate = nil
  929. M.updateGFX = nil
  930. M.toggleESCMode = toggleESCMode
  931. M.setESCMode = setESCMode
  932. M.getCarData = getCarData
  933. M.getCurrentConfigData = getCurrentConfigData
  934. M.startESCCalibration = startESCCalibration
  935. M.stopESCCalibration = stopESCCalibration
  936. M.serialize = serialize
  937. M.deserialize = deserialize
  938.  
  939. return M
Advertisement
Add Comment
Please, Sign In to add comment