Part 27: Four-Wheel Weight Transfer

©Copyright December 2001

In this instalment, we revisit the four-wheel statics of Part 20, solving the
statics problem for ** level ground**, which is very common in
simulation. The problem is:

First, let's introduce the standard coordinate frame used by the SAE, which
is documented in the usual source books by Milliken (*Race Car Vehicle
Dynamics*) and Gillespie (*Fundamentals of Vehicle Dynamics*). In this
frame, *X* is forward (** longitudinal**),

The symbols have the following meanings:

h |
vertical distance of the Centre of Gravity (CG) from the ground |

a, b |
longitudinal distance from the CG to the front and rear axle geometry centres |

t_{f}, t_{r} |
front and rear half-track lateral distances from axle centres to contact patch (CP) |

These give us the geometry needed to locate the CPs. Let's code this up in
*Mathematica *(MMA, see http://www.wolfram.com/). We open up an MMA
'Notebook' and write

**tyreLocs = {{a, tf, h}, {-b, tr,
h},{-b, -tr, h}, {a, -tf, h}} **

This code defines **tyreLocs** as a list
of 3-vectors, each being a list of locations in three-dimensional space in the
car-fixed, SAE coordinate frame. Numbered the tyres clockwise from right front
(RF). So the RF tyre has location *X = a*,
*Y = t _{f}*, and

Define a numerical sample early so that it's handy for intuitive checks. The following are for a Lamborghini Diablo, found from a web search, in Meters and Newtons:

This code defines a variable, **numRz**,
whose value is a list of MMA ** rules**. The following function of two
parameters shows how to apply rules:

**num18 [term_, fRzInput_] := ((term
/. numRz) /. fRzInput)**

This code defines a ** function** named

** nm18 [tyreLocs, {}]**

{{1.425, 0.8675, 0.42}, {-1.029, 0.88,
0.42},

{-1.029, -0.88, 0.42}, {1.425, -0.8675, 0.42}}

Here is see an example of input and output syntax from MMA. We applied the
function **nml8** to the pre-existing list
of geometry vectors and to a null list of extra rules to get numerical locations
of the CPs. These numbers are sensible, by inspection.

Now make a couple of symbolic lists of forces operating on the tyres.
Separate the forces operating in the *X - Y* plane from the
vertical forces since the former are given and the latter are the final objects
of our solution efforts.

fxy =
{{f1x, f1y}, {f2x, f2y}, (* right side *) {f3x, f3y}, {f4x, f4y}} (* left side *) fz = {f1z, f2z, f3z, f4z} |

For immediate purposes, combine them into three-forces, and there is some magic juju for doing that in MMA:

** threeForces = MapThread [Append, {fxy,
-fz}]**

{{f1x, f1y, -f1z}, {f2x, f2y, -f2z}, {f3x, f3y, -f3z}, {f4x, f4y, -f4z}}

Lisp programmers will say " Aha!" **MapThread** runs a function, in this case, **Append**, down some lists, and **Append** glues lists together. The two threaded lists are
**fxy** and **fz**, defined immediately above. Use the negative of **fz** so that the vertical force vectors point
upwards and the force components can be positive numbers. This code makes a new
list of four 3-forces named **threeForces**.

Now to the physics. Remember that torque is lever - arm force, where is the ** vector cross product**. We have
lever-arms in one list,

** threeTorques = MapThread [Cross,
{tyreLocs, threeForces}]**

{{-f1y h - f1z tf, a f1z + f1x h, a f1y -
f1x tf},

{-f2y h - f2z tr, -b f2z + f2x h, - b f2y - f2x
tr},

{-f3y h + f3z tr, -b f3z + f3x h, -b f3y + f3x
tr},

{-f4y h + f4z tf, a f4z + f4x h, a f4y + f4x
tf}}

When the car is not flipping over, the *X* and
*Y* torques are in equilibrium. The *Z* torque accounts for
yaw, so it's free. Add up the *X* and *Y* torques:

** sumTorques = Simplify [Plus @@
threeTorques]**

{{-f1y h - f2y h - f3y h - f4y h - f1z tf +
f4z tf - f2z tr + f3z tr,

-b (f2z + f3z) + a (f1z + f4z) +
(f1x + f2x + f3x + f4x) h,

-b (f2y + f3y) + a (f1y + f4y)
- f1x tf + f4x tf - f2x tr + f3x tr}

"
@@"
is MMA juju for applying the function **Plus** across a list of vectors. It's similar to **MapThread**, but not quite the same (see the MMA
documentation for details). Construct and solve the *X* and
*Y* equations for torque equilibrium:

**torqueEquations = {sumTorques[[1]]
== 0, sumTorques[[2]] == 0} **

The double square brackets pick out elements of lists, so **sumTorques[[1]]** is the first element of
sumTorques, that is, the torque about the *X* axis. The double-equals
is an assertion that **sumTorques[[1]]** is
zero, and solvable MMA equations must contain double equals. We have a similar
equation for **sumTorques[[2]]**, the
*Y* torque. Thus, **torqueEquations** is a list of two equations. Solving:

** rightHandRules = Solve [torqueEquations,
{f1z, f2z}] // FullSimplify**

This code solves the equations for the specified variables, **f1z** and **f2z**, expressing the solution as rules that can be applied in
other contexts. We've already seen numerical rules in action, but we can have
symbolic ones too, and equation solutions are an example.

These solutions are a 'mouthful', but notice, slightly surprisingly, that the lateral and longitudinal forces show up only as their sums, so reduce the amount of 'verbiage' by defining and applying a couple of rules by hand

**fxRule = f1x + f2x + f3x + f4x fx fyRule = f1y + f2y + f3y + f4y fy rhr = rightHandRules /. {fxRule, fyRule}**

** lhr = leftHandRules /. {fxRule,
fyRule}**

Notice the solution for the left-hand side (LHS) of the car, obtained by
methods analogous to those for the right-hand side (RHS). These expressions are
much more digestible. The first thing to notice is that the solutions for
**f1z** and **f2z**, on the RHS, depend on the solutions for **f3z** and **f4z** on the LHS. This is no help. As discussed in Part 20 of
the *Physics of Racing*, we need more information. ** Posit that cross
ratios of weights are equal: that any weight-jacking in the car is
symmetric**. For instance, the ratio of left to right is the same at front
as at rear, or, equivalently, that the ratio of front to rear is the same on
left as at right. These conditions yield another equation.

**eq1 = f1z f3z == f2z
f4z
**

Get one more equation from force equilibrium: the sum of all vertical loads equals the weight of the car.

**eq2 = mg == (f1z + f2z + f3z +
f4z)
**

Solve for rules to eliminate **f3z** and
**f4z** from the right-hand rules obtained
above:

** s34 = Solve [{eq1, eq2}, {f3z,
f4z}]**

** rTea = Flatten [FullSimplify [rhr /.
s34]]**

The important thing here is the expression **rhr/.s34**, which applies the elimination rules to **rhr**. The functions **FullSimplify** and **Flatten** are afterthoughts to reduce the verbosity of the
symbolic results.

This is great. We have expressions that depend *only* on **f1z** and **f2z**, so we have successfully isolated the RHS. Convert
these rules to equations thusly, and solve again:

** rNoah = Map [Apply [Equal, #] & ,
rTea] rSoln = Simplify [Solve [rNoah, {f1z, f2z}]] sf1z =
rSoln [[1, 1, 2]] **

** sf1zs = FullSimplify
[sf1z]**

Some of the above, you must take on faith due to lack of space to explain. Suffice it to say that we do likewise for tyres 2, 3, and 4, getting

** sf2zs = FullSimplify
[sf2z]**

** sf4zs = FullSimplify
[sf4z]**

** sf3zs = FullSimplify
[sf3z]**

Finally, we check the results

** FullSimplify [ (FullSimplify
[sumTorques /. fxRule] /. fyRule) /. lSoln /.
rSoln]**

{{{0, 0, -b (f2y + f3y)
+

a (f1y + f4y) - f1x tf + f4x tf - f2x
tr + f3x tr}}}

getting 0 for the *X* and *Y* torques, as required.
Visually, we see regular patterns in the solutions above. returning to
traditional notation, first define

The first two terms have the dimensions of torques, that is, force by distance. It is, however, difficult to interpret them as torques on the chassis with some sort of intuitively meaningful relevance to the problem at hand. Think of them as just some expressions with interesting form extruded from the solution. The next two terms have the dimensions of inverse length. The forces, then, have the following convenient, almost pretty forms:

Finally, we can use some of MMA's graphics functions to get a visual check on these results. Apply the numerical rules to the solution for tyre 1

** f1zPrototype = Simplify [nm18 [sf1z,
{}]]**

(0.203749 (-17163.7 + 0.42 fx)

(-35806.2 - 0.00525
fx + 1.03068 fy)) /

(35806.2 + 0.00525 fx)

Redefine it as a function by hand so we can plot it

** func1z [fx_, fy_] := (0.203749 *
(-17163.7 + 0.42 * fx) * (-35806.25 - 0.00525 * fx +
1.03068 * fy))/ (35906.25 + 0.00525 * fx) Plot3D
[func1z [x,y], {x, 0, 20000}, {y, 0, 20000}]**

As expected intuitively, the weight on tyre 1, the right front, decreases with increasing lateral and longitudinal forces, which range from 0 to 20,000 Newtons in the plot. As the longitudinal force increases, the car is forced forward and the weight is taken off the nose. As the lateral force increases, the car is forced rightwards and weight transfers to the left as in a right-hand turn. The other three tyres behave likewise reasonably.

We have posed and solved a familiar racing problem using a programming language for symbolic mathematics. We can code up these solutions in an ordinary language like C++ and use them in our simulation program. This methodology illustrates the application of one programming language, MMA in this case, to the design of software in another programming language. In fact, there is very significant time savings in using powerful tools like this, since the alternative is coding up algebraic mistakes in C++ and then debugging them in the context of a running simulation. This latter approach is very, very time-consuming and labour-intensive. In future articles, we will use another tool, Matlab, to design some more simulation software.