added two stroke scenario with vehicle
This commit is contained in:
166
Components/Vehicle.cs
Normal file
166
Components/Vehicle.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
|
||||
namespace FluidSim.Components
|
||||
{
|
||||
public class Vehicle
|
||||
{
|
||||
// ---- Gearbox ----
|
||||
public int CurrentGear { get; private set; } = 0;
|
||||
public readonly float[] GearRatios = { 2.5f, 1.8f, 1.4f, 1.1f, 0.9f, 0.75f };
|
||||
public float FinalDriveRatio = 3.0f;
|
||||
public float PrimaryReduction = 2.5f;
|
||||
|
||||
// ---- Clutch ----
|
||||
public float ClutchInput { get; set; }
|
||||
public float ClutchDisengageTime = 0.15f;
|
||||
private float _clutchTimer;
|
||||
private float _currentEngagement = 0f;
|
||||
|
||||
/// <summary>Time constant for clutch engagement smoothing (seconds).</summary>
|
||||
public float EngagementSmoothTime = 0.5f; // longer, gentler bite
|
||||
|
||||
private float TargetEngagement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ClutchInput > 0.01f) return 1f - ClutchInput;
|
||||
if (CurrentGear == 0 || _clutchTimer > 0f) return 0f;
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public float Engagement => _currentEngagement;
|
||||
|
||||
// ---- Clutch torque model ----
|
||||
/// <summary>Peak clutch friction torque (Nm) when fully engaged at high RPM.</summary>
|
||||
public float BaseMaxTorque = 80f; // much lower than before
|
||||
|
||||
/// <summary>Stiffness when slipping (Nm per rad/s). Lower = softer engagement.</summary>
|
||||
public float ClutchStiffness = 50f; // very soft
|
||||
|
||||
/// <summary>Below this engine RPM, the clutch torque is progressively reduced to prevent stalling.</summary>
|
||||
public float IdleRpm = 1200f;
|
||||
public float StallPreventionRamp = 300f; // RPM band above idle where torque ramps up
|
||||
|
||||
// ---- Physical constants ----
|
||||
public float Mass = 160f;
|
||||
public float WheelRadius = 0.32f;
|
||||
public float DragCoefficient = 0.35f;
|
||||
public float FrontalArea = 0.8f;
|
||||
public float AirDensity = 1.225f;
|
||||
public float RollingFrictionCoeff = 0.01f;
|
||||
public float Gravity = 9.81f;
|
||||
|
||||
// ---- State ----
|
||||
public float Speed { get; private set; }
|
||||
|
||||
public (float clutchTorqueOnEngine, float effectiveEngineInertia) Update(float engineRpm, float engineInertia, float dt)
|
||||
{
|
||||
if (_clutchTimer > 0f)
|
||||
{
|
||||
_clutchTimer -= dt;
|
||||
if (_clutchTimer < 0f) _clutchTimer = 0f;
|
||||
}
|
||||
|
||||
float target = TargetEngagement;
|
||||
float smoothing = 1f - MathF.Exp(-dt / Math.Max(EngagementSmoothTime, 0.001f));
|
||||
_currentEngagement += (target - _currentEngagement) * smoothing;
|
||||
if (MathF.Abs(_currentEngagement - target) < 0.001f)
|
||||
_currentEngagement = target;
|
||||
|
||||
float engagement = _currentEngagement;
|
||||
|
||||
float totalGear = 1f;
|
||||
if (CurrentGear > 0)
|
||||
totalGear = GearRatios[CurrentGear - 1] * FinalDriveRatio * PrimaryReduction;
|
||||
|
||||
float engineRadPerSec = engineRpm * 2f * MathF.PI / 60f;
|
||||
|
||||
float v = MathF.Max(Speed, 0f);
|
||||
float drag = 0.5f * AirDensity * DragCoefficient * FrontalArea * v * v;
|
||||
float rolling = RollingFrictionCoeff * Mass * Gravity;
|
||||
float resistanceForce = drag + rolling;
|
||||
|
||||
float clutchTorque = 0f;
|
||||
float effectiveInertia = engineInertia;
|
||||
|
||||
if (engagement > 0f && CurrentGear > 0)
|
||||
{
|
||||
float vehicleReflectedRadPerSec = (Speed / WheelRadius) * totalGear;
|
||||
float slip = engineRadPerSec - vehicleReflectedRadPerSec;
|
||||
|
||||
// Stall prevention: reduce max torque when engine RPM is near idle
|
||||
float torqueLimit = BaseMaxTorque * engagement;
|
||||
if (engineRpm < IdleRpm + StallPreventionRamp)
|
||||
{
|
||||
float factor = Math.Clamp((engineRpm - IdleRpm) / StallPreventionRamp, 0f, 1f);
|
||||
torqueLimit *= factor;
|
||||
}
|
||||
|
||||
float stiffnessTorque = ClutchStiffness * engagement * slip;
|
||||
clutchTorque = Math.Clamp(stiffnessTorque, -torqueLimit, torqueLimit);
|
||||
|
||||
// Lock if slip negligible and engagement high
|
||||
if (engagement >= 0.99f && MathF.Abs(slip) < 1.0f)
|
||||
{
|
||||
float vehicleInertia = Mass * WheelRadius * WheelRadius;
|
||||
float reflectedVehicleInertia = vehicleInertia / (totalGear * totalGear);
|
||||
effectiveInertia = engineInertia + reflectedVehicleInertia;
|
||||
|
||||
Speed = engineRadPerSec * WheelRadius / totalGear;
|
||||
float loadTorque = resistanceForce * WheelRadius / totalGear;
|
||||
return (loadTorque, effectiveInertia);
|
||||
}
|
||||
}
|
||||
|
||||
float driveTorqueAtWheel = clutchTorque * totalGear;
|
||||
float driveForce = driveTorqueAtWheel / WheelRadius;
|
||||
float netForce = driveForce - resistanceForce;
|
||||
float acceleration = netForce / Mass;
|
||||
Speed += acceleration * dt;
|
||||
if (Speed < 0f) Speed = 0f;
|
||||
|
||||
return (clutchTorque, engineInertia);
|
||||
}
|
||||
|
||||
public void ShiftUp()
|
||||
{
|
||||
if (CurrentGear < GearRatios.Length)
|
||||
{
|
||||
CurrentGear++;
|
||||
AutoDisengageClutch();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShiftDown()
|
||||
{
|
||||
if (CurrentGear > 1)
|
||||
{
|
||||
CurrentGear--;
|
||||
AutoDisengageClutch();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNeutral()
|
||||
{
|
||||
CurrentGear = 0;
|
||||
_clutchTimer = 0f;
|
||||
}
|
||||
|
||||
public void SetFirstGear()
|
||||
{
|
||||
if (CurrentGear == 0)
|
||||
{
|
||||
CurrentGear = 1;
|
||||
AutoDisengageClutch();
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoDisengageClutch()
|
||||
{
|
||||
_clutchTimer = ClutchDisengageTime;
|
||||
}
|
||||
|
||||
public float SpeedKmh => Speed * 3.6f;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user