Skip to content

Commit

Permalink
HKG: Car Port for Hyundai Palisade and Kia Telluride 2023-24 (HDA2)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnyhaibin committed Sep 27, 2024
1 parent f253ee2 commit c76da5a
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 28 deletions.
14 changes: 9 additions & 5 deletions opendbc/car/hyundai/carcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,13 @@ def update(self, CC, CS, now_nanos):
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append(make_tester_present_msg(0x7b1, self.CAN.ECAN, suppress_response=True))

# Common shared configuration

hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
can_canfd_hybrid = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID)

# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
if self.CP.carFingerprint in CANFD_CAR or can_canfd_hybrid:
hda2_long = hda2 and self.CP.openpilotLongitudinalControl

# steering control
Expand Down Expand Up @@ -132,7 +136,7 @@ def update(self, CC, CS, now_nanos):
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=(hda2 and can_canfd_hybrid)))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
Expand Down Expand Up @@ -174,12 +178,12 @@ def create_button_messages(self, CC: structs.CarControl, CS: CarState, use_clu11
can_sends = []
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP, self.CAN))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25)
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP, self.CAN)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
Expand Down
35 changes: 25 additions & 10 deletions opendbc/car/hyundai/carstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,12 @@ def update(self, cp, cp_cam, *_) -> structs.CarState:
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
else:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
scc_bus = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else "SCC11"
ret.cruiseState.available = cp_cruise.vl[scc_bus]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
ret.cruiseState.standstill = cp_cruise.vl[scc_bus]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl[scc_bus]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl[scc_bus]["VSetDis"] * speed_conv

# TODO: Find brake pressure
ret.brake = 0
Expand Down Expand Up @@ -151,7 +152,7 @@ def update(self, cp, cp_cam, *_) -> structs.CarState:

ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))

if not self.CP.openpilotLongitudinalControl:
if not self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID):
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
Expand All @@ -172,6 +173,9 @@ def update(self, cp, cp_cam, *_) -> structs.CarState:
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])

if self.CP.flags & (HyundaiFlags.CAN_CANFD_HYBRID | HyundaiFlags.CANFD_HDA2):
self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x2a4"])

if self.CP.openpilotLongitudinalControl:
ret.buttonEvents = create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT)

Expand Down Expand Up @@ -266,9 +270,11 @@ def get_can_parser(self, CP):
if CP.carFingerprint in CANFD_CAR:
return self.get_can_parser_canfd(CP)

mdps12_freq = 100 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50

messages = [
# address, frequency
("MDPS12", 50),
("MDPS12", mdps12_freq),
("TCS11", 100),
("TCS13", 50),
("TCS15", 10),
Expand All @@ -284,14 +290,18 @@ def get_can_parser(self, CP):

if not CP.openpilotLongitudinalControl and CP.carFingerprint not in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]

if not (CP.flags & HyundaiFlags.CAN_CANFD_HYBRID):
messages.append(("SCC11", 50))

if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))

if CP.enableBsm:
messages.append(("LCA11", 50))
lca11_freq = 20 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50
messages.append(("LCA11", lca11_freq))

if CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
messages.append(("E_EMS11", 50))
Expand All @@ -310,7 +320,8 @@ def get_can_parser(self, CP):
else:
messages.append(("LVR12", 100))

return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
bus = CanBus(CP).ECAN if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0
return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus)

@staticmethod
def get_cam_can_parser(CP):
Expand All @@ -321,6 +332,9 @@ def get_cam_can_parser(CP):
("LKAS11", 100)
]

if CP.flags & (HyundaiFlags.CAN_CANFD_HYBRID | HyundaiFlags.CANFD_HDA2):
messages.append(("CAM_0x2a4", 20))

if not CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
Expand All @@ -330,7 +344,8 @@ def get_cam_can_parser(CP):
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))

return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2)
bus = CanBus(CP).CAM if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 2
return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus)

def get_can_parser_canfd(self, CP):
messages = [
Expand Down
17 changes: 17 additions & 0 deletions opendbc/car/hyundai/fingerprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,4 +1155,21 @@
b'\xf1\x00US4_ RDR ----- 1.00 1.00 99110-CG000 ',
],
},
CAR.HYUNDAI_PALISADE_2023: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.04 99211-S8150 220622',
b'\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9150 220708',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.03 99211-S8150 220527',
b'\xf1\x00ON MFC AT USA LHD 1.00 1.00 99211-S9160 230303',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.00 99211-S8600 230317',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.01 99211-S8600 230817',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00LX2_ SCC ----- 1.00 1.01 99110-S8150 ',
b'\xf1\x00ON__ SCC ----- 1.00 1.01 99110-S9150 ',
b'\xf1\x00LX2_ SCC ----- 1.00 1.00 99110-S8150 ',
b'\xf1\x00ON__ SCC ----- 1.00 1.00 99110-S9160 ',
b'\xf1\x00LX2_ SCC ----- 1.00 1.00 99110-S8600 ',
],
},
}
12 changes: 9 additions & 3 deletions opendbc/car/hyundai/hyundaican.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def create_lkas11(packer, frame, CP, apply_steer, steer_req,
return packer.make_can_msg("LKAS11", 0, values)


def create_clu11(packer, frame, clu11, button, CP):
def create_clu11(packer, frame, clu11, button, CP, CAN):
values = {s: clu11[s] for s in [
"CF_Clu_CruiseSwState",
"CF_Clu_CruiseSwMain",
Expand All @@ -112,8 +112,14 @@ def create_clu11(packer, frame, clu11, button, CP):
]}
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = frame % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0

if CP.flags & HyundaiFlags.CAMERA_SCC: # send buttons to camera on camera-scc based cars
bus = 2
elif CP.flags & HyundaiFlags.CAN_CANFD_HYBRID:
bus = CAN.ECAN
else:
bus = 0

return packer.make_can_msg("CLU11", bus, values)


Expand Down
26 changes: 20 additions & 6 deletions opendbc/car/hyundai/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime
hda2 = 0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
CAN = CanBus(None, fingerprint, hda2)

# detect HDA2 with HDA2-only steering messages
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value

if candidate in CANFD_CAR:
# Shared configuration for CAN-FD cars
ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
Expand All @@ -30,9 +34,7 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime
if 0x105 in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.HYBRID.value

# detect HDA2 with ADAS Driving ECU
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
else:
Expand All @@ -56,7 +58,6 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime
ret.safetyConfigs = cfgs

if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
Expand All @@ -67,7 +68,8 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime
else:
# Shared configuration for non CAN-FD cars
ret.experimentalLongitudinalAvailable = candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR)
ret.enableBsm = 0x58b in fingerprint[0]
bsm_bus = CAN.ECAN if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0
ret.enableBsm = 0x58b in fingerprint[bsm_bus]

# Send LFA message on cars with HDA
if 0x485 in fingerprint[2]:
Expand All @@ -81,11 +83,22 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime
# these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiLegacy)]
else:
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundai, 0)]
cfgs = [get_safety_config(structs.CarParams.SafetyModel.hyundai), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs

if candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC

if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAN_CANFD_HYBRID

# Common shared configuration

if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2

# Common lateral control setup

ret.centerToFront = ret.wheelbase * 0.4
Expand Down Expand Up @@ -122,7 +135,8 @@ def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experime

# Dashcam cars are missing a test route, or otherwise need validation
# TODO: Optima Hybrid 2017 uses a different SCC12 checksum
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
# TODO: Palisade/Telluride 2023-24 non-HDA2 will be supported in another PR
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, } or (candidate in (CAR.HYUNDAI_PALISADE_2023, ) and not hda2)

return ret

Expand Down
5 changes: 3 additions & 2 deletions opendbc/car/hyundai/tests/test_hyundai.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def test_expected_platform_codes(self, subtests):

# Third and fourth character are usually EV/hybrid identifiers
codes = {code.split(b"-")[0][:2] for code, _ in get_platform_codes(fws)}
if car_model == CAR.HYUNDAI_PALISADE:
if car_model in (CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_PALISADE_2023):
assert codes == {b"LX", b"ON"}, f"Car has unexpected platform codes: {car_model} {codes}"
elif car_model == CAR.HYUNDAI_KONA_EV and ecu[0] == Ecu.fwdCamera:
assert codes == {b"OE", b"OS"}, f"Car has unexpected platform codes: {car_model} {codes}"
Expand All @@ -139,7 +139,8 @@ def test_expected_platform_codes(self, subtests):
def test_platform_code_ecus_available(self, subtests):
# TODO: add queries for these non-CAN FD cars to get EPS
no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H,
CAR.KIA_OPTIMA_H_G4_FL, CAR.HYUNDAI_SONATA_LF, CAR.HYUNDAI_TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA}
CAR.KIA_OPTIMA_H_G4_FL, CAR.HYUNDAI_SONATA_LF, CAR.HYUNDAI_TUCSON, CAR.GENESIS_G90,
CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_PALISADE_2023}

# Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms
for car_model, ecus in FW_VERSIONS.items():
Expand Down
20 changes: 18 additions & 2 deletions opendbc/car/hyundai/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ class HyundaiFlags(IntFlag):

MIN_STEER_32_MPH = 2 ** 23

CAN_CANFD_HYBRID = 2 ** 24


class Footnote(Enum):
CANFD = CarFootnote(
Expand Down Expand Up @@ -123,10 +125,13 @@ def init(self):
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)

if self.flags & HyundaiFlags.CAN_CANFD_HYBRID:
self.dbc_dict = dbc_dict('hyundai_palisade_2023_generated', None)


@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_canfd", None))
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_canfd_generated", None))

def init(self):
self.flags |= HyundaiFlags.CANFD
Expand Down Expand Up @@ -294,6 +299,14 @@ class CAR(Platforms):
CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_PALISADE_2023 = HyundaiPlatformConfig(
[
HyundaiCarDocs("Hyundai Palisade (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_r])),
HyundaiCarDocs("Kia Telluride (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])),
],
HYUNDAI_PALISADE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_HYBRID | HyundaiFlags.UNSUPPORTED_LONGITUDINAL,
)
HYUNDAI_VELOSTER = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e]))],
CarSpecs(mass=2917 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75 * 1.15, tireStiffnessFactor=0.5),
Expand Down Expand Up @@ -585,7 +598,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str
# Non-electric CAN FD platforms often do not have platform code specifiers needed
# to distinguish between hybrid and ICE. All EVs so far are either exclusively
# electric or specify electric in the platform code.
fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)}
fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - CAN_CANFD_HYBRID_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)}
candidates: set[str] = set()

for candidate, fws in offline_fw_versions.items():
Expand Down Expand Up @@ -749,6 +762,9 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str
# responds with 0x7F2822 - 'conditions not correct'
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE)

# These cars have both CAN and CAN-FD definitions required in openpilot
CAN_CANFD_HYBRID_CAR = CAR.with_flags(HyundaiFlags.CAN_CANFD_HYBRID)

# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)

Expand Down
1 change: 1 addition & 0 deletions opendbc/car/tests/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class CarTestRoute(NamedTuple):
CarTestRoute("fc19648042eb6896|2023-08-16--11-43-27", HYUNDAI.KIA_SORENTO_HEV_4TH_GEN, segment=14),
CarTestRoute("628935d7d3e5f4f7|2022-11-30--01-12-46", HYUNDAI.KIA_SORENTO_HEV_4TH_GEN), # plug-in hybrid
CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.HYUNDAI_PALISADE),
CarTestRoute("696748e0ac8082fb|2023-08-31--14-25-28", HYUNDAI.HYUNDAI_PALISADE_2023),
CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.HYUNDAI_IONIQ_5), # HDA2
CarTestRoute("eb4eae1476647463|2023-08-26--18-07-04", HYUNDAI.HYUNDAI_IONIQ_6, segment=6), # HDA2
CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.HYUNDAI_IONIQ_PHEV_2019),
Expand Down
1 change: 1 addition & 0 deletions opendbc/car/torque_data/override.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_STARIA_4TH_GEN" = [1.8, 2.0, 0.15]
"GENESIS_GV70_ELECTRIFIED_1ST_GEN" = [1.9, 1.9, 0.09]
"GENESIS_G80_2ND_GEN_FL" = [2.5819356441497803, 2.5, 0.11244568973779678]
"HYUNDAI_PALISADE_2023" = [2.32, 2.32, 0.05]

# Dashcam or fallback configured as ideal car
"MOCK" = [10.0, 10, 0.0]
Expand Down

0 comments on commit c76da5a

Please sign in to comment.