The Saturday Threshold

This commit is contained in:
firestar5683
2026-08-15 11:22:14 -05:00
parent 603f9b56f9
commit f73af2211d
12 changed files with 136 additions and 24 deletions
@@ -101,6 +101,7 @@ class Request:
@dataclass
class FwQueryConfig:
requests: list[Request]
non_tester_present_ecus: list[Ecu] = field(default_factory=list)
# TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus
# Overrides and removes from essential ecus for specific models and ecus (exact matching)
non_essential_ecus: dict[Ecu, list[str]] = field(default_factory=dict)
+6
View File
@@ -181,6 +181,9 @@ def get_present_ecus(can_recv: CanRecvCallable, can_send: CanSendCallable, set_o
continue
for ecu_type, addr, sub_addr in config.get_all_ecus(VERSIONS[brand]):
if ecu_type in config.non_tester_present_ecus:
continue
# Only query ecus in whitelist if whitelist is not empty
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
a = (addr, sub_addr, r.bus)
@@ -216,6 +219,9 @@ def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, list[b
# Since we can't know what request an ecu responded to, add matches for all possible rx offsets
for brand, config, r in REQUESTS:
for ecu in config.get_all_ecus(VERSIONS[brand]):
if ecu[0] in config.non_tester_present_ecus:
continue
if len(r.whitelist_ecus) == 0 or ecu[0] in r.whitelist_ecus:
brand_rx_addrs[brand].add((uds.get_rx_addr_for_tx_addr(ecu[1], r.rx_offset), ecu[2]))
@@ -5,14 +5,16 @@ from types import SimpleNamespace
import pytest
from opendbc.can import CANPacker, CANParser
from opendbc.car import Bus, structs
from opendbc.car import Bus, fw_versions, structs
from opendbc.car.fw_query_definitions import StdQueries
from opendbc.car.subaru import subarucan
from opendbc.car.subaru.carcontroller import CarController
from opendbc.car.subaru.carstate import CarState
from opendbc.car.subaru.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import match_fw_to_car
from opendbc.car.subaru.interface import CarInterface
from opendbc.car.subaru.values import CAR, DBC, CanBus, SubaruFlags, SubaruSafetyFlags
from opendbc.car.subaru.values import CAR, DBC, FW_QUERY_CONFIG, SUBARU_ALT_VERSION_REQUEST, SUBARU_VERSION_REQUEST, CanBus, \
SubaruFlags, SubaruSafetyFlags
from opendbc.car.structs import CarParams
@@ -66,6 +68,32 @@ def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle(
class TestSubaruFingerprint:
def test_eyesight_queries_do_not_change_diagnostic_state(self, monkeypatch):
camera_requests = [request for request in FW_QUERY_CONFIG.requests if CarParams.Ecu.fwdCamera in request.whitelist_ecus]
assert CarParams.Ecu.fwdCamera in FW_QUERY_CONFIG.non_tester_present_ecus
assert {tuple(request.request) for request in camera_requests} == {
(SUBARU_VERSION_REQUEST,),
(SUBARU_ALT_VERSION_REQUEST,),
}
for request in camera_requests:
assert StdQueries.TESTER_PRESENT_REQUEST not in request.request
assert StdQueries.DEFAULT_DIAGNOSTIC_REQUEST not in request.request
queried_ecus = set()
def collect_queries(_can_recv, _can_send, queries, _responses, timeout):
queried_ecus.update(queries)
return set()
monkeypatch.setattr(fw_versions, "REQUESTS", [("subaru", FW_QUERY_CONFIG, request) for request in FW_QUERY_CONFIG.requests])
monkeypatch.setattr(fw_versions, "VERSIONS", {"subaru": FW_VERSIONS})
monkeypatch.setattr(fw_versions, "get_ecu_addrs", collect_queries)
fw_versions.get_present_ecus(lambda **_kwargs: [], lambda _msgs: None, lambda _enabled: None)
assert queried_ecus
assert all(address != 0x787 for address, _subaddress, _bus in queried_ecus)
def test_fw_version_format(self):
for platform, fws_per_ecu in FW_VERSIONS.items():
for (ecu, _, _), fws in fws_per_ecu.items():
+4 -12
View File
@@ -281,12 +281,10 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.engine, Ecu.transmission],
logging=True,
),
# Non-OBD requests
# Some Eyesight modules fail on TESTER_PRESENT_REQUEST
# TODO: check if this resolves the fingerprinting issue for the 2023 Ascent and other new Subaru cars
Request(
[SUBARU_VERSION_REQUEST],
[SUBARU_VERSION_RESPONSE],
@@ -300,28 +298,22 @@ FW_QUERY_CONFIG = FwQueryConfig(
bus=0,
logging=True,
),
Request(
[StdQueries.DEFAULT_DIAGNOSTIC_REQUEST, StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.DEFAULT_DIAGNOSTIC_RESPONSE, StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera],
bus=0,
logging=True,
),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.engine, Ecu.transmission],
bus=0,
),
# GEN2 powertrain bus query
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.engine, Ecu.transmission],
bus=1,
obd_multiplexing=False,
),
],
non_tester_present_ecus=[Ecu.fwdCamera],
# We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists
non_essential_ecus={
Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)),
@@ -265,7 +265,7 @@ class TestFwFingerprintTiming:
print(f'get_vin {name} case, query time={self.total_time / self.N} seconds')
def test_fw_query_timing(self, subtests, mocker):
total_ref_time = {1: 7.4, 2: 8.0}
total_ref_time = {1: 7.3, 2: 7.9}
brand_ref_times = {
1: {
'gm': 1.0,
@@ -276,7 +276,7 @@ class TestFwFingerprintTiming:
'hyundai': 0.65,
'mazda': 0.1,
'nissan': 0.8,
'subaru': 0.65,
'subaru': 0.55,
'tesla': 0.1,
'toyota': 0.7,
'volkswagen': 0.65,
@@ -21,6 +21,7 @@ from openpilot.selfdrive.controls.lib.lead_follow_policy import is_nonurgent_dup
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import (
get_far_follow_output_slew_rates,
get_follow_prebrake_min_headway,
get_force_stop_handoff_distance,
is_gm_silverado_early_follow_lead,
is_toyota_rav4_tss2_post_departure_tune,
get_toyota_sienna_post_departure_restop_cap,
@@ -2177,7 +2178,8 @@ class LongitudinalPlanner:
# the line parks us short. Below that the existing v_cruise=0 path finishes the stop,
# since forcingStopLength is decaying to zero and the obstacle would land behind us.
force_stop_x = None
if sm['starpilotPlan'].forcingStop and sm['starpilotPlan'].forcingStopLength > STOP_DISTANCE:
force_stop_handoff_m = get_force_stop_handoff_distance(self.CP.carFingerprint)
if sm['starpilotPlan'].forcingStop and sm['starpilotPlan'].forcingStopLength > force_stop_handoff_m:
force_stop_x = float(sm['starpilotPlan'].forcingStopLength) + STOP_DISTANCE
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j,
@@ -18,6 +18,8 @@ TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MIN_MODEL_PROB = 0.95
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_LATERAL_OFFSET = 1.75
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MIN_BRAKE = 0.18
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_BRAKE = 0.32
TOYOTA_CAMRY_TSS2_FORCE_STOP_HANDOFF_M = 4.5
DEFAULT_FORCE_STOP_HANDOFF_M = 6.0
def is_toyota_rav4_tss2_post_departure_tune(CP):
@@ -109,3 +111,10 @@ def get_toyota_sienna_post_departure_restop_cap(CP, lead, v_ego, accel_min,
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_BRAKE,
))
return brake_floor if accel_min >= 0.0 else max(float(accel_min), brake_floor)
def get_force_stop_handoff_distance(car_fingerprint):
"""Return the distance at which force-stop control hands off to MPC."""
if str(car_fingerprint) == "TOYOTA_CAMRY_TSS2":
return TOYOTA_CAMRY_TSS2_FORCE_STOP_HANDOFF_M
return DEFAULT_FORCE_STOP_HANDOFF_M
@@ -13,6 +13,7 @@ from openpilot.starpilot.controls.lib.starpilot_vcruise import (
get_lead_veto_distance,
get_slc_lead_drop_relaxed_target,
)
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import get_force_stop_handoff_distance
from types import SimpleNamespace
@@ -116,6 +117,11 @@ def test_elantra_gets_lead_veto_margin_before_force_stop():
assert get_lead_veto_distance(SimpleNamespace(carFingerprint="OTHER_CAR")) == pytest.approx(75.0)
def test_camry_tss2_uses_closer_force_stop_handoff():
assert get_force_stop_handoff_distance("TOYOTA_CAMRY_TSS2") == pytest.approx(4.5)
assert get_force_stop_handoff_distance("TOYOTA_RAV4_TSS2") == pytest.approx(6.0)
def test_curve_speed_controller_holds_target_through_brief_detector_dropout():
planner, vcruise = make_vcruise()
sm = make_sm(standstill=False)
@@ -478,6 +484,24 @@ def test_force_stop_stays_committed_while_moving_even_if_scene_opens():
assert vcruise.forcing_stop
def test_force_stop_releases_after_cem_light_clears_while_moving():
planner, vcruise = make_vcruise(red_light=True, raw_model_stopped=False, forcing_stop=True)
sm = make_sm(standstill=False)
toggles = make_toggles()
update_vcruise(vcruise, sm, toggles, now=0.0, v_ego=3.0)
assert vcruise.force_stop_from_light
planner.starpilot_cem.stop_light_detected = False
update_vcruise(vcruise, sm, toggles, now=0.25, v_ego=3.0)
assert vcruise.forcing_stop
result = update_vcruise(vcruise, sm, toggles, now=0.75, v_ego=3.0)
assert result == pytest.approx(20.0)
assert not vcruise.forcing_stop
assert not vcruise.force_stop_from_light
def test_force_stop_turn_scene_veto_blocks_new_activation():
_, vcruise = make_vcruise(red_light=False, raw_model_stopped=False, forcing_stop=False)
sm = make_sm(standstill=False)
+12 -2
View File
@@ -6,7 +6,7 @@ import pyray as rl
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import PC, TICI
from openpilot.system.hardware import HARDWARE, PC, TICI
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.egl import (
init_egl, is_egl_initialized, finish_gl, create_egl_image, destroy_egl_image,
@@ -17,7 +17,17 @@ from openpilot.selfdrive.ui.ui_state import ui_state
CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts
STREAM_DISCOVERY_REFRESH_INTERVAL = 0.5 # seconds between nonblocking stream advertisements
MICI_FORCE_TEXTURE_CAMERA = os.getenv("MICI_FORCE_TEXTURE_CAMERA", "0") == "1"
def _default_force_texture_camera(device_type: str) -> bool:
return device_type == "mici"
DEVICE_TYPE = HARDWARE.get_device_type()
MICI_FORCE_TEXTURE_CAMERA = os.getenv(
"MICI_FORCE_TEXTURE_CAMERA",
"1" if _default_force_texture_camera(DEVICE_TYPE) else "0",
) == "1"
# One stale frame can be normal ring-buffer reuse; repeated consecutive regressions demote EGL.
EGL_REGRESSIVE_FRAME_FALLBACK_THRESHOLD = 3
@@ -30,6 +30,12 @@ def test_mici_uses_shared_camera_view():
assert not big_cameraview.CameraView._use_upstream_engaged_color
def test_c4_defaults_to_ui_owned_camera_textures():
assert big_cameraview._default_force_texture_camera("mici")
assert not big_cameraview._default_force_texture_camera("tici")
assert not big_cameraview._default_force_texture_camera("tizi")
def test_pending_switch_is_cancelled_when_requested_stream_is_current():
view = _camera_view()
view._stream_type = big_cameraview.VisionStreamType.VISION_STREAM_ROAD
+38 -5
View File
@@ -8,12 +8,14 @@ from openpilot.common.realtime import DT_MDL
from openpilot.starpilot.common.starpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED
from openpilot.starpilot.controls.lib.curve_speed_controller import CurveSpeedController, is_manual_speed_control
from openpilot.starpilot.controls.lib.speed_limit_controller import SpeedLimitController
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import get_force_stop_handoff_distance
CSC_MIN_SPEED = CITY_SPEED_LIMIT * CV.MPH_TO_MS
CSC_CURVE_RELEASE_HOLD_TIME = 0.75
OVERRIDE_FORCE_STOP_TIMER = 10
STANDSTILL_FORCE_STOP_CLEAR_TIME = 0.75
STANDSTILL_FORCE_STOP_LIGHT_HOLD_TIME = 5.0
FORCE_STOP_LIGHT_CLEAR_TIME = 0.5
SLC_LEAD_DROP_RELAXATION_MIN_SPEED = 20.0 * CV.MPH_TO_MS
SLC_LEAD_DROP_RELAXATION_MIN_DISTANCE = 30.0
SLC_LEAD_DROP_RELAXATION_MIN_HEADWAY = 1.2
@@ -48,7 +50,6 @@ LEAD_VETO_M = 75.0 # m — lead proximity that vetoes Force Stop (kept of
LEAD_VETO_M_OVERRIDES = {
"HYUNDAI_ELANTRA_2021": 90.0,
}
MPC_HANDOFF_M = 6.0 # m — below this, command 0 and let MPC finish the stop
FORCE_STOP_APPROACH_DECEL = 0.65 # m/s^2 — speed ceiling before commit. LOWER = more early
# braking; don't go under FORCE_STOP_MODEL_APPROACH_DECEL
ADAS_MAX_MS = 17.88 # 40 mph — cross-street ADAS guard
@@ -166,6 +167,8 @@ class StarPilotVCruise:
self.standstill_force_stop_clear_since = 0.0
self.standstill_force_stop_started_at = None
self.standstill_force_stop_reason = None
self.force_stop_from_light = False
self.force_stop_light_clear_since = None
self.controls_enabled_previously = False
# Kinematic distance estimator. Same attribute also published as
# starpilotPlan.forcingStopLength, so the existing reader keeps working.
@@ -314,6 +317,9 @@ class StarPilotVCruise:
self._applied_slc_control_target = 0.0
long_control_active = sm["carControl"].longActive
force_stop_handoff_m = get_force_stop_handoff_distance(
getattr(starpilot_toggles, "car_model", "")
)
raw_stop_seen = bool(
self.starpilot_planner.starpilot_cem.stop_light_detected
@@ -393,6 +399,13 @@ class StarPilotVCruise:
force_stop_active = cem_path or dash_path
if cem_path:
self.force_stop_from_light = True
self.force_stop_light_clear_since = None
elif not self.forcing_stop:
self.force_stop_from_light = False
self.force_stop_light_clear_since = None
# Latch on first dash frame so the CEM pin can fire and we don't release on
# transient dashboard dropouts. Cleared in the no-force-stop branch below.
if dash_path:
@@ -463,6 +476,26 @@ class StarPilotVCruise:
force_stop_enabled |= self.forcing_stop and not sm["carState"].standstill
force_stop_enabled |= self.standstill_force_stop_hold
light_stop_cleared = (
self.forcing_stop and
self.force_stop_from_light and
not sm["carState"].standstill and
not stop_light_detected and
not raw_model_stopped and
not dash_active
)
if light_stop_cleared:
if self.force_stop_light_clear_since is None:
self.force_stop_light_clear_since = now
elif self._elapsed_seconds(now, self.force_stop_light_clear_since) >= FORCE_STOP_LIGHT_CLEAR_TIME:
self.forcing_stop = False
self.force_stop_from_light = False
self.force_stop_light_clear_since = None
self.force_stop_timer = 0.0
force_stop_enabled = False
else:
self.force_stop_light_clear_since = None
if self.forcing_stop and standstill and not force_stop_enabled and self.standstill_force_stop_reason != "sign":
self.override_force_stop_timer = OVERRIDE_FORCE_STOP_TIMER
@@ -583,11 +616,11 @@ class StarPilotVCruise:
# Kinematic profile with user offset. Positive offset shifts the perceived
# line further down the road -> car rolls further before commanding 0.
effective_d = self.tracked_model_length + offset_m
if effective_d <= MPC_HANDOFF_M:
if effective_d <= force_stop_handoff_m:
v_target = 0.0
else:
approach_decel = FORCE_STOP_DASH_APPROACH_DECEL if dash_active else FORCE_STOP_MODEL_APPROACH_DECEL
v_target = math.sqrt(2.0 * approach_decel * (effective_d - MPC_HANDOFF_M))
v_target = math.sqrt(2.0 * approach_decel * (effective_d - force_stop_handoff_m))
v_cruise = min(v_target, v_cruise)
@@ -644,8 +677,8 @@ class StarPilotVCruise:
if adjacent_stop_d is not None:
approach_d = min(approach_d, adjacent_stop_d)
approach_d += offset_m
if approach_d > MPC_HANDOFF_M:
targets.append(math.sqrt(2.0 * FORCE_STOP_APPROACH_DECEL * (approach_d - MPC_HANDOFF_M)))
if approach_d > force_stop_handoff_m:
targets.append(math.sqrt(2.0 * FORCE_STOP_APPROACH_DECEL * (approach_d - force_stop_handoff_m)))
v_cruise = min(targets)
+1
View File
@@ -12,6 +12,7 @@ Quick start:
Set `BURN_IN_PREVENTION=0` to disable it or tune it with `BURN_IN_SHIFT_PIXELS`, `BURN_IN_SHIFT_INTERVAL`, and
`BURN_IN_SHIFT_TRANSITION_SECONDS`. TICI/TIZI shift the completed frame through the offscreen presentation path;
MICI uses direct shifting by default. Setting `WHITE_LUMINANCE_CAP` below `1.0` also enables the offscreen presentation path.
* the C4 UI uses copied camera textures by default to avoid sampling camerad's reusable EGL buffers; set `MICI_FORCE_TEXTURE_CAMERA=0` to re-enable EGL for diagnostics
* set `MICI_FORCE_RENDER_TEXTURE=1` to force the C4 UI through the offscreen presentation path for diagnostics
* set `GRID=50` to show a 50-pixel alignment grid overlay
* set `MAGIC_DEBUG=1` to show every dropped frames (only on device)