#!/usr/bin/env python3
import numpy as np
from opendbc . car import get_safety_config , structs
from opendbc . car . common . conversions import Conversions as CV
from opendbc . car . disable_ecu import disable_ecu
from opendbc . car . honda . hondacan import CanBus
from opendbc . car . honda . values import CarControllerParams , HondaFlags , CAR , HONDA_BOSCH , HONDA_BOSCH_CANFD , \
HONDA_NIDEC_ALT_SCM_MESSAGES , HONDA_BOSCH_RADARLESS , HondaSafetyFlags
from opendbc . car . honda . carcontroller import CarController
from opendbc . car . honda . carstate import CarState
from opendbc . car . honda . radar_interface import RadarInterface
from opendbc . car . interfaces import CarInterfaceBase
TransmissionType = structs . CarParams . TransmissionType
class CarInterface ( CarInterfaceBase ) :
CarState = CarState
CarController = CarController
RadarInterface = RadarInterface
@staticmethod
def get_pid_accel_limits ( CP , current_speed , cruise_speed ) :
if CP . carFingerprint in HONDA_BOSCH :
return CarControllerParams . BOSCH_ACCEL_MIN , CarControllerParams . BOSCH_ACCEL_MAX
else :
# NIDECs don't allow acceleration near cruise_speed,
# so limit limits of pid to prevent windup
ACCEL_MAX_VALS = [ CarControllerParams . NIDEC_ACCEL_MAX , 0.2 ]
ACCEL_MAX_BP = [ cruise_speed - 2. , cruise_speed - .2 ]
return CarControllerParams . NIDEC_ACCEL_MIN , np . interp ( current_speed , ACCEL_MAX_BP , ACCEL_MAX_VALS )
@staticmethod
def _get_params ( ret : structs . CarParams , candidate , fingerprint , car_fw , alpha_long , docs ) - > structs . CarParams :
ret . brand = " honda "
CAN = CanBus ( ret , fingerprint )
# Recent test route is needed to undashcam these cars
ret . dashcamOnly = candidate in HONDA_BOSCH_CANFD
if candidate in HONDA_BOSCH :
cfgs = [ get_safety_config ( structs . CarParams . SafetyModel . hondaBosch ) ]
if candidate in HONDA_BOSCH_CANFD and CAN . pt > = 4 :
cfgs . insert ( 0 , get_safety_config ( structs . CarParams . SafetyModel . noOutput ) )
ret . safetyConfigs = cfgs
ret . radarUnavailable = True
# Disable the radar and let openpilot control longitudinal
# WARNING: THIS DISABLES AEB!
# If Bosch radarless, this blocks ACC messages from the camera
# TODO: get radar disable working on Bosch CANFD
ret . alphaLongitudinalAvailable = candidate not in HONDA_BOSCH_CANFD
ret . openpilotLongitudinalControl = alpha_long
ret . pcmCruise = not ret . openpilotLongitudinalControl
else :
ret . safetyConfigs = [ get_safety_config ( structs . CarParams . SafetyModel . hondaNidec ) ]
ret . openpilotLongitudinalControl = True
ret . pcmCruise = True
if candidate == CAR . HONDA_CRV_5G :
ret . enableBsm = 0x12f8bfa7 in fingerprint [ CAN . radar ]
# Detect Bosch cars with new HUD msgs
if any ( 0x33DA in f for f in fingerprint . values ( ) ) :
ret . flags | = HondaFlags . BOSCH_EXT_HUD . value
# Accord ICE 1.5T CVT has different gearbox message
if candidate == CAR . HONDA_ACCORD and 0x191 in fingerprint [ CAN . pt ] :
ret . transmissionType = TransmissionType . cvt
# Civic Type R is missing 0x191 and 0x1A3
elif candidate == CAR . HONDA_CIVIC_2022 and all ( msg not in fingerprint [ CAN . pt ] for msg in ( 0x191 , 0x1A3 ) ) :
ret . transmissionType = TransmissionType . manual
# New Civics don't have 0x191, but do have 0x1A3
elif candidate == CAR . HONDA_CIVIC_2022 and 0x1A3 in fingerprint [ CAN . pt ] :
ret . transmissionType = TransmissionType . cvt
# Certain Hondas have an extra steering sensor at the bottom of the steering rack,
# which improves controls quality as it removes the steering column torsion from feedback.
# Tire stiffness factor fictitiously lower if it includes the steering column torsion effect.
# For modeling details, see p.198-200 in "The Science of Vehicle Dynamics (2014), M. Guiggiani"
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 ] , [ 0 ] ]
ret . lateralTuning . pid . kiBP , ret . lateralTuning . pid . kpBP = [ [ 0. ] , [ 0. ] ]
ret . lateralTuning . pid . kf = 0.00006 # conservative feed-forward
if candidate in HONDA_BOSCH :
ret . longitudinalActuatorDelay = 0.5 # s
if candidate in HONDA_BOSCH_RADARLESS :
ret . stopAccel = CarControllerParams . BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
else :
# default longitudinal tuning for all hondas
ret . longitudinalTuning . kiBP = [ 0. , 5. , 35. ]
ret . longitudinalTuning . kiV = [ 1.2 , 0.8 , 0.5 ]
eps_modified = False
for fw in car_fw :
if fw . ecu == " eps " and b " , " in fw . fwVersion :
eps_modified = True
if candidate == CAR . HONDA_CIVIC :
if eps_modified :
# stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE
# stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680
# modified request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x2880, 0x3180
# stock filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108
# modified filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0400, 0x0480
# note: max request allowed is 4096, but request is capped at 3840 in firmware, so modifications result in 2x max
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 2560 , 8000 ] , [ 0 , 2560 , 3840 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.3 ] , [ 0.1 ] ]
else :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 2560 ] , [ 0 , 2560 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 1.1 ] , [ 0.33 ] ]
elif candidate in ( CAR . HONDA_CIVIC_BOSCH , CAR . HONDA_CIVIC_BOSCH_DIESEL ) :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.8 ] , [ 0.24 ] ]
elif candidate == CAR . HONDA_CIVIC_2022 :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpBP , ret . lateralTuning . pid . kpV = [ [ 0 , 10 ] , [ 0.05 , 0.5 ] ]
ret . lateralTuning . pid . kiBP , ret . lateralTuning . pid . kiV = [ [ 0 , 10 ] , [ 0.0125 , 0.125 ] ]
elif candidate == CAR . HONDA_ACCORD :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
if eps_modified :
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.3 ] , [ 0.09 ] ]
else :
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.6 ] , [ 0.18 ] ]
elif candidate == CAR . ACURA_ILX :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 3840 ] , [ 0 , 3840 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.8 ] , [ 0.24 ] ]
elif candidate in ( CAR . HONDA_CRV , CAR . HONDA_CRV_EU ) :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 1000 ] , [ 0 , 1000 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.8 ] , [ 0.24 ] ]
ret . wheelSpeedFactor = 1.025
elif candidate == CAR . HONDA_CRV_5G :
if eps_modified :
# stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F
# stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400
# modified request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x1ACD, 0x239A, 0x2800
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 2560 , 10000 ] , [ 0 , 2560 , 3840 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.21 ] , [ 0.07 ] ]
else :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 3840 ] , [ 0 , 3840 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.64 ] , [ 0.192 ] ]
ret . wheelSpeedFactor = 1.025
elif candidate == CAR . HONDA_CRV_HYBRID :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.6 ] , [ 0.18 ] ]
ret . wheelSpeedFactor = 1.025
elif candidate == CAR . HONDA_FIT :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.2 ] , [ 0.05 ] ]
elif candidate == CAR . HONDA_FREED :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.2 ] , [ 0.05 ] ]
elif candidate in ( CAR . HONDA_HRV , CAR . HONDA_HRV_3G ) :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ]
if candidate == CAR . HONDA_HRV :
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.16 ] , [ 0.025 ] ]
ret . wheelSpeedFactor = 1.025
else :
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.8 ] , [ 0.24 ] ] # TODO: can probably use some tuning
elif candidate == CAR . ACURA_RDX :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 1000 ] , [ 0 , 1000 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.8 ] , [ 0.24 ] ]
elif candidate == CAR . ACURA_RDX_3G :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 3840 ] , [ 0 , 3840 ] ]
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.2 ] , [ 0.06 ] ]
elif candidate in ( CAR . HONDA_ODYSSEY , CAR . HONDA_ODYSSEY_CHN ) :
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.28 ] , [ 0.08 ] ]
if candidate == CAR . HONDA_ODYSSEY_CHN :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 32767 ] , [ 0 , 32767 ] ] # TODO: determine if there is a dead zone at the top end
else :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
elif candidate in ( CAR . HONDA_PILOT , CAR . HONDA_PILOT_4G ) :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.38 ] , [ 0.11 ] ]
elif candidate == CAR . HONDA_RIDGELINE :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.38 ] , [ 0.11 ] ]
elif candidate == CAR . HONDA_INSIGHT :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.6 ] , [ 0.18 ] ]
elif candidate == CAR . HONDA_E :
ret . lateralParams . torqueBP , ret . lateralParams . torqueV = [ [ 0 , 4096 ] , [ 0 , 4096 ] ] # TODO: determine if there is a dead zone at the top end
ret . lateralTuning . pid . kpV , ret . lateralTuning . pid . kiV = [ [ 0.6 ] , [ 0.18 ] ] # TODO: can probably use some tuning
else :
raise ValueError ( f " unsupported car { candidate } " )
# These cars use alternate user brake msg (0x1BE)
# TODO: Only detect feature for Accord/Accord Hybrid, not all Bosch DBCs have BRAKE_MODULE
if 0x1BE in fingerprint [ CAN . pt ] and candidate in ( CAR . HONDA_ACCORD , CAR . HONDA_HRV_3G ) :
ret . flags | = HondaFlags . BOSCH_ALT_BRAKE . value
if ret . flags & HondaFlags . BOSCH_ALT_BRAKE :
ret . safetyConfigs [ - 1 ] . safetyParam | = HondaSafetyFlags . ALT_BRAKE . value
# These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)
if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES :
ret . safetyConfigs [ - 1 ] . safetyParam | = HondaSafetyFlags . NIDEC_ALT . value
if ret . openpilotLongitudinalControl and candidate in HONDA_BOSCH :
ret . safetyConfigs [ - 1 ] . safetyParam | = HondaSafetyFlags . BOSCH_LONG . value
if candidate in HONDA_BOSCH_RADARLESS :
ret . safetyConfigs [ - 1 ] . safetyParam | = HondaSafetyFlags . RADARLESS . value
# min speed to enable ACC. if car can do stop and go, then set enabling speed
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc
ret . autoResumeSng = candidate in ( HONDA_BOSCH | { CAR . HONDA_CIVIC } )
ret . minEnableSpeed = - 1. if ret . autoResumeSng else 25.51 * CV . MPH_TO_MS
ret . steerActuatorDelay = 0.1
ret . steerLimitTimer = 0.8
ret . radarDelay = 0.1
return ret
@staticmethod
def init ( CP , can_recv , can_send ) :
if CP . carFingerprint in ( HONDA_BOSCH - HONDA_BOSCH_RADARLESS ) and CP . openpilotLongitudinalControl :
disable_ecu ( can_recv , can_send , bus = CanBus ( CP ) . pt , addr = 0x18DAB0F1 , com_cont_req = b ' \x28 \x83 \x03 ' )