Friday, 10 May 2013

Suspension design - Applying to rFactor

Suspension design's a deceptive thing. There's some very basic principals at work, but some of them don't really show unless you're looking for them. At a first glance, the PM files in rFactor look pretty simple, and I'd say they are very simple (though I'll be going over them anyway just to make sure everything's clear).

I'll try to quickly go over the main details to keep in mind during suspension details. This is written primarily for use with rFactor. I think the functionality is pretty much the same in GTR2, GTL, GSC, Race 07, etc. and to some extent, rF2 aswell, but there may be some differences I haven't mentioned here.



For starters, here's some comments from a PM file, typically the comments you find at the start of any PM file by ISI. (I'll be using the F3 car provided as base content in rFactor as an example)

//////////////////////////////////////////////////////////////////////////
//
// Conventions:
//
// +x = left
// +z = rear
// +y = up
// +pitch = nose up
// +yaw = nose right
// +roll = right
//
// [BODY]  - a rigid mass with mass and inertial properties
// [JOINT] - a ball joint constraining an offset of one body to an
//           offset of another body (eliminates 3 DOF)
// [HINGE] - a constraint restricting the relative rotations of two
//           bodies to be around a single axis (eliminates 2 DOF).
// [BAR]   - a constraint holding an offset of one body from an offset of
//           another body at a fixed distance (eliminates 1 DOF).
// [JOINT&HINGE] - both the joint and hinge constraints, forming the
//           conventional definition of a hinge (eliminates 5 DOF).
//
////////////////////////////////////////////////////////////////////////// 

Anything on a line after // appears will be ignored by the physics engine, so this is a perfect place for you to place notes for yourself with no consequence in engine. A lot of mod developers delete this particular segment from their own files, but I'd suggest leaving it there for your own reference. Plus, you never know what newbie's gonna try learning from your stuff.

The main things we want to take note of in the coordinates is that positive X coordinates move to the left of the car, and positive Z coordinates move to the rear. So X = 1 and Z = -1 would put us in the front left quarter of the car. Individual components which are available are here too, so lets go through those one by one.

// Body including all rigidly attached parts (wings, barge boards, etc.)
[BODY]
name=body mass=(0.0) inertia=(0.0,0.0,0.0)
pos=(0.0,0.0,0.0) ori=(0.0,0.0,0.0)

// Front spindles

[BODY]
name=fl_spindle mass=(8.0) inertia=(0.0175,0.0160,0.0145)
pos=(0.67,0.0,-1.65) ori=(0.0,0.0,0.0)

[BODY]

name=fr_spindle mass=(8.0) inertia=(0.0175,0.0160,0.0145)
pos=(-0.67,0.0,-1.65) ori=(0.0,0.0,0.0)

// Front wheels

[BODY]
name=fl_wheel mass=(20.0) inertia=(0.913,0.594,0.594)
pos=(0.67,0.0,-1.65) ori=(0.0,0.0,0.0)

[BODY]

name=fr_wheel mass=(20.0) inertia=(0.913,0.594,0.594)
pos=(-0.67,0.0,-1.65) ori=(0.0,0.0,0.0)

// Rear spindles

[BODY]
name=rl_spindle mass=(8.5) inertia=(0.0175,0.0160,0.0145)
pos=(0.68,0.0,1.35) ori=(0.0,0.0,0.0)

[BODY]

name=rr_spindle mass=(8.5) inertia=(0.0175,0.0160,0.0145)
pos=(-0.68,0.0,1.35) ori=(0.0,0.0,0.0)

// Rear wheels (includes half of rear-axle)

[BODY]
name=rl_wheel mass=(22.0) inertia=(0.996,0.655,0.655)
pos=(0.68,0.0,1.35) ori=(0.0,0.0,0.0)

[BODY]

name=rr_wheel mass=(22.0) inertia=(0.996,0.655,0.655)
pos=(-0.68,0.0,1.35) ori=(0.0,0.0,0.0)

// Fuel in tank is not rigidly attached - it is attached with springs and

// dampers to simulate movement.  Properties are defined in the HDV file.
[BODY]
name=fuel_tank mass=(1.0) inertia=(1.0,1.0,1.0)
pos=(0.0,0.0,0.0) ori=(0.0,0.0,0.0)

// Driver's head is not rigidly attached, and it does NOT affect the vehicle

// physics.  Position is from the eyepoint defined in the VEH file, while
// other properties are defined in the head physics file.
[BODY]
name=driver_head mass=(5.0) inertia=(0.02,0.02,0.02)
pos=(0.0,0.0,0.0) ori=(0.0,0.0,0.0)


A body in your PM file is effectively just an object with mass, pretty straight forward. It is given a body, a mass, inertia, position and origin. The properties are given one after another, and are separated by a space. Forget the spaces, and rF gets brainfreeze!

The name of the body is important, as rF will search for them individually by their name later for various reasons later (such as changing the mass of the fuel tank based on how full it is). Another point to mention is that the mass and inertia of the main body in your car you choose in the PM file is later discarded. It will instead be replaced by the mass specified in the HDV file minus the weight of every other body listed in the PM file. Anyone particularly obsessive about housekeeping could correct it in the PM file according to that, but there is no need. If however, the mass of the body ends up being 0 or less at this point, rFactor will crash.


  • name is a string of text which should not include a space. (Not sure if it's case sensitive)
  • mass is provided as kilograms
  • pos is the Position for the object as 3 coordinates along the X, Y and Z axis respectively. (Provide in meters, assume 0,0,0 is the center of the car)
  • ori is where the origin of the object is (its center of gravity?). Again, XYZ coordinates in meters, measured relative to the body's position. I'd suggest leaving this at 0,0,0 for simplicity's sake.
  • Intertia is a parameter I don't fully understand yet. (If someone could lend me a hand here, please let me know!). But I'll share what I do know; the higher this number is, the more torque is required to achieve a desired roation. This number will increase if the body has weight spread far from it's center of gravity, and will be reduced if its weight is concentrated around the center of gravity. This number will double if the body's mass doubles.


Each corner should have a spindle (named fl_spindle, fr_spindle, rl_spindle, rr_spindle) which will be your upright. Your wheels (named fl_wheel, fr_wheel, rl_wheel, rr_wheel) each attach to their respective spindle, and the spindles are attached to the body by suspension as per your design.

While the spindles are attached manually by the developer, the fuel tank and driver's head are attached automatically, as described in their comments above. The moveable driver's head is to provide the driver with an animated view inside the car. Those head movements where you're pushed forward under braking, and pulled to the outside of a corner? that's what this does. It's implemented in such a way that defies the laws of physics, just so no chosen head movement option yields a competitive advantage/disadvantage. The movement in the fuel tank simulates the fuel itself moving around in the tank, something which can be controlled a bit more in your HDV file.

So we've got four wheels, four uprights, a fuel tank and a car. That's great! We just need to put them together now, as they're not much use to anyone if they're just a pile of bits.

The first thing we need to do is attach our wheels to our car. The attachment has some specific requirements, as we need the wheel to rotate freely around the horizontal axis, but not spin around wildly in all other directions. On top of that, we don't want it freely moving left/right, up/down, backwards/forwards... We're controlling that motion with the suspension. This is where constraints come into play. Lets just remind ourselves of what kinds of constraints we have access to.

// [BODY]  - a rigid mass with mass and inertial properties
// [JOINT] - a ball joint constraining an offset of one body to an
//           offset of another body (eliminates 3 DOF)
// [HINGE] - a constraint restricting the relative rotations of two
//           bodies to be around a single axis (eliminates 2 DOF).
// [BAR]   - a constraint holding an offset of one body from an offset of
//           another body at a fixed distance (eliminates 1 DOF).
// [JOINT&HINGE] - both the joint and hinge constraints, forming the
//           conventional definition of a hinge (eliminates 5 DOF).

This is also where our spindles fit in. We can achieve all the movements we want while eliminating pretty much everything we don't want by combining the body, spindle and wheel with the correct constraints. Lets start by attaching our wheels to our spindles using a [JOINT&HINGE] constraint. This constraint will not let the wheel move away from the spindle in any direction whatsoever. It will also limit rotation of the wheel to be around the spindle's horizontal axis.


// Front wheel and spindle connections
[JOINT&HINGE]
posbody=fl_wheel negbody=fl_spindle pos=fl_wheel axis=(-1.0,0.0,0.0)


Here, the Posbody parameter is the name of the first object this constrain attaches to. In this case, it's fl_wheel, our front left wheel
negbody is the second object it attaches to, which is fl_spindle, our front left spindle.
pos defines the position of this contraint. This is important because if the axis doesn't line up perfectly with the center of the wheel, the wheel won't rotate around a perfect circle. It'll look more like a novelty bicycle where the wheels aren't lined up, oscillating with every revolution. Thankfully, to make lining it up easier, we can just point to the name of the wheel, and the engine will straighten it out perfectly! How easy's that?
axis is the axis around which the wheel will rotate. The axis should be horizontal to the car, enabling the wheel to roll forwards/backwards. Generally a value of (-1.0,0.0,0.0) works perfectly for the left side of the car, and (1.0,0.0,0.0) for the right.

We can imagine the axis as being a pole our wheel is mounted on. The pole lets it rotate around, but doesn't let it tilt in any way. A nice illustration of this is how any previous owners of K'nex made cars using that, as pictured below. The white rod being the horizontal axis around which the wheel turns.


Next step is to include how the spindles mount onto the chassis. Often, this will be your double wishbones, but with a bit of imagination, a lot of arrangements can be achieved.

// Front left suspension (2 A-arms + 1 steering arm = 5 links)
[BAR] // forward upper arm
name=fl_fore_upper posbody=body negbody=fl_spindle pos=(0.157,0.103,-1.65) neg=(0.607,0.113,-1.65)

[BAR] // rearward upper arm
posbody=body negbody=fl_spindle pos=(0.157,0.103,-1.45) neg=(0.607,0.113,-1.65)

[BAR] // forward lower arm
posbody=body negbody=fl_spindle pos=(0.00,-0.113,-1.65) neg=(0.617,-0.113,-1.65)

[BAR] // rearward lower arm
name=fl_fore_lower posbody=body negbody=fl_spindle pos=(0.00,-0.113,-1.425) neg=(0.617,-0.113,-1.65)


This is the two A arms in the front left corner of the F3 car. The comment mentions a steering arm too, but we'll get to that in a moment. There's nothing we haven't seen before here, posbody and negbody are the two bodies each bar attaches to, and pos and neg are the positions of the two ends of the bar relative to the reference point in the middle. (fl_fore_upper attaches to posbody at pos, and negbody at neg, etc.)

BAR constraints effectively try to maintain their length. Each one can rotate freely any way it wants, however using multiple BARs together can eliminate certain movements, limiting you to any position that they would both find comfortable. This is how A arms are typically modelled; two bars forming an A shape, which is illustrated here. They will rotate freely with the point where they meet following a circular orbit around an axis defined by the other points on the two bars. This is the only way it moves, as this is the only set of positions it can be in without compressing or extending the bars.

Both upper bars have a neg pos in exactly the same place, as do the lower bars, which is what gives us the A shape described above. The same applies to the lower two bars.

Another important detail in this arrangement is that having the two points where the A arm attaches to the body at a distance prevents the body rotating forwards/backwards against where they attach. The distance from the two points is what causes this. However, there is 0 distance between where the two bars end on the spindle, enabling the spindle to rotate freely. This is required to make steering possible.

Finally, for the front two wheels, we want steering arms.

[BAR] // steering arm (must be named for identification)
name=fl_steering posbody=body negbody=fl_spindle pos=(0.155,0.113,-1.740) neg=(0.617,0.113,-1.735)

As the comment says, the name is important. This must be identified by the engine in order to be moved by the steering mechanism, giving the driver steering control over the car. The position of this is also important, as where it is located dictates the leverage strength it has over the wheel. It can go either in front of the A arms, or behind.

The rear should have something similar, referred instead to as a 'straight link'. Just one last rod to maintain the wheel's heading. 

[BAR] // straight link
posbody=body negbody=rl_spindle pos=(0.089,0.02,1.52) neg=(0.600,0.02,1.52)

With this, we can prevent the rear wheels from steering. And that's job done! :)

To wrap this up, there's nothing particularly complicated going on in the PM file. It is easy to get lost in the flood of coordinates, but it's all quite simple practice. Details such as how camber changes with suspension travel, bump steer, body roll, etc. all comes from the placement of all these parts. It's a complex subject I'm still getting to grips with myself, but I hope to put together another post explaining the most critical parts of that soon. :)

A few things left to mention is a reminder that your physics described in the PM file isn't converted into graphics. You can have different suspension geometry in your 3D model to what's in the physics engine, and you also don't have to worry about your suspension components intersecting anything else.

One final tip before I leave you though - This program will parse your HDV, TBC and PM files, and then provide you with a 3D representation of your suspension. It's a great way to confirm what you've got so far is what you expect, and avoid any nasty surprises further down the line.

3 comments:

Philip Roberts said...

nice job explaining this difficult subject,I am working on the suspension of a few cars i have added to RallyWorld 2013, mk2 Escort,Stratos AND Fiat 131.
As these are missing, and the suspension has been hard work.
many thanks
phil

Lem said...

Glad I could help. :)

I'd like to do a follow up, on more theory related aspects of suspension design. But I need graphics to help me explain things, and I'm having a royal nightmare trying to get that sorted.

Either way, I'm not done. Expect more at some point! :)

avatar7 said...

Thanks for the really useful guide! There is one thing I'm still not entirely sure about and which has given me a lot of headache. It appears that the 3D model somehow affects the suspension geometry and thus the handling of the car, because the trackwidth and wheelbase changes according to the position of the wheels in the 3D model and the PM file seems to be ignored. Unless I set those specifically in the HDV file (FrontWheelTrack, RearWheelTrack, LeftWheelBase and RightWheelBase), it seems each 3D model (i.e. in an F1 mod) has slightly different handling, even if there is just one set of physics for all cars. Can you comment on that? Any help on how to deal with this would be welcome.