Docs Get started Workspace repo
Docs menu

The cars

The hardware on each car, how the two differ, and the measured geometry and calibration every controller relies on.

Both cars are 1/10-scale Traxxas Ford Fiesta ST Rally VXL chassis (74276-4) with the same computer, motor controller and gamepad. They differ in how the LiDAR connects, in the camera, and in each car’s own steering and speed calibration. Both run the same workspace, and each car’s values are kept in its car profile.

Hardware

Part Model Role
Computer NVIDIA Jetson Orin Nano Super Developer Kit, 8 GB, JetPack 7.2, Ubuntu 24.04, ROS 2 Jazzy Runs every node
Motor controller VESC, firmware 7.0, USB at /dev/sensors/vesc Drives the motor and the steering servo, reports wheel speed
LiDAR Hokuyo UST series, about 40 scans per second Distance to everything around the car
IMU The VESC’s built-in IMU and a BNO085 on the Jetson’s I2C bus 7, on both cars. The VESC IMU reads all zeros if it is turned off in VESC Tool. Yaw rate and acceleration for imu_bridge
Gamepad Logitech F710, XInput mode Manual driving, and the LB button every driving mode requires

The F710 must be in XInput mode, set by the switch on its back. In DirectInput mode the Jetson creates no joystick device, and no error appears anywhere.

F710 control Index in /joy Use
LB button 4 Hold to drive, in every mode
Left stick, vertical axis 1 Speed in manual driving
Right stick, horizontal axis 3 Steering in manual driving

Car 1 and car 2

Sensors
Car 1Car 2
LiDARHokuyo UST-30LX over USB, at /dev/sensors/hokuyoHokuyo UST-10LX over Ethernet, at 192.168.0.10, from the Jetson’s enP8p1s0 at 192.168.0.15/24
CameraUVC USB webcam at /dev/video0, no ROS image topicIntel RealSense D435i, with the realsense-ros driver
BNO085 IMUJetson I2C bus 7, address 0x4A, read through a bridge scriptJetson I2C bus 7, address 0x4A, read through a bridge script. Mount yaw and position not yet measured.
Dashboard site IDrb1rb2

Car profiles

A car profile holds everything that differs between the cars, so the driving code and its tuning stay shared. Each car’s profile is a folder under src/racerbot_car/cars/: rb1/Car 1 for car 1 and rb2/Car 2 for car 2.

File in cars/<id>/ Holds
car.yaml The LiDAR’s connection, the camera, the dashboard site and the measured full-lock steering angles
calibration/vesc.yaml The VESC’s speed gains, servo map and servo range
calibration/sensors.yaml The LiDAR’s serial port or IP address
calibration/joy_teleop.yaml The full-stick steering angle for manual driving
calibration/gap_follow.yaml, pure_pursuit.yaml, neural_racer.yaml Each controller’s steering limit
calibration/odom_calibration.yaml The calibration wizard’s starting values
imu/ The IMU mount rotations and calibration
tuning/ Per-car algorithm overrides, each with a reason. Neither car has one.

A Jetson finds its car from its serial number in machines.yamlGitHub. On a development computer, or for the simulator, set RACERBOT_CAR=rb1 or RACERBOT_CAR=rb2 instead. On a registered Jetson, leave RACERBOT_CAR unset: a value that names the other car stops every launch with CarMismatchError, rather than loading the wrong car’s calibration. ros2 run racerbot_doctor doctor prints the car it found on its first line.

Every launch loads the shared configuration first, then the car’s calibration/ file, then its tuning/ file if there is one. A tuning file can never relax a safety setting: guards.pyGitHub refuses an overlay that turns off the deadman, the TTC brake or the swept-clearance check, changes the deadman button, raises a speed cap or a timeout, or lowers a clearance, and the launch stops with TuningOverlayError. The full description is docs/car-profiles.mdGitHub, and every difference between the cars, with the file that holds it, is listed in docs/car1-vs-car2.mdGitHub. test_car_profiles.pyGitHub checks that each car’s LiDAR, camera, dashboard and steering settings still match its profile and the hardware reference.

Steering and speed calibration

The VESC turns the speed and steering in each drive command into a motor speed and a servo position. Both conversions are straight lines, set in each car’s calibration/vesc.yaml (car 1Car 1, car 2Car 2):

is the speed in m/s and ERPM is the motor’s electrical revolutions per minute. is the steering angle in radians and is the servo position, from 0 to 1. Lecture 7 (notes) derives the same two lines for dead reckoning.

calibration/vesc.yaml
Car 1Car 2
Speed to ERPM, 36504614
Steering to servo, −0.83−0.78
Servo centre, 0.3660.5904
Servo range0.05 to 0.750.15 to 0.95
Odometry ERPM gain (vesc_to_odom_node)−4040−4447.98

The servo centre is the position at which the car drives straight, so /commands/servo/position reads 0.366 on car 1 and 0.5904 on car 2 while the wheels point ahead. The odometry gain is negative on purpose: vesc_to_odom.cpp negates the ERPM it reads, and the negative gain cancels that so /odom reads forward as positive.

Steering limits

A car can only steer as far as its servo range allows, so each car has three limits, each smaller than the one before:

Steering limits
Car 1Car 2
Full stick, manual (calibration/joy_teleop.yaml)0.38 rad (21.8°)0.436 rad (25.0°)
Autonomy, max_steering_angle (calibration/)0.33 rad (18.9°)0.349 rad (20.0°)
Race planner, profile_max_steering_angle (shared)0.30 rad (17.2°)0.30 rad (17.2°)

The planner plans with less steering than the controller may use, so the controller has steering left over on the tightest planned corner. src/racerbot_launch/test/test_steering_limits_fit_servo.pyGitHub fails if any limit would be clipped by the servo range, or if the planner’s limit is not below the controller’s.

The tightest turn at each limit follows from the kinematic bicycle model in Lecture 6 (notes). With wheelbase , a steering angle drives a circle of radius

about the rear axle. With m, car 1’s autonomy limit gives m, car 2’s gives 0.99 m, and the planner’s 0.30 rad gives 1.16 m on both cars.

Geometry

Every pose, scan overlay and collision check is measured from base_link, the centre of the rear axle. Car 2 was tape-measured on August 24, 2026. Car 1 uses the same numbers and has not been measured separately.

Quantity Value Notes
Wheelbase 0.36 m Front axle centre to rear axle centre. The Traxxas figure, 0.324 m, is wrong for these cars.
Width, tire to tire 0.30 m Measured
car_width 0.31 m The measured width plus 5 mm of padding each side
car_length 0.58 m Not measured. It pads an assumed overall length.
LiDAR position 0.26 m ahead of base_link, 0.11 m up 0.10 m behind the front axle

car_width and car_length describe a rectangle drawn larger than the car, so a clearance of zero means the padding is used up, not that the body has touched. Each number appears in several files, listed in docs/hardware-reference.mdGitHub under “Where each number lives”. Change all the files in a row together, or two parts of the stack model different cars.

Device names

udev rules on each car give the USB devices fixed names, so the configuration never depends on which /dev/ttyACM* number a device gets.

Link Device USB ID
/dev/sensors/vesc VESC (STMicroelectronics virtual COM port) 0483:5740
/dev/sensors/hokuyo Hokuyo UST-30LX (car 1 only) 15d1:0000

The user that runs bringup needs to be in the input group to read the gamepad, and in the video group on car 1 to open the webcam. The pre-run checklist checks both.

Checked against workspace commit 556284c.