235 lines
7.0 KiB
C#
235 lines
7.0 KiB
C#
using Sandbox;
|
|
using System;
|
|
|
|
namespace VeloX;
|
|
|
|
|
|
public partial class Differential : PowertrainComponent
|
|
{
|
|
|
|
|
|
/// <param name="T">Input torque</param>
|
|
/// <param name="Wa">Angular velocity of the outputA</param>
|
|
/// <param name="Wb">Angular velocity of the outputB</param>
|
|
/// <param name="Ia">Inertia of the outputA</param>
|
|
/// <param name="Ib">Inertia of the outputB</param>
|
|
/// <param name="dt">Time step</param>
|
|
/// <param name="biasAB">Torque bias between outputA and outputB. 0 = all torque goes to A, 1 = all torque goes to B</param>
|
|
/// <param name="stiffness">Stiffness of the limited slip or locked differential 0-1</param>
|
|
/// <param name="powerRamp">Stiffness under power</param>
|
|
/// <param name="coastRamp">Stiffness under braking</param>
|
|
/// <param name="slipTorque">Slip torque of the limited slip differential</param>
|
|
/// <param name="Ta">Torque output towards outputA</param>
|
|
/// <param name="Tb">Torque output towards outputB</param>
|
|
public delegate void SplitTorque( float T, float Wa, float Wb, float Ia, float Ib, float dt, float biasAB,
|
|
float stiffness, float powerRamp, float coastRamp, float slipTorque, out float Ta, out float Tb );
|
|
|
|
protected override void OnAwake()
|
|
{
|
|
base.OnAwake();
|
|
Name ??= "Differential";
|
|
AssignDifferentialDelegate();
|
|
}
|
|
public enum DifferentialType
|
|
{
|
|
Open,
|
|
Locked,
|
|
LimitedSlip,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Differential type.
|
|
/// </summary>
|
|
[Property]
|
|
public DifferentialType Type
|
|
{
|
|
get => _differentialType;
|
|
set
|
|
{
|
|
_differentialType = value;
|
|
AssignDifferentialDelegate();
|
|
}
|
|
}
|
|
private DifferentialType _differentialType;
|
|
|
|
/// <summary>
|
|
/// Torque bias between left (A) and right (B) output in [0,1] range.
|
|
/// </summary>
|
|
[Property, Range( 0, 1 )] public float BiasAB { get; set; } = 0.5f;
|
|
|
|
/// <summary>
|
|
/// Stiffness of locking differential [0,1]. Higher value
|
|
/// will result in lower difference in rotational velocity between left and right wheel.
|
|
/// Too high value might introduce slight oscillation due to drivetrain windup and a vehicle that is hard to steer.
|
|
/// </summary>
|
|
[Property, Range( 0, 1 ), HideIf( nameof( _differentialType ), DifferentialType.Open )] public float Stiffness { get; set; } = 0.5f;
|
|
|
|
/// <summary>
|
|
/// Stiffness of the LSD differential under acceleration.
|
|
/// </summary>
|
|
[Property, Range( 0, 1 ), ShowIf( nameof( _differentialType ), DifferentialType.LimitedSlip )] public float PowerRamp { get; set; } = 1f;
|
|
|
|
/// <summary>
|
|
/// Stiffness of the LSD differential under braking.
|
|
/// </summary>
|
|
[Property, Range( 0, 1 ), ShowIf( nameof( _differentialType ), DifferentialType.LimitedSlip )] public float CoastRamp { get; set; } = 0.5f;
|
|
|
|
|
|
/// <summary>
|
|
/// Second output of differential.
|
|
/// </summary>
|
|
[Property]
|
|
public PowertrainComponent OutputB
|
|
{
|
|
get { return _outputB; }
|
|
set
|
|
{
|
|
if ( value == this )
|
|
{
|
|
Log.Warning( $"{Name}: PowertrainComponent Output can not be self." );
|
|
OutputBNameHash = 0;
|
|
_output = null;
|
|
return;
|
|
}
|
|
if ( _outputB != null )
|
|
{
|
|
_outputB.InputNameHash = 0;
|
|
_outputB.Input = null;
|
|
}
|
|
|
|
_outputB = value;
|
|
|
|
if ( _outputB != null )
|
|
{
|
|
_outputB.Input = this;
|
|
OutputBNameHash = _outputB.ToString().GetHashCode();
|
|
}
|
|
else
|
|
{
|
|
OutputBNameHash = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected PowertrainComponent _outputB;
|
|
public int OutputBNameHash;
|
|
|
|
|
|
/// <summary>
|
|
/// Slip torque of limited slip differentials.
|
|
/// </summary>
|
|
[Property, ShowIf( nameof( _differentialType ), DifferentialType.LimitedSlip )] public float SlipTorque { get; set; } = 400f;
|
|
|
|
/// <summary>
|
|
/// Function delegate that will be used to split the torque between output(A) and outputB.
|
|
/// </summary>
|
|
public SplitTorque SplitTorqueDelegate;
|
|
|
|
private void AssignDifferentialDelegate()
|
|
{
|
|
SplitTorqueDelegate = _differentialType switch
|
|
{
|
|
DifferentialType.Open => OpenDiffTorqueSplit,
|
|
DifferentialType.Locked => LockingDiffTorqueSplit,
|
|
DifferentialType.LimitedSlip => LimitedDiffTorqueSplit,
|
|
_ => OpenDiffTorqueSplit,
|
|
};
|
|
}
|
|
|
|
public static void OpenDiffTorqueSplit( float T, float Wa, float Wb, float Ia, float Ib, float dt, float biasAB,
|
|
float stiffness, float powerRamp, float coastRamp, float slipTorque, out float Ta, out float Tb )
|
|
{
|
|
Ta = T * (1f - biasAB);
|
|
Tb = T * biasAB;
|
|
}
|
|
|
|
|
|
public static void LockingDiffTorqueSplit( float T, float Wa, float Wb, float Ia, float Ib, float dt, float biasAB,
|
|
float stiffness, float powerRamp, float coastRamp, float slipTorque, out float Ta, out float Tb )
|
|
{
|
|
Ta = T * (1f - biasAB);
|
|
Tb = T * biasAB;
|
|
|
|
float syncTorque = (Wa - Wb) * stiffness * (Ia + Ib) * 0.5f / dt;
|
|
|
|
Ta -= syncTorque;
|
|
Tb += syncTorque;
|
|
}
|
|
|
|
|
|
public static void LimitedDiffTorqueSplit( float T, float Wa, float Wb, float Ia, float Ib, float dt, float biasAB,
|
|
float stiffness, float powerRamp, float coastRamp, float slipTorque, out float Ta, out float Tb )
|
|
{
|
|
if ( Wa < 0 || Wb < 0 )
|
|
{
|
|
Ta = T * (1f - biasAB);
|
|
Tb = T * biasAB;
|
|
return;
|
|
}
|
|
|
|
// Минимальный момент трения, даже если разницы скоростей нет
|
|
float preloadTorque = MathF.Abs( T ) * 0.5f;
|
|
float speedDiff = Wa - Wb;
|
|
float ramp = T > 0 ? powerRamp : coastRamp;
|
|
|
|
// Основной момент трения LSD (зависит от разницы скоростей и preload)
|
|
float frictionTorque = ramp * (slipTorque * MathF.Abs( speedDiff ) + preloadTorque);
|
|
frictionTorque = MathF.Min( frictionTorque, MathF.Abs( T ) * 0.5f );
|
|
|
|
Ta = T * (1f - biasAB) - MathF.Sign( speedDiff ) * frictionTorque;
|
|
Tb = T * biasAB + MathF.Sign( speedDiff ) * frictionTorque;
|
|
}
|
|
public override float QueryAngularVelocity( float angularVelocity, float dt )
|
|
{
|
|
InputAngularVelocity = angularVelocity;
|
|
|
|
if ( OutputNameHash == 0 || OutputBNameHash == 0 )
|
|
return angularVelocity;
|
|
|
|
OutputAngularVelocity = InputAngularVelocity;
|
|
float Wa = _output.QueryAngularVelocity( OutputAngularVelocity, dt );
|
|
float Wb = _outputB.QueryAngularVelocity( OutputAngularVelocity, dt );
|
|
return (Wa + Wb) * 0.5f;
|
|
}
|
|
|
|
public override float QueryInertia()
|
|
{
|
|
if ( OutputNameHash == 0 || OutputBNameHash == 0 )
|
|
return Inertia;
|
|
|
|
float Ia = _output.QueryInertia();
|
|
float Ib = _outputB.QueryInertia();
|
|
float I = Inertia + (Ia + Ib);
|
|
|
|
return I;
|
|
}
|
|
|
|
|
|
public override float ForwardStep( float torque, float inertiaSum, float dt )
|
|
{
|
|
InputTorque = torque;
|
|
InputInertia = inertiaSum;
|
|
|
|
if ( OutputNameHash == 0 || OutputBNameHash == 0 )
|
|
return torque;
|
|
|
|
float Wa = _output.QueryAngularVelocity( OutputAngularVelocity, dt );
|
|
float Wb = _outputB.QueryAngularVelocity( OutputAngularVelocity, dt );
|
|
|
|
float Ia = _output.QueryInertia();
|
|
float Ib = _outputB.QueryInertia();
|
|
SplitTorqueDelegate.Invoke( torque, Wa, Wb, Ia, Ib, dt, BiasAB, Stiffness, PowerRamp,
|
|
CoastRamp, SlipTorque, out float Ta, out float Tb );
|
|
|
|
float outAInertia = inertiaSum * 0.5f + Ia;
|
|
float outBInertia = inertiaSum * 0.5f + Ib;
|
|
|
|
OutputTorque = Ta + Tb;
|
|
OutputInertia = outAInertia + outBInertia;
|
|
|
|
return _output.ForwardStep( Ta, outAInertia, dt ) + _outputB.ForwardStep( Tb, outBInertia, dt );
|
|
}
|
|
|
|
|
|
}
|