STEERING CENTER.
When you call rc.drive.set_speed_angle(speed, 0) the wheels should point dead ahead. Out of the box they usually lean a touch, because the steering linkage sits a few counts off true center. Unlike the motor, this one has a real software trim: a single constant you nudge and rebuild.
HOW IT WORKS.
Your steering angle is normalized in the range [-1, 1] all the way through the drive pipeline. The controller node writes the per-tick command v <m/s> <deg> over USB-CDC to the ESP32, where the angle in degrees is what the servo eventually sees. Before that send, the controller adds one YAML value, steering_trim_deg, from config/controller.yaml. That value starts at 0.0. Nudge it a degree or two and the wheels at zero angle shift with it.
WHAT YOU NEED.
A straight line
Motor checked first
SSH to the Jetson
config/controller.yaml on the car. You edit it over SSH, covered in Networking.THE PROCEDURE.
bash# 1. SSH into the Jetson. ssh racecar@neoracer # 2. Open the controller YAML and find the trim line. $EDITOR ~/ros2_ws/src/neoracer_ros2_driver/config/controller.yaml # controller: # ros__parameters: # steering_trim_deg: 0.0 # degrees added to the servo angle # 3. Roll-test first (Section 04) to see which way it drifts, then nudge: # drifts LEFT at angle 0 -> steer it right: lower the trim # drifts RIGHT at angle 0 -> steer it left: raise the trim # Step a degree at a time. Half a degree is usually enough at the end. # 4. Re-launch teleop so the new trim is picked up. racecar teleop
VERIFY THE CENTER.
This holds the servo at center and the motor at zero so you can roll the car by hand along the tape and watch the drift:
pythonimport racecar_core rc = racecar_core.create_racecar() HOLD_S = 8.0 timer = 0.0 def start(): global timer timer = 0.0 rc.drive.set_speed_angle(0, 0) print("Servo verify: holding straight, push the car 1 m along the tape") def update(): global timer timer += rc.get_delta_time() rc.drive.set_speed_angle(0, 0) # motor at zero, servo centered if timer > HOLD_S: rc.drive.stop() rc.set_start_update(start, update) rc.go()
A pass is finishing within a wheel's width of the line after a metre. A bigger gap means another nudge to the offset and a rebuild.
WHERE IT LIVES.
The trim is a YAML value the controller node reads on launch, so a re-launch is what applies it. There is no colcon build to wait on. Keep your edited controller.yaml in your own copy of the workspace so a re-image does not reset it to zero.
yaml# ~/ros2_ws/src/neoracer_ros2_driver/config/controller.yaml controller: ros__parameters: port: /dev/osrbot_base max_speed_mps: 2.0 steering_trim_deg: 0.0 # your trim, in degrees throttle_channel: 2 steering_channel: 0 mode_channel: 4
- A positive
steering_trim_degsteers the zero-angle wheels one way, negative the other. Worth confirming the direction on your own car with a roll test. - The top-speed cap is
max_speed_mpson the same file; leave it alone if you only meant to trim the steering. - Every script that sends a steering angle of 0 now points true ahead, no per-program tweak needed.
