SLC
This commit is contained in:
@@ -193,8 +193,18 @@ BOOKMARK_MANIFEST_FIELDS = [
|
||||
"candidate_confidence",
|
||||
"speed_limit_mph",
|
||||
"confidence",
|
||||
"source_confidence",
|
||||
"source_event",
|
||||
"published_speed_limit_mph",
|
||||
"published_confidence",
|
||||
"map_source",
|
||||
"map_current_speed_limit_mph",
|
||||
"map_next_speed_limit_mph",
|
||||
"map_next_speed_limit_distance_m",
|
||||
"map_expected_speed_limit_mph",
|
||||
"map_relation",
|
||||
"previous_map_speed_limit_mph",
|
||||
"review_bucket",
|
||||
"bookmark_count",
|
||||
"snapshot_path",
|
||||
"source_session_path",
|
||||
|
||||
@@ -41,7 +41,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--latest", type=int, default=1, help="How many latest sessions to import when no session ids are provided.")
|
||||
parser.add_argument("--mode", choices=("symlink", "copy"), default="symlink", help="How to place snapshots into the workspace review/images directory.")
|
||||
parser.add_argument("--force", action="store_true", help="Overwrite snapshot links/files if they already exist.")
|
||||
parser.add_argument("--events", nargs="+", default=["bookmark", "auto_bookmark", "training_candidate", "publish", "candidate"], help="Event types to include in the manifest.")
|
||||
parser.add_argument("--events", nargs="+", default=["bookmark", "auto_bookmark", "training_candidate", "map_transition_miss", "publish", "candidate"], help="Event types to include in the manifest.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -106,8 +106,18 @@ def event_row(event: dict, session_id: str, session_path: Path, event_index: int
|
||||
"candidate_confidence": str(event.get("candidateConfidence") or ""),
|
||||
"speed_limit_mph": str(event.get("speedLimitMph") or ""),
|
||||
"confidence": str(event.get("confidence") or ""),
|
||||
"source_confidence": str(event.get("sourceConfidence") or ""),
|
||||
"source_event": str(event.get("sourceEvent") or ""),
|
||||
"published_speed_limit_mph": str(event.get("publishedSpeedLimitMph") or ""),
|
||||
"published_confidence": str(event.get("publishedConfidence") or ""),
|
||||
"map_source": str(event.get("mapSource") or ""),
|
||||
"map_current_speed_limit_mph": str(event.get("mapCurrentSpeedLimitMph") or ""),
|
||||
"map_next_speed_limit_mph": str(event.get("mapNextSpeedLimitMph") or ""),
|
||||
"map_next_speed_limit_distance_m": str(event.get("mapNextSpeedLimitDistanceM") or ""),
|
||||
"map_expected_speed_limit_mph": str(event.get("mapExpectedSpeedLimitMph") or ""),
|
||||
"map_relation": str(event.get("mapRelation") or ""),
|
||||
"previous_map_speed_limit_mph": str(event.get("previousMapSpeedLimitMph") or ""),
|
||||
"review_bucket": str(event.get("reviewBucket") or ""),
|
||||
"bookmark_count": str(event.get("bookmarkCount") or ""),
|
||||
"snapshot_path": snapshot_path,
|
||||
"source_session_path": str(session_path),
|
||||
|
||||
@@ -118,6 +118,8 @@ class HudRenderer(Widget):
|
||||
self._engaged: bool = False
|
||||
self._show_speed_limit: bool = False
|
||||
self._speed_limit: float = 0.0
|
||||
self._speed_limit_offset: float = 0.0
|
||||
self._show_speed_limit_offset: bool = False
|
||||
self._speed_limit_overridden: bool = False
|
||||
self._pending_speed_limit: float = 0.0
|
||||
self._prompt_visible: bool = False
|
||||
@@ -194,6 +196,7 @@ class HudRenderer(Widget):
|
||||
if self._show_speed_limit:
|
||||
dashboard_speed_limit = sm["starpilotCarState"].dashboardSpeedLimit if sm.valid.get("starpilotCarState", False) else 0.0
|
||||
vision_speed_limit = ui_state.params_memory.get_float("VisionSpeedLimit") if ui_state.params.get_bool("VisionSpeedLimitDetection") else 0.0
|
||||
self._show_speed_limit_offset = ui_state.params.get_bool("ShowSLCOffset")
|
||||
primary_priority = ui_state.params.get("SLCPriority1", encoding='utf-8') or "Map Data"
|
||||
secondary_priority = ui_state.params.get("SLCPriority2", encoding='utf-8') or "None"
|
||||
source_limits = {
|
||||
@@ -210,16 +213,23 @@ class HudRenderer(Widget):
|
||||
secondary_priority=secondary_priority,
|
||||
)
|
||||
self._speed_limit = max(0.0, resolved_speed_limit * speed_conversion)
|
||||
self._speed_limit_offset = starpilot_plan.slcSpeedLimitOffset * speed_conversion
|
||||
self._speed_limit_overridden = bool(starpilot_plan.slcOverriddenSpeed > 0 and starpilot_plan.slcSpeedLimit > 0)
|
||||
if self._speed_limit > 0 and not self._speed_limit_overridden and not self._show_speed_limit_offset:
|
||||
self._speed_limit += self._speed_limit_offset
|
||||
self._pending_speed_limit = max(0.0, starpilot_plan.unconfirmedSlcSpeedLimit * speed_conversion)
|
||||
else:
|
||||
self._speed_limit = 0.0
|
||||
self._speed_limit_offset = 0.0
|
||||
self._show_speed_limit_offset = False
|
||||
self._speed_limit_overridden = False
|
||||
self._pending_speed_limit = 0.0
|
||||
self._prompt_visible = self._pending_speed_limit > 0
|
||||
else:
|
||||
self._show_speed_limit = False
|
||||
self._speed_limit = 0.0
|
||||
self._speed_limit_offset = 0.0
|
||||
self._show_speed_limit_offset = False
|
||||
self._speed_limit_overridden = False
|
||||
self._pending_speed_limit = 0.0
|
||||
self._prompt_visible = False
|
||||
@@ -335,6 +345,64 @@ class HudRenderer(Widget):
|
||||
max_color,
|
||||
)
|
||||
|
||||
def _draw_us_speed_limit_sign(
|
||||
self,
|
||||
sign_rect: rl.Rectangle,
|
||||
speed_text: str,
|
||||
sign_alpha: int,
|
||||
*,
|
||||
border_thickness: float,
|
||||
header_font_size: int,
|
||||
header_gap: int,
|
||||
speed_font_size: int,
|
||||
header_top: float,
|
||||
speed_top: float,
|
||||
footer_text: str = "",
|
||||
footer_font_size: int = 0,
|
||||
footer_top: float = 0.0,
|
||||
) -> None:
|
||||
border_color = rl.Color(255, 255, 255, sign_alpha)
|
||||
text_color = rl.Color(255, 255, 255, sign_alpha)
|
||||
|
||||
inner_border_rect = rl.Rectangle(
|
||||
sign_rect.x + 8,
|
||||
sign_rect.y + 8,
|
||||
sign_rect.width - 16,
|
||||
sign_rect.height - 16,
|
||||
)
|
||||
rl.draw_rectangle_rounded_lines_ex(inner_border_rect, 0.14, 16, max(border_thickness - 2, 1), border_color)
|
||||
|
||||
speed_label = tr("SPEED")
|
||||
limit_label = tr("LIMIT")
|
||||
speed_label_size = measure_text_cached(self._font_semi_bold, speed_label, header_font_size)
|
||||
limit_label_size = measure_text_cached(self._font_semi_bold, limit_label, header_font_size)
|
||||
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold,
|
||||
speed_label,
|
||||
rl.Vector2(sign_rect.x + sign_rect.width / 2 - speed_label_size.x / 2, sign_rect.y + header_top),
|
||||
header_font_size,
|
||||
0,
|
||||
text_color,
|
||||
)
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold,
|
||||
limit_label,
|
||||
rl.Vector2(sign_rect.x + sign_rect.width / 2 - limit_label_size.x / 2, sign_rect.y + header_top + header_gap),
|
||||
header_font_size,
|
||||
0,
|
||||
text_color,
|
||||
)
|
||||
|
||||
speed_text_size = measure_text_cached(self._font_bold, speed_text, speed_font_size)
|
||||
speed_text_pos = rl.Vector2(sign_rect.x + sign_rect.width / 2 - speed_text_size.x / 2, sign_rect.y + speed_top)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, speed_text_pos, speed_font_size, 0, text_color)
|
||||
|
||||
if footer_text and footer_font_size > 0:
|
||||
footer_size = measure_text_cached(self._font_semi_bold, footer_text, footer_font_size)
|
||||
footer_pos = rl.Vector2(sign_rect.x + sign_rect.width / 2 - footer_size.x / 2, sign_rect.y + footer_top)
|
||||
rl.draw_text_ex(self._font_semi_bold, footer_text, footer_pos, footer_font_size, 0, text_color)
|
||||
|
||||
def _draw_speed_limit(self, rect: rl.Rectangle) -> None:
|
||||
if not self._show_speed_limit:
|
||||
return
|
||||
@@ -352,6 +420,10 @@ class HudRenderer(Widget):
|
||||
sign_y = rect.y + (28 if use_vienna_speed_limit else 20)
|
||||
|
||||
speed_text = str(round(display_speed))
|
||||
offset_text = ""
|
||||
if self._show_speed_limit_offset and not self._speed_limit_overridden:
|
||||
rounded_offset = round(self._speed_limit_offset)
|
||||
offset_text = "–" if rounded_offset == 0 else f"{rounded_offset:+d}"
|
||||
if use_vienna_speed_limit:
|
||||
center_x = sign_x + sign_width / 2
|
||||
center_y = sign_y + sign_height / 2
|
||||
@@ -370,30 +442,29 @@ class HudRenderer(Widget):
|
||||
|
||||
font_size = 58 if len(speed_text) <= 2 else 48
|
||||
text_size = measure_text_cached(self._font_bold, speed_text, font_size)
|
||||
text_pos = rl.Vector2(center_x - text_size.x / 2, center_y - text_size.y / 2 + 4)
|
||||
text_pos = rl.Vector2(center_x - text_size.x / 2, center_y - text_size.y / 2 + (-14 if offset_text else 4))
|
||||
rl.draw_text_ex(self._font_bold, speed_text, text_pos, font_size, 0, rl.Color(0, 0, 0, sign_alpha))
|
||||
if offset_text:
|
||||
offset_font_size = 34
|
||||
offset_size = measure_text_cached(self._font_semi_bold, offset_text, offset_font_size)
|
||||
offset_pos = rl.Vector2(center_x - offset_size.x / 2, sign_y + sign_height - 42)
|
||||
rl.draw_text_ex(self._font_semi_bold, offset_text, offset_pos, offset_font_size, 0, rl.Color(0, 0, 0, sign_alpha))
|
||||
else:
|
||||
sign_rect = rl.Rectangle(sign_x, sign_y, sign_width, sign_height)
|
||||
border_rect = rl.Rectangle(sign_x + 6, sign_y + 6, sign_width - 12, sign_height - 12)
|
||||
rl.draw_rectangle_rounded(sign_rect, 0.18, 16, rl.Color(255, 255, 255, sign_alpha))
|
||||
rl.draw_rectangle_rounded_lines_ex(border_rect, 0.14, 16, 4, rl.Color(0, 0, 0, sign_alpha))
|
||||
|
||||
header_font_size = 20
|
||||
header_gap = 18
|
||||
speed_font_size = 50 if len(speed_text) <= 2 else 42
|
||||
|
||||
speed_label = tr("SPEED")
|
||||
limit_label = tr("LIMIT")
|
||||
speed_label_size = measure_text_cached(self._font_semi_bold, speed_label, header_font_size)
|
||||
limit_label_size = measure_text_cached(self._font_semi_bold, limit_label, header_font_size)
|
||||
speed_label_pos = rl.Vector2(sign_x + sign_width / 2 - speed_label_size.x / 2, sign_y + 18)
|
||||
limit_label_pos = rl.Vector2(sign_x + sign_width / 2 - limit_label_size.x / 2, sign_y + 18 + header_gap)
|
||||
rl.draw_text_ex(self._font_semi_bold, speed_label, speed_label_pos, header_font_size, 0, rl.Color(0, 0, 0, sign_alpha))
|
||||
rl.draw_text_ex(self._font_semi_bold, limit_label, limit_label_pos, header_font_size, 0, rl.Color(0, 0, 0, sign_alpha))
|
||||
|
||||
speed_text_size = measure_text_cached(self._font_bold, speed_text, speed_font_size)
|
||||
speed_text_pos = rl.Vector2(sign_x + sign_width / 2 - speed_text_size.x / 2, sign_y + 66)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, speed_text_pos, speed_font_size, 0, rl.Color(0, 0, 0, sign_alpha))
|
||||
self._draw_us_speed_limit_sign(
|
||||
sign_rect,
|
||||
speed_text,
|
||||
sign_alpha,
|
||||
border_thickness=4,
|
||||
header_font_size=20,
|
||||
header_gap=18,
|
||||
speed_font_size=44 if offset_text else (50 if len(speed_text) <= 2 else 42),
|
||||
header_top=18,
|
||||
speed_top=58 if offset_text else 66,
|
||||
footer_text=offset_text,
|
||||
footer_font_size=28 if offset_text else 0,
|
||||
footer_top=100,
|
||||
)
|
||||
|
||||
def _update_prompt_layout(self, rect: rl.Rectangle) -> None:
|
||||
if not self._prompt_visible:
|
||||
@@ -484,38 +555,17 @@ class HudRenderer(Widget):
|
||||
text_pos = rl.Vector2(center_x - text_size.x / 2, center_y - text_size.y / 2 + 4)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, text_pos, font_size, 0, rl.BLACK)
|
||||
else:
|
||||
border_rect = rl.Rectangle(sign_rect.x + 8, sign_rect.y + 8, sign_rect.width - 16, sign_rect.height - 16)
|
||||
rl.draw_rectangle_rounded(sign_rect, 0.18, 16, rl.WHITE)
|
||||
rl.draw_rectangle_rounded_lines_ex(border_rect, 0.14, 16, 5, rl.BLACK)
|
||||
|
||||
header_font_size = 24
|
||||
header_gap = 20
|
||||
speed_font_size = 72 if len(speed_text) <= 2 else 60
|
||||
|
||||
speed_label = tr("SPEED")
|
||||
limit_label = tr("LIMIT")
|
||||
speed_label_size = measure_text_cached(self._font_semi_bold, speed_label, header_font_size)
|
||||
limit_label_size = measure_text_cached(self._font_semi_bold, limit_label, header_font_size)
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold,
|
||||
speed_label,
|
||||
rl.Vector2(sign_rect.x + sign_rect.width / 2 - speed_label_size.x / 2, sign_rect.y + 20),
|
||||
header_font_size,
|
||||
0,
|
||||
rl.BLACK,
|
||||
self._draw_us_speed_limit_sign(
|
||||
sign_rect,
|
||||
speed_text,
|
||||
255,
|
||||
border_thickness=5,
|
||||
header_font_size=24,
|
||||
header_gap=20,
|
||||
speed_font_size=72 if len(speed_text) <= 2 else 60,
|
||||
header_top=20,
|
||||
speed_top=80,
|
||||
)
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold,
|
||||
limit_label,
|
||||
rl.Vector2(sign_rect.x + sign_rect.width / 2 - limit_label_size.x / 2, sign_rect.y + 20 + header_gap),
|
||||
header_font_size,
|
||||
0,
|
||||
rl.BLACK,
|
||||
)
|
||||
|
||||
speed_size = measure_text_cached(self._font_bold, speed_text, speed_font_size)
|
||||
speed_pos = rl.Vector2(sign_rect.x + sign_rect.width / 2 - speed_size.x / 2, sign_rect.y + 80)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, speed_pos, speed_font_size, 0, rl.BLACK)
|
||||
|
||||
self._draw_prompt_button(
|
||||
self._prompt_deny_rect,
|
||||
@@ -532,7 +582,7 @@ class HudRenderer(Widget):
|
||||
False,
|
||||
)
|
||||
|
||||
hint_text = tr("USE WHEEL - / +")
|
||||
hint_text = tr("SET/+ TO CONFIRM RES/- TO DENY")
|
||||
hint_size = measure_text_cached(self._font_medium, hint_text, 24)
|
||||
hint_pos = rl.Vector2(card_rect.x + card_rect.width / 2 - hint_size.x / 2, card_rect.y + card_rect.height - 34)
|
||||
rl.draw_text_ex(self._font_medium, hint_text, hint_pos, 24, 0, rl.Color(255, 255, 255, 180))
|
||||
|
||||
@@ -34,6 +34,9 @@ AUTO_BOOKMARK_MIN_CONFIDENCE = 0.62
|
||||
TRAINING_COLLECTOR_CONFIRM_DELAY_SECONDS = 0.7
|
||||
TRAINING_COLLECTOR_COOLDOWN_SECONDS = 2.5
|
||||
TRAINING_COLLECTOR_MIN_CONFIDENCE = 0.40
|
||||
MAP_NEXT_REVIEW_DISTANCE_METERS = 120.0
|
||||
MAP_TRANSITION_MISS_CAPTURE_COOLDOWN_SECONDS = 8.0
|
||||
MAP_VISION_MATCH_WINDOW_SECONDS = 2.5
|
||||
MODEL_PROPOSAL_MIN_CONFIDENCE = 0.0001
|
||||
MODEL_PROPOSAL_MAX_COUNT = 16
|
||||
MODEL_PROPOSAL_MAX_AREA_RATIO = 0.18
|
||||
@@ -229,6 +232,9 @@ class SpeedLimitVisionDaemon:
|
||||
self.last_training_capture_at = 0.0
|
||||
self.last_training_capture_speed_limit_mph = 0
|
||||
self.pending_training_capture = None
|
||||
self.last_map_speed_limit_mph = 0
|
||||
self.last_map_transition_miss_at = 0.0
|
||||
self.last_map_transition_miss_speed_limit_mph = 0
|
||||
self.ignore_next_user_bookmark = False
|
||||
self.current_frame_bgr = None
|
||||
|
||||
@@ -286,6 +292,120 @@ class SpeedLimitVisionDaemon:
|
||||
self.last_logged_status = ""
|
||||
self.last_logged_candidate = None
|
||||
|
||||
def _read_next_map_speed_limit(self):
|
||||
if self.params_memory is None:
|
||||
return {}
|
||||
|
||||
next_map_speed_limit = self.params_memory.get("NextMapSpeedLimit") or {}
|
||||
if isinstance(next_map_speed_limit, (bytes, str)):
|
||||
try:
|
||||
next_map_speed_limit = json.loads(next_map_speed_limit)
|
||||
except Exception:
|
||||
next_map_speed_limit = {}
|
||||
return next_map_speed_limit if isinstance(next_map_speed_limit, dict) else {}
|
||||
|
||||
def _get_map_context(self):
|
||||
current_limit_ms = 0.0
|
||||
next_limit_ms = 0.0
|
||||
next_distance_m = 0.0
|
||||
source = "none"
|
||||
|
||||
if self.sm is not None:
|
||||
try:
|
||||
current_limit_ms = float(self.sm["mapdOut"].speedLimit or 0.0)
|
||||
next_limit_ms = float(self.sm["mapdOut"].nextSpeedLimit or 0.0)
|
||||
next_distance_m = float(self.sm["mapdOut"].nextSpeedLimitDistance or 0.0)
|
||||
if current_limit_ms > 0.0 or next_limit_ms > 0.0:
|
||||
source = "mapd"
|
||||
except Exception:
|
||||
current_limit_ms = 0.0
|
||||
next_limit_ms = 0.0
|
||||
next_distance_m = 0.0
|
||||
|
||||
if self.params_memory is not None:
|
||||
filler_current_limit_ms = float(self.params_memory.get_float("MapSpeedLimit") or 0.0)
|
||||
next_map_speed_limit = self._read_next_map_speed_limit()
|
||||
filler_next_limit_ms = float(next_map_speed_limit.get("speedlimit") or 0.0)
|
||||
filler_next_distance_m = float(next_map_speed_limit.get("distance") or 0.0)
|
||||
if filler_current_limit_ms > 0.0 or filler_next_limit_ms > 0.0:
|
||||
current_limit_ms = filler_current_limit_ms if filler_current_limit_ms > 0.0 else current_limit_ms
|
||||
next_limit_ms = filler_next_limit_ms if filler_next_limit_ms > 0.0 else next_limit_ms
|
||||
next_distance_m = filler_next_distance_m if filler_next_distance_m > 0.0 else next_distance_m
|
||||
source = "filler"
|
||||
|
||||
current_limit_mph = int(round(current_limit_ms * CV.MS_TO_MPH)) if current_limit_ms > 0.0 else 0
|
||||
next_limit_mph = int(round(next_limit_ms * CV.MS_TO_MPH)) if next_limit_ms > 0.0 else 0
|
||||
next_distance_m = round(next_distance_m, 1) if next_distance_m > 0.0 else 0.0
|
||||
|
||||
return {
|
||||
"source": source,
|
||||
"current_speed_limit_mph": current_limit_mph,
|
||||
"next_speed_limit_mph": next_limit_mph,
|
||||
"next_speed_limit_distance_m": next_distance_m,
|
||||
}
|
||||
|
||||
def _map_fields(self, speed_limit_mph=0):
|
||||
map_context = self._get_map_context()
|
||||
current_limit_mph = int(map_context["current_speed_limit_mph"])
|
||||
next_limit_mph = int(map_context["next_speed_limit_mph"])
|
||||
next_distance_m = float(map_context["next_speed_limit_distance_m"])
|
||||
map_source = str(map_context["source"])
|
||||
|
||||
expected_speed_limit_mph = current_limit_mph if current_limit_mph > 0 else 0
|
||||
map_relation = "no_map"
|
||||
review_bucket = "vision_only"
|
||||
|
||||
next_is_relevant = next_limit_mph > 0 and next_limit_mph != current_limit_mph and 0.0 < next_distance_m <= MAP_NEXT_REVIEW_DISTANCE_METERS
|
||||
|
||||
if current_limit_mph > 0:
|
||||
if speed_limit_mph > 0 and speed_limit_mph == current_limit_mph:
|
||||
map_relation = "agree_current"
|
||||
review_bucket = "map_agreement"
|
||||
else:
|
||||
map_relation = "disagree_current"
|
||||
review_bucket = "map_disagreement"
|
||||
|
||||
if next_is_relevant:
|
||||
if speed_limit_mph > 0 and speed_limit_mph == next_limit_mph:
|
||||
expected_speed_limit_mph = next_limit_mph
|
||||
map_relation = "agree_next"
|
||||
review_bucket = "map_agreement"
|
||||
elif current_limit_mph <= 0:
|
||||
expected_speed_limit_mph = next_limit_mph
|
||||
map_relation = "disagree_next"
|
||||
review_bucket = "map_disagreement"
|
||||
|
||||
if speed_limit_mph <= 0:
|
||||
if next_is_relevant:
|
||||
expected_speed_limit_mph = next_limit_mph
|
||||
map_relation = "map_transition_pending"
|
||||
review_bucket = "map_transition_review"
|
||||
elif current_limit_mph > 0:
|
||||
map_relation = "map_present"
|
||||
review_bucket = "map_context_only"
|
||||
|
||||
return {
|
||||
"mapSource": map_source,
|
||||
"mapCurrentSpeedLimitMph": current_limit_mph,
|
||||
"mapNextSpeedLimitMph": next_limit_mph,
|
||||
"mapNextSpeedLimitDistanceM": next_distance_m,
|
||||
"mapExpectedSpeedLimitMph": expected_speed_limit_mph,
|
||||
"mapRelation": map_relation,
|
||||
"reviewBucket": review_bucket,
|
||||
}
|
||||
|
||||
def _vision_recently_supported(self, speed_limit_mph, now):
|
||||
if speed_limit_mph <= 0:
|
||||
return False
|
||||
if self.last_candidate_speed_limit_mph == speed_limit_mph and now - self.last_candidate_at <= MAP_VISION_MATCH_WINDOW_SECONDS:
|
||||
return True
|
||||
if self.published_speed_limit_mph == speed_limit_mph and now - self.last_detection_at <= MAP_VISION_MATCH_WINDOW_SECONDS:
|
||||
return True
|
||||
return any(
|
||||
entry.speed_limit_mph == speed_limit_mph and now - entry.created_at <= MAP_VISION_MATCH_WINDOW_SECONDS
|
||||
for entry in self.history
|
||||
)
|
||||
|
||||
def _write_debug_event(self, event_type, frame_bgr=None, snapshot_prefix=None, **fields):
|
||||
if not self.use_runtime or self.params_memory is None:
|
||||
return
|
||||
@@ -307,6 +427,7 @@ class SpeedLimitVisionDaemon:
|
||||
}
|
||||
if self.debug_session_started_at > 0.0:
|
||||
event["sessionSeconds"] = round(max(time.monotonic() - self.debug_session_started_at, 0.0), 3)
|
||||
event.update(self._map_fields(int(fields.get("speedLimitMph") or fields.get("candidateSpeedLimitMph") or 0)))
|
||||
event.update(fields)
|
||||
|
||||
if frame_bgr is not None and self.debug_capture_dir is not None and snapshot_prefix:
|
||||
@@ -390,6 +511,24 @@ class SpeedLimitVisionDaemon:
|
||||
sourceEvent=source_event,
|
||||
)
|
||||
|
||||
def _record_map_transition_miss(self, speed_limit_mph, previous_speed_limit_mph):
|
||||
if not self.use_runtime or self.params_memory is None or not self.debug_log_path:
|
||||
return
|
||||
|
||||
self._write_debug_event(
|
||||
"map_transition_miss",
|
||||
frame_bgr=self.current_frame_bgr,
|
||||
snapshot_prefix=f"map_transition_miss_{speed_limit_mph:03d}",
|
||||
speedLimitMph=speed_limit_mph,
|
||||
previousMapSpeedLimitMph=previous_speed_limit_mph,
|
||||
candidateSpeedLimitMph=self.last_candidate_speed_limit_mph,
|
||||
candidateConfidence=round(self.last_candidate_confidence, 4),
|
||||
publishedSpeedLimitMph=self.published_speed_limit_mph,
|
||||
publishedConfidence=round(self.published_confidence, 4),
|
||||
reason="map_change_without_vision_support",
|
||||
reviewBucket="map_transition_miss",
|
||||
)
|
||||
|
||||
def _schedule_auto_bookmark(self, speed_limit_mph, confidence, published_at):
|
||||
if not self.use_runtime or self.params is None:
|
||||
return
|
||||
@@ -503,6 +642,27 @@ class SpeedLimitVisionDaemon:
|
||||
self.last_training_capture_speed_limit_mph = speed_limit_mph
|
||||
self._record_training_candidate(speed_limit_mph, confidence, source_confidence, source_event)
|
||||
|
||||
def _maybe_capture_map_transition_miss(self, now):
|
||||
map_context = self._get_map_context()
|
||||
current_speed_limit_mph = int(map_context["current_speed_limit_mph"])
|
||||
previous_speed_limit_mph = self.last_map_speed_limit_mph
|
||||
|
||||
if current_speed_limit_mph != previous_speed_limit_mph:
|
||||
self.last_map_speed_limit_mph = current_speed_limit_mph
|
||||
|
||||
if current_speed_limit_mph <= 0 or current_speed_limit_mph == previous_speed_limit_mph or previous_speed_limit_mph <= 0:
|
||||
return
|
||||
if self.current_frame_bgr is None:
|
||||
return
|
||||
if now - self.last_map_transition_miss_at < MAP_TRANSITION_MISS_CAPTURE_COOLDOWN_SECONDS and current_speed_limit_mph == self.last_map_transition_miss_speed_limit_mph:
|
||||
return
|
||||
if self._vision_recently_supported(current_speed_limit_mph, now):
|
||||
return
|
||||
|
||||
self.last_map_transition_miss_at = now
|
||||
self.last_map_transition_miss_speed_limit_mph = current_speed_limit_mph
|
||||
self._record_map_transition_miss(current_speed_limit_mph, previous_speed_limit_mph)
|
||||
|
||||
def _published_detection_stale(self, now):
|
||||
return self.published_speed_limit_mph > 0 and now - self.last_detection_at > PUBLISHED_HOLD_SECONDS
|
||||
|
||||
@@ -1617,6 +1777,7 @@ class SpeedLimitVisionDaemon:
|
||||
|
||||
self._maybe_commit_auto_bookmark(now)
|
||||
self._maybe_commit_training_capture(now)
|
||||
self._maybe_capture_map_transition_miss(now)
|
||||
|
||||
ratekeeper.keep_time()
|
||||
|
||||
|
||||
@@ -1181,8 +1181,25 @@
|
||||
"description": "Ask before changing to a new speed limit. To accept, tap the flashing on-screen widget or press the Cruise Increase button. To deny, press the Cruise Decrease button or ignore the prompt for 30 seconds.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"is_parent_toggle": true,
|
||||
"parent_key": "SpeedLimitController"
|
||||
},
|
||||
{
|
||||
"key": "SLCConfirmationLower",
|
||||
"label": "Confirm Lower Limits",
|
||||
"description": "Require confirmation before applying a newly detected lower speed limit.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"parent_key": "SLCConfirmation"
|
||||
},
|
||||
{
|
||||
"key": "SLCConfirmationHigher",
|
||||
"label": "Confirm Higher Limits",
|
||||
"description": "Require confirmation before applying a newly detected higher speed limit.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"parent_key": "SLCConfirmation"
|
||||
},
|
||||
{
|
||||
"key": "SLCLookaheadHigher",
|
||||
"label": "Higher Limit Lookahead Time",
|
||||
@@ -1211,6 +1228,50 @@
|
||||
"ui_type": "toggle",
|
||||
"parent_key": "SpeedLimitController"
|
||||
},
|
||||
{
|
||||
"key": "SLCFallback",
|
||||
"label": "Fallback Speed",
|
||||
"description": "Choose the speed to use when no speed limit source is currently available.",
|
||||
"data_type": "int",
|
||||
"ui_type": "dropdown",
|
||||
"options": [
|
||||
{
|
||||
"value": 0,
|
||||
"label": "Set Speed"
|
||||
},
|
||||
{
|
||||
"value": 1,
|
||||
"label": "Experimental Mode"
|
||||
},
|
||||
{
|
||||
"value": 2,
|
||||
"label": "Previous Limit"
|
||||
}
|
||||
],
|
||||
"parent_key": "SpeedLimitController"
|
||||
},
|
||||
{
|
||||
"key": "SLCOverride",
|
||||
"label": "Override Speed",
|
||||
"description": "Choose how SLC behaves after you manually drive faster than the posted speed limit.",
|
||||
"data_type": "int",
|
||||
"ui_type": "dropdown",
|
||||
"options": [
|
||||
{
|
||||
"value": 0,
|
||||
"label": "None"
|
||||
},
|
||||
{
|
||||
"value": 1,
|
||||
"label": "Set With Gas Pedal"
|
||||
},
|
||||
{
|
||||
"value": 2,
|
||||
"label": "Max Set Speed"
|
||||
}
|
||||
],
|
||||
"parent_key": "SpeedLimitController"
|
||||
},
|
||||
{
|
||||
"key": "SLCMapboxFiller",
|
||||
"label": "Use Mapbox as Fallback",
|
||||
|
||||
Reference in New Issue
Block a user