Jump to content
  • Advertisement


This topic is now archived and is closed to further replies.


Car engine simulation questions

This topic is 5293 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I just started driving school and also decided to try to simulate a car (engine at first though)... Well, I''ve done this little "simulator", which works quite well. The problem is that I don''t understand how should I disconnect rpm from the wheel velocity! I calculate the torque from the rpm which I calculate from wheel''s angular velocity. So to get the motor even running, I have to give it some fixed velocity at the start Now if the rpm gets too low, I can''t get any torque from the engine - I need a clutch! How in the hell should I implement one? How should I simulate engine braking? At the moment only thing that lowers rpm is air drag (actually air drag decreases velocity which in case decreases rpm) At the moment torque curve (in this simulation) limits the maximum rpm to 6000. How should I limit the rpm in case I get the clutch to work? (in neutral when flooring the gas?) Any ideas are welcomed
namespace Obnoxious
	public sealed class CarEngine
		private float GetRpm()
			// Get the rpm from the velocity

			float wheelAngularVelocity = velocity / wheelRadius;
			float rpm = wheelAngularVelocity * ratios[gear - 1] * differentialRatio;

			// Convert to revolutions per minute

			rpm *= (60.0f / (2 * (float)Math.PI));
			return rpm;

		private float GetTorque(float rpm)
			// Clamp the rpm between 0 and 10000

			rpm = Math.Max(0, Math.Min(rpm, 10000.0f));

			// Get the torque array location for the rpm

			// The closest array location is searched and the

			// correct torque is then interpolated between the

			// current array location and the next one

			float rpmIncrementInTorqueArray = 500.0f;
			float floatingArrayPosition = rpm / rpmIncrementInTorqueArray;
			int exactArrayPosition = (int)floatingArrayPosition;
			float interpolationAmount = floatingArrayPosition - (float)exactArrayPosition;

			// Get the torques around the current position

			float firstTorque = torque[exactArrayPosition];

			if(++exactArrayPosition > torque.Length - 1)
				exactArrayPosition = torque.Length - 1;

			float secondTorque = torque[exactArrayPosition];

			// Calculate the interpolated torque using the remainder

			// left from the division above

			float interpolatedTorque = (1.0f - interpolationAmount) * firstTorque + interpolationAmount * secondTorque;
			return interpolatedTorque;

		private float GetDrive(float rpm)
			// Return the real torque the engine would give

			float maximumTorque = GetTorque(rpm);
			float engineTorque = maximumTorque * throttle;
			float gearTorque = engineTorque * ratios[gear - 1];
			float driveTorque = gearTorque * differentialRatio;
			return (driveTorque * transmissionEfficiency);

		private float GetDrag()
			// Simply calculate the air drag, everyone knows the formula :)

			return (0.5f * coefficientOfFriction * frontalArea * airDensity * (velocity * velocity));

		public void Update()
			float acceleratingForce = GetDrive(GetRpm()) - GetDrag() - (brake * brakingForce);
			velocity += acceleratingForce / mass;

			if(velocity < 0)
				velocity = 0;

		public int Gear
			get { return gear; }
			set { gear = Math.Max(1, Math.Min(value, ratios.Length)); }

		public float Throttle
			get { return throttle; }
			set { throttle = Math.Max(0, Math.Min(value, 1.0f)); }

		public float Brake
			get { return brake; }
			set { brake = Math.Max(0, Math.Min(value, 1.0f)); }

		public float Velocity { get { return velocity; } }
		public float Rpm { get { return GetRpm(); } }

		// Values that user changes to affect the engine

		private int gear = 1;
		private float throttle = 0;
		private float brake = 0;

		// User cannot change

		private float velocity = 4.0f;

		// Values that will be ready or will be read from the disk

		private float differentialRatio = 3.42f;
		private float transmissionEfficiency = 0.7f;
		private float wheelRadius = 0.34f;
		private float mass = 2000.0f;
		private float coefficientOfFriction = 0.3f;
		private float frontalArea = 2.2f;
		private float airDensity = 1.29f;
		private float brakingForce = 1000.0f;

		private float[] torque = {
									 // Torque	// Rpm		// Array location

									 0,			// 0		0

									 0,			// 500		1

									 390.0f,	// 1000		2

									 420.0f,	// 1500		3

									 430.0f,	// 2000		4

									 440.0f,	// 2500		5

									 450.0f,	// 3000		6

									 460.0f,	// 3500		7

									 480.0f,	// 4000		8

									 480.0f,	// 4500		9

									 470.0f,	// 5000		10

									 450.0f,	// 5500		11

									 0,			// 6000		12

									 0,			// 6500		13

									 0,			// 7000		14

									 0,			// 7500		15

									 0,			// 8000		16

									 0,			// 8500		17

									 0,			// 9000		18

									 0,			// 9500		19

									 0};		// 10000	20

		private float[] ratios = {
									 // Ratio	// Gear number	// Array location

									 2.66f,		// 1			// 0

									 1.78f,		// 2			// 1

									 1.3f,		// 3			// 2

									 1.0f,		// 4			// 3

									 0.74f,		// 5			// 4

									 0.5f};		// 6			// 5

Business is business and Moses is Moses [Homesite]
Yes, you do need a clutch. Basically you can make it a boolean value for engaged or disengaged. When it is disengaged (from the driveline) the engine RPM will be independent of wheel RPM. You can limit the RPM in neutral the same way everyone else does: simulate an electronic rev limiter that just kills fuel at a certain RPM.

Share this post

Link to post
Share on other sites
Ok, but how do I calculate the rpm when disengaged from wheels? I''m now calculating the rpm from the wheel angular velocity. To make things a bit harder, the clutch cannot be boolean... It should rather be a value in range [0,1].

The problem is that I cannot figure out a way to calculate the rpm from somewhere else than the wheels. Also the engine breaking should be modeled, but I haven''t got a clue which is the realistic way.

Business is business and Moses is Moses
*laugh* Realistically simulating clutch depression on a computer is absurd. Even the most realistic racing games (NASCAR Thunder 2004 f.i.) that have clutches in them do not support real clutch depression. But if you want to do it, have at it, I don''t really have any suggestions for that.

To simulate engine speed when the clutch is disengaged you do the following:

e = engine speed at idle
e[m] = engine speed at rev limiter
e[c] = e[i] + %pedalDepression * e[m]

Engine braking is calculated as follows:

When the car is in gear but the gas pedal is not depressed then you have a situation where the momentum of the car is turning the axles, which in turn turns the differential, which in turn turns the driveshaft, turning the gears, turning the input shaft, turning the crankshaft. The resistance to motion is the crankshaft''s resistance to motion. You need to find the moment of inertia of the crankshaft and use that to retard momentum.

Share this post

Link to post
Share on other sites
Thanks, I'll start thinking this now. I just wonder how the clutch is implemented in lfs (Live For Speed)? It seems quite realistic.

What do you actually mean with crankshaft's inertia? My english lacks here, so could you explain how could it be implemented?

edit: A new problem I can't get fixed It goes like this: I update the engine 20 times a second, and that's a constant. I have every other value physically correct, expect this update frequency. The motor is reacting way too fast... If I floor the gas with 1st gear, car accelerates to 85 km/h way too fast. This far I have just multiplied the final acceleratingForce with some constant (0.2f for example). How should I make this work realistically? (Like if I put in a real cars values, and floor the gas, the car would accelerate just like in real life)

Business is business and Moses is Moses

[edited by - Miksan on March 26, 2004 4:29:06 PM]

Engine: I suggest having a separate class for this part of car (too). It would store its behaviour (torque curve), inertia, friction coeff, minimal rpm etc.
Variables would be the actual rpm, and the torque at this rpm. The final torque will include the friction as well.
I don''t think you need a limiter. Because at 6000 your net torque is 0, so the engine''s rpm won''t increase any more, rather decrease because of the friction. So if you integrate correctly and your calculation are based on laws of physics, it should behave this way.

If I were you I would handle the car parts separatly, I would not integrate it so much. For driveline modelling (including clutch !!!) check out google with ''driveline modelling'' keyword. The first will be a PDF, which is quite good! I will also apply it.
Clutch can be simulated correctly, you just should have a pedal to control it.

For accurate simulation you should use better integration than Euler (it''s rather unstable). I use RK4, which is very accurate and stable, but slower of course. But RK2 is also good for a game. You can also mix them to get optimal performance and accuracy.

So my suggestions:
- separate classes for parts of the car, like engine, clutch, gearbox, differential, wheel, tyre, body, suspension, control (for applying input)
- use OOP technics, like abstract classes, inheritence, virtual method. For example CTyre is an abstract class, and you derive specific models like CTyrePacejka. This way you can easily change tyre models, even during running the simulation (!)
- good integrators, I have separate classes for these like RK4, RK4Linear, RK4Rotational
- use accurte timing with PerformanceCounter
- low, perhaps constant timestep

Good luck!

The most impressive simulator was LFS for me so far. I have just tried CMR4, but I could not notice much difference to CMR2 in it. And when revesing with quite low speed (no slide), the back wheels move VERY strange !!! Check it out. It''s a shame. MY sim behaves better in this situation, seriously.

Share this post

Link to post
Share on other sites
When you simulate the motion of a rigid body, you will have a timestep of for example 2 ms. This shows how often you "take a sample" of the forces, torques acting your rigid body. Knowing the force and mass you can calculate the acceleration at that moment. From the torque and the inertia you can calculate the angular acceleration at that moment. Then you integrate acceleration through the timestep to get the new velocity: vel+=timestep*acc.
And then integrate the velocity to get the new position: pos+=timestep*vel.
This was the Euler integration method. This is easy to understand, it''s quite unaccurate and sometimes unstable. That''s why it is wise to use different integration methods for example to get the new velocity from the acceleration, the timestep, and the previous velocity.
Euler method is a first order method, so you have to take sample once through a timestep.
RK2 is a second order one, so you have to take sample twice: once at the current time, and once at the middle of the timestep (current time + timestep/2). It gives a more precise result.
RK4 is a fourth order so 4 sample per timestep (one at the current time, two at the middle, and one at the end of the timestep), the result is accurate, provided the timestep is small enough. With a timestep about 0.01 sec this methos also may become unaccurate and unstable.
The best and simplest example to compare the methods is to simulate an ideal spring with the same timestep. With Euler the amplitude will be larger and larger as the time goes by.
With Runge-Kutta methods this increase is quite smaller. But in real life there is always some kind of damping everywhere, so for these methods it does not care.
Hope this helps

Share this post

Link to post
Share on other sites
Miksan: now your Update() method realizes an Euler integration of acceleration. But your timestep seems to be 1 sec or what.
I suggest using SI units.
Use QueryPerformanceCounter() and QueryPerformanceFrequency() to get the accurete time.

[edited by - szinkopa on March 27, 2004 6:30:58 PM]

Share this post

Link to post
Share on other sites

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!