The Vehicle Class
Defined indonkeycar/vehicle.py:61, the Vehicle class is simple but powerful:
Adding Parts
Parts are registered with theadd() method:
complete.py:105):
Parts are executed in the order they are added. This execution order is important because later parts can use outputs from earlier parts in the same iteration.
The Drive Loop
The main loop is started with thestart() method:
Loop Initialization
Fromvehicle.py:137-147:
Main Loop Execution
The core loop structure (vehicle.py:151-172):
The loop automatically sleeps between iterations to maintain the target
rate_hz. If processing takes too long, you’ll see “jitter violation” warnings when running in verbose mode.Update Parts Cycle
Theupdate_parts() method executes all registered parts (vehicle.py:186-215):
Execution Steps
For each part, the vehicle:- Check run condition - If a
run_conditionis specified, check if it’s True in Memory - Get inputs - Retrieve all input values from Memory using
mem.get() - Execute part - Call either
run()orrun_threaded()with the inputs - Store outputs - Save return values to Memory using
mem.put() - Profile - Track execution time for performance analysis
Rate Control
Therate_hz parameter controls loop frequency:
How It Works
- Calculates how long the current iteration took
- Sleeps for the remaining time to hit target rate
- If processing takes longer than
1/rate_hz, no sleep (jitter violation)
Threading Model
Synchronous Parts
Default behavior - part runs in main loop:Threaded Parts
For I/O-bound operations:Thread Creation
Fromvehicle.py:102-105:
Thread Execution
The part must implementupdate() which runs continuously:
update()runs in background thread, continuously capturing framesrun_threaded()is called by main loop to get latest result
camera.py:71-74:
Threaded parts should store their latest result in an instance variable (like
self.frame). The main loop just reads this cached value, so slow I/O doesn’t block the drive loop.Run Conditions
Parts can execute conditionally based on Memory values:Memory['run_pilot'] == True.
Example from complete.py
- User vs autopilot mode switching
- Conditional recording
- Button-triggered actions
- Feature toggles
Performance Profiling
ThePartProfiler tracks execution time for each part:
Profiler Report
When the vehicle stops, it prints a summary:vehicle.py:39-58:
Shutdown Sequence
When the vehicle stops (vehicle.py:217-228):
- Calls
shutdown()on all parts (if they have it) - Prints final profiling report
- Gracefully handles missing shutdown methods
Example: Complete Drive Loop
Here’s a simplified example showing the entire lifecycle:What Happens:
- Initialization: Vehicle creates Memory, registers 8 parts
- Startup: Starts camera and controller threads
- Loop (every 50ms at 20Hz):
- Camera updates in background,
run_threaded()returns latest frame →cam/image_array - Controller polls for input →
user/angle,user/throttle,user/mode - PilotCondition checks mode →
run_pilot - Model runs (if
run_pilot=True) →pilot/angle,pilot/throttle - DriveMode selects user or pilot outputs →
angle,throttle - Steering and throttle actuators execute
- Camera updates in background,
- Sleep: Calculate remaining time to hit 50ms, sleep
- Repeat until interrupted
- Shutdown: Call shutdown on all parts, print profiling report
Timing Considerations
Target Rate
Commonrate_hz values:
- 20 Hz (50ms): Standard for most cars
- 10 Hz (100ms): For slower hardware or heavy processing
- 30 Hz (33ms): For faster response times
Jitter Violations
If processing takes longer than1/rate_hz:
- Blocking parts taking too long
- Camera capture delays
- Model inference too slow
- Too many synchronous parts
Solutions
- Make parts threaded: Move slow I/O to background
- Optimize processing: Reduce image resolution, use faster models
- Lower rate_hz: If 20Hz is too fast, try 15Hz or 10Hz
- Profile: Use verbose mode to identify bottlenecks
Best Practices
- Thread I/O operations: Cameras, network, sensors should be threaded
- Keep processing light: Main loop should be fast (< 50ms total)
- Order parts correctly: Later parts can use earlier outputs in same iteration
- Use run conditions: Avoid unnecessary work with conditional execution
- Profile regularly: Check execution times to catch performance regressions
- Handle shutdown: Implement cleanup in
shutdown()method
Next Steps
- Learn about Parts and how to implement them
- Understand the Memory system
- Explore different Templates
- Read about the Architecture overview
