Dear Community:
The Openpilot Community needs your help to continue to keep opc.ai's lights on and support the path to Workbench v0.2. With only a one person crew and without commercial funding it's not always financially feasible to fund these services out of pocket. Please consider becoming a Patreon supporter of the Openpilot Community project and receive exclusive perks and benefits!
Become a patreon Learn more Maybe Later

DISCLAIMER: This website is maintained by openpilot users and contributors. The content found here (including Workbench and Oppey the Bot) are not approved, supported, affiliated or funded by Comma.ai, Inc. Please DO NOT contact Comma.ai, Inc. staff about these things as they WILL NOT provide support. For support on openpilot, opc.ai, oppey, workbench, etc. please contact a community member on Discord.

jfrux
Jan 11·5 min read

Openpilot RFC: Vehicle Support Classes

DOCUMENT IS A WORK-IN-PROGRESS. FEEL FREE TO GIVE FEEDBACK AT ANY POINT ALONG THE WAY.

Summary

Community developers need to begin focusing on hooks, actions, filters, rather than user-preference specific features and car ports.

We need to begin refactoring selfdrive/car/* by creating base classes / methods required for OP to work.

PLEASE NOTE:
I'm not a Python expert so forgive me if things I say are incorrect or not how it works. But please provide a solution to solve the same problem versus just telling me it's not how it works.

Problems

  • Vehicle Support Conflicts! i.e. 2019 Insight and Accord Hybrid share the
    same fingerprintbut have different commands. If the vehicle was a chosen plugin by the user somehow or in a config with an class-based abstraction of what a vehicle is, it's methods required by Openpilot, etc. then all we do is load that version of the vehicle via its plugin and Openpilot remains happy.
  • User-specific Tuning! i.e. User doesn't like the steering so they want to mod the kV/kP they can fork the vehicle-specific core repo instead of forking Openpilot.
  • User-specific Preferences! i.e. User wants to add automatic lane change support. They need a hook in particular place in code, request RFC and use the Openpilot plugin management system hooks / filters etc. to make a PR where they need their hook. That gets merged, then their plugin can work that injects the code needed. (this part could be part of a separate RFC "Plugin Base Class RFC")

I'm proposing a core team of community devs band together to maintain a fork and PR of Openpilot that begins this work immediately.

Note: The goal of this RFC is to start the discussion. We will need to work on getting an RFC repo similar to other communities like Rust and others who have followed the Rust way.

Solutions

I propse that this problem be solved by a core team of community developers that are already working on their own forks, mods, and other vehicle specific changes to shift their efforts and begin working on a more object-oriented version of Openpilot where we can add code through python modules and dependency injection so that developers can focus on just their code instead of maintaining forks core code for enhancement / preference features. This is a Python based approach but even starting out with it being able to inject an override Vehicle base-classes for controls, state, etc.

Wouldn't it be nice if the Openpilot running on the device was only concerned with one car for each instance rather than 100? Manufacturers are going to continue making cars, we cannot continue inserting more if statements for each vehicle. This has to change now.

I propose we use the existing Python package management via namespaced packages such as described in https://packaging.python.org/guides/creating-and-discovering-plugins/#using-namespace-packages or we roll our own solution to fetch packages and pull them in which will be complicated and probably take longer. We can do what the npm / node community does and just prefix packages for our community like openpilot_core_* for core functionality and openpilot_contrib_* for community supported solutions.

Both of which can be maintained and contributed to by the community.

openpilot_core_* packages are required for a vehicle to work.

openpilot_contrib_* packages are addons to existing vehicle packages.

1. Vehicle Support Conflicts

openpilot-core repo should maintain and test against base-classes necessary for all of it's services to communicate and control a vehicle.

First we have to start in openpilot-core which is what I'm calling the current commaai/openpilot repo for the remainder of this document.

We should first start in selfdrive/car/* to limit the scope of this document and to give us a good baseline for the future.

Add Base Classes

  • selfdrive/car/State.py
  • selfdrive/car/Controller.py
  • selfdrive/car/CAN.py
  • selfdrive/car/Interface.py
  • selfdrive/car/RadarInterface.py
  • selfdrive/car/Values.py

We need to imagine we're trying to merge all of each of these respective classes within their Make directory into 1 file per class. For instance, take the carstate.py from each of the vehicle make directories. We take all method names from each and merge them into one file. Any duplicate methods are just ignored and only one is added versus renaming.

Obviously the method logic won't be even close to the same at first but we have to begin somewhere. There will be a lot of crazy methods in the base classes that do not make sense for all vehicles but we will refactor that AFTER this is working.

So we create the base interface classes with empty methods for ALL possible requirements (at first) to have placeholders for each possible method with comments what purpose each serves when it was extracted from its Make-specific class. An empty class so that Openpilot can call any of them without error without fear. This base class also helps us with auto-documentation later too and helps other devs learn more about how to add support for a specific vehicle.

Make-specific Base Core Plugins

Now that we have the base classes merged, we begin our make-specific bases.

Let's call each module openpilot_core_honda, openpilot_core_toyota, and so on.

Each plugin whether core or contrib will use Python namespace packages to place the core classes in their respective directories.

For instance, maybe we know that ALL Honda's State.py require State::parse_gear_shifter(gear, vals) and the code is always:

def parse_gear_shifter(gear, vals):
  val_to_capnp = {'P': 'park', 'R': 'reverse', 'N': 'neutral',
                  'D': 'drive', 'S': 'sport', 'L': 'low'}
  try:
    return val_to_capnp[vals[gear]]
  except KeyError:
    return "unknown"

Then we need to create selfdrive/car/honda/State.py which extends / inherits the selfdrive/car/State.py and overrides the parse_gear_shifter method of course and add that in.

Once we have the base classes for everything, we can begin modularizing model specific changes.

All of our extended classes will be injected.

Specifics on New Classes