Skip to content

Cars

A car tells the simulator how it's built: how heavy it is and where that weight sits, how its axles and wheels are arranged, and how much friction and drag it has. This page describes the car model, the essential properties every car provides, and the car classes in pwdsim. The forward model example sets up a typical car, and the API reference documents every class.

The car model

pwdsim models a car as a rigid body riding on two axles. Its position on the track is given by \(s\), the location of the rear wheel contact point along the track. The front wheel contact follows from the car's geometry.

Car dimensions are measured in a body frame attached to the car:

  • the origin is at the center of the rear axle;
  • the first axis points toward the center of the front axle;
  • the second axis is perpendicular to the first, pointing up, away from the track.

So a center of gravity at \(\mathbf{x}_g = (1\ \mathrm{in}, 0.4\ \mathrm{in})\) sits 1 in ahead of the rear axle and 0.4 in above the line through the axle centers. The body frame pitches with the car as it moves through the curve, so \(\mathbf{x}_g\) is fixed however the car is oriented.

Essential properties

Every car provides these properties, which are everything the simulation needs to know about it:

Property Symbol Description SI unit Used by
cg \(\mathbf{x}_g\) Center of gravity relative to the rear axle, in the body frame m all
wheelbase \(w\) Distance between the axle centers m all
front_offset \(d\) Distance from the rear axle to the front of the car m start and finish
mass \(M\) Total mass, including the wheels kg gravity, translation
body_inertia \(I_b\) Pitching moment of inertia about the center of gravity kg m² body_rotation
n_rear_wheels, n_front_wheels \(n_r\), \(n_f\) Number of wheels on the track at each axle - wheel_spin
rear_wheel_inertia, front_wheel_inertia \(I_r\), \(I_f\) Moment of inertia of each wheel about its axle kg m² wheel_spin
rear_wheel_radius, front_wheel_radius \(r_r\), \(r_f\) Wheel radius m all
rear_axle_radius, front_axle_radius \(a_r\), \(a_f\) Axle radius m axle_friction
rear_axle_friction, front_axle_friction \(\mu_r\), \(\mu_f\) Wheel to axle friction coefficient - axle_friction
frontal_area \(A\) Cross-sectional area facing the air m² drag
drag_coefficient \(C_d\) Aerodynamic drag coefficient - drag
rolling_friction \(c_r\) Rolling friction coefficient of the wheels on the track - rolling_friction

The last column lists the physics terms that use each property. The geometry (cg, wheelbase, and the wheel radii) locates the car on the track, so every term depends on it.

A few notes:

  • Front offset. The simulation positions a car at the start, and decides when it finishes, using the front of the car. Only the distance from the rear axle to the front matters, not the total length of the car.
  • Wheel counts. Cars normally have two wheels on each axle. A popular trick is to raise one front wheel so it never touches the track, giving \(n_f = 1\). The wheel inertias are per wheel, so the counts scale the energy of the spinning wheels. Friction depends on the total load on each axle, which doesn't change with the number of wheels sharing it.
  • Axle friction. Friction between a wheel and its axle acts at the axle radius, so it resists the car with a force of \(\mu N a / r\) at the wheel rim, for a normal load \(N\). Thin axles and large wheels reduce it.

Values are always SI inside pwdsim. To work in customary units, multiply by the constants in pwdsim.units (5 * OUNCE, 4.375 * INCH) and divide to convert back.

Typical values

For a legal car built from the standard BSA kit:

Property Typical value Notes
mass 5 oz (0.142 kg) the usual race limit
wheelbase 4 3/8 in kit axle slot spacing
front_offset 6 1/8 in 7 in block with the rear slot 7/8 in from the back
cg (1.0 in, 0.4 in) fast cars often put the center of gravity 3/4 to 1 in ahead of the rear axle
body_inertia \(3.9 \times 10^{-4}\) kg m² uniform 7 in by 1.25 in block
wheel radius 0.595 in 95.0 mm circumference
wheel inertia \(3.4 \times 10^{-7}\) kg m² 2.6 g wheel, \(I \approx 0.58\, m r^2\)
axle radius 0.0435 in 0.087 in diameter axles
axle friction 0.1 lubricated; about 0.24 dry
rolling_friction 0.002
drag_coefficient 0.4
frontal_area 0.0014 m² uncut block

Most of these come from the Wikibooks derby physics page. They're starting points, not measurements of any particular car.

Car classes

Car

Car is the abstract base class that defines the interface. Each essential property is an abstract, read-only property. A concrete car class decides where the values come from, whether that's stored values, measurements, or calculations from a detailed description of the car. The simulation only ever uses these properties, so it works with any car class.

Car is also a torch.nn.Module. The parameters of a concrete car, whatever they are, are available from car.parameters() to hand to a torch optimizer. Because the properties are computed with torch operations from those parameters, gradients of race times flow back through them.

Car provides two helper methods:

  • summary returns the current property values as plain numbers, for display.
  • check raises an error if the properties are not physically meaningful: for example a negative mass, axles larger than the wheels, or a front axle ahead of the front of the car.

SimpleCar

SimpleCar is the simplest car: you give it every essential property directly, and it stores each one (except the wheel counts) as a float64 torch.nn.Parameter. The parameters are named after the properties with a trailing underscore, so the mass property returns the mass_ parameter.

import pwdsim
from pwdsim.units import GRAM, INCH, OUNCE

wheel_radius = 0.595 * INCH
wheel_inertia = 0.58 * (2.6 * GRAM) * wheel_radius**2

car = pwdsim.SimpleCar(
    cg=(1.0 * INCH, 0.4 * INCH),
    wheelbase=4.375 * INCH,
    front_offset=6.125 * INCH,
    mass=5 * OUNCE,
    body_inertia=3.9e-4,
    rear_wheel_inertia=wheel_inertia,
    front_wheel_inertia=wheel_inertia,
    rear_wheel_radius=wheel_radius,
    front_wheel_radius=wheel_radius,
    rear_axle_radius=0.0435 * INCH,
    front_axle_radius=0.0435 * INCH,
    rear_axle_friction=0.1,
    front_axle_friction=0.1,
    frontal_area=0.0014,
    drag_coefficient=0.4,
    rolling_friction=0.002,
    n_front_wheels=1,  # raise a front wheel
)

Every parameter is trainable by default. Most optimization studies only vary a few of them, so turn off the gradient for the ones that should stay fixed:

for parameter in car.parameters():
    parameter.requires_grad_(False)
car.cg_.requires_grad_(True)  # only optimize the center of gravity

SimpleCar doesn't enforce any bounds while optimizing. The optimization routines will be responsible for keeping the parameters physical and within the race rules.

GeometricCar

GeometricCar computes the mass, center of gravity, moment of inertia, and front offset from a description of the car body: a B-spline side profile cut from the block, and weights and pockets inside it. Any of those can be given as a number instead. See Car geometry.

Writing a new car class

To describe a car in another way, subclass Car, store the fundamental parameters, and implement each essential property as a calculation from them.

For small variations it's often easier to extend SimpleCar. For example, here's a car whose wheels and axles are the same front and rear. It shares one parameter between each front and rear pair, so an optimizer can't make them differ:

from pwdsim import SimpleCar


class MatchedWheelCar(SimpleCar):
    """A car with identical front and rear wheels and axles."""

    def __init__(
        self, wheel_radius, wheel_inertia, axle_radius, axle_friction, **kwargs
    ):
        super().__init__(
            rear_wheel_radius=wheel_radius,
            front_wheel_radius=wheel_radius,
            rear_wheel_inertia=wheel_inertia,
            front_wheel_inertia=wheel_inertia,
            rear_axle_radius=axle_radius,
            front_axle_radius=axle_radius,
            rear_axle_friction=axle_friction,
            front_axle_friction=axle_friction,
            **kwargs,
        )
        # Share one parameter between the front and rear wheels
        self.front_wheel_radius_ = self.rear_wheel_radius_
        self.front_wheel_inertia_ = self.rear_wheel_inertia_
        self.front_axle_radius_ = self.rear_axle_radius_
        self.front_axle_friction_ = self.rear_axle_friction_

A car built from its shape would instead derive its mass, center of gravity, inertia, and frontal area from a spline profile of the body and the density of the wood and weights.