Skip to main content
Parts are the fundamental building blocks of Donkeycar. A part is any Python class that implements a run() or run_threaded() method and can be added to the vehicle loop.

What is a Part?

A part is a modular component that:
  • Takes inputs from Memory
  • Performs some processing or I/O
  • Returns outputs to Memory
  • Runs on each iteration of the vehicle loop
Parts can be anything: sensors, actuators, controllers, processors, filters, or business logic.

Part Interface

The minimal part interface is simply a run() method:

Full Interface

A complete part may implement:

Adding Parts to Vehicle

Parts are registered with the Vehicle using add():

Example

Input/Output Flow

The Vehicle automatically manages data flow:
  1. Get inputs: Vehicle reads input values from Memory
  2. Call part: Passes inputs as arguments to run() or run_threaded()
  3. Store outputs: Vehicle writes return values to Memory

Single Output

Multiple Outputs

No Output

The number of return values must match the number of outputs specified. For multiple outputs, return a tuple: return output1, output2

Synchronous vs Threaded Parts

Synchronous Parts (Default)

When to use: Fast operations (< 10ms)
Example from throttle_filter.py:2-25.

Threaded Parts

When to use: Slow I/O operations (cameras, network, sensors)
Adapted from camera.py:23-82. How it works:
  • update() runs in a background thread, continuously calling run()
  • Main vehicle loop calls run_threaded() to get latest cached result
  • No blocking of the main drive loop
Threaded parts should store results in instance variables (like self.frame). The main loop just reads the cached value, preventing slow I/O from blocking the drive loop.

Real-World Part Examples

Example 1: Transform Part

Simple functional wrapper from transform.py:5-19:

Example 2: DriveMode Part

Logic part from basic.py:28-42:

Example 3: PID Controller

Stateful processing part from transform.py:53-115:

Example 4: TriggeredCallback

Event-driven part from transform.py:21-31:

Example 5: Conditional Logic Part

From simulator.py:122-128:
Parts can be simple! Many parts are just a few lines of logic. Don’t over-engineer - if it has inputs, outputs, and a run() method, it’s a valid part.

Run Conditions

Parts can execute conditionally:
From the Vehicle implementation (vehicle.py:194-196):
Example usage from complete.py:415:

Use Cases

  1. Mode switching: User vs autopilot
  2. Conditional recording: Only record when button pressed
  3. Button handlers: Execute when button pressed
  4. Feature toggles: Enable/disable features

Memory Channel Naming Conventions

Follow these patterns for clarity:
  • Sensors: cam/image_array, imu/acl_x, lidar/dist_array
  • User input: user/angle, user/throttle, user/mode
  • Autopilot: pilot/angle, pilot/throttle, pilot/loc
  • Actuators: steering, throttle, brake
  • Recording: recording, tub/num_records
  • Conditions: run_pilot, run_user
  • Web UI: web/buttons, web/w1, web/w2

Creating Custom Parts

Simple Processing Part

Sensor Part with Threading

Actuator Part

Filter Part

Best Practices

  1. Keep parts simple: Each part should do one thing well
  2. Make I/O threaded: Cameras, network, sensors should run in background
  3. Store state in instance variables: For stateful parts like filters or accumulators
  4. Implement shutdown: Clean up resources (close files, stop motors, etc.)
  5. Handle None gracefully: Inputs may be None on first iteration
  6. Use meaningful names: Clear input/output channel names
  7. Document your parts: Add docstrings explaining purpose and usage
  8. Test independently: Parts should be testable without the vehicle loop

Part Testing

Test parts independently before adding to vehicle:

Common Patterns

Button Handler

Data Pipeline

Conditional Branching

Next Steps