mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-02 14:13:44 +08:00
145bf39587
Keep the Bosch-A lead probability filter cadence change limited to the verified Honda Bosch-A path. Preserve the existing radar timestep for every other platform and add regression coverage for the gate and both timing paths. Co-authored-by: JamesL787 <jamezelle1@gmail.com>
124 lines
4.7 KiB
Python
124 lines
4.7 KiB
Python
import platform
|
|
from types import SimpleNamespace
|
|
|
|
import cereal.messaging as messaging
|
|
from cereal import log
|
|
import pytest
|
|
|
|
from opendbc.car.toyota.values import CAR as TOYOTA
|
|
from opendbc.car.honda.values import CAR as HONDA
|
|
from openpilot.selfdrive.test.process_replay import replay_process_with_name
|
|
from openpilot.selfdrive.controls.radard import (
|
|
DT_MDL,
|
|
HONDA_BOSCH_A_RADAR_TS,
|
|
RadarD,
|
|
g90_low_speed_radar_lead_sane,
|
|
g90_radar_lead_lateral_sane,
|
|
is_bosch_a_radar_car,
|
|
match_vision_to_track,
|
|
)
|
|
|
|
|
|
class TestLeads:
|
|
@staticmethod
|
|
def make_track(d_rel: float, y_rel: float, v_rel: float, cnt: int = 5):
|
|
return SimpleNamespace(dRel=d_rel, yRel=y_rel, vRel=v_rel, cnt=cnt)
|
|
|
|
@staticmethod
|
|
def make_lead(x: float, y: float, v: float, x_std: float = 1.5, y_std: float = 0.3, v_std: float = 1.0):
|
|
return SimpleNamespace(x=[x], y=[y], v=[v], xStd=[x_std], yStd=[y_std], vStd=[v_std])
|
|
|
|
@staticmethod
|
|
def make_model_data():
|
|
model = log.ModelDataV2.new_message()
|
|
model.init('laneLines', 4)
|
|
model.meta.laneChangeState = 0
|
|
model.meta.laneChangeDirection = 0
|
|
return model
|
|
|
|
def test_g90_radar_filters_side_tracks(self):
|
|
side_track = SimpleNamespace(dRel=13.0, yRel=-10.38, cnt=10)
|
|
centered_track = SimpleNamespace(dRel=10.8, yRel=-0.21, cnt=5)
|
|
close_side_ghost = SimpleNamespace(dRel=2.2, yRel=2.41, cnt=10)
|
|
close_centered_track = SimpleNamespace(dRel=2.2, yRel=1.2, cnt=10)
|
|
far_low_speed_track = SimpleNamespace(dRel=15.5, yRel=0.58, cnt=5)
|
|
|
|
assert not g90_radar_lead_lateral_sane(side_track)
|
|
assert g90_radar_lead_lateral_sane(centered_track)
|
|
assert not g90_radar_lead_lateral_sane(close_side_ghost)
|
|
assert g90_radar_lead_lateral_sane(close_centered_track)
|
|
assert g90_low_speed_radar_lead_sane(centered_track, 2.0)
|
|
assert not g90_low_speed_radar_lead_sane(far_low_speed_track, 3.5)
|
|
|
|
def test_match_vision_to_track_sticky_holds_preferred_track(self):
|
|
v_ego = 21.2
|
|
lead = self.make_lead(x=37.72, y=0.07, v=20.03)
|
|
preferred_track = self.make_track(d_rel=46.95, y_rel=0.05, v_rel=-0.58, cnt=8)
|
|
tracks = {3647: preferred_track}
|
|
|
|
track = match_vision_to_track(
|
|
v_ego,
|
|
lead,
|
|
self.make_model_data(),
|
|
tracks,
|
|
SimpleNamespace(human_lane_changes=False),
|
|
preferred_track_id=3647,
|
|
)
|
|
|
|
assert track is preferred_track
|
|
|
|
def test_match_vision_to_track_sticky_rejects_bad_preferred_track(self):
|
|
v_ego = 21.2
|
|
lead = self.make_lead(x=37.72, y=0.07, v=20.03)
|
|
preferred_track = self.make_track(d_rel=58.0, y_rel=2.8, v_rel=-4.5, cnt=8)
|
|
tracks = {3647: preferred_track}
|
|
|
|
track = match_vision_to_track(
|
|
v_ego,
|
|
lead,
|
|
self.make_model_data(),
|
|
tracks,
|
|
SimpleNamespace(human_lane_changes=False),
|
|
preferred_track_id=3647,
|
|
)
|
|
|
|
assert track is None
|
|
|
|
def test_bosch_a_radard_path_is_gated_to_verified_honda_radar(self):
|
|
assert is_bosch_a_radar_car(SimpleNamespace(brand="honda", carFingerprint=HONDA.HONDA_CIVIC_BOSCH, radarUnavailable=False))
|
|
assert not is_bosch_a_radar_car(SimpleNamespace(brand="toyota", carFingerprint=HONDA.HONDA_CIVIC_BOSCH, radarUnavailable=False))
|
|
assert not is_bosch_a_radar_car(SimpleNamespace(brand="honda", carFingerprint=HONDA.HONDA_CIVIC_BOSCH, radarUnavailable=True))
|
|
|
|
def test_bosch_a_and_legacy_lead_filter_timesteps_are_scoped(self):
|
|
legacy = RadarD(radar_ts=0.06)
|
|
bosch_a = RadarD(radar_ts=0.06, honda_bosch_a_radar=True)
|
|
assert legacy.lead_prob_filters[0].dt == pytest.approx(0.06)
|
|
assert bosch_a.lead_prob_filters[0].dt == pytest.approx(DT_MDL)
|
|
assert bosch_a.kalman_params.A[0][1] == pytest.approx(HONDA_BOSCH_A_RADAR_TS)
|
|
|
|
@pytest.mark.skipif(platform.system() == "Darwin", reason="SocketEventHandle requires eventfd")
|
|
def test_radar_fault(self):
|
|
# if there's no radar-related can traffic, radard should either not respond or respond with an error
|
|
# this is tightly coupled with underlying car radar_interface implementation, but it's a good sanity check
|
|
def single_iter_pkg():
|
|
# single iter package, with meaningless cans and empty carState/modelV2
|
|
msgs = []
|
|
for _ in range(500):
|
|
can = messaging.new_message("can", 1)
|
|
cs = messaging.new_message("carState")
|
|
cp = messaging.new_message("carParams")
|
|
msgs.append(can.as_reader())
|
|
msgs.append(cs.as_reader())
|
|
msgs.append(cp.as_reader())
|
|
model = messaging.new_message("modelV2")
|
|
msgs.append(model.as_reader())
|
|
|
|
return msgs
|
|
|
|
msgs = [m for _ in range(3) for m in single_iter_pkg()]
|
|
out = replay_process_with_name("card", msgs, fingerprint=TOYOTA.TOYOTA_COROLLA_TSS2)
|
|
states = [m for m in out if m.which() == "liveTracks"]
|
|
failures = [not state.valid for state in states]
|
|
|
|
assert len(states) == 0 or all(failures)
|