MOTOR TRIM.
When you call rc.drive.set_speed_angle(0, 0) the car should sit still, and a full command should give a speed you chose on purpose. Both of those are decided by the driver: the neutral is fixed, and the speed you actually get comes from a couple of constants you can read and change.
HOW IT WORKS.
The drive pipeline ends at the controller node, which owns the USB-CDC link to the OSCORE ESP32 and writes the per-tick command v <m/s> <deg> as a normalized request. The ESP32 turns that into the actual for the ESC. Neutral lives on the ESP32 itself, so when speed is zero the controller writes a zero command and the wheels should be still. If the car creeps at zero on a charged pack and a flat floor, the cause is the ESC's own neutral or mechanical drag in the drivetrain, covered in Hardware · Drivetrain, not a number you set here.
What you do control is how far a command goes. A speed of 1.0 does not send the ESC to full throttle; it is scaled down hard, on purpose, so a first program cannot launch the car across the room.
WHAT YOU'LL NEED.
Open floor
Charged pack
SSH to the Jetson
A way to stop
THE PROCEDURE.
bash# 1. SSH into the Jetson (see Networking for the address). ssh racecar@neoracer # 2. Open the YAML the driver loads at launch. # config/throttle.yaml is the single source of truth for the speed # and steering caps. config/controller.yaml holds the ESP32 m/s # mapping and the steering trim in degrees. $EDITOR ~/ros2_ws/src/neoracer_ros2_driver/config/throttle.yaml $EDITOR ~/ros2_ws/src/neoracer_ros2_driver/config/controller.yaml # 3. Bump the top-speed cap a little if you want more headroom. # Small steps. The ESC response is not linear near the bottom of the # range, so a small change near zero can mean a large change at the # wheel. # 4. Re-launch teleop. Configs are read on launch, no colcon build needed. racecar teleop
VERIFY THE NEUTRAL.
From the car, this holds a zero command for five seconds so you can watch for creep. The same script runs in the Playground first if you want to see the expected behaviour before trying it on hardware.
pythonimport racecar_core rc = racecar_core.create_racecar() HOLD_S = 5.0 timer = 0.0 def start(): global timer timer = 0.0 rc.drive.stop() print("Trim verify: holding zero for 5 s, watch for creep") def update(): global timer timer += rc.get_delta_time() rc.drive.set_speed_angle(0, 0) if timer > HOLD_S: rc.drive.stop() print("Moved? Check the ESC neutral and drivetrain drag, not software.") rc.set_start_update(start, update) rc.go()
Less than a centimetre of motion over five seconds is a pass. More than that points at the ESC neutral or mechanical drag, since the software is already sending exact neutral.
WHERE IT LIVES.
Both files are plain YAML the launch system reads on startup. Edit, re-launch racecar teleop, and the new caps are live. No colcon build, no firmware reflash. They live under the driver workspace at ~/ros2_ws/src/neoracer_ros2_driver/config/.
yaml# ~/ros2_ws/src/neoracer_ros2_driver/config/throttle.yaml # Single source of truth for the top speed and steering caps. # All values are normalized to [-1, 1] across the pipeline. throttle: ros__parameters: max_forward: 0.25 # the speed /drive is measured against max_reverse: 0.25 max_steer: 1.00
yaml# ~/ros2_ws/src/neoracer_ros2_driver/config/controller.yaml # ESP32 serial port, the normalized -> m/s drive mapping, the # steering trim, and the Flysky RC channel map. controller: ros__parameters: port: /dev/osrbot_base max_speed_mps: 2.0 steering_trim_deg: 0.0 throttle_channel: 2 steering_channel: 0 mode_channel: 4
- A higher
max_forwardin throttle.yaml means more top speed for the same command. - Neutral is owned by the ESP32 firmware, not the YAML, so a creep at zero is the ESC neutral or drivetrain drag, not a value you set here.
- Every student script that calls
rc.drive.set_speed_angle()gets your caps for free on the nextracecar teleop.
