Tick Slayer 3000
← All Documents

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:

  1. Center: Slowly adjust from 90 until the wheels point perfectly straight. Note this value.
  2. 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.
  3. 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:

  1. 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...
  2. 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.
  3. Max safe forward: Probably around 0.15-0.25 — you never need to go fast.
  4. 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:

  1. Set the truck on flat ground
  2. SSH in from your laptop: ssh pi@tickslayer.local
  3. Run python ~/rover/drive_manual.py
  4. Wait for "Ready!" (ESC arming)
  5. Press w for forward — the truck should creep forward slowly
  6. Press s to stop
  7. Try steering with a and d while moving
  8. Press q when done

What you're watching for:

  • Does it drive straight when steering is centered? If not, adjust STEERING_CENTER by 1-2 degrees.
  • Is the speed right for cloth dragging (~walking pace)? Adjust THROTTLE_FORWARD_MIN.
  • Does steering feel proportional? Adjust STEER_STEP for 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):

  1. Lay out two markers exactly 10 meters apart
  2. Drive the truck between them at your target throttle value
  3. 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:

  1. Set steering to full left
  2. Drive forward slowly for a full circle
  3. Measure the diameter of the circle
  4. 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:

  1. Drive forward at drag speed
  2. Press s (stop) — the truck should stop within a body length
  3. Try Ctrl+C — the finally block should send neutral and stop everything
  4. 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.