Tom Bombadil

This commit is contained in:
firestar5683
2026-06-23 21:45:36 -05:00
parent d6dc906ff8
commit 16c3d5f48a
6 changed files with 180 additions and 5 deletions
+35 -4
View File
@@ -7,6 +7,16 @@ VISION_LEAD_TRACK_MIN_DISTANCE = 25.0
VISION_LEAD_TRACK_BASE_TIME_GAP = 1.75
VISION_LEAD_TRACK_CLOSING_GAIN = 0.20
VISION_LEAD_TRACK_CLOSING_CAP = 2.50
TRACKED_LEAD_CATCHUP_BIAS_MIN_HEADWAY_MARGIN = 0.35
TRACKED_LEAD_CATCHUP_BIAS_FULL_HEADWAY_MARGIN = 0.65
TRACKED_LEAD_CATCHUP_BIAS_MIN_FADE_START_MARGIN = 0.75
TRACKED_LEAD_CATCHUP_BIAS_MIN_FADE_END_MARGIN = 1.05
TRACKED_LEAD_CATCHUP_BIAS_ABSOLUTE_FADE_START = 2.75
TRACKED_LEAD_CATCHUP_BIAS_ABSOLUTE_FADE_END = 3.10
TRACKED_LEAD_CATCHUP_BIAS_FULL_LATERAL_OFFSET = 0.90
TRACKED_LEAD_CATCHUP_BIAS_MAX_LATERAL_OFFSET = 1.60
TRACKED_LEAD_CATCHUP_BIAS_GAIN = 0.65
TRACKED_LEAD_CATCHUP_BIAS_SPEED_FACTOR = 0.75
RADARLESS_MATCHED_FOLLOW_MIN_SPEED = 22.0
RADARLESS_MATCHED_FOLLOW_MAX_REL_SPEED = 2.0
RADARLESS_MATCHED_FOLLOW_MIN_HEADWAY = 0.95
@@ -56,10 +66,11 @@ def is_radarless_matched_follow_window(v_ego: float, lead_distance: float, v_lea
def get_tracked_lead_catchup_bias(v_ego: float, lead_distance: float, desired_gap: float, closing_speed: float,
v_cruise: float | None = None) -> float:
v_cruise: float | None = None, y_rel: float | None = None) -> float:
gap_error = lead_distance - desired_gap
actual_hw = lead_distance / max(v_ego, 1e-3)
desired_hw = desired_gap / max(v_ego, 1e-3)
headway_margin = actual_hw - desired_hw
if v_ego <= HIGHWAY_LEAD_BEHAVIOR_MIN_SPEED:
return 0.0
@@ -71,14 +82,34 @@ def get_tracked_lead_catchup_bias(v_ego: float, lead_distance: float, desired_ga
# Encourage ACC to treat a tracked lead as the active constraint when we're
# hanging far above the requested time gap, but don't override cruise for a
# truly distant lead or one we're already closing on decisively.
if actual_hw <= max(desired_hw + 0.3, 1.72):
if headway_margin <= TRACKED_LEAD_CATCHUP_BIAS_MIN_HEADWAY_MARGIN:
return 0.0
if actual_hw >= max(desired_hw + 1.6, 3.0):
fade_start_margin = max(TRACKED_LEAD_CATCHUP_BIAS_MIN_FADE_START_MARGIN,
TRACKED_LEAD_CATCHUP_BIAS_ABSOLUTE_FADE_START - desired_hw)
fade_end_margin = max(TRACKED_LEAD_CATCHUP_BIAS_MIN_FADE_END_MARGIN,
TRACKED_LEAD_CATCHUP_BIAS_ABSOLUTE_FADE_END - desired_hw)
if headway_margin >= fade_end_margin:
return 0.0
if closing_speed > max(2.5, 0.12 * v_ego):
return 0.0
return min(gap_error * 0.65, max(14.0, 0.75 * v_ego))
entry_factor = min(1.0, max(0.0, (headway_margin - TRACKED_LEAD_CATCHUP_BIAS_MIN_HEADWAY_MARGIN) /
max(TRACKED_LEAD_CATCHUP_BIAS_FULL_HEADWAY_MARGIN - TRACKED_LEAD_CATCHUP_BIAS_MIN_HEADWAY_MARGIN, 1e-3)))
exit_factor = 1.0
if headway_margin > fade_start_margin:
exit_factor = min(1.0, max(0.0, (fade_end_margin - headway_margin) / max(fade_end_margin - fade_start_margin, 1e-3)))
lateral_factor = 1.0
if y_rel is not None:
lateral_offset = abs(float(y_rel))
if lateral_offset >= TRACKED_LEAD_CATCHUP_BIAS_MAX_LATERAL_OFFSET:
return 0.0
if lateral_offset > TRACKED_LEAD_CATCHUP_BIAS_FULL_LATERAL_OFFSET:
lateral_factor = min(1.0, max(0.0, (TRACKED_LEAD_CATCHUP_BIAS_MAX_LATERAL_OFFSET - lateral_offset) /
max(TRACKED_LEAD_CATCHUP_BIAS_MAX_LATERAL_OFFSET - TRACKED_LEAD_CATCHUP_BIAS_FULL_LATERAL_OFFSET, 1e-3)))
bias_cap = max(14.0, TRACKED_LEAD_CATCHUP_BIAS_SPEED_FACTOR * v_ego)
return min(gap_error * TRACKED_LEAD_CATCHUP_BIAS_GAIN, bias_cap) * entry_factor * exit_factor * lateral_factor
def should_disable_far_lead_throttle(v_ego: float, lead_distance: float, desired_gap: float,
@@ -99,6 +99,12 @@ IDENTICAL_RADAR_DUPLICATE_CRUISE_HOLD_MAX_HEADWAY_ABOVE_TARGET = 0.40
IDENTICAL_RADAR_DUPLICATE_CRUISE_HOLD_MAX_LEAD_BRAKE = 0.25
IDENTICAL_RADAR_DUPLICATE_CRUISE_HOLD_MAX_PULLAWAY_SPEED = 1.5
IDENTICAL_RADAR_DUPLICATE_CRUISE_HOLD_MAX_CRUISE_ADVANTAGE = 10.0
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MIN_SPEED = 15.0
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_HEADWAY_ABOVE_TARGET = 0.55
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MIN_HEADWAY_BELOW_TARGET = -0.15
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_LEAD_BRAKE = 0.25
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_PULLAWAY_SPEED = 0.75
IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX = 10.0
# Function to get parameter value based on current speed
def get_speed_based_param(speed_mph, param_array):
@@ -722,6 +728,37 @@ class LongitudinalMpc:
return prev_source
def get_identical_radar_duplicate_cruise_bias(self, lead_one, lead_two, v_ego, t_follow):
if float(v_ego) < IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MIN_SPEED:
return 0.0
if not self.leads_share_identical_radar_track(lead_one, lead_two):
return 0.0
lead = lead_one if lead_one.status else lead_two
if lead is None or not lead.status:
return 0.0
actual_headway = float(lead.dRel) / max(float(v_ego), 1e-3)
headway_margin = actual_headway - float(t_follow)
if headway_margin < IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MIN_HEADWAY_BELOW_TARGET:
return 0.0
if headway_margin > IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_HEADWAY_ABOVE_TARGET:
return 0.0
lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0)))
if lead_brake > IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_LEAD_BRAKE:
return 0.0
lead_delta = float(lead.vLead) - float(v_ego)
if lead_delta > IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_PULLAWAY_SPEED:
return 0.0
return float(np.interp(
headway_margin,
[IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MIN_HEADWAY_BELOW_TARGET, 0.0, IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX_HEADWAY_ABOVE_TARGET],
[IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX, IDENTICAL_RADAR_DUPLICATE_CRUISE_BIAS_MAX * 0.85, 0.0],
))
def set_accel_limits(self, min_a, max_a):
# TODO this sets a max accel limit, but the minimum limit is only for cruise decel
# needs refactor
@@ -769,7 +806,16 @@ class LongitudinalMpc:
if optional_far_lead_comfort and tracking_lead and lead_one.status:
desired_gap = desired_follow_distance(v_ego, lead_one.vLead, t_follow)
closing_speed = max(0.0, v_ego - lead_one.vLead)
cruise_obstacle += get_tracked_lead_catchup_bias(v_ego, lead_one.dRel, desired_gap, closing_speed, v_cruise=v_cruise)
cruise_obstacle += get_tracked_lead_catchup_bias(
v_ego,
lead_one.dRel,
desired_gap,
closing_speed,
v_cruise=v_cruise,
y_rel=float(getattr(lead_one, "yRel", 0.0)),
)
if optional_far_lead_comfort:
cruise_obstacle += self.get_identical_radar_duplicate_cruise_bias(lead_one, lead_two, v_ego, t_follow)
if optional_far_lead_comfort:
lead_0_bias, lead_1_bias = self.get_near_duplicate_lead_source_hysteresis(prev_source, lead_one, lead_two, v_ego)
lead_0_obstacle = lead_0_obstacle + lead_0_bias
@@ -26,6 +26,20 @@ def test_tracked_lead_catchup_bias_applies_to_two_second_highway_gap():
assert bias > 14.0
def test_tracked_lead_catchup_bias_reduces_for_laterally_offset_lead():
centered = get_tracked_lead_catchup_bias(34.0, 103.0, 73.0, 1.9, y_rel=0.2)
offset = get_tracked_lead_catchup_bias(34.0, 103.0, 73.0, 1.9, y_rel=1.95)
assert centered > 0.0
assert offset == 0.0
def test_tracked_lead_catchup_bias_fades_before_very_far_gap_cutoff():
near_upper = get_tracked_lead_catchup_bias(34.0, 103.0, 73.0, 1.9)
smaller_gap = get_tracked_lead_catchup_bias(34.0, 96.0, 73.0, 1.9)
assert near_upper > 0.0
assert near_upper < smaller_gap
def test_tracked_lead_catchup_bias_stays_off_once_at_set_speed():
bias = get_tracked_lead_catchup_bias(31.4, 78.7, 38.0, 0.1, v_cruise=31.4)
assert bias == 0.0
@@ -3039,6 +3039,36 @@ def test_identical_radar_duplicate_cruise_hold_skips_clear_pullaway():
assert sticky is None
def test_identical_radar_duplicate_cruise_bias_penalizes_near_target_follow():
v_ego = 23.8
t_follow = 1.15
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
planner = LongitudinalPlanner(CP, init_v=v_ego)
lead_one = make_lead(status=True, d_rel=35.8, v_lead=23.2, a_lead=0.02, radar=True, model_prob=1.0)
lead_two = make_lead(status=True, d_rel=35.8, v_lead=23.2, a_lead=0.02, radar=True, model_prob=1.0)
lead_one.radarTrackId = 2493
lead_two.radarTrackId = 2493
bias = planner.mpc.get_identical_radar_duplicate_cruise_bias(lead_one, lead_two, v_ego, t_follow)
assert bias > 0.0
def test_identical_radar_duplicate_cruise_bias_skips_far_pullaway_follow():
v_ego = 23.8
t_follow = 1.15
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
planner = LongitudinalPlanner(CP, init_v=v_ego)
lead_one = make_lead(status=True, d_rel=52.0, v_lead=25.5, a_lead=0.08, radar=True, model_prob=1.0)
lead_two = make_lead(status=True, d_rel=52.0, v_lead=25.5, a_lead=0.08, radar=True, model_prob=1.0)
lead_one.radarTrackId = 2493
lead_two.radarTrackId = 2493
bias = planner.mpc.get_identical_radar_duplicate_cruise_bias(lead_one, lead_two, v_ego, t_follow)
assert bias == 0.0
def test_near_duplicate_lead_source_hysteresis_skips_distinct_leads():
v_ego = 27.0
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
+9
View File
@@ -37,6 +37,7 @@ class StarPilotCard:
getattr(self.CP, "carFingerprint", None) in (HYUNDAI_CAR.KIA_FORTE_2019_NON_SCC, HYUNDAI_CAR.KIA_FORTE_2021_NON_SCC) and
bool(hyundai_flags & HyundaiFlags.NON_SCC)
)
self.hyundai_lkas_aol_requires_engagement = 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.prev_active = False
@@ -113,6 +114,12 @@ class StarPilotCard:
def update(self, carState, starpilotCarState, sm, starpilot_toggles):
self.switchback_mode_enabled = self.params_memory.get_bool("SwitchbackModeEnabled")
button_event_types = [self._button_type_raw(be) for be in carState.buttonEvents]
hyundai_lkas_aol_can_toggle = (
not self.hyundai_lkas_aol_requires_engagement or
self.hyundai_aol_ready or
sm["selfdriveState"].active or
carState.cruiseState.enabled
)
if self.hyundai_aol_needs_engagement:
if carState.gearShifter in NON_DRIVING_GEARS:
@@ -124,6 +131,8 @@ class StarPilotCard:
if self.CP.brand == "hyundai" or starpilot_toggles.lkas_allowed_for_aol:
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:
if not hyundai_lkas_aol_can_toggle:
continue
if self.hyundai_aol_needs_engagement:
self.hyundai_aol_ready = True
self.always_on_lateral_allowed = not self.always_on_lateral_allowed
@@ -146,6 +146,51 @@ def test_hyundai_lkas_button_can_start_aol_before_normal_engagement(monkeypatch,
assert ret.pauseLateral is False
def test_sonata_hybrid_lkas_button_does_not_start_aol_before_engagement(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", carFingerprint=spc.HYUNDAI_CAR.HYUNDAI_SONATA_HYBRID),
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
)
car_state = make_car_state(available=False, enabled=False, button_events=[SimpleNamespace(type=spc.ButtonType.lkas, pressed=True)])
starpilot_car_state = SimpleNamespace(distancePressed=False)
sm = make_sm()
toggles = make_toggles(always_on_lateral=True, always_on_lateral_lkas=True)
ret = card.update(car_state, starpilot_car_state, sm, toggles)
assert ret.alwaysOnLateralAllowed is False
assert ret.alwaysOnLateralEnabled is False
def test_sonata_hybrid_lkas_button_can_toggle_aol_after_engagement(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", carFingerprint=spc.HYUNDAI_CAR.HYUNDAI_SONATA_HYBRID),
SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL),
)
starpilot_car_state = SimpleNamespace(distancePressed=False)
sm = make_sm()
toggles = make_toggles(always_on_lateral=True, always_on_lateral_lkas=True)
engaged_state = make_car_state(available=True, enabled=True)
card.update(engaged_state, starpilot_car_state, sm, toggles)
lkas_state = make_car_state(available=False, enabled=False, button_events=[SimpleNamespace(type=spc.ButtonType.lkas, pressed=True)])
ret = card.update(lkas_state, starpilot_car_state, sm, toggles)
assert ret.alwaysOnLateralAllowed is True
assert ret.alwaysOnLateralEnabled is True
def test_hyundai_aol_does_not_auto_start_from_cruise_availability(monkeypatch, tmp_path):
monkeypatch.setattr(spc, "Params", FakeParams)
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)