Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync: commaai/opendbc:master into sunnypilot/opendbc:master-new #71

Merged
merged 59 commits into from
Mar 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
8289d1e
Hyundai CAN-FD Safety: Update wheel speed signal parsing (#1850)
sunnyhaibin Feb 28, 2025
78ecabe
Angle safety: test above max angle (#1879)
sshane Feb 28, 2025
e43a2eb
fingerprint: 2023 Tesla Model Y (HW3) (#1882)
greatgitsby Feb 28, 2025
2a75806
Hyundai CAN FD: ADAS steering API updates (#1873)
sunnyhaibin Mar 1, 2025
a4c0305
Remove test_generator from commit hooks (#1883)
nworb-cire Mar 1, 2025
e3d641f
angle limits: use a struct (#1885)
sshane Mar 1, 2025
52f89f1
Revert "Hyundai CAN FD: ADAS steering API updates" (#1888)
sshane Mar 1, 2025
e0cacf2
Hyundai CAN FD: add angle control signals to the DBC (#1887)
sshane Mar 1, 2025
f8d9760
Hyundai CAN FD angle control: add fault signal
sshane Mar 1, 2025
58841eb
Run format_fingerprints.py
sshane Mar 1, 2025
7df0d85
Fix Ford CAN FD safety replay (#1889)
sshane Mar 1, 2025
7afe451
safety replay: fix messages on segment boundaries not sorted (#1890)
sshane Mar 1, 2025
966df19
master-ci is dead
adeebshihadeh Mar 1, 2025
785b5ec
Replace gcov & lcov with gcovr (#1867)
ccdunder Mar 2, 2025
46fa666
Ford Q4: limit max curvature from vehicle speed (#1852)
sshane Mar 3, 2025
bc83987
Ford CAN FD: enable safety in release (#1896)
sshane Mar 3, 2025
3612798
Hyundai CAN FD: test ACC cancel (#1898)
sshane Mar 3, 2025
7495359
Coverage fixups (#1900)
sshane Mar 3, 2025
129ce34
Only run tx hooks on msgs in safety_config.tx_msgs (#1851)
ccdunder Mar 3, 2025
ca4f77b
Add Bosch C harness
sshane Mar 3, 2025
e37bd99
Ford CAN FD: upstream car docs (#1904)
sshane Mar 3, 2025
1c46c78
Car docs: small cleanup
sshane Mar 3, 2025
df48fc9
Toyota: low speed lockout is a fault only if openpilot longitudinal (…
sshane Mar 3, 2025
4b72aae
Ford CAN FD: SecOC is dashcam (#1908)
sshane Mar 3, 2025
34a109c
Ford CAN FD: fix docs
sshane Mar 4, 2025
41bd77b
add CI test artifacts to gitignore (#1916)
jyoung8607 Mar 4, 2025
26859fc
Tesla: add Model Y 2022 (HW3) (#1915)
lukasloetkolben Mar 4, 2025
da1a739
website: use new highlight class (#1920)
sshane Mar 4, 2025
066277c
`test_models_trigger.yaml`: add checkout check to prevent opendbc bum…
BBBmau Mar 4, 2025
fbad366
Rivian: add VDM fault signal (#1918)
sshane Mar 5, 2025
08bce38
Rivian: accFaulted comments
sshane Mar 5, 2025
e7d08ac
Fulfillment parts (#1921)
sshane Mar 5, 2025
01c1a42
Tesla: setSpeed is vEgo while disabled (#1923)
sshane Mar 5, 2025
716d4ad
carcontroller cleanup (#1924)
sshane Mar 5, 2025
1c2d86a
docs: start extra cars clean up (#1925)
sshane Mar 5, 2025
241ba37
Rivian safety: minor clean up
sshane Mar 6, 2025
c9c4972
Rivian safety: test accel actuation message w/ stock long (#1927)
sshane Mar 6, 2025
b426c1c
Rivian: cancel command (#1910)
sshane Mar 6, 2025
40ac4ea
Rivian: fix fuzzy test_models failures (#1929)
sshane Mar 6, 2025
de65067
Rivian: don't show set speed in UI (#1842)
sshane Mar 6, 2025
c7967fd
remove a dynamic import
sshane Mar 6, 2025
5470304
Replace @parameterized.expand with @pytest.mark.parametrize to improv…
Aniurm Mar 6, 2025
12516ae
Tesla: support 0 cruise state speed (#1928)
sshane Mar 6, 2025
b6a062c
VW MEB: Reserve safety identifier (#1931)
jyoung8607 Mar 6, 2025
0180796
VW PQ: Add FW for 2012 Jetta Sportwagen TDI (#1712)
insertwittyusername Mar 6, 2025
f0cc3ca
HKG: Add FW for 2020 Hyundai Ioniq HEV (#1914)
royjr Mar 6, 2025
6751145
vin: add parse function (#1935)
sshane Mar 7, 2025
f1893c8
Rivian: values cleanup
sshane Mar 7, 2025
f50e470
Vin helper class (#1937)
sshane Mar 7, 2025
866beab
Rivian: VIN fuzzy fingerprinting (#1821)
lukasloetkolben Mar 7, 2025
10fd5b7
safety: checksum and counter are explicitly opt-out (#1939)
sshane Mar 7, 2025
5030a47
safety replay: fix torque init
sshane Mar 7, 2025
60e0183
safety replay: start to support Hyundai CAN FD
sshane Mar 7, 2025
5c5273a
safety tests: common attrs (#1948)
sshane Mar 8, 2025
3a5b13e
Rivian safety: check missing rx addr (#1949)
sshane Mar 8, 2025
0efdb71
Rivian: remove a VDM ACC fault signal (#1951)
sshane Mar 8, 2025
e371204
Merge branch 'master-0efdb71b8f2440ee51a806d057f20a553b849a3a' into m…
devtekve Mar 9, 2025
353a2d3
SP fixes for sync-20250309 (#72)
devtekve Mar 9, 2025
4b2472a
Bring replay_drive from SP into opendbc instead of panda
devtekve Mar 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test_models_trigger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
submodules: 'true'

- name: bump opendbc
if: steps.check_comment.outputs.result == 'true'
run: |
cd opendbc_repo
git fetch origin pull/${{ github.event.issue.number }}/head
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ jobs:
mutation:
name: Safety mutation tests
runs-on: ${{ github.repository == 'commaai/opendbc' && 'namespace-profile-amd64-8x16' || 'ubuntu-latest' }}
timeout-minutes: 20
timeout-minutes: 60
env:
GIT_REF: ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.event.before || format('origin/{0}', github.event.repository.default_branch) }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need master to get diff
- name: Run mutation tests
timeout-minutes: 10
timeout-minutes: 60
run: |
source setup.sh
scons -j8
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ opendbc/can/packer_pyx.cpp
opendbc/can/parser_pyx.cpp
opendbc/can/packer_pyx.html
opendbc/can/parser_pyx.html
opendbc/dbc/*_generated.dbc
opendbc/dbc/*_generated.dbc

cppcheck-addon-ctu-file-list
opendbc/safety/tests/coverage-out
7 changes: 0 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,3 @@ repos:
- --counting=detailed
- --linelength=240
- --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces
- repo: local
hooks:
- id: generator
name: dbc generator
entry: opendbc/dbc/generator/test_generator.py
language: script
pass_filenames: false
8 changes: 4 additions & 4 deletions docs/CARS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|[Under review](#upstream)|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|[Under review](#upstream)|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Focus 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Focus Hybrid 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
Expand All @@ -47,8 +47,8 @@
|Ford|Maverick 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-23|All|[Under review](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Under review](#upstream)|
|Ford|Mustang Mach-E 2021-23|All|[Upstream](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Genesis|G70 2018|All|[Upstream](#upstream)|
|Genesis|G70 2019-21|All|[Upstream](#upstream)|
|Genesis|G70 2022-23|All|[Upstream](#upstream)|
Expand Down
4 changes: 2 additions & 2 deletions opendbc/car/CARS_template.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->

# Support Information for {{car_docs_with_extras | length}} Known Cars
# Support Information for {{all_car_docs | length}} Known Cars

|{{ExtraCarsColumn | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|---|---|---|{% for _ in range((ExtraCarsColumn | length) - 3) %}{{':---:|'}}{% endfor +%}
{% for car_docs in car_docs_with_extras %}
{% for car_docs in all_car_docs %}
|{% for column in ExtraCarsColumn %}{{car_docs.get_extra_cars_column(column)}}|{% endfor %}

{% endfor %}
Expand Down
18 changes: 12 additions & 6 deletions opendbc/car/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# functions common among cars
import numpy as np
from collections import namedtuple
from dataclasses import dataclass, field
from enum import IntFlag, ReprEnum, StrEnum, EnumType, auto
from dataclasses import replace
Expand All @@ -17,7 +16,13 @@
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2

ButtonType = structs.CarState.ButtonEvent.Type
AngleRateLimit = namedtuple('AngleRateLimit', ['speed_bp', 'angle_v'])


@dataclass
class AngleSteeringLimits:
STEER_ANGLE_MAX: float
ANGLE_RATE_LIMIT_UP: tuple[list[float], list[float]]
ANGLE_RATE_LIMIT_DOWN: tuple[list[float], list[float]]


def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
Expand Down Expand Up @@ -141,19 +146,20 @@ def apply_meas_steer_torque_limits(apply_torque, apply_torque_last, motor_torque
LIMITS.STEER_ERROR_MAX, LIMITS.STEER_MAX)))


def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, steering_angle, lat_active, LIMITS):
def apply_std_steer_angle_limits(apply_angle: float, apply_angle_last: float, v_ego: float, steering_angle: float,
lat_active: bool, limits: AngleSteeringLimits) -> float:
# pick angle rate limits based on wind up/down
steer_up = apply_angle_last * apply_angle >= 0. and abs(apply_angle) > abs(apply_angle_last)
rate_limits = LIMITS.ANGLE_RATE_LIMIT_UP if steer_up else LIMITS.ANGLE_RATE_LIMIT_DOWN
rate_limits = limits.ANGLE_RATE_LIMIT_UP if steer_up else limits.ANGLE_RATE_LIMIT_DOWN

angle_rate_lim = np.interp(v_ego, rate_limits.speed_bp, rate_limits.angle_v)
angle_rate_lim = np.interp(v_ego, rate_limits[0], rate_limits[1])
new_apply_angle = np.clip(apply_angle, apply_angle_last - angle_rate_lim, apply_angle_last + angle_rate_lim)

# angle is current steering wheel angle when inactive on all angle cars
if not lat_active:
new_apply_angle = steering_angle

return float(np.clip(new_apply_angle, -LIMITS.STEER_ANGLE_MAX, LIMITS.STEER_ANGLE_MAX))
return float(np.clip(new_apply_angle, -limits.STEER_ANGLE_MAX, limits.STEER_ANGLE_MAX))


def common_fault_avoidance(fault_condition: bool, request: bool, above_limit_frames: int,
Expand Down
4 changes: 2 additions & 2 deletions opendbc/car/body/carcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def __init__(self, dbc_names, CP, CP_SP):
self.packer = CANPacker(dbc_names[Bus.main])

# PIDs
self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.turn_pid = PIDController(110, k_i=11.5, rate=1 / DT_CTRL)
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1 / DT_CTRL)

self.torque_r_filtered = 0.
self.torque_l_filtered = 0.
Expand Down
1 change: 1 addition & 0 deletions opendbc/car/car.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ struct CarParams {
psa @31;
fcaGiorgio @32;
rivian @33;
volkswagenMeb @34;
}

enum SteerControlType {
Expand Down
7 changes: 4 additions & 3 deletions opendbc/car/car_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from opendbc.car.structs import CarParams, CarParamsT, CarParamsSP
from opendbc.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from opendbc.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car
from opendbc.car.interfaces import get_interface_attr
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.values import BRANDS
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN

FRAME_FINGERPRINT = 100 # 1s
Expand All @@ -30,8 +30,9 @@ def load_interfaces(brand_names):
def _get_interface_names() -> dict[str, list[str]]:
# returns a dict of brand name and its respective models
brand_names = {}
for brand_name, brand_models in get_interface_attr("CAR").items():
brand_names[brand_name] = [model.value for model in brand_models]
for brand in BRANDS:
brand_name = brand.__module__.split('.')[-2]
brand_names[brand_name] = [model.value for model in brand]

return brand_names

Expand Down
2 changes: 1 addition & 1 deletion opendbc/car/chrysler/carcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def update(self, CC, CC_SP, CS, now_nanos):
lkas_active = CC.latActive and self.lkas_control_bit_prev

# cruise buttons
if (self.frame - self.last_button_frame)*DT_CTRL > 0.05:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.05:
das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0

# ACC cancellation
Expand Down
47 changes: 18 additions & 29 deletions opendbc/car/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,28 @@
from opendbc.car.common.basedir import BASEDIR
from opendbc.car import gen_empty_fingerprint
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarDocs, Device, ExtraCarDocs, Column, ExtraCarsColumn, CommonFootnote, PartType
from opendbc.car.car_helpers import interfaces, get_interface_attr
from opendbc.car.values import Platform, PLATFORMS
from opendbc.car.docs_definitions import BaseCarHarness, CarDocs, Device, ExtraCarDocs, Column, ExtraCarsColumn, CommonFootnote, PartType, SupportType
from opendbc.car.car_helpers import interfaces
from opendbc.car.interfaces import get_interface_attr
from opendbc.car.values import Platform
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.extra_cars import CAR as EXTRA


EXTRA_CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "docs", "CARS.md")
EXTRA_CARS_MD_TEMPLATE = os.path.join(BASEDIR, "CARS_template.md")

# TODO: merge these platforms into normal car ports with SupportType flag
ExtraPlatform = Platform | EXTRA
EXTRA_BRANDS = get_args(ExtraPlatform)
EXTRA_PLATFORMS: dict[str, ExtraPlatform] = {str(platform): platform for brand in EXTRA_BRANDS for platform in brand}


def get_params_for_docs(model, platform) -> CarParams:
cp_model, cp_platform = (model, platform) if model in interfaces else ("MOCK", MOCK.MOCK)
CP: CarParams = interfaces[cp_model][0].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
experimental_long=True, docs=True)
def get_params_for_docs(platform) -> CarParams:
cp_platform = platform if platform in interfaces else MOCK.MOCK
CP: CarParams = interfaces[cp_platform][0].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
experimental_long=True, docs=True)
return CP


Expand All @@ -41,13 +43,13 @@ def get_all_footnotes() -> dict[Enum, int]:
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}


def build_sorted_car_docs_list(platforms, footnotes=None, include_dashcam=False):
def build_sorted_car_docs_list(platforms, footnotes=None):
collected_car_docs: list[CarDocs | ExtraCarDocs] = []
for model, platform in platforms.items():
for platform in platforms.values():
car_docs = platform.config.car_docs
CP = get_params_for_docs(model, platform)
CP = get_params_for_docs(platform)

if (CP.dashcamOnly and not include_dashcam) or not len(car_docs):
if not len(car_docs):
continue

# A platform can include multiple car models
Expand All @@ -65,12 +67,7 @@ def build_sorted_car_docs_list(platforms, footnotes=None, include_dashcam=False)
# CAUTION: This function is imported by shop.comma.ai and comma.ai/vehicles, test changes carefully
def get_all_car_docs() -> list[CarDocs]:
collected_footnotes = get_all_footnotes()
sorted_list: list[CarDocs] = build_sorted_car_docs_list(PLATFORMS, footnotes=collected_footnotes)
return sorted_list


def get_car_docs_with_extras() -> list[CarDocs | ExtraCarDocs]:
sorted_list: list[CarDocs] = build_sorted_car_docs_list(EXTRA_PLATFORMS, include_dashcam=True)
sorted_list: list[CarDocs] = build_sorted_car_docs_list(EXTRA_PLATFORMS, footnotes=collected_footnotes)
return sorted_list


Expand All @@ -89,16 +86,8 @@ def generate_cars_md(all_car_docs: list[CarDocs], template_fn: str) -> str:
footnotes = [fn.value.text for fn in get_all_footnotes()]
cars_md: str = template.render(all_car_docs=all_car_docs, PartType=PartType,
group_by_make=group_by_make, footnotes=footnotes,
Device=Device, Column=Column)
return cars_md


def generate_cars_md_with_extras(car_docs_with_extras: list[CarDocs | ExtraCarDocs], template_fn: str) -> str:
with open(template_fn) as f:
template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True)

cars_md: str = template.render(car_docs_with_extras=car_docs_with_extras, PartType=PartType,
group_by_make=group_by_make, ExtraCarsColumn=ExtraCarsColumn)
Device=Device, Column=Column, ExtraCarsColumn=ExtraCarsColumn,
BaseCarHarness=BaseCarHarness, SupportType=SupportType)
return cars_md


Expand All @@ -111,5 +100,5 @@ def generate_cars_md_with_extras(car_docs_with_extras: list[CarDocs | ExtraCarDo
args = parser.parse_args()

with open(args.out, 'w') as f:
f.write(generate_cars_md_with_extras(get_car_docs_with_extras(), args.template))
f.write(generate_cars_md(get_all_car_docs(), args.template))
print(f"Generated and written to {args.out}")
7 changes: 4 additions & 3 deletions opendbc/car/docs_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class CarHarness(EnumBase):
nidec = BaseCarHarness("Honda Nidec connector")
bosch_a = BaseCarHarness("Honda Bosch A connector")
bosch_b = BaseCarHarness("Honda Bosch B connector")
bosch_c = BaseCarHarness("Honda Bosch C connector")
toyota_a = BaseCarHarness("Toyota A connector")
toyota_b = BaseCarHarness("Toyota B connector")
subaru_a = BaseCarHarness("Subaru A connector")
Expand Down Expand Up @@ -195,7 +196,7 @@ def all_parts(self):
class CommonFootnote(Enum):
EXP_LONG_AVAIL = CarFootnote(
"openpilot Longitudinal Control (Alpha) is available behind a toggle; " +
"the toggle is only available in non-release branches such as `devel` or `master-ci`.",
"the toggle is only available in non-release branches such as `devel` or `nightly-dev`.",
Column.LONGITUDINAL, docs_only=True)
EXP_LONG_DSU = CarFootnote(
"By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. " +
Expand Down Expand Up @@ -380,15 +381,15 @@ def get_detail_sentence(self, CP):
sentence_builder += " This car may not be able to take tight turns on its own."

# experimental mode
exp_link = "<a href='https://blog.comma.ai/090release/#experimental-mode' target='_blank' class='link-light-new-regular-text'>Experimental mode</a>"
exp_link = "<a href='https://blog.comma.ai/090release/#experimental-mode' target='_blank' class='highlight'>Experimental mode</a>"
if CP.openpilotLongitudinalControl and not CP.experimentalLongitudinalAvailable:
sentence_builder += f" Traffic light and stop sign handling is also available in {exp_link}."

return sentence_builder.format(car_model=f"{self.make} {self.model}", alc=alc, acc=acc)

else:
if CP.carFingerprint == "COMMA_BODY":
return "The body is a robotics dev kit that can run openpilot. <a href='https://www.commabody.com'>Learn more.</a>"
return "The body is a robotics dev kit that can run openpilot. <a href='https://www.commabody.com' target='_blank' class='highlight'>Learn more.</a>"
else:
raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}")

Expand Down
23 changes: 20 additions & 3 deletions opendbc/car/ford/carcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,32 @@
LongCtrlState = structs.CarControl.Actuators.LongControlState
VisualAlert = structs.CarControl.HUDControl.VisualAlert

# ISO 11270
ISO_LATERAL_ACCEL = 3.0 # m/s^2 # TODO: import from test lateral limits file?

def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw, steering_angle, lat_active):
# Limit to average banked road since safety doesn't have the roll
EARTH_G = 9.81
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (EARTH_G * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2


def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw, steering_angle, lat_active, CP):
# No blending at low speed due to lack of torque wind-up and inaccurate current curvature
if v_ego_raw > 9:
apply_curvature = np.clip(apply_curvature, current_curvature - CarControllerParams.CURVATURE_ERROR,
current_curvature + CarControllerParams.CURVATURE_ERROR)

# Curvature rate limit after driver torque limit
return apply_std_steer_angle_limits(apply_curvature, apply_curvature_last, v_ego_raw, steering_angle, lat_active, CarControllerParams)
apply_curvature = apply_std_steer_angle_limits(apply_curvature, apply_curvature_last, v_ego_raw, steering_angle, lat_active, CarControllerParams.ANGLE_LIMITS)

# Ford Q4/CAN FD has more torque available compared to Q3/CAN so we limit it based on lateral acceleration.
# Safety is not aware of the road roll so we subtract a conservative amount at all times
if CP.flags & FordFlags.CANFD:
# Limit curvature to conservative max lateral acceleration
curvature_accel_limit = MAX_LATERAL_ACCEL / (max(v_ego_raw, 1) ** 2)
apply_curvature = float(np.clip(apply_curvature, -curvature_accel_limit, curvature_accel_limit))

return apply_curvature


def apply_creep_compensation(accel: float, v_ego: float) -> float:
Expand Down Expand Up @@ -71,7 +88,7 @@ def update(self, CC, CC_SP, CS, now_nanos):
# apply rate limits, curvature error limit, and clip to signal range
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
CS.out.vEgoRaw, 0., CC.latActive)
CS.out.vEgoRaw, 0., CC.latActive, self.CP)

if self.CP.flags & FordFlags.CANFD:
# TODO: extended mode
Expand Down
2 changes: 1 addition & 1 deletion opendbc/car/ford/fingerprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x730, None): [
b'RL38-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'RL38-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.FORD_MUSTANG_MACH_E_MK1: {
Expand Down
10 changes: 10 additions & 0 deletions opendbc/car/ford/interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np
from opendbc.car import Bus, get_safety_config, structs
from opendbc.car.carlog import carlog
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.ford.fordcan import CanBus
from opendbc.car.ford.values import CarControllerParams, DBC, Ecu, FordFlags, RADAR, FordSafetyFlags
Expand Down Expand Up @@ -47,17 +48,26 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime

if ret.flags & FordFlags.CANFD:
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.CANFD.value

# TRON (SecOC) platforms are not supported
# LateralMotionControl2, ACCDATA are 16 bytes on these platforms
if len(fingerprint[CAN.camera]):
if fingerprint[CAN.camera].get(0x3d6) != 8 or fingerprint[CAN.camera].get(0x186) != 8:
carlog.error('dashcamOnly: SecOC is unsupported')
ret.dashcamOnly = True
else:
# Lock out if the car does not have needed lateral and longitudinal control APIs.
# Note that we also check CAN for adaptive cruise, but no known signal for LCA exists
pscm_config = next((fw for fw in car_fw if fw.ecu == Ecu.eps and b'\x22\xDE\x01' in fw.request), None)
if pscm_config:
if len(pscm_config.fwVersion) != 24:
carlog.error('dashcamOnly: Invalid EPS FW version')
ret.dashcamOnly = True
else:
config_tja = pscm_config.fwVersion[7] # Traffic Jam Assist
config_lca = pscm_config.fwVersion[8] # Lane Centering Assist
if config_tja != 0xFF or config_lca != 0xFF:
carlog.error('dashcamOnly: Car lacks required lateral control APIs')
ret.dashcamOnly = True

# Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1
Expand Down
Loading
Loading