Ford: add F-150 2023 support (dashcam only) (#28974)

old-commit-hash: 17e71cdf44
beeps
Cameron Clough 2 years ago committed by GitHub
parent 8c026eff7c
commit 381a6a35fd
  1. 32
      selfdrive/car/ford/carstate.py
  2. 7
      selfdrive/car/ford/interface.py
  3. 34
      selfdrive/car/ford/values.py
  4. 1
      selfdrive/car/tests/routes.py
  5. 5
      selfdrive/car/tests/test_fw_fingerprint.py
  6. 1
      selfdrive/car/torque_data/override.yaml

@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.ford.fordcan import CanBus from selfdrive.car.ford.fordcan import CanBus
from selfdrive.car.ford.values import DBC, CarControllerParams from selfdrive.car.ford.values import CANFD_CARS, CarControllerParams, DBC
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType
@ -55,6 +55,10 @@ class CarState(CarStateBase):
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3) ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
# ret.espDisabled = False # TODO: find traction control signal # ret.espDisabled = False # TODO: find traction control signal
if self.CP.carFingerprint in CANFD_CARS:
# this signal is always 0 on non-CAN FD cars
ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3)
# cruise state # cruise state
ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * CV.MPH_TO_MS ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * CV.MPH_TO_MS
ret.cruiseState.enabled = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (4, 5) ret.cruiseState.enabled = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (4, 5)
@ -93,8 +97,9 @@ class CarState(CarStateBase):
# blindspot sensors # blindspot sensors
if self.CP.enableBsm: if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0 cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CARS else cp
ret.rightBlindspot = cp.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0 ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
# Stock steering buttons so that we can passthru blinkers etc. # Stock steering buttons so that we can passthru blinkers etc.
self.buttons_stock_values = cp.vl["Steering_Data_FD1"] self.buttons_stock_values = cp.vl["Steering_Data_FD1"]
@ -181,12 +186,19 @@ class CarState(CarStateBase):
("Cluster_Info1_FD1", 10), ("Cluster_Info1_FD1", 10),
("SteeringPinion_Data", 100), ("SteeringPinion_Data", 100),
("EPAS_INFO", 50), ("EPAS_INFO", 50),
("Lane_Assist_Data3_FD1", 33),
("Steering_Data_FD1", 10), ("Steering_Data_FD1", 10),
("BodyInfo_3_FD1", 2), ("BodyInfo_3_FD1", 2),
("RCMStatusMessage2_FD1", 10), ("RCMStatusMessage2_FD1", 10),
] ]
if CP.carFingerprint in CANFD_CARS:
signals += [
("LatCtlSte_D_Stat", "Lane_Assist_Data3_FD1"), # PSCM lateral control status
]
checks += [
("Lane_Assist_Data3_FD1", 33),
]
if CP.transmissionType == TransmissionType.automatic: if CP.transmissionType == TransmissionType.automatic:
signals += [ signals += [
("TrnRng_D_RqGsm", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position ("TrnRng_D_RqGsm", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position
@ -204,7 +216,7 @@ class CarState(CarStateBase):
("BCM_Lamp_Stat_FD1", 1), ("BCM_Lamp_Stat_FD1", 1),
] ]
if CP.enableBsm: if CP.enableBsm and CP.carFingerprint not in CANFD_CARS:
signals += [ signals += [
("SodDetctLeft_D_Stat", "Side_Detect_L_Stat"), # Blindspot sensor, left ("SodDetctLeft_D_Stat", "Side_Detect_L_Stat"), # Blindspot sensor, left
("SodDetctRight_D_Stat", "Side_Detect_R_Stat"), # Blindspot sensor, right ("SodDetctRight_D_Stat", "Side_Detect_R_Stat"), # Blindspot sensor, right
@ -274,4 +286,14 @@ class CarState(CarStateBase):
("IPMA_Data", 1), ("IPMA_Data", 1),
] ]
if CP.enableBsm and CP.carFingerprint in CANFD_CARS:
signals += [
("SodDetctLeft_D_Stat", "Side_Detect_L_Stat"), # Blindspot sensor, left
("SodDetctRight_D_Stat", "Side_Detect_R_Stat"), # Blindspot sensor, right
]
checks += [
("Side_Detect_L_Stat", 5),
("Side_Detect_R_Stat", 5),
]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus(CP).camera) return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus(CP).camera)

@ -15,6 +15,7 @@ class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "ford" ret.carName = "ford"
ret.dashcamOnly = candidate in {CAR.F_150_MK14}
ret.radarUnavailable = True ret.radarUnavailable = True
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
@ -50,6 +51,12 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16.8 ret.steerRatio = 16.8
ret.mass = 2050 + STD_CARGO_KG ret.mass = 2050 + STD_CARGO_KG
elif candidate == CAR.F_150_MK14:
# required trim only on SuperCrew
ret.wheelbase = 3.69
ret.steerRatio = 17.0
ret.mass = 2000 + STD_CARGO_KG
elif candidate == CAR.FOCUS_MK4: elif candidate == CAR.FOCUS_MK4:
ret.wheelbase = 2.7 ret.wheelbase = 2.7
ret.steerRatio = 15.0 ret.steerRatio = 15.0

@ -1,7 +1,7 @@
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Dict, List, Set, Union from typing import Dict, List, Union
from cereal import car from cereal import car
from selfdrive.car import AngleRateLimit, dbc_dict from selfdrive.car import AngleRateLimit, dbc_dict
@ -44,11 +44,12 @@ class CAR:
BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN" BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN"
ESCAPE_MK4 = "FORD ESCAPE 4TH GEN" ESCAPE_MK4 = "FORD ESCAPE 4TH GEN"
EXPLORER_MK6 = "FORD EXPLORER 6TH GEN" EXPLORER_MK6 = "FORD EXPLORER 6TH GEN"
F_150_MK14 = "FORD F-150 14TH GEN"
FOCUS_MK4 = "FORD FOCUS 4TH GEN" FOCUS_MK4 = "FORD FOCUS 4TH GEN"
MAVERICK_MK1 = "FORD MAVERICK 1ST GEN" MAVERICK_MK1 = "FORD MAVERICK 1ST GEN"
CANFD_CARS: Set[str] = set() CANFD_CARS = {CAR.F_150_MK14}
class RADAR: class RADAR:
@ -58,6 +59,9 @@ class RADAR:
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR)) DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR))
# F-150 radar is not yet supported
DBC[CAR.F_150_MK14] = dbc_dict("ford_lincoln_base_pt", None)
class Footnote(Enum): class Footnote(Enum):
FOCUS = CarFootnote( FOCUS = CarFootnote(
@ -87,6 +91,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
FordCarInfo("Ford Explorer 2020-22"), FordCarInfo("Ford Explorer 2020-22"),
FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"), FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"),
], ],
CAR.F_150_MK14: FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"),
CAR.FOCUS_MK4: FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), CAR.FOCUS_MK4: FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]),
CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022-23", "Co-Pilot360 Assist"), CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022-23", "Co-Pilot360 Assist"),
} }
@ -96,13 +101,17 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request( Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine],
), ),
Request( Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
bus=0, bus=0,
whitelist_ecus=[Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.shiftByWire], ),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
bus=0,
auxiliary=True,
), ),
], ],
extra_ecus=[ extra_ecus=[
@ -197,6 +206,23 @@ FW_VERSIONS = {
b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
}, },
CAR.F_150_MK14: {
(Ecu.eps, 0x730, None): [
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x706, None): [
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x7E0, None): [
b'PL3A-14C204-BRB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.FOCUS_MK4: { CAR.FOCUS_MK4: {
(Ecu.eps, 0x730, None): [ (Ecu.eps, 0x730, None): [
b'JX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'JX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',

@ -16,6 +16,7 @@ from selfdrive.car.body.values import CAR as COMMA
# TODO: add routes for these cars # TODO: add routes for these cars
non_tested_cars = [ non_tested_cars = [
FORD.F_150_MK14,
GM.CADILLAC_ATS, GM.CADILLAC_ATS,
GM.HOLDEN_ASTRA, GM.HOLDEN_ASTRA,
GM.MALIBU, GM.MALIBU,

@ -220,12 +220,12 @@ class TestFwFingerprintTiming(unittest.TestCase):
print(f'get_vin, query time={vin_time / self.N} seconds') print(f'get_vin, query time={vin_time / self.N} seconds')
def test_fw_query_timing(self): def test_fw_query_timing(self):
total_ref_time = 6.2 total_ref_time = 6.7
brand_ref_times = { brand_ref_times = {
1: { 1: {
'body': 0.1, 'body': 0.1,
'chrysler': 0.3, 'chrysler': 0.3,
'ford': 0.2, 'ford': 0.3,
'honda': 0.5, 'honda': 0.5,
'hyundai': 0.7, 'hyundai': 0.7,
'mazda': 0.2, 'mazda': 0.2,
@ -236,6 +236,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
'volkswagen': 0.2, 'volkswagen': 0.2,
}, },
2: { 2: {
'ford': 0.4,
'hyundai': 1.1, 'hyundai': 1.1,
} }
} }

@ -19,6 +19,7 @@ TESLA AP2 MODEL S: [.nan, 2.5, .nan]
FORD BRONCO SPORT 1ST GEN: [.nan, 1.5, .nan] FORD BRONCO SPORT 1ST GEN: [.nan, 1.5, .nan]
FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan] FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan]
FORD EXPLORER 6TH GEN: [.nan, 1.5, .nan] FORD EXPLORER 6TH GEN: [.nan, 1.5, .nan]
FORD F-150 14TH GEN: [.nan, 1.5, .nan]
FORD FOCUS 4TH GEN: [.nan, 1.5, .nan] FORD FOCUS 4TH GEN: [.nan, 1.5, .nan]
FORD MAVERICK 1ST GEN: [.nan, 1.5, .nan] FORD MAVERICK 1ST GEN: [.nan, 1.5, .nan]
### ###

Loading…
Cancel
Save