Update Chrysler vehicle parameters and controller behavior with focused platform tests.
This commit is contained in:
firestar5683
2026-09-25 14:53:18 -05:00
parent 694b460f80
commit d705dffb1f
6 changed files with 185 additions and 6 deletions
@@ -2,7 +2,8 @@ from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.car.lateral import apply_meas_steer_torque_limits
from opendbc.car.chrysler import chryslercan
from opendbc.car.chrysler.values import CUSW_CARS, RAM_CARS, CarControllerParams, ChryslerFlags
from opendbc.car.chrysler.values import CUSW_CARS, RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags
from opendbc.car.structs import CarState
from opendbc.car.interfaces import CarControllerBase
@@ -50,7 +51,15 @@ class CarController(CarControllerBase):
# TODO: can we make this more sane? why is it different for all the cars?
lkas_control_bit = self.lkas_control_bit_prev
if CS.out.vEgo > self.CP.minSteerSpeed:
if self.CP.flags & ChryslerFlags.STEERING_SPEED_BYPASS:
lkas_control_bit = CC.latActive
elif self.CP.carFingerprint in RAM_DT:
# Arm in the EPS speed window, then keep the request through lower speeds.
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
lkas_control_bit = True
if self.CP.minEnableSpeed >= 14.5 and CS.out.gearShifter != CarState.GearShifter.drive:
lkas_control_bit = False
elif CS.out.vEgo > self.CP.minSteerSpeed:
lkas_control_bit = True
elif self.CP.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0):
@@ -66,9 +66,8 @@ class CarInterface(CarInterfaceBase):
# Ram
elif candidate == CAR.RAM_1500_5TH_GEN:
ret.steerActuatorDelay = 0.2
# Older EPS FW allow steer to zero
if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw):
ret.minSteerSpeed = 0.
ret.minEnableSpeed = 14.5
ret.minSteerSpeed = 0.5
elif candidate == CAR.RAM_HD_5TH_GEN:
ret.steerActuatorDelay = 0.2
@@ -81,6 +80,10 @@ class CarInterface(CarInterfaceBase):
# TODO: Durango 2020 may be able to steer to zero once above 38 kph
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
if candidate not in CUSW_CARS and 0x4FF in fingerprint[0]:
ret.flags |= ChryslerFlags.STEERING_SPEED_BYPASS.value
ret.minSteerSpeed = 0.
ret.centerToFront = ret.wheelbase * 0.44
if (0x62cc033 if candidate in CUSW_CARS else 0x2d0) in fingerprint[0]:
ret.flags |= ChryslerFlags.HAS_BSM.value
@@ -0,0 +1,84 @@
import unittest
from opendbc.can import CANParser
from opendbc.car import Bus, gen_empty_fingerprint, structs
from opendbc.car.chrysler.interface import CarInterface
from opendbc.car.chrysler.values import CAR, DBC
def params(alpha=False, release=False, firmware=b"68410000"):
fw = structs.CarParams.CarFw(ecu=structs.CarParams.Ecu.eps, fwVersion=firmware)
return CarInterface.get_params(CAR.RAM_1500_5TH_GEN, gen_empty_fingerprint(), [fw], alpha, release, False)
class TestRamSteering(unittest.TestCase):
def test_enable_window_is_separate_from_steering_minimum(self):
for firmware in (b"68310000", b"68410000"):
for alpha in (False, True):
for release in (False, True):
with self.subTest(firmware=firmware, alpha=alpha, release=release):
cp = params(alpha, release, firmware)
self.assertEqual(cp.minEnableSpeed, 14.5)
self.assertEqual(cp.minSteerSpeed, 0.5)
self.assertEqual(cp.safetyConfigs[0].safetyParam, 1)
self.assertTrue(cp.pcmCruise)
self.assertFalse(cp.openpilotLongitudinalControl)
def test_window_latches_but_gear_change_and_cooldown_block_reentry(self):
ci = CarInterface(params())
ci.update([])
command = structs.CarControl(latActive=True)
command.actuators.torque = 0.1
gear = structs.CarState.GearShifter
parser = CANParser(DBC[CAR.RAM_1500_5TH_GEN][Bus.pt], [("LKAS_COMMAND", 0)], 0)
cases = (
(202, 15.1, gear.drive, False),
(204, 14.5, gear.drive, True),
(206, 25, gear.drive, True),
(208, 2, gear.drive, True),
(210, 14.75, gear.reverse, False),
(212, 14.75, gear.drive, False),
(410, 14.75, gear.drive, False),
(412, 14.75, gear.drive, True),
(414, 14.75, gear.neutral, False),
(616, 15, gear.drive, True),
)
for frame, speed, selected_gear, expected in cases:
with self.subTest(frame=frame):
ci.CC.frame = frame
ci.CS.out.vEgo = speed
ci.CS.out.gearShifter = selected_gear
_, messages = ci.CC.update(command.as_reader(), ci.CS, frame * 10_000_000)
parser.update((frame * 10_000_000, messages))
self.assertEqual(ci.CC.lkas_control_bit_prev, expected)
if not expected:
self.assertEqual(parser.vl["LKAS_COMMAND"]["STEERING_TORQUE"], 0)
def test_full_host_torque_ramp_passes_existing_native_limits(self):
from opendbc.safety.tests.libsafety import libsafety_py
from opendbc.safety.tests.libsafety.libsafety_py import make_CANPacket
native = libsafety_py.libsafety
self.assertEqual(native.set_safety_hooks(9, 1), 0)
native.init_tests()
native.set_controls_allowed(True)
ci = CarInterface(params())
ci.update([])
ci.CS.out.vEgo = 14.75
ci.CS.out.gearShifter = structs.CarState.GearShifter.drive
command = structs.CarControl(latActive=True)
command.actuators.torque = 1
for frame in range(202, 342, 2):
ci.CC.frame = frame
ci.CS.out.steeringTorqueEps = ci.CC.apply_torque_last
native.set_torque_meas(ci.CC.apply_torque_last, ci.CC.apply_torque_last)
native.set_timer(frame * 10_000)
actuators, messages = ci.CC.update(command.as_reader(), ci.CS, frame * 10_000_000)
for address, data, bus in messages:
self.assertTrue(native.safety_tx_hook(make_CANPacket(address, bus, data)), (frame, address, data))
self.assertEqual(actuators.torqueOutputCan, 350)
self.assertEqual(actuators.torque, 1)
command.latActive = False
ci.CC.frame = 344
actuators, _ = ci.CC.update(command.as_reader(), ci.CS, 3_440_000_000)
self.assertEqual(actuators.torqueOutputCan, 0)
@@ -0,0 +1,82 @@
import unittest
from opendbc.can import CANParser
from opendbc.car import Bus, gen_empty_fingerprint, structs
from opendbc.car.chrysler.interface import CarInterface
from opendbc.car.chrysler.values import CAR, CUSW_CARS, DBC, ChryslerFlags
def params(car, bus=None):
fingerprint = gen_empty_fingerprint()
if bus is not None:
fingerprint[bus][0x4FF] = 8
return CarInterface.get_params(car, fingerprint, [], False, False, False)
class TestSteeringSpeedBypass(unittest.TestCase):
def test_blind_spot_detection_does_not_enable_speed_bypass(self):
car = CAR.CHRYSLER_PACIFICA_2020
baseline = params(car)
for blind_spot in (False, True):
for bypass in (False, True):
for bus in (0, 1, 2):
with self.subTest(blind_spot=blind_spot, bypass=bypass, bus=bus):
fingerprint = gen_empty_fingerprint()
if blind_spot:
fingerprint[bus][0x2d0] = 8
if bypass:
fingerprint[bus][0x4ff] = 8
cp = CarInterface.get_params(car, fingerprint, [], False, False, False)
self.assertEqual(bool(cp.flags & ChryslerFlags.HAS_BSM), blind_spot and bus == 0)
self.assertEqual(bool(cp.flags & ChryslerFlags.STEERING_SPEED_BYPASS), bypass and bus == 0)
self.assertEqual(cp.minSteerSpeed, 0 if bypass and bus == 0 else baseline.minSteerSpeed)
self.assertEqual([(str(c.safetyModel), c.safetyParam) for c in cp.safetyConfigs],
[(str(c.safetyModel), c.safetyParam) for c in baseline.safetyConfigs])
def test_hardware_is_scoped_to_original_platforms_and_bus(self):
for car in CAR:
baseline = params(car)
for bus in (0, 1, 2):
with self.subTest(car=car, bus=bus):
cp = params(car, bus)
supported = bus == 0 and car not in CUSW_CARS
self.assertEqual(bool(cp.flags & ChryslerFlags.STEERING_SPEED_BYPASS), supported)
self.assertEqual(cp.minSteerSpeed, 0 if supported else baseline.minSteerSpeed)
self.assertEqual([(str(c.safetyModel), c.safetyParam) for c in cp.safetyConfigs],
[(str(c.safetyModel), c.safetyParam) for c in baseline.safetyConfigs])
self.assertEqual(cp.dashcamOnly, baseline.dashcamOnly)
self.assertEqual(cp.pcmCruise, baseline.pcmCruise)
self.assertFalse(cp.openpilotLongitudinalControl)
def test_standstill_engagement_and_reentry_keep_eps_cooldown(self):
for car in set(CAR) - CUSW_CARS:
with self.subTest(car=car):
ci = CarInterface(params(car, 0))
ci.update([])
ci.CS.out.vEgo = 0
command = structs.CarControl()
command.actuators.torque = 0.1
parser = CANParser(DBC[car][Bus.pt], [('LKAS_COMMAND', 0)], 0)
for frame, active, expected in ((200, True, False), (202, True, True), (204, True, True),
(206, False, False), (208, True, False),
(406, True, False), (408, True, True)):
ci.CC.frame = frame
command.latActive = active
_, sends = ci.CC.update(command.as_reader(), ci.CS, frame * 10_000_000)
parser.update((frame * 10_000_000, sends))
self.assertEqual(ci.CC.lkas_control_bit_prev, expected)
if not expected:
self.assertEqual(parser.vl['LKAS_COMMAND']['STEERING_TORQUE'], 0)
def test_without_hardware_standstill_does_not_enable_steering(self):
for car in set(CAR) - CUSW_CARS:
with self.subTest(car=car):
ci = CarInterface(params(car))
ci.update([])
ci.CC.frame = 202
command = structs.CarControl()
command.latActive = True
command.actuators.torque = 0.1
actuators, _ = ci.CC.update(command.as_reader(), ci.CS, 2_020_000_000)
self.assertFalse(ci.CC.lkas_control_bit_prev)
self.assertEqual(actuators.torqueOutputCan, 0)
+2 -1
View File
@@ -18,6 +18,7 @@ class ChryslerFlags(IntFlag):
# Detected flags
HIGHER_MIN_STEERING_SPEED = 1
HAS_BSM = 2 # blind spot monitoring
STEERING_SPEED_BYPASS = 4
@dataclass
@@ -110,7 +111,7 @@ class CarControllerParams:
elif CP.carFingerprint in RAM_DT:
self.STEER_DELTA_UP = 6
self.STEER_DELTA_DOWN = 6
self.STEER_MAX = 261 # EPS allows more, up to 350?
self.STEER_MAX = 350
elif CP.carFingerprint in CUSW_CARS:
self.STEER_STEP = 1 # 100 Hz
self.STEER_DELTA_UP = 4