fotos glo glo

This commit is contained in:
firestar5683
2026-08-20 15:31:44 -05:00
parent d78902d73b
commit d8090be19d
12 changed files with 173 additions and 93 deletions
@@ -22,14 +22,14 @@ _LEGACY_2025_REENGAGE_MAX_STEER_RATE = 2.0
_LEGACY_2025_REENGAGE_MAX_ANGLE_DELTA = 1.0
_LEGACY_2025_RECLAIM_FRAMES = 36
_LEGACY_2025_RECLAIM_EXPONENT = 2.5
_ASCENT_OVERRIDE_HOLD_FRAMES = 10
_ASCENT_REENGAGE_SETTLE_FRAMES = 8
_ASCENT_REENGAGE_MAX_STEER_RATE = 2.0
_ASCENT_REENGAGE_MAX_ANGLE_DELTA = 1.0
_ASCENT_RECLAIM_FRAMES = 36
_ASCENT_RECLAIM_EXPONENT = 2.5
_ASCENT_MADS_MIN_SPEED = 0.44704
_ASCENT_MADS_MAX_STEER_ANGLE = 120.0
_ANGLE_OVERRIDE_HOLD_FRAMES = 10
_ANGLE_REENGAGE_SETTLE_FRAMES = 8
_ANGLE_REENGAGE_MAX_STEER_RATE = 2.0
_ANGLE_REENGAGE_MAX_ANGLE_DELTA = 1.0
_ANGLE_RECLAIM_FRAMES = 36
_ANGLE_RECLAIM_EXPONENT = 2.5
_ANGLE_MADS_MIN_SPEED = 0.44704
_ANGLE_MADS_MAX_STEER_ANGLE = 120.0
def get_safety_CP():
@@ -50,13 +50,13 @@ class CarController(CarControllerBase):
self.legacy_2025_reengage_reference_angle = 0.0
self.legacy_2025_reclaim_frames = 0
self.legacy_2025_reclaim_start_angle = 0.0
self.ascent_lkas_active = False
self.ascent_handoff_active = False
self.ascent_override_hold_frames = 0
self.ascent_reengage_settle_frames = 0
self.ascent_reengage_reference_angle = 0.0
self.ascent_reclaim_frames = 0
self.ascent_reclaim_start_angle = 0.0
self.angle_lkas_active = False
self.angle_handoff_active = False
self.angle_override_hold_frames = 0
self.angle_reengage_settle_frames = 0
self.angle_reengage_reference_angle = 0.0
self.angle_reclaim_frames = 0
self.angle_reclaim_start_angle = 0.0
self.cruise_button_prev = 0
self.steer_rate_counter = 0
@@ -137,67 +137,67 @@ class CarController(CarControllerBase):
self.legacy_2025_reclaim_frames -= 1
return target_angle
def _reset_ascent_handoff(self):
self.ascent_handoff_active = False
self.ascent_override_hold_frames = 0
self.ascent_reengage_settle_frames = 0
self.ascent_reengage_reference_angle = 0.0
self.ascent_reclaim_frames = 0
self.ascent_reclaim_start_angle = 0.0
def _reset_angle_handoff(self):
self.angle_handoff_active = False
self.angle_override_hold_frames = 0
self.angle_reengage_settle_frames = 0
self.angle_reengage_reference_angle = 0.0
self.angle_reclaim_frames = 0
self.angle_reclaim_start_angle = 0.0
def _ascent_manual_handoff(self, CS, lat_active):
def _angle_manual_handoff(self, CS, lat_active):
if not lat_active:
self._reset_ascent_handoff()
self._reset_angle_handoff()
return False
if CS.out.steeringPressed:
self.ascent_handoff_active = True
self.ascent_override_hold_frames = _ASCENT_OVERRIDE_HOLD_FRAMES
self.ascent_reengage_settle_frames = 0
self.ascent_reengage_reference_angle = CS.out.steeringAngleDeg
self.ascent_reclaim_frames = 0
self.angle_handoff_active = True
self.angle_override_hold_frames = _ANGLE_OVERRIDE_HOLD_FRAMES
self.angle_reengage_settle_frames = 0
self.angle_reengage_reference_angle = CS.out.steeringAngleDeg
self.angle_reclaim_frames = 0
return True
if not self.ascent_handoff_active and not self.ascent_lkas_active and \
abs(CS.out.steeringRateDeg) > _ASCENT_REENGAGE_MAX_STEER_RATE:
self.ascent_handoff_active = True
self.ascent_reengage_reference_angle = CS.out.steeringAngleDeg
if not self.angle_handoff_active and not self.angle_lkas_active and \
abs(CS.out.steeringRateDeg) > _ANGLE_REENGAGE_MAX_STEER_RATE:
self.angle_handoff_active = True
self.angle_reengage_reference_angle = CS.out.steeringAngleDeg
if not self.ascent_handoff_active:
if not self.angle_handoff_active:
return False
if self.ascent_override_hold_frames > 0:
self.ascent_override_hold_frames -= 1
if self.ascent_override_hold_frames == 0:
self.ascent_reengage_reference_angle = CS.out.steeringAngleDeg
if self.angle_override_hold_frames > 0:
self.angle_override_hold_frames -= 1
if self.angle_override_hold_frames == 0:
self.angle_reengage_reference_angle = CS.out.steeringAngleDeg
return True
wheel_stable = abs(CS.out.steeringRateDeg) <= _ASCENT_REENGAGE_MAX_STEER_RATE and \
abs(CS.out.steeringAngleDeg - self.ascent_reengage_reference_angle) <= _ASCENT_REENGAGE_MAX_ANGLE_DELTA
wheel_stable = abs(CS.out.steeringRateDeg) <= _ANGLE_REENGAGE_MAX_STEER_RATE and \
abs(CS.out.steeringAngleDeg - self.angle_reengage_reference_angle) <= _ANGLE_REENGAGE_MAX_ANGLE_DELTA
if wheel_stable:
self.ascent_reengage_settle_frames += 1
self.angle_reengage_settle_frames += 1
else:
self.ascent_reengage_settle_frames = 0
self.ascent_reengage_reference_angle = CS.out.steeringAngleDeg
self.angle_reengage_settle_frames = 0
self.angle_reengage_reference_angle = CS.out.steeringAngleDeg
if self.ascent_reengage_settle_frames < _ASCENT_REENGAGE_SETTLE_FRAMES:
if self.angle_reengage_settle_frames < _ANGLE_REENGAGE_SETTLE_FRAMES:
return True
self.ascent_handoff_active = False
self.ascent_reengage_settle_frames = 0
self.ascent_reclaim_frames = _ASCENT_RECLAIM_FRAMES
self.ascent_reclaim_start_angle = CS.out.steeringAngleDeg
self.angle_handoff_active = False
self.angle_reengage_settle_frames = 0
self.angle_reclaim_frames = _ANGLE_RECLAIM_FRAMES
self.angle_reclaim_start_angle = CS.out.steeringAngleDeg
return True
def _ascent_reclaim_target(self, target_angle):
if self.ascent_reclaim_frames <= 0:
def _angle_reclaim_target(self, target_angle):
if self.angle_reclaim_frames <= 0:
return target_angle
progress = (_ASCENT_RECLAIM_FRAMES - self.ascent_reclaim_frames + 1) / _ASCENT_RECLAIM_FRAMES
eased_progress = progress ** _ASCENT_RECLAIM_EXPONENT
target_angle = self.ascent_reclaim_start_angle + eased_progress * \
(target_angle - self.ascent_reclaim_start_angle)
self.ascent_reclaim_frames -= 1
progress = (_ANGLE_RECLAIM_FRAMES - self.angle_reclaim_frames + 1) / _ANGLE_RECLAIM_FRAMES
eased_progress = progress ** _ANGLE_RECLAIM_EXPONENT
target_angle = self.angle_reclaim_start_angle + eased_progress * \
(target_angle - self.angle_reclaim_start_angle)
self.angle_reclaim_frames -= 1
return target_angle
def lateral_angle(self, CC, CS):
@@ -224,30 +224,41 @@ class CarController(CarControllerBase):
self.legacy_2025_lkas_active = lkas_active
return subarucan.create_steering_control_angle(self.packer, apply_steer, lkas_active, self.angle_bus)
if self.CP.carFingerprint == CAR.SUBARU_ASCENT_2023:
if self.CP.carFingerprint in (CAR.SUBARU_ASCENT_2023, CAR.SUBARU_OUTBACK_2023):
mads_only = CC.latActive and not CC.enabled
mads_only_ok = CS.out.vEgoRaw > _ASCENT_MADS_MIN_SPEED and \
abs(CS.out.steeringAngleDeg) < _ASCENT_MADS_MAX_STEER_ANGLE
mads_only_ok = CS.out.vEgoRaw > _ANGLE_MADS_MIN_SPEED and \
abs(CS.out.steeringAngleDeg) < _ANGLE_MADS_MAX_STEER_ANGLE
lkas_available = CC.latActive and (not mads_only or mads_only_ok) and \
CS.out.gearShifter == structs.CarState.GearShifter.drive and not CS.out.standstill
manual_handoff = self._ascent_manual_handoff(CS, lkas_available)
manual_handoff = self._angle_manual_handoff(CS, lkas_available)
lkas_active = lkas_available and not manual_handoff
if lkas_active and not self.ascent_lkas_active:
if lkas_active and not self.angle_lkas_active:
self.apply_steer_last = CS.out.steeringAngleDeg
steer_target = self._ascent_reclaim_target(CC.actuators.steeringAngleDeg) if lkas_active else CC.actuators.steeringAngleDeg
apply_steer = apply_std_steer_angle_limits(
steer_target,
self.apply_steer_last,
CS.out.vEgoRaw,
CS.out.steeringAngleDeg,
lkas_active,
self.p.FIXED_ANGLE_LIMITS,
)
steer_target = self._angle_reclaim_target(CC.actuators.steeringAngleDeg) if lkas_active else CC.actuators.steeringAngleDeg
if self.CP.carFingerprint == CAR.SUBARU_ASCENT_2023:
apply_steer = apply_std_steer_angle_limits(
steer_target,
self.apply_steer_last,
CS.out.vEgoRaw,
CS.out.steeringAngleDeg,
lkas_active,
self.p.FIXED_ANGLE_LIMITS,
)
else:
apply_steer = apply_steer_angle_limits_vm(
steer_target,
self.apply_steer_last,
CS.out.vEgoRaw,
CS.out.steeringAngleDeg,
lkas_active,
self.p,
self.VM,
)
self.apply_steer_last = apply_steer
self.ascent_lkas_active = lkas_active
self.angle_lkas_active = lkas_active
return subarucan.create_steering_control_angle(self.packer, apply_steer, lkas_active, self.angle_bus)
abs_torque = abs(CS.out.steeringTorque)
@@ -51,6 +51,8 @@ class CarInterface(CarInterfaceBase):
if ret.flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = structs.CarParams.SteerControlType.angle
if candidate == CAR.SUBARU_OUTBACK_2023:
ret.lateralSmoothSeconds = 0.4
elif candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023):
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
@@ -202,6 +202,7 @@ def test_outback_2023_uses_d_platform_bus_layout():
assert parsers[Bus.main].bus == CanBus.main
assert controller.angle_bus == CanBus.main
assert controller.status_bus == CanBus.main
assert CP.lateralSmoothSeconds == pytest.approx(0.4)
def test_legacy_2025_uses_gen2_angle_bus_layout():
@@ -458,8 +459,9 @@ def test_ascent_angle_controller_uses_fixed_angle_rate_limits():
assert CS.out.steeringAngleDeg < parser.vl["ES_LKAS_ANGLE"]["LKAS_Output"] < -25.0
def test_ascent_angle_controller_yields_until_manual_steering_settles():
CP = CarInterface.get_non_essential_params(CAR.SUBARU_ASCENT_2023)
@pytest.mark.parametrize("platform", (CAR.SUBARU_ASCENT_2023, CAR.SUBARU_OUTBACK_2023))
def test_angle_controller_yields_until_manual_steering_settles(platform):
CP = CarInterface.get_non_essential_params(platform)
controller = CarController({}, CP)
CC = SimpleNamespace(enabled=True, latActive=True, actuators=SimpleNamespace(steeringAngleDeg=-10.0))
CS = SimpleNamespace(out=SimpleNamespace(
+3 -3
View File
@@ -221,9 +221,9 @@ def get_plan_reach(model_v2) -> float:
def get_control_lateral_smooth_seconds(brand: str, v_ego: float, vehicle_smooth_seconds: float) -> float:
if brand != "rivian":
return LAT_SMOOTH_SECONDS
return get_car_lateral_smooth_seconds(brand, v_ego, vehicle_smooth_seconds)
if brand == "rivian" or (brand == "subaru" and vehicle_smooth_seconds > 0.0):
return get_car_lateral_smooth_seconds(brand, v_ego, vehicle_smooth_seconds)
return LAT_SMOOTH_SECONDS
def turn_lead_allowed(brand: str, lateral_control_mode: car.CarControl.Actuators.LateralControlMode) -> bool:
@@ -21,6 +21,15 @@ def test_non_rivian_control_smoothing_matches_starpilot(v_ego):
assert get_control_lateral_smooth_seconds("toyota", v_ego, 0.0) == 0.1
@pytest.mark.parametrize(("v_ego", "expected"), [
(0.0, 0.4),
(5.0, 0.2),
(30.0, 0.0),
])
def test_subaru_control_smoothing_uses_vehicle_schedule(v_ego, expected):
assert get_control_lateral_smooth_seconds("subaru", v_ego, 0.4) == pytest.approx(expected)
@pytest.mark.parametrize(("v_ego", "expected"), [
(0.0, 0.4),
(5.0, 0.2),
+6 -6
View File
@@ -68,8 +68,8 @@ def _model_smooth_seconds(params, key, default):
return round(min(max(value, SMOOTH_SECONDS_STEP), 2.0) / SMOOTH_SECONDS_STEP) * SMOOTH_SECONDS_STEP
def _should_publish_model_output(model_output, vipc_dropped_frames: int) -> bool:
return model_output is not None and vipc_dropped_frames == 0
def _should_publish_model_output(model_output, vipc_dropped_frames: int, external_gpu_active: bool = False) -> bool:
return model_output is not None and (external_gpu_active or vipc_dropped_frames == 0)
MIN_LAT_CONTROL_SPEED = 0.3
@@ -129,7 +129,7 @@ def get_lateral_smooth_seconds(v_ego: float, maximum: float = 0.0) -> float:
def get_car_lateral_smooth_seconds(brand: str, v_ego: float, maximum: float) -> float:
if brand == "rivian":
if brand in ("rivian", "subaru"):
return get_lateral_smooth_seconds(v_ego, maximum)
return maximum
@@ -844,7 +844,7 @@ def main(demo=False):
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
lat_smooth_default = CP.lateralSmoothSeconds if CP.brand == "rivian" else LAT_SMOOTH_SECONDS
lat_smooth_default = CP.lateralSmoothSeconds if (CP.brand == "rivian" or CP.lateralSmoothSeconds > 0.0) else LAT_SMOOTH_SECONDS
lat_smooth_maximum = _model_smooth_seconds(params, "LatSmoothSeconds", lat_smooth_default)
lat_smooth_seconds = get_car_lateral_smooth_seconds(CP.brand, v_ego, lat_smooth_maximum)
lat_delay = sm["liveDelay"].lateralDelay + lat_smooth_seconds
@@ -939,10 +939,10 @@ def main(demo=False):
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1
if model_output is not None and vipc_dropped_frames > 0:
if model_output is not None and vipc_dropped_frames > 0 and not external_gpu_active:
cloudlog.error(f"suppressing model output after dropping {vipc_dropped_frames} frames")
if _should_publish_model_output(model_output, vipc_dropped_frames):
if _should_publish_model_output(model_output, vipc_dropped_frames, external_gpu_active):
modelv2_send = messaging.new_message('modelV2')
starpilot_modelv2_send = messaging.new_message('starpilotModelV2')
drivingdata_send = messaging.new_message('drivingModelData')
@@ -24,6 +24,17 @@ def test_non_rivian_cars_keep_configured_starpilot_smoothing(v_ego):
assert get_car_lateral_smooth_seconds("toyota", v_ego, 0.4) == 0.4
@pytest.mark.parametrize(("v_ego", "expected"), [
(0.0, 0.4),
(2.0, 0.4),
(5.0, 0.2),
(8.0, 0.0),
(30.0, 0.0),
])
def test_subaru_uses_low_speed_configured_smoothing(v_ego, expected):
assert get_car_lateral_smooth_seconds("subaru", v_ego, 0.4) == pytest.approx(expected)
@pytest.mark.parametrize(("v_ego", "maximum", "expected"), [
(0.0, 0.4, 0.4),
(5.0, 0.4, 0.2),
+10 -7
View File
@@ -11,14 +11,17 @@ class FakeParams:
self.values[key] = value
@pytest.mark.parametrize(("model_output", "dropped_frames", "expected"), [
(object(), 0, True),
(object(), 1, False),
(object(), 2, False),
(None, 0, False),
@pytest.mark.parametrize(("model_output", "dropped_frames", "external_gpu_active", "expected"), [
(object(), 0, False, True),
(object(), 1, False, False),
(object(), 2, False, False),
(object(), 1, True, True),
(object(), 2, True, True),
(None, 0, False, False),
(None, 1, True, False),
])
def test_model_output_is_suppressed_after_vipc_drop(model_output, dropped_frames, expected):
assert modeld._should_publish_model_output(model_output, dropped_frames) is expected
def test_model_output_is_suppressed_after_vipc_drop(model_output, dropped_frames, external_gpu_active, expected):
assert modeld._should_publish_model_output(model_output, dropped_frames, external_gpu_active) is expected
def test_incompatible_downloaded_model_falls_back_to_builtin(monkeypatch):
@@ -37,6 +37,36 @@ def test_external_gpu_uses_a_longer_load_watchdog():
assert modeld.BIG_MODEL_RUN_WAIT_TIMEOUT_MS == 3000
def test_external_gpu_signal_wait_yields_cpu():
from tinygrad.runtime import ops_amd
sleeps = []
signal = ops_amd.AMDSignal.__new__(ops_amd.AMDSignal)
signal.should_return = False
signal.owner = SimpleNamespace(is_usb=lambda: True, iface=SimpleNamespace(sleep=sleeps.append))
signal._sleep(0)
assert sleeps == [1]
def test_native_amd_signal_keeps_existing_short_wait_behavior():
from tinygrad.runtime import ops_amd
sleeps = []
signal = ops_amd.AMDSignal.__new__(ops_amd.AMDSignal)
signal.should_return = False
signal.owner = SimpleNamespace(is_usb=lambda: False, iface=SimpleNamespace(sleep=sleeps.append))
signal._sleep(199)
assert sleeps == []
signal._sleep(201)
assert sleeps == [200]
def test_external_gpu_power_must_be_stable_after_vehicle_start():
panda_type = modeld.log.PandaState.PandaType.tres
+3 -2
View File
@@ -155,8 +155,9 @@ class FordLateralController:
def _lane_change(self) -> tuple[bool, int]:
if self.model is None:
return False, 0
state = int(self.model.meta.laneChangeState)
return state in (1, 2, 3), int(self.model.meta.laneChangeDirection)
state = int(getattr(self.model.meta.laneChangeState, "raw", self.model.meta.laneChangeState))
direction = int(getattr(self.model.meta.laneChangeDirection, "raw", self.model.meta.laneChangeDirection))
return state in (1, 2, 3), direction
@staticmethod
def _current_curvature(CS) -> float:
+9 -1
View File
@@ -49,13 +49,21 @@ def test_curvature_strategy_uses_polynomial_signals(controller):
assert result.ramp_type == 2
def test_lane_change_accepts_capnp_enum_wrappers(controller):
controller.model = SimpleNamespace(meta=SimpleNamespace(
laneChangeState=SimpleNamespace(raw=2),
laneChangeDirection=SimpleNamespace(raw=1),
))
assert controller._lane_change() == (True, 1)
def test_angle_strategy_uses_path_angle_and_shadow(controller):
result = controller.update_angle(
SimpleNamespace(latActive=True), car_state(curvature=0.001), SimpleNamespace(curvature=0.001))
assert result.active
assert result.curvature == 0.0
assert result.path_angle > 0.0
assert result.shadow_curvature == pytest.approx(0.001)
assert result.shadow_curvature == pytest.approx(0.0005)
def test_manual_turn_releases_lateral(controller):
+5 -2
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from typing import cast
import os, ctypes, struct, hashlib, functools, importlib, mmap, errno, array, contextlib, sys, weakref, itertools, collections, atexit
import os, ctypes, struct, hashlib, functools, importlib, mmap, errno, array, contextlib, sys, weakref, itertools, collections, atexit, time
assert sys.platform != 'win32'
from dataclasses import dataclass
from tinygrad.runtime.support.hcq import HCQCompiled, HCQAllocator, HCQBuffer, HWQueue, CLikeArgsState, HCQSignal, HCQProgram, FileIOInterface
@@ -45,6 +45,9 @@ class AMDSignal(HCQSignal):
def __init__(self, *args, **kwargs): super().__init__(*args, **{**kwargs, 'timestamp_divider': 100})
def _sleep(self, time_spent_since_last_sleep_ms:int):
if self.owner is not None and self.owner.is_usb():
self.owner.iface.sleep(1)
return
# Reasonable to sleep for long workloads (which take more than 200ms) and only timeline signals.
if time_spent_since_last_sleep_ms > 200 and self.owner is not None: self.owner.iface.sleep(200)
@@ -933,7 +936,7 @@ class USBIface(PCIIface):
# force devmem
return super().alloc(size, host=False, uncached=uncached, cpu_access=cpu_access, contiguous=contiguous, force_devmem=True, **kwargs)
def sleep(self, timeout): pass
def sleep(self, timeout): time.sleep(timeout / 1000)
def _mock(iface, name=None): return type(name or f"MOCK{iface.__name__}", (iface,), {})