CARLA physics - SethKitchen/ThreeMojo GitHub Wiki
CARLA physics
extensions/carla/physics/ simulates CARLA's actors: rigid bodies with contacts and friction, wheeled vehicles driven by VehicleControl, and walkers driven by WalkerControl. The physics engine is the port's own. It takes CARLA's field names, defaults and controllers, and it gives the results that mechanics predicts.
The port follows the CARLA source at commit 1360bb9. The records come from LibCarla/source/carla/rpc. The controllers come from the Carla/Vehicle and Carla/Walker sources of CARLA's simulator plugin. No game-engine source code is ported. See CARLA for the frame and the roads.
The rigid-body core lives in Shared physics. Its CARLA import paths remain compatible.
Modules
| Module | What it gives |
|---|---|
quantities |
Torque, Energy, Momentum, Stiffness, DampingRate, CorneringStiffness, Jerk, and units such as REVOLUTION_PER_MINUTE and MILE_PER_HOUR. |
shape |
Shape: sphere, box, capsule, convex hull and static mesh. PhysicsMaterial, Polyhedron, mass properties. |
body |
RigidBody: six degrees of freedom, static, dynamic or kinematic. BodyId, BodyKind. |
collide |
The narrow phase: the contact points of two shapes. |
world |
PhysicsWorld: the fixed step, the broad phase, the contact solver, ray casts and collision events. |
vehicle_physics |
VehiclePhysicsControl and WheelPhysicsControl, with CARLA's defaults. |
vehicle_control |
VehicleControl, VehicleAckermannControl, AckermannControllerSettings, the telemetry records, and CARLA's AckermannController. |
wheeled_vehicle |
WheeledVehicle: the chassis, the ray-cast suspension, the engine, the gearbox and the tires. |
walker |
Walker, WalkerControl and WalkerParameters: a kinematic character controller. |
simulation |
CarlaPhysics: one world with its vehicles and walkers, and carla_transform. |
What the port reuses
- Positions, velocities and directions are
math.vector3.Vector3, in meters. Orientations aremath.quaternion.Quaternion, and inertia tensors aremath.matrix3.Matrix3. - A convex hull comes from
math.convex_hull.ConvexHull. The port merges its coplanar triangles into faces. - The static meshes go into one
math.octree.Octree. A moving shape asks it for the triangles near it, and a ray asks it for the triangles on its path. - A sphere or a capsule against a mesh triangle uses
triangle_sphere_intersectandtriangle_capsule_intersectfrommath.octree. - The nearest points of two segments come from
math.triangle.Line3.closest_points_to_line. The nearest point of a face comes fromTriangle.closest_point_to_point. - A ray is a
math.ray.Ray. A sphere and a mesh triangle use its tests. A box, a hull and a capsule use the port's own tests, because they need the surface normal. - The walker's capsule is a
math.capsule.Capsulewhen it meets a mesh. - A pose from CARLA is a
CarlaTransformfromextensions/carla/transform.mojo.Quaternion.from_matrixturns its matrix into the body's rotation. - The torque, speed and slip units extend
units.si.
core/raycaster is not used. It casts rays through a scene graph of meshes, and the physics world has no scene graph.
Step a world
CarlaPhysics.tick advances the world by CARLA's fixed_delta_seconds. It can cut the step into substeps. Each substep updates every vehicle and walker, and then steps the world. External forces and torques applied before the tick act through every substep. They are cleared after the tick.
The tick-force benchmark retains all-body snapshots after comparing dynamic-only alternatives. Static and kinematic forces are consumed without acceleration. Ghost dynamic bodies still receive their forces.
from extensions.carla.physics.shape import PhysicsMaterial
from extensions.carla.physics.simulation import CarlaPhysics
from units.si import Duration, SECOND
var sim = CarlaPhysics()
var road = sim.add_static_mesh(triangles^, PhysicsMaterial.default())
sim.tick(Duration(0.05, SECOND), 2)
for event in sim.events:
print(event.body.value, event.other.value, event.normal_impulse.x)
Each mesh triangle is solid from its front: the side where (b − a) × (c − a) points. A shape behind a triangle does not touch it.
sim.events holds the collision events of the last tick. An event gives the body, the other body and the impulse that the other body gave. That is the data of CARLA's collision sensor.
Drive a vehicle
add_vehicle spawns a chassis body from a transform, a bounding box and a VehiclePhysicsControl. The bounding box is in the vehicle's frame. The chassis collider starts at the lowest wheel center, so that the wheels and not the box touch the road.
from extensions.carla.physics.vehicle_control import VehicleControl
var car = sim.add_vehicle(transform, box_center, box_extent, physics^)
var control = VehicleControl()
control.throttle = 0.6
sim.apply_vehicle_control(car, control)
sim.tick(Duration(0.05, SECOND), 2)
var telemetry = sim.telemetry(car)
apply_ackermann_control hands the pedals to CARLA's Ackermann controller. A later apply_vehicle_control turns the controller off and resets it, as in CARLA. apply_physics_control replaces the setup, and it must keep the number of wheels.
The world worker can also call WheeledVehicle.update and PhysicsWorld.step directly. update must run before each step.
The order of one vehicle update
- The Ackermann controller, when it is active, writes the control.
- A change of
reverseselects gear −1 or 1 at once. Otherwisemanual_gear_shiftselectsgearat once. This is CARLA'sProcessControl. - Each wheel casts a ray along its suspension axis. The spring force is the spring rate times the compression from full drop, plus the preload, plus a damper.
- The engine locks to the driven wheels while a gear is engaged. The automatic gearbox shifts at
change_up_rpmandchange_down_rpm. - The tires make their forces inside the friction circle. The tires are solved together, with projected Gauss-Seidel.
- The air drag and the downforce act at the center of mass.
- CARLA's rollover behavior damps the spin of a car that rolls past 130 degrees.
The units of CARLA's fields
CARLA's setup records keep lengths in centimeters. Some fields have no written unit. The port chooses these units, and the defaults agree with them: four default springs hold a 1000 kg car at their drop limit.
| Field | Unit in the port | CARLA default |
|---|---|---|
wheel_radius, suspension_max_raise, suspension_max_drop |
Length |
30 cm, 10 cm, 10 cm |
spring_rate |
Stiffness in N/cm |
250 N/cm |
spring_preload |
Force |
50 N |
cornering_stiffness |
CorneringStiffness in N/deg |
1000 N/deg |
max_brake_torque, max_hand_brake_torque, max_torque |
Torque |
1500, 3000, 300 N m |
max_rpm, idle_rpm, change_up_rpm, change_down_rpm |
AngularVelocity in rpm |
5000, 1, 4500, 2000 rpm |
brake_effect |
Torque of engine braking at the largest engine speed |
1 N m |
rev_down_rate |
AngularAcceleration in rpm/s |
600 rpm/s |
steering_curve x |
forward speed in km/h, the unit of CARLA's speed limits | (0, 1), (10, 0.5) |
torque_curve |
rpm against N m, limited to max_torque |
(0, 500), (5000, 500) |
A tire's grip is friction_force_multiplier times the friction of the ground's material. Its load blends an equal share of the weight with the spring force, by wheel_load_ratio.
What a vehicle does
- A brake holds the wheel like static friction, up to its torque over the wheel radius. If that is more than the tire's grip, the wheel locks and slides. With
abs_enabledit does not lock. - A driven wheel whose drive passes its grip spins, up to
max_wheelspin_rotationfaster than the ground. Withtraction_control_enabledit does not spin. - The side force stops the slide across the tire, up to the cornering stiffness times the slip angle.
- Without a throttle, the engine brakes the car a little. Out of gear, the engine revs against
rev_up_moi. - The differential follows
differential_type: each wheel withaffected_by_engine, all wheels withfront_rear_split, the front axle, or the rear axle.
Moving supports
Tire slip and wheel spin use motion relative to the support surface.
At a contact point, each body's velocity is v + omega cross r.
Here, r runs from that body's center of mass to the contact point.
The tire solver subtracts the support velocity from the chassis velocity.
It predicts both bodies' pending gravity, force, torque and damping kicks.
Both bodies' mass and world inertia determine the tire's effective mass. Each incremental impulse updates both predicted states. Wheels on the same support share its prediction. Each final wheel force and its opposite reaction are added once. Static and kinematic supports have no impulse response. A moving kinematic support can supply work through its prescribed motion.
The solver keeps eight passes and the existing tire force limits. It solves one vehicle's wheels at a time. It does not predict later aerodynamic forces or collision impulses. For physical inertia in a closed finite-mass pair without drive, braking does not increase kinetic energy. It preserves total linear and angular momentum within rounding. World damping, external forces and engine torque can change these totals.
A uniform translation leaves the tire result unchanged when the tire directions and relative motion stay the same. Air drag still uses world velocity in still air. Chassis speed telemetry and the controller's speed input remain world quantities. They do not become wheel rolling speed.
The relative-velocity and effective-mass equations follow rigid-body impulse mechanics. See Catto's sequential-impulse derivation, slides 18–24. The focused tests also use independent scalar impulse calculations.
Walk a walker
add_walker spawns a capsule at a location. apply_walker_control takes a direction, a speed and a jump.
from extensions.carla.physics.walker import WalkerControl, WalkerParameters
from units.si import Velocity
var walker = sim.add_walker(Vector3(10, 2, 1), WalkerParameters())
var control = WalkerControl()
control.speed = Velocity(1.4)
sim.apply_walker_control(walker, control)
The walker is a kinematic character controller of the port's own design. CARLA's walker controller gives the semantics: the walker goes along the control's direction at the control's speed, capped at CARLA's 40.96 m/s, and jump jumps. CARLA allows two jumps before the walker lands, so a second press in the air jumps again.
Each step, nine rays look down for a floor: one at the axis and eight around the rim. A floor must be no higher than the step height above the feet, and no steeper than the slope limit.
On the ground, the horizontal velocity moves straight toward the target. The change in one step is at most the acceleration times the step, or the braking deceleration times the step when there is no input. The ground's friction caps both at μg, so a walker on ice starts and stops slowly. The walker follows the floor's plane, so it keeps its horizontal speed on a slope, steps up a curb and steps down a step. In the air, gravity pulls it, and input steers it at a fraction of the ground rate. A walker that falls to the floor snaps onto it and does not bounce.
| Setting | Default | Source |
|---|---|---|
radius, half_height, mass |
0.25 m, 0.9 m, 75 kg | Adult body-size surveys: a stature of 1.6 to 1.9 m, a shoulder breadth of 0.4 to 0.5 m, a mass of 60 to 90 kg. |
max_acceleration |
1 m/s² | Gait studies: 0.5 to 1.5 m/s² when a pedestrian starts to walk. |
braking_deceleration |
2 m/s² | Gait studies: a stop from walking speed in one or two steps. |
max_step_height |
0.3 m | Building codes cap a stair riser at about 0.2 m, and a curb is 0.1 to 0.2 m high. |
max_slope |
35° | Past about 30 to 35 degrees, a slope is a scramble and not a walk. |
jump_speed |
3 m/s | A lift of about 0.46 m, near an adult's standing vertical jump of 0.4 to 0.5 m. |
air_control, skin |
0.2, 1 cm | Design choices: a little steering in a fall, and a gap that keeps resting contacts steady. |
max_speed, jump_max_count |
40.96 m/s, 2 | CARLA's walker controller. |
The capsule is a dynamic body that does not turn. A vehicle or a prop pushes it. The controller then brings the velocity back toward the target.
Rigid bodies and contacts
A body has one shape, placed in the body's frame. A dynamic body gets the mass properties of its solid shape. The inertia of a hull is exact, from signed tetrahedra.
One step of PhysicsWorld does this:
- Gravity, forces and damping change the velocities.
- Sweep and prune pairs the moving shapes. The octree gives the mesh triangles near each one.
- The narrow phase gives the contact points. Two polyhedra use the separating-axis test and clip the incident face. A sphere or a capsule meets a polyhedron at the minimum of its signed distance.
- Sequential impulses solve the contacts, with Coulomb friction in a cone.
- A second pass gives the restitution to each point that hit faster than 1 m/s, Box2D's restitution threshold.
- Split impulses push overlapping bodies apart without adding energy.
- The velocities move the bodies.
A contact can have a gap of up to 2 cm. The solver lets the bodies close the gap and no more, so a fast body stops at a surface and not in it. Two materials use the geometric mean of the frictions and the larger restitution, as Box2D mixes them. The default material is Box2D's: a friction of 0.6 and a restitution of 0. The damping is Box2D's implicit damping, and it is 0 by default. The gravity is 9.8 m/s², standard gravity rounded.
Ray casts
PhysicsWorld.raycast and CarlaPhysics.raycast give the nearest shape on a ray, within a distance. The hit has the body, the point, the surface normal, the distance and the surface material. A ray can ignore one body, such as the body that casts it.
Differences from CARLA
- The physics engine is the port's own. The tire, spring, engine and walker models are the ones described above.
- The shared world has an opt-in sphere/static-mesh continuous mode. It refuses unsupported shapes and unsupported moving-body interactions. Vehicle chassis remain outside this mode. A capsule walker is supported only with its rotation locked. Discrete mode is unchanged.
- The moving-support tire correction changes replay from the port at
af6c253. A car and platform moving together at 10 m/s previously produced about −20000 N of braking with zero relative motion. The corrected force is zero when aerodynamic forces are disabled. This is a correction to the port's model. It does not claim identical CARLA trajectories. SPHERECASTandSHAPECASTwheels cast a ray, asRAYCASTwheels do.lateral_slip_graph,suspension_smoothing,sleep_thresholdandsleep_slope_limitare kept for CARLA's API, and the model does not read them. Bodies do not sleep.WheelPhysicsControl's run-time fieldswheel_index,location,old_locationandvelocityare not part of the setup.WheelStatereports the location and the velocity.- The front wheels turn by one angle. The inner wheel does not turn more than the outer wheel.
- The chassis has no linear damping, because the air drag is modeled on its own.
- There are no joints, so vehicle doors do not swing.
Tests
The four suites tests/test_carla_physics_bodies.mojo, _world.mojo, _vehicle.mojo and _walker.mojo check the behavior against mechanics worked by hand:
- A free fall, the velocities after an elastic and a plastic hit, and the conservation of linear and angular momentum.
- The rebound height from the restitution.
- A box that holds on a slope, and a box that slides with a = g (sin θ − μ cos θ).
- The spring compression that holds the weight, and the stopping distance from a brake torque or from the tire grip.
- The speeds of the gear changes, and the top speed at the engine's limit and against the drag.
- The roll down a slope, with and without the handbrake.
- The yaw rate of a steered car, the Ackermann controller's steps and its convergence, and CARLA's rollover behavior.
- The walker's speed, braking, turning, traction limit, slope, curb, step, wall, landing, jump height and double jump.
tests/test_carla_moving_supports.mojo checks uniform translations,
common rigid rotation and accelerating supports. It also checks coupled
off-center mass, shared supports and separate finite supports.
Other controls check forward and reverse traction, braking energy loss,
momentum conservation and the still-air distinction.
Tire benchmark
bench/carla_moving_supports_bench.mojo measures three four-wheel tire solves.
The support can be static, one moving body or four moving bodies.
Each case has 5000 warm-up solves and 500000 measured solves.
The checksum consumes chassis force and every wheel's speed on each iteration.
Build the same benchmark source against each implementation before comparison. Use the same CPU and balanced variant order when possible. These costs exclude suspension ray casts and the world's contact solve.