# OpenPilot Car Interface Architecture: How It Supports 275+ Vehicle Models

> Discover the OpenPilot car interface architecture enabling support for 275+ vehicle models. Learn how its abstraction layer maps CAN bus fingerprints to Python classes for broad compatibility.

- Repository: [comma.ai/openpilot](https://github.com/commaai/openpilot)
- Tags: architecture
- Published: 2026-03-05

---

**OpenPilot isolates all vehicle-specific logic behind a factory-pattern car interface abstraction that maps CAN bus fingerprints to brand-specific Python classes, exposing only the generic `CarInterfaceBase` and `RadarInterfaceBase` APIs to the rest of the system.**

OpenPilot's ability to drive over 275 distinct car models stems from a sophisticated car interface abstraction layer that decouples driving logic from manufacturer-specific implementations. This architecture, implemented across the `opendbc` and `selfdrive` modules in the commaai/openpilot repository, automatically detects any supported vehicle via CAN fingerprinting and instantiates the correct control interface without requiring changes to core planning or control code.

## The Four Layers of the Car Interface Abstraction

The architecture separates concerns across four distinct layers, each with a specific role in the vehicle abstraction pipeline.

### 1. Factory Layer: `get_car()` in [`car_helpers.py`](https://github.com/commaai/openpilot/blob/main/car_helpers.py)

The entry point for all vehicle detection lives in [`opendbc/car/car_helpers.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/car_helpers.py). The **`get_car()`** function acts as a factory that reads the CAN fingerprint, selects the correct `CarInterface` subclass from a registry, and returns a fully configured instance.

```python
from opendbc.car.car_helpers import get_car

# During initialization in selfdrive/car/card.py

self.CI = get_car(
    *self.can_callbacks,
    obd_callback(self.params),
    alpha_long_allowed,
    is_release,
    num_pandas,
    cached_params
)

```

### 2. Base Class Contracts in [`interfaces.py`](https://github.com/commaai/openpilot/blob/main/interfaces.py)

The file [`opendbc/car/interfaces.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/interfaces.py) defines the abstract contracts that every brand implementation must satisfy. The **`CarInterfaceBase`** class mandates three critical methods:

- **`init(CP, can_recv, can_send)`**: Called once after CAN identification to initialize parsers and safety configurations.
- **`update(can_strings)`**: Executed every CAN cycle (~100 Hz) to parse messages and return a `car.CarState` object.
- **`apply(CC, now_nanos)`**: Translates high-level control commands into vehicle-specific CAN messages.

Similarly, **`RadarInterfaceBase`** enforces a uniform API for obstacle detection across all supported models.

### 3. Brand-Specific Implementations

Each manufacturer resides in its own package under `opendbc/car/<brand>/`. For example, [`opendbc/car/toyota/interface.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/toyota/interface.py) contains the concrete `CarInterface` subclass for all Toyota models. These modules implement the base class methods using manufacturer-specific CAN protocols while exposing the same generic interface to the rest of the system.

### 4. High-Level Orchestration in [`card.py`](https://github.com/commaai/openpilot/blob/main/card.py)

The [`selfdrive/car/card.py`](https://github.com/commaai/openpilot/blob/main/selfdrive/car/card.py) module serves as the high-level driver. It calls `get_car()` to build the car interface (`CI`) and radar interface (`RI`), then runs the main control loops (`state_update`, `controls_update`) entirely through the abstract base class APIs.

## Vehicle Detection via CAN Fingerprinting

The car interface abstraction relies on data-driven detection rather than manual configuration. The process occurs in three steps:

1. **CAN Collection**: During startup, the `card` process collects several seconds of CAN traffic from the vehicle bus.
2. **Fingerprint Matching**: `get_car()` compares the observed set of CAN message IDs against the fingerprint database in [`opendbc/car/fingerprints.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/fingerprints.py).
3. **Enum Resolution**: A matching fingerprint resolves to a brand and model enum (e.g., `Toyota.COROLLA`), which indexes into the `interfaces` dictionary to retrieve the correct class pair.

This lookup happens once at startup, after which the system operates purely through the abstract interface.

## Instantiating the Correct Interface

The factory returns a concrete implementation through a dictionary mapping that connects fingerprints to classes. The `interfaces` dictionary, generated in [`car_helpers.py`](https://github.com/commaai/openpilot/blob/main/car_helpers.py), maps each fingerprint to a tuple containing `(CarInterface, RadarInterface)`.

```python

# Radar interface instantiation following car detection

self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP)

```

This design ensures that [`selfdrive/car/card.py`](https://github.com/commaai/openpilot/blob/main/selfdrive/car/card.py) and all downstream modules (planner, controls, UI) interact only with the generic base class, remaining oblivious to whether the vehicle is a Toyota, Honda, or Hyundai.

## Adding Support for New Car Models

Extending OpenPilot to support a new vehicle requires no changes to core driving logic. The modular architecture requires only these steps:

1. **Create a brand directory** at `opendbc/car/<brand>/`.
2. **Define the model enum** in [`values.py`](https://github.com/commaai/openpilot/blob/main/values.py) (e.g., `class CAR(IntEnum): MY_MODEL = 0`).
3. **Implement the interface** in [`interface.py`](https://github.com/commaai/openpilot/blob/main/interface.py) by subclassing `CarInterfaceBase` and providing the three required methods.
4. **Add state parsing logic** in [`carstate.py`](https://github.com/commaai/openpilot/blob/main/carstate.py) to convert CAN signals into standardized state objects.
5. **Register the fingerprint** in [`opendbc/car/fingerprints.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/fingerprints.py) with the specific CAN IDs observed on the new model's bus.

Once these files exist, the factory automatically returns the new interface when the matching fingerprint is detected.

## Why This Design Scales to 275+ Models

The car interface abstraction enables massive vehicle support through four key architectural decisions:

- **Data-driven lookup**: Runtime selection requires only a dictionary lookup based on the CAN fingerprint, eliminating conditional logic for each supported model.
- **Uniform API contract**: All high-level modules interact exclusively with `CarInterfaceBase` and `RadarInterfaceBase`, ensuring that adding a new car never breaks existing functionality.
- **Modular brand packages**: Each manufacturer lives in an isolated directory, preventing namespace collisions and enabling independent testing and development.
- **Lazy instantiation**: Heavy vehicle-specific logic loads only after fingerprint confirmation, avoiding unnecessary imports for unsupported vehicles.

## Summary

- OpenPilot's car interface abstraction lives in [`opendbc/car/car_helpers.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/car_helpers.py) (factory), [`opendbc/car/interfaces.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/interfaces.py) (base classes), and brand-specific packages under `opendbc/car/<brand>/`.
- The `get_car()` factory function detects vehicles via CAN fingerprinting and returns the appropriate `CarInterfaceBase` subclass.
- All brand implementations must provide `init()`, `update()`, and `apply()` methods to satisfy the abstract base class contract.
- New car models require only the addition of a brand package and fingerprint entry, with zero changes to [`selfdrive/car/card.py`](https://github.com/commaai/openpilot/blob/main/selfdrive/car/card.py) or downstream modules.
- This factory-pattern architecture currently supports over 275 vehicle models while maintaining a single, consistent API for the driving stack.

## Frequently Asked Questions

### How does OpenPilot identify which car model is connected?

OpenPilot identifies the car model through **CAN fingerprinting**. During startup, the system collects CAN messages from the vehicle bus and compares the observed message IDs against a database in [`opendbc/car/fingerprints.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/fingerprints.py). When a match is found, the fingerprint resolves to a specific brand and model enum that determines which `CarInterface` subclass the factory instantiates.

### What methods must a new CarInterface implementation provide?

Every new implementation must subclass `CarInterfaceBase` from [`opendbc/car/interfaces.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/interfaces.py) and implement three core methods: **`init(CP, can_recv, can_send)`** for one-time initialization, **`update(can_strings)`** for parsing CAN data into vehicle state at ~100 Hz, and **`apply(CC, now_nanos)`** for converting control commands into vehicle-specific CAN messages.

### Can I add a new car model without modifying core OpenPilot code?

Yes. The architecture supports adding new vehicles by creating a brand package under `opendbc/car/<brand>/` containing [`values.py`](https://github.com/commaai/openpilot/blob/main/values.py), [`interface.py`](https://github.com/commaai/openpilot/blob/main/interface.py), and [`carstate.py`](https://github.com/commaai/openpilot/blob/main/carstate.py), plus registering the CAN fingerprint in [`opendbc/car/fingerprints.py`](https://github.com/commaai/openpilot/blob/main/opendbc/car/fingerprints.py). The factory in [`car_helpers.py`](https://github.com/commaai/openpilot/blob/main/car_helpers.py) automatically discovers and loads the new interface without requiring changes to [`selfdrive/car/card.py`](https://github.com/commaai/openpilot/blob/main/selfdrive/car/card.py) or any planning and control modules.

### How does the radar interface fit into the abstraction?

The radar interface follows the same factory pattern as the car interface. The `interfaces` dictionary maps each fingerprint to a tuple containing both the `CarInterface` and `RadarInterface` classes. After instantiating the car interface, [`selfdrive/car/card.py`](https://github.com/commaai/openpilot/blob/main/selfdrive/car/card.py) creates the radar interface using `interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP)`, ensuring that obstacle detection logic remains isolated behind the `RadarInterfaceBase` API.