PaceDynamics Documentation Open the app
Contents

Suspension

Roll and pitch centres

Where the links take a tyre's force into the body, and how much of the load transfer skips the springs.

Model as of 0.338Figures from the Formula Student single-car templateOctave check Exact

A tyre's force reaches the body two ways: through the springs, which compress and let the body move, and through the links, which do not. The roll centre and the pitch centre say how a corner divides the force between the two. They come from the geometry alone, from the hardpoints, before any spring is fitted.

The instant centre

Seen from the front, an upright on two wishbones moves, for a small travel, as if it turned about one point: its instant centre (IC). On a drawing it is where the two wishbones' lines cross. The upright and everything on it, the contact patch included, swings about it on an arm, the front-view swing arm (FVSA), from the patch to the IC.

A force at the contact patch that points along the line to the IC does no work as the wheel moves: it neither lifts nor lowers the wheel against its spring. Such a force goes into the body through the links alone. That line is the wheel's force line.

The roll centre

Draw the force line of each wheel of an axle. Where the two cross is the axle's roll centre. A sideways force at the roll centre is shared by the two force lines and loads no spring, so it does not roll the body; a force anywhere else does, by its height above the roll centre.

Constructing the roll centre in front view: each wheel’s instant centre from its wishbones, the lines from the contact patches through them, and where they crossroll centreIC of the left wheelIC of the right wheelhrcFront view, looking forward from behind the axle; not to scale
Fig. 1
Each wheel turns about its instant centre (IC); the lines from the contact patches through them cross at the roll centre.
Schematic

On a car that is the same on both sides the roll centre is on the centre-line, and similar triangles give its height:

Show the equations with
hrc=t2hICdIC(1)
hrc,f=603.9⋅26.204921.2=3.215 mm(1)
t/2
the contact patch's distance from the centre-line
hIC
the instant centre's height above the road
dIC
the instant centre's distance across from the patch; the IC of the left wheel lies across the car, to the right
The template’s front suspension to scale in front view, with the force lines from the contact patches and the roll centre they cross atroll centre 3.2 mm above the roadEach force line runs to its wheel’s IC, 4.92 m away across the cartrack 1208 mmFront axle from behind, to scale: the template’s hardpoints, the tyres drawn upright
Fig. 2
This car’s front force lines are within half a degree of the road, so they cross 3.2 mm above it.
Measured

The template's wishbones are close to parallel, so its instant centres are far away, 4921 mm from the front patches and 5504 mm from the rear ones, and only a little above the road. Its roll centres are low: 3.2 mm at the front, 2.7 mm at the rear. Almost all of its cornering force rolls the body on its springs; see Load transfer.

The line through the two roll centres is the roll axis. The body rolls about it, and the CG's height above it is the lever the cornering force rolls the body with (Ride and roll rates).

How the app finds them

A drawing in front view is exact only for wishbones whose pivot axes run fore and aft. Real ones are angled in plan and in side view, so the app works in three dimensions. Five links hold the upright: the two legs of each wishbone and the tie rod, with the rack held still. Each keeps its length, so the velocity of its outer end has no component along it:

(𝐏i−𝐐i)⋅(𝐯+𝝎×(𝐏i−𝐂))=0,i=1…5(2)
𝐐i,𝐏i
link i's chassis and upright ends
𝐯,𝝎
the upright's velocity at the wheel centre 𝐂 and its rate of turn, per millimetre the wheel centre rises

The contact patch 𝐊 is a loaded radius below the wheel centre, in the wheel's plane; it moves at 𝐯K=𝐯+𝝎×(𝐊−𝐂). In front view the force line leaves 𝐊 square to 𝐯K, and the instant centre is the point on it the upright turns about, 𝐊+(−vKz, vKy)/ωx. The roll-centre height is measured square to the road, the line through the two patches.

The app does not solve equation (2) directly. It solves the upright's position with the wheel centre half a millimetre above and below the static ride, and differences the two. That is the derivative to within the square of the step: on the template the roll-centre heights differ from the exact ones by 2·10⁻⁵ mm.

The side view and the anti features

The same construction in side view gives each wheel a side-view instant centre and a force line through its patch. Braking and drive forces act along the road at the patches. The part of the force line's slope that points into the car carries some of the pitch transfer through the links instead of the springs. Measured as a share of all the transfer, that is the anti effect:

Side view: the force lines from the front and rear contact patches through each wheel’s side-view instant centre, their angles to the road, and the pitch centre where they crossfront ICrear ICpitch centreθfθrhLSide view, not to scale; front to the right
Fig. 3
The anti features read the slope of each force line; the pitch centre is where the two cross.
Schematic
Show the equations with
anti-dive=tan⁡θfLhβf,anti-squat=tan⁡θrLhβr(3)
anti-squat=tan⁡(−0.515∘)⋅1600300⋅1=−4.79 %(3)
θf,θr
the force line's rise towards the middle of the car, from the front patch rearwards and from the rear patch forwards
βf
the front axle's share of the braking force, 0.62 on the template
βr
the rear axle's share of the drive force; the template drives the rear wheels only

100 % anti-dive would hold the nose level under braking; 0 % leaves it all to the springs; a negative value makes the dive worse. The template is close to zero both ways. A brake on the chassis, or a motor on the chassis driving through a shaft, reacts its torque on the body, not the upright: the app then takes the slope of the wheel centre's path instead of the patch's. Anti-dive and anti-squat works the percentages through.

Where the front and rear force lines of one side cross is that side's pitch centre, the side-view twin of the roll centre. On the template the two lines are close to parallel and cross 3.8 mm above the road, behind the rear axle.

What the model assumes

  • Every link is rigid and every joint is a point; bushes do not deflect.
  • The centres are those of the static ride. They move as the car heaves, rolls and pitches; the app's load transfer uses the static ones.
  • The car is the same left and right, so each axle's roll centre lies on the centre-line at the static ride.
  • The rack is held still: the tie rod is one of the five links.
  • An instant centre far away, as this car's front side-view one is (572 362 mm), is a poor number however it is computed: a small change in the arms moves it a long way. The force line through the patch is not, and the roll centre, the anti percentages and the pitch centre built from it are not either.

In the app

Kinematics, in the readout rail: "Front view" gives the roll centre height, the same height in the chassis frame, its lateral position and the FVSA length; "Side view" gives the SVSA length, the swing-arm angle, anti-dive, anti-lift and anti-squat. Each moves with the state.

In the 3-D view, Draw › Roll centre & axis and Draw › Anti & pitch axis draw the two constructions.

Vehicle Dynamics uses each axle's roll centre at the static ride for its load transfer.

The swing-arm angle in the readout rail is the angle of the line from the patch to the instant centre, positive with the instant centre above the road. Past 90° the instant centre is behind the patch.

Check it yourself

The script reads the template's hardpoints from example_suspension.m, solves the upright's motion and builds every number above. By default it differences the poses as the app does; set step = 0 for the exact derivative.

roll_centres.mwith example_suspension.m, pd_compare.mExact: agrees to machine precision
>> roll_centres

  Upright motion: poses 0.5 mm either side, differenced (the app's way)

  Quantity                                This script   PaceDynamics       Diff
  -----------------------------------------------------------------------------
  Standing radius, front [mm]                 221.713        221.713         0% 
  Standing radius, rear [mm]                   220.91         220.91         0% 
  Roll centre, front [mm]                     3.21515        3.21515   1.3e-07% 
  Roll centre, rear [mm]                      2.72063        2.72063     2e-07% 
    as load transfer uses it, front           3.21515        3.21515   2.5e-05% 
    as load transfer uses it, rear            2.72063        2.72063   2.4e-05% 
  FL instant centre y [mm]                    4317.29        4317.29   4.6e-09% 
  FL instant centre z [mm]                    34.5226        34.5226   9.8e-08% 
  RL instant centre y [mm]                    4887.97        4887.97     7e-09% 
  RL instant centre z [mm]                    35.4822        35.4822   1.4e-07% 
  FVSA length, front [mm]                     4921.23        4921.23     4e-09% 
  FVSA length, rear [mm]                      5503.81        5503.81   6.2e-09% 
  FL side-view IC x [mm]                       573159         573159   9.5e-10% 
  FL side-view IC z [mm]                     -2079.94       -2079.94   1.6e-08% 
  RL side-view IC x [mm]                      49206.2        49206.2     3e-09% 
  RL side-view IC z [mm]                     -526.947       -526.947   1.1e-08% 
  SVSA length, front [mm]                      572362         572362   9.5e-10% 
  SVSA length, rear [mm]                      50009.1        50009.1     3e-09% 
  Swing-arm angle, front [deg]               -0.10718       -0.10718   3.2e-08% 
  Swing-arm angle, rear [deg]               -0.514668      -0.514668     9e-09% 
  Anti-dive [%]                              0.618563       0.618563   3.2e-08% 
  Anti-squat [%]                             -4.79087       -4.79087     9e-09% 
  Pitch centre, station x [mm]               -1220.82       -1220.82     1e-08% 
  Pitch centre, above road [mm]               3.78028        3.78028   3.8e-08% 

  All 24 quantities agree with PaceDynamics (largest difference 2.5e-05 %).
The script
%% Roll and pitch centres: the lines a tyre force can take without loading a spring
%
% Companion to the documentation page "Roll and pitch centres". Runs unchanged in GNU
% Octave (7 or later) and MATLAB. Edit the car in example_suspension.m, or type your own
% hardpoints from the Kinematics tool, and run:
%
%   >> roll_centres
%
% What this computes, for each wheel at the static ride:
%
% 1. How the upright moves. Five links hold it: the two legs of each wishbone and the
%    tie rod, with the rack held still. A link keeps its length, so the velocity of its
%    outer end has no component along it. Written as a twist (v, w) about the wheel
%    centre C, an upright point P moves at v + w x (P - C), and link i, from the chassis
%    point Q_i to the upright point P_i, says
%
%        (P_i - Q_i) . v  +  ((P_i - C) x (P_i - Q_i)) . w  =  0.
%
%    Five equations in six unknowns: the motion is fixed up to its size, which is taken
%    per millimetre the wheel centre rises (v_z = 1).
%
% 2. The force line. The contact patch K is a loaded radius below the wheel centre, in
%    the wheel's plane, on the radius the tyre stands on: R = R_free - F_z / k_t. The
%    point of the upright at K moves at v_K = v + w x (K - C). A force at the patch
%    square to v_K does no work as the wheel rises, so it loads no spring.
%
% 3. Front view (y, z). The force line leaves K square to (v_Ky, v_Kz). The wheel's
%    instant centre is the point on it the upright turns about, K + (-v_Kz, v_Ky) / w_x,
%    and the front-view swing arm is its distance from K. The axle's roll centre is where
%    the left and right lines cross; its height is taken square to the road, the line
%    through the two patches.
%
% 4. Side view (x, z). The same with w_y: the instant centre is K + (v_Kz, -v_Kx) / w_y.
%    The anti features read the slope of the patch's path against the road,
%    tan(theta) = -(v_K . along) / (v_K . up):
%
%        anti-dive  = -tan(theta_f) * L / h * (front share of the braking force)
%        anti-squat =  tan(theta_r) * L / h * (rear share of the drive force)
%
%    (the wheel centre's slope instead where the brake or motor sits on the chassis),
%    and a side's pitch centre is where its front and rear lines cross.
%
% Arms nearly parallel in a view put the instant centre far away: this car's front
% upright turns in side view about a point 570 m ahead of it. That position is then a
% poor number however it is computed. The force line through the patch is not, and
% neither are the roll centre, the anti percentages or the pitch centre built from it.
%
% References: Milliken & Milliken, Race Car Vehicle Dynamics (SAE, 1995), ch. 17;
% J. C. Dixon, Suspension Geometry and Computation (Wiley, 2009), on velocity analysis.
%
% The app does not take the derivative in step 1. It solves the upright's pose with the
% wheel centre half a millimetre above and below the static ride and differences the
% two, which is the derivative to within step^2: 1e-5 of the roll-centre heights here
% (2e-5 mm), 5e-5 of the 570 m instant centre, less elsewhere. This script does the same
% by default, so it agrees with the app to its last digits. Set `step = 0` before running
% for the derivative itself.
%
% Part of the PaceDynamics documentation, https://pacedyn.dev/docs
% SPDX-License-Identifier: MIT

[car, app] = example_suspension();
if ~exist('step', 'var'), step = 0.5; end    % mm: 0.5 = the app's difference, 0 = the derivative

skew   = @(a) [0, -a(3), a(2); a(3), 0, -a(1); -a(2), a(1), 0];
axial  = @(M) [M(3,2) - M(2,3), M(1,3) - M(3,1), M(2,1) - M(1,2)] / 2;   % w from a small rotation
mirror = @(p) p .* [1, -1, 1];                                           % left wheel -> right wheel
deg = pi / 180;

%% The radius each axle stands on
g  = 9.81;                                                    % m/s^2
Fz = car.massTotal * g * [car.frontMassFrac, 1 - car.frontMassFrac] / 2;   % N, per wheel
R0 = car.tyreFreeRadius - Fz / car.tyreRate;                  % mm, front and rear

%% Each wheel's motion, and its contact patch
% Four wheels, FL FR RL RR. The right-hand ones are the left mirrored in y.
axles = {car.front, car.rear};
for k = 1:4
  a = axles{ceil(k / 2)};
  right = mod(k, 2) == 0;
  % The five links: lower wishbone (2), upper wishbone (2), tie rod. Q on the chassis, P on the upright.
  Q = [a.lcaFront; a.lcaRear; a.ucaFront; a.ucaRear; a.tieInner];
  P = [a.lbj;      a.lbj;     a.ubj;      a.ubj;     a.tieOuter];
  C = a.wheelCentre;
  % The wheel's spin axis, outboard: tilted by the camber (top in when negative), turned by the toe.
  n = [sin(a.toe * deg) * cos(a.camber * deg), -cos(a.toe * deg) * cos(a.camber * deg), -sin(a.camber * deg)];
  if right
    Q = mirror(Q);  P = mirror(P);  C = mirror(C);  n = mirror(n);
  end
  % The patch: straight down in the wheel's plane, a loaded radius from the centre.
  down = [0, 0, -1] + n * n(3);
  K = C + R0(ceil(k / 2)) * down / norm(down);

  if step == 0
    % The twist per mm of wheel-centre rise: five link rows and v_z = 1.
    A = zeros(6);
    for i = 1:5
      d = P(i, :) - Q(i, :);
      A(i, :) = [d, cross(P(i, :) - C, d)];
    end
    A(6, :) = [0, 0, 1, 0, 0, 0];
    tw = A \ [0; 0; 0; 0; 0; 1];
    v = tw(1:3)';  w = tw(4:6)';
    vK = v + cross(w, K - C);
    vC = v;
  else
    % The app's way: solve the pose with the wheel centre `step` above and below, and
    % difference them. Newton on the five link lengths and the wheel-centre height, each
    % step a small turn about the current wheel centre and a shift: its Jacobian is the
    % matrix above, at the current pose.
    L2 = sum((P - Q).^2, 2);
    for s = [-1, 1]
      Rm = eye(3);  t = [0, 0, 0];
      for it = 1:30
        Pn = (P - C) * Rm' + C + t;                         % the upright's points now
        res = [sum((Pn - Q).^2, 2) - L2; t(3) - s * step];
        J = zeros(6);
        for i = 1:5
          d = Pn(i, :) - Q(i, :);
          J(i, :) = 2 * [d, cross(Pn(i, :) - (C + t), d)];
        end
        J(6, :) = [0, 0, 1, 0, 0, 0];
        dx = -J \ res;
        t  = t + dx(1:3)';
        Rm = expm(skew(dx(4:6))) * Rm;
        if norm(dx) < 1e-14, break; end
      end
      pose(s + 2).R = Rm;  pose(s + 2).t = t;               % 1 = below, 3 = above
    end
    at = @(j, p) (p - C) * pose(j).R' + C + pose(j).t;      % where pose j carries point p
    vK = (at(3, K) - at(1, K)) / (2 * step);
    vC = (at(3, C) - at(1, C)) / (2 * step);
    w  = (axial(pose(3).R) - axial(pose(1).R)) / (2 * step);
  end
  wheel(k).K = K;  wheel(k).vK = vK;  wheel(k).vC = vC;  wheel(k).w = w;
end

%% Front view: instant centres, swing arms, roll centres
% Where the line from p1 along d1 meets the line from p2 along d2 (Cramer's rule).
crossing = @(p1, d1, p2, d2) p1 + d1 * ((p2(1) - p1(1)) * d2(2) - (p2(2) - p1(2)) * d2(1)) ...
                                     / (d1(1) * d2(2) - d1(2) * d2(1));
% (Arms exactly parallel in a view make w_x or w_y zero: the instant centre is then at
% infinity and the line runs along the arms. This car has none; the script does not handle it.)
for k = 1:4
  K = wheel(k).K([2, 3]);  vK = wheel(k).vK([2, 3]);  wx = wheel(k).w(1);
  wheel(k).ic   = K + [-vK(2), vK(1)] / wx;                 % (y, z)
  wheel(k).fvsa = norm(wheel(k).ic - K);
end
for ax = 1:2
  L = wheel(2 * ax - 1);  Rw = wheel(2 * ax);
  KL = L.K([2, 3]);  KR = Rw.K([2, 3]);
  rc = crossing(KL, L.ic - KL, KR, Rw.ic - KR);
  road = (KR - KL) / norm(KR - KL);
  rcHeight(ax) = (rc - KL) * [-road(2); road(1)];           % mm, square to the road
end

%% Side view: instant centres, swing arms, anti, pitch centre
% The car is the same on both sides, so its left side stands for both. The road in
% side view is the line through the front and rear patches.
KF = wheel(1).K([1, 3]);  KRr = wheel(3).K([1, 3]);
along = (KF - KRr) / norm(KF - KRr);  up = [-along(2), along(1)];
slope = @(u) -(u * along') / (u * up');                     % tan(theta) of a path u = (u_x, u_z)
for k = [1, 3]
  K = wheel(k).K([1, 3]);  vK = wheel(k).vK([1, 3]);  wy = wheel(k).w(2);
  wheel(k).svIc  = K + [vK(2), -vK(1)] / wy;                % (x, z)
  wheel(k).svsa  = norm(wheel(k).svIc - K);
  arm = (wheel(k).svIc - K) / wheel(k).svsa;
  wheel(k).angle = atan2(arm * up', arm * along') / deg;    % deg above the road
  wheel(k).tanPatch = slope(vK);
  wheel(k).tanHub   = slope(wheel(k).vC([1, 3]));
end
tanF = wheel(1).tanPatch;  if car.brakeInboard(1), tanF = wheel(1).tanHub; end
tanR = wheel(3).tanPatch;  if car.motorInboard(2), tanR = wheel(3).tanHub; end
antiDive  = -100 * tanF * car.wheelbase / car.cgHeight * car.brakeFront;
antiSquat =  100 * tanR * car.wheelbase / car.cgHeight * (1 - car.driveFront);

pc = crossing(KF, wheel(1).svIc - KF, KRr, wheel(3).svIc - KRr);   % left side
pcHeight = (pc - KRr) * up';

%% Against the app
if step == 0
  fprintf('\n  Upright motion: the exact derivative\n');
else
  fprintf('\n  Upright motion: poses %g mm either side, differenced (the app''s way)\n', step);
end
pd_compare('Standing radius, front [mm]',       R0(1),              app.standingRadiusFront);
pd_compare('Standing radius, rear [mm]',        R0(2),              app.standingRadiusRear);
pd_compare('Roll centre, front [mm]',           rcHeight(1),        app.front.rcHeight);
pd_compare('Roll centre, rear [mm]',            rcHeight(2),        app.rear.rcHeight);
% Vehicle Dynamics solves the roll centre on the radius typed with the hardpoints and
% moves it to the standing radius along a straight line: 2.5e-7 of it apart.
pd_compare('  as load transfer uses it, front', rcHeight(1),        app.dyn.rcHeightFront);
pd_compare('  as load transfer uses it, rear',  rcHeight(2),        app.dyn.rcHeightRear);
pd_compare('FL instant centre y [mm]',          wheel(1).ic(1),     app.front.ic(1));
pd_compare('FL instant centre z [mm]',          wheel(1).ic(2),     app.front.ic(2));
pd_compare('RL instant centre y [mm]',          wheel(3).ic(1),     app.rear.ic(1));
pd_compare('RL instant centre z [mm]',          wheel(3).ic(2),     app.rear.ic(2));
pd_compare('FVSA length, front [mm]',           wheel(1).fvsa,      app.front.fvsa);
pd_compare('FVSA length, rear [mm]',            wheel(3).fvsa,      app.rear.fvsa);
pd_compare('FL side-view IC x [mm]',            wheel(1).svIc(1),   app.front.svIc(1));
pd_compare('FL side-view IC z [mm]',            wheel(1).svIc(2),   app.front.svIc(2));
pd_compare('RL side-view IC x [mm]',            wheel(3).svIc(1),   app.rear.svIc(1));
pd_compare('RL side-view IC z [mm]',            wheel(3).svIc(2),   app.rear.svIc(2));
pd_compare('SVSA length, front [mm]',           wheel(1).svsa,      app.front.svsa);
pd_compare('SVSA length, rear [mm]',            wheel(3).svsa,      app.rear.svsa);
pd_compare('Swing-arm angle, front [deg]',      wheel(1).angle,     app.front.swingArmAngle);
pd_compare('Swing-arm angle, rear [deg]',       wheel(3).angle,     app.rear.swingArmAngle);
pd_compare('Anti-dive [%]',                     antiDive,           app.front.antiDive);
pd_compare('Anti-squat [%]',                    antiSquat,          app.rear.antiSquat);
pd_compare('Pitch centre, station x [mm]',      pc(1),              app.pitch.x);
pd_compare('Pitch centre, above road [mm]',     pcHeight,           app.pitch.height);
pd_compare();

Download: roll_centres.m · example_suspension.m · pd_compare.m. The scripts are MIT licensed.

Further reading

  • W. F. Milliken and D. L. Milliken, Race Car Vehicle Dynamics (SAE, 1995), ch. 17, on suspension geometry.
  • J. C. Dixon, Suspension Geometry and Computation (Wiley, 2009), on instant centres and velocity analysis.
  • T. D. Gillespie, Fundamentals of Vehicle Dynamics (SAE, 1992), ch. 7.
ChapterRoll and pitch centres
PartSuspension
Model as of0.338
Example carthe Formula Student single-car template