Skip to content

Commit dd3cd2d

Browse files
authored
Merge pull request #620 from peternewell/wheel-slip
Adjust kinietic friction calculations
2 parents ab364f3 + 39938b2 commit dd3cd2d

File tree

2 files changed

+123
-59
lines changed

2 files changed

+123
-59
lines changed

Source/Orts.Simulation/Simulation/RollingStocks/MSTSLocomotive.cs

Lines changed: 102 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
//#define ALLOW_ORTS_SPECIFIC_ENG_PARAMETERS
4343

4444
// Debug for Advanced Adhesion Model
45-
//#define DEBUG_ADHESION
45+
// #define DEBUG_ADHESION
4646

4747
using Microsoft.Xna.Framework;
4848
using Microsoft.Xna.Framework.Graphics;
@@ -215,10 +215,18 @@ public float CurrentLocomotiveSteamHeatBoilerWaterCapacityL
215215

216216
// Adhesion parameters
217217
float BaseFrictionCoefficientFactor; // Factor used to adjust Curtius formula depending upon weather conditions
218+
float SlipFrictionCoefficientFactor;
218219
public float SteamStaticWheelForce;
219220
public float SteamTangentialWheelForce;
220221
public float SteamDrvWheelWeightLbs; // Weight on each drive axle
221222
public float PreviousThrottleSetting = 0.0f; // Holds the value of the previous throttle setting for calculating the correct antislip speed
223+
float WheelSlipTimeS;
224+
float WheelStopSlipTimeS;
225+
float CurrentWheelSlipAdhesionMultiplier;
226+
float DebugTimer; // Used for debugging adhesion coefficient
227+
bool DebugSpeedReached = false; // Used for debugging adhesion coefficient
228+
float DebugSpeedIncrement = 1; // Used for debugging adhesion coefficient
229+
float DebugSpeed = 1; // Used for debugging adhesion coefficient
222230

223231
// parameters for Track Sander based upon compressor air and abrasive table for 1/2" sand blasting nozzle @ 50psi
224232
public float MaxTrackSandBoxCapacityM3 = Me3.FromFt3(40.0f); // Capacity of sandbox - assume 40.0 cu ft
@@ -2901,43 +2909,60 @@ public virtual void UpdateWaterTroughRefill(float elapsedClockSeconds, float abs
29012909
public virtual void UpdateFrictionCoefficient(float elapsedClockSeconds)
29022910
{
29032911
float BaseuMax = (Curtius_KnifflerA / (MpS.ToKpH(AbsSpeedMpS) + Curtius_KnifflerB) + Curtius_KnifflerC); // Base Curtius - Kniffler equation - u = 0.33, all other values are scaled off this formula
2904-
float SandingFrictionCoefficientFactor = 0.0f;
2905-
//Set the friction coeff due to weather
2906-
if (Simulator.WeatherType == WeatherType.Rain || Simulator.WeatherType == WeatherType.Snow)
2912+
float SandingFrictionCoefficientFactor = 1.0f;
2913+
2914+
//Set the friction coeff due to weather - uses the vlaues set in the precipitation module to determine whether clear, rain or snow.
2915+
2916+
// Adjust clear weather for precipitation presence, ie base value between 60% and 80%
2917+
// note lowest friction will be for drizzle (light) rain; friction will increase for precipitation higher than drizzle rail
2918+
if (!Simulator.Paused)
29072919
{
2908-
if (Train.SlipperySpotDistanceM < 0)
2920+
var fogBaseFrictionCoefficientFactor = 0.0f;
2921+
var pricBaseFrictionCoefficientFactor = 0.0f;
2922+
float pric = Simulator.Weather.PricipitationIntensityPPSPM2 * 1000;
2923+
// precipitation will calculate a base coefficient value between 60% (light rain) and 80% (heavy rain) - this will be a factor that is used to adjust the base value - assume linear value between upper and lower precipitation values
2924+
if (pric >= 0.5)
2925+
pricBaseFrictionCoefficientFactor = Math.Min((pric - 0.5f) * 0.0078f + 0.6f, 0.8f); // should give a value between 0.6 and 0.8
2926+
else
2927+
pricBaseFrictionCoefficientFactor = 0.6f + 0.8f * (0.5f - pric); // should give a transition value between 1.0 and 0.6 as rain starts
2928+
2929+
// Adjust adhesion for impact of fog - default = 20000m = 20km
2930+
float fog = Simulator.Weather.FogDistance;
2931+
if (fog < 20000) // as fog thickens then decrease adhesion
29092932
{
2910-
Train.SlipperySpotLengthM = 10 + 40 * (float)Simulator.Random.NextDouble();
2911-
Train.SlipperySpotDistanceM = Train.SlipperySpotLengthM + 2000 * (float)Simulator.Random.NextDouble();
2933+
fogBaseFrictionCoefficientFactor = Math.Min((fog * 2.75e-4f + 0.6f), 1.0f); // If fog is less then 2km then it will impact friction, decrease adhesion to 60% (same as light rain transition)
29122934
}
2913-
if (Train.SlipperySpotDistanceM < Train.SlipperySpotLengthM)
2935+
else
29142936
{
2915-
BaseFrictionCoefficientFactor = 0.8f;
2937+
fogBaseFrictionCoefficientFactor = 1;
29162938
}
2917-
if (Simulator.WeatherType == WeatherType.Rain) // Wet weather
2939+
2940+
BaseFrictionCoefficientFactor = Math.Min(fogBaseFrictionCoefficientFactor, pricBaseFrictionCoefficientFactor);
2941+
}
2942+
2943+
// Random slippery track
2944+
if (Train.SlipperySpotDistanceM < 0)
2945+
{
2946+
Train.SlipperySpotLengthM = 10 + 40 * (float)Simulator.Random.NextDouble();
2947+
Train.SlipperySpotDistanceM = Train.SlipperySpotLengthM + 2000 * (float)Simulator.Random.NextDouble();
2948+
}
2949+
if (Train.SlipperySpotDistanceM < Train.SlipperySpotLengthM)
2950+
{
2951+
if (BaseFrictionCoefficientFactor > 0.6 && BaseFrictionCoefficientFactor < 0.8)
29182952
{
2919-
if (Simulator.Settings.AdhesionProportionalToWeather && AdvancedAdhesionModel && !Simulator.Paused) // Adjust clear weather for precipitation presence - base friction value will be approximately between 0.15 and 0.2
2920-
// ie base value between 0.8 and 1.0 (TODO)
2921-
// note lowest friction will be for drizzle rain; friction will increase for precipitation both higher and lower than drizzle rail
2922-
{
2923-
float pric = Simulator.Weather.PricipitationIntensityPPSPM2 * 1000;
2924-
// precipitation will calculate a value between 0.15 (light rain) and 0.2 (heavy rain) - this will be a factor that is used to adjust the base value - assume linear value between upper and lower precipitation values
2925-
if (pric >= 0.5)
2926-
BaseFrictionCoefficientFactor = Math.Min((pric * 0.0078f + 0.45f), 0.8f); // should give a minimum value between 0.8 and 1.0
2927-
else
2928-
BaseFrictionCoefficientFactor = Math.Min((0.4539f + 1.0922f * (0.5f - pric)), 0.8f); // should give a minimum value between 0.8 and 1.0
2929-
}
2930-
else // if not proportional to precipitation use fixed friction value of 0.8 x friction coefficient of 0.33
2931-
{
2932-
BaseFrictionCoefficientFactor = 0.8f;
2933-
}
2953+
BaseFrictionCoefficientFactor = 0.6f;
29342954
}
2935-
else // Snow weather
2955+
else
29362956
{
2937-
BaseFrictionCoefficientFactor = 0.6f;
2957+
BaseFrictionCoefficientFactor = 0.8f;
29382958
}
2959+
}
2960+
2961+
BaseFrictionCoefficientFactor = MathHelper.Clamp(BaseFrictionCoefficientFactor, 0.5f, 1.0f);
29392962

2940-
//add sander - more effective in wet weather, so increases adhesion by more
2963+
if (Simulator.WeatherType == WeatherType.Rain || Simulator.WeatherType == WeatherType.Snow)
2964+
{
2965+
//sander - more effective in wet weather, so increases adhesion by more
29412966
if (AbsSpeedMpS < SanderSpeedOfMpS && CurrentTrackSandBoxCapacityM3 > 0.0 && MainResPressurePSI > 80.0 && (AbsSpeedMpS > 0))
29422967
{
29432968
if (SanderSpeedEffectUpToMpS > 0.0f)
@@ -2946,7 +2971,7 @@ public virtual void UpdateFrictionCoefficient(float elapsedClockSeconds)
29462971
{
29472972
SandingFrictionCoefficientFactor = (1.0f - 0.5f / SanderSpeedEffectUpToMpS * AbsSpeedMpS) * 1.75f;
29482973
BaseFrictionCoefficientFactor *= SandingFrictionCoefficientFactor;
2949-
2974+
29502975
}
29512976
}
29522977
else
@@ -2959,27 +2984,9 @@ public virtual void UpdateFrictionCoefficient(float elapsedClockSeconds)
29592984
}
29602985
}
29612986
}
2962-
else // Default to Dry (Clear) weather
2987+
else // dry weather
29632988
{
2964-
2965-
if (Simulator.Settings.AdhesionProportionalToWeather && AdvancedAdhesionModel && !Simulator.Paused) // Adjust clear weather for fog presence
2966-
{
2967-
float fog = Simulator.Weather.FogDistance;
2968-
if (fog > 2000)
2969-
{
2970-
BaseFrictionCoefficientFactor = 1.0f; // if fog is not too thick don't change the friction
2971-
}
2972-
else
2973-
{
2974-
BaseFrictionCoefficientFactor = Math.Min((fog * 2.75e-4f + 0.8f), 0.8f); // If fog is less then 2km then it will impact friction, decrease adhesion by up to 20% (same as clear to wet transition)
2975-
}
2976-
}
2977-
else // if not proportional to fog use fixed friction value approximately equal to 0.33, thus factor will be 1.0 x friction coefficient of 0.33
2978-
{
2979-
BaseFrictionCoefficientFactor = 1.0f;
2980-
}
2981-
2982-
//add sander - not as effective in dry weather
2989+
//sander - not as effective in dry weather
29832990
if (AbsSpeedMpS < SanderSpeedOfMpS && CurrentTrackSandBoxCapacityM3 > 0.0 && MainResPressurePSI > 80.0 && (AbsSpeedMpS > 0))
29842991
{
29852992
if (SanderSpeedEffectUpToMpS > 0.0f)
@@ -2999,6 +3006,7 @@ public virtual void UpdateFrictionCoefficient(float elapsedClockSeconds)
29993006
}
30003007
}
30013008
}
3009+
30023010
}
30033011

30043012
// For wagons use base Curtius-Kniffler adhesion factor - u = 0.33
@@ -3016,13 +3024,54 @@ public virtual void UpdateFrictionCoefficient(float elapsedClockSeconds)
30163024

30173025
}
30183026

3019-
if (WheelSlip && ThrottlePercent > 0.2f && !BrakeSkid) // Test to see if loco wheel is slipping, then coeff of friction will be decreased below static value. Sanding will override this somewhat
3027+
// When wheel slips or skids, then dynamic (kinetic) coeff of friction will be decreased below static value. Sanding will override this somewhat.
3028+
// The transition between static and dynamic friction appears to decrease at an exponential rate until it reaches a steady state dynamic value.
3029+
//
3030+
3031+
3032+
// Test to see if loco wheel is slipping or skidding due to brake application
3033+
if ((EngineType == EngineTypes.Steam && SteamEngineType != MSTSSteamLocomotive.SteamEngineTypes.Geared) && WheelSlip && ((ThrottlePercent > 0.2f && !BrakeSkid) || (ThrottlePercent < 0.1f && BrakeSkid)))
30203034
{
3021-
BaseFrictionCoefficientFactor = 0.15f * SandingFrictionCoefficientFactor; // Descrease friction to take into account dynamic (kinetic) friction U = 0.0525
3035+
3036+
WheelStopSlipTimeS = 0; // Reset stop slip time if wheel slip starts
3037+
3038+
// Exponential curve is used to transition between static friction and dynamic friction when wheel slips
3039+
// Exponential constant calculated between two points, using this tool - https://mathcracker.com/exponential-function-calculator#results
3040+
// Google search suggests that Steel on steel has a static coeff = 0.74, and a dynamic coeff = 0.57. Hence reduction = 0.77.
3041+
// Constant points facilitate a decrease from 1 to 0.7 in 3 seconds - P1 = (0, 1), P2 = (5, 0.77). Hence exp constant = −0.0523
3042+
var expAdhesion = -0.0523;
3043+
WheelSlipTimeS += elapsedClockSeconds;
3044+
WheelSlipTimeS = MathHelper.Clamp(WheelSlipTimeS, 0.0f, 5.0f); // Ensure that time to transition between the two friction cases is maintained - currently set to 3 secs
3045+
3046+
float adhesionMultiplier = (float) Math.Exp(expAdhesion * WheelSlipTimeS);
3047+
CurrentWheelSlipAdhesionMultiplier = adhesionMultiplier;
3048+
3049+
BaseFrictionCoefficientFactor *= adhesionMultiplier; // Descrease friction to take into account dynamic (kinetic) friction, typically kinetic friction is approximately 50% of static friction.
3050+
SlipFrictionCoefficientFactor = BaseFrictionCoefficientFactor;
3051+
3052+
BaseFrictionCoefficientFactor = MathHelper.Clamp(BaseFrictionCoefficientFactor, 0.05f, 1.0f); // Ensure friction coefficient never exceeds a "reasonable" value
30223053
}
3023-
else if (WheelSlip && ThrottlePercent < 0.1f && BrakeSkid) // Test to see if loco wheel is skidding due to brake application
3054+
else
30243055
{
3025-
BaseFrictionCoefficientFactor = 0.15f * SandingFrictionCoefficientFactor; // Descrease friction to take into account dynamic (kinetic) friction U = 0.0525
3056+
WheelSlipTimeS = 0; // Reset slip time if wheel slip stops
3057+
3058+
if ((EngineType == EngineTypes.Steam && SteamEngineType != MSTSSteamLocomotive.SteamEngineTypes.Geared) && SlipFrictionCoefficientFactor < BaseFrictionCoefficientFactor && SlipFrictionCoefficientFactor != 0) // Once these two are equal then assume that wheels have stopped slipping.
3059+
{
3060+
// Trace.TraceInformation("SlipFriction {0} Base {1}", SlipFrictionCoefficientFactor, BaseFrictionCoefficientFactor);
3061+
// Exponential curve is used to transition between dynamic friction and static friction when wheel stops slipping
3062+
// Constant points facilitate an increase from 0.7 to 1 in 3 seconds - P1 = (5, 0.77), P2 = (0, 1). Hence exp constant = 0.0523
3063+
var expAdhesion = 0.0523;
3064+
WheelStopSlipTimeS += elapsedClockSeconds;
3065+
WheelStopSlipTimeS = MathHelper.Clamp(WheelStopSlipTimeS, 0.0f, 5.0f); // Ensure that time to transition between the two friction cases is maintained - currently set to 3 secs
3066+
3067+
float adhesionMultiplier = CurrentWheelSlipAdhesionMultiplier * (float)Math.Exp(expAdhesion * WheelStopSlipTimeS);
3068+
3069+
// Trace.TraceInformation("adhesion {0} StopTime {1} Base {2} Current {3}", adhesionMultiplier, WheelStopSlipTimeS, BaseFrictionCoefficientFactor, CurrentWheelSlipAdhesionMultiplier);
3070+
3071+
BaseFrictionCoefficientFactor *= adhesionMultiplier; // Descrease friction to take into account dynamic (kinetic) friction, typically kinetic friction is approximately 50% of static friction.
3072+
SlipFrictionCoefficientFactor = BaseFrictionCoefficientFactor;
3073+
BaseFrictionCoefficientFactor = MathHelper.Clamp(BaseFrictionCoefficientFactor, 0.05f, 1.0f); // Ensure friction coefficient never exceeds a "reasonable" value
3074+
}
30263075
}
30273076

30283077
var AdhesionMultiplier = Simulator.Settings.AdhesionFactor / 100.0f; // Convert to a factor where 100% = no change to adhesion

0 commit comments

Comments
 (0)