mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-22 09:42:10 +08:00
Japanese BBQ Sauce
This commit is contained in:
@@ -15,8 +15,15 @@ import cv2
|
||||
|
||||
from starpilot.system.speed_limit_vision import DETECTOR_CLASSIFIER_EXPANSIONS
|
||||
|
||||
if __package__ in (None, ""):
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from common import SUPPORTED_SPEED_VALUES # type: ignore # noqa: TID251
|
||||
else:
|
||||
from .common import SUPPORTED_SPEED_VALUES
|
||||
|
||||
SPEED_VALUES = frozenset((15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75))
|
||||
|
||||
SPEED_VALUES = frozenset(SUPPORTED_SPEED_VALUES)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
|
||||
@@ -23,6 +23,11 @@ DETECTOR_CLASS_NAMES = (
|
||||
"school_zone_speed_limit",
|
||||
)
|
||||
DEFAULT_SPEED_VALUES = (15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75)
|
||||
# Values the review and dataset tooling can accept. Keep DEFAULT_SPEED_VALUES
|
||||
# aligned with the currently deployed classifier until an expanded model is
|
||||
# promoted; adding a class changes every output index after it.
|
||||
SUPPORTED_SPEED_VALUES = (10, *DEFAULT_SPEED_VALUES, 80, 90, 100)
|
||||
EXTENDED_CLASSIFIER_SPEED_VALUES = tuple(sorted(SUPPORTED_SPEED_VALUES, key=str))
|
||||
|
||||
DETECTOR_EXPORT_NAME = "speed_limit_us_detector.onnx"
|
||||
CLASSIFIER_EXPORT_NAME = "speed_limit_us_value_classifier.onnx"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import random
|
||||
|
||||
from dataclasses import dataclass
|
||||
@@ -16,7 +15,7 @@ from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont
|
||||
if __package__ in (None, ""):
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
|
||||
else:
|
||||
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
|
||||
|
||||
@@ -100,7 +99,6 @@ def render_regulatory_sign(speed_value: int, school_zone: bool, seed: int) -> Im
|
||||
def render_advisory_sign(speed_value: int, seed: int) -> Image.Image:
|
||||
rng = random.Random(seed)
|
||||
size = rng.randint(240, 320)
|
||||
image = Image.new("RGBA", (size, size), (255, 255, 255, 0))
|
||||
base = Image.new("RGBA", (size, size), (255, 255, 255, 0))
|
||||
draw = ImageDraw.Draw(base)
|
||||
|
||||
@@ -234,6 +232,7 @@ def main():
|
||||
parser.add_argument("--train-count", type=int, default=9000, help="Number of synthetic training detector images.")
|
||||
parser.add_argument("--val-count", type=int, default=1200, help="Number of synthetic validation detector images.")
|
||||
parser.add_argument("--negative-ratio", type=float, default=0.18, help="Share of detector images with no sign.")
|
||||
parser.add_argument("--speed-values", nargs="+", type=int, default=list(DEFAULT_SPEED_VALUES), help="Posted values to synthesize.")
|
||||
parser.add_argument("--seed", type=int, default=20260330, help="Random seed.")
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -246,7 +245,7 @@ def main():
|
||||
detector_image_dir = workspace / "detector" / "images"
|
||||
detector_label_dir = workspace / "detector" / "labels"
|
||||
classifier_dir = workspace / "classifier"
|
||||
speed_values = tuple(DEFAULT_SPEED_VALUES)
|
||||
speed_values = tuple(dict.fromkeys(args.speed_values))
|
||||
rng = random.Random(args.seed)
|
||||
|
||||
for split, count in (("train", max(args.train_count, 0)), ("val", max(args.val_count, 0))):
|
||||
|
||||
@@ -13,7 +13,7 @@ import numpy as np
|
||||
if __package__ in (None, ""):
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
|
||||
from generate_synthetic_us_speed_limits import KNOWN_REAL_CROPS, augment_sign, render_regulatory_sign # type: ignore
|
||||
else:
|
||||
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
|
||||
@@ -146,6 +146,7 @@ def main():
|
||||
parser.add_argument("--train-per-class", type=int, default=1800, help="Synthetic training samples per value.")
|
||||
parser.add_argument("--val-per-class", type=int, default=260, help="Synthetic validation samples per value.")
|
||||
parser.add_argument("--real-augmentations", type=int, default=28, help="Augmented mask samples to create per known real crop.")
|
||||
parser.add_argument("--speed-values", nargs="+", type=int, default=list(DEFAULT_SPEED_VALUES), help="Posted values to synthesize.")
|
||||
parser.add_argument("--seed", type=int, default=20260330, help="Random seed.")
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -157,7 +158,7 @@ def main():
|
||||
ensure_dir(classifier_dir / "val")
|
||||
|
||||
rng = random.Random(args.seed)
|
||||
speed_values = tuple(DEFAULT_SPEED_VALUES)
|
||||
speed_values = tuple(dict.fromkeys(args.speed_values))
|
||||
|
||||
for split, per_class in (("train", max(args.train_per_class, 0)), ("val", max(args.val_per_class, 0))):
|
||||
for speed_value in speed_values:
|
||||
|
||||
@@ -15,11 +15,11 @@ if __package__ in (None, ""):
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from build_value_dataset import crop_box, parse_yolo_labels # type: ignore
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
|
||||
from common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
|
||||
from generate_value_roi_classifier_dataset import augment_mask, extract_value_mask # type: ignore
|
||||
else:
|
||||
from .build_value_dataset import crop_box, parse_yolo_labels
|
||||
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
|
||||
from .common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace
|
||||
from .generate_value_roi_classifier_dataset import augment_mask, extract_value_mask
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ def parse_speed_from_read(text: str) -> int:
|
||||
if not match:
|
||||
return 0
|
||||
value = int(match.group(1))
|
||||
return value if value in DEFAULT_SPEED_VALUES else 0
|
||||
return value if value in SUPPORTED_SPEED_VALUES else 0
|
||||
|
||||
|
||||
def row_speed(row: dict[str, str]) -> int:
|
||||
@@ -68,7 +68,7 @@ def row_speed(row: dict[str, str]) -> int:
|
||||
text = (row.get(field) or "").strip()
|
||||
if text.isdigit():
|
||||
value = int(text)
|
||||
if value in DEFAULT_SPEED_VALUES:
|
||||
if value in SUPPORTED_SPEED_VALUES:
|
||||
return value
|
||||
for field in ("full_detection", "model_read", "ocr_read"):
|
||||
value = parse_speed_from_read(row.get(field, ""))
|
||||
@@ -235,12 +235,12 @@ def main() -> int:
|
||||
if args.max_rows > 0 and attempted >= args.max_rows:
|
||||
break
|
||||
|
||||
print(
|
||||
"Imported manifest classifier masks: "
|
||||
f"attempted={attempted} imported={imported} written={written} "
|
||||
f"skipped_no_speed={skipped_no_speed} skipped_no_crop={skipped_no_crop} skipped_no_mask={skipped_no_mask} "
|
||||
f"skipped_write_failed={skipped_write_failed}"
|
||||
)
|
||||
print("".join((
|
||||
"Imported manifest classifier masks: ",
|
||||
f"attempted={attempted} imported={imported} written={written} ",
|
||||
f"skipped_no_speed={skipped_no_speed} skipped_no_crop={skipped_no_crop} skipped_no_mask={skipped_no_mask} ",
|
||||
f"skipped_write_failed={skipped_write_failed}",
|
||||
)))
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ import cv2
|
||||
if __package__ in (None, ""):
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
|
||||
from common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
|
||||
else:
|
||||
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
|
||||
from .common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace
|
||||
|
||||
|
||||
CLASSIFIER_FIELDNAMES = [
|
||||
@@ -149,7 +149,7 @@ def parse_speed(text: str) -> int:
|
||||
value = int(float(text))
|
||||
except ValueError:
|
||||
return 0
|
||||
return value if value in DEFAULT_SPEED_VALUES else 0
|
||||
return value if value in SUPPORTED_SPEED_VALUES else 0
|
||||
|
||||
|
||||
def parse_bbox(text: str) -> tuple[int, int, int, int] | None:
|
||||
|
||||
@@ -37,6 +37,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("routes", nargs="+", help="Route ids like 'dongle/logid'.")
|
||||
parser.add_argument("--workspace", type=Path, default=DEFAULT_WORKSPACE, help="Training workspace root.")
|
||||
parser.add_argument("--clip-root", type=Path, default=preferred_clip_root(), help="Downloaded route clip root.")
|
||||
parser.add_argument("--output-dir", type=Path, help="Output directory. Defaults to <workspace>/review/connect_route_bookmarks.")
|
||||
parser.add_argument("--models-dir", type=Path, help="Optional directory containing speed_limit_us_detector.onnx and speed_limit_us_value_classifier.onnx.")
|
||||
parser.add_argument("--lead-in", type=float, default=7.0, help="Seconds before each bookmark to sample into review sheets.")
|
||||
parser.add_argument("--sample-every", type=float, default=0.5, help="Seconds between sampled lead-in frames.")
|
||||
@@ -154,7 +155,7 @@ def main() -> int:
|
||||
args = parse_args()
|
||||
workspace = resolve_workspace(args.workspace)
|
||||
clip_root = args.clip_root.expanduser().resolve()
|
||||
review_root = ensure_dir(workspace / "review" / "connect_route_bookmarks")
|
||||
review_root = ensure_dir(args.output_dir.expanduser().resolve()) if args.output_dir else ensure_dir(workspace / "review" / "connect_route_bookmarks")
|
||||
frame_dir = ensure_dir(review_root / "frames")
|
||||
crop_dir = ensure_dir(review_root / "crops")
|
||||
contact_sheet_dir = ensure_dir(review_root / "contact_sheets")
|
||||
|
||||
@@ -9,7 +9,7 @@ from collections import Counter, defaultdict, deque
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PRIORITY_SPEED_ORDER = (60, 65, 55, 50, 45, 40, 35, 30, 25, 20, 70, 15, 75)
|
||||
PRIORITY_SPEED_ORDER = (60, 65, 55, 50, 45, 40, 35, 30, 25, 20, 70, 75, 80, 90, 100, 15, 10)
|
||||
COMPARISON_PRIORITY_BONUS = {
|
||||
"value_changed": 4.0,
|
||||
"gained_read": 3.0,
|
||||
|
||||
@@ -130,7 +130,7 @@ HTML = r"""<!doctype html>
|
||||
</aside>
|
||||
</main>
|
||||
<script>
|
||||
const speeds = [15,20,25,30,35,40,45,50,55,60,65,70,75];
|
||||
const speeds = [10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,90,100];
|
||||
let rows = [];
|
||||
let index = 0;
|
||||
let current = null;
|
||||
|
||||
@@ -33,6 +33,16 @@ def test_raw_comma_camera_uses_real_frame_rate():
|
||||
assert common.source_video_fps(Path("clip.mp4"), 0.0) == 20.0
|
||||
|
||||
|
||||
def test_extended_classifier_order_matches_lexical_dataset_classes():
|
||||
assert common.SUPPORTED_SPEED_VALUES == (10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90, 100)
|
||||
assert common.EXTENDED_CLASSIFIER_SPEED_VALUES == (10, 100, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("speed", (10, 80, 90, 100))
|
||||
def test_manual_import_accepts_extended_speed_values(speed):
|
||||
assert import_queue.parse_speed(str(speed)) == speed
|
||||
|
||||
|
||||
def test_localized_bookmark_source_position_normalizes_previous_segment():
|
||||
previous = {"segment": "26", "relative_time_s": "-18.950"}
|
||||
current = {"segment": "26", "relative_time_s": "12.500"}
|
||||
|
||||
@@ -346,6 +346,25 @@ class LongControl:
|
||||
target_factor *= 0.75
|
||||
self.pid.i *= target_factor
|
||||
|
||||
def _trim_gm_truck_negative_hold_integrator(self, a_target, error, CS):
|
||||
if not self.is_gm_stock_truck or self.pid.i >= -0.02:
|
||||
return
|
||||
if CS.vEgo < 12.0 or a_target <= -0.85:
|
||||
return
|
||||
if error <= 0.04:
|
||||
return
|
||||
|
||||
authority_mismatch = float(a_target) - float(self.last_output_accel)
|
||||
if authority_mismatch <= 0.10:
|
||||
return
|
||||
|
||||
release = float(interp(
|
||||
max(authority_mismatch, error),
|
||||
[0.10, 0.25, 0.50],
|
||||
[0.0008, 0.0020, 0.0040],
|
||||
))
|
||||
self.pid.i = min(0.0, self.pid.i + release)
|
||||
|
||||
def _apply_pedal_long_brake_bias(self, output_accel, a_target, CS):
|
||||
if not self.is_gm_pedal_long:
|
||||
return output_accel
|
||||
@@ -431,6 +450,7 @@ class LongControl:
|
||||
self._shape_volt_test_tune_integrator(error, CS.vEgo)
|
||||
self._trim_positive_overshoot_integrator(a_target, error, CS)
|
||||
self._trim_gm_truck_positive_hold_integrator(a_target, error, CS)
|
||||
self._trim_gm_truck_negative_hold_integrator(a_target, error, CS)
|
||||
feedforward = self._get_longitudinal_feedforward(a_target, CS.vEgo)
|
||||
freeze_integrator = self._get_pedal_long_freeze(a_target, error, CS.vEgo, accel_limits)
|
||||
raw_output_accel = self.pid.update(error, speed=CS.vEgo, feedforward=feedforward,
|
||||
|
||||
@@ -726,3 +726,92 @@ def test_gm_stock_truck_positive_i_trim_preserves_low_speed_launch():
|
||||
lc._trim_gm_truck_positive_hold_integrator(0.05, 0.05, CS)
|
||||
|
||||
assert lc.pid.i == pytest.approx(0.25, abs=1e-9)
|
||||
|
||||
|
||||
def test_gm_stock_truck_negative_i_unwinds_when_already_overbraking():
|
||||
CP = make_longcontrol_cp(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_SILVERADO,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
)
|
||||
lc = LongControl(CP)
|
||||
lc.pid.i = -0.22
|
||||
lc.last_output_accel = -0.66
|
||||
CS = car.CarState.new_message(vEgo=29.6, aEgo=-0.49, brakePressed=False)
|
||||
|
||||
lc._trim_gm_truck_negative_hold_integrator(-0.44, 0.05, CS)
|
||||
|
||||
assert -0.22 < lc.pid.i < 0.0
|
||||
|
||||
|
||||
def test_gm_stock_truck_negative_i_stays_when_decel_is_not_achieved():
|
||||
CP = make_longcontrol_cp(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_SILVERADO,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
)
|
||||
lc = LongControl(CP)
|
||||
lc.pid.i = -0.22
|
||||
lc.last_output_accel = -0.66
|
||||
CS = car.CarState.new_message(vEgo=29.6, aEgo=0.05, brakePressed=False)
|
||||
|
||||
lc._trim_gm_truck_negative_hold_integrator(-0.44, -0.49, CS)
|
||||
|
||||
assert lc.pid.i == pytest.approx(-0.22, abs=1e-9)
|
||||
|
||||
|
||||
def test_gm_stock_truck_negative_i_stays_for_urgent_braking():
|
||||
CP = make_longcontrol_cp(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_SILVERADO,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
)
|
||||
lc = LongControl(CP)
|
||||
lc.pid.i = -0.22
|
||||
lc.last_output_accel = -1.30
|
||||
CS = car.CarState.new_message(vEgo=29.6, aEgo=-1.20, brakePressed=False)
|
||||
|
||||
lc._trim_gm_truck_negative_hold_integrator(-1.00, 0.20, CS)
|
||||
|
||||
assert lc.pid.i == pytest.approx(-0.22, abs=1e-9)
|
||||
|
||||
|
||||
def test_gm_stock_truck_negative_i_trim_does_not_affect_other_gm_cars():
|
||||
CP = make_longcontrol_cp(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
)
|
||||
lc = LongControl(CP)
|
||||
lc.pid.i = -0.22
|
||||
lc.last_output_accel = -0.66
|
||||
CS = car.CarState.new_message(vEgo=29.6, aEgo=-0.49, brakePressed=False)
|
||||
|
||||
lc._trim_gm_truck_negative_hold_integrator(-0.44, 0.05, CS)
|
||||
|
||||
assert lc.pid.i == pytest.approx(-0.22, abs=1e-9)
|
||||
|
||||
|
||||
def test_gm_stock_truck_update_gradually_releases_stale_brake_integral():
|
||||
CP = make_longcontrol_cp(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_SILVERADO,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
)
|
||||
lc = LongControl(CP)
|
||||
lc.pid.i = -0.22
|
||||
lc.last_output_accel = -0.66
|
||||
CS = car.CarState.new_message(vEgo=29.6, aEgo=-0.49, brakePressed=False)
|
||||
CS.cruiseState.standstill = False
|
||||
|
||||
output_accel = lc.update(
|
||||
active=True,
|
||||
CS=CS,
|
||||
a_target=-0.44,
|
||||
should_stop=False,
|
||||
accel_limits=(-3.5, 2.0),
|
||||
starpilot_toggles=make_toggles(),
|
||||
has_lead=True,
|
||||
)
|
||||
|
||||
assert -0.66 < output_accel < -0.44
|
||||
|
||||
@@ -18,6 +18,9 @@ from openpilot.starpilot.common.favorite_slots import toggle_favorite_slot
|
||||
from openpilot.starpilot.common.starpilot_utilities import is_FrogsGoMoo
|
||||
from openpilot.starpilot.common.starpilot_variables import ERROR_LOGS_PATH, GearShifter, NON_DRIVING_GEARS
|
||||
|
||||
HYUNDAI_MAIN_CRUISE_AOL_CONFIRM_TIMEOUT_FRAMES = 100
|
||||
|
||||
|
||||
class StarPilotCard:
|
||||
@staticmethod
|
||||
def _button_type_raw(button_event) -> int:
|
||||
@@ -40,6 +43,9 @@ class StarPilotCard:
|
||||
self.hyundai_preserve_aol_across_reverse = getattr(self.CP, "carFingerprint", None) == HYUNDAI_CAR.HYUNDAI_SONATA_HYBRID
|
||||
self.hyundai_aol_needs_engagement = self.CP.brand == "hyundai" and not (hyundai_flags & HyundaiFlags.CANFD) and not kia_forte_non_scc
|
||||
self.hyundai_aol_ready = False
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
self.prev_cruise_available = None
|
||||
self.prev_active = False
|
||||
self.prev_cruise_enabled = False
|
||||
self.decel_pressed = False
|
||||
@@ -116,6 +122,11 @@ class StarPilotCard:
|
||||
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)
|
||||
hyundai_main_cruise_aol_managed = self.CP.brand == "hyundai" and starpilot_toggles.main_cruise_aol_toggle
|
||||
|
||||
if carState.gearShifter in NON_DRIVING_GEARS or not hyundai_main_cruise_aol_managed:
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
|
||||
if self.hyundai_aol_needs_engagement:
|
||||
if carState.gearShifter in NON_DRIVING_GEARS:
|
||||
@@ -129,6 +140,8 @@ class StarPilotCard:
|
||||
if button_aol_supported:
|
||||
for be, be_type in zip(carState.buttonEvents, button_event_types, strict=False):
|
||||
if be_type == ButtonType.lkas and be.pressed and starpilot_toggles.always_on_lateral_lkas:
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
if self.hyundai_aol_needs_engagement:
|
||||
self.hyundai_aol_ready = True
|
||||
self.always_on_lateral_allowed = not self.always_on_lateral_allowed
|
||||
@@ -138,9 +151,27 @@ class StarPilotCard:
|
||||
if starpilot_toggles.main_cruise_aol_toggle:
|
||||
if self.hyundai_aol_needs_engagement:
|
||||
self.hyundai_aol_ready = True
|
||||
self.always_on_lateral_allowed = not self.always_on_lateral_allowed
|
||||
if hyundai_main_cruise_aol_managed:
|
||||
# Panda permits Hyundai main-button AOL only after the vehicle reports main on.
|
||||
self.main_cruise_aol_pending = True
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
else:
|
||||
self.always_on_lateral_allowed = not self.always_on_lateral_allowed
|
||||
elif starpilot_toggles.main_cruise_slc_adopt and starpilot_toggles.speed_limit_controller:
|
||||
self.params_memory.put_bool("SLCAdoptSpeedLimit", True)
|
||||
|
||||
cruise_available_changed = self.prev_cruise_available is not None and carState.cruiseState.available != self.prev_cruise_available
|
||||
if self.main_cruise_aol_pending:
|
||||
if cruise_available_changed:
|
||||
self.always_on_lateral_allowed = carState.cruiseState.available
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
else:
|
||||
self.main_cruise_aol_pending_frames += 1
|
||||
if self.main_cruise_aol_pending_frames >= HYUNDAI_MAIN_CRUISE_AOL_CONFIRM_TIMEOUT_FRAMES:
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
|
||||
if starpilot_toggles.always_on_lateral_main and not button_managed_aol:
|
||||
car_fingerprint = getattr(self.CP, "carFingerprint", None)
|
||||
pcm_cruise = getattr(self.CP, "pcmCruise", False)
|
||||
@@ -157,11 +188,14 @@ class StarPilotCard:
|
||||
# On rising edge of engagement (SET press enabling lat+long), auto-enable AOL
|
||||
# so that lateral persists when braking disengages longitudinal
|
||||
if sm["selfdriveState"].active and not self.prev_active and self.always_on_lateral_set and starpilot_toggles.always_on_lateral_lkas:
|
||||
self.main_cruise_aol_pending = False
|
||||
self.main_cruise_aol_pending_frames = 0
|
||||
if self.hyundai_aol_needs_engagement:
|
||||
self.hyundai_aol_ready = True
|
||||
self.always_on_lateral_allowed = True
|
||||
|
||||
self.prev_active = sm["selfdriveState"].active
|
||||
self.prev_cruise_available = carState.cruiseState.available
|
||||
self.prev_cruise_enabled = carState.cruiseState.enabled
|
||||
|
||||
self.always_on_lateral_enabled = self.always_on_lateral_allowed and self.always_on_lateral_set
|
||||
|
||||
@@ -278,7 +278,7 @@ def test_hyundai_canfd_lkas_button_wrapped_enum_can_toggle_aol(monkeypatch, tmp_
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
|
||||
|
||||
def test_kia_forte_non_scc_main_cruise_button_can_toggle_aol_before_engagement(monkeypatch, tmp_path):
|
||||
def test_kia_forte_non_scc_main_cruise_button_waits_for_main_confirmation(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
|
||||
@@ -292,18 +292,24 @@ def test_kia_forte_non_scc_main_cruise_button_can_toggle_aol_before_engagement(m
|
||||
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
|
||||
)
|
||||
|
||||
car_state = make_car_state(available=True, button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
car_state = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False)
|
||||
sm = make_sm()
|
||||
toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True)
|
||||
|
||||
card.update(make_car_state(), starpilot_car_state, sm, toggles)
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
|
||||
car_state.buttonEvents = []
|
||||
car_state.cruiseState.available = True
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
assert ret.alwaysOnLateralEnabled is True
|
||||
|
||||
|
||||
def test_hyundai_main_cruise_button_can_start_aol_before_normal_engagement(monkeypatch, tmp_path):
|
||||
def test_hyundai_main_cruise_button_follows_confirmed_main_state(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
|
||||
@@ -313,17 +319,29 @@ def test_hyundai_main_cruise_button_can_start_aol_before_normal_engagement(monke
|
||||
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
|
||||
)
|
||||
|
||||
car_state = make_car_state(available=True, button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
car_state = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False)
|
||||
sm = make_sm()
|
||||
toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True)
|
||||
|
||||
card.update(make_car_state(), starpilot_car_state, sm, toggles)
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
|
||||
car_state.buttonEvents = []
|
||||
car_state.cruiseState.available = True
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
assert ret.alwaysOnLateralEnabled is True
|
||||
|
||||
car_state.buttonEvents = [SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)]
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
|
||||
car_state.buttonEvents = []
|
||||
car_state.cruiseState.available = False
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
|
||||
|
||||
@@ -337,21 +355,78 @@ def test_hyundai_main_cruise_button_wrapped_enum_can_toggle_aol(monkeypatch, tmp
|
||||
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
|
||||
)
|
||||
|
||||
car_state = make_car_state(available=True, button_events=[make_wrapped_button_event(spc.ButtonType.mainCruise, True)])
|
||||
car_state = make_car_state()
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False)
|
||||
sm = make_sm()
|
||||
toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True)
|
||||
|
||||
card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
car_state.buttonEvents = [make_wrapped_button_event(spc.ButtonType.mainCruise, True)]
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
|
||||
car_state.buttonEvents = []
|
||||
car_state.cruiseState.available = True
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
assert ret.alwaysOnLateralEnabled is True
|
||||
|
||||
car_state.buttonEvents = [make_wrapped_button_event(spc.ButtonType.mainCruise, True)]
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
assert ret.alwaysOnLateralEnabled is True
|
||||
|
||||
car_state.buttonEvents = []
|
||||
car_state.cruiseState.available = False
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
|
||||
|
||||
def test_hyundai_main_cruise_aol_ignores_unconfirmed_button_press(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="hyundai"),
|
||||
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
|
||||
)
|
||||
car_state = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
starpilot_car_state = SimpleNamespace(distancePressed=False)
|
||||
sm = make_sm()
|
||||
toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True)
|
||||
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
car_state.buttonEvents = []
|
||||
for _ in range(spc.HYUNDAI_MAIN_CRUISE_AOL_CONFIRM_TIMEOUT_FRAMES):
|
||||
ret = card.update(car_state, starpilot_car_state, sm, toggles)
|
||||
|
||||
assert ret.alwaysOnLateralAllowed is False
|
||||
assert ret.alwaysOnLateralEnabled is False
|
||||
assert card.main_cruise_aol_pending is False
|
||||
|
||||
|
||||
def test_honda_mapped_main_cruise_button_keeps_immediate_toggle(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="honda"),
|
||||
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
|
||||
)
|
||||
car_state = make_car_state(button_events=[SimpleNamespace(type=spc.ButtonType.mainCruise, pressed=True)])
|
||||
toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True, lkas_allowed_for_aol=True)
|
||||
|
||||
ret = card.update(car_state, SimpleNamespace(distancePressed=False), make_sm(), toggles)
|
||||
|
||||
assert ret.alwaysOnLateralAllowed is True
|
||||
assert ret.alwaysOnLateralEnabled is True
|
||||
assert card.main_cruise_aol_pending is False
|
||||
|
||||
|
||||
def test_hyundai_main_cruise_button_adopts_slc_when_assigned_to_slc(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(spc, "Params", FakeParams)
|
||||
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
|
||||
|
||||
Reference in New Issue
Block a user