Programming a driving simulator, how hard can it be?

  • Thread starter eran0004
  • 50 comments
  • 21,705 views
10,943
Sweden
Sweden
eran0004
complex.jpg

(Pretty hard, actually...)


Programming a driving simulator


The goal of this project is to accurately model the components of a car and stitch them together into a driving simulation where the car performs in a realistic manner.

The car I have selected is a Peugeot 106 1.6 Rallye and the scripting language I'm using is Python. The simulation is intended to be run in the Blender Game Engine. As a guide to the world of car game physics, I'm using this excellent resource.

To keep the thread structured, each component will have a post of its own and this post will contain links to those.

Feel free to ask questions, discuss the physics or provide feedback. If you spot anything that doesn't look right I would be happy to know :)

Components
(more components will be added later on...)


 
Last edited:
How hard it can be depends on how good you are at programming and coding and stuff.
 
Engine

In my imagination, a car begins with its engine. And that is why it's the component that I'm going to start with.


Torque

The first thing I'm doing is to map the torque curve. I found some pretty good reference data here: http://106owners.co.uk/forums/showt...s-XS-16v-JP4&p=2401882&viewfull=1#post2401882

Based on the chart, I create two lists: One containing the torque data and one containing the rpm data:

Code:
L_RPM = [0, 500, 1000, 1800, 2100, 2200, 2900, 3100, 3400, 4200, 4600, 5500, 6100, 7100, 7500, 8200, 8500]

L_wotTorque = [0.0, 22.5, 45.0, 84.2, 103.0, 107.0, 115.4, 115.8, 117.8, 118.5, 126.0, 129.1, 124.0, 103.0, 90.0, 0.0, 0.0]

The "wot" in "wotTorque" means "wide open throttle", i.e. it's the torque output when the engine is operating at full throttle. What we have now is a 2-dimensional map of the torque: RPM * wotTorque. We need a third dimension though: RPM * wotTorque * Throttle, because an engine isn't always operating at full throttle. We could assume that the torque would simply scale with the throttle, that if the throttle is at 50%, then the torque is at 50% as well. However, that's not how it works in real life:


throttle_map.png

^ This is a chart showing the torque curves of 2 different engines. The % is the throttle, where 100% would be WOT (wide open throttle). What the chart shows is that the high rpm range of the curves drops more than the low rpm range when the throttle closes, making the torque peak shift towards the low rpm range.

So, how do we replicate that? Do we have to make a torque curve for each individual throttle value? (100%, 99%, 98%...) Well, that would be an impossible task. A better way would be to apply some sort of formula to the torque curve so that it behaves like the curves above on its own.

The solution I came up with was to add the torque curve for the engine when it's running on idle throttle (idleTorque), and then have the torque curve interpolate between wotTorque and idleTorque, depending on the throttle value. To make the high rpm range drop faster then the low rpm range, I also let the torque curve drop in an exponential function rather than a linear, and the value of the exponent is in turn a function of the rpm and the throttle value:

Code:
torque = idleTorque + (Accelerator**(RPM/(RevLimit*Throttle)))*(wotTorque - idleTorque)

(** is the Python function for ^, so 10**2 = 10*10 = 100.)

This resulted in the following graph:

torquecurve3.jpg

The tallest curve is the WOT curve and the smallest curve is the idle curve. The curves in between shows the torque curves for different throttle values. Note that the torque is negative towards the high rpm range, this would replicate engine braking. The real life engine braking value is unknown to me, so just to have some kind of reference I performed an engine braking test with the Peugeot 106 Rallye in GT6 and found that between 7000 - 5000 rpm it produced an average of -6 BHP of engine braking. Realistic or not? I don't know, but it's the best I've got for now.

So what we have here is a torque curve where wotTorque is based on actual real life data, where idleTorque is based on semi-realistic data and where the patterns of the curves in between resembles the Optimum G chart above.

Fuel consumption

This is rather simple, in theory. All we need to do is to calculate how much fuel goes into the cylinders per cycle and subtract that value from the fuel tank.

What we need to know then is:

  • Number of cylinders and cylinder volume
  • How many revolutions does the engine do per cycle?
  • How much of the cylinder volume is fuel and how much is air
So to enter that data:

  • It's a 4-cylinder engine, the total volume of the cylinders is 1587cc
  • It's a 4-stroke engine, which means that there are two revolutions of the crankshaft per cylinder cycle (one cycle have four strokes: 1. downstroke intake, 2. upstroke compress, 3. downstroke ignite, 4. upstroke exhaust)
  • On the internet I found an air-to-fuel ratio of 14.7 for this engine. However, that ratio is measured in terms of mass rather than volume. To convert from mass to volume we need the density of air and fuel. To make a long story short, for every liter of air there would be 0.000113 liters of fuel. Online sources also mentioned a volumetric efficiency of 0.9 for naturally aspirated engines (since air is being sucked in, it's got slightly lower pressure than the surrounding atmosphere).
Now we can do the calculation. First the calculation of how much fuel goes into the cylinders each cycle:

Code:
CylLoad = cc * VoluEff * AFRv * Throttle

cc = displacement in cc = 1587
VoluEff = volumetric efficiency = 0.9
AFRv = air to fuel ratio (volume) = 0.000113
Throttle = a value from 1.0 to 0.0 where 0.0 is closed and 1.0 is wide open.

To get the fuel consumption per revolution we take the CylLoad value and divides it by number of crankshaft revolutions per cycle:

Code:
FPR = CylLoad / 2

And to convert that to liters per minute we take the fuel per revolution multiplied by the revolutions per minute. Since that gives us a value in cc we need to divide it by 1000 to get the value in liters:

Code:
FLPM = FPR * RPM / 1000

Those are the basics of the engine script. The full script contains more details, such as the spark plugs only firing if the ignition is turned on and the RPM is greater than a minimum value and smaller than the rev limit. It also sets the idle throttle to 0.18 and provides a basic conversion from accelerator input to throttle value (i.e. 1 accelerator = 1 throttle, 0 accelerator = 0.18 throttle, and anything on between).

Code:
import bisect

##----- DRIVER INPUTS -----##

#Ignition
IgnKey = 1

#Accelerator
aMin = 0.0
aMax = 1.0
aInc = 0.1
Accelerator = 0.5

##----- CAR SPECS -----##

#Throttle
tMin = 0.18 #Throttle bypass, adjust for engine idle speed
tMax = 1.0
tRange = tMax - tMin
Throttle = tRange * Accelerator + tMin

print('Throttle: %.2f' % Throttle)

#Engine data

Cylinders = 4
cc = 1587
stroke = 4

L_RPM = [0, 500, 1000, 1800, 2100, 2200, 2900, 3100, 3400, 4200, 4600, 5500, 6100, 7100, 7500, 8200, 8500]
L_wotTorque = [0.0, 22.5, 45.0, 84.2, 103.0, 107.0, 115.4, 115.8, 117.8, 118.5, 126.0, 129.1, 124.0, 103.0, 90.0, 0.0, 0.0]
L_idleTorque = [0.0, 7.5, -0.1, -0.2, -0.3, -0.3, -1.1, -1.4, -1.8, -3.0, -3.6, -5.2, -6.4, -8.8, -9.9, -12.8, -13.5]
L_nosparkTorque = [0.0, -1.0, -1.1, -1.2, -1.3, -1.3, -2.1, -2.4, -2.8, -4.0, -4.6, -6.2, -7.4, -9.8, -10.9, -13.8, -14.5]

RevLimit = 7250
RevCutOff = 700

##----- PHYSICS -----##

#Engine RPM

SomeFunction = 4000

RPM = SomeFunction #get this value from the wheel rotation

#Ignition and spark plugs

if IgnKey == 1 and RevCutOff < RPM < RevLimit : Spark = 1
else : Spark = 0

#Fuel
Fuel = 80
AFR = 14.7
AFRv = 0.000113
VoluEff = 0.9

CylLoad = cc * VoluEff * AFRv * Throttle

FPR = CylLoad / 2

#Liters per minute
FLPM = FPR * RPM / 1000

print('Fuel consumption per minute (L): %.3f' % FLPM )

#Interpolating torque curve
g = RPM

i2 = bisect.bisect(L_RPM, RPM)

i1 = i2 - 1

g1 = L_RPM[i1]
g2 = L_RPM[i2]

G = (g-g1) / (g2-g1)

if Spark == 1:

    d1 = L_wotTorque[i1]
    d1_2 = L_idleTorque[i1]

    d2 = L_wotTorque[i2]
    d2_2 = L_idleTorque[i2]

    wotTorque = d1 + G * (d2-d1)
    idleTorque = d1_2 + G * (d2_2-d1_2)
    torque = idleTorque + (Accelerator**(RPM/(RevLimit*Throttle)))*(wotTorque - idleTorque)

else:

    d1_3 = L_nosparkTorque[i1]
    d2_3 = L_nosparkTorque[i2]

    torque = d1_3 + G * (d2_3-d1_3)

BHP = torque * RPM / 7120.91

print('Engine speed RPM: %.0f' % RPM)
print('Torque Nm: %.2f' % torque)
print('Power BHP %.1f' % BHP)


Return to OP
 
Last edited:
Flywheel and engine RPM

We have a pretty good model for engine torque, but the question now is how to determine the RPM. Currently it's vaguely described by the placeholder "SomeFunction".

According to this resource it can be determined by calculating the rotational speed of the driving wheels and then run that number through the gearbox to get the rotational speed of the crankshaft.

However, that would only work for as long as the clutch is engaged, and it also means that I can't test the engine properly until I have modelled the rest of the drivetrain. So what I'm going to do instead is to add a flywheel, because the crankshaft rotates at the same speed as the flywheel.

So how does the flywheel obtain its speed? It's a product of acceleration from the forces of the engine, the starter engine and (if the cluth is engaged) the transmission. Usually when we talk about cars, acceleration means increasing the speed while we use decceleration for slowing down. However, for physics purposes they're all one and the same: decceleration is simply acceleration in the direction opposite of travel. (Cornering is also a form of acceleration, but we can save that one for later).

Total force of flywheel acceleration = A + B + C

Where A is the engine torque
Where B is the starter engine torque
Where C is the gearbox torque

The engine (A) is always connected to the flywheel, but the starter engine (B) is only connected when starting the engine, and the transmission (C) is only connected when the clutch is engaged. So basically we can just leave out B and C when they're not relevant.

It's worth to note that any of the values A, B and C may be negative values, i.e. a force in the opposite direction. So if A = 100 Nm and C = -50 Nm, the net torque accelerating the flywheel would be 100 + -50 = 50.

So far, so good. But since acceleration = force / mass we'd end up with infinite acceleration if we don't give the flywheel some mass, or inertia.

The formula for calculating inertia of a solid cylinder (this is a simplification, a real flywheel is neither 100% solid nor a perfect cylinder) is:

Inertia = 0.5 * mass * radius^2

Where mass is in kg and radius in meters.

Unfortunately, flywheel data is hard to come across. What I've found is this, mentioning a mass of the flywheel of 5.5 kg. The radius isn't mentioned, but the clutch appears to be 200 mm in diameter. I guess I'll have to make a guesstimate and say that the flywheel is 220 mm in diameter. Since the entire calculation is an approximation, having the wrong size of the flywheel isn't going to be the end of the world. Let's just say "realistic enough" and be done with it.

Mass: 5.5 kg
Radius: 0.11 m (= half of 220 mm)
Inertia: 0.033275

Flywheel acceleration for a given net torque of 100 Nm would then be:

100 / 0.033275 = 3005.259 rads per second squared. Multiply by 60 to get rads per minute per second, divide by 2*pi (one rad = a full ciricle divided by 2*pi) and we get an acceleration of 28698 RPM per second.

Code:
flywheelAcc = netTorque / Inertia *60 / (2*pi)

Apply the acceleration to the flywheel divided by the physics framerate (1 logic tic per frame):

Code:
RPM +=flywheelAcc/fps

However, the crankshaft (and pistons) itself do also have a mass. This inertia is harder to estimate, and the best solution is probably to assign a placeholder value to it and then finetune it when we run the simulation and can see how realistic it appears to be.

Crankshaft inertia: 0.01 (placeholder)

The combined inertia of crankshaft and flywheel then is 0.043.

All of this in a Python script would be:

Code:
pi = 3.1415927
fps = 60
RPM = 1800 #needs to be updated every frame

#Inertia
crankInertia = 0.01

flywheelMass = 5.5
flywheelRadius = 0.11
flyInertia = 0.5*flywheelMass*(flywheelRadius**2)

engInertia = crankInertia + flyInertia

#Inputs

torque = 45 #collected from engine
starterTorque = 0 #collected from starter engine
gearTorque = 0 #collected from transmission

A = torque
B = starterTorque
C = gearTorque

#Net torque

netTorque = A + B + C

flywheelAcc = netTorque / engInertia * 60 /(2*pi)

RPM +=flywheelAcc/fps

Return to OP
 
Last edited:
Momentum and clutch, part 1

Update: A new friction clutch script is now available here.

Momentum and inertia have been a bit of a headache for me. There are forces from the crankshaft acting on the transmission, and forces from the transmission acting on the crankshaft. The influence they have on each other is controlled by the clutch. Basically it's something like this:

clutch
x
crankshaft <-- --> transmission
After studying endless articles on physics on the internet I think I may be on my way to finding a solution. Basically, if we pretend like there's no friction or heat losses in the system (please, let's dont), and 0 torque added to the system (engine is operating at exactly 0 torque and the car is suspended from the ground so the wheels are spinning in the air) then the momentum of the system remains unchanged.

So when the clutch is disengaged, what we have here is a system of two rotating bodies (crankshaft and transmission), each with their own momentum. What I'm going do to is that when I engage the clutch, these bodies are going to collide, and this is what happens:

crankshaft momentum + transmission momentum
____________________
(divided by)
crankshaft inertia + transmission inertia

Adding values to these data is probably going to make more sense.

Crankshaft momentum

Angular momentum (momentum for a rotating object) is defined as:

L (momentum) = I (inertia) * ω (angular velocity)

In the flywheel post I arrived at a combined inertia for the crankshaft and flywheel of 0.043

Let's say that the crankshaft is revolving at 4000 rpm, which gives an angular velocity of 4000 / 60 *2pi = 418.879 rads/second.

L = 0.043 * 418.879 = 18.0118

The momentum of the crankshaft then, is 18.0118

Transmission momentum

If we define the transmission as the entire drivetrain from the clutch to the wheels, then we'd have inertia from the individual gears, the driveshafts and the wheels. To simplify things I'm just going to calculate the inertia of the wheels for now; inertia values for the gearbox and the driveshafts can always be added later on.

A Peugeot 106 Rallye steel wheel has a mass of 5-7.2 kg according to an online source. I'm going to go with 6.1 kg, since that's right in between of those values. There's also a tyre, and the same source states that the total mass of wheel and tyre is 11-12 kg, so aiming right in between of that would be 11.5 kg, which gives the tyre a mass of 11.5 - 6.1 = 5.4 kg.

So we have the data for the mass of the wheel and the tyre. But what about the inertia? When we calculated the flywheel inertia we used a formula for a solid cylinder, where the mass is equally distributed. A wheel, however, is not solid. Instead it has most of its mass near the edges. If we look at the tyre, it has no mass at all in the middle, in fact all of its mass is distributed outside of the wheel. To calculate the inertia, we need to pin-point the center of mass, for a cross section of the wheel:


wheelcom.png

The picture above shows a cross section of the wheel (blue) and tyre (black) as seen from above. The dashed black line is the rotational axis of the wheel. For the entire wheel the center of mass is (or should be) obviously in the center of the wheel, but if we look at half the wheel (the "radius", don't know if that's the proper term here) we get a center of mass that's further out. Just how far is hard for me to say, so I'm just going to define a point and hope that it's fairly realistic. The point I've chosen is the center of the length of the tyre wall, so outside of the wheel and halfway through the tyre.

For the next step I think I know what I'm doing, but I'm not 100% sure of how this works, so don't quote me on this:

The calulation of the inertia is (mass * radius^2), multiplied by a form factor, i.e. how close to the edge the center of gravity is for the cross section. For the perfect (well... perfect enough) cylinder of the flywheel, that form factor was 0.5, because the center of mass was halfway between the edge and the rotational axis (0.5 = 50%). The form factor for the wheel would then be:

(wheel radius + (tyrewall length/2)) / (wheel radius + tyrewall length).

The wheel radius is 0.1775 meter, the tyrewall length is 0.105 meters.

(0.1775 + (0.105/2)) / (0.1775 + 0.105) = 0.230 / 0.2825 = 0.814159, or roughly 81.4%

The inertia calculation then is: 0.814 * 11.5 * 0.2825^2 = 0.747066. Let's round it off to 0.747 and multiply this by the number of wheels connected to the drive train (2) and we get an inertia of 1.494.

Since we're excluding the gears and the drive shafts (for now), this is the inertia of the transmission.

Before we can calculate the momentum, we need an angular velocity. Let's say that the wheels are at 0 angular velocity, i.e. stationary (but still suspended in the air).

This gives a transmission momentum of: 1.494 * 0 = 0.

Let's engage the clutch and see what happens

Here is what happens when the crankshaft "collides" with the transmission:

crankshaft momentum + transmission momentum
____________________
(divided by)
crankshaft inertia + transmission inertia

Using the values we have:

18.0118 + 0
___________________ = 11.7188

0.043 + 1.494​

Let's round off the answer to 11.7. So, what does this answer mean? 11.7 what? Well, the output of the formula is the angular velocity. We let the crankshaft rotating at 418.879 rads/s collide with the transmission rotating at 0 rads/s, and as a result of that, they're now both rotating at 11.7 rads/s.

So far so good, but what happens when the clutch is partially engaged? Well, stay tuned...

Return to OP
 
Last edited:
Momentum and clutch, part 2

Update: A new friction clutch script is now available here.

Okay, so the clutch is a vital part of cars. When you start from 0, the clutch needs to slip so that the engine can keep the revs up during that initial acceleration.

In the formula we ended with in part 1, the clutch is always engaged. So how do we make the clutch dynamic?

Let's start by defining the clutch: The clutch can be disengaged, engaged and partially engaged. Disengaged can be represented by the value 0, engaged by the value 1 and any value in between would be partially engaged (half clutch = 0.5, quarter clutch = 0.25, etc.).

The clever bit is, that because these are values from 1 to 0, we can use them as multiplyers in the clutch formula, to calculate how much of the momentum that is transferred between the rotating bodies. Because the two bodies are not locked together when the clutch is less than 1, we calculate both of their velocities.

For the crankshaft, the calculation would be this:

Code:
ωc =(Lt+Lc)/(It+Ic)*Clutch+uc*(1-Clutch)

And for the transmission:

Code:
ωt =(Lt+Lc)/(It+Ic)*Clutch+ut*(1-Clutch)

ωc = crankshaft angular velocity
ωt = transmission angular velocity

Lc = crankshaft momentum
Lt = transmission momentum

Ic = crankshaft inertia
It = transmission inertia

uc = crankshaft initial angular velocity
ut = transmission initial angular velocity

Let's provide values for these:

Lc = 18.0118
Lt = 0
Ic = 0.043
It = 1.494
uc = 418.879 (4000 rpm)
ut = 0
clutch = 0.5

Code:
ωc =(0+18.0118)/(1.494+0.043)*0.5+418.879*(1-0.5)

ωt =(0+18.0118)/(1.494+0.043)*0.5+0*(1-0.5)

This gives:

ωc = 52.43482 rad/s

ωt = 10.54692 rad/s

Since angular momentum is L = I * ω and since no momentum should be lost or added in our calculation, we can control that the calculation is correct by multiplying the angular velocities of the two bodies with their inertias:

crankshaft: 0.043 * 52.43482 = 2.2547
transmission: 1.494 * 10.54692 = 15.7571

Let's add the results: 2.2547 + 15.7571 = 18.0118.

The momentum we started with was 18.0118 (Lc) and 0 (Lt) and the momentum we end with is 18.0118 (Lc + Lt). All momentum is kept, none is added.

All's well that ends well! :)

Return to OP
 
Last edited:
The starter motor and its logic bricks

A starter motor has now been added, which means that the engine can be fired up in the simulator.

The starter motor is based on logic bricks and contains the following properties:

isRunning (true indicates engine is running, false indicates engine is not running)
IgnKey (true indicates ignition is switched on, false indicates ignition is switched off)
starter (true indicates the starter motor is engaged, false indicates the starter motor is disengaged)

This is how they work:

isRunning evaluates the engine RPM and the ignition. If the RPM is greater than the engine cutoff threshold (below which the engine is programmed to cut off) and the ignition is on, then the engine is running. A fuel condition should be added here as well.

IgnKey evaluates keyboard inputs and the state of the isRunning property. If isRunning == false and the ignition key (I) is pressed, then ingition will be switched on for as long as the button is pressed. If the key is released while isRunning == false, then ignition is switched off. If the key is pressed while isRunning == false and released while is Running = true, then ignition is left unchanged (on). If the key is pressed while isRunning == true, then ignition is turned off.

starter evaluates the state of IgnKey and isRunning. If IgnKey == true and isRunning == false, then the starter engine is turned on. In all other cases the starter engine is turned off. When the starter evaluates as true, torque is applied to the flywheel (which in turn builds engine revs and once they pass the cutoff threshold the engine will start to run).

Here's a short video of the engine and flywheel scripts running in the Blender Game Engine, with the starter engine added to them.


The left cube represents the flywheel speed.
The right cube rotates only when the engine is running.


These are the logic bricks I'm using:

starterlogic2.jpg


To the far left is the game properties (for this object).
  • IgnKey (Boolean, i.e. True / False)
  • flywheelSpeed (Floating number, i.e. decimal value)
  • isRunning (Boolean)
  • starter (Boolean)
The game properties can be called and assigned from the Python script by using the name of the object owning the property and the name of the property. So if the object is named "own" and the property is named "flywheelSpeed", then the script calling the property would be:

Code:
own['flywheelSpeed']

So for instance I can make a script that defines RPM as the value of the flywheelSpeed property:

Code:
RPM = own['flywheelSpeed']

To the right of the properties we have the logic bricks. From left to right there are Sensors, Controllers and Actuators.

Sensors are the eyes and ears: they monitor and evaluate properties, looking for a certain condition to be true, and when it is they send a pulse on to the controllers.

The controllers, in turn, are like brains. Very simple brains, but still. They recieve pulses from the sensors and decide wether to pass them on or not. For instance, an [and] controller will only pass a pulse on if it recieves pulses from all of its sensors. An [or] controller will pass a pulse on if it recieves a pulse from either of its sensors. A [nand] (not and) controller will pass a pulse on only if it recieves a pulse from one of its sensors and not from the other.

Actuators are the muscles of the logic bricks, they are the ones who makes things happen. But they only do so when they're ordered, i.e. when they get they pulses from the controllers.

An example:

You stand in the left lane of the road and you have two sensors: the first one is programmed send a pulse only when there is a car within a certain distance of you. The second one is programmed to send a pulse only when the car is heading towards you.

Your controller is programmed to pass on a pulse when it recieves pulses from both of its sensors.

Your actuator is programmed to make you step to the side when it recieves a signal from the controller.

Now, a car is approaching in the right lane. The first sensor sends a pulse, because the car is now within a certain distance of you. The second sensor can tell that the car is not heading directly towards you, so it's not sending a pulse. The controller recieves a pulse only from one of its sensors so it doesn't send anything to the actuator.

Now, a car is approaching in the left lane. This time both sensors sends a pulse, the controller pass it on to the actuator and the actuator does its job and makes you step to the side.

So, if we look at the logic bricks I'm using, the first sensor is an [always] sensor, which means that it always sends a pulse. Simple enough. It's connected to a [Python] controller. That chain is basically telling the game engine to [always] [run the python script].

Next sensor is called [IgnKeyTap]. It will detect when the ignition key (I) is pressed, and because it's set to "tap" it will treat the key input as a tap (it will just send one pulse regardless of how long you press the key). It's connected to an [and] controller and because it's the only sensor connected to it, the controller will pass on the signal to the actuator [IgnKey]. [IgnKey] is a property actuator and it's set to toggle the IgnKey property on/off.

Next there are three sensors connected to the same controller:
  • [IgnKeyUntap] - is the opposite of the tap sensor, any time the key (I) is unpressed it will send a positive pulse. Since it's set to "tap" it only sends one pulse, regardless of how long the key remains unpressed. Effectively it will only send a pulse at the very moment the key is unpressed and then stop.
  • [isRunningFalse] - Reads the isRunning property and sends a pulse when isRunning is False.
  • [ignkeyTrue] - Reads the IgnKey property and sends a pulse when IgnKey is True
This is where it gets complicated, but let's take a look at it:

If the ignition key is released AND if the engine is not running AND if the ignition key value is true, then pass on the signal. The signal then is passed on to the [IgnKey] actuator, to toggle the IgnKey property on/off.
However, since the signal is only passed on while the IgnKey property = true (on), this function can only set the property to off, because if the property if already off then one of the sensors will not be triggered and the controller will not send a pulse. So why do we want to set the IgnKey to off? Well, because we released the ignition key, while the engine was not running. If we didn't turn off the ingition while that happened, the ignition would be left on and the next time we pressed the ignition key, the ignition would switch from on to off, which is not what we want when we want to start the engine.

^ That function above is what caused me the most headache when building the starter engine...

The [isRunningFalse] sensor is also connected to a second controller, together with the [ignkeyTrue] sensor. The controller is then linked to the [StarterOn] actuator, which assigns the starter property with the value of 1. Now, the starter property is a boolean property and can only be true / false, so why assign a value of 1 to it? Well, it's because 1 is interpreted as True (and 0 is intepreted as False). So, when the engine is not running and the ignition is on, the starter engine property will be set to true.

In the Python script there is a corresponding line:

Code:
if own['starter'] == True:
    starterTorque = 1.0
else:
    starterTorque = 0.0

If you remember the flywheel script, we set the torque on the flywheel to be the engineTorque + the starterTorque. So when the starter property is true, torque will be applied to the flywheel, and as a result the crankshaft will start to revolve. When the RPM is greater than the EngineCutOff value (90 RPM), the spark plugs will fire and the engine will come to life!

6759ff044c6366116a82b8507ccecdd4480d40b0af18462342931a05622791dc.jpg

We're almost by the end now, only two more sensors. Because now that we have a way to engage the starter motor we need a way to disengage it.

Those sensors are:

  • [isRunningTrue]
  • [ignkeyFalse]
You may recognize these, they do sound familiar. Yep, they're the "evil twins" of the [isRunningFalse] and [ignkeyTrue], that was used to turn the starter engine on. These "evil twins" will send pulses whenever their "good twins" don't, and because they're connected to an [or] controller either of these sensors will set the starter value to 0 = False = starter motor disengaged. Because we don't want the starter motor to be engaged when the engine is running or when the ignition is off.

(The grayed out controller and actuator at the bottom is just a function to get a visual cue of the state of the engine. When the engine is running it would apply rotation to the cube. I deactivated them for the screenshot to not make the logic bricks more confusing than they already are).

Return to OP
 
Last edited:
Really flying on this mate or should i say driving. i still haven't installed blender if i am honest but this is going so fast is looks like it will be almost drag and drop when i do :D.

Do you mind if i attempt to change the script's to C-Sharp? see if they work in unity i will put your name in credit's commented out if it is ok.

I think it's time GTP get's a love button like inst appropriate for something this epic so much graph porn :D
 
Really flying on this mate or should i say driving. i still haven't installed blender if i am honest but this is going so fast is looks like it will be almost drag and drop when i do :D.

Do you mind if i attempt to change the script's to C-Sharp? see if they work in unity i will put your name in credit's commented out if it is ok.

I think it's time GTP get's a love button like inst appropriate for something this epic so much graph porn :D

Go ahead and adapt it :) Just remember that this isn't final code, it's just something I create on the fly so I'm not even sure if it will all work together in the end.
 
Go ahead and adapt it :) Just remember that this isn't final code, it's just something I create on the fly so I'm not even sure if it will all work together in the end.

Is that a disclaimer ? if my pc turn's into a time machine i am suing you :P nah course.

I never look a gift horse in the mouth :):tup:much appreciated
 
Coulomb friction clutch

I've been thinking about the clutch script, and the one I used in the "momentum and clutch" parts doesn't really work as a real clutch. It transmitted a force, yes, but it didn't have any friction. So as long as the clutch is engaged, no force no matter how strong can make it slip. For the Peugeot 106 that might be a minor issue, because the engine is not that strong, but it's still a flaw that I would like to correct.

So now I've been taking crash courses in physics (hail the mighty Walter Lewin :bowdown:) and I think I've got the hang of how I could use a Coulomb friction model for the clutch.

Coefficients of friction

Friction depends on the materials involved. Some materials slip more than others. Wet ice, for instance, has almost no friction at all. Rubber on tarmac has a lot more friction. The value that describes the frictional properties of a combination of materials (no material can have friction unless it's in contact with another material) is called a friction coefficient, and there are two kinds:

1. Static friction coefficient - how strong the friction is when the objects are at reast relative to each other.

2. Kinetic friction coefficient - how strong the friction is when the objects are in motion relative to each other.

The static friction coefficient is in most cases higher than the kinetic, so it takes more force to make an object slip from rest than it takes to maintain the motion once it has started to slip.

The friction coefficient is denoted with the Greek letter mu (μ), and I found a source online giving a typical friction coefficient of a clutch as 0.35.

That is probably the kinetic friction value, so let's define the kinetic μ, or μk, as 0.35. And then we can pick an aribtrary value for μs (static), let's say 0.4.

Normal force

The coefficient alone is not enough to calculate the force of friction, because friction is not only depending on material, but also on how hard they're pushed against each other. That is called the normal force, and is denoted with the letter N.

Normal force is defined as the force acting peripendicular to the contact surface, for an object resting on a horizontal plane it's equal to the gravitational force on the object (provided that no additional forces are acting on it).

A clutch is not affected by gravity though, but instead the plates are being pressed together by some other force, usually a spring. The exact method isn't really relevant for the simulation, all we need to know if how much force is being used to push them together.

I haven't been able to find a good source on the amount of force that is used, so we have to do a bit of guesswork and adjust the values when we test the simulation.

Just for example, let's say that the plates are being pressed together by a force of 100N.

The calculations

The force of friction is being defined as f = μ * N.

So in this example, the force of friction between the plates would be = 0.35 * 100 N = 35 N.

What does that mean then? Well, it means that if a force of, say 50N, would be applied in a direction parallell to the contact surface, then 35N would be absorbed by friction, and the object would be accelerating by a force of 50-35 = 15N.

So that's Newton, but how do we get to Newton meters (Nm)?

Now, because a clutch has angular velocity and because we're working with Torque (Nm) rather than Force (N), it makes more sense to convert the clutch friction to torque. How do we do that?

Well, Torque = Force * distance from the axis. So what we need to find out is the distance from the axis where the friction force is acting. Here I have run into another problem, because no one on the entire internet seems to know (or be willing to share) information on the diameters of a clutch. So, let's guess again:

Let's say that the outer radius of the friction plate is 12 centimeters (I have no idea if this is way off or not...) and the inner radius of the friction plate is 8 centimeters. Then the average radius of the friction plate = (8+12) / 2 = 10 centimeters. 10 centimeters = 0.1 meters, so if we multiply that with the force in Newtons, we get 35 N * 0.1 m = 3.5 Nm.

Adjusting the values

3.5 Nm is not a lot of torque, the clutch would slip if the engine produces more than 35 Nm of torque, so perhaps we need to bump the normal force quite a bit, making the spring stiffer.

Let's say that the normal force is 5000 N, then we get 1750 N of friction force * 0.1 = 175 Nm of torque.

How does the friction impact on the speed of the crankshaft and the gearbox?

So, what does this all mean? Well, since this is the kinetic friction, it means that if the crankshaft and gearbox are having different angular velocity (one body rotating faster than the other), then the slower rotating body will be accelerated by a torque of 175 Nm and the faster rotating body will be slowed down by a force of 175 Nm, provided that no additional torque is being applied to these bodies. (If the crankshaft is the faster rotating body, and the engine is producing 100 Nm of torque, then the crankshaft will slow down by 175 Nm - 100 Nm = 75 Nm.

And the script?

Update: The Python script is now available below.

Edit: Based on this formula, clutch wear could easily be implemented by reducing the friction coefficient. And material fatigue in the spring could be simulated, by reducing the normal force. None if this could be done in the other clutch model that was based on collision rather than friction.

Basically, the more detailed and accurate you make the models, the more detailed mechanical damage you can implement.

Python script

Code:
## flywheel and clutch data ##

fly_w = 200 #rads/s #get input from flywheel data

fly_Nm = 50 #Nm #get this from engine data

clu_w = 190 #rads/s #get this from transmission data

clu_Nm = -110 #Nm # get this from transmission data

## compare torque

rel_Nm = fly_Nm - clu_Nm #relative torque

## compare angular velocity

rel_w = fly_w - clu_w #relative angular velocity

## clutch data ##

clu_muS = 0.4 #static friction coefficient

clu_muK = 0.35 #kinetic friction coefficient

if rel_w == 0: #determine which coefficient to use
    clu_Co = clu_muS
else:
    clu_Co = clu_muK

clu_R = 0.1 #clutch radius

clu_N = 5000.0 #clutch spring force, Newton

clu_fMax = clu_Co * (clu_R * clu_N)

## torque calculation

if rel_w > 0: #if relative velocity > 0
    fly_Nm -= clu_fMax/2
    clu_Nm += clu_fMax/2

elif rel_w < 0: #if relative velocity < 0
    fly_Nm += clu_fMax/2
    clu_Nm -= clu_fMax/2

elif rel_Nm > clu_fMax: #if relative torque > fMax
    fly_Nm -= clu_fMax/2
    clu_Nm += clu_fMax/2
elif rel_Nm < clu_fMax * -1: #if relative torque < negative fMax
    fly_Nm += clu_fMax/2
    clu_Nm -= clu_fMax/2
else: #if clutch is locked
    fly_Nm += clu_Nm
    clu_Nm += (fly_Nm - clu_Nm)

print(rel_Nm, 'Nm')
print(rel_w, 'rads/s')
print(clu_fMax, 'Nm')
print(fly_Nm, 'Nm;', clu_Nm, 'Nm')
 
Last edited:
Fuel consumption and binary representation

I have a little issue with fuel consumption. Every logic tick a small amount of fuel is subtracted from the fuel tank. The problem is that when the fuel consumption is low (like during idle) the change in fuel level is so small that the accuracy of the binary representation of the value drops below what's acceptable.

An example: I started measuring the fuel tank in liters, so the initial value of the fuel tank was 80.0. The fuel consumption per minute was 0.013 liters, or 0.0000036 liters per logic tick.

When I started the simulation and let the engine run at idle, nothing happened to the fuel. It just stayed at 80.0. Once I revved up the engine the fuel consumption started to work and when I measured the consumption at max revs it was perfectly accurate, with 0.58 liters consumed in a minute. Yet, when the engine dropped back to idle, the fuel consumption stopped.

I couldn't figure out why, until I realised that it must be related to the binary system of representing values. So I checked what the binary representation of 79.9999964 (max fuel - fuel consumption of a single logic tick) is and sure enough, the most accurate representation is 01000010 10100000 00000000 00000000 = 8.0E1 = 80.0. So even though a small amount of fuel was subtracted, the binary representation couldn't describe it any better than as 80.0, which meant that the fuel consumption effectively stopped working at idle.

I tried a workaround, by describing the fuel tank in centiliters (8000.0), which would reduce the number of decimals needed. Doing that I managed to at least get the consumption to work at idle, but it's still not very accurate. It's off by almost 50%.

I have a fix in mind, and that is to have a separate counter for the fuel you have consumed. 0.0000036 can be represented to much higher accuracy than 80.0000036, so the idea is to have a counter accumulate these values at 60fps, then have a separate script read the value every second, subtract it from the fuel tank and reset the counter.

Update: The fix described above works! The fuel consumption is now accurate :) And the separate script I wrote is really simple:

Code:
cont = bge.logic.getCurrentController()

own = cont.owner

own['fuel'] -= own['fuelCon']
own['fuelCon'] = 0

^ That script is set to run once every 60 logic tics. It subtracts from the fuel tank (own['fuel']) the sum of fuel consumed over the period (own['fuelCon']) and then resets the fuel consumption counter to 0.
 
Last edited:
I have an issue with the clutch. The physics are all correct as far as I can tell, but the problem is that real life physics is running at infinite "framerate" while I'm only having 60 fps in the simulation.

The result is that the clutch never really is at 0 velocity (relative to the crankshaft) because there's always an acceleration from the friction. In the end, it keeps oscillating between just above 0 and just below 0 velocity.

clutchissue.jpg


In the picture above the green line marks the border between two frames. In frame A (the top one) the net torque on the flywheel is 86.9 Nm, almost all of it is because of friction from the clutch, because the transmission is rotating faster than the crankshaft and the clutch is dragging the crankshaft along, so to speak.

In frame B (1/60th of a second later) the net torque from frame A has given the crankshaft so much speed that it's now spinning faster then the clutch, so now the friction is acting in the opposite direction, with a net torque of -88.6 Nm. In real life, the net force would be 0 at 0 velocity, but the problem in the simulation is that 0 velocity ends up right in between two frames so the actual net force is never 0.

If there was a frame C you'd see that the crankshaft is once again slower and that the friction is accelerating it. And so on and so forth for every frame at 60 fps.

Obviously this isn't good enough. The clutch should be locked together until there's enough torque or little enough normal force to make it slip. I need to figure out a good way to make that happen...
 
Last edited:
I have an issue with the clutch. The physics are all correct as far as I can tell, but the problem is that real life physics is running at infinite "framerate" while I'm only having 60 fps in the simulation.

The result is that the clutch never really is at 0 velocity (relative to the crankshaft) because there's always an acceleration from the friction. In the end, it keeps oscillating between just above 0 and just below 0 velocity.

View attachment 327930

In the picture above the green line marks the border between two frames. In frame A (the top one) the net torque on the flywheel is 86.9 Nm, almost all of it is because of friction from the clutch, because the transmission is rotating faster than the crankshaft and the clutch is dragging the crankshaft along, so to speak.

In frame B (1/60th of a second later) the net torque from frame A has given the crankshaft so much speed that it's now spinning faster then the clutch, so now the friction is acting in the opposite direction, with a net torque of -88.6 Nm. In real life, the net force would be 0 at 0 velocity, but the problem in the simulation is that 0 velocity ends up right in between two frames so the actual net force is never 0.

If there was a frame C you'd see that the crankshaft is once again slower and that the friction is accelerating it. And so on and so forth for every frame at 60 fps.

Obviously this isn't good enough. The clutch should be locked together until there's enough torque or little enough normal force to make it slip. I need to figure out a good way to make that happen...

Drop it by one Hz to 59hz i think that is how asseto corsa does it ...
 
Drop it by one Hz to 59hz i think that is how asseto corsa does it ...

I'm not sure how that would work, the 0 velocity will still appear in between frames (because there's infinite space in between logic tics, so the probability of 0 velocity to occur in between logic tics is something like 0.9999999...). I do have another fix in mind though, and that is to make the speeds clamp to each other when they are within a certain percentage of the other, like 1% or something like that.

Something like this:

Code:
if 0.99 < (velocity_A / velocity_B) < 1.01:
     velocity_A = (velocity_A + velocity_B) / 2
     velocity_B = velocity_A

(Update: This script contains a possible division by zero. Make sure you create an exception if you're thinking about using this code.)

So if the crankshaft has an angular velocity of 500 radians per second and the transmission 504 radians per second, the velocities would be withn 1% of each other so both speeds would get clamped to (500+504)/2 = 502 radians per second.

Not sure if 1% would do the trick, perhaps it needs to be 5% or even higher than that? Perhaps I'd need to change the force of friction to reduce as the speed approaches zero, so it doesn't zoom past the target.

An alternative method, which may be more realistic (because 99 isn't 100 in real life), might be to evaluate when the change from relative velocity below 1:1 to relative velocity above 1:1 occurs, because if the relative velocity goes from being smaller in one frame to being greater in the next, you know that at some point in between the frames it must have been equal, and when that occurs you can clamp the speeds to each other:

Code:
w_relative = velocity_A - velocity_B

if w_storage < 0.0 < w_relative:
     velocity_A = (velocity_A + velocity_B) / 2
     velocity_B = velocity_A

elif: w_storage > 0.0 > w_relative:
     velocity_A = (velocity_A + velocity_B) / 2
     velocity_B = velocity_A

w_storage = velocity_A - velocity_B

Edit: Detected a fatal mistake in the script above: never divide by velocity, because velocity can be 0. So I changed from division to subtraction.

It also means that I should scrap the code at the top of the post, since there is a division by velocity in it.
 
Last edited:
It's alive!



There's no sound, but what you see is the engine (the tall grey block), the flywheel (the light grey spinning block), the clutch (the dark grey spinning block) and the fuel tank (the tall orange block).

Next step: Adding a gearbox.

Edit: Here's a video with a basic engine sound.



Engine script:

Code:
import bisect

scene = bge.logic.getCurrentScene()

cont = bge.logic.getCurrentController()

own = cont.owner

flywheel = scene.objects['Flywheel']
fly_w = flywheel['flywheelSpeed']

clutchPlate = scene.objects['ClutchPlate']
clu_w = clutchPlate['clutchSpeed']
w_storage = clutchPlate['w_storage']

fuelTank = scene.objects['Fueltank']
fuelLevel = fuelTank['fuel']

## framerate
fps = bge.logic.getLogicTicRate()
pi = 3.1415927

##----- DRIVER INPUTS -----##

#Ignition
IgnKey = own['IgnKey']

print('Ignition key:', IgnKey)

#Accelerator
aDrop = 0.9
Accelerator = own['acc']
own['acc'] *= aDrop

#clutch pedal
cDrop = 0.01
clutchPedal = clutchPlate['clutchPedal']
clutchVal = 1 - clutchPedal**3
if clutchPedal > 0:
    clutchPlate['clutchPedal'] -= cDrop
if clutchPlate['clutchPedal'] < 0:
    clutchPlate['clutchPedal'] = 0

##----- ENGINE SPECS -----##

#Throttle
tMin = 0.18 #Throttle bypass, adjust for engine idle speed
tMax = 1.0
tRange = tMax - tMin
Throttle = tRange * Accelerator + tMin

print('Throttle: %.2f' % Throttle)

#Engine data

Cylinders = 4
cc = 1587
stroke = 4

L_RPM = [0, 500, 1000, 1800, 2100, 2200, 2900, 3100, 3400, 4200, 4600, 5500, 6100, 7100, 7500, 8200, 8500]
L_wotTorque = [0.0, 22.5, 45.0, 84.2, 103.0, 107.0, 115.4, 115.8, 117.8, 118.5, 126.0, 129.1, 124.0, 103.0, 90.0, 0.0, 0.0]
L_idleTorque = [0.0, 7.5, -1.8, -4.0, -4.9, -5.2, -7.1, -7.7, -8.5, -10.8, -11.9, -14.4, -16.1, -18.9, -20.0, -22.0, -22.8]
L_nosparkTorque = [0.0, -1.4, -2.8, -5.0, -5.9, -6.2, -8.1, -8.7, -9.5, -11.8, -12.9, -15.4, -17.1, -19.9, -21.0, -23.0, -23.8]

RevLimit = 7250
RevCutOff = 90

##----- PHYSICS -----##

#Engine RPM

RPM = fly_w * 60 /(2*pi) #get this value from the flywheel rotation

#Ignition and spark plugs

if IgnKey == 1 and RevCutOff < RPM < RevLimit : Spark = 1
else : Spark = 0

#Fuel
Fuel = 80
AFR = 14.7
AFRv = 0.000113
VoluEff = 0.9

CylLoad = cc * VoluEff * AFRv * Throttle

FPR = CylLoad / 2

#Liters per minute
FLPM = FPR * RPM / 1000

fuelTank['fuelCon'] +=FLPM/(60*fps)

print('Fuel consumption per minute (L): %.3f' % FLPM )
print('Fuel (L): %.3f' % (fuelLevel))
print('FuelCon %.3f' %fuelTank['fuelCon'])

#Interpolating torque curve

i2 = bisect.bisect(L_RPM, RPM)

i1 = i2 - 1

g1 = L_RPM[i1]
g2 = L_RPM[i2]

G = (RPM-g1) / (g2-g1)

if Spark == 1:

    d1 = L_wotTorque[i1]
    d1_2 = L_idleTorque[i1]
  
    d2 = L_wotTorque[i2]
    d2_2 = L_idleTorque[i2]
  
    wotTorque = d1 + G * (d2-d1)
    idleTorque = d1_2 + G * (d2_2-d1_2)
    torque = idleTorque + (Accelerator**(RPM/(RevLimit*Throttle)))*(wotTorque - idleTorque)
  
else:

    d1_3 = L_nosparkTorque[i1]
    d2_3 = L_nosparkTorque[i2]
  
    torque = d1_3 + G * (d2_3-d1_3)

BHP = torque * RPM / 7120.91

print('Engine speed RPM: %.0f' % RPM)
print('Torque Nm: %.2f' % torque)
print('Power BHP %.1f' % BHP)

##--- FLYWHEEL ---##

if own['IgnKey'] == True and RevCutOff < RPM:
    own['isRunning'] = 1
else:
    own['isRunning'] = 0
  
print('Is running:', own['isRunning'])

frot = (RPM * (2*pi) / 60) /1000
crot = clu_w /1000

flywheel.applyRotation([frot, 0.0, 0.0])
clutchPlate.applyRotation([crot, 0.0, 0.0])

if own['starter'] == True:
    starterTorque = 1.0
else:
    starterTorque = 0.0

#Inertia engine
crankInertia = 0.05

flywheelMass = 5.5
flywheelRadius = 0.11
flyInertia = 0.5*flywheelMass*(flywheelRadius**2)

engInertia = crankInertia + flyInertia

#Inertia transmission
#weight of car, inertia of wheels

wheelInertia = 1.494

bodyInertia = 0

traction = 0

tranInertia = wheelInertia + bodyInertia * traction

totInertia = engInertia + tranInertia

#drivetrainTorque
drivetrainTorque = 0

### --- CLUTCH --- ###

Atorque = torque
Btorque = starterTorque
#Ctorque = clutchFrictionTorque
Dtorque = drivetrainTorque

clutchFrictionTorque = 0
drivetrainTorque = 0 #collected from transmission

fly_Nm = Atorque + Btorque
clu_Nm = Dtorque

#compare torque and angular velocity
rel_Nm = fly_Nm - clu_Nm
rel_w = fly_w - clu_w

if w_storage < 0.0 < rel_w or w_storage > 0.0 > rel_w:
    fly_w = (fly_w + clu_w) / 2
    clu_w = fly_w
    flywheel['flywheelSpeed'] = fly_w
    rel_w = 0
  
clutchPlate['w_storage'] = fly_w - clu_w

clu_muS = 0.4 #static friction coefficient
clu_muK = 0.35 #kinetic friction coefficient

if rel_w == 0: #determine static or kinetic friction
    clu_Co = clu_muS
else:
    clu_Co = clu_muK
  
clu_R = 0.1 #clutch radius
clu_N = 8000.0 * clutchVal #spring force, N

clu_fMax = clu_Co * (clu_R * clu_N)

## torque calculation

if rel_w > 0.0: #if relative velocity > 0
    fly_Nm -= clu_fMax/2
    clu_Nm += clu_fMax/2
    clu_lock = False

elif rel_w < 0.0: #if relative velocity < 0
    fly_Nm += clu_fMax/2
    clu_Nm -= clu_fMax/2
    clu_lock = False

elif rel_Nm > clu_fMax: #if relative torque > fMax
    fly_Nm -= clu_fMax/2
    clu_Nm += clu_fMax/2
    clu_lock = False
    print('Clutch slip init')
elif rel_Nm < clu_fMax * -1: #if relative torque < negative fMax
    fly_Nm += clu_fMax/2
    clu_Nm -= clu_fMax/2
    clu_lock = False
    print('CLutch slip init')
else: #if clutch is locked
    fly_Nm = clu_Nm + fly_Nm
    clu_Nm = fly_Nm
    clu_lock = True
    print('Clutch locked')

#Net torque

netFlyTorque = fly_Nm
netCluTorque = clu_Nm

if clu_lock == True:
    flywheelAcc = fly_Nm / totInertia
    flywheel['flywheelSpeed'] += flywheelAcc/fps
    clutchPlate['clutchSpeed'] = flywheel['flywheelSpeed']
else:
    flywheelAcc = fly_Nm / engInertia
    clutchAcc = clu_Nm / tranInertia

    flywheel['flywheelSpeed'] +=flywheelAcc/fps
    clutchPlate['clutchSpeed'] +=clutchAcc/fps

print('Clutch: %.2f' %clutchVal)
print('Net Torque: %.1f' %netFlyTorque)
print('Relative velocity: %.1f' %rel_w)
print('Relative torque: %.1f' %rel_Nm)
print ('fMax: %.1f' %clu_fMax)
print('FwAcceleration: %.1f' %flywheelAcc, "rpm per second")
print('RPM: %.0f' %RPM)
print(fps)
print('-------------')

### Engine sound ###

EngineSound = own.actuators["EngineSound"]
EngineSound.volume = Throttle
EngineSound.pitch = 0.5+RPM/2000

Fuel tank script:

Code:
cont = bge.logic.getCurrentController()

own = cont.owner

own['fuel'] -= own['fuelCon']
own['fuelCon'] = 0
 
Last edited:
I was looking for a meme that would do it justice .. i couldn't!! like jason said it's awesome. Now all you need is a gear box and diff/axle's etc well you know the rest. It will be very resource heavy due to calculation's/physic's doing it this way.

But i think even it could run only one car on a track with good models and graphics it be worth it. I'am personally using wheel colider's for my game and it's basically faking reality. Were as i think your trying simulation the whole thing physically with rigid body's and colider's? .... If thats is the case it's a real achievement.

amazing
 
I added a gearbox and a wheel. It works just like intended in 1st and 2nd gear, but when I shift to 3rd it starts acting in an odd way, with the RPM jumping up and down for no obvious reason.

gearbox.jpg
 
@eran0004

Much respect for doing this, looks great! I'm currently using Python a bit, and have been messing around with simple spring-mass-damper systems for fun - it's been a pleasure looking at what you have written.

Re the clutch/crankshaft oscillating around zero velocity problem, I had a similar problem a few years ago in Fortran doing some heat conduction (temps instead of velocities, but same thing). I used a similar "if close enough, then set to equal each other" approach. But setting the "close enough" threshold, as you wrote, can be hard (especially if you ever want to model someone redlining an F1 car then jumping the clutch up!). I mucked around with my model rate and increased it if needed to, but this obviously will take more computational power and may be unnecessary for other parts of the model. I think your "change from relative velocity below 1:1 to relative velocity above 1:1" is a good solution.

Walter Lewin.... I knew I'd heard that name somewhere before:


Cheers,

Bread
 
Update: The simulation seems to run a lot smoother with a higher framerate. I'm running it at 120 fps right now and it doesn't cough and sneeze as much as it did at 60 fps. Or maybe it's just doing it twice as fast so it's shorter and much harder to notice... At 60 fps 1st and 2nd gear worked just fine, but 3rd and above were very bumpy. At 120 fps I can go all the way up to 5th gear and there's just minor bumps in the simulation.

I think there's something about the way the velocity / torque is calculated and transmitted between the various components that's not right, and that's what's causing the coughing and sneezing in the first place. I have a feeling that I'm mixing torque and angular velocity when I should only use one or the other. I need to look into that.
 
Here I have run into another problem, because no one on the entire internet seems to know (or be willing to share) information on the diameters of a clutch. So, let's guess again:

http://mgaguru.com/mgtech/clutch/ct_110.htm

(OK - it's a 50's car with 75 BHP!)

175 lb "force" / 2.2 lb/kg = 79.5 kg "force"
79.5 kg * 9.81 ms-2 = 780 N

Dia 8", so rad 4" = 10 cm

I would say you're pretty close... I'm guessing your clutch spring will be stronger to give a better friction force, but maybe the mu of the plates is so much better in modern cars that the spring doesn't have to be?! I guess one limit is the pedal/linkage lever length and strength of drivers left leg!

EDIT: from ebay "Peugeot 106 Release Bearing (18.5) for 200mm Clutch 1.6 RALLYE GTi VTS S16 - New"

200 mm has to be dia, so you are bang on with r = 10 cm!

Cheers,

Bread
 
http://mgaguru.com/mgtech/clutch/ct_110.htm

(OK - it's a 50's car with 75 BHP!)

175 lb "force" / 2.2 lb/kg = 79.5 kg "force"
79.5 kg * 9.81 ms-2 = 780 N

Dia 8", so rad 4" = 10 cm

I would say you're pretty close... I'm guessing your clutch spring will be stronger to give a better friction force, but maybe the mu of the plates is so much better in modern cars that the spring doesn't have to be?! I guess one limit is the pedal/linkage lever length and strength of drivers left leg!

EDIT: from ebay "Peugeot 106 Release Bearing (18.5) for 200mm Clutch 1.6 RALLYE GTi VTS S16 - New"

200 mm has to be dia, so you are bang on with r = 10 cm!

Cheers,

Bread

I'm trying to wrap my head around this and I can't really get the math together. One of the clutches gives a frictional force of 142 Nm, with a spring force of 195 lbs (or roughly 867 N). A typical friction coefficient of a clutch is 0.3, and 0.3 * 867 = 260 N. 260 N acting on an average distance of 0.09 meters (assuming outer radius 0.1 m and inner radius 0.08 m) from the center of rotation is 23.4 Nm. That's a long way from the target of 142 Nm.

Could it be that the spring force is the force for each spring, and that with a total of 6 springs the total force is 6 times higher? The end result then would be 23.4*6 = 140.4 Nm. That would make a lot more sense if that was the case.

The total spring force would be 5200 N, which sounds like a lot to push with your foot. But on the other hand you just need the clutch to move a few millimeters or so. If we assume that it would need to move 1 millimeter (all it needs to do is not to touch the other surface) and it would travel that distance in 1 second then it would require 5200 N * 0.001 m = 5.2 Nm (or Joules) of work. If the clutch pedal is linked so that it travels 10 centimeters for every millimeter of the clutch it would require 5.2 Nm / 0.1m = 52 N of force from your leg.

Which, if this chart by NASA is anything to go by, should be doable:

Image137.gif


Does it make sense?
 
Yep, I think you're right. I thought all clutches had the "star" leaf style spring in them, but from a quick google it looks like some are multiple individual coil springs. Leg forces seem OK too!
 
More charts i love it. glad to see your still working hard, I am getting confused with all that spring talk are we talking the spring's on a friction plate here?
 
More charts i love it. glad to see your still working hard, I am getting confused with all that spring talk are we talking the spring's on a friction plate here?

It's the spring force pushing the surfaces of the clutch together, it's being used to calculate the frictional force.

Glad you like the charts :D
 

Latest Posts

Back