Documentation Index
Fetch the complete documentation index at: https://mintlify.com/autorope/donkeycar/llms.txt
Use this file to discover all available pages before exploring further.
Sensor parts provide additional data about your vehicle’s state and environment.
IMU (Inertial Measurement Unit)
Measures acceleration, rotation, and orientation.
Location: donkeycar/parts/imu.py:9
Supported Sensors
- MPU6050: 6-axis (accelerometer + gyroscope)
- MPU9250: 9-axis (accelerometer + gyroscope + magnetometer)
from donkeycar.parts.imu import IMU, SENSOR_MPU6050, SENSOR_MPU9250
imu = IMU(
addr=0x68, # I2C address
poll_delay=0.0166, # Poll interval (60Hz)
sensor=SENSOR_MPU6050, # Sensor type
dlp_setting=0 # Digital low-pass filter (0=disabled)
)
Installation:
# For MPU6050
sudo apt install python3-smbus
pip install mpu6050-raspberrypi
# For MPU9250
pip install mpu9250-jmdev
Enable I2C:
sudo raspi-config
# Interface Options -> I2C -> Enable
Usage:
# Add to vehicle
V.add(imu,
outputs=['imu/accel_x', 'imu/accel_y', 'imu/accel_z',
'imu/gyro_x', 'imu/gyro_y', 'imu/gyro_z', 'imu/temp'],
threaded=True)
# Or run directly
accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z, temp = imu.run()
Outputs:
- Accelerometer (m/s²): Linear acceleration on X, Y, Z axes
- Gyroscope (rad/s): Angular velocity on X, Y, Z axes
- Temperature (°C): Die temperature
- Magnetometer (MPU9250 only, μT): Magnetic field on X, Y, Z axes
Digital Low-Pass Filter:
# Common DLP settings (gyro/accel bandwidth)
# 0: Disabled (8kHz/1kHz)
# 1: 184Hz/188Hz
# 2: 94Hz/98Hz
# 3: 44Hz/42Hz
# 4: 21Hz/20Hz
# 5: 10Hz/10Hz
# 6: 5Hz/5Hz
imu = IMU(sensor=SENSOR_MPU6050, dlp_setting=3) # 44Hz filter
Testing:
cd ~/mycar
python -c "from donkeycar.parts.imu import IMU; import time; imu = IMU(); [print(imu.run()) or time.sleep(0.1) for _ in range(10)]"
GPS
Global positioning from NMEA-compatible GPS modules.
Location: donkeycar/parts/gps.py
GpsNmeaPositions
Converts NMEA sentences to UTM coordinates.
from donkeycar.parts.gps import GpsNmeaPositions
from donkeycar.parts.serial_port import SerialPort, SerialLineReader
# Open serial port
serial_port = SerialPort(
port='/dev/ttyUSB0', # Or /dev/ttyAMA0 for Pi GPIO
baudrate=9600,
timeout=0.5
)
# Read NMEA lines
line_reader = SerialLineReader(
serial_port,
max_lines=10
)
# Convert to positions
gps_reader = GpsNmeaPositions(debug=False)
# Add to vehicle
V.add(line_reader, outputs=['nmea/lines'], threaded=True)
V.add(gps_reader, inputs=['nmea/lines'], outputs=['gps/positions'])
Outputs:
- List of (timestamp, x, y) tuples
- x, y in UTM coordinates (meters)
GpsPosition
Combined GPS reader that returns the latest position.
Location: donkeycar/parts/gps.py:54
from donkeycar.parts.gps import GpsPosition
from donkeycar.parts.serial_port import SerialPort
serial_port = SerialPort('/dev/ttyUSB0', baudrate=9600)
gps = GpsPosition(serial=serial_port, debug=False)
V.add(gps, outputs=['gps/timestamp', 'gps/x', 'gps/y'], threaded=True)
NMEA Parsing:
Supports GPRMC and GNRMC sentences:
$GPRMC,003918.00,A,3806.92281,N,12235.64362,W,0.090,,060322,,,D*67
Parsed to:
- Latitude/Longitude in degrees
- Converted to UTM (x, y) in meters
- Timestamp from sentence
Hardware Setup:
# For USB GPS
ls /dev/ttyUSB* # Find device
# For GPIO serial (disable console first)
sudo raspi-config
# Interface Options -> Serial Port
# -> "Would you like a login shell accessible over serial?" No
# -> "Would you like the serial port hardware enabled?" Yes
# Check
ls /dev/ttyAMA0 /dev/serial0
Installation:
pip install pynmea2 pyserial utm
Testing:
# Raw NMEA output
cat /dev/ttyUSB0
# Test with script
cd ~/mycar
python -m donkeycar.parts.gps --serial /dev/ttyUSB0 --baudrate 9600
Encoders
Measure wheel rotation for odometry and velocity.
Note: The encoder.py module is deprecated. Use tachometer.py and odometer.py instead.
Rotary Encoder (Legacy)
Location: donkeycar/parts/encoder.py:128
GPIO-based rotary encoder (deprecated in favor of donkeycar.parts.tachometer.Tachometer).
from donkeycar.parts.encoder import RotaryEncoder
encoder = RotaryEncoder(
mm_per_tick=0.306096, # Distance per encoder tick
pin=13, # GPIO pin
poll_delay=0.0166, # Poll rate (60Hz)
debug=False
)
V.add(encoder,
inputs=['user/throttle'],
outputs=['enc/velocity'],
threaded=True)
Requires: pigpio
sudo apt-get install pigpio python3-pigpio
sudo systemctl enable pigpiod
sudo systemctl start pigpiod
Calibration:
- Measure wheel circumference (C) in mm
- Count encoder ticks per revolution (T)
- Set
mm_per_tick = C / T
Outputs:
- velocity (m/s): Current speed
- distance (m): Total distance traveled
Tachometer (Modern)
For current speed measurement, use:
from donkeycar.parts.tachometer import Tachometer, GpioEncoder
encoder = GpioEncoder(pin=13)
tach = Tachometer(encoder, meters_per_tick=0.000306096)
V.add(tach, outputs=['enc/velocity'], threaded=True)
Odometer (Modern)
For distance measurement:
from donkeycar.parts.odometer import Odometer
odometer = Odometer(tachometer=tach)
V.add(odometer,
inputs=['enc/velocity'],
outputs=['enc/distance'])
Ultrasonic Sensors
For obstacle detection and distance measurement.
Location: Custom implementation needed
import RPi.GPIO as GPIO
import time
class UltrasonicSensor:
def __init__(self, trigger_pin, echo_pin):
self.trigger = trigger_pin
self.echo = echo_pin
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.trigger, GPIO.OUT)
GPIO.setup(self.echo, GPIO.IN)
def run(self):
# Send trigger pulse
GPIO.output(self.trigger, GPIO.HIGH)
time.sleep(0.00001)
GPIO.output(self.trigger, GPIO.LOW)
# Measure echo
while GPIO.input(self.echo) == GPIO.LOW:
start = time.time()
while GPIO.input(self.echo) == GPIO.HIGH:
end = time.time()
# Calculate distance
duration = end - start
distance = duration * 34300 / 2 # cm
return distance
def shutdown(self):
GPIO.cleanup()
# Usage
sensor = UltrasonicSensor(trigger_pin=23, echo_pin=24)
V.add(sensor, outputs=['ultrasonic/distance'])
LIDAR
For 2D/3D environment scanning.
Location: donkeycar/parts/lidar.py (if available)
# Example for RPLidar
from rplidar import RPLidar
class LidarPart:
def __init__(self, port='/dev/ttyUSB0'):
self.lidar = RPLidar(port)
self.running = True
self.scan_data = []
def update(self):
for scan in self.lidar.iter_scans():
if not self.running:
break
self.scan_data = scan
def run_threaded(self):
return self.scan_data
def shutdown(self):
self.running = False
self.lidar.stop()
self.lidar.disconnect()
Configuration
In myconfig.py:
# IMU
HAVE_IMU = True
IMU_SENSOR = 'mpu6050' # or 'mpu9250'
IMU_ADDRESS = 0x68
IMU_DLP_CONFIG = 0
# GPS
HAVE_GPS = True
GPS_SERIAL_PORT = '/dev/ttyUSB0'
GPS_BAUDRATE = 9600
# Encoder
HAVE_ODOM = True
ENCODER_TYPE = 'GPIO' # or 'ARDUINO'
ENCODER_PIN = 13
MM_PER_TICK = 0.306096
ENC_POLL_RATE = 60
# Ultrasonic
HAVE_ULTRASONIC = False
ULTRASONIC_TRIGGER_PIN = 23
ULTRASONIC_ECHO_PIN = 24
Data Recording
Add sensor outputs to TubWriter:
# In manage.py
inputs = ['cam/image_array',
'user/angle', 'user/throttle', 'user/mode',
'imu/accel_x', 'imu/accel_y', 'imu/accel_z',
'imu/gyro_x', 'imu/gyro_y', 'imu/gyro_z',
'gps/x', 'gps/y',
'enc/velocity']
types = ['image_array',
'float', 'float', 'str',
'float', 'float', 'float',
'float', 'float', 'float',
'float', 'float',
'float']
Using Sensor Data in Models
For IMU-augmented models:
from donkeycar.parts.keras import KerasPilot
class ImuPilot(KerasPilot):
def run(self, img_arr, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z):
# Use IMU data along with image
return self.inference_from_dict({
'img_in': normalize_image(img_arr),
'imu_in': np.array([accel_x, accel_y, accel_z,
gyro_x, gyro_y, gyro_z])
})
Common Issues
IMU Not Detected
# Check I2C
sudo i2cdetect -y 1
# Should show 0x68 or 0x69
# If not found, check connections and enable I2C
sudo raspi-config
GPS No Fix
- Ensure antenna has clear view of sky
- Wait 30-60 seconds for initial fix (cold start)
- Check NMEA output:
cat /dev/ttyUSB0
- Verify GPS LED is blinking
Encoder Not Counting
- Check GPIO pin connections
- Verify pigpio daemon is running:
sudo systemctl status pigpiod
- Test with multimeter or oscilloscope
- Check for proper pull-up/pull-down resistors
Serial Port Permission Denied
# Add user to dialout group
sudo usermod -a -G dialout $USER
# Log out and back in
# Or temporarily
sudo chmod 666 /dev/ttyUSB0
Next Steps