уже лучше
This commit is contained in:
@@ -87,7 +87,7 @@ public class Engine : PowertrainComponent
|
||||
angularVelocity += finalTorque / inertiaSum * Time.Delta;
|
||||
angularVelocity = Math.Max( angularVelocity, 0 );
|
||||
|
||||
//UpdateStream();
|
||||
UpdateStream();
|
||||
|
||||
return finalTorque;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ public class PowerWheel : PowertrainComponent
|
||||
{
|
||||
[Property] public VeloXWheel Wheel { get; set; }
|
||||
|
||||
public override float QueryInertia() => Wheel.Inertia;
|
||||
public override float QueryInertia()
|
||||
{
|
||||
float dtScale = Math.Clamp( Time.Delta, 0.01f, 0.05f ) / 0.005f;
|
||||
return Wheel.BaseInertia * dtScale;
|
||||
}
|
||||
|
||||
public override float QueryAngularVelocity( float angularVelocity )
|
||||
{
|
||||
@@ -19,7 +23,8 @@ public class PowerWheel : PowertrainComponent
|
||||
Wheel.AutoPhysics = false;
|
||||
Wheel.Torque = torque;
|
||||
Wheel.Brake = Vehicle.Brake;
|
||||
|
||||
Inertia = Wheel.BaseInertia + inertia;
|
||||
Wheel.Inertia = inertia;
|
||||
Wheel.DoPhysics( Vehicle );
|
||||
|
||||
angularVelocity = Wheel.AngularVelocity;
|
||||
|
||||
@@ -167,7 +167,7 @@ public class Pacejka
|
||||
|
||||
|
||||
/// pacejka magic formula for longitudinal force
|
||||
public float PacejkaFx( float sigma, float Fz, float friction_coeff )
|
||||
public float PacejkaFx( float sigma, float Fz, float friction_coeff, out float maxforce_output )
|
||||
{
|
||||
var b = Longitudinal;
|
||||
|
||||
@@ -198,6 +198,7 @@ public class Pacejka
|
||||
|
||||
// scale by surface friction
|
||||
Fx *= friction_coeff;
|
||||
maxforce_output = D;
|
||||
|
||||
return Fx;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,24 @@ public class TirePreset : GameResource
|
||||
return resistance;
|
||||
}
|
||||
|
||||
public void ComputeSlip( float vlon, float vlat, float vrot, out float slip_ratio, out float slip_angle )
|
||||
public static void ComputeSlip( float lon_velocity, float lat_velocity, float rot_velocity, float wheel_radius, out float slip_ratio, out float slip_angle )
|
||||
{
|
||||
float rvlon = 1 / MathF.Max( MathF.Abs( vlon ), 1E-3f );
|
||||
float vslip = vrot - vlon;
|
||||
slip_ratio = vslip * rvlon;
|
||||
slip_angle = -MathF.Atan( vlat * rvlon );
|
||||
var abs_lon = Math.Max( MathF.Abs( lon_velocity ), 1e-3f );
|
||||
|
||||
slip_ratio = lon_velocity - rot_velocity * wheel_radius;
|
||||
|
||||
if ( abs_lon >= 0.005f )
|
||||
slip_ratio /= abs_lon;
|
||||
else
|
||||
slip_ratio *= abs_lon;
|
||||
|
||||
if ( abs_lon >= 0.5f )
|
||||
slip_angle = MathF.Atan2( -lat_velocity, abs_lon ).RadianToDegree() / 50f;
|
||||
else
|
||||
slip_angle = -lat_velocity * (0.01f / Time.Delta);
|
||||
|
||||
slip_ratio = Math.Clamp( slip_ratio, -1, 1 );
|
||||
slip_angle = Math.Clamp( slip_angle, -1, 1 );
|
||||
|
||||
}
|
||||
|
||||
@@ -40,104 +52,6 @@ public class TirePreset : GameResource
|
||||
return ((1 / 6.0f) * (sc * sc) + 1) * sc;
|
||||
}
|
||||
|
||||
public struct TireState()
|
||||
{
|
||||
/// <summary>
|
||||
/// surface friction coefficient
|
||||
/// </summary>
|
||||
public float friction = 0;
|
||||
|
||||
/// <summary>
|
||||
/// tire camber angle relative to track surface
|
||||
/// </summary>
|
||||
public float camber = 0;
|
||||
|
||||
/// <summary>
|
||||
/// camber thrust induced lateral slip velocity
|
||||
/// </summary>
|
||||
public float vcam = 0;
|
||||
|
||||
/// <summary>
|
||||
/// ratio of tire contact patch speed to road speed
|
||||
/// </summary>
|
||||
public float slip = 0;
|
||||
|
||||
/// <summary>
|
||||
/// the angle between the wheel heading and the wheel velocity
|
||||
/// </summary>
|
||||
public float slip_angle = 0;
|
||||
|
||||
/// <summary>
|
||||
/// peak force slip ratio
|
||||
/// </summary>
|
||||
public float ideal_slip = 0;
|
||||
|
||||
/// <summary>
|
||||
/// peak force slip angle
|
||||
/// </summary>
|
||||
public float ideal_slip_angle = 0;
|
||||
|
||||
/// <summary>
|
||||
/// positive during traction
|
||||
/// </summary>
|
||||
public float fx = 0;
|
||||
|
||||
/// <summary>
|
||||
/// positive during traction in a right turn
|
||||
/// </summary>
|
||||
public float fy = 0;
|
||||
|
||||
/// <summary>
|
||||
/// positive during traction in a left turn
|
||||
/// </summary>
|
||||
public float mz = 0;
|
||||
};
|
||||
|
||||
public void ComputeState(
|
||||
float normal_force,
|
||||
float rot_velocity,
|
||||
float lon_velocity,
|
||||
float lat_velocity,
|
||||
float camber_angle,
|
||||
out TireState s
|
||||
)
|
||||
{
|
||||
s = new TireState
|
||||
{
|
||||
camber = camber_angle,
|
||||
friction = 1.0f
|
||||
};
|
||||
|
||||
if ( normal_force * s.friction < 1E-6f )
|
||||
{
|
||||
s.slip = s.slip_angle = 0;
|
||||
s.fx = s.fy = s.mz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
float Fz = MathF.Min( normal_force * 0.001f, 30f );
|
||||
|
||||
ComputeSlip( lon_velocity, lat_velocity, rot_velocity, out float slip, out float slip_angle );
|
||||
float sigma = slip;
|
||||
float alpha = slip_angle.RadianToDegree();
|
||||
float gamma = s.camber.RadianToDegree();
|
||||
|
||||
float Fx0 = Pacejka.PacejkaFx( sigma, Fz, s.friction );
|
||||
float Fy0 = Pacejka.PacejkaFy( alpha, Fz, gamma, s.friction, out float camber_alpha );
|
||||
|
||||
// combined slip
|
||||
float Gx = Pacejka.PacejkaGx( slip, slip_angle );
|
||||
float Gy = Pacejka.PacejkaGy( slip, slip_angle );
|
||||
float Fx = Gx * Fx0;
|
||||
float Fy = Gy * Fy0;
|
||||
|
||||
s.vcam = ComputeCamberVelocity( camber_alpha.DegreeToRadian(), lon_velocity );
|
||||
s.slip = slip;
|
||||
s.slip_angle = slip_angle;
|
||||
s.fx = Fx;
|
||||
s.fy = Fy;
|
||||
}
|
||||
|
||||
public static float ComputeCamberVelocity( float sa, float vx )
|
||||
{
|
||||
float tansa = (1 / 3.0f * (sa * sa) + 1) * sa;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Rendering;
|
||||
using Sandbox.Services;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using static Sandbox.CameraComponent;
|
||||
using static Sandbox.Package;
|
||||
using static Sandbox.SkinnedModelRenderer;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
@@ -40,7 +44,7 @@ public partial class VeloXWheel : Component
|
||||
public float Spin { get; private set; }
|
||||
|
||||
public float RPM { get => angularVelocity * 30f / MathF.PI; set => angularVelocity = value / (30f / MathF.PI); }
|
||||
public float AngularVelocity => angularVelocity;
|
||||
public float AngularVelocity { get => angularVelocity; set => angularVelocity = value; }
|
||||
|
||||
internal float DistributionFactor { get; set; }
|
||||
|
||||
@@ -65,8 +69,12 @@ public partial class VeloXWheel : Component
|
||||
private Vector3 force;
|
||||
public float CounterTorque { get; private set; }
|
||||
|
||||
private float BaseInertia => Mass * MathF.Pow( Radius.InchToMeter(), 2 );
|
||||
public float Inertia => BaseInertia;
|
||||
internal float BaseInertia => 0.5f * Mass * MathF.Pow( Radius.InchToMeter(), 2 );
|
||||
public float Inertia
|
||||
{
|
||||
get => BaseInertia + inertia;
|
||||
set => inertia = value;
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAwake()
|
||||
@@ -83,12 +91,8 @@ public partial class VeloXWheel : Component
|
||||
|
||||
private void UpdateVisuals( VeloXBase vehicle, in float dt )
|
||||
{
|
||||
var entityAngles = vehicle.WorldRotation;
|
||||
|
||||
Spin -= angularVelocity.MeterToInch() * dt;
|
||||
|
||||
Spin -= angularVelocity.RadianToDegree() * dt;
|
||||
WorldRotation = vehicle.WorldTransform.RotationToWorld( GetSteer( vehicle.SteerAngle.yaw ) ) * Rotation.FromAxis( Vector3.Right, Spin );
|
||||
|
||||
}
|
||||
|
||||
private Rotation GetSteer( float steer )
|
||||
@@ -108,16 +112,14 @@ public partial class VeloXWheel : Component
|
||||
private static float GetLongitudinalLoadCoefficient( float load ) => 11000 * (1 - MathF.Exp( -0.00014f * load ));
|
||||
private static float GetLateralLoadCoefficient( float load ) => 18000 * (1 - MathF.Exp( -0.0001f * load ));
|
||||
float lastload;
|
||||
private float inertia;
|
||||
|
||||
public void DoPhysics( VeloXBase vehicle )
|
||||
{
|
||||
var pos = vehicle.WorldTransform.PointToWorld( StartPos );
|
||||
|
||||
var ang = vehicle.WorldTransform.RotationToWorld( GetSteer( vehicle.SteerAngle.yaw ) );
|
||||
|
||||
forward = ang.Forward;
|
||||
right = ang.Right;
|
||||
up = ang.Up;
|
||||
|
||||
var maxLen = SuspensionLength;
|
||||
|
||||
var endPos = pos + ang.Down * maxLen;
|
||||
@@ -129,6 +131,12 @@ public partial class VeloXWheel : Component
|
||||
.UseHitPosition( false )
|
||||
.WithoutTags( vehicle.WheelIgnoredTags )
|
||||
.Run();
|
||||
//forward = ang.Forward;
|
||||
//right = ang.Right;
|
||||
up = ang.Up;
|
||||
|
||||
forward = Vector3.VectorPlaneProject( ang.Forward, Trace.Normal ).Normal;
|
||||
right = Vector3.VectorPlaneProject( ang.Right, Trace.Normal ).Normal;
|
||||
|
||||
var fraction = Trace.Fraction;
|
||||
|
||||
@@ -141,7 +149,9 @@ public partial class VeloXWheel : Component
|
||||
|
||||
var normal = Trace.Normal;
|
||||
|
||||
var vel = vehicle.Body.GetVelocityAtPoint( pos );
|
||||
var vel = vehicle.Body.GetVelocityAtPoint( contactPos );
|
||||
|
||||
//var vel = vehicle.Body.GetVelocityAtPoint( pos );
|
||||
|
||||
if ( !IsOnGround )
|
||||
{
|
||||
@@ -191,76 +201,105 @@ public partial class VeloXWheel : Component
|
||||
|
||||
float camber_rad = CamberAngle.DegreeToRadian();
|
||||
float R = Radius.InchToMeter();
|
||||
TirePreset.ComputeState(
|
||||
2500,
|
||||
angularVelocity * R,
|
||||
forwardSpeed,
|
||||
sideSpeed,
|
||||
camber_rad,
|
||||
out var tireState
|
||||
);
|
||||
|
||||
//TirePreset.ComputeState(
|
||||
// load,
|
||||
// angularVelocity,
|
||||
// forwardSpeed,
|
||||
// sideSpeed,
|
||||
// camber_rad,
|
||||
// R,
|
||||
// Inertia,
|
||||
// out var tireState
|
||||
//);
|
||||
|
||||
float linearSpeed = angularVelocity * Radius.InchToMeter();
|
||||
float F_roll = TirePreset.GetRollingResistance( linearSpeed, 1.0f );
|
||||
F_roll = -MathF.Sign( forwardSpeed ) * F_roll;
|
||||
F_roll *= -Math.Clamp( linearSpeed * 0.25f, -1, 1 );
|
||||
|
||||
float Fx_total = tireState.fx + F_roll;
|
||||
//float Fx_total = tireState.fx + F_roll;
|
||||
|
||||
float T_brake = Brake * BrakePowerMax;
|
||||
|
||||
if ( angularVelocity > 0 ) T_brake = -T_brake;
|
||||
else T_brake = angularVelocity < 0 ? T_brake : -MathF.Sign( Torque ) * T_brake;
|
||||
float totalTorque = Torque + T_brake - Fx_total * R;
|
||||
//float totalTorque = Torque + tireState.fx;
|
||||
|
||||
// not work
|
||||
Vector3 c = pos.Cross( forward );
|
||||
Vector3 v = (c * vehicle.Body.PhysicsBody.Inertia).Cross( pos );
|
||||
var m = 1 / (1 / vehicle.Body.Mass + forward.Dot( v ));
|
||||
m *= Inertia / (m * R * R + Inertia);
|
||||
float ve = forward.Dot( vel ).InchToMeter() - angularVelocity;
|
||||
angularVelocity += Torque / Inertia * Time.Delta;
|
||||
angularVelocity += T_brake / Inertia * Time.Delta;
|
||||
angularVelocity += F_roll * 9.80665f / Inertia * Time.Delta;
|
||||
TirePreset.ComputeSlip( forwardSpeed, sideSpeed, angularVelocity, R, out var slip, out var slip_ang );
|
||||
var fx = TirePreset.Pacejka.PacejkaFx( slip, load, 1, out var maxTorque );
|
||||
var fy = TirePreset.Pacejka.PacejkaFy( slip_ang * load, load, camber_rad, 1, out var _ );
|
||||
|
||||
angularVelocity += ve * m * Radius * (1 / Inertia);// totalTorque * (1 / Inertia) * Time.Delta;
|
||||
maxTorque *= R;
|
||||
var errorTorque = (angularVelocity - forwardSpeed / R) * Inertia / Time.Delta;
|
||||
|
||||
var surfaceTorque = Math.Clamp( errorTorque, -maxTorque, maxTorque );
|
||||
|
||||
angularVelocity -= surfaceTorque / Inertia * Time.Delta;
|
||||
|
||||
forwardFriction = new Friction()
|
||||
{
|
||||
Slip = tireState.slip,
|
||||
Force = Fx_total,
|
||||
Slip = slip,
|
||||
Force = -fx,
|
||||
Speed = forwardSpeed
|
||||
};
|
||||
|
||||
sideFriction = new Friction()
|
||||
{
|
||||
Slip = tireState.slip_angle,
|
||||
Force = tireState.fy,
|
||||
Slip = slip_ang,
|
||||
Force = fy,
|
||||
Speed = sideSpeed
|
||||
};
|
||||
|
||||
Vector3 frictionForce = forward * forwardFriction.Force + right * sideFriction.Force;
|
||||
vehicle.Body.ApplyForceAt( contactPos, (force + frictionForce) / Time.Delta );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Колесо в воздухе: сбрасываем силы
|
||||
forwardFriction = new Friction();
|
||||
sideFriction = new Friction();
|
||||
vehicle.Body.ApplyForceAt( pos, force / Time.Delta * ProjectSettings.Physics.SubSteps );
|
||||
vehicle.Body.ApplyForceAt( pos, frictionForce * ProjectSettings.Physics.SubSteps );
|
||||
|
||||
// Обновление угловой скорости только от мотора/тормозов
|
||||
float T_brake = Brake * BrakePowerMax;
|
||||
|
||||
if ( angularVelocity > 0 ) T_brake = -T_brake;
|
||||
else T_brake = angularVelocity < 0 ? T_brake : -MathF.Sign( Torque ) * T_brake;
|
||||
angularVelocity += (Torque + T_brake) / Inertia * Time.Delta;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//todo
|
||||
protected (float Mass, float Inertia) CalcMassAndInertia()
|
||||
{
|
||||
// section width in millimeters, measured from sidewall to sidewall
|
||||
// ratio of sidewall height to section width in percent
|
||||
// diameter of the wheel in inches
|
||||
var tire_size = new Vector3( 215, 45, 17 );
|
||||
|
||||
float tire_width = tire_size[0] * 0.001f;
|
||||
float tire_aspect_ratio = tire_size[1] * 0.01f;
|
||||
float tire_radius = tire_size[2] * 0.5f * 0.0254f + tire_width * tire_aspect_ratio;
|
||||
float tire_thickness = 0.02f;
|
||||
float tire_density = 1000; // rubber
|
||||
|
||||
float rim_radius = tire_radius - tire_width * tire_aspect_ratio;
|
||||
float rim_width = tire_width;
|
||||
float rim_thickness = 0.01f;
|
||||
float rim_density = 2700; // aluminium
|
||||
|
||||
float tire_volume = float.Pi * tire_width * tire_thickness * (2 * tire_radius - tire_thickness);
|
||||
float rim_volume = float.Pi * rim_width * rim_thickness * (2 * rim_radius - rim_thickness);
|
||||
float tire_mass = tire_density * tire_volume;
|
||||
float rim_mass = rim_density * rim_volume;
|
||||
float tire_inertia = tire_mass * tire_radius * tire_radius;
|
||||
float rim_inertia = rim_mass * rim_radius * rim_radius;
|
||||
|
||||
float mass = tire_mass + rim_mass;
|
||||
float inertia = tire_inertia + rim_inertia;
|
||||
|
||||
return (mass, inertia);
|
||||
}
|
||||
|
||||
// debug
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
DebugOverlay.Normal( contactPos, forward * forwardFriction.Force / 100f, Color.Red, overlay: true );
|
||||
DebugOverlay.Normal( contactPos, right * sideFriction.Force / 100f, Color.Green, overlay: true );
|
||||
DebugOverlay.Normal( contactPos, forward * forwardFriction.Force / 1000f, Color.Red, overlay: true );
|
||||
DebugOverlay.Normal( contactPos, right * sideFriction.Force / 1000f, Color.Green, overlay: true );
|
||||
DebugOverlay.Normal( contactPos, up * force / 1000f, Color.Blue, overlay: true );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user