This commit is contained in:
2025-05-13 03:19:28 +03:00
parent e2847b060d
commit 762dcf4524
784 changed files with 542617 additions and 2 deletions
+44
View File
@@ -0,0 +1,44 @@
using UnityEngine;
using System.Collections;
public class Brake : MonoBehaviour {
public float _friction = 100;
public float _radius = 0.2f;
public float _area = 0.2f;
public float _max_pressure = 10;
public float _bias = 1;
private float _threshold = 4.0e-4f;
private bool _is_locked;
public Brake() {
}
public Brake (float sliding, float radius, float area, float max_pressure, float bias) {
_friction = sliding;
_radius = radius;
_area = area;
_max_pressure = max_pressure * bias;
_bias = bias;
}
public float GetTorque(float factor, float rotational_speed) {
// `factor' is the fraction of maximum pressure applied.
float pressure = factor * _bias * _max_pressure;
float normal = pressure * _area;
float torque = _friction * normal * _radius;
float velocity = _radius * rotational_speed;
if(velocity < 0.0f)
torque *= -1;
// See if the brake is locked.
if(Mathf.Abs(velocity) < (_threshold * normal)) {
_is_locked = true;
torque = 0.0f;
} else {
_is_locked = false;
}
return torque;
}
}
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c2696fc12f129fb43910ceeaa8bcea37
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+110
View File
@@ -0,0 +1,110 @@
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
[Serializable]
public class IdleThrolleController
{
public double IdleSpeed;
public double SpeedThreshold = 1.0;
public double LagConstant = 1.0;
public double IdleThrolle = 0.0;
public void Integrate(double dt, double speed)
{
var throlleDerivative = ((0.5f*(1.0 - Math.Tanh(4.0*(speed - IdleSpeed)/SpeedThreshold))) - IdleThrolle)/LagConstant;
IdleThrolle += throlleDerivative * dt;
}
}
public class CarEngine : MonoBehaviour {
public Flywheel flywheel;
public int cylinderCount = 4;
public float startRpm = 50;
public double inertia = 0.3f;
public double inputInertia = 0.0;
public double inputTorque = 0.0;
/*public abstract Curve TorqueCurve { get;}
public abstract Curve FrictionCurve { get;}*/
public float Weight = 100;
public Starter starter;
public double inputThrolle = 0.0f;
public double engineThrolle = 0.0f;
public IdleThrolleController idleThrolleController;
public double crankshaftAngle = 0.0; //radians
public double angularSpeed;
public double angularSpeedRPM;
public double k_Friction = 0.000003;
public Curve powerCurve;
public double k_FrictionTorque = 0.75f;
public double kinematicFrictionTorque = 0;
public double Rpm
{
get { return angularSpeed * MathEx.RadSToRpm; }
}
/* private static KeyValuePair<int, float>[] _starterTable = new KeyValuePair<int, float>[]{
new KeyValuePair<int, float>(2, 12.5f),
new KeyValuePair<int, float>(4, 8.0f),
new KeyValuePair<int, float>(6, 6.5f),
new KeyValuePair<int, float>(8, 6.0f),
new KeyValuePair<int, float>(12, 5.5f)
};*/
void FixedUpdate()
{
Integrate(Time.fixedDeltaTime);
}
public void Integrate(double dt) {
//do idle throlle
idleThrolleController.Integrate(dt, Rpm);
engineThrolle = Math.Max(inputThrolle, idleThrolleController.IdleThrolle);
//integrate angular acceleration and speed
var angularDerivative = (GetTorque(engineThrolle, Rpm) - inputTorque) / (inertia + inputInertia);
angularSpeed += angularDerivative * dt;
angularSpeedRPM = MathEx.RadSToRpm * angularSpeed;
//integrate angle
crankshaftAngle += angularSpeed * dt;
kinematicFrictionTorque = (angularSpeedRPM/60.0)*k_FrictionTorque;
}
public double GetTorque(double throlle, double rpm)
{
var t = GetTorque(rpm);
t = t * throlle - k_Friction * rpm * rpm * (1.0 - throlle);
return t;
}
public double GetTorque(double rpm){
if (powerCurve == null)
{
return 0;
}
return powerCurve.Sample((float)rpm);
}
public static float GetWatts(float torque, float rpm){
return torque * rpm / 9549.0f;
}
public static float GetHp(float torque, float rpm){
return GetWatts(torque, rpm) * 1.36f;
}
public bool Started = false;
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 84c8f10b9cc105c4d8c0f3565df12479
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,24 @@
using UnityEngine;
using System.Collections;
public class CarEngineDebug : MonoBehaviour{
public CarEngine engine;
void OnGUI()
{
if (engine != null)
{
GUI.Label(new Rect(0, 10, 200, 30), "Engine RPM: " + engine.Rpm);
}
if (GUI.Button(new Rect(0, 70, 100, 30), "Reset engine"))
{
engine.engineThrolle = 0.0548135214770327;
engine.inputThrolle = 0;
engine.angularSpeed = MathEx.RpmToRadS * 500;
engine.angularSpeedRPM = MathEx.RadSToRpm * engine.angularSpeed;
engine.crankshaftAngle = 0.0;
engine.idleThrolleController.IdleThrolle = 0.0548135214770327;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8a5446a43ae59804f8686e318dd3d03a
timeCreated: 1453922163
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+19
View File
@@ -0,0 +1,19 @@
using UnityEngine;
using System.Collections;
using System;
[Serializable]
public class CarWheelAxis {
public Suspension leftWheel;
public Suspension rightWheel;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 2cdc318b1a3403e40b45e62d9bd84067
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+16
View File
@@ -0,0 +1,16 @@
using UnityEngine;
using System.Collections;
public class Chassis : MonoBehaviour {
public Suspension[] suspension;
public CarWheelAxis[] axisCollection;
public void Integrate(float dt, Car car){
foreach(var s in suspension){
s.Integrate(dt, car);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b0f74529597105e4b93451888357b273
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+61
View File
@@ -0,0 +1,61 @@
using UnityEngine;
using System.Collections;
public class Differential : MonoBehaviour {
public float gearRatio = 3.5f;
public DriveShaft left;
public DriveShaft right;
public DriveShaft center;
// Use this for initialization
void Start () {
/*if (left != null) {
left.target = this;
}
if (right != null) {
right.target = this;
}
if (center != null) {
center.target = this;
}*/
}
// Update is called once per frame
void Update () {
}
/*
void OnDrawGizmos(){
AdvancedGizmo.DrawCircle (transform.position, Vector3.up, 0.2f, Color.green, 12, false, 1);
Gizmos.color = Color.red;
if (left != null) {
Gizmos.DrawLine(left.transform.position, gameObject.transform.position);
}
if (right != null) {
Gizmos.DrawLine(right.transform.position, gameObject.transform.position);
}
if (center != null) {
Gizmos.color = Color.blue;
Gizmos.DrawLine(center.transform.position, gameObject.transform.position);
}
var pt = Tools.WorldToGuiPoint (transform.position);
if (pt.z > 0) {
Tools.BeginGui ();
var textRect = new Rect (pt.x, pt.y, 100, 20);
Tools.DrawRectangle(textRect, new Color(0,0,0,0.7f));
GUI.Label (textRect, "Gear: " + gearRatio);
Tools.EndGui ();
}
}*/
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c7f2e113d7104154882152882f45afdd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+57
View File
@@ -0,0 +1,57 @@
using UnityEngine;
using System.Collections;
public class DriveShaft : MonoBehaviour
{
public Transform endpoint0;
public Transform endpoint1;
float inv_inertia;
public float Inv_inertia {
get {
return inv_inertia;
}
set {
inv_inertia = value;
}
}
float ang_velocity;
float angle;
public float Angle{
get{return angle;}
}
public float AngularVelocity{
get{return ang_velocity;}
set {
ang_velocity = value;
}
}
public DriveShaft() {
inv_inertia = 1;
ang_velocity = 0;
angle = 0;
}
// amount of momentum to reach angular velocity
public float GetMomentum(float angvel)
{
return (angvel - ang_velocity) / inv_inertia;
}
// update angular velocity
public void ApplyMomentum(float momentum)
{
ang_velocity += inv_inertia * momentum;
}
// update angle
public void Integrate(float dt){
angle += ang_velocity * dt;
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bb743870935c8804bb18ddd60eeca428
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e3a5c241b3709404b8e82672a96f1b12
folderAsset: yes
timeCreated: 1453140670
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using System;
using UnityEngine;
using System.Collections;
public class EnginePowerCurve : Curve
{
public float maxPower = 150000; //in Watts
public float rpmAtMaxPower = 8000;
public float k_p1 = 1;
public float k_p2 = 1;
public float k_p3 = 1;
public override float Sample(float arg) {
var o_max = rpmAtMaxPower;
double o_e = arg;
var p = new double[4];
p[0] = 0;
p[1] = maxPower / o_max * k_p1;
p[2] = maxPower / (o_max * o_max) * k_p2;
p[3] = -maxPower / (o_max * o_max * o_max) * k_p3;
double Pe = 0.0;
for (int i = 0; i < 4; ++i) {
Pe += p[i] * Math.Pow(o_e, i);
}
var t = MathEx.Torque(Pe, MathEx.RpmToRadS * (arg + 0.000001));
return (float)t;
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 242e7bc229da7d840be56306cd243fc1
timeCreated: 1454257928
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+26
View File
@@ -0,0 +1,26 @@
using UnityEngine;
using System.Collections;
public class Flywheel : MonoBehaviour
{
public uint teethCount = 129;
public float mass = 6.6f;
public float radius = 0.14f;
public float inertia = 0.06468f;
[ContextMenu("Calculate inertia")]
void CalcInertia()
{
inertia = (mass*radius*radius)*0.5f;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
+12
View File
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d20f72f97cc2b864389b190cb34b1d47
timeCreated: 1454252724
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+15
View File
@@ -0,0 +1,15 @@
using UnityEngine;
using System.Collections;
public enum GearboxControlType{
Manual,
Automatic
}
public abstract class Gearbox : MonoBehaviour {
public abstract GearboxControlType ControlType{ get;}
public abstract float MaxOutputTorque{ get;}
public abstract int GearsCount{ get;}
public abstract float GetGearRatio(int gear);
public abstract int Gear{ get; set;}
}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 47ea6b7730855674fba79ad64b9691a1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,34 @@
using UnityEngine;
using System.Collections;
public class PacejkaParameters : MonoBehaviour {
[ContextMenuItem("Reset coeffs", "resetPacejkaCoeffs")]
public PacejkaCoeffs _coeffs = new PacejkaCoeffs();
[ContextMenuItem("Reset coeffs2", "resetPacejkaCoeffs2")]
public PacejkaCoeffs2 _coeffs2;
public double minLoad = 0;
public double maxLoad = 8900;
private void resetPacejkaCoeffs() {
_coeffs = new PacejkaCoeffs();
}
private void resetPacejkaCoeffs2() {
_coeffs2 = new PacejkaCoeffs2();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb98de270c82d434c8c66084fdbf89af
timeCreated: 1453140700
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
using UnityEngine;
using System.Collections;
public class SimpleSteerController : SteerController {
private float _steer;
public float maxSteerAngle = 30.0f;
public WheelPhysics frontLeftWheel;
public WheelPhysics frontRightWheel;
public WheelPhysics rearLeftWheel;
public WheelPhysics rearRightWheel;
private Vector3 frontLeftStartAngle;
private Vector3 frontRightStartAngle;
public SteeringWheel steeringWheel;
public override float Steer {
get {
return _steer;
}
set {
_steer = Mathf.Clamp(value, -1.0f, 1.0f);
UpdateWheelAngles();
}
}
void UpdateWheelAngles(){
float d = (frontRightWheel.transform.position - frontLeftWheel.transform.position).magnitude;
float l = (frontRightWheel.transform.position - rearRightWheel.transform.position).magnitude;
float innerAngle = maxSteerAngle * _steer;
float outerAngle = OuterWheelAngle (innerAngle, d, l);
if (_steer < 0.0f) {
UpdateWheelSteering (frontLeftWheel, frontLeftStartAngle, innerAngle);
UpdateWheelSteering (frontRightWheel, frontRightStartAngle, outerAngle);
} else {
UpdateWheelSteering (frontRightWheel, frontRightStartAngle, innerAngle);
UpdateWheelSteering (frontLeftWheel, frontLeftStartAngle, outerAngle);
}
steeringWheel.SetAngle(steeringWheel.degrees*0.5f*_steer);
}
void UpdateWheelSteering(WheelPhysics wheel, Vector3 initialAngles, float steerAngle){
initialAngles.y += steerAngle;
wheel.transform.localEulerAngles = initialAngles;
}
public static float OuterWheelAngle(float innerDegree, float d, float l){
float sign = Mathf.Sign (innerDegree);
float rad = Mathf.Deg2Rad * innerDegree * sign;
float ctgB = Mathf.Cos (rad) / Mathf.Sin (rad);
return Arcctg (ctgB + d / l) * Mathf.Rad2Deg * sign;
}
static float Arcctg(float x){
return Mathf.PI / 2.0f - Mathf.Atan (x);
}
// Use this for initialization
void Start () {
frontLeftStartAngle = frontLeftWheel.transform.localEulerAngles;
frontRightStartAngle = frontRightWheel.transform.localEulerAngles;
}
void OnGUI(){
Steer = GUI.HorizontalSlider (new Rect (10, 10, 300, 20), Steer, -1, 1);
}
public float testSteerSpeed = 20.0f;
public float torque = 1000;
void FixedUpdate()
{
float finalTorque = Input.GetKey(KeyCode.W) ? torque : (Input.GetKey(KeyCode.S) ? -torque : 0);
frontLeftWheel.driveTorque = finalTorque;
frontRightWheel.driveTorque = finalTorque;
double breakTorque = 0;
if (Input.GetKey(KeyCode.Space))
{
breakTorque = 1600;
}
frontLeftWheel.brakeTorque = breakTorque;
frontRightWheel.brakeTorque = breakTorque;
rearLeftWheel.brakeTorque = breakTorque;
rearRightWheel.brakeTorque = breakTorque;
}
// Update is called once per frame
void Update () {
float steerDelta = testSteerSpeed * Time.fixedDeltaTime;
if (Input.GetKey (KeyCode.D)) {
Steer += steerDelta;
}
if (Input.GetKey (KeyCode.A)) {
Steer -= steerDelta;
}
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5ff6449b097dd764d99cf320db63ebce
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
@@ -0,0 +1,33 @@
using UnityEngine;
using System.Collections;
public class SimpleSuspension : Suspension {
private Quaternion _startupRotation;
// Use this for initialization
void Start () {
_startupRotation = transform.localRotation;
}
// Update is called once per frame
void Update () {
}
public override void OnSteerChanged()
{
transform.localRotation = _startupRotation * Quaternion.AngleAxis((float)Steer, Vector3.up);
}
void OnDrawGizmos(){
var up = transform.up;
var center = transform.position;
Gizmos.DrawLine(center + up * (float)((1.0 - suspensionTarget) * travelDistance), center - up * (float)(suspensionTarget* travelDistance));
bool front = wheel.name[wheel.name.Length - 2] == 'f';
Vector3 vOffset = front ? Vector3.up : Vector3.up * 0.5f;
Gizmos.DrawLine(dbgWheelForcePos + vOffset, dbgWheelForcePos + dbgWheelForce + vOffset);
Gizmos.DrawLine(dbgWheelForcePos + vOffset, dbgWheelForcePos);
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6d16ee8693777b14b952662a59be4ad7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
@@ -0,0 +1,7 @@
using UnityEngine;
using System.Collections;
public abstract class SteerController : MonoBehaviour {
public abstract float Steer{ get; set; }
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: dd8405aff4937ff4e948598fa5c04f53
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+142
View File
@@ -0,0 +1,142 @@
using UnityEngine;
using System.Collections;
public abstract class Suspension : MonoBehaviour {
private double _steer;
protected double _normalForce;
public WheelPhysics wheel;
public double travelDistance = 0.2f;
public double suspensionTarget = 0.2f;
private double _displacement;
protected double _previousFraction;
private double _overtravel;
private double _lastDisplacement;
public double springForce = 9000;
public double springDamping = 400;
public double Steer{
get{return _steer;}
set
{
_steer = value;
OnSteerChanged();
}
}
public virtual void OnSteerChanged() {
}
// Use this for initialization
void Start () {
}
public double Displacement {
get{return _displacement;}
set{
_lastDisplacement = _displacement;
_displacement = MathEx.Clamp(value, 0, travelDistance);
_overtravel = value - travelDistance;
if(_overtravel < 0){
_overtravel = 0;
}
}
}
public void CalculateForce(double dt){
double velocity = ((_displacement - _lastDisplacement) / travelDistance) / dt;
double spring = (_displacement / travelDistance) * springForce;
double damping = velocity * springDamping;
_normalForce = damping + spring;
}
double computeImpulseDenominator(Vector3 pos, Vector3 normal, Rigidbody rb){
Vector3 r0 = pos - rb.worldCenterOfMass;
Vector3 c0 = Vector3.Cross(r0, normal);
var tensorLocal = Matrix4x4.TRS(rb.worldCenterOfMass, rb.inertiaTensorRotation, rb.inertiaTensor);
var tensorWorld = transform.localToWorldMatrix * tensorLocal;
tensorWorld = Matrix4x4.Inverse(tensorWorld);
Vector3 vec = Vector3.Cross(tensorWorld.MultiplyPoint(c0), r0);
return 1.0f / rb.mass + Vector3.Dot(normal, vec);
}
protected Vector3 dbgWheelForce;
protected Vector3 dbgWheelForcePos;
public void Integrate(double dt, Car car){
if(wheel != null){
//var rb = car.GetRigidBody();
//raycast wheel
wheel.UpdateContactPoint(car.transform.up);
double fraction;
double relativeDisplacement = wheel.TotalWheelRadius - wheel.Contact.hit.distance;
Displacement += relativeDisplacement;
fraction = _displacement / travelDistance;
CalculateForce(dt);
var wheelWorldVelocity = car.GetRigidBody().GetPointVelocity(wheel.transform.position);
if (_overtravel > 0)
{
double correction_factor = 0.0f;
double dv = Vector3.Dot(wheelWorldVelocity, transform.up);// body->getVelocityInLocalPoint(suspension_force_application_point).dot(forcedirection);
dv -= correction_factor * _overtravel / dt;
// double effectiveMass = 1.0 / computeImpulseDenominator(wheel.transform.position, transform.up, car.GetRigidBody());//1.0 / body->computeImpulseDenominator(wheel_position[i], forcedirection);
// double correction = -effectiveMass * dv / dt;
/*if (correction > 0)
{
_normalForce = correction;
}*/
}
if(!wheel.Contact.hasHit) {
_normalForce = 0;
}
if(_normalForce > 0.0f){
car.GetRigidBody().AddForceAtPosition(wheel.Contact.hit.normal * (float)_normalForce, transform.position);
}
wheel.transform.position = GetWheelPosition(fraction);
Vector3 wheelForce;
wheel.Integrate(dt, wheelWorldVelocity, _normalForce, out wheelForce);
car.GetRigidBody().AddForceAtPosition(wheelForce, wheel.transform.position);
dbgWheelForce = wheelForce;
dbgWheelForcePos = wheel.transform.position;
}
}
public void CalculateSuspensionForce(double dt, double fraction) {
_normalForce = springForce * (suspensionTarget - fraction) + springDamping * (_previousFraction - fraction) / dt;
_previousFraction = fraction;
}
public Vector3 GetWheelPosition(double fraction) {
var up = transform.up;
var center = transform.position;
var top = center + up * (float)((1.0 - suspensionTarget) * travelDistance);
var bottom = center - up * (float)(suspensionTarget * travelDistance);
return bottom + (top - bottom) * Mathf.Clamp01((float)fraction);
}
// Update is called once per frame
void Update () {
/* */
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cd4c241e105bc7144a70f4d5e141b4bb
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+150
View File
@@ -0,0 +1,150 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
[ExecuteInEditMode]
public class Tyre : MonoBehaviour {
public double height = 0.15f;
public double k_RollLinear = 0.0038;
public double k_RollQuad = 0.000026;
public double GetRollingResistance(double Fz, double wheelEffectiveRadius, double velocity, double resistanceFactor) {
return Fz * wheelEffectiveRadius * resistanceFactor * (k_RollLinear + k_RollQuad * (velocity )) * Math.Sign(velocity);
}
private double oldLatSlip;
public double latFilter = 0.85f;
public PacejkaParameters pacejkaParams;
/* public void Slip(double patch_speed, Vector3 hub_velocity, out double slipLon, out double slipLat) {
var absLongVelocity = (double)Mathf.Abs(hub_velocity.z);
absLongVelocity = Math.Max(absLongVelocity, dampAbsRoadVelo);
slipLon = CalcSlipRatio(patch_speed, hub_velocity) * 100.0f;
slipLat = (Mathf.Atan2(hub_velocity.x, (float)absLongVelocity)) * Mathf.Rad2Deg;
}
public Double3 FrictionForce(double normalForce, double slipLon, double slipLat, double camber, PacejkaCoeffs coeffs) {
if(normalForce < 1e-4f) { return Double3.zero; }
normalForce = MathEx.Clamp(normalForce, 0, 100000);
//compute Pacejka params
var lateralParams = Pacejka.LateralParams(normalForce, camber, coeffs);
var longitudinalParams = Pacejka.LongitudinalParams(normalForce, coeffs);
var aligningParams = Pacejka.AligningParams(normalForce, camber, coeffs);
//compute friction forces
Double3 result;
result.x = Pacejka.MagicFormula(slipLon, longitudinalParams);
result.y = Pacejka.MagicFormula(slipLat, lateralParams);
result.z = Pacejka.MagicFormula(slipLat, aligningParams);
/* //do combined friction
double d_lon = slipLon / (1.0 + slipLon);
double d_lat = Math.Tan(slipLat) / (1.0 + slipLon);
double d = Math.Sqrt(d_lat * d_lat + d_lon * d_lon);
if(d > 0.001) {
result.x *= (float)Math.Abs(d_lon / d);
result.y *= (float)Math.Abs(d_lat / d);
}*/
// return result;
// }
void OnValidate() {
if(enabled) {
if(transform.parent != null && gameObject.activeInHierarchy)
{
SceneView.RepaintAll();
// transform.parent.gameObject.SendMessage("OnValidate");
}
}
}
// SAE950311 implementation
// private double lastU;
// private double signU = 1;
//private double oldSlip = 0;
// public double tau;
// public double B = 0.091f;//relaxationLengthLong
public double relaxLong = 0.00172688486f;
// private double _integratedSlip;
public double speedThreshold = 3.0f;
public double dampAbsRoadVelo = 1;
public double kBLong = 0.35f;
public double fixedTimeBase = 0.02f;
public double CalcSlipRatio(double patch_speed, Vector3 hub_velocity) {
bool IsOnSurface = true;
if(!IsOnSurface) {
// _integratedSlip = 0;
return 0;
}
/* double deltaTime = Time.fixedDeltaTime;
double dampSRreversal = 0.5f;
double B = relaxLong*100;
double fixedTimeStepScalar = fixedTimeBase / Time.fixedDeltaTime;
double invFixedTimeStepScalar = 1.0f / fixedTimeStepScalar;
if(B < kBLong * invFixedTimeStepScalar)
B = kBLong * invFixedTimeStepScalar;
// damp sliAngle and slipRatio oscilation
double factor = hub_velocity.z * 3.6f * 0.02f; // divided by 50
if(factor < 1)
factor = 1;
B *= factor;
*/
// SAE950311 algorithm
var longVelocity = (double)hub_velocity.z;
// var absLongVelocity = Math.Abs(longVelocity);
if (Math.Abs(longVelocity - patch_speed) > 1.0e-4)
{
double denom = Math.Max(Math.Abs(longVelocity), dampAbsRoadVelo);
return MathEx.Clamp((patch_speed - longVelocity)/denom, -1.0, 1.0);
}
else
{
return 0;
}
/*
absLongVelocity = Math.Max(absLongVelocity, dampAbsRoadVelo);
if((lastU < 0 && longVelocity >= 0) || (lastU >= 0 && longVelocity < 0)) {
_integratedSlip = -dampSRreversal * _integratedSlip;
}
lastU = Math.Sign(longVelocity);
double derivative = (patch_speed - longVelocity) - absLongVelocity * _integratedSlip;
derivative /= B;
// Integrate
_integratedSlip += derivative * deltaTime;
return MathEx.Clamp(_integratedSlip + tau * derivative, -1.5, 1.5);*/
}
}
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 70404261169dc6f498752a2108433b55
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+366
View File
@@ -0,0 +1,366 @@
/*
using UnityEngine;
[ExecuteInEditMode]
public class Wheel : MonoBehaviour {
public Tyre tyre;
public Brake brake;
public DriveShaft _shaft;
public float discRadius = 0.385f; //15 inch by default
private float _totalWheelRadius;
public float TotalWheelRadius {
get {
return _totalWheelRadius;
}
}
public float _slipLon;
public float _slipLat;
private Vector3 _localVelocity;
private Vector3 _friction;
private Vector3 _peakSlip;
public float camber;
public bool DrawDebug = true;
public float weight = 50;
public float driveTorque = 0.0f;
public float brakeTorque = 0.0f;
public float brakingState = 0.0f;
private WheelContact _contact;
public Oscilloscope oscilloscope;
private int t;
public bool Grounded {
get {
return _contact.grounded;
}
}
public WheelContact Contact {
get {
return _contact;
}
}
[ContextMenuItem("Reset coeffs", "resetPacejkaCoeffs")]
public PacejkaCoeffs _coeffs = new PacejkaCoeffs();
public float dtMul = 1;
private void resetPacejkaCoeffs() {
_coeffs = new PacejkaCoeffs();
}
public float AngularVelocity {
get { return _shaft.AngularVelocity; }
set {
_shaft.AngularVelocity = value;
}
}
// Use this for initialization
void Start () {
InitWheel();
float normalForce = 3.210758f * 1000.0f;
var lateralParams = Pacejka.LateralParams(normalForce, 0, _coeffs);
var longitudinalParams = Pacejka.LongitudinalParams(normalForce, _coeffs);
var aligningParams = Pacejka.AligningParams(normalForce, 0, _coeffs);
_peakSlip.x = Pacejka.Extremum(longitudinalParams);
_peakSlip.y = Pacejka.Extremum(lateralParams);
_peakSlip.z = Pacejka.Extremum(aligningParams);
if(oscilloscope != null)
{
oscilloscope.channels[0].sampler = Sampler0;
oscilloscope.channels[1].sampler = Sampler1;
oscilloscope.channels[2].sampler = Sampler2;
}
}
float Sampler0()
{
return _slipLat;
}
float Sampler1()
{
return localVelocity.x;
}
float Sampler2()
{
return 0;//Mathf.Repeat(_shaft.Angle, Mathf.PI * 2);
}
float Inertia{
get{return 1.0f / _shaft.Inv_inertia;}
set{_shaft.Inv_inertia = 1.0f / value;}
}
void InitWheel() {
if (_shaft == null) {
_shaft = new DriveShaft();
}
if (tyre != null) {
_totalWheelRadius = discRadius + tyre.height;
}
Inertia = weight*_totalWheelRadius*_totalWheelRadius*0.5f;
}
void OnValidate() {
InitWheel();
}
public void SetBraking(float amount) {
brakingState = Mathf.Clamp01(amount);
}
void SetTorque(float torque, float dt) {
_shaft.ApplyMomentum(torque * dt);
}
void OnDrawGizmosSelected() {
if (!DrawDebug) {
return;
}
var p = _coeffs;
Curve[] curves = new Curve[]{new Curve(), new Curve(), new Curve()};
int sampleCnt = 250;
float minArg = -100;
float maxArg = 100;
float argDiff = maxArg - minArg;
for (int i = 0; i < sampleCnt; ++i) {
var arg = minArg + argDiff / ((float)sampleCnt - 1) * i;
curves[0].AddArg(arg);
curves[1].AddArg(arg);
curves[2].AddArg(arg);
curves[0].AddVal(Pacejka.LongitudinalForce (5000.0f, arg, p));
curves[1].AddVal(Pacejka.LateralForce (5000.0f, camber, arg, p));
curves[2].AddVal(Pacejka.AligningForce (5000.0f, camber, arg, p));
}
float maxSlip = MathEx.FindExtremumValue(x => Pacejka.LongitudinalForce(5000.0f, x, p), 0, 100, 0.5f);
Tools.BeginGui ();
GUI.Label(new Rect(20,30,300,100), "Maximum wheel slip: " + maxSlip + " Value at that slip: " + Pacejka.LongitudinalForce(5000.0f, maxSlip, p));
Plot2D plot = new Plot2D(curves[0]);
plot.Title = "Longitudinal friction";
plot.OnGUI(new Rect(90,100,500,400));
camber = GUI.HorizontalSlider (new Rect (10, 10, 100, 10), camber, -10, 10);
Tools.EndGui ();
}
float GetTorque(float new_angvel, float dt) {
return _shaft.GetMomentum(new_angvel) / dt;
}
private float roll_resistance_lin = 0.00001f;
private float roll_resistance_quad = 0.00001f;
float getRollingResistance(
float velocity,
float resistance_factor)
{
// surface influence on rolling resistance
float rolling_resistance = resistance_factor * roll_resistance_lin;
// heat due to tire deformation increases rolling resistance
// approximate by quadratic function
rolling_resistance += velocity * velocity * roll_resistance_quad;
// rolling resistance direction
float resistance = -rolling_resistance;
if (velocity < 0) resistance = -resistance;
return resistance;
}
public void UpdateContactPoint(Vector3 up){
var raycastRay = new Ray (transform.position, -up);
_contact.hasHit = false;
_contact.grounded = false;
if (Physics.Raycast (raycastRay, out _contact.hit)) {
_contact.hasHit = true;
if(_contact.hit.distance <= _totalWheelRadius){
_contact.grounded = true;
}
}
}
int pp = 0;
private Vector3 lonForce;
private Vector3 latForce;
private float wheel_torque;
public float dampOsc = 0.85f;
public float fixedTimeBase = 0.02f;
private Vector3 localVelocity;
public void Integrate(float dt, Vector3 wheelWorldVelocity, float normal_force, out Vector3 force)
{
//normal_force = 5000.0f;
float wheel_drive_torque = driveTorque;
float patch_speed = AngularVelocity * _totalWheelRadius;
localVelocity = transform.InverseTransformDirection(wheelWorldVelocity);
tyre.Slip(patch_speed, localVelocity, out _slipLon, out _slipLat);
Vector3 friction_force = tyre.FrictionForce(normal_force, _slipLon, _slipLat, 0, _coeffs);
lonForce = transform.forward * friction_force[0];
latForce = -transform.right * friction_force[1];
Vector3 tire_force = lonForce + latForce;
float tire_friction_torque = friction_force[0] * _totalWheelRadius;
//calculate brake torque
float wheel_lock_torque = -AngularVelocity / dt * Inertia;
float wheel_brake_torque = wheel_lock_torque - wheel_drive_torque + tire_friction_torque;
float maxBreakTorque = brakeTorque;
if (wheel_brake_torque > 0 && wheel_brake_torque > maxBreakTorque)
{
wheel_brake_torque = maxBreakTorque;
}
else if (wheel_brake_torque < 0 && wheel_brake_torque < -maxBreakTorque)
{
wheel_brake_torque = -maxBreakTorque;
}
//limit the reaction torque to the applied drive and braking torque
float reaction_torque = tire_friction_torque;
float applied_torque = wheel_drive_torque + wheel_brake_torque;
if ( ( applied_torque > 0 && reaction_torque > applied_torque ) ||
( applied_torque < 0 && reaction_torque < applied_torque ) )
reaction_torque = applied_torque;
Vector3 tire_torque = Vector3.right * reaction_torque;// - direction::up * friction_force[2];
//set wheel torque due to tire rolling resistance
float rollResistanceCoefficient = 0;
float rolling_resistance = getRollingResistance(AngularVelocity, rollResistanceCoefficient);
float rolling_resistance_torque = rolling_resistance * _totalWheelRadius - tire_friction_torque;
wheel_torque = wheel_drive_torque + wheel_brake_torque + rolling_resistance_torque;
//have the wheels internally apply forces, or just forcibly set the wheel speed if the brakes are locked
float oldAngularVelocity = AngularVelocity;
SetTorque(wheel_torque, dt);
/ *float fixedTimeStepScalar = fixedTimeBase / Time.fixedDeltaTime;
float invFixedTimeStepScalar = 1.0f / fixedTimeStepScalar;
// damp low speed wheel oscillations
float deltaAngularVelocity = AngularVelocity - oldAngularVelocity;
float delta = deltaAngularVelocity * dampOsc * Mathf.Clamp01(invFixedTimeStepScalar);
AngularVelocity -= delta;* /
_shaft.Integrate(dt);
//viscous tire contact drag (hack)
float rollingDrag = 0.0f;
Vector3 wheel_drag = _localVelocity * rollingDrag;
force = Vector3.zero;
//apply forces to body
// Vector3 tire_pos = wheel_position[i] - body->getCenterOfMassPosition();
Vector3 world_tire_force = tire_force;
//Vector3 world_tire_torque = quatRotate ( wheel_orientation[i], tire_torque);
world_tire_force += wheel_drag;
//world_tire_torque += tire_pos.cross(world_tire_force);
force = world_tire_force;
//torque = torque + world_tire_torque;
}
float RPM
{
get {
return _shaft.AngularVelocity * 30.0f / 3.1415926535f;
}
}
public DriveShaft Shaft {
get {
return _shaft;
}
}
void OnGUI()
{
DrawLabels();
}
void DrawLabels() {
Label3D.AtWorldPosition(transform.position + Vector3.up * 1, "RPM: " + RPM);
Label3D.AtWorldPosition(transform.position + Vector3.up * 1.3f, "F: " + _friction.x + " S: " + _slipLon);
}
void OnDrawGizmos() {
if (tyre == null || _shaft == null) {
return;
}
DrawLabels();
var forward = transform.forward;
var right = transform.right;
//draw wheel radius
var wheelCenter = transform.position;
var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(_shaft.Angle * Mathf.Rad2Deg, right), Vector3.one);
var radiusP0 = -forward * _totalWheelRadius;
var radiusP1 = forward * _totalWheelRadius * 1.2f;
radiusP0 = rollMatrix.MultiplyPoint3x4 (radiusP0);
radiusP1 = rollMatrix.MultiplyPoint3x4 (radiusP1);
Gizmos.DrawLine (wheelCenter + radiusP0, wheelCenter + radiusP1);
//draw wheel outer circle
AdvancedGizmo.DrawCircle(wheelCenter, right, _totalWheelRadius, Color.green, 36);
//draw wheel inner circle
AdvancedGizmo.DrawCircle(wheelCenter, right, discRadius, Color.green, 36);
//draw forces
Gizmos.color = Color.red;
Gizmos.DrawLine(wheelCenter, wheelCenter + lonForce);
Gizmos.color = Color.green;
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce);
Gizmos.color = Color.blue;
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce + lonForce);
Gizmos.color = Color.black;
Gizmos.DrawLine(wheelCenter, wheelCenter + forward * 100);
}
}
*/
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a0c7faf1940de454e9a95ef9e629f173
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
+169
View File
@@ -0,0 +1,169 @@
using UnityEngine;
using System.Collections;
using System.Net.Security;
using System;
public struct WheelContact {
public RaycastHit hit;
public bool hasHit;
public bool grounded;
}
public class WheelPhysics : MonoBehaviour {
public Tyre tyre;
public Brake brake;
public double driveTorque = 0.0f;
public double brakeTorque = 0.0f;
public double angularVelocity; //rad/sec
public double inertiaMomentum = 3.45f;
public double angle; //radian
private WheelContact _contact;
public bool Grounded {
get {
return _contact.grounded;
}
}
public double discRadius = 0.385f; //15 inch by default
public double TotalWheelRadius {
get {
return discRadius + tyre.height;
}
}
public WheelContact Contact {
get {
return _contact;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
}
private double _slipLon;
private double _slipLat;
private Vector3 localVelocity;
private int t = 0;
public double reactionTorque;
public void Integrate(double dt, Vector3 wheelWorldVelocity, double normal_force, out Vector3 force) {
t++;
localVelocity = transform.InverseTransformDirection(wheelWorldVelocity);
double patchSpeed = angularVelocity * TotalWheelRadius;
// tyre.Slip(patchSpeed, localVelocity, out _slipLon, out _slipLat);
var Vc = new Double3(localVelocity.z, localVelocity.x, localVelocity.y);
Double3 frictionForces = Pacejka.CalcPacejka(Vc, angularVelocity, TotalWheelRadius, TotalWheelRadius, normal_force, 0, tyre.pacejkaParams._coeffs2, true);
frictionForces.y *= -1.0;
// Double3 frictionForces = tyre.FrictionForce(normal_force, _slipLon, _slipLat, 0, tyre.pacejkaParams._coeffs);
/* if (name[name.Length - 2] == 'f' && t % 250 == 0) {
Debug.LogFormat("Fz = {0} Fx = {1} Slip = {2} Vlon = {4} Name = {3}", normal_force, frictionForces.x, _slipLon, name, localVelocity.z);
}*/
double T_engine = driveTorque;
double T_break = brakeTorque * -Math.Sign(angularVelocity);
double T_input = T_engine + T_break;
double T_reaction = frictionForces.x * TotalWheelRadius;
reactionTorque = T_reaction;
/*if (T_input < 0 && (T_reaction < T_input) || (T_input > 0 && (T_reaction > T_input)))
{
T_reaction = T_input;
}*/
double T_rolling_resist = tyre.GetRollingResistance(normal_force, TotalWheelRadius, localVelocity.z, 1.0f);
double angularAcceleration = (T_engine - T_reaction + T_break - T_rolling_resist) /inertiaMomentum;
angularVelocity += angularAcceleration * dt;
//dw/dt = (T_engine + T_reaction - T_break - T_friction) / WheelInertia
angle += angularVelocity * dt;
Vector3 lonForce = transform.forward * (float)frictionForces.x;
Vector3 latForce = -transform.right * (float)frictionForces.y;
force = lonForce + latForce;
}
public void UpdateContactPoint(Vector3 up) {
var raycastRay = new Ray(transform.position, -up);
_contact.hasHit = false;
_contact.grounded = false;
if (Physics.Raycast(raycastRay, out _contact.hit)) {
_contact.hasHit = true;
if (_contact.hit.distance <= TotalWheelRadius) {
_contact.grounded = true;
}
}
}
void OnDrawGizmos() {
if (tyre == null) {
return;
}
//DrawLabels();
var forward = transform.forward;
var right = transform.right;
//draw wheel radius
var wheelCenter = transform.position;
//var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(_shaft.Angle * Mathf.Rad2Deg, right), Vector3.one);
var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(0, right), Vector3.one);
var radiusP0 = -forward * (float)TotalWheelRadius;
var radiusP1 = forward * (float)(TotalWheelRadius * 1.2);
radiusP0 = rollMatrix.MultiplyPoint3x4(radiusP0);
radiusP1 = rollMatrix.MultiplyPoint3x4(radiusP1);
Gizmos.DrawLine(wheelCenter + radiusP0, wheelCenter + radiusP1);
//draw wheel outer circle
AdvancedGizmo.DrawCircle(wheelCenter, right, (float)TotalWheelRadius, Color.green, 36);
//draw wheel inner circle
AdvancedGizmo.DrawCircle(wheelCenter, right, (float)discRadius, Color.green, 36);
//draw forces
/*Gizmos.color = Color.red;
Gizmos.DrawLine(wheelCenter, wheelCenter + lonForce);
Gizmos.color = Color.green;
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce);
Gizmos.color = Color.blue;
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce + lonForce);
Gizmos.color = Color.black;
Gizmos.DrawLine(wheelCenter, wheelCenter + forward * 100);*/
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f0f3b45c60aa04c4c9e0c48be7e96fa3
timeCreated: 1453062224
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: