This commit is contained in:
firestar5683
2026-07-02 10:10:45 -05:00
parent c7a5c880cc
commit 6c392185be
5 changed files with 69 additions and 18 deletions
@@ -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,
@@ -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
+5 -4
View File
@@ -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),
+36 -6
View File
@@ -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),
+13 -1
View File
@@ -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", {