Skip to content

Commit e5815eb

Browse files
committed
Add support for AC induction motor
1 parent 95a3398 commit e5815eb

File tree

6 files changed

+178
-188
lines changed

6 files changed

+178
-188
lines changed

Source/Orts.Simulation/Orts.Simulation.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
<Compile Include="Simulation\Turntables.cs" />
195195
<Compile Include="Simulation\Weather.cs" />
196196
<Compile Include="Common\Scripting\EuropeanTrainControlSystem.cs" />
197+
<Compile Include="Simulation\RollingStocks\SubSystems\PowerTransmissions\InductionMotor.cs" />
197198
</ItemGroup>
198199
<ItemGroup>
199200
<ProjectReference Include="..\ORTS.Common\ORTS.Common.csproj">

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

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,10 @@ public float OdometerM
442442

443443
public enum TractionMotorTypes
444444
{
445+
DC,
445446
AC,
446-
DC
447447
}
448-
public TractionMotorTypes TractionMotorType;
448+
public TractionMotorTypes TractionMotorType = TractionMotorTypes.DC;
449449

450450
public ILocomotivePowerSupply LocomotivePowerSupply => PowerSupply as ILocomotivePowerSupply;
451451
public ScriptedTrainControlSystem TrainControlSystem;
@@ -1460,7 +1460,11 @@ public override void Initialize()
14601460
Trace.TraceInformation("Number of Locomotive Drive Axles set to default value of {0}", LocoNumDrvAxles);
14611461
}
14621462
}
1463-
1463+
if (TractionMotorType == TractionMotorTypes.AC)
1464+
{
1465+
InductionMotor motor = new InductionMotor(LocomotiveAxle, this);
1466+
}
1467+
14641468

14651469
// Calculate minimum speed to pickup water
14661470
const float Aconst = 2;
@@ -1873,6 +1877,7 @@ public override void Update(float elapsedClockSeconds)
18731877
}
18741878
else
18751879
DynamicBrakeForceN = 0; // Set dynamic brake force to zero if in Notch 0 position
1880+
18761881

18771882
UpdateFrictionCoefficient(elapsedClockSeconds); // Find the current coefficient of friction depending upon the weather
18781883

@@ -2242,11 +2247,6 @@ protected virtual void UpdateTractiveForce(float elapsedClockSeconds, float t, f
22422247
if (TractionMotorType == TractionMotorTypes.AC)
22432248
{
22442249
AbsTractionSpeedMpS = AbsSpeedMpS;
2245-
if (AbsWheelSpeedMpS > 1.1 * MaxSpeedMpS)
2246-
{
2247-
AverageForceN = TractiveForceN = 0;
2248-
return;
2249-
}
22502250
}
22512251
else
22522252
{
@@ -2644,21 +2644,30 @@ public void AdvancedAdhesion(float elapsedClockSeconds)
26442644
LocomotiveAxle.DampingNs = MassKG / 1000.0f;
26452645
LocomotiveAxle.FrictionN = MassKG / 1000.0f;
26462646

2647-
if (SlipControlSystem == SlipControlType.Full)
2647+
if (LocomotiveAxle.Motor is InductionMotor motor)
26482648
{
2649-
// Simple slip control
2650-
// Motive force is reduced to the maximum adhesive force
2651-
// In wheelslip situations, motive force is set to zero
2652-
MotiveForceN = Math.Sign(MotiveForceN) * Math.Min(LocomotiveAxle.AdhesionLimit * LocomotiveAxle.AxleWeightN, Math.Abs(MotiveForceN));
2653-
if (LocomotiveAxle.IsWheelSlip) MotiveForceN = 0;
2649+
motor.SlipControl = SlipControlSystem == SlipControlType.Full;
2650+
motor.TargetForceN = MotiveForceN;
2651+
motor.EngineMaxSpeedMpS = MaxSpeedMpS;
2652+
}
2653+
else
2654+
{
2655+
if (SlipControlSystem == SlipControlType.Full)
2656+
{
2657+
// Simple slip control
2658+
// Motive force is reduced to the maximum adhesive force
2659+
// In wheelslip situations, motive force is set to zero
2660+
MotiveForceN = Math.Sign(MotiveForceN) * Math.Min(LocomotiveAxle.AdhesionLimit * LocomotiveAxle.AxleWeightN, Math.Abs(MotiveForceN));
2661+
if (LocomotiveAxle.IsWheelSlip) MotiveForceN = 0;
2662+
}
2663+
LocomotiveAxle.DriveForceN = MotiveForceN; //Total force applied to wheels
26542664
}
26552665

26562666
//Set axle model parameters
26572667

26582668
// Inputs
26592669
LocomotiveAxle.BrakeRetardForceN = BrakeRetardForceN;
26602670
LocomotiveAxle.AxleWeightN = 9.81f * DrvWheelWeightKg; //will be computed each time considering the tilting
2661-
LocomotiveAxle.DriveForceN = MotiveForceN; //Total force applied to wheels
26622671
LocomotiveAxle.TrainSpeedMpS = SpeedMpS; //Set the train speed of the axle mod
26632672
var watch = new Stopwatch();
26642673
watch.Start();

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/PowerTransmissions/Axle.cs

Lines changed: 59 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,7 @@ public class Axle
7777
/// <summary>
7878
/// Axle drive type covered by DriveType interface
7979
/// </summary>
80-
protected AxleDriveType driveType;
81-
/// <summary>
82-
/// Read/Write Axle drive type flag
83-
/// </summary>
84-
public AxleDriveType DriveType { set { driveType = value; } get { return driveType; } }
80+
public AxleDriveType DriveType;
8581

8682
/// <summary>
8783
/// Axle drive represented by a motor, covered by ElectricMotor interface
@@ -96,17 +92,12 @@ public ElectricMotor Motor
9692
set
9793
{
9894
motor = value;
99-
switch(driveType)
95+
DriveType = motor != null ? AxleDriveType.MotorDriven : AxleDriveType.ForceDriven;
96+
switch(DriveType)
10097
{
101-
case AxleDriveType.NotDriven:
102-
break;
10398
case AxleDriveType.MotorDriven:
104-
//Total inertia considering gearbox
10599
totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor.InertiaKgm2;
106100
break;
107-
case AxleDriveType.ForceDriven:
108-
totalInertiaKgm2 = inertiaKgm2;
109-
break;
110101
default:
111102
totalInertiaKgm2 = inertiaKgm2;
112103
break;
@@ -149,7 +140,7 @@ public float InertiaKgm2
149140
if (value <= 0.0)
150141
throw new NotSupportedException("Inertia must be greater than zero");
151142
inertiaKgm2 = value;
152-
switch (driveType)
143+
switch (DriveType)
153144
{
154145
case AxleDriveType.NotDriven:
155146
break;
@@ -398,54 +389,8 @@ public Axle()
398389
{
399390
transmissionEfficiency = 0.99f;
400391
SlipWarningTresholdPercent = 70.0f;
401-
driveType = AxleDriveType.ForceDriven;
402-
403-
switch (driveType)
404-
{
405-
case AxleDriveType.NotDriven:
406-
break;
407-
case AxleDriveType.MotorDriven:
408-
totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor.InertiaKgm2;
409-
break;
410-
case AxleDriveType.ForceDriven:
411-
totalInertiaKgm2 = inertiaKgm2;
412-
break;
413-
default:
414-
totalInertiaKgm2 = inertiaKgm2;
415-
break;
416-
}
417-
}
418-
419-
/// <summary>
420-
/// Creates motor driven axle class instance
421-
/// - sets TransmissionEfficiency to 0.99 (99%)
422-
/// - sets SlipWarningThresholdPercent to 70%
423-
/// - sets axle DriveType to MotorDriven
424-
/// - updates totalInertiaKgm2 parameter
425-
/// </summary>
426-
/// <param name="electricMotor">Electric motor connected with the axle</param>
427-
public Axle(ElectricMotor electricMotor)
428-
{
429-
motor = electricMotor;
430-
motor.AxleConnected = this;
431-
transmissionEfficiency = 0.99f;
432-
driveType = AxleDriveType.MotorDriven;
433-
434-
switch (driveType)
435-
{
436-
case AxleDriveType.NotDriven:
437-
totalInertiaKgm2 = inertiaKgm2;
438-
break;
439-
case AxleDriveType.MotorDriven:
440-
totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor.InertiaKgm2;
441-
break;
442-
case AxleDriveType.ForceDriven:
443-
totalInertiaKgm2 = inertiaKgm2;
444-
break;
445-
default:
446-
totalInertiaKgm2 = inertiaKgm2;
447-
break;
448-
}
392+
DriveType = AxleDriveType.ForceDriven;
393+
totalInertiaKgm2 = inertiaKgm2;
449394
}
450395

451396
/// <summary>
@@ -478,10 +423,10 @@ public void Save(BinaryWriter outf)
478423
float axleForceN = AxleWeightN * SlipCharacteristics(axleSpeedMpS - TrainSpeedMpS, TrainSpeedMpS, AdhesionK, AdhesionLimit);
479424

480425
float motiveAxleForceN = -axleForceN - dampingNs * (axleSpeedMpS - TrainSpeedMpS); // Force transmitted to rail + heat losses
481-
if (driveType == AxleDriveType.ForceDriven)
482-
motiveAxleForceN += driveForceN * transmissionEfficiency;
483-
else if (driveType == AxleDriveType.MotorDriven)
484-
motiveAxleForceN += motor.DevelopedTorqueNm * transmissionEfficiency * WheelRadiusM;
426+
if (DriveType == AxleDriveType.ForceDriven)
427+
motiveAxleForceN += DriveForceN * transmissionEfficiency;
428+
else if (DriveType == AxleDriveType.MotorDriven)
429+
motiveAxleForceN += motor.GetDevelopedTorqueNm(axleSpeedMpS * transmissionRatio / WheelRadiusM) * transmissionEfficiency / WheelRadiusM;
485430

486431
// Dissipative forces: they will never increase wheel speed
487432
float frictionalForceN = BrakeRetardForceN + frictionN;
@@ -501,12 +446,17 @@ public void Save(BinaryWriter outf)
501446
return (totalAxleForceN * forceToAccelerationFactor, axleSpeedMpS / WheelRadiusM, axleForceN);
502447
}
503448

449+
/// <summary>
450+
/// Integrates the wheel rotation movement using a RK4 method,
451+
/// calculating the required number of substeps
452+
/// Outputs: wheel speed, wheel angular position and motive force
453+
/// </summary>
504454
void Integrate(float elapsedClockSeconds)
505455
{
506456
if (elapsedClockSeconds <= 0) return;
507457
float prevSpeedMpS = AxleSpeedMpS;
508458

509-
if (Math.Abs(integratorError) > Math.Max(SlipSpeedMpS, 1) / 1000)
459+
if (Math.Abs(integratorError) > Math.Max((Math.Abs(SlipSpeedMpS)-1)*0.01f, 0.001f))
510460
{
511461
++NumOfSubstepsPS;
512462
waitBeforeSpeedingUp = 100;
@@ -527,7 +477,7 @@ void Integrate(float elapsedClockSeconds)
527477
for (int i=0; i<NumOfSubstepsPS; i++)
528478
{
529479
var k1 = GetAxleMotionVariation(AxleSpeedMpS);
530-
if (i == 0 && k1.Item1 * dt > Math.Max(SlipSpeedMpS, 1) / 100)
480+
if (i == 0 && k1.Item1 * dt > Math.Max((Math.Abs(SlipSpeedMpS) - 1) * 10, 1) / 100)
531481
{
532482
NumOfSubstepsPS = Math.Min(NumOfSubstepsPS + 5, 50);
533483
dt = elapsedClockSeconds / NumOfSubstepsPS;
@@ -549,6 +499,37 @@ void Integrate(float elapsedClockSeconds)
549499
}
550500
}
551501

502+
/// <summary>
503+
/// Work in progress. Calculates wheel creep assuming that wheel inertia
504+
/// is low, removing the need of an integrator
505+
/// Useful for slow CPUs
506+
/// </summary>
507+
void StationaryCalculation(float elapsedClockSeconds)
508+
{
509+
var res = GetAxleMotionVariation(AxleSpeedMpS);
510+
float force = res.Item1 / forceToAccelerationFactor + res.Item3;
511+
float maxAdhesiveForce = AxleWeightN * AdhesionLimit;
512+
if (maxAdhesiveForce == 0) return;
513+
float forceRatio = force / maxAdhesiveForce;
514+
float absForceRatio = Math.Abs(forceRatio);
515+
float characteristicTime = WheelSlipThresholdMpS / (maxAdhesiveForce * forceToAccelerationFactor);
516+
if (absForceRatio > 1 || IsWheelSlip || Math.Abs(res.Item1 * elapsedClockSeconds) < WheelSlipThresholdMpS)
517+
{
518+
Integrate(elapsedClockSeconds);
519+
return;
520+
}
521+
NumOfSubstepsPS = 1;
522+
if (absForceRatio < 0.001f)
523+
{
524+
AxleSpeedMpS = TrainSpeedMpS;
525+
AxleForceN = 0;
526+
return;
527+
}
528+
float x = (float)((1 - Math.Sqrt(1 - forceRatio * forceRatio)) / forceRatio);
529+
AxleSpeedMpS = TrainSpeedMpS + MpS.FromKpH(AdhesionK * x / AdhesionLimit);
530+
AxleForceN = (force + res.Item3)/2;
531+
}
532+
552533
/// <summary>
553534
/// Main Update method
554535
/// - computes slip characteristics to get new axle force
@@ -570,11 +551,8 @@ public virtual void Update(float timeSpan)
570551
CompensatedAxleForceN = AxleForceN + Math.Sign(TrainSpeedMpS) * BrakeRetardForceN;
571552
if (AxleForceN == 0) CompensatedAxleForceN = 0;
572553

573-
if (driveType == AxleDriveType.MotorDriven)
574-
{
575-
motor.RevolutionsRad = AxleSpeedMpS * transmissionRatio / WheelRadiusM;
576-
motor.Update(timeSpan);
577-
}
554+
motor?.Update(timeSpan);
555+
578556
if (timeSpan > 0.0f)
579557
{
580558
slipDerivationMpSS = (SlipSpeedMpS - previousSlipSpeedMpS) / timeSpan;
@@ -591,8 +569,7 @@ public virtual void Update(float timeSpan)
591569
public void Reset()
592570
{
593571
AxleSpeedMpS = 0;
594-
if (motor != null)
595-
motor.Reset();
572+
motor?.Reset();
596573

597574
}
598575

@@ -604,8 +581,7 @@ public void Reset(double resetTime, float initValue)
604581
{
605582
AxleSpeedMpS = initValue;
606583
ResetTime = resetTime;
607-
if (motor != null)
608-
motor.Reset();
584+
motor?.Reset();
609585
}
610586

611587
/// <summary>
@@ -619,11 +595,12 @@ public void Reset(double resetTime, float initValue)
619595
/// 2*K*umax^2*dV
620596
/// u = ---------------------
621597
/// umax^2*dv^2 + K^2
622-
/// For high slip speeds (after the inflexion point of u), the formula is
623-
/// replaced with an exponentially decaying function (with smooth coupling)
624-
/// reaching a 40% of maximum adhesion at infinity. Quick fix until
625-
/// further investigation is done to get a single formula that provides
626-
/// non zero adhesion at infinity.
598+
///
599+
/// For high slip speeds the formula is replaced with an exponentially
600+
/// decaying function (with smooth coupling) which reaches 40% of
601+
/// maximum adhesion at infinity. The transition point between equations
602+
/// is at dV = sqrt(3)*K/umax (inflection point)
603+
///
627604
/// </summary>
628605
/// <param name="slipSpeedMpS">Difference between train speed and wheel speed</param>
629606
/// <param name="speedMpS">Current speed</param>
@@ -637,7 +614,7 @@ public float SlipCharacteristics(float slipSpeedMpS, float speedMpS, float K, fl
637614
float absx = Math.Abs(x);
638615
float sqrt3 = (float)Math.Sqrt(3);
639616
if (absx > sqrt3)
640-
{
617+
{
641618
// At infinity, adhesion is 40% of maximum (Polach, 2005)
642619
// The value must be lower than 85% for the formula to work
643620
float inftyFactor = 0.4f;

0 commit comments

Comments
 (0)