mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 07:43:48 +08:00
rolos
This commit is contained in:
Binary file not shown.
@@ -617,6 +617,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VisionSpeedLimitAutoBookmark", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitAutoPreserveSegment", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"VisionSpeedLimitLowLimitFilter", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitLowLimitThreshold", {PERSISTENT, INT, "25", "25", 0}},
|
||||
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
|
||||
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
|
||||
|
||||
Binary file not shown.
@@ -233,7 +233,9 @@ class CarState(CarStateBase):
|
||||
return button_events
|
||||
|
||||
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA:
|
||||
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0
|
||||
elif self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
|
||||
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
|
||||
else:
|
||||
source_states = (
|
||||
@@ -425,9 +427,6 @@ class CarState(CarStateBase):
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
|
||||
*lkas_button_events]
|
||||
|
||||
if getattr(self.FPCP, "flags", 0) & HyundaiStarPilotFlags.MAIN_CRUISE_STATE_TRACKING:
|
||||
ret.cruiseState.available = self.update_main_cruise(ret)
|
||||
|
||||
ret.blockPcmEnable = not self.recent_button_interaction()
|
||||
|
||||
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
|
||||
|
||||
@@ -676,20 +676,14 @@ class TestHyundaiFingerprint:
|
||||
)
|
||||
assert not (minimal_fpcp.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC)
|
||||
|
||||
def test_classic_hyundai_long_tracks_main_cruise_state(self):
|
||||
@pytest.mark.parametrize("candidate", (CAR.HYUNDAI_ELANTRA_2021, CAR.HYUNDAI_SONATA_HYBRID))
|
||||
def test_legacy_hyundai_long_does_not_gate_availability_on_main_cruise(self, candidate):
|
||||
toggles = get_test_toggles()
|
||||
classic_cp = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_2021, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
classic_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.HYUNDAI_ELANTRA_2021, gen_empty_fingerprint(), [], classic_cp, toggles,
|
||||
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
FPCP = CarInterface.get_starpilot_params(
|
||||
candidate, gen_empty_fingerprint(), [], CP, toggles,
|
||||
)
|
||||
assert classic_fpcp.flags & HyundaiStarPilotFlags.MAIN_CRUISE_STATE_TRACKING
|
||||
|
||||
car_state = CarState(classic_cp, classic_fpcp)
|
||||
ret = SimpleNamespace(
|
||||
cruiseState=SimpleNamespace(available=True),
|
||||
buttonEvents=[structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)],
|
||||
)
|
||||
assert car_state.update_main_cruise(ret)
|
||||
assert not (FPCP.flags & HyundaiStarPilotFlags.MAIN_CRUISE_STATE_TRACKING)
|
||||
|
||||
ioniq_cp = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
ioniq_fpcp = CarInterface.get_starpilot_params(
|
||||
@@ -1486,7 +1480,7 @@ class TestHyundaiFingerprint:
|
||||
|
||||
assert Bus.alt not in can_parsers
|
||||
|
||||
def test_sonata_alt_bus_clu13_swl_stat_lkas_button_event(self):
|
||||
def test_sonata_uses_main_bus_bcm_lkas_button_event(self):
|
||||
toggles = get_test_toggles()
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x391] = 8
|
||||
@@ -1498,16 +1492,26 @@ class TestHyundaiFingerprint:
|
||||
can_parsers = car_state.get_can_parsers(CP)
|
||||
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
assert Bus.alt not in can_parsers
|
||||
|
||||
def update(lkas_button: int, frame: int):
|
||||
msg = packer.make_can_msg("CLU13", 1, {
|
||||
"CF_Clu_SWL_Stat": lkas_button,
|
||||
})
|
||||
can_parsers[Bus.alt].update([(frame, [msg])])
|
||||
msgs = [
|
||||
packer.make_can_msg("CLU13", 0, {
|
||||
"CF_Clu_LdwsLkasSW": 0,
|
||||
"CF_Clu_SWL_Stat": 4,
|
||||
}),
|
||||
packer.make_can_msg("BCM_PO_11", 0, {
|
||||
"LDA_BTN": lkas_button,
|
||||
}),
|
||||
]
|
||||
can_parsers[Bus.pt].update([(frame, msgs)])
|
||||
return car_state.update(can_parsers, toggles)[0]
|
||||
|
||||
update(0, 1)
|
||||
ret = update(4, 2)
|
||||
ret = update(1, 2)
|
||||
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
|
||||
|
||||
ret = update(0, 3)
|
||||
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
|
||||
|
||||
def test_genesis_g90_does_not_use_alt_bus_lkas_parser(self):
|
||||
|
||||
@@ -973,18 +973,8 @@ KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
|
||||
})
|
||||
|
||||
|
||||
# These classic HKG platforms publish the LKAS button on CLU13 over the alt bus.
|
||||
# Keep G90 excluded until its alt-bus path is route-proven without the recent
|
||||
# engage/disengage regression.
|
||||
ALT_BUS_LDA_BUTTON_CARS = frozenset({
|
||||
CAR.HYUNDAI_SONATA,
|
||||
})
|
||||
|
||||
# On these Sonata layouts the alt-bus LKAS button pulses through the CLU13
|
||||
# steering-wheel-status field instead of the dedicated LKAS bit.
|
||||
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset({
|
||||
CAR.HYUNDAI_SONATA,
|
||||
})
|
||||
ALT_BUS_LDA_BUTTON_CARS = frozenset()
|
||||
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset()
|
||||
|
||||
|
||||
def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
|
||||
|
||||
@@ -232,9 +232,6 @@ class CarInterfaceBase(ABC):
|
||||
fp_ret.flags |= int(HondaStarPilotFlags.HAS_CAMERA_MESSAGES)
|
||||
|
||||
elif platform in HYUNDAI:
|
||||
if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD):
|
||||
fp_ret.flags |= HyundaiStarPilotFlags.MAIN_CRUISE_STATE_TRACKING.value
|
||||
|
||||
if candidate in CANFD_CAR:
|
||||
hda2 = Ecu.adas in [fw.ecu for fw in car_fw]
|
||||
CAN = CanBus(None, fingerprint, bool(CP.flags & HyundaiFlags.CANFD_LKA_STEERING))
|
||||
|
||||
@@ -513,13 +513,8 @@ class CarController(CarControllerBase):
|
||||
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
|
||||
|
||||
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
|
||||
# Toyota's physical distance-button hold can collide with StarPilot's wheel-button
|
||||
# actions and trip a temporary EPS fault. Suppress native long-press handling while
|
||||
# the physical gap button is held so ACC only sees the hold as a plain button press.
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead,
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase,
|
||||
allow_long_press))
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
if self.CP.flags & ToyotaFlags.SECOC.value:
|
||||
acc_cmd_2 = toyotacan.create_accel_command_2(self.packer, pcm_accel_cmd)
|
||||
acc_cmd_2 = add_mac(self.secoc_key,
|
||||
@@ -538,9 +533,8 @@ class CarController(CarControllerBase):
|
||||
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
|
||||
can_sends.append(toyotacan.create_acc_cancel_command(self.packer))
|
||||
else:
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False,
|
||||
self.distance_button, starpilot_toggles.reverse_cruise_increase, allow_long_press))
|
||||
self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
|
||||
# *** hud ui ***
|
||||
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
|
||||
|
||||
@@ -752,21 +752,21 @@ class TestToyotaCarController:
|
||||
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
|
||||
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
|
||||
|
||||
def test_acc_control_can_suppress_long_press_behavior_while_gap_button_is_held(self):
|
||||
def test_acc_control_uses_valid_long_press_modes(self):
|
||||
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("ACC_CONTROL", 0)], 0)
|
||||
|
||||
default_msg = toyotacan.create_accel_command(
|
||||
normal_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False,
|
||||
)
|
||||
parser.update([(1, [default_msg])])
|
||||
parser.update([(1, [normal_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 1
|
||||
|
||||
suppressed_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False, allow_long_press=0,
|
||||
reverse_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, True,
|
||||
)
|
||||
parser.update([(1, [suppressed_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 0
|
||||
parser.update([(1, [reverse_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 2
|
||||
|
||||
def test_auto_brake_hold_sends_modified_pre_collision_after_timer(self):
|
||||
controller = self._make_controller()
|
||||
|
||||
@@ -41,10 +41,9 @@ def create_lta_steer_command_2(packer, frame):
|
||||
|
||||
|
||||
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert,
|
||||
distance, reverse_cruise_active, allow_long_press=None):
|
||||
distance, reverse_cruise_active):
|
||||
# TODO: find the exact canceling bit that does not create a chime
|
||||
if allow_long_press is None:
|
||||
allow_long_press = 2 if reverse_cruise_active else 1
|
||||
allow_long_press = 2 if reverse_cruise_active else 1
|
||||
|
||||
values = {
|
||||
"ACCEL_CMD": accel,
|
||||
|
||||
@@ -12,6 +12,7 @@ from openpilot.common.swaglog import cloudlog
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.chrysler.values import pacifica_hybrid_aol_stock_acc_mode
|
||||
from opendbc.car.gm.values import CAR as GM_CAR
|
||||
from opendbc.car.honda.values import CAR as HONDA_CAR
|
||||
from opendbc.car.nissan.values import CAR as NISSAN_CAR
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MAX_LATERAL_JERK, clip_curvature, get_lateral_active
|
||||
@@ -23,6 +24,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
BOLT_2018_2021_STEER_RATIO_TEST_SCALE,
|
||||
LatControlTorque,
|
||||
get_bolt_2017_steer_ratio_scale,
|
||||
get_honda_accord_steer_ratio_scale,
|
||||
)
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||
from openpilot.selfdrive.car.cruise_state import should_cancel_stock_cruise
|
||||
@@ -394,10 +396,15 @@ class Controls:
|
||||
lp = self.sm['liveParameters']
|
||||
x = max(lp.stiffnessFactor, 0.1)
|
||||
sr = max(lp.steerRatio, 0.1)
|
||||
custom_accord_ratio = getattr(self.starpilot_toggles, "steerRatio", self.CP.steerRatio)
|
||||
accord_ratio_is_explicit = getattr(self.starpilot_toggles, "use_custom_steerRatio", False) and \
|
||||
abs(custom_accord_ratio - self.CP.steerRatio) > 0.01
|
||||
if self.CP.carFingerprint == GM_CAR.CHEVROLET_BOLT_CC_2017:
|
||||
sr *= get_bolt_2017_steer_ratio_scale(CS.vEgo)
|
||||
elif self.CP.carFingerprint == GM_CAR.CHEVROLET_BOLT_CC_2018_2021:
|
||||
sr *= BOLT_2018_2021_STEER_RATIO_TEST_SCALE
|
||||
elif self.CP.carFingerprint == HONDA_CAR.HONDA_ACCORD and not accord_ratio_is_explicit:
|
||||
sr *= get_honda_accord_steer_ratio_scale(CS.vEgo)
|
||||
self.VM.update_params(x, sr)
|
||||
|
||||
steer_angle_without_offset = math.radians(CS.steeringAngleDeg - lp.angleOffsetDeg)
|
||||
|
||||
@@ -483,14 +483,24 @@ class LatControlTorque(LatControl):
|
||||
ff *= get_genesis_g70_unwind_ff_scale(
|
||||
setpoint, measurement, desired_lateral_jerk, CS.vEgo,
|
||||
)
|
||||
if kia_carnival_active:
|
||||
ff *= get_kia_carnival_unwind_ff_scale(
|
||||
setpoint, measurement, desired_lateral_jerk, CS.vEgo,
|
||||
)
|
||||
if ioniq_6_active:
|
||||
vehicle_friction_jerk_deadzone = (
|
||||
IONIQ_6_2025_FRICTION_JERK_DEADZONE if self.is_ioniq_6_2025 else IONIQ_6_FRICTION_JERK_DEADZONE
|
||||
)
|
||||
elif ioniq_5_active:
|
||||
vehicle_friction_jerk_deadzone = get_ioniq_5_friction_jerk_deadzone(CS.vEgo, setpoint)
|
||||
elif prius_active:
|
||||
vehicle_friction_jerk_deadzone = get_prius_friction_jerk_deadzone(CS.vEgo, setpoint)
|
||||
elif genesis_g70_active:
|
||||
vehicle_friction_jerk_deadzone = get_genesis_g70_friction_jerk_deadzone(CS.vEgo, setpoint)
|
||||
elif kia_carnival_active:
|
||||
vehicle_friction_jerk_deadzone = get_kia_carnival_friction_jerk_deadzone(
|
||||
CS.vEgo, setpoint, desired_lateral_jerk,
|
||||
)
|
||||
else:
|
||||
vehicle_friction_jerk_deadzone = 0.0
|
||||
friction_jerk_deadzone = get_center_chatter_friction_jerk_deadzone(
|
||||
|
||||
@@ -73,6 +73,7 @@ BOLT_2017_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
)
|
||||
BOLT_CARS = BOLT_2022_2023_CARS + BOLT_2018_2021_CARS + BOLT_2017_CARS
|
||||
HONDA_ACCORD_STEER_RATIO_SCALE = 14.0 / 16.33
|
||||
VOLT_STANDARD_CARS = (
|
||||
GM_CAR.CHEVROLET_VOLT,
|
||||
GM_CAR.CHEVROLET_VOLT_2019,
|
||||
@@ -547,6 +548,24 @@ KIA_CARNIVAL_HIGHWAY_TRANSITION_JERK = 0.45
|
||||
KIA_CARNIVAL_HIGHWAY_TRANSITION_JERK_WIDTH = 0.15
|
||||
KIA_CARNIVAL_HIGHWAY_TRANSITION_LAT_CUTOFF = 1.20
|
||||
KIA_CARNIVAL_HIGHWAY_TRANSITION_LAT_WIDTH = 0.20
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_MAX = 0.34
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED = 15.0
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_WIDTH = 2.0
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_CUTOFF = 23.0
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_CUTOFF_WIDTH = 2.0
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_LAT = 0.35
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_LAT_WIDTH = 0.18
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_JERK = 0.65
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_JERK_WIDTH = 0.25
|
||||
KIA_CARNIVAL_UNWIND_FF_REDUCTION_MAX = 0.45
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED = 15.0
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED_WIDTH = 2.0
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED_CUTOFF = 23.0
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED_CUTOFF_WIDTH = 2.0
|
||||
KIA_CARNIVAL_UNWIND_FF_OVERSHOOT = 0.20
|
||||
KIA_CARNIVAL_UNWIND_FF_OVERSHOOT_WIDTH = 0.12
|
||||
KIA_CARNIVAL_UNWIND_FF_JERK = 0.65
|
||||
KIA_CARNIVAL_UNWIND_FF_JERK_WIDTH = 0.25
|
||||
|
||||
TUCSON_4TH_GEN_CENTER_TAPER_MAX = 0.44
|
||||
TUCSON_4TH_GEN_CENTER_TAPER_LAT = 0.28
|
||||
@@ -688,6 +707,11 @@ IONIQ_5_LOW_SPEED_CENTER_LAT = 0.40
|
||||
IONIQ_5_LOW_SPEED_CENTER_LAT_WIDTH = 0.10
|
||||
IONIQ_5_LOW_SPEED_CENTER_JERK = 0.40
|
||||
IONIQ_5_LOW_SPEED_CENTER_JERK_WIDTH = 0.12
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_MAX = 0.30
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_LAT = 1.25
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_LAT_WIDTH = 0.35
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_SPEED = 18.0
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_SPEED_WIDTH = 3.0
|
||||
|
||||
IONIQ_EV_OLD_BASE_LAT_ACCEL_FACTOR_MULT = 1.16
|
||||
IONIQ_EV_OLD_FF_REDUCTION_LEFT = 0.16
|
||||
@@ -1082,9 +1106,6 @@ TOYOTA_COROLLA_TSS2_CENTER_OUTPUT_TAPER_SPEED = 4.5
|
||||
TOYOTA_COROLLA_TSS2_CENTER_OUTPUT_TAPER_SPEED_WIDTH = 1.5
|
||||
|
||||
LEXUS_IS_PHASE_SCALE = 0.10
|
||||
# The Lexus route still fell short during a clean high-speed turn-in while
|
||||
# already at the controller limit. Keep this correction small and phase-gated
|
||||
# so straight-line behavior and unwind tuning are unchanged.
|
||||
LEXUS_IS_TURN_IN_FF_BOOST_LEFT = 0.06
|
||||
LEXUS_IS_TURN_IN_FF_BOOST_RIGHT = 0.06
|
||||
LEXUS_IS_UNWIND_FF_REDUCTION_LEFT = 0.10
|
||||
@@ -1886,6 +1907,10 @@ def get_bolt_2017_steer_ratio_scale(v_ego: float) -> float:
|
||||
return 1.0 + ((BOLT_2017_STEER_RATIO_TEST_SCALE - 1.0) * _bolt_2017_high_speed_factor(v_ego))
|
||||
|
||||
|
||||
def get_honda_accord_steer_ratio_scale(_v_ego: float) -> float:
|
||||
return HONDA_ACCORD_STEER_RATIO_SCALE
|
||||
|
||||
|
||||
def get_bolt_2017_center_taper_scale(desired_lateral_accel: float, v_ego: float) -> float:
|
||||
center_window = _bolt_2017_sigmoid((BOLT_2017_CENTER_TAPER_LAT - abs(desired_lateral_accel)) / BOLT_2017_CENTER_TAPER_WIDTH)
|
||||
return 1.0 - (BOLT_2017_CENTER_TAPER_GAIN * _bolt_2017_high_speed_factor(v_ego) * center_window)
|
||||
@@ -2528,6 +2553,41 @@ def get_kia_carnival_highway_transition_output_scale(desired_lateral_accel: floa
|
||||
return 1.0 - (KIA_CARNIVAL_HIGHWAY_TRANSITION_TAPER_MAX * speed_weight * jerk_weight * lat_weight)
|
||||
|
||||
|
||||
def get_kia_carnival_friction_jerk_deadzone(v_ego: float, desired_lateral_accel: float,
|
||||
desired_lateral_jerk: float) -> float:
|
||||
"""Reduce abrupt friction reversals during mid-speed curve exits only."""
|
||||
speed_weight = _sigmoid((v_ego - KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED) /
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_WIDTH)
|
||||
speed_cutoff = _sigmoid((KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_CUTOFF - v_ego) /
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_SPEED_CUTOFF_WIDTH)
|
||||
center_weight = _sigmoid((KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_LAT - abs(desired_lateral_accel)) /
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_LAT_WIDTH)
|
||||
jerk_weight = _sigmoid((abs(desired_lateral_jerk) - KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_JERK) /
|
||||
KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_JERK_WIDTH)
|
||||
return KIA_CARNIVAL_UNWIND_FRICTION_JERK_DEADZONE_MAX * speed_weight * speed_cutoff * center_weight * jerk_weight
|
||||
|
||||
|
||||
def get_kia_carnival_unwind_ff_scale(setpoint: float, measured_lateral_accel: float,
|
||||
desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
"""Remove stale turn feedforward when the measured response carries through an unwind."""
|
||||
if setpoint * desired_lateral_jerk >= 0.0:
|
||||
return 1.0
|
||||
|
||||
overshoot = max(abs(measured_lateral_accel) - abs(setpoint), 0.0)
|
||||
if overshoot <= 0.0:
|
||||
return 1.0
|
||||
|
||||
speed_weight = (_sigmoid((v_ego - KIA_CARNIVAL_UNWIND_FF_SPEED) /
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED_WIDTH) *
|
||||
_sigmoid((KIA_CARNIVAL_UNWIND_FF_SPEED_CUTOFF - v_ego) /
|
||||
KIA_CARNIVAL_UNWIND_FF_SPEED_CUTOFF_WIDTH))
|
||||
overshoot_weight = _sigmoid((overshoot - KIA_CARNIVAL_UNWIND_FF_OVERSHOOT) /
|
||||
KIA_CARNIVAL_UNWIND_FF_OVERSHOOT_WIDTH)
|
||||
jerk_weight = _sigmoid((abs(desired_lateral_jerk) - KIA_CARNIVAL_UNWIND_FF_JERK) /
|
||||
KIA_CARNIVAL_UNWIND_FF_JERK_WIDTH)
|
||||
return 1.0 - (KIA_CARNIVAL_UNWIND_FF_REDUCTION_MAX * speed_weight * overshoot_weight * jerk_weight)
|
||||
|
||||
|
||||
def _tucson_4th_gen_center_weights(desired_lateral_accel: float, v_ego: float) -> tuple[float, float]:
|
||||
speed_weight = _sigmoid((TUCSON_4TH_GEN_CENTER_TAPER_SPEED_MAX - v_ego) / TUCSON_4TH_GEN_CENTER_TAPER_SPEED_WIDTH)
|
||||
center_weight = _sigmoid((TUCSON_4TH_GEN_CENTER_TAPER_LAT - abs(desired_lateral_accel)) / TUCSON_4TH_GEN_CENTER_TAPER_LAT_WIDTH)
|
||||
@@ -3029,6 +3089,15 @@ def get_ioniq_5_low_speed_output_limit(desired_lateral_accel: float,
|
||||
return float(np.clip(limit, IONIQ_5_LOW_SPEED_OUTPUT_LIMIT_BASE, 1.0))
|
||||
|
||||
|
||||
def get_ioniq_5_friction_jerk_deadzone(v_ego: float, desired_lateral_accel: float) -> float:
|
||||
"""Suppress high-speed friction reversals without reducing steady-turn torque."""
|
||||
speed_weight = _ioniq_5_sigmoid((max(v_ego, 0.0) - IONIQ_5_FRICTION_JERK_DEADZONE_SPEED) /
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_SPEED_WIDTH)
|
||||
curve_weight = _ioniq_5_sigmoid((IONIQ_5_FRICTION_JERK_DEADZONE_LAT - abs(desired_lateral_accel)) /
|
||||
IONIQ_5_FRICTION_JERK_DEADZONE_LAT_WIDTH)
|
||||
return IONIQ_5_FRICTION_JERK_DEADZONE_MAX * speed_weight * curve_weight
|
||||
|
||||
|
||||
def _ioniq_ev_old_sigmoid(x: float) -> float:
|
||||
return _sigmoid(x)
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
get_genesis_gv70_high_speed_error_scale,
|
||||
get_genesis_gv70_unwind_ff_scale,
|
||||
get_elantra_non_scc_ff_scale,
|
||||
get_honda_accord_steer_ratio_scale,
|
||||
get_palisade_ff_scale,
|
||||
get_palisade_center_output_scale,
|
||||
get_palisade_center_taper_scale,
|
||||
@@ -113,6 +114,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
get_ioniq_5_friction_scale,
|
||||
get_ioniq_5_friction_threshold,
|
||||
get_ioniq_5_center_taper_scale,
|
||||
get_ioniq_5_friction_jerk_deadzone,
|
||||
get_ioniq_5_low_speed_output_limit,
|
||||
get_ioniq_ev_old_center_taper_scale,
|
||||
get_ioniq_ev_old_ff_scale,
|
||||
@@ -132,8 +134,10 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
get_kia_forte_ff_scale,
|
||||
get_kia_carnival_center_taper_scale,
|
||||
get_kia_carnival_friction_center_fade_scale,
|
||||
get_kia_carnival_friction_jerk_deadzone,
|
||||
get_kia_carnival_friction_threshold,
|
||||
get_kia_carnival_highway_transition_output_scale,
|
||||
get_kia_carnival_unwind_ff_scale,
|
||||
get_kia_stinger_2022_center_taper_scale,
|
||||
get_kia_stinger_2022_friction_threshold,
|
||||
get_tucson_4th_gen_center_taper_scale,
|
||||
@@ -668,6 +672,30 @@ class TestLatControl:
|
||||
assert low_speed_abrupt > 0.99
|
||||
assert large_curve_abrupt > 0.96
|
||||
|
||||
def test_kia_carnival_unwind_friction_jerk_deadzone_is_mid_speed_and_center_gated(self):
|
||||
low_speed = get_kia_carnival_friction_jerk_deadzone(8.5, 0.0, 1.5)
|
||||
mid_speed_center = get_kia_carnival_friction_jerk_deadzone(18.0, 0.0, 1.5)
|
||||
mid_speed_curve = get_kia_carnival_friction_jerk_deadzone(18.0, 0.8, 1.5)
|
||||
high_speed = get_kia_carnival_friction_jerk_deadzone(30.0, 0.0, 1.5)
|
||||
calm_transition = get_kia_carnival_friction_jerk_deadzone(18.0, 0.0, 0.2)
|
||||
|
||||
assert low_speed < 0.02
|
||||
assert mid_speed_center > 0.18
|
||||
assert mid_speed_curve < 0.05
|
||||
assert high_speed < 0.05
|
||||
assert calm_transition < 0.05
|
||||
|
||||
def test_kia_carnival_unwind_ff_scale_only_reduces_overshoot(self):
|
||||
steady_turn = get_kia_carnival_unwind_ff_scale(0.80, 0.90, 0.60, 18.0)
|
||||
clean_unwind = get_kia_carnival_unwind_ff_scale(0.20, 0.20, -1.5, 18.0)
|
||||
overshooting_unwind = get_kia_carnival_unwind_ff_scale(0.20, 0.90, -1.5, 18.0)
|
||||
highway_overshoot = get_kia_carnival_unwind_ff_scale(0.20, 0.90, -1.5, 30.0)
|
||||
|
||||
assert steady_turn == pytest.approx(1.0)
|
||||
assert clean_unwind == pytest.approx(1.0)
|
||||
assert overshooting_unwind < 0.70
|
||||
assert highway_overshoot > overshooting_unwind
|
||||
|
||||
def test_genesis_g90_ff_scale_curve(self):
|
||||
assert get_genesis_g90_ff_scale(0.0, 0.0, 20.0) == 1.0
|
||||
assert get_genesis_g90_ff_scale(0.5, 0.0, 20.0) > get_genesis_g90_ff_scale(-0.5, 0.0, 20.0)
|
||||
@@ -904,6 +932,16 @@ class TestLatControl:
|
||||
assert unwind_right_scale <= unwind_left_scale
|
||||
assert get_ioniq_5_friction_threshold(25.0, 0.0, 0.0) >= get_hkg_canfd_base_friction_threshold(25.0)
|
||||
|
||||
def test_ioniq_5_friction_jerk_deadzone_is_high_speed_curve_gated(self):
|
||||
low_speed = get_ioniq_5_friction_jerk_deadzone(8.0, 0.9)
|
||||
high_speed_center = get_ioniq_5_friction_jerk_deadzone(25.0, 0.0)
|
||||
high_speed_curve = get_ioniq_5_friction_jerk_deadzone(25.0, 0.9)
|
||||
high_lateral_accel = get_ioniq_5_friction_jerk_deadzone(25.0, 2.0)
|
||||
|
||||
assert low_speed < 0.02
|
||||
assert high_speed_center > high_speed_curve > 0.0
|
||||
assert high_lateral_accel < high_speed_curve
|
||||
|
||||
def test_rav4_prime_phase_shaping(self):
|
||||
left_turn_in = get_rav4_prime_ff_scale(1.0, 0.8, 13.0)
|
||||
right_turn_in = get_rav4_prime_ff_scale(-1.0, -0.8, 13.0)
|
||||
@@ -1665,6 +1703,11 @@ class TestLatControl:
|
||||
assert controller.pid._k_p[1] == pytest.approx([value * 2.0 for value in base_kp_v])
|
||||
assert controller.pid._k_i[1] == pytest.approx([value * 1.25 for value in base_ki_v])
|
||||
|
||||
def test_honda_accord_steer_ratio_calibration(self):
|
||||
expected_scale = 14.0 / 16.33
|
||||
assert get_honda_accord_steer_ratio_scale(0.0) == pytest.approx(expected_scale)
|
||||
assert get_honda_accord_steer_ratio_scale(20.0) == pytest.approx(expected_scale)
|
||||
|
||||
def test_subaru_impreza_pid_output_scale_preserves_small_errors(self):
|
||||
assert get_subaru_impreza_pid_output_scale(0.0) == 1.0
|
||||
assert get_subaru_impreza_pid_output_scale(0.75) == 1.0
|
||||
|
||||
@@ -63,6 +63,8 @@ def make_toggles(**overrides):
|
||||
"speed_limit_priority_highest": False,
|
||||
"speed_limit_priority_lowest": False,
|
||||
"vision_speed_limit_detection": False,
|
||||
"vision_speed_limit_low_limit_filter": False,
|
||||
"vision_speed_limit_low_limit_threshold": mph(25),
|
||||
}
|
||||
defaults.update(overrides)
|
||||
return SimpleNamespace(**defaults)
|
||||
@@ -96,6 +98,107 @@ def mph(value):
|
||||
return value * CV.MPH_TO_MS
|
||||
|
||||
|
||||
@pytest.mark.parametrize("limit_mph", [15, 25])
|
||||
def test_low_vision_limit_filter_blocks_configured_boundary(limit_mph):
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
vision_speed_limit_detection=True,
|
||||
vision_speed_limit_low_limit_filter=True,
|
||||
vision_speed_limit_low_limit_threshold=mph(25),
|
||||
)
|
||||
try:
|
||||
controller.starpilot_planner.params_memory.put_float("VisionSpeedLimit", mph(limit_mph))
|
||||
sm = make_sm(gas_pressed=False, v_cruise_kph=25 * CV.MPH_TO_KPH)
|
||||
|
||||
controller.update_limits(0.0, datetime.now(timezone.utc), False, mph(25), mph(20), sm)
|
||||
|
||||
assert controller.vision_limit == pytest.approx(mph(limit_mph))
|
||||
assert controller.target == 0
|
||||
assert controller.source == "None"
|
||||
finally:
|
||||
controller.shutdown()
|
||||
|
||||
|
||||
def test_low_vision_limit_filter_allows_limit_above_threshold():
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
vision_speed_limit_detection=True,
|
||||
vision_speed_limit_low_limit_filter=True,
|
||||
vision_speed_limit_low_limit_threshold=mph(25),
|
||||
)
|
||||
try:
|
||||
controller.starpilot_planner.params_memory.put_float("VisionSpeedLimit", mph(30))
|
||||
sm = make_sm(gas_pressed=False, v_cruise_kph=30 * CV.MPH_TO_KPH)
|
||||
|
||||
controller.update_limits(0.0, datetime.now(timezone.utc), False, mph(30), mph(25), sm)
|
||||
|
||||
assert controller.target == pytest.approx(mph(30))
|
||||
assert controller.source == "Vision"
|
||||
finally:
|
||||
controller.shutdown()
|
||||
|
||||
|
||||
def test_low_vision_limit_filter_is_action_only_for_display():
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
vision_speed_limit_detection=True,
|
||||
vision_speed_limit_low_limit_filter=True,
|
||||
vision_speed_limit_low_limit_threshold=mph(25),
|
||||
)
|
||||
try:
|
||||
controller.starpilot_planner.params_memory.put_float("VisionSpeedLimit", mph(15))
|
||||
sm = make_sm(gas_pressed=False, v_cruise_kph=20 * CV.MPH_TO_KPH)
|
||||
|
||||
controller.update_limits(0.0, datetime.now(timezone.utc), False, mph(20), mph(15), sm, display_only=True)
|
||||
|
||||
assert controller.target == pytest.approx(mph(15))
|
||||
assert controller.source == "Vision"
|
||||
finally:
|
||||
controller.shutdown()
|
||||
|
||||
|
||||
def test_low_vision_limit_filter_does_not_filter_dashboard_source():
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
speed_limit_priority2="Dashboard",
|
||||
vision_speed_limit_detection=True,
|
||||
vision_speed_limit_low_limit_filter=True,
|
||||
vision_speed_limit_low_limit_threshold=mph(25),
|
||||
)
|
||||
try:
|
||||
controller.starpilot_planner.params_memory.put_float("VisionSpeedLimit", mph(15))
|
||||
sm = make_sm(gas_pressed=False, v_cruise_kph=20 * CV.MPH_TO_KPH)
|
||||
|
||||
controller.update_limits(mph(15), datetime.now(timezone.utc), False, mph(20), mph(15), sm)
|
||||
|
||||
assert controller.target == pytest.approx(mph(15))
|
||||
assert controller.source == "Dashboard"
|
||||
finally:
|
||||
controller.shutdown()
|
||||
|
||||
|
||||
def test_low_vision_limit_filter_does_not_restore_filtered_vision_fallback():
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
slc_fallback_previous_speed_limit=True,
|
||||
vision_speed_limit_detection=True,
|
||||
vision_speed_limit_low_limit_filter=True,
|
||||
vision_speed_limit_low_limit_threshold=mph(25),
|
||||
)
|
||||
try:
|
||||
controller.previous_source = "Vision"
|
||||
controller.previous_target = mph(15)
|
||||
controller.starpilot_planner.params_memory.put_float("VisionSpeedLimit", mph(15))
|
||||
sm = make_sm(gas_pressed=False, v_cruise_kph=20 * CV.MPH_TO_KPH)
|
||||
|
||||
controller.update_limits(0.0, datetime.now(timezone.utc), False, mph(20), mph(15), sm)
|
||||
|
||||
assert controller.target == 0
|
||||
assert controller.source == "None"
|
||||
finally:
|
||||
controller.shutdown()
|
||||
|
||||
|
||||
def test_large_vision_delta_requires_three_detector_frames():
|
||||
controller = make_controller(
|
||||
speed_limit_priority1="Vision",
|
||||
|
||||
@@ -153,6 +153,8 @@ SAFE_MODE_MANAGED_KEYS = (
|
||||
"Offset7",
|
||||
"SpeedLimitFiller",
|
||||
"VisionSpeedLimitDetection",
|
||||
"VisionSpeedLimitLowLimitFilter",
|
||||
"VisionSpeedLimitLowLimitThreshold",
|
||||
"VASMEnabled",
|
||||
"CustomPersonalities",
|
||||
"TrafficPersonalityProfile",
|
||||
|
||||
@@ -1341,6 +1341,16 @@ class StarPilotVariables:
|
||||
|
||||
toggle.speed_limit_filler = self.get_value("SpeedLimitFiller")
|
||||
toggle.vision_speed_limit_detection = self.get_value("VisionSpeedLimitDetection")
|
||||
toggle.vision_speed_limit_low_limit_filter = self.get_value(
|
||||
"VisionSpeedLimitLowLimitFilter",
|
||||
condition=toggle.speed_limit_controller and toggle.vision_speed_limit_detection,
|
||||
)
|
||||
toggle.vision_speed_limit_low_limit_threshold = self.get_value(
|
||||
"VisionSpeedLimitLowLimitThreshold",
|
||||
cast=float,
|
||||
condition=toggle.vision_speed_limit_low_limit_filter,
|
||||
conversion=speed_conversion,
|
||||
)
|
||||
toggle.v_asm_enabled = self.get_value("VASMEnabled")
|
||||
|
||||
toggle.startup_alert_top = self.get_value("StartupMessageTop", cast=str, default="")
|
||||
|
||||
@@ -139,6 +139,12 @@ class SpeedLimitController:
|
||||
(gas_pressed and v_ego > target_with_offset)
|
||||
)
|
||||
|
||||
def low_vision_limit_filtered(self, limit):
|
||||
return (
|
||||
getattr(self.starpilot_toggles, "vision_speed_limit_low_limit_filter", False) and
|
||||
0 < limit <= max(getattr(self.starpilot_toggles, "vision_speed_limit_low_limit_threshold", 0), 0)
|
||||
)
|
||||
|
||||
def clear_override_for_source_limit(self, desired_source, desired_target, had_override):
|
||||
if desired_source == "None" or desired_target <= 0:
|
||||
return
|
||||
@@ -347,6 +353,8 @@ class SpeedLimitController:
|
||||
vision_enabled = getattr(self.starpilot_toggles, "vision_speed_limit_detection", False)
|
||||
self.vision_limit = self.starpilot_planner.params_memory.get_float("VisionSpeedLimit") if vision_enabled else 0
|
||||
usable_vision_limit = self.vision_limit
|
||||
if not display_only and self.low_vision_limit_filtered(usable_vision_limit):
|
||||
usable_vision_limit = 0
|
||||
# The planner clamps V_CRUISE_UNSET to V_CRUISE_MAX, so plausibility must use the raw selected speed.
|
||||
raw_set_speed_kph = float(sm["carState"].vCruise)
|
||||
selected_set_speed = raw_set_speed_kph * CV.KPH_TO_MS if 0 < raw_set_speed_kph < V_CRUISE_UNSET else 0
|
||||
@@ -406,7 +414,8 @@ class SpeedLimitController:
|
||||
desired_target = self.mapbox_limit
|
||||
|
||||
if not display_only and desired_target == 0:
|
||||
if self.previous_target > 0 and self.starpilot_toggles.slc_fallback_previous_speed_limit:
|
||||
previous_vision_limit_filtered = self.previous_source == "Vision" and self.low_vision_limit_filtered(self.previous_target)
|
||||
if self.previous_target > 0 and self.starpilot_toggles.slc_fallback_previous_speed_limit and not previous_vision_limit_filtered:
|
||||
desired_source = self.previous_source
|
||||
desired_target = self.previous_target
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class StarPilotCard:
|
||||
self.prev_cruise_enabled = False
|
||||
self.decel_pressed = False
|
||||
self.cancelPressed_previously = False
|
||||
self.cancel_pulse_glide_suppressed = False
|
||||
self.distancePressed_previously = False
|
||||
self.force_coast = False
|
||||
self.pulse_and_glide = False
|
||||
@@ -89,6 +90,7 @@ class StarPilotCard:
|
||||
elif getattr(starpilot_toggles, f"pulse_and_glide_via_{key}"):
|
||||
if getattr(sm["carControl"], "longActive", False):
|
||||
self.pulse_and_glide = not self.pulse_and_glide
|
||||
return True
|
||||
elif getattr(starpilot_toggles, f"pause_lateral_via_{key}"):
|
||||
self.pause_lateral = not self.pause_lateral
|
||||
elif getattr(starpilot_toggles, f"pause_longitudinal_via_{key}"):
|
||||
@@ -136,6 +138,34 @@ class StarPilotCard:
|
||||
def update(self, carState, starpilotCarState, sm, starpilot_toggles):
|
||||
self.switchback_mode_enabled = self.params_memory.get_bool("SwitchbackModeEnabled")
|
||||
self._handle_favorite_traffic_mode_action(sm)
|
||||
|
||||
pulse_glide_cancel_override = bool(getattr(sm["carControl"], "longActive", False)) and any(
|
||||
getattr(starpilot_toggles, f"pulse_and_glide_via_cancel{suffix}", False)
|
||||
for suffix in ("", "_long", "_very_long")
|
||||
)
|
||||
cancel_pressed = bool(getattr(starpilotCarState, "cancelPressed", False))
|
||||
if pulse_glide_cancel_override:
|
||||
carState.buttonEvents = [
|
||||
be for be in carState.buttonEvents
|
||||
if not (
|
||||
self._button_type_raw(be) == int(ButtonType.cancel) and
|
||||
(be.pressed or self.cancel_pulse_glide_suppressed)
|
||||
)
|
||||
]
|
||||
|
||||
lkas_pressed = any(
|
||||
self._button_type_raw(be) == int(ButtonType.lkas) and be.pressed
|
||||
for be in carState.buttonEvents
|
||||
)
|
||||
pulse_glide_lkas_override = bool(getattr(sm["carControl"], "longActive", False)) and getattr(
|
||||
starpilot_toggles, "pulse_and_glide_via_lkas", False
|
||||
)
|
||||
if pulse_glide_lkas_override:
|
||||
carState.buttonEvents = [
|
||||
be for be in carState.buttonEvents
|
||||
if self._button_type_raw(be) != int(ButtonType.lkas)
|
||||
]
|
||||
|
||||
button_event_types = [self._button_type_raw(be) for be in carState.buttonEvents]
|
||||
button_aol_supported = self.CP.brand == "hyundai" or starpilot_toggles.lkas_allowed_for_aol
|
||||
button_managed_aol = starpilot_toggles.always_on_lateral_lkas or (button_aol_supported and starpilot_toggles.main_cruise_aol_toggle)
|
||||
@@ -254,7 +284,6 @@ class StarPilotCard:
|
||||
self.handle_button_event("distance_long", sm, starpilot_toggles)
|
||||
self.handle_button_event("distance_very_long", sm, starpilot_toggles)
|
||||
|
||||
cancel_pressed = bool(getattr(starpilotCarState, "cancelPressed", False))
|
||||
if cancel_pressed:
|
||||
self.cancel_counter += 1
|
||||
elif not self.cancelPressed_previously:
|
||||
@@ -262,15 +291,27 @@ class StarPilotCard:
|
||||
|
||||
self.cancelPressed_previously = cancel_pressed
|
||||
|
||||
if not cancel_pressed and 1 <= self.cancel_counter < self.long_press_threshold:
|
||||
self.handle_button_event("cancel", sm, starpilot_toggles)
|
||||
pulse_glide_cancel_consumed = False
|
||||
if not cancel_pressed and self.cancel_pulse_glide_suppressed:
|
||||
pass
|
||||
elif not cancel_pressed and 1 <= self.cancel_counter < self.long_press_threshold:
|
||||
pulse_glide_cancel_consumed = self.handle_button_event("cancel", sm, starpilot_toggles) or False
|
||||
elif self.cancel_counter == self.long_press_threshold:
|
||||
self.handle_button_event("cancel_long", sm, starpilot_toggles)
|
||||
pulse_glide_cancel_consumed = self.handle_button_event("cancel_long", sm, starpilot_toggles) or False
|
||||
elif self.cancel_counter == self.very_long_press_threshold:
|
||||
self.handle_button_event("cancel_long", sm, starpilot_toggles)
|
||||
self.handle_button_event("cancel_very_long", sm, starpilot_toggles)
|
||||
pulse_glide_cancel_consumed = self.handle_button_event("cancel_long", sm, starpilot_toggles) or False
|
||||
pulse_glide_cancel_consumed |= self.handle_button_event("cancel_very_long", sm, starpilot_toggles) or False
|
||||
|
||||
if any(be.pressed and be_type == ButtonType.lkas for be, be_type in zip(carState.buttonEvents, button_event_types, strict=False)):
|
||||
if pulse_glide_cancel_consumed:
|
||||
self.cancel_pulse_glide_suppressed = True
|
||||
carState.buttonEvents = [
|
||||
be for be in carState.buttonEvents
|
||||
if self._button_type_raw(be) != int(ButtonType.cancel)
|
||||
]
|
||||
elif not cancel_pressed and self.cancel_pulse_glide_suppressed:
|
||||
self.cancel_pulse_glide_suppressed = False
|
||||
|
||||
if lkas_pressed:
|
||||
self.handle_button_event("lkas", sm, starpilot_toggles)
|
||||
|
||||
if getattr(starpilot_toggles, "has_canfd_media_buttons", False):
|
||||
|
||||
@@ -73,11 +73,20 @@ def make_toggles(**overrides):
|
||||
"bookmark_via_cancel": False,
|
||||
"bookmark_via_cancel_long": False,
|
||||
"bookmark_via_cancel_very_long": False,
|
||||
"experimental_mode_via_cancel": False,
|
||||
"experimental_mode_via_cancel_long": False,
|
||||
"experimental_mode_via_cancel_very_long": False,
|
||||
"force_coast_via_cancel": False,
|
||||
"force_coast_via_cancel_long": False,
|
||||
"force_coast_via_cancel_very_long": False,
|
||||
"bookmark_via_lkas": False,
|
||||
"conditional_experimental_mode": False,
|
||||
"experimental_mode_via_lkas": False,
|
||||
"force_coast_via_lkas": False,
|
||||
"pulse_and_glide_available": False,
|
||||
"pulse_and_glide_via_cancel": False,
|
||||
"pulse_and_glide_via_cancel_long": False,
|
||||
"pulse_and_glide_via_cancel_very_long": False,
|
||||
"pulse_and_glide_via_lkas": False,
|
||||
"lkas_allowed_for_aol": False,
|
||||
"main_cruise_aol_toggle": False,
|
||||
@@ -117,6 +126,83 @@ def test_pulse_and_glide_requires_developer_access_and_active_longitudinal(monke
|
||||
assert result.pulseAndGlide is False
|
||||
|
||||
|
||||
def test_pulse_and_glide_consumes_native_cancel_when_mapped(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
|
||||
|
||||
card = spc.StarPilotCard(SimpleNamespace(brand="gm"), SimpleNamespace(alternativeExperience=0))
|
||||
sm = make_sm()
|
||||
sm["carControl"].longActive = True
|
||||
toggles = make_toggles(
|
||||
pulse_and_glide_available=True,
|
||||
pulse_and_glide_via_cancel=True,
|
||||
)
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False, cancelPressed=True)
|
||||
|
||||
press = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.cancel, pressed=True)])
|
||||
card.update(press, starpilot_car_state, sm, toggles)
|
||||
assert press.buttonEvents == []
|
||||
|
||||
starpilot_car_state.cancelPressed = False
|
||||
release = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.cancel, pressed=False)])
|
||||
result = card.update(release, starpilot_car_state, sm, toggles)
|
||||
|
||||
assert card.pulse_and_glide is True
|
||||
assert result.pulseAndGlide is True
|
||||
assert release.buttonEvents == []
|
||||
|
||||
|
||||
def test_pulse_and_glide_consumes_lkas_when_mapped(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
|
||||
|
||||
card = spc.StarPilotCard(SimpleNamespace(brand="gm"), SimpleNamespace(alternativeExperience=0))
|
||||
sm = make_sm()
|
||||
sm["carControl"].longActive = True
|
||||
toggles = make_toggles(
|
||||
pulse_and_glide_available=True,
|
||||
pulse_and_glide_via_lkas=True,
|
||||
)
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False)
|
||||
car_state = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.lkas, pressed=True)])
|
||||
|
||||
result = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
|
||||
assert card.pulse_and_glide is True
|
||||
assert result.pulseAndGlide is True
|
||||
assert car_state.buttonEvents == []
|
||||
|
||||
|
||||
def test_pulse_and_glide_long_cancel_consumes_release_after_threshold(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
|
||||
|
||||
card = spc.StarPilotCard(SimpleNamespace(brand="gm"), SimpleNamespace(alternativeExperience=0))
|
||||
sm = make_sm()
|
||||
sm["carControl"].longActive = True
|
||||
toggles = make_toggles(
|
||||
pulse_and_glide_available=True,
|
||||
pulse_and_glide_via_cancel_long=True,
|
||||
)
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False, cancelPressed=True)
|
||||
|
||||
for frame in range(card.long_press_threshold):
|
||||
button_events = [SimpleNamespace(type=spc.ButtonType.cancel, pressed=True)] if frame == 0 else []
|
||||
card.update(make_car_state(button_events=button_events), starpilot_car_state, sm, toggles)
|
||||
|
||||
assert card.pulse_and_glide is True
|
||||
|
||||
starpilot_car_state.cancelPressed = False
|
||||
release = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.cancel, pressed=False)])
|
||||
card.update(release, starpilot_car_state, sm, toggles)
|
||||
|
||||
assert card.pulse_and_glide is True
|
||||
assert release.buttonEvents == []
|
||||
|
||||
|
||||
def make_car_state(available=False, enabled=False, button_events=None, brake_pressed=False, gas_pressed=False):
|
||||
return SimpleNamespace(
|
||||
buttonEvents=button_events or [],
|
||||
|
||||
@@ -1607,6 +1607,27 @@
|
||||
"parent_key": "SpeedLimitController",
|
||||
"settings_tier": "advanced"
|
||||
},
|
||||
{
|
||||
"key": "VisionSpeedLimitLowLimitFilter",
|
||||
"label": "Ignore Low Vision Speed Limits",
|
||||
"description": "Prevent SLC from acting on vision-detected speed limits at or below the configured threshold. Detection, display, debugging, and training collection remain active.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"parent_key": "VisionSpeedLimitDetection",
|
||||
"settings_tier": "advanced"
|
||||
},
|
||||
{
|
||||
"key": "VisionSpeedLimitLowLimitThreshold",
|
||||
"label": "Ignore At or Below",
|
||||
"description": "Vision-detected limits at or below this value will not control speed. The value uses your selected mph or km/h unit.",
|
||||
"data_type": "int",
|
||||
"ui_type": "numeric",
|
||||
"min": 5,
|
||||
"max": 80,
|
||||
"step": 5,
|
||||
"parent_key": "VisionSpeedLimitLowLimitFilter",
|
||||
"settings_tier": "advanced"
|
||||
},
|
||||
{
|
||||
"key": "VisionSpeedLimitAutoBookmark",
|
||||
"label": "Auto-Bookmark Vision Signs",
|
||||
|
||||
@@ -197,6 +197,27 @@ def test_vasm_is_default_off_and_configured_only_in_galaxy():
|
||||
assert all("VASM" not in path.read_text(encoding="utf-8") for path in physical_settings)
|
||||
|
||||
|
||||
def test_low_vision_limit_filter_is_default_off_and_configured_only_in_galaxy():
|
||||
sections = _params_by_section(_layout())
|
||||
longitudinal = sections["Longitudinal (Speed & Following)"]
|
||||
toggle = longitudinal["VisionSpeedLimitLowLimitFilter"]
|
||||
threshold = longitudinal["VisionSpeedLimitLowLimitThreshold"]
|
||||
|
||||
assert toggle["parent_key"] == "VisionSpeedLimitDetection"
|
||||
assert threshold["parent_key"] == "VisionSpeedLimitLowLimitFilter"
|
||||
assert threshold["min"] == 5
|
||||
assert threshold["max"] == 80
|
||||
assert threshold["step"] == 5
|
||||
assert _declared_default("VisionSpeedLimitLowLimitFilter") == "0"
|
||||
assert _declared_default("VisionSpeedLimitLowLimitThreshold") == "25"
|
||||
|
||||
physical_settings = (
|
||||
REPO_ROOT / "selfdrive/ui/layouts/settings/starpilot/longitudinal.py",
|
||||
REPO_ROOT / "selfdrive/ui/layouts/settings/starpilot/aethergrid.py",
|
||||
)
|
||||
assert all("VisionSpeedLimitLowLimit" not in path.read_text(encoding="utf-8") for path in physical_settings)
|
||||
|
||||
|
||||
def test_pip_preview_is_under_driving_screen_widgets_and_configured_only_in_galaxy():
|
||||
sections = _params_by_section(_layout())
|
||||
visual = sections["Visual (Display & UI)"]
|
||||
|
||||
Reference in New Issue
Block a user