PaceDynamics Documentation Open the app
Contents

Suspension

Kinematics and compliance

What a K&C rig measures: the springs as the wheel sees them, where it stops, and how far the links give under the tyre's forces.

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

A kinematics and compliance rig clamps the body and moves the pads the wheels stand on. What it reads splits in two. The kinematics are what the links do as the wheel moves: the gains on Wishbones and struts. The compliance is what they do as they bend under the tyre's forces. Between the two sit the springs, read as the wheel sees them. The Kinematics tool is that rig, and this page follows its three readings: the wheel standing, the spring at the wheel, and the give.

The wheel standing

The tyre carries the corner's whole load. The springs carry it less the wheel's own weight, which stands on the tyre directly. The tyre is a spring as well, in series with the suspension, and squashes under its load to the radius it stands on:

Show the equations with
Fs=Fz−mug,R=R0−Fzkt(1)
Fs=691.6−8⋅9.81=613.1 N,R=228−691.6110=221.71 mm(1)
Fz
the wheel's static load
mu
the unsprung mass at one corner: the wheel, upright, brake and half the links
R0,kt
the tyre's free radius and its vertical rate

A tyre can push on the road and cannot pull. Lighten a wheel past its static load and the spring extends; lighten it to nothing and it lifts.

The spring at the wheel

On a decoupled axle the springs are one heave element between the two rockers (Wishbones and struts). Each wheel moves it by h(t), half its change in length, and its force is a preload and a rate on the sum of the two:

Show the equations with
Fe=P+k(hL+hR),P=2FshL′+hR′,k=2kw(hL′+hR′)2(2)
P=2⋅613.12⋅0.6055=1013 N,k=2⋅34.93(2⋅0.6055)2=47.64 N/mm(2)
hL,hR
each wheel's share of the element's travel, positive compressed; h′ is its change per millimetre of wheel travel
P
the element's force at the static ride: what holds the axle up
kw
the element's rate at the wheel, 34.93 N/mm (the Rates readout)

The force at a wheel is Feh′, by virtual work. Move both wheels together and its slope, the wheel rate, has two terms: the rate through the ratio, and the preload working on a ratio that changes:

Show the equations with
dFdt=kw+Fsh′′h′(3)
dFdt=34.93+(−2.08)=32.84 N/mm(3)
h′′
the change of the ratio per millimetre of travel; negative here, the ratio falling in bump

The template's ratio falls in bump, so its loaded spring is 2.08 N/mm softer at the wheel than the element's rate alone. A rising ratio would stiffen it. With the tyre in series, the rate the road sees is

Show the equations with
kride=(1dF/dt+1kt)−1(4)
kride=(132.84+1110)−1=25.29 N/mm(4)

The rear reads 36.28 and 27.28 N/mm.

Where the wheel stops

The element sits on its seats and only pushes. Take load off both wheels and it extends until its force is spent; at that travel, hL+hR=−P/k, the spring leaves its seat and the wheel hangs below it on nothing but the links and its own weight. On the template that is −17.0 mm at the front and −17.5 mm at the rear.

The heave element’s force at one wheel against wheel travel, both wheels moving together, front and rear0500100015002000-20-1001020304050Wheel travel, + bump (mm)Force at the wheel (N)the spring leaves its seatstanding
FrontRear
Fig. 1
Each wheel’s spring force falls to nothing 17.0 mm below the static ride at the front, and flattens in bump as the ratio falls.
Measured

A damper stated with its length and stroke stops the wheel instead, at full extension in droop and closed in bump. A spring that runs out of rate past a falling ratio may give the wheel no more force however far it moves: then no travel holds the load, and the Forces tool stands the car on a stop.

Compliance

No link is rigid. Each one, with the rod ends, brackets and upright in its load path, stretches under its force by the force over its rate, and the upright moves to where the longer links put it. The app does what a rig's own solve does: it takes the links' forces at the pose (Forces in the links), gives each its stretch, solves the corner again with the links that long, and repeats until the wheel stops moving. The lower wishbone is held at its angle throughout: on the rig the springs do not move.

Δℓi=fiki(5)
fi
the force in link i, positive in tension
ki
its rate, link and bushing in series

It does that about the standing load, with a kilonewton of side force either way, then of fore-and-aft force, then 100 N·m of aligning torque, and reads the rig's table off the differences. With every link a 20 × 2 mm steel tube at a fifth of its bare rate, as the button "From 20×2 steel tubes" sets them:

per kN, or per 100 N·m front rear
toe, side force [°] −0.0330 0.0392
camber, side force [°] −0.0189 −0.0246
toe, fore-and-aft force [°] 0.0323 0.0102
toe, aligning torque [°] −0.0968 −0.0588
patch sideways [mm] 0.099 0.129
patch fore and aft [mm] 0.252 0.221

A side force is positive outboard and toe positive in; a fore-and-aft force is positive forward. Hundredths of a degree per kilonewton read as nothing in a table, but a loaded tyre makes more than a kilonewton. In a corner the outside tyres' side forces point inboard. Under them the template's front wheels toe in, 0.033° per kilonewton, and steer further into the turn; its rear wheels toe out, 0.039° per kilonewton, and steer the rear out of it. Both add oversteer.

What the model assumes

  • The rig holds the body; the elements and the lower wishbone stand where the static ride puts them.
  • A link's stretch is its force over its rate. Bushings are rigid unless given, and a rate left blank is rigid.
  • The lower wishbone's two legs take its reaction as two axial forces, the least-squares reading of a rigid arm; its bending is left out (Forces in the links).
  • Each corner is probed alone, with the rack held. A rack rate couples the two front wheels; this page's table has none.

In the app

Kinematics, in the readout rail: "Tyre" gives the wheel load, the ride rate and the wheel rate at load; "Rates" gives the heave element at the wheel and the static wheel load.

Kinematics, on the Elements tab, under "Compliance", takes each link's rate; "From 20×2 steel tubes" fills them as on this page. Under "Heave element" it takes the element's damper and its stops. Kinematics › Analysis › Compliance draws the rig's sweeps and its table.

Check it yourself

The script works out the standing wheel, the heave element from the exact geometry, and the compliance by the same solve-stretch-solve loop. It agrees with the app to a part in a thousand on the element's rows and to a part in ten thousand on the compliance. The app reads the element's ratio off a table at 2 mm steps; the script reads it off the geometry half a millimetre either side. The app stops its loop when the wheel centre moves less than 10⁻⁵ mm and lets the pushrod stretch too, a turn of the rocker the script leaves out.

kinematics_compliance.mwith example_suspension.m, pd_corner.m, pd_upright.m, pd_metrics.m, pd_heave.m, pd_rocker.m, pd_members.m, pd_compare.mClose: agrees to the tolerance the page states
>> kinematics_compliance

  Front, the wheel standing (exact)

  Quantity                                This script   PaceDynamics       Diff
  -----------------------------------------------------------------------------
  Wheel load [N]                              691.605        691.605         0% 
  On the springs [N]                          613.125        613.125         0% 
  Standing radius [mm]                        221.713        221.713         0% 
  Front, the heave element (to 1e-3)
  Element rate [N/mm]                          47.644        47.6426    0.0029% 
  Element preload [N]                         1012.69        1012.67    0.0015% 
  To its free length [mm]                    -21.2553       -21.2556    0.0015% 
  Wheel rate at load [N/mm]                   32.8335        32.8442     0.033% 
  Ride rate, tyre in series [N/mm]             25.286        25.2923     0.025% 
  Wheel hangs below [mm]                     -17.0191       -17.0171     0.012% 
  Front, compliance "From tubes" (to 1e-4)
  Toe per side force [deg/kN]              -0.0329841     -0.0329841   0.00018% 
  Camber per side force [deg/kN]            -0.018907     -0.0189071   0.00011% 
  Toe per fore-aft force [deg/kN]           0.0322666      0.0322665   0.00013% 
  Toe per aligning torque [/100 N*m]       -0.0968403     -0.0968405   0.00025% 
  Patch sideways [mm/kN]                    0.0986941      0.0986942   0.00013% 
  Patch fore-aft [mm/kN]                     0.251893       0.251893   2.6e-05% 

  Rear, the wheel standing (exact)
  Wheel load [N]                              779.895        779.895         0% 
  On the springs [N]                          701.415        701.415         0% 
  Standing radius [mm]                         220.91         220.91         0% 
  Rear, the heave element (to 1e-3)
  Element rate [N/mm]                         54.4504        54.4486    0.0033% 
  Element preload [N]                         1176.09        1176.07    0.0017% 
  To its free length [mm]                    -21.5993       -21.5996    0.0017% 
  Wheel rate at load [N/mm]                   36.2665        36.2809      0.04% 
  Ride rate, tyre in series [N/mm]            27.2743        27.2825      0.03% 
  Wheel hangs below [mm]                     -17.5211       -17.5197    0.0082% 
  Rear, compliance "From tubes" (to 1e-4)
  Toe per side force [deg/kN]               0.0392474      0.0392475   0.00012% 
  Camber per side force [deg/kN]           -0.0245814     -0.0245814    0.0001% 
  Toe per fore-aft force [deg/kN]           0.0101732      0.0101733    0.0011% 
  Toe per aligning torque [/100 N*m]       -0.0588341     -0.0588341   2.3e-06% 
  Patch sideways [mm/kN]                     0.128725       0.128725   0.00011% 
  Patch fore-aft [mm/kN]                      0.22098        0.22098   2.9e-05% 

  All 30 quantities agree with PaceDynamics (largest difference 0.04 %).
The script
%% Kinematics and compliance: what a K&C rig measures, from the springs and the links
%
% Companion to the documentation page "Kinematics and compliance". Runs unchanged in GNU
% Octave (7 or later) and MATLAB. Edit the car in example_suspension.m, or type your own
% from the Kinematics tool, and run:
%
%   >> kinematics_compliance
%
% A K&C rig clamps the body and moves the wheel pads. What it reads splits in two: the
% kinematics, what the links do as the wheel moves, and the compliance, what they do as
% they bend under the tyre's forces. This script takes three of its readings.
%
% 1. The wheel standing. Its load, less the wheel's own weight, is what the springs hold;
%    the tyre squashes under the whole load, to the radius it stands on.
%
% 2. The heave element as the rig takes it. Each side moves the element by h(t), half the
%    change in its length (pd_heave), and the element's force is P + k s with s the sum
%    of both sides. At the static ride the springs hold the car up, so
%        P = (both wheels' spring load) / (h'_L + h'_R)      the element's preload,
%        k = 2 k_wheel / (h'_L + h'_R)^2                     its rate, from the rate at the wheel,
%    and one wheel's rate with both moving together is
%        dF/dt = k_wheel + (spring load) * h'' / h',
%    the second term because a rising motion ratio stiffens a loaded spring. The tyre is
%    in series with that. Where P + k s reaches zero the spring leaves its seat, and
%    below that travel the wheel hangs.
%
% 3. Compliance, with every link a 20 x 2 mm steel tube ("From tubes" in the app): each
%    link stretches by its force over its rate (pd_members gives the forces), the corner
%    is solved again with the links that long, the lower wishbone held at its angle, and
%    that is repeated until the wheel stops moving. Taken about the standing load with a
%    kilonewton either way, it gives the rig's table: toe and camber per kilonewton of
%    side force, and so on.
%
% Agreement. The app takes h' and h'' off a table of the element's travel at 2 mm steps;
% the script takes them off the exact geometry, 0.5 mm either side. The two agree to a
% few parts in ten thousand. The compliance rows agree to about 1e-5: the app stops
% iterating when the wheel centre moves less than 1e-5 mm, and it lets the pushrod
% stretch too (a turn of the rocker this script leaves out).
%
% Part of the PaceDynamics documentation, https://pacedyn.dev/docs
% SPDX-License-Identifier: MIT

[car, app] = example_suspension();
g = 9.81;
names = {'front', 'rear'};
frac = [car.frontMassFrac, 1 - car.frontMassFrac];

for ax = 1:2
  c = car.(names{ax});
  a = pd_corner(c);
  K = app.kc.(names{ax});

  %% 1. The wheel standing
  r.staticLoad = car.massTotal * g * frac(ax) / 2;                 % N
  r.springLoad = r.staticLoad - car.unsprungPerCorner * g;          % N
  r.standingRadius = car.tyreFreeRadius - r.staticLoad / car.tyreRate;   % mm

  %% 2. The heave element
  L0 = pd_heave(a, 0);
  share = @(t) (L0 - pd_heave(a, t)) / 2;                           % one side's share, + = compressed
  d = 0.5;
  h1 = (share(d) - share(-d)) / (2 * d);                            % h'
  h2 = (share(d) - 2 * share(0) + share(-d)) / d^2;                 % h''
  ch = app.wishbones.heave.(names{ax}).chord;                       % the rate at the wheel, as the
  mr = (pd_heave(a, ch(1)) - pd_heave(a, ch(2))) / (ch(2) - ch(1)); % Rates readout takes it
  kWheel = c.heaveRate * mr^2 / 2;
  r.elementRate = 2 * kWheel / (2 * h1)^2;
  r.preload = 2 * r.springLoad / (2 * h1);
  r.free = -r.preload / r.elementRate;
  r.wheelRateAtLoad = kWheel + r.springLoad * h2 / h1;
  r.rideRate = 1 / (1 / r.wheelRateAtLoad + 1 / car.tyreRate);
  r.droop = fzero(@(t) 2 * share(t) - r.free, [-40, 0]);

  %% 3. Compliance, links "From tubes"
  rate = c.linkRates([3, 4, 1, 2, 5]);                              % lower front, lower rear, upper front, upper rear, tie
  probes = {'lateral', [0, 1000, 0], 0; 'longitudinal', [1000, 0, 0], 0; 'aligning', [0, 0, 0], 1e5};
  for i = 1:3
    for sgn = [-1, 1]
      F = [0, 0, r.staticLoad] + sgn * probes{i, 2};              % N, corner frame: y outboard
      M = [0, 0, sgn * probes{i, 3}];                               % N*mm about the corner's vertical
      stretch = zeros(1, 5);
      for it = 1:20
        p = pd_upright(a, 'beta', 0, 0, stretch);
        m = pd_metrics(p.at(a.ubj), p.at(a.lbj), p.at(a.C), p.n, p.at(a.tieOuter), c.surveyRadius);
        geo = struct('ucaFront', a.ucaFront, 'ucaRear', a.ucaRear, 'lcaFront', a.lcaFront, 'lcaRear', a.lcaRear, ...
                     'uprightUpper', p.at(a.ubj), 'uprightLower', p.at(a.lbj), 'tieInner', a.tieInner, ...
                     'tieOuter', p.at(a.tieOuter), 'wheelCenter', p.at(a.C), 'contactPatch', m.patch, ...
                     'pushrodOuter', a.pushrodOuter, 'rockerToPushrod', a.rockerToPushrod);
        f = pd_members(geo, F, M);
        next = [f.lcaFront, f.lcaRear, f.ucaFront, f.ucaRear, f.tieRod] ./ rate;   % mm
        if max(abs(next - stretch)) < 1e-12, break; end
        stretch = next;
      end
      got(i, (sgn + 3) / 2) = m;                                    % 1 = minus, 2 = plus
    end
  end
  half = @(i, field, k) (got(i, 2).(field)(k) - got(i, 1).(field)(k)) / 2;
  r.toePerLateral      = half(1, 'toe', 1);                         % deg/kN
  r.camberPerLateral   = half(1, 'camber', 1);
  r.toePerLongitudinal = half(2, 'toe', 1);
  r.toePerAligning     = half(3, 'toe', 1);                         % deg per 100 N*m
  r.lateralPatch       = half(1, 'patch', 2);                       % mm/kN
  r.longitudinalPatch  = half(2, 'patch', 1);
  out.(names{ax}) = r;
end

%% Against the app
W = {'Front', 'Rear'};
for ax = 1:2
  r = out.(names{ax});  K = app.kc.(names{ax});  T = app.kc.tubes.(names{ax});
  fprintf('\n  %s, the wheel standing (exact)\n', W{ax});
  pd_compare('Wheel load [N]',                     r.staticLoad,     K.staticLoad);
  pd_compare('On the springs [N]',                 r.springLoad,     K.springLoad);
  pd_compare('Standing radius [mm]',               r.standingRadius, K.standingRadius);
  fprintf('  %s, the heave element (to 1e-3)\n', W{ax});
  pd_compare('Element rate [N/mm]',                r.elementRate,    K.elementRate, 1e-3);
  pd_compare('Element preload [N]',                r.preload,        K.preload, 1e-3);
  pd_compare('To its free length [mm]',            r.free,           K.free, 1e-3);
  pd_compare('Wheel rate at load [N/mm]',          r.wheelRateAtLoad, K.wheelRateAtLoad, 1e-3);
  pd_compare('Ride rate, tyre in series [N/mm]',   r.rideRate,       K.rideRate, 1e-3);
  pd_compare('Wheel hangs below [mm]',             r.droop,          K.droop, 1e-3);
  fprintf('  %s, compliance "From tubes" (to 1e-4)\n', W{ax});
  pd_compare('Toe per side force [deg/kN]',        r.toePerLateral,      T.toePerLateral, 1e-4);
  pd_compare('Camber per side force [deg/kN]',     r.camberPerLateral,   T.camberPerLateral, 1e-4);
  pd_compare('Toe per fore-aft force [deg/kN]',    r.toePerLongitudinal, T.toePerLongitudinal, 1e-4);
  pd_compare('Toe per aligning torque [/100 N*m]', r.toePerAligning,     T.toePerAligning, 1e-4);
  pd_compare('Patch sideways [mm/kN]',             r.lateralPatch,       T.lateralPatch, 1e-4);
  pd_compare('Patch fore-aft [mm/kN]',             r.longitudinalPatch,  T.longitudinalPatch, 1e-4);
end
pd_compare();

Download: kinematics_compliance.m · example_suspension.m · pd_corner.m · pd_upright.m · pd_metrics.m · pd_heave.m · pd_rocker.m · pd_members.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. 18, on wheel rates.
  • J. C. Dixon, Suspension Geometry and Computation (Wiley, 2009), on compliance steer.
  • J. Reimpell, H. Stoll and J. W. Betzler, The Automotive Chassis: Engineering Principles (Butterworth-Heinemann, 2001), on elastokinematics.
ChapterKinematics and compliance
PartSuspension
Model as of0.338
Example carthe Formula Student single-car template