603 | function VehicleMotor:findGearChangeTargetGear(curGear, prevGear, gearRatios, gearSign, acceleratorPedal, dt) |
604 | local newGear = curGear |
605 | |
606 | local minAllowedRpm, maxAllowedRpm = self:getRequiredMotorRpmRange() |
607 | |
608 | local differentialRpm = math.max(self.differentialRotSpeed * 30 / math.pi * gearSign, 0.0001) |
609 | |
610 | -- find the gear that would give the max power |
611 | local maxPower = 0 |
612 | local maxPowerGear = 0 |
613 | for gear=1,#gearRatios do |
614 | local rpm = differentialRpm * gearRatios[gear] |
615 | if rpm <= maxAllowedRpm and rpm >= minAllowedRpm then |
616 | local power = self:getTorqueCurveValue(rpm) * rpm |
617 | if power >= maxPower then |
618 | maxPower = power |
619 | maxPowerGear = gear |
620 | end |
621 | end |
622 | end |
623 | if maxPowerGear ~= 0 then |
624 | |
625 | local bestTradeoff = 0 |
626 | |
627 | -- find the gear ratio with the best tradeoff |
628 | for gear=#gearRatios,1,-1 do |
629 | local nextRpm = differentialRpm * gearRatios[gear] |
630 | if nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm then |
631 | local nextPower = self:getTorqueCurveValue(nextRpm) * nextRpm |
632 | |
633 | -- Choose the gear if it gets close enough to the max power |
634 | if nextPower >= maxPower*0.8 then |
635 | local powerFactor = (nextPower - 0.8*maxPower) / (maxPower*0.2) -- 0 when at 80% of maxPower, 1 when at maxPower |
636 | local rpmFactor = (maxAllowedRpm - nextRpm) / math.max(maxAllowedRpm-minAllowedRpm, 0.001) |
637 | |
638 | if acceleratorPedal == 0 then |
639 | rpmFactor = 1-rpmFactor -- choose a high rpm when decelerating |
640 | else |
641 | rpmFactor = rpmFactor * 3 |
642 | end |
643 | local newGearFactor = gear ~= prevGear and 0.5 or 0 -- prefer choosing a different gear than what we had before the gear change |
644 | |
645 | local tradeoff = powerFactor + rpmFactor + newGearFactor |
646 | |
647 | if tradeoff > bestTradeoff then |
648 | bestTradeoff = tradeoff |
649 | newGear = gear |
650 | --print(string.format("better final tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f prevGear %d factors: %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, prevGear, powerFactor, rpmFactor, newGearFactor)) |
651 | else |
652 | --print(string.format("worse final tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f prevGear %d factors: %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, prevGear, powerFactor, rpmFactor, newGearFactor)) |
653 | end |
654 | end |
655 | end |
656 | end |
657 | else |
658 | local minDiffGear = 0 |
659 | local minDiff = math.huge |
660 | for gear=1,#gearRatios do |
661 | local rpm = differentialRpm * gearRatios[gear] |
662 | local diff = math.max(rpm - maxAllowedRpm, minAllowedRpm - rpm) |
663 | if diff < minDiff then |
664 | minDiff = diff |
665 | minDiffGear = gear |
666 | end |
667 | end |
668 | --print(string.format("use min diff gear: %d diff: %.2f", minDiffGear, minDiff)) |
669 | newGear = minDiffGear |
670 | end |
671 | --if newGear ~= curGear then |
672 | -- print(string.format("Selected a different gear: %d vs %d", newGear, curGear)) |
673 | --end |
674 | return newGear |
675 | end |
679 | function VehicleMotor:findGearChangeTargetGearPrediction(curGear, gearRatios, gearSign, gearChangeTimer, acceleratorPedal, dt) |
680 | local newGear = curGear |
681 | |
682 | local minAllowedRpm, maxAllowedRpm = self:getRequiredMotorRpmRange() |
683 | --print(string.format("rpmRange [%.2f %.2f]", minAllowedRpm, maxAllowedRpm)) |
684 | local gearRatio = gearRatios[curGear] |
685 | |
686 | local differentialRotSpeed = math.max(self.differentialRotSpeed*gearSign, 0.0001) |
687 | local differentialRpm = differentialRotSpeed * 30 / math.pi |
688 | local clutchRpm = differentialRpm * gearRatio |
689 | |
690 | |
691 | -- 1. Predict the velocity of the vehicle after the gear change |
692 | local diffSpeedAfterChange |
693 | if math.abs(acceleratorPedal) < 0.0001 then |
694 | -- Assume that we will continue decelerating with 80% of the current deceleration |
695 | local brakeAcc = math.min(self.differentialRotAccelerationSmoothed*gearSign*0.8, 0) |
696 | diffSpeedAfterChange = math.max(differentialRotSpeed + brakeAcc * self.gearChangeTime*0.001, 0) |
697 | --print(string.format("brake expectedAcc: %.3f realAcc %.3f %.3f max: %.2f gr: %.2f speed: %.2f", brakeAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAccelerationSmoothed, maxExpectedAcc, gearRatio, self.vehicle.lastSpeedReal*1000)) |
698 | else |
699 | -- Ignore wheels mass as it is usually negligible and the calculation below is not correct when the differential acceleration is not uniformely distributed |
700 | --[[local neededWheelsInertiaTorque = 0 |
701 | local specWheels = self.vehicle.spec_wheels |
702 | for _, wheel in pairs(specWheels.wheels) do |
703 | local invRotInterita = 2.0 / (wheel.mass*wheel.radius * wheel.radius); |
704 | neededWheelsInertiaTorque = neededWheelsInertiaTorque + invRotInterita * self.differentialRotAcceleration * wheel.radius |
705 | end |
706 | neededWheelsInertiaTorque = neededWheelsInertiaTorque / (gearRatio*gearRatio)]] |
707 | |
708 | local lastMotorRotSpeed = self.motorRotSpeed - self.motorRotAcceleration * (g_physicsDtLastValidNonInterpolated*0.001) |
709 | local lastDampedMotorRotSpeed = lastMotorRotSpeed / (1.0 + self.dampingRateFullThrottle/self.rotInertia*g_physicsDtLastValidNonInterpolated*0.001) |
710 | |
711 | |
712 | local neededInertiaTorque = (self.motorRotSpeed - lastDampedMotorRotSpeed)/(g_physicsDtLastValidNonInterpolated*0.001) * self.rotInertia |
713 | |
714 | local lastMotorTorque = (self.motorAppliedTorque - self.motorExternalTorque - neededInertiaTorque) |
715 | |
716 | --print(string.format("load: %.3f expected torque: %.3f neededPtoTorque %.3f neededInertiaTorque %.4f", self.motorAppliedTorque, self.motorAvailableTorque, self.motorExternalTorque, neededInertiaTorque)) |
717 | |
718 | local totalMass = self.vehicle:getTotalMass() |
719 | local expectedAcc = lastMotorTorque * gearRatio / totalMass -- differential rad/s^2 |
720 | |
721 | -- The the difference in acceleration is due to gravity and thus will pull back the vehicle when changing gears and some other reasons (non-accounted mass (e.g. trees), collisions, wheel damping, wheel mass, ...) |
722 | -- Use a fixed factor of 90% to separate the effect of the gravity |
723 | local uncalculatedAccFactor = 0.9 |
724 | local gravityAcc = math.max(expectedAcc*uncalculatedAccFactor - math.max(self.differentialRotAcceleration*gearSign, 0), 0) |
725 | |
726 | --print(string.format("expectedAcc: %.3f realAcc: %.3f %.3f gravityAcc: %.3f gr: %.2f mass %.1f speed: %.3f dt %.2fms", expectedAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAcceleration, gravityAcc, gearRatio, totalMass, self.vehicle.lastSpeedReal*1000, g_physicsDtLastValidNonInterpolated)) |
727 | |
728 | diffSpeedAfterChange = math.max(differentialRotSpeed - gravityAcc * self.gearChangeTime*0.001, 0) |
729 | end |
730 | |
731 | |
732 | -- 2. Find the gear that gives the maximum power in the valid rpm range after the gear change |
733 | -- If none is valid, store the gear that will get closest to the valid rpm range |
734 | |
735 | -- TODO allow some clutch slippage to extend the possible rpm range (e.g. when accelerating and switching from gear 1 to gear 2) |
736 | |
737 | local maxPower = 0 |
738 | local maxPowerGear = 0 |
739 | for gear=1,#gearRatios do |
740 | local rpm |
741 | if gear == curGear then |
742 | rpm = clutchRpm |
743 | else |
744 | rpm = diffSpeedAfterChange * gearRatios[gear] * 30 / math.pi |
745 | end |
746 | if rpm <= maxAllowedRpm and rpm >= minAllowedRpm then |
747 | local power = self:getTorqueCurveValue(rpm) * rpm |
748 | --print(string.format(" power %.2f @ %.d", power, gear)) |
749 | if power >= maxPower then |
750 | maxPower = power |
751 | maxPowerGear = gear |
752 | end |
753 | end |
754 | end |
755 | |
756 | --local curPower = self:getTorqueCurveValue(clutchRpm) * clutchRpm |
757 | --print(string.format("power %.2f @ %d rpms: %.2f %.2f drpm: %.2f", curPower, curGear, clutchRpm, diffSpeedAfterChange* gearRatio * 30 / math.pi, self.differentialRotAccelerationSmoothed * gearRatio * 30 / math.pi)) |
758 | |
759 | -- 3. Find the gear with the best tradeoff (lots of power with low rpm) |
760 | -- Or use the the gear will get closest to the valid rpm range if none of the gears are good |
761 | if maxPowerGear ~= 0 then |
762 | |
763 | local bestTradeoff = 0 |
764 | |
765 | for gear=#gearRatios,1,-1 do |
766 | local nextRpm |
767 | if gear == curGear then |
768 | nextRpm = clutchRpm |
769 | else |
770 | nextRpm = diffSpeedAfterChange * gearRatios[gear] * 30 / math.pi |
771 | end |
772 | if nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm then |
773 | local nextPower = self:getTorqueCurveValue(nextRpm) * nextRpm |
774 | |
775 | -- Choose the gear if it gets close enough to the max power |
776 | if nextPower >= maxPower*0.8 then |
777 | local powerFactor = (nextPower - maxPower*0.8) / (maxPower*0.2) -- 0 when at 80% of maxPower, 1 when at maxPower |
778 | local curSpeedRpm = differentialRpm * gearRatios[gear] |
779 | local rpmFactor = MathUtil.clamp((maxAllowedRpm - curSpeedRpm) / math.max(maxAllowedRpm-minAllowedRpm, 0.001), 0, 1) |
780 | local gearChangeFactor |
781 | if gear == curGear then |
782 | gearChangeFactor = 1 |
783 | else |
784 | gearChangeFactor = math.min(-gearChangeTimer / 2000, 0.9) -- the longer we wait, the less penality we add for gear changes |
785 | end |
786 | |
787 | if math.abs(acceleratorPedal) < 0.0001 then |
788 | rpmFactor = 1-rpmFactor -- choose a high rpm when decelerating |
789 | else |
790 | rpmFactor = rpmFactor * 3 |
791 | end |
792 | |
793 | local tradeoff = powerFactor + rpmFactor + gearChangeFactor |
794 | |
795 | if tradeoff > bestTradeoff then |
796 | bestTradeoff = tradeoff |
797 | newGear = gear |
798 | --print(string.format("better tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor)) |
799 | else |
800 | --print(string.format("worse tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor)) |
801 | end |
802 | end |
803 | end |
804 | end |
805 | else |
806 | local minDiffGear = 0 |
807 | local minDiff = math.huge |
808 | for gear=1,#gearRatios do |
809 | local rpm = diffSpeedAfterChange * gearRatios[gear] * 30 / math.pi |
810 | local diff = math.max(rpm - maxAllowedRpm, minAllowedRpm - rpm) |
811 | if diff < minDiff then |
812 | --print(string.format("better min diff gear: %d diff: %.2f rpm: %.2f" , gear, diff, rpm)) |
813 | minDiff = diff |
814 | minDiffGear = gear |
815 | end |
816 | end |
817 | newGear = minDiffGear |
818 | end |
819 | return newGear |
820 | end |
530 | function VehicleMotor:getBestGearRatio(wheelSpeedRpm, minRatio, maxRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
531 | |
532 | if requiredMotorRpm ~= 0 then |
533 | local gearRatio = math.max(requiredMotorRpm-accSafeMotorRpm, requiredMotorRpm*0.8) / math.max(wheelSpeedRpm, 0.001) |
534 | gearRatio = MathUtil.clamp(gearRatio, minRatio, maxRatio) |
535 | return gearRatio |
536 | end |
537 | |
538 | -- Use a minimum wheel rpm to avoid that gearRatio is ignored |
539 | wheelSpeedRpm = math.max(wheelSpeedRpm, 0.0001) |
540 | |
541 | local bestMotorPower = 0 |
542 | local bestGearRatio = minRatio |
543 | --local bestRPM = 0 |
544 | -- TODO make this more efficient |
545 | for gearRatio = minRatio, maxRatio, 0.5 do |
546 | local motorRpm = wheelSpeedRpm * gearRatio |
547 | if motorRpm > self.maxRpm - accSafeMotorRpm then |
548 | break |
549 | end |
550 | local motorPower = self:getTorqueCurveValue(math.max(motorRpm, self.minRpm)) * motorRpm *math.pi/30 |
551 | if motorPower > bestMotorPower then |
552 | bestMotorPower = motorPower |
553 | bestGearRatio = gearRatio |
554 | --bestRPM = motorRpm |
555 | end |
556 | |
557 | if motorPower >= requiredMotorPower then |
558 | break |
559 | end |
560 | end |
561 | --print(string.format("Selected best gear: %f, %.2fkW rpm %.2f wheel %.2f", bestGearRatio, bestMotorPower, bestRPM, wheelSpeedRpm,)) |
562 | |
563 | return bestGearRatio |
564 | end |
33 | function VehicleMotor:new(vehicle, minRpm, maxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatios, backwardGearRatios, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, minSpeed) |
34 | |
35 | local self = {} |
36 | setmetatable(self, VehicleMotor_mt) |
37 | |
38 | self.vehicle = vehicle |
39 | self.minRpm = minRpm |
40 | self.maxRpm = maxRpm |
41 | self.minSpeed = minSpeed |
42 | self.maxForwardSpeed = maxForwardSpeed -- speed in m/s |
43 | self.maxBackwardSpeed = maxBackwardSpeed |
44 | |
45 | self.maxClutchTorque = 5 -- amount of torque that can be transferred from motor to clutch/wheels [t m s^-2] |
46 | |
47 | self.torqueCurve = torqueCurve |
48 | self.brakeForce = brakeForce |
49 | |
50 | self.gear = 0 |
51 | self.minGearRatio = 0 |
52 | self.maxGearRatio = 0 |
53 | |
54 | self.forwardGearRatios = forwardGearRatios |
55 | self.backwardGearRatios = backwardGearRatios |
56 | self.minForwardGearRatio = minForwardGearRatio |
57 | self.maxForwardGearRatio = maxForwardGearRatio |
58 | self.minBackwardGearRatio = minBackwardGearRatio |
59 | self.maxBackwardGearRatio = maxBackwardGearRatio |
60 | |
61 | self.manualTargetGear = nil |
62 | self.targetGear = 0 |
63 | self.previousGear = 0 |
64 | self.gearChangeTimer = -1 |
65 | self.gearChangeTime = 250 |
66 | self.autoGearChangeTimer = -1 |
67 | self.autoGearChangeTime = 1000 |
68 | |
69 | self.lastRealMotorRpm = 0 |
70 | self.lastMotorRpm = 0 |
71 | |
72 | self.rpmLimit = math.huge |
73 | self.speedLimit = math.huge -- Speed limit in km/h |
74 | self.speedLimitAcc = math.huge |
75 | |
76 | self.accelerationLimit = 2 -- m s^-2 |
77 | |
78 | self.motorRotationAccelerationLimit = (maxRpm - minRpm)*math.pi/30 / 2 -- rad s^-2 default accelerate from min rpm to max rpm in 2 sec |
79 | |
80 | self.equalizedMotorRpm = 0 |
81 | |
82 | self.requiredMotorPower = 0 |
83 | |
84 | if self.maxForwardSpeed == nil then |
85 | self.maxForwardSpeed = self:calculatePhysicalMaximumForwardSpeed() |
86 | end |
87 | if self.maxBackwardSpeed == nil then |
88 | self.maxBackwardSpeed = self:calculatePhysicalMaximumBackwardSpeed() |
89 | end |
90 | |
91 | self.peakMotorTorque = self.torqueCurve:getMaximum() |
92 | |
93 | -- Calculate peak power. Assume we have a linear interpolation on the torque values |
94 | -- For each segment, find the maximum power (D[torque(x, i) * x] == 0) and take the maximum segment |
95 | -- D[ ((x-x0) / (x1-x0) (y1-y0) + y0) x] == 0 |
96 | -- -> (x1 y0 - x0 y1) / (2 (y0 - y1)) if y0 != y1 |
97 | self.peakMotorPower = 0 |
98 | self.peakMotorPowerRotSpeed = 0 |
99 | local numKeyFrames = #self.torqueCurve.keyframes |
100 | if numKeyFrames >= 2 then |
101 | for i=2,numKeyFrames do |
102 | local v0 = self.torqueCurve.keyframes[i-1] |
103 | local v1 = self.torqueCurve.keyframes[i] |
104 | local torque0 = self.torqueCurve:getFromKeyframes(v0, v0, i-1, i-1, 0) |
105 | local torque1 = self.torqueCurve:getFromKeyframes(v1, v1, i, i, 0) |
106 | local rpm, torque |
107 | if math.abs(torque0 - torque1) > 0.0001 then |
108 | rpm = (v1.time * torque0 - v0.time * torque1) / (2.0 * (torque0 - torque1)) |
109 | rpm = math.min(math.max(rpm, v0.time), v1.time) |
110 | torque = self.torqueCurve:getFromKeyframes(v0, v1, i-1, i, (v1.time - rpm) / (v1.time - v0.time)) |
111 | else |
112 | rpm = v0.time |
113 | torque = torque0 |
114 | end |
115 | local power = torque * rpm |
116 | if power > self.peakMotorPower then |
117 | self.peakMotorPower = power |
118 | self.peakMotorPowerRotSpeed = rpm |
119 | end |
120 | end |
121 | -- Convert from rpm to rad/s |
122 | self.peakMotorPower = self.peakMotorPower * math.pi/30 |
123 | self.peakMotorPowerRotSpeed = self.peakMotorPowerRotSpeed * math.pi/30 |
124 | else |
125 | local v = self.torqueCurve.keyframes[1] |
126 | local rotSpeed = v.time*math.pi/30 |
127 | local torque = self.torqueCurve:getFromKeyframes(v, v, i, i, 0) |
128 | self.peakMotorPower = rotSpeed*torque |
129 | self.peakMotorPowerRotSpeed = rotSpeed |
130 | end |
131 | |
132 | self.ptoMotorRpmRatio = ptoMotorRpmRatio |
133 | |
134 | self.rotInertia = self.peakMotorTorque / 600 -- Rotational inertia of the motor, mostly defined by the flywheel [t m^2] |
135 | self.dampingRateFullThrottle = 0.00015 -- Damping rate of the motor if the acceleration pedal is 1 [t m^2 s^-1] |
136 | self.dampingRateZeroThrottleClutchEngaged = 0.001 -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is engaged [t m^2 s^-1] |
137 | self.dampingRateZeroThrottleClutchDisengaged = 0.001 -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is disengaged [t m^2 s^-1] |
138 | |
139 | |
140 | |
141 | -- Motor properties as read from the physics engine |
142 | self.gearRatio = 0 |
143 | self.motorRotSpeed = 0 -- motor rotation speed [rad/s] |
144 | self.motorRotAcceleration = 0 -- motor rotation acceleration [rad/s^2] |
145 | self.motorRotAccelerationSmoothed = 0 -- motor rotation acceleration smoothed [rad/s^2] |
146 | |
147 | self.motorAvailableTorque = 0 -- torque that was available to the physics simulation [kN == t m/s^2] |
148 | self.motorAppliedTorque = 0 -- torque that was applied (<= available), can be smaller when acceleration/speed is limited [kN == t m/s^2] |
149 | self.motorExternalTorque = 0 -- torque that was removed from the motor and was not applied to the wheels (e.g. PTO) [kN == t m/s^2] |
150 | |
151 | self.differentialRotSpeed = 0 -- rotation speed of the main differential [rad/s] |
152 | self.differentialRotAcceleration = 0 -- rotation accleration of the main differential [rad/s^2] |
153 | self.differentialRotAccelerationSmoothed = 0 -- smoothed rotation accleration of the main differential [rad/s^2] |
154 | |
155 | return self |
156 | end |
464 | function VehicleMotor:update(dt) |
465 | local vehicle = self.vehicle |
466 | if next(vehicle.spec_motorized.differentials) ~= nil and vehicle.spec_motorized.motorizedNode ~= nil then |
467 | -- Only update the physics values if a physics simulation was performed |
468 | if g_physicsDtNonInterpolated > 0.0 then |
469 | local lastMotorRotSpeed = self.motorRotSpeed; |
470 | local lastDiffRotSpeed = self.differentialRotSpeed |
471 | self.motorRotSpeed, self.differentialRotSpeed, self.gearRatio = getMotorRotationSpeed(vehicle.spec_motorized.motorizedNode) |
472 | |
473 | self.motorAvailableTorque, self.motorAppliedTorque, self.motorExternalTorque = getMotorTorque(vehicle.spec_motorized.motorizedNode) |
474 | |
475 | |
476 | local motorRotAcceleration = (self.motorRotSpeed - lastMotorRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
477 | self.motorRotAcceleration = motorRotAcceleration |
478 | self.motorRotAccelerationSmoothed = 0.8 * self.motorRotAccelerationSmoothed + 0.2 * motorRotAcceleration |
479 | |
480 | local diffRotAcc = (self.differentialRotSpeed - lastDiffRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
481 | self.differentialRotAcceleration = diffRotAcc |
482 | self.differentialRotAccelerationSmoothed = 0.8 * self.differentialRotAccelerationSmoothed + 0.2 * diffRotAcc |
483 | |
484 | --print(string.format("update rpms: %.2f %.2f acc: %.2f", self.motorRotSpeed*30/math.pi, self.differentialRotSpeed*self.gearRatio*30/math.pi, motorRotAcceleration)) |
485 | end |
486 | |
487 | self.requiredMotorPower = math.huge |
488 | |
489 | else |
490 | local _, gearRatio = self:getMinMaxGearRatio() |
491 | self.differentialRotSpeed = WheelsUtil.computeDifferentialRotSpeedNonMotor(vehicle) |
492 | self.motorRotSpeed = math.max(math.abs(self.differentialRotSpeed * gearRatio), 0) |
493 | self.gearRatio = gearRatio |
494 | end |
495 | |
496 | -- the clamped motor rpm always is higher-equal than the required rpm by the pto |
497 | --local ptoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm) |
498 | -- smoothing for raise/fall of ptoRpm |
499 | if self.lastPtoRpm == nil then |
500 | self.lastPtoRpm = self.minRpm |
501 | end |
502 | local ptoRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio |
503 | if ptoRpm > self.lastPtoRpm then |
504 | self.lastPtoRpm = math.min(ptoRpm, self.lastPtoRpm + self.maxRpm*dt/2000) |
505 | elseif ptoRpm < self.lastPtoRpm then |
506 | self.lastPtoRpm = math.max(self.minRpm, self.lastPtoRpm - self.maxRpm*dt/1000) |
507 | end |
508 | local ptoRpm = math.min(self.lastPtoRpm, self.maxRpm) |
509 | |
510 | local clampedMotorRpm = math.max(self.motorRotSpeed*30/math.pi, ptoRpm, self.minRpm) |
511 | |
512 | self:setLastRpm(clampedMotorRpm) |
513 | |
514 | -- client will recieve this value from the server |
515 | if self.vehicle.isServer then |
516 | self.equalizedMotorRpm = clampedMotorRpm |
517 | end |
518 | end |
826 | function VehicleMotor:updateGear(acceleratorPedal, dt) |
827 | local adjAcceleratorPedal = acceleratorPedal |
828 | if self.gearChangeTimer >= 0 then |
829 | self.gearChangeTimer = self.gearChangeTimer - dt; |
830 | if self.gearChangeTimer < 0 then |
831 | if self.targetGear > 0 then |
832 | -- give the logic another chance to choose a better gear at the time when the gear change should happen |
833 | self.gear = self:findGearChangeTargetGear(self.targetGear, self.previousGear, self.forwardGearRatios, 1, acceleratorPedal, dt) |
834 | self.minGearRatio = self.forwardGearRatios[self.gear] |
835 | else |
836 | self.gear = -self:findGearChangeTargetGear(-self.targetGear, -self.previousGear, self.backwardGearRatios, -1, acceleratorPedal, dt) |
837 | self.minGearRatio = -self.backwardGearRatios[-self.gear] |
838 | end |
839 | self.maxGearRatio = self.minGearRatio |
840 | end |
841 | adjAcceleratorPedal = 0 |
842 | else |
843 | local gearSign = 0 |
844 | if acceleratorPedal > 0 then |
845 | if self.minForwardGearRatio ~= nil then |
846 | self.minGearRatio = self.minForwardGearRatio |
847 | self.maxGearRatio = self.maxForwardGearRatio |
848 | else |
849 | gearSign = 1 |
850 | end |
851 | elseif acceleratorPedal < 0 then |
852 | if self.minBackwardGearRatio ~= nil then |
853 | self.minGearRatio = -self.minBackwardGearRatio |
854 | self.maxGearRatio = -self.maxBackwardGearRatio |
855 | else |
856 | gearSign = -1 |
857 | end |
858 | else |
859 | if self.maxGearRatio > 0 then |
860 | if self.minForwardGearRatio == nil then |
861 | gearSign = 1 |
862 | end |
863 | elseif self.maxGearRatio < 0 then |
864 | if self.minBackwardGearRatio == nil then |
865 | gearSign = -1 |
866 | end |
867 | end |
868 | end |
869 | |
870 | self.autoGearChangeTimer = self.autoGearChangeTimer - dt |
871 | local newGear = self.gear |
872 | if g_manualGearShift then |
873 | if self.manualTargetGear ~= nil then |
874 | newGear = self.manualTargetGear |
875 | self.manualTargetGear = nil |
876 | end |
877 | else |
878 | if gearSign > 0 then |
879 | if self.gear <= 0 then |
880 | newGear = 1 |
881 | else |
882 | if self.autoGearChangeTimer <= 0 then |
883 | newGear = self:findGearChangeTargetGearPrediction(self.gear, self.forwardGearRatios, 1, self.autoGearChangeTimer, acceleratorPedal, dt) |
884 | end |
885 | newGear = math.min(math.max(newGear, 1), #self.forwardGearRatios) |
886 | end |
887 | elseif gearSign < 0 then |
888 | if self.gear >= 0 then |
889 | newGear = -1 |
890 | else |
891 | if self.autoGearChangeTimer <= 0 then |
892 | newGear = -self:findGearChangeTargetGearPrediction(-self.gear, self.backwardGearRatios, -1, self.autoGearChangeTimer, acceleratorPedal, dt) |
893 | end |
894 | newGear = math.max(math.min(newGear, -1), -#self.backwardGearRatios) |
895 | end |
896 | end |
897 | end |
898 | if newGear ~= self.gear then |
899 | self.targetGear = newGear |
900 | self.previousGear = self.gear |
901 | self.gear = 0 |
902 | self.minGearRatio = 0 |
903 | self.maxGearRatio = 0 |
904 | self.autoGearChangeTimer = self.autoGearChangeTime |
905 | self.gearChangeTimer = self.gearChangeTime |
906 | adjAcceleratorPedal = 0 |
907 | end |
908 | end |
909 | return adjAcceleratorPedal |
910 | end |