racecar-neo-libraryNEORACER DOCS
These docs are public and open source.Edit on GitHub
SOFTWARE / RACECAR-NEO-LIBRARY

THE PYTHON LIBRARY.

racecar-neo-library is the Python module every student writes against. Five modules under an rc.* namespace, with identical signatures in the NeoRacer Playground browser simulator and on the physical car. The same file works in both places.

Sim ↔ car portable0 modulesset_start_update + goPyodide-compatible
0 Hz
LiDAR scan rate
0
~, on the car (720 in sim)
get_samples() length
0 Hz
IMU sample rate
0
× 480, RGB (no depth)
Camera frame width
FIG. A / SIM TO CAR, ONE TRUNK
//Playground (sim)
Three.js
WebGL physics + render
Web Worker
postMessage sensor data
Pyodide
CPython, WASM-compiled
Playground Sim
simulated LiDAR · camera · IMU
//Your Python
ONE API
rc.*
lidar · camera · imu
drive · controller
in browser
on car
The same student script runs in both runtimes unchanged.
//NeoRacer (car)
racecar-neo-library
Python shim, ROS 2 client
ROS 2 Humble
topics · services · params
Jetson Orin Nano
Ubuntu · 67 TOPS
Hardware
LiDAR · camera · IMU · motors
The red trunk is the rc.* API you write against. Whichever side you run on, the API contract is identical: same names, same shapes, same units.
01 / MODULES AT A GLANCE

THE FIVE MODULES.

Click any module to see the methods it exposes. Every behaviour you'll ever ship (, wall follow, gap follower, end-to-end RL) is some combination of these five.

rc.lidar
The distance map you steer by.
rc.lidar.get_samples()The latest scan in centimetres: ~1440 samples on the car, 720 in the sim. Index 0 is dead ahead; the rear wedge reads 0.
rc_utils.get_lidar_average_distance(scan, angle)In racecar_utils. Mean range over a small angle window, for gap finding that holds up against noisy samples.
rc_utils.get_lidar_closest_point(scan)In racecar_utils. The (angle, distance) of the nearest return.
02 / WHAT THE SCAN LOOKS LIKE

THE LIDAR SCAN.

0° FORWARDlidar_link000° · 30 Hz

The sweep on the left is what the scanner is doing while you read this. Each tick mark is one of the samples get_samples() hands you. The full coordinate frame and array layout live on the LiDAR hardware page.

  • Index 0 is dead ahead; get_num_samples() // 2 is directly behind. The array sweeps clockwise: 720 samples in the sim, ~1440 on the car, so always index relative to the length.
  • Samples come back in centimetres. A 0 means no return inside the range gate.
  • One scan arrives every ~33 ms. Reading it inside update() is fast enough for any you'll write.
FIG. B / THE LIFECYCLE
start()update()EVERY FRAMEPER-TICKget_delta_time()READ rc.lidar / camera / imu / controllerWRITE rc.drive.set_speed_angle(...)
start() runs once when the script loads. update() runs every frame after that. You wire both in with rc.set_start_update(start, update), then call rc.go() at the bottom of the file and the runtime handles the rest.
03 / HELLO WORLD, DRIVE A SQUARE

DRIVE A SQUARE.

The smallest interesting program. The Python tab is the file you save. The other two tabs show how to run it in the Playground or on the car.

python
import racecar_core

rc = racecar_core.create_racecar()

SIDE_TIME = 1.0   # seconds per straight leg
TURN_TIME = 0.7   # seconds per 90-degree turn
SPEED = 0.25      # 25% throttle
TURN = 1.0        # full lock

timer = 0.0
leg = 0           # 8 legs: even legs drive straight, odd legs turn

def start():
    global timer, leg
    timer, leg = 0.0, 0
    rc.drive.stop()

def update():
    global timer, leg
    if leg >= 8:                     # four straights + four turns
        rc.drive.stop()
        return
    timer += rc.get_delta_time()     # seconds since the last frame
    if leg % 2 == 0:
        rc.drive.set_speed_angle(SPEED, 0)
        if timer > SIDE_TIME:
            leg, timer = leg + 1, 0.0
    else:
        rc.drive.set_speed_angle(SPEED, TURN)
        if timer > TURN_TIME:
            leg, timer = leg + 1, 0.0

rc.set_start_update(start, update)
rc.go()
FIG. C / EXPECTED PATH
START1 m1 m1 m1 mHELLOrc.drive4 EDGES
Floor view. Square sides are about 1 m at the suggested speed. If your room is smaller, lowering SIDE_TIME shrinks the square to fit.
04 / TWO WAYS TO SHIP

TWO WAYS TO SHIP.

Iterate in the Playground until you trust the behaviour, and the same file then runs on the car without a single change. The runtime swaps the Pyodide-backed sensor sources for the ones for you.

  • In the sim: open NeoRacer Playground, paste the script into the editor, and press Run. Pyodide spins up a Web Worker, and your code executes inside it.
  • On the car: SSH in, the file lands in ~/jupyter_ws/neoracer-os/labs/, and python3 ~/jupyter_ws/neoracer-os/labs/drive_square.py runs it.