33
44 See http://www-cdr.stanford.edu/dynamic/bywire/tires.pdf.
55 Used as reference for this implementation.
6+ Note that this is a Pacejka '94 version model. Not '89.
67
78 TODO: Massive simplifications and optimisations are possible once everything is finalised.
89
@@ -25,13 +26,15 @@ local REALISTIC_GRAVITY = 35 -- 35 studs/s^2 -> 9.8 m/s^2
2526local PI = math.pi
2627local TWO_PI = PI * 2
2728local sin = math.sin
29+ local tan = math.tan
2830local atan = math.atan
2931local atan2 = math.atan2
3032local abs = math.abs
3133local min = math.min
3234local exp = math.exp
3335local deg = math.deg
3436local sign = math.sign
37+ local max = math.max
3538local euler = exp (1 )
3639
3740local PacejkaWheel = setmetatable ({}, WheelVehicleWheelClient )
@@ -40,7 +43,7 @@ PacejkaWheel.__index = PacejkaWheel
4043
4144export type PacejkaWheel =
4245 typeof (setmetatable (
43- {} :: { _vehicleMass : number , _torqueToCarForceRatio : number },
46+ {} :: { _vehicleMass : number , _torqueToCarForceRatio : number , _wheelInertia : number },
4447 {} :: typeof ({ __index = PacejkaWheel })
4548 ))
4649 & WheelVehicleWheelClient .Wheel
@@ -49,9 +52,10 @@ function PacejkaWheel.new(
4952 wheelObj : BasePart ,
5053 wheelConfig : WheelVehicleTypes .WheelConfig ,
5154 links : WheelVehicleUtils .Links ,
55+ _forceAttachment : Attachment ,
5256 _vehicleMass : number
5357): PacejkaWheel
54- local self = setmetatable (WheelVehicleWheelClient .new (wheelObj , wheelConfig , links ), PacejkaWheel )
58+ local self = setmetatable (WheelVehicleWheelClient .new (wheelObj , wheelConfig , links , _forceAttachment ), PacejkaWheel )
5559
5660 self ._vehicleMass = _vehicleMass
5761
9094function PacejkaWheel .SetConstants (self : PacejkaWheel )
9195 WheelVehicleWheelClient .SetConstants (self )
9296
93- local wheelInertia = 1 / 2 * self .mass * self .radius ^ 2
94- self ._torqueToCarForceRatio = (wheelInertia / self .radius ^ 2 )
95- / (self ._vehicleMass + wheelInertia / self .radius ^ 2 )
97+ self . _wheelInertia = 1 / 2 * self .mass * self .radius ^ 2
98+ self ._torqueToCarForceRatio = (self . _wheelInertia / self .radius ^ 2 )
99+ / (self ._vehicleMass + self . _wheelInertia / self .radius ^ 2 )
96100end
97101
98102function PacejkaWheel .UpdateStepped (
@@ -130,7 +134,8 @@ function PacejkaWheel.UpdateStepped(
130134 local torque = self .baseTorque -- +ve when accelerating, -ve when braking
131135 local lateralForce = 0
132136 local longitudinalForce = 0
133- local longitudinalSlip = longitudeVelocity - self .angularVelocity * self .radius -- -ve when accelerating, +ve when braking.
137+ local wheelContactVelocity = self .angularVelocity * self .radius
138+ local longitudinalSlip = longitudeVelocity - wheelContactVelocity -- -ve when accelerating, +ve when braking.
134139
135140 -- Pacejka mysterious and magical constants.
136141 -- See http://www-cdr.stanford.edu/dynamic/bywire/tires.pdf.
@@ -167,11 +172,11 @@ function PacejkaWheel.UpdateStepped(
167172
168173 if abs (speed ) > 2.5 then
169174 -- Lateral force component (F_y -> Fy)
170- local FyC = 1.30
171- local FyD = - 22.1 * Fz ^ 2 + 1011 * Fz
172- local FyBCD = 1078 * sin (1.82 * atan (0.208 * Fz ))
175+ local FyC = 1.35 -- 1. 30
176+ local FyD = 1.00 * Fz * 1000 -- -22.1 * Fz ^ 2 + 1011 * Fz
177+ local FyBCD = ( 13.5 * Fz ^ 1.07 ) * 1000 -- 1078 * sin(1.82 * atan(0.208 * Fz))
173178 local FyB = FyBCD / (FyC * FyD )
174- local FyE = 0 * Fz ^ 2 - 0.354 * Fz + 0.707
179+ local FyE = 0.78 - 0.025 * Fz -- 0 * Fz ^ 2 - 0.354 * Fz + 0.707
175180
176181 -- Lateral force camber angle factor
177182 local FySh = 0.028 * camberAngle
@@ -187,13 +192,21 @@ function PacejkaWheel.UpdateStepped(
187192 -- * REALISTIC_GRAVITY
188193 -- * (sin(contactLinkOrientationX + PI / 2) * (1 - lateralVelocity * 0.4) - lateralVelocity * 0.1)
189194
190- -- NOTE: Lazy solution is using the suspension's global force
191- lateralForce = lateralVelocity * 900
195+ local forceAttachmentRightVector = self ._forceAttachment .WorldCFrame .RightVector
196+ local y = math.atan2 (forceAttachmentRightVector .Z , forceAttachmentRightVector .X )
197+ local relativeRightVector = CFrame .Angles (0 , - y , 0 )
198+ :VectorToObjectSpace (self ._forceAttachment .WorldCFrame .RightVector )
199+
200+ lateralForce = verticalForce * tan (atan2 (relativeRightVector .X , relativeRightVector .Y ) - PI / 2 )
201+ + lateralVelocity * 900
192202 end
193203
194204 -- Longitudinal force component (F_x -> Fx)
195205 if longitudeVelocity ~= 0 then
196- percentLongitudinalSlip = longitudinalSlip / abs (longitudeVelocity ) * 100 * min (1 , abs (speed ) * 0.07 )
206+ percentLongitudinalSlip = longitudinalSlip
207+ / max (abs (longitudeVelocity ), abs (wheelContactVelocity ))
208+ * 100
209+ * min (1 , abs (speed ) * 0.07 )
197210
198211 local FxC = 1.65
199212 local FxD = - 21.3 * Fz ^ 2 + 1144 * Fz
@@ -255,6 +268,7 @@ function PacejkaWheel.UpdateStepped(
255268 -- deg(atan2(contactNormal.Y, contactNormal.Z)),
256269 -- deg(atan2(contactNormal.Y, contactNormal.X))
257270 -- )
271+
258272 -- print("-")
259273 end
260274 end
@@ -276,7 +290,7 @@ function PacejkaWheel.UpdateStepped(
276290 -- Wheel rotation
277291 -- local angularVelocityRelative = longitudeVelocity / self.radius - self.angularVelocity
278292
279- self .angularVelocity += torque * deltaTimeSim
293+ self .angularVelocity += torque / self . _wheelInertia * deltaTimeSim
280294
281295 self .rotation += self .angularVelocity * deltaTimeSim
282296 if (self .rotation :: number ) > TWO_PI then
0 commit comments