build-guides
Guide 5: First Drive & Calibration
Calibrate steering and throttle, keyboard-controlled driving, speed and turn radius measurement
Build Guide 5: First Drive & Calibration
Type: Build Guide
The truck moves under Pi control. Now we need to calibrate it — find the exact PWM values for neutral steering, throttle limits, and turn radius. Then we'll take it off the stand for its first real drive.
What You Need
- Fully wired TRX-4 from Guide 4
- A flat, open outdoor space (driveway, parking lot, or lawn)
- Measuring tape
- Laptop with SSH access to the Pi (over WiFi)
- A friend to spot the truck (recommended — it may drive somewhere unexpected)
Step 1: Steering Calibration
The goal: find the exact angle values for center, full-left, and full-right.
With the truck on the stand, wheels visible:
# ~/rover/calibrate_steering.py
from adafruit_pca9685 import PCA9685
from adafruit_motor import servo
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
pca = PCA9685(i2c)
pca.frequency = 50
steer = servo.Servo(pca.channels[0])
while True:
try:
angle = float(input("Enter angle (0-180, or 'q' to quit): "))
steer.angle = angle
print(f" Set to {angle}°")
except ValueError:
break
pca.deinit()
Find these three values:
- Center: Slowly adjust from 90 until the wheels point perfectly straight. Note this value.
- Full left: Decrease the angle until the wheels stop turning or the servo buzzes. Back off 2-3 degrees from the buzz point. Note this value.
- Full right: Same thing, increasing the angle.
Update your config:
# ~/rover/config/rover_config.py
STEERING_CENTER = 88 # Example — yours will differ
STEERING_LEFT_MAX = 52 # Example
STEERING_RIGHT_MAX = 124 # Example
Step 2: Throttle Calibration
Safety: truck still on the stand, wheels off the ground.
The ESC interprets PWM as:
- Neutral = no movement
- Above neutral = forward
- Below neutral = reverse
# ~/rover/calibrate_throttle.py
from adafruit_pca9685 import PCA9685
from adafruit_motor import servo
import board
import busio
import time
i2c = busio.I2C(board.SCL, board.SDA)
pca = PCA9685(i2c)
pca.frequency = 50
esc = servo.ContinuousServo(pca.channels[1])
# Arm the ESC first
print("Arming ESC... make sure battery is connected")
esc.throttle = 0
time.sleep(3)
print("Armed.")
while True:
try:
t = float(input("Enter throttle (-1.0 to 1.0, or 'q' to quit): "))
t = max(-1.0, min(1.0, t))
esc.throttle = t
print(f" Throttle: {t}")
except ValueError:
break
esc.throttle = 0
pca.deinit()
Find these values:
- Forward creep: The smallest positive throttle that makes the wheels spin. For dragging cloth at walking speed, you want a value just above this. Try
0.05,0.08,0.10... - Drag speed: The throttle value that gives ~0.5-0.8 m/s. You'll fine-tune this on the ground, but get a rough idea from wheel speed.
- Max safe forward: Probably around
0.15-0.25— you never need to go fast. - Reverse: Negative values. Note the ESC might require a double-tap (neutral → reverse → neutral → reverse) to engage reverse. Test this.
Update your config:
THROTTLE_FORWARD_MIN = 0.06 # Example — creep speed
THROTTLE_FORWARD_MAX = 0.15 # Example — max drag speed
THROTTLE_REVERSE = -0.08 # Example — gentle reverse
Step 3: Write a Basic Drive Controller
Before going outdoors, create a simple keyboard-controlled drive script:
# ~/rover/drive_manual.py
"""
Manual keyboard drive control.
Run over SSH and use WASD keys.
w = forward, s = stop, a = left, d = right, q = quit
r = reverse
"""
from adafruit_pca9685 import PCA9685
from adafruit_motor import servo
import board
import busio
import sys
import tty
import termios
import time
# Load config
sys.path.insert(0, '/home/pi/rover')
from config.rover_config import *
i2c = busio.I2C(board.SCL, board.SDA)
pca = PCA9685(i2c)
pca.frequency = 50
steer = servo.Servo(pca.channels[STEERING_CHANNEL])
esc = servo.ContinuousServo(pca.channels[THROTTLE_CHANNEL])
# Arm ESC
print("Arming ESC...")
esc.throttle = 0
steer.angle = STEERING_CENTER
time.sleep(3)
print("Ready! Controls: W=forward S=stop A=left D=right R=reverse Q=quit")
current_throttle = 0
current_steer = STEERING_CENTER
STEER_STEP = 5
def get_key():
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
try:
while True:
key = get_key().lower()
if key == 'w':
current_throttle = THROTTLE_FORWARD_MIN
print(f"\rForward: {current_throttle} ", end='')
elif key == 's':
current_throttle = 0
current_steer = STEERING_CENTER
print(f"\rStop + center ", end='')
elif key == 'r':
current_throttle = THROTTLE_REVERSE
print(f"\rReverse: {current_throttle} ", end='')
elif key == 'a':
current_steer = max(STEERING_LEFT_MAX, current_steer - STEER_STEP)
print(f"\rSteer: {current_steer}° ", end='')
elif key == 'd':
current_steer = min(STEERING_RIGHT_MAX, current_steer + STEER_STEP)
print(f"\rSteer: {current_steer}° ", end='')
elif key == 'q':
break
esc.throttle = current_throttle
steer.angle = current_steer
except KeyboardInterrupt:
pass
finally:
esc.throttle = 0
steer.angle = STEERING_CENTER
pca.deinit()
print("\nShutdown complete.")
Test on the stand first to make sure the controls feel right and the truck responds as expected.
Step 4: First Drive on the Ground
This is the moment. Take the truck outside.
Pre-flight checklist:
- TRX-4 main battery fully charged
- Pi USB battery fully charged
- All cables secure (give everything a gentle tug)
- Camera ribbon cable has slack for suspension movement
- Nothing dragging under the chassis
- Enclosure lid closed
- WiFi range — stay close enough for SSH
First drive procedure:
- Set the truck on flat ground
- SSH in from your laptop:
ssh pi@tickslayer.local - Run
python ~/rover/drive_manual.py - Wait for "Ready!" (ESC arming)
- Press
wfor forward — the truck should creep forward slowly - Press
sto stop - Try steering with
aanddwhile moving - Press
qwhen done
What you're watching for:
- Does it drive straight when steering is centered? If not, adjust
STEERING_CENTERby 1-2 degrees. - Is the speed right for cloth dragging (~walking pace)? Adjust
THROTTLE_FORWARD_MIN. - Does steering feel proportional? Adjust
STEER_STEPfor finer or coarser control. - Does it reverse? Some ESCs need a specific arming sequence for reverse.
- Does SSH stay connected as the truck moves? If it drops, you're hitting WiFi range limits.
Step 5: Measure Actual Speed
We need to know the ground speed for the capture interval (photos every 15 meters):
- Lay out two markers exactly 10 meters apart
- Drive the truck between them at your target throttle value
- Time it with a stopwatch
Speed = distance / time
10m in 15 seconds = 0.67 m/s ← right in our target range
If it's too fast, lower the throttle value. If too slow, raise it slightly. The target is 0.5-0.8 m/s.
Record the calibrated throttle value in your config.
Step 6: Turn Radius Test
For path-following, we need to know the turn radius:
- Set steering to full left
- Drive forward slowly for a full circle
- Measure the diameter of the circle
- Turn radius = diameter / 2
Do the same for full right. They should be roughly equal — if not, your steering center is off.
Record this for the navigation code:
# ~/rover/config/rover_config.py
TURN_RADIUS_MIN_M = 0.8 # Example — meters, at full steering lock
Step 7: Emergency Stop Test
You need a reliable way to stop the truck immediately:
- Drive forward at drag speed
- Press
s(stop) — the truck should stop within a body length - Try
Ctrl+C— thefinallyblock should send neutral and stop everything - Try closing the SSH session — what happens? (The script should terminate and the ESC should go to neutral)
If the truck keeps going when SSH drops: This is a problem. The ESC holds its last command. We'll add a software watchdog in Guide 7 that stops the truck if it hasn't received a command in 2 seconds. For now, just be aware of this and stay close to the power switch.
Step 8: Battery Monitoring Under Load
While driving, check that battery monitoring works:
# In a separate SSH session
python ~/rover/test_adc.py
- Note the voltage while driving vs. sitting still — it'll be lower under load, that's normal
- Drive until the voltage drops to your
BATTERY_LOW_THRESHOLD(around 6.8V for a 2S LiPo) and verify the reading - Do NOT actually run the battery to critical — just note the trend
Checklist
- Steering calibrated: center, left max, right max values recorded
- Throttle calibrated: creep, drag speed, and max values recorded
- Manual drive script working
- First drive completed on flat ground
- Ground speed measured and in target range (0.5-0.8 m/s)
- Turn radius measured
- Emergency stop tested (keyboard + SSH disconnect)
- Battery monitoring reads correctly under load
- Config file updated with all calibrated values
The truck drives. You can steer it. It's time to give it something to drag — Guide 6: Drag Cloth & Sampling Rig.
