From 6c392185be00ef34d4feb191f8e3f9e30568fc08 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 2 Jul 2026 10:10:45 -0500 Subject: [PATCH] oopsie --- .../build_manual_review_queue.py | 18 +++++--- .../controls/lib/latcontrol_vehicle_tunes.py | 4 +- system/manager/process.py | 9 ++-- system/manager/process_config.py | 42 ++++++++++++++++--- system/manager/test/test_manager.py | 14 ++++++- 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/scripts/speed_limit_vision/build_manual_review_queue.py b/scripts/speed_limit_vision/build_manual_review_queue.py index 6df57b117..30ca813fe 100644 --- a/scripts/speed_limit_vision/build_manual_review_queue.py +++ b/scripts/speed_limit_vision/build_manual_review_queue.py @@ -120,6 +120,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--transition-step", type=float, default=0.75, help="Seconds between transition-window samples.") parser.add_argument("--max-frames-per-route", type=int, default=1200, help="Maximum frames to score per route.") parser.add_argument("--max-candidates-per-route", type=int, default=500, help="Maximum review candidates to keep per route.") + parser.add_argument("--max-candidates-per-frame", type=int, default=1, help="Maximum detector candidates to keep from a single video frame. 0 keeps all.") parser.add_argument("--max-negatives-per-route", type=int, default=60, help="Maximum empty/no-candidate frames to keep per route.") parser.add_argument("--min-proposal-confidence", type=float, default=0.025, help="Loose detector confidence floor for review candidates.") parser.add_argument("--no-read-min-proposal-confidence", type=float, default=0.12, help="Keep no-value detector boxes above this confidence.") @@ -375,8 +376,7 @@ def cluster_key(route_id: str, segment: int, time_s: float, frame_shape: tuple[i time_bucket = int(math.floor(time_s / max(dedupe_seconds, 0.1))) grid_x = int(center_x * 12) grid_y = int(center_y * 8) - value = candidate["candidate_speed_limit_mph"] or "none" - return f"{route_id}|{segment}|{time_bucket}|{candidate['class_id']}|{value}|{grid_x}|{grid_y}" + return f"{route_id}|{segment}|{time_bucket}|{candidate['class_id']}|{grid_x}|{grid_y}" def candidate_record_key(route_key: str, segment: int, time_s: float, index: int) -> str: @@ -423,17 +423,25 @@ def mine_route(route_id: str, daemon: slv.SpeedLimitVisionDaemon, args: argparse full_detection = daemon._detect_sign(frame_bgr) if args.include_full_detection else None proposals = daemon._collect_detector_classifier_proposals(frame_bgr) candidate_index = 0 - kept_any = False + frame_candidates: list[tuple[int, dict[str, object], object]] = [] for proposal in proposals: candidate = analyze_proposal(daemon, frame_bgr, proposal, full_detection, context, args) if candidate is None: continue candidate_index += 1 + x1, y1, x2, y2 = candidate["crop_bbox"] + crop = frame_bgr[y1:y2, x1:x2] + frame_candidates.append((candidate_index, candidate, crop)) + + frame_candidates.sort(key=lambda item: float(item[1]["review_priority"]), reverse=True) + if args.max_candidates_per_frame > 0: + frame_candidates = frame_candidates[:args.max_candidates_per_frame] + kept_any = bool(frame_candidates) + + for candidate_index, candidate, crop in frame_candidates: record_key = candidate_record_key(route_key, segment.segment, time_s, candidate_index) frame_path = frame_dir / f"{record_key}.jpg" crop_path = crop_dir / f"{record_key}_crop.jpg" - x1, y1, x2, y2 = candidate["crop_bbox"] - crop = frame_bgr[y1:y2, x1:x2] row = { "record_key": record_key, "route": route_id, diff --git a/selfdrive/controls/lib/latcontrol_vehicle_tunes.py b/selfdrive/controls/lib/latcontrol_vehicle_tunes.py index b51ab0450..630ae35fe 100644 --- a/selfdrive/controls/lib/latcontrol_vehicle_tunes.py +++ b/selfdrive/controls/lib/latcontrol_vehicle_tunes.py @@ -560,8 +560,8 @@ IONIQ_6_HIGH_SPEED_RIGHT_TURN_IN_FF_LAT_WIDTH = 0.035 IONIQ_6_LOW_SPEED_PID_RESET_SPEED = 0.1 * CV.MPH_TO_MS IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_START = 0.90 IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_WIDTH = 0.18 -IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_LEFT = 0.06 -IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_RIGHT = 0.17 +IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_LEFT = 0.03 +IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_RIGHT = 0.11 IONIQ_6_HEAVY_DIRECTIONAL_TAPER_UNWIND_LEFT = 0.78 IONIQ_6_HEAVY_DIRECTIONAL_TAPER_UNWIND_RIGHT = 1.10 IONIQ_6_OUTPUT_TAPER_SPEED = 8.5 diff --git a/system/manager/process.py b/system/manager/process.py index aadf38f82..a85355fb5 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -24,7 +24,6 @@ from openpilot.common.swaglog import cloudlog from openpilot.common.watchdog import WATCHDOG_FN ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None -PYTHON_PROCESS_START_METHOD = os.getenv("PYTHON_PROCESS_START_METHOD", "subprocess") DEBUG_ENV_KEYS = ( "XDG_RUNTIME_DIR", @@ -644,7 +643,7 @@ class NativeProcess(ManagerProcess): class PythonProcess(ManagerProcess): - def __init__(self, name, module, should_run, enabled=True, sigkill=False, watchdog_max_dt=None, nice=None): + def __init__(self, name, module, should_run, enabled=True, sigkill=False, watchdog_max_dt=None, nice=None, start_method=None): self.name = name self.module = module self.should_run = should_run @@ -652,6 +651,7 @@ class PythonProcess(ManagerProcess): self.sigkill = sigkill self.watchdog_max_dt = watchdog_max_dt self.nice = nice + self.start_method = start_method self.launcher = launcher def prepare(self) -> None: @@ -682,14 +682,15 @@ class PythonProcess(ManagerProcess): name = self.name if "modeld" not in self.name else "MainProcess" cloudlog.info(f"starting python {self.module}") - if PYTHON_PROCESS_START_METHOD == "subprocess": + start_method = self.start_method or os.getenv("PYTHON_PROCESS_START_METHOD", "fork") + if start_method == "subprocess": launcher_code = ( "from openpilot.system.manager.process import launcher; " f"launcher({self.module!r}, {self.name!r}, {self.nice!r})" ) self.proc = SubprocessProcess(subprocess.Popen([sys.executable, "-c", launcher_code])) else: - self.proc = multiprocessing.get_context(PYTHON_PROCESS_START_METHOD).Process( + self.proc = multiprocessing.get_context(start_method).Process( name=name, target=self.launcher, args=(self.module, self.name, self.nice), diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 6f6bee01b..cfce04dc6 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -7,10 +7,42 @@ from types import SimpleNamespace from cereal import car from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE, PC, TICI -from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess WEBCAM = os.getenv("USE_WEBCAM") is not None UI_WATCHDOG_MAX_DT = int(os.getenv("UI_WATCHDOG_MAX_DT", "10")) +device_type = HARDWARE.get_device_type() + + +def _env_bool(name: str) -> bool | None: + value = os.getenv(name) + if value is None: + return None + return value.strip().lower() in ("1", "true", "yes", "on") + + +def python_ui_enabled(device_type: str) -> bool: + for env_name in ("USE_PYTHON_UI", "USE_RAYLIB_UI"): + enabled = _env_bool(env_name) + if enabled is not None: + return enabled + + native_ui = _env_bool("USE_NATIVE_UI") + if native_ui is not None: + return not native_ui + + return device_type not in ("tici", "tizi") + + +def python_process_start_method(uses_python_ui: bool, is_pc: bool = PC) -> str: + # Native/QT UI devices rely on fork copy-on-write to keep onroad memory low. + # Python/raylib UI uses subprocess to avoid fork/import-lock boot hangs. + return "fork" if is_pc or not uses_python_ui else "subprocess" + + +PYTHON_UI = python_ui_enabled(device_type) +os.environ.setdefault("PYTHON_PROCESS_START_METHOD", python_process_start_method(PYTHON_UI)) + +from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess def driverview(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool: return started or params.get_bool("IsDriverViewEnabled") @@ -143,12 +175,10 @@ procs += [ PythonProcess("galaxy", "starpilot.system.galaxy.galaxy", always_run, nice=19), ] -device_type = HARDWARE.get_device_type() -if device_type in ("tici", "tizi"): - procs.append(NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=UI_WATCHDOG_MAX_DT)) -else: - # C4 (mici) runs the Python raylib UI path. +if PYTHON_UI: procs.append(PythonProcess("ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=UI_WATCHDOG_MAX_DT)) +else: + procs.append(NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=UI_WATCHDOG_MAX_DT)) procs += [ PythonProcess("device_syncd", "starpilot.system.device_syncd", always_run), diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 7b908807b..375bc4611 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -9,7 +9,7 @@ from cereal import car from openpilot.common.params import Params, ParamKeyFlag import openpilot.system.manager.manager as manager from openpilot.system.manager.process import ensure_running -from openpilot.system.manager.process_config import managed_processes, procs +from openpilot.system.manager.process_config import managed_processes, procs, python_process_start_method, python_ui_enabled from openpilot.system.hardware import HARDWARE os.environ['FAKEUPLOAD'] = "1" @@ -111,6 +111,18 @@ class TestManager: assert names.index("the_galaxy") < ui_idx assert names.index("galaxy") < ui_idx + def test_python_process_start_method_follows_ui_implementation(self): + assert python_process_start_method(False, False) == "fork" + assert python_process_start_method(True, False) == "subprocess" + assert python_process_start_method(True, True) == "fork" + + def test_python_ui_env_override(self, monkeypatch): + monkeypatch.setenv("USE_RAYLIB_UI", "1") + assert python_ui_enabled("tici") is True + + monkeypatch.setenv("USE_RAYLIB_UI", "0") + assert python_ui_enabled("mici") is False + def test_manager_startup_toggles_use_params_only(self, tmp_path, monkeypatch): monkeypatch.setenv("GIT_BRANCH", "StarPilot") params = FileBackedFakeParams(tmp_path / "params", {