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
| Car 1 | Car 2 | |
|---|---|---|
| LiDAR | Hokuyo UST-30LX over USB, at /dev/sensors/hokuyo | Hokuyo UST-10LX over Ethernet, at 192.168.0.10, from the Jetson’s enP8p1s0 at 192.168.0.15/24 |
| Camera | UVC USB webcam at /dev/video0, no ROS image topic | Intel RealSense D435i, with the realsense-ros driver |
| BNO085 IMU | Jetson I2C bus 7, address 0x4A, read through a bridge script | Jetson I2C bus 7, address 0x4A, read through a bridge script. Mount yaw and position not yet measured. |
| Dashboard site ID | rb1 | rb2 |
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.
| Car 1 | Car 2 | |
|---|---|---|
| Speed to ERPM, | 3650 | 4614 |
| Steering to servo, | −0.83 | −0.78 |
| Servo centre, | 0.366 | 0.5904 |
| Servo range | 0.05 to 0.75 | 0.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:
| Car 1 | Car 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.