mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-06-28 18:12:05 +08:00
1113 lines
38 KiB
Python
1113 lines
38 KiB
Python
import sys
|
|
import types
|
|
from types import SimpleNamespace
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from cereal import log
|
|
from opendbc.car.honda.interface import CarInterface
|
|
from opendbc.car.honda.values import CAR
|
|
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
|
|
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner, get_coast_accel, get_vehicle_min_accel
|
|
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import soften_far_radar_lead_accel, should_trigger_planner_fcw
|
|
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
|
|
|
|
|
def make_lead(*, status: bool, d_rel: float = 200.0, v_lead: float = 0.0, a_lead: float = 0.0,
|
|
radar: bool = False, model_prob: float = 0.0):
|
|
lead = log.RadarState.LeadData.new_message()
|
|
lead.status = status
|
|
lead.dRel = d_rel
|
|
lead.vLead = v_lead
|
|
lead.vLeadK = v_lead
|
|
lead.aLeadK = a_lead
|
|
lead.vRel = 0.0
|
|
lead.aRel = 0.0
|
|
lead.modelProb = model_prob
|
|
lead.radar = radar
|
|
return lead
|
|
|
|
|
|
def make_model(v_ego: float, desired_accel: float, gas_press_prob: float = 1.0, brake_press_prob: float = 0.0):
|
|
model = log.ModelDataV2.new_message()
|
|
t_idxs = ModelConstants.T_IDXS
|
|
|
|
model.position.x = [float(v_ego * t) for t in t_idxs]
|
|
model.position.y = [0.0] * len(t_idxs)
|
|
model.position.z = [0.0] * len(t_idxs)
|
|
model.position.t = [float(t) for t in t_idxs]
|
|
|
|
model.velocity.x = [float(v_ego)] * len(t_idxs)
|
|
model.velocity.y = [0.0] * len(t_idxs)
|
|
model.velocity.z = [0.0] * len(t_idxs)
|
|
model.velocity.t = [float(t) for t in t_idxs]
|
|
|
|
model.acceleration.x = [0.0] * len(t_idxs)
|
|
model.acceleration.y = [0.0] * len(t_idxs)
|
|
model.acceleration.z = [0.0] * len(t_idxs)
|
|
model.acceleration.t = [float(t) for t in t_idxs]
|
|
|
|
model.meta.disengagePredictions.gasPressProbs = [float(gas_press_prob)] * 6
|
|
model.meta.disengagePredictions.brakePressProbs = [float(brake_press_prob)] * 6
|
|
model.action.desiredAcceleration = desired_accel
|
|
model.action.shouldStop = False
|
|
return model
|
|
|
|
|
|
def make_sm(v_ego: float, desired_accel: float, min_accel: float, *, experimental_mode: bool = True,
|
|
tracking_lead: bool = False, lead_one=None, lead_two=None,
|
|
gas_press_prob: float = 1.0, brake_press_prob: float = 0.0, disable_throttle: bool = False):
|
|
return {
|
|
"carControl": SimpleNamespace(orientationNED=[0.0, 0.0, 0.0]),
|
|
"carState": SimpleNamespace(
|
|
vEgo=v_ego,
|
|
vEgoCluster=v_ego,
|
|
aEgo=0.0,
|
|
vCruise=100.0,
|
|
standstill=False,
|
|
steeringAngleDeg=0.0,
|
|
),
|
|
"controlsState": SimpleNamespace(
|
|
longControlState=LongCtrlState.pid,
|
|
forceDecel=False,
|
|
),
|
|
"liveParameters": SimpleNamespace(angleOffsetDeg=0.0),
|
|
"modelV2": make_model(v_ego, desired_accel, gas_press_prob=gas_press_prob, brake_press_prob=brake_press_prob),
|
|
"radarState": SimpleNamespace(
|
|
leadOne=lead_one if lead_one is not None else make_lead(status=False),
|
|
leadTwo=lead_two if lead_two is not None else make_lead(status=False),
|
|
),
|
|
"selfdriveState": SimpleNamespace(enabled=True, experimentalMode=experimental_mode, personality=0),
|
|
"starpilotPlan": SimpleNamespace(
|
|
vCruise=v_ego + 5.0,
|
|
minAcceleration=min_accel,
|
|
maxAcceleration=2.0,
|
|
disableThrottle=disable_throttle,
|
|
trackingLead=tracking_lead,
|
|
accelerationJerk=5.0,
|
|
dangerJerk=5.0,
|
|
speedJerk=5.0,
|
|
dangerFactor=1.0,
|
|
tFollow=1.45,
|
|
forcingStopLength=2,
|
|
),
|
|
}
|
|
|
|
|
|
def make_toggles(model_version: str = "v11"):
|
|
return SimpleNamespace(
|
|
taco_tune=False,
|
|
classic_model=False,
|
|
tinygrad_model=True,
|
|
model_version=model_version,
|
|
stop_distance=6.0,
|
|
vEgoStopping=0.5,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_experimental_mlsim_uses_vehicle_min_accel_floor(model_version):
|
|
v_ego = 18.0
|
|
desired_accel = -1.0
|
|
comfort_min_accel = -0.5
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(v_ego, desired_accel, comfort_min_accel)
|
|
|
|
vehicle_min_accel = get_vehicle_min_accel(CP, v_ego)
|
|
assert vehicle_min_accel < comfort_min_accel
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.mode == "blended"
|
|
assert planner.mlsim
|
|
assert planner.output_a_target == pytest.approx(desired_accel, abs=1e-3)
|
|
assert planner.output_a_target < comfort_min_accel
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_uses_close_raw_lead_when_tracking_lead_is_debounced(model_version):
|
|
v_ego = 5.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=-0.6,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=24.0, v_lead=0.3),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 12.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.mode == "acc"
|
|
assert planner.raw_close_lead_needs_control(sm["radarState"].leadOne, v_ego)
|
|
assert planner.output_a_target == pytest.approx(
|
|
planner.get_close_lead_brake_cap(sm["radarState"].leadOne, v_ego, sm["starpilotPlan"].minAcceleration)
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_matches_no_lead_baseline_for_far_vision_only_lead_without_tracking(model_version):
|
|
v_ego = 29.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_far_vision = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_far_vision = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=82.0, v_lead=25.0, radar=False, model_prob=0.9),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 2.0
|
|
sm_far_vision["starpilotPlan"].vCruise = v_ego + 2.0
|
|
|
|
no_lead_outputs = []
|
|
far_vision_outputs = []
|
|
for _ in range(8):
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
planner_far_vision.update(sm_far_vision, make_toggles(model_version))
|
|
no_lead_outputs.append(planner_no_lead.output_a_target)
|
|
far_vision_outputs.append(planner_far_vision.output_a_target)
|
|
|
|
assert planner_far_vision.mode == "acc"
|
|
assert not planner_far_vision.raw_close_lead_needs_control(sm_far_vision["radarState"].leadOne, v_ego)
|
|
np.testing.assert_allclose(far_vision_outputs, no_lead_outputs, atol=1e-6)
|
|
|
|
|
|
def test_soften_far_radar_lead_accel_reduces_gentle_far_brake():
|
|
softened = soften_far_radar_lead_accel(114.8, 28.88, -0.75, 29.26, 1.45, radar=True)
|
|
assert softened > -0.35
|
|
assert softened < 0.0
|
|
|
|
|
|
def test_soften_far_radar_lead_accel_keeps_close_closing_brake():
|
|
baseline = -0.76
|
|
softened = soften_far_radar_lead_accel(68.0, 26.38, baseline, 29.38, 1.45, radar=True)
|
|
assert softened == pytest.approx(baseline)
|
|
|
|
|
|
def test_planner_fcw_suppresses_low_speed_opening_or_low_ttc_false_positives():
|
|
assert not should_trigger_planner_fcw(
|
|
make_lead(status=True, d_rel=7.156, v_lead=0.798, a_lead=0.021, radar=False, model_prob=0.99),
|
|
0.402,
|
|
)
|
|
assert not should_trigger_planner_fcw(
|
|
make_lead(status=True, d_rel=9.311, v_lead=0.911, a_lead=-0.263, radar=False, model_prob=0.99),
|
|
1.252,
|
|
)
|
|
|
|
|
|
def test_planner_fcw_keeps_real_low_speed_closing_alerts():
|
|
assert should_trigger_planner_fcw(
|
|
make_lead(status=True, d_rel=1.8, v_lead=0.0, a_lead=0.0, radar=False, model_prob=0.99),
|
|
1.6,
|
|
)
|
|
|
|
|
|
def test_vision_lead_approach_cap_brakes_before_hard_cap():
|
|
v_ego = 21.535
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=38.9, v_lead=18.04, a_lead=-0.026, radar=False, model_prob=0.984)
|
|
|
|
hard_cap = planner.get_close_lead_brake_cap(lead, v_ego, -1.0)
|
|
approach_cap = planner.get_vision_lead_approach_cap(lead, v_ego, -1.0, 1.45)
|
|
|
|
assert hard_cap == pytest.approx(-0.212, abs=1e-2)
|
|
assert approach_cap is not None
|
|
assert approach_cap < hard_cap
|
|
assert approach_cap > -1.2
|
|
|
|
|
|
def test_vision_lead_approach_cap_brakes_harder_when_inside_tight_gap():
|
|
v_ego = 26.18
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=39.72, v_lead=22.46, a_lead=-0.15, radar=False, model_prob=0.97)
|
|
|
|
approach_cap = planner.get_vision_lead_approach_cap(lead, v_ego, -1.0, 1.49)
|
|
|
|
assert approach_cap is not None
|
|
assert approach_cap < -0.5
|
|
|
|
|
|
def test_vision_lead_approach_cap_brakes_harder_for_braking_tracked_lead_inside_tight_gap():
|
|
v_ego = 19.50
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=19.7, v_lead=16.25, a_lead=-0.83, radar=False, model_prob=0.98)
|
|
|
|
hard_cap = planner.get_close_lead_brake_cap(lead, v_ego, -3.0)
|
|
approach_cap = planner.get_vision_lead_approach_cap(lead, v_ego, -3.0, 1.45)
|
|
|
|
assert hard_cap == pytest.approx(-1.01, abs=0.03)
|
|
assert approach_cap is not None
|
|
assert approach_cap < -1.35
|
|
assert approach_cap < hard_cap
|
|
|
|
|
|
def test_vision_lead_approach_cap_ignores_opening_lead_with_large_gap():
|
|
v_ego = 19.37
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=66.168, v_lead=20.751, a_lead=0.261, radar=False, model_prob=0.975)
|
|
|
|
assert planner.get_vision_lead_approach_cap(lead, v_ego, -1.0, 1.45) is None
|
|
|
|
|
|
def test_vision_untracked_slow_lead_cap_triggers_only_for_meaningful_closing_case():
|
|
route_v_ego = 23.23
|
|
far_v_ego = 29.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=route_v_ego)
|
|
route_like_lead = make_lead(status=True, d_rel=66.7, v_lead=18.49, a_lead=0.0, radar=False, model_prob=0.92)
|
|
far_mild_lead = make_lead(status=True, d_rel=82.0, v_lead=25.0, a_lead=0.0, radar=False, model_prob=0.9)
|
|
|
|
route_cap = planner.get_vision_untracked_slow_lead_cap(route_like_lead, route_v_ego, -1.0)
|
|
far_cap = planner.get_vision_untracked_slow_lead_cap(far_mild_lead, far_v_ego, -1.0)
|
|
|
|
assert route_cap is not None
|
|
assert route_cap < -0.1
|
|
assert far_cap is None
|
|
|
|
|
|
def test_vision_untracked_slow_lead_cap_reaches_high_confidence_far_slower_lead_before_raw_close_lead():
|
|
v_ego = 21.48
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
route_like_lead = make_lead(status=True, d_rel=93.0, v_lead=12.84, a_lead=0.0, radar=False, model_prob=0.935)
|
|
|
|
route_cap = planner.get_vision_untracked_slow_lead_cap(route_like_lead, v_ego, -1.0)
|
|
|
|
assert route_cap is not None
|
|
assert route_cap < -0.5
|
|
assert not planner.raw_close_lead_needs_control(route_like_lead, v_ego)
|
|
|
|
|
|
def test_vision_untracked_slow_lead_cap_relaxes_confidence_for_near_stopped_high_closure_lead():
|
|
v_ego = 20.35
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
route_like_lead = make_lead(status=True, d_rel=115.4, v_lead=3.76, a_lead=0.0, radar=False, model_prob=0.70)
|
|
|
|
route_cap = planner.get_vision_untracked_slow_lead_cap(route_like_lead, v_ego, -1.0)
|
|
|
|
assert route_cap is not None
|
|
assert route_cap < -0.55
|
|
|
|
|
|
def test_vision_untracked_slow_lead_cap_keeps_low_confidence_floor_for_less_threatening_lead():
|
|
v_ego = 20.35
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
less_threatening_lead = make_lead(status=True, d_rel=115.4, v_lead=9.5, a_lead=0.0, radar=False, model_prob=0.75)
|
|
|
|
assert planner.get_vision_untracked_slow_lead_cap(less_threatening_lead, v_ego, -1.0) is None
|
|
|
|
|
|
def test_vision_slow_stopped_lead_cap_brakes_earlier_for_confident_stop():
|
|
v_ego = 13.207
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=49.131, v_lead=1.837, a_lead=-0.312, radar=False, model_prob=0.942)
|
|
|
|
slow_stop_cap = planner.get_vision_slow_stopped_lead_cap(lead, v_ego, -1.0, 1.75)
|
|
|
|
assert slow_stop_cap is not None
|
|
assert slow_stop_cap < -0.9
|
|
assert slow_stop_cap > -1.25
|
|
|
|
|
|
def test_vision_slow_stopped_lead_cap_ignores_far_high_speed_stop_candidate():
|
|
v_ego = 33.5
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=183.0, v_lead=0.0, a_lead=0.0, radar=False, model_prob=0.995)
|
|
|
|
assert planner.get_vision_slow_stopped_lead_cap(lead, v_ego, -1.0, 1.45) is None
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_dynamic_t_follow_increases_modestly_for_closing_lead(model_version):
|
|
v_ego = 21.535
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-3.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=38.9, v_lead=18.04, a_lead=-0.026, radar=False, model_prob=0.984),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 8.0
|
|
|
|
for _ in range(8):
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.effective_t_follow is not None
|
|
assert planner.effective_t_follow > sm["starpilotPlan"].tFollow + 0.15
|
|
assert planner.effective_t_follow < sm["starpilotPlan"].tFollow + 0.45
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_dynamic_t_follow_stays_near_base_for_far_highway_lead(model_version):
|
|
v_ego = 29.26
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=114.8, v_lead=28.88, a_lead=-0.75, radar=True, model_prob=0.9),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 3.0
|
|
|
|
for _ in range(12):
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.effective_t_follow == pytest.approx(sm["starpilotPlan"].tFollow, abs=0.02)
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_dynamic_t_follow_releases_toward_base_after_lead_opens(model_version):
|
|
v_ego = 21.535
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-3.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=38.9, v_lead=18.04, a_lead=-0.026, radar=False, model_prob=0.984),
|
|
)
|
|
|
|
for _ in range(8):
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
boosted_t_follow = planner.effective_t_follow
|
|
sm["radarState"].leadOne = make_lead(status=True, d_rel=66.168, v_lead=20.751, a_lead=0.261, radar=False, model_prob=0.975)
|
|
for _ in range(12):
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert boosted_t_follow is not None
|
|
assert planner.effective_t_follow < boosted_t_follow
|
|
assert planner.effective_t_follow == pytest.approx(sm["starpilotPlan"].tFollow, abs=0.02)
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_vision_lead_approach_cap_smooths_before_close_brake(model_version):
|
|
approach_v_ego = 21.535
|
|
close_v_ego = 21.435
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_approach = LongitudinalPlanner(CP, init_v=approach_v_ego)
|
|
planner_close = LongitudinalPlanner(CP, init_v=close_v_ego)
|
|
|
|
sm_approach = make_sm(
|
|
approach_v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=38.9, v_lead=18.04, a_lead=-0.026, radar=False, model_prob=0.984),
|
|
)
|
|
sm_close = make_sm(
|
|
close_v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=27.18, v_lead=15.76, a_lead=-0.824, radar=False, model_prob=0.988),
|
|
)
|
|
sm_approach["starpilotPlan"].vCruise = approach_v_ego + 8.0
|
|
sm_close["starpilotPlan"].vCruise = close_v_ego + 8.0
|
|
|
|
approach_outputs = []
|
|
for _ in range(6):
|
|
planner_approach.update(sm_approach, make_toggles(model_version))
|
|
approach_outputs.append(planner_approach.output_a_target)
|
|
|
|
planner_close.update(sm_close, make_toggles(model_version))
|
|
|
|
assert planner_approach.mode == "acc"
|
|
assert planner_close.mode == "acc"
|
|
assert min(approach_outputs[:2]) > -0.55
|
|
assert approach_outputs[-1] < -1.3
|
|
assert planner_close.output_a_target < approach_outputs[0] - 0.8
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_tracked_vision_far_mild_closure_does_not_bypass_persistence(model_version):
|
|
v_ego = 37.45
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=42.8, v_lead=35.31, a_lead=0.18, radar=False, model_prob=0.98)
|
|
|
|
approach_cap = planner.get_vision_lead_approach_cap(lead, v_ego, -1.0, 1.45)
|
|
|
|
assert approach_cap is not None
|
|
assert approach_cap > -1.0
|
|
assert not planner.tracked_vision_lead_approach_needs_immediate_brake(lead, v_ego, approach_cap)
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_tracked_vision_close_or_braking_lead_bypasses_persistence(model_version):
|
|
v_ego = 19.50
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=19.7, v_lead=16.25, a_lead=-0.83, radar=False, model_prob=0.98),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.output_a_target < -1.3
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_vision_slow_lead_blocks_positive_catchup(model_version):
|
|
v_ego = 23.23
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_with_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_with_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=66.7, v_lead=18.49, a_lead=0.0, radar=False, model_prob=0.92),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
sm_with_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
for _ in range(6):
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
planner_with_lead.update(sm_with_lead, make_toggles(model_version))
|
|
|
|
assert planner_with_lead.mode == "acc"
|
|
assert not planner_with_lead.raw_close_lead_needs_control(sm_with_lead["radarState"].leadOne, v_ego)
|
|
assert planner_with_lead.output_a_target <= planner_no_lead.output_a_target - 0.04
|
|
assert planner_with_lead.output_a_target < -0.2
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_vision_far_slower_lead_starts_braking_before_tracking(model_version):
|
|
v_ego = 21.48
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_with_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_with_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=93.0, v_lead=12.84, a_lead=0.0, radar=False, model_prob=0.935),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
sm_with_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
no_lead_outputs = []
|
|
lead_outputs = []
|
|
for _ in range(8):
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
planner_with_lead.update(sm_with_lead, make_toggles(model_version))
|
|
no_lead_outputs.append(planner_no_lead.output_a_target)
|
|
lead_outputs.append(planner_with_lead.output_a_target)
|
|
|
|
assert planner_with_lead.mode == "acc"
|
|
assert not planner_with_lead.raw_close_lead_needs_control(sm_with_lead["radarState"].leadOne, v_ego)
|
|
assert all(lead_output <= no_lead_output + 1e-6
|
|
for lead_output, no_lead_output in zip(lead_outputs[5:], no_lead_outputs[5:]))
|
|
assert min(lead_outputs[5:]) < min(no_lead_outputs[5:]) - 0.08
|
|
assert lead_outputs[-1] < no_lead_outputs[-1] - 0.15
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_vision_far_slower_lead_can_still_brake_immediately(model_version):
|
|
v_ego = 21.48
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=93.0, v_lead=12.84, a_lead=0.0, radar=False, model_prob=0.935),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.mode == "acc"
|
|
assert planner.output_a_target < -0.45
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_closer_braking_vision_lead_bypasses_far_lead_persistence(model_version):
|
|
v_ego = 17.46
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=41.9, v_lead=14.86, a_lead=-0.03, radar=False, model_prob=1.0),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.mode == "acc"
|
|
assert planner.output_a_target < -0.35
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_flappy_far_lead_requires_persistence(model_version):
|
|
v_ego = 26.09
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_flappy = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_flappy = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=74.75, v_lead=26.63, a_lead=0.01, radar=False, model_prob=0.989),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
sm_flappy["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
flappy_sequence = [
|
|
(74.75, 26.63, 0.01, 0.989),
|
|
(68.17, 20.81, 0.094, 0.971),
|
|
(69.73, 24.12, 0.057, 0.981),
|
|
(62.15, 21.38, 0.064, 0.983),
|
|
(66.29, 23.19, 0.069, 0.985),
|
|
(70.58, 27.51, 0.036, 0.988),
|
|
]
|
|
|
|
no_lead_outputs = []
|
|
flappy_outputs = []
|
|
for d_rel, v_lead, a_lead, model_prob in flappy_sequence:
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
sm_flappy["radarState"].leadOne = make_lead(
|
|
status=True, d_rel=d_rel, v_lead=v_lead, a_lead=a_lead, radar=False, model_prob=model_prob,
|
|
)
|
|
planner_flappy.update(sm_flappy, make_toggles(model_version))
|
|
no_lead_outputs.append(planner_no_lead.output_a_target)
|
|
flappy_outputs.append(planner_flappy.output_a_target)
|
|
|
|
assert planner_flappy.mode == "acc"
|
|
assert min(flappy_outputs) > min(no_lead_outputs) - 0.12
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_pretracking_near_stopped_vision_lead_does_not_relax_when_confidence_is_midrange(model_version):
|
|
v_ego = 20.35
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_with_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_with_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
lead_one=make_lead(status=True, d_rel=115.4, v_lead=3.76, a_lead=0.0, radar=False, model_prob=0.70),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
sm_with_lead["starpilotPlan"].vCruise = v_ego + 6.0
|
|
|
|
for _ in range(8):
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
planner_with_lead.update(sm_with_lead, make_toggles(model_version))
|
|
|
|
assert planner_with_lead.mode == "acc"
|
|
assert planner_with_lead.output_a_target < planner_no_lead.output_a_target - 0.12
|
|
assert planner_with_lead.output_a_target < -0.45
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_tracked_pace_matched_lead_caps_positive_catchup(model_version):
|
|
v_ego = 28.7
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_no_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner_with_lead = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm_no_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.5,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=False,
|
|
)
|
|
sm_with_lead = make_sm(
|
|
v_ego,
|
|
desired_accel=0.5,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=22.0, v_lead=29.4, a_lead=0.0, radar=False, model_prob=0.995),
|
|
)
|
|
sm_no_lead["starpilotPlan"].vCruise = v_ego + 4.0
|
|
sm_with_lead["starpilotPlan"].vCruise = v_ego + 4.0
|
|
|
|
for _ in range(8):
|
|
planner_no_lead.update(sm_no_lead, make_toggles(model_version))
|
|
planner_with_lead.update(sm_with_lead, make_toggles(model_version))
|
|
|
|
assert planner_with_lead.mode == "acc"
|
|
assert planner_with_lead.output_a_target <= planner_no_lead.output_a_target - 0.15
|
|
assert planner_with_lead.output_a_target < 0.08
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_low_speed_vision_stop_buffer_sets_should_stop_before_tiny_gap(model_version):
|
|
v_ego = 3.8
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.1,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=5.75, v_lead=0.58, a_lead=-0.1, radar=False, model_prob=0.99),
|
|
)
|
|
sm["starpilotPlan"].vCruise = v_ego + 4.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.mode == "acc"
|
|
assert planner.output_should_stop
|
|
assert planner.output_a_target < -1.0
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_standstill_moving_lead_does_not_force_resume_while_should_stop(model_version):
|
|
v_ego = 0.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.0,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=7.1, v_lead=2.3, a_lead=1.8, radar=True, model_prob=1.0),
|
|
)
|
|
sm["carState"].standstill = True
|
|
sm["controlsState"].longControlState = LongCtrlState.stopping
|
|
sm["starpilotPlan"].vCruise = 10.0
|
|
|
|
planner.update(sm, make_toggles(model_version))
|
|
|
|
assert planner.output_should_stop
|
|
assert planner.output_a_target < 0.1
|
|
|
|
|
|
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14"])
|
|
def test_acc_mode_damps_far_radar_mild_lead_brake_more_than_close_brake(model_version):
|
|
far_v_ego = 29.26
|
|
far_v_cruise = 32.22
|
|
close_v_ego = 29.38
|
|
close_v_cruise = 32.22
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner_far = LongitudinalPlanner(CP, init_v=far_v_ego)
|
|
planner_close = LongitudinalPlanner(CP, init_v=close_v_ego)
|
|
|
|
sm_far = make_sm(
|
|
far_v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=114.8, v_lead=28.88, a_lead=-0.75, radar=True, model_prob=0.9),
|
|
)
|
|
sm_close = make_sm(
|
|
close_v_ego,
|
|
desired_accel=0.2,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=68.0, v_lead=26.38, a_lead=-0.76, radar=True, model_prob=0.9),
|
|
)
|
|
sm_far["starpilotPlan"].vCruise = far_v_cruise
|
|
sm_close["starpilotPlan"].vCruise = close_v_cruise
|
|
|
|
for _ in range(80):
|
|
planner_far.update(sm_far, make_toggles(model_version))
|
|
planner_close.update(sm_close, make_toggles(model_version))
|
|
|
|
assert planner_far.mode == "acc"
|
|
assert planner_close.mode == "acc"
|
|
assert planner_far.output_a_target > -0.4
|
|
assert planner_close.output_a_target < planner_far.output_a_target - 0.1
|
|
|
|
|
|
def test_modeld_action_passes_tomb_raider_longitudinal_params(monkeypatch):
|
|
monkeypatch.setenv("DEBUG", "0")
|
|
fake_commonmodel = types.ModuleType("openpilot.selfdrive.modeld.models.commonmodel_pyx")
|
|
fake_commonmodel.DrivingModelFrame = object
|
|
fake_commonmodel.CLContext = object
|
|
monkeypatch.setitem(sys.modules, fake_commonmodel.__name__, fake_commonmodel)
|
|
|
|
from openpilot.selfdrive.modeld import modeld
|
|
|
|
captured = {}
|
|
|
|
def fake_get_accel_from_plan(speeds, accels, t_idxs, *, action_t, vEgoStopping):
|
|
captured["speeds"] = speeds
|
|
captured["accels"] = accels
|
|
captured["t_idxs"] = t_idxs
|
|
captured["action_t"] = action_t
|
|
captured["vEgoStopping"] = vEgoStopping
|
|
return 0.4, True
|
|
|
|
monkeypatch.setattr(modeld, "get_accel_from_plan_tomb_raider", fake_get_accel_from_plan)
|
|
|
|
plan = np.zeros((1, ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH), dtype=np.float32)
|
|
plan[0, :, Plan.VELOCITY] = 3.0
|
|
plan[0, :, Plan.ACCELERATION] = -0.1
|
|
prev_action = log.ModelDataV2.Action.new_message()
|
|
toggles = SimpleNamespace(vEgoStopping=0.42)
|
|
|
|
action = modeld.get_action_from_model(
|
|
{"plan": plan},
|
|
prev_action,
|
|
lat_action_t=0.2,
|
|
long_action_t=0.73,
|
|
v_ego=5.0,
|
|
mlsim=True,
|
|
is_v9=True,
|
|
is_v14=False,
|
|
starpilot_toggles=toggles,
|
|
)
|
|
|
|
assert captured["action_t"] == pytest.approx(0.73)
|
|
assert captured["vEgoStopping"] == pytest.approx(0.42)
|
|
assert list(captured["t_idxs"]) == ModelConstants.T_IDXS
|
|
np.testing.assert_allclose(captured["speeds"], 3.0)
|
|
np.testing.assert_allclose(captured["accels"], -0.1)
|
|
assert action.shouldStop
|
|
|
|
|
|
def test_modeld_action_uses_direct_action_head_for_v14(monkeypatch):
|
|
monkeypatch.setenv("DEBUG", "0")
|
|
fake_commonmodel = types.ModuleType("openpilot.selfdrive.modeld.models.commonmodel_pyx")
|
|
fake_commonmodel.DrivingModelFrame = object
|
|
fake_commonmodel.CLContext = object
|
|
monkeypatch.setitem(sys.modules, fake_commonmodel.__name__, fake_commonmodel)
|
|
|
|
from openpilot.selfdrive.modeld import modeld
|
|
|
|
prev_action = log.ModelDataV2.Action.new_message()
|
|
prev_action.desiredCurvature = 0.05
|
|
prev_action.desiredAcceleration = -0.2
|
|
toggles = SimpleNamespace(vEgoStopping=0.42)
|
|
|
|
action = modeld.get_action_from_model(
|
|
{"action": np.array([[12.0, -0.8]], dtype=np.float32)},
|
|
prev_action,
|
|
lat_action_t=0.2,
|
|
long_action_t=0.73,
|
|
v_ego=5.0,
|
|
mlsim=True,
|
|
is_v9=False,
|
|
is_v14=True,
|
|
starpilot_toggles=toggles,
|
|
)
|
|
|
|
assert action.desiredCurvature == pytest.approx(0.12)
|
|
assert action.desiredAcceleration < -0.2
|
|
assert not action.shouldStop
|
|
|
|
|
|
def test_allow_throttle_hysteresis_filters_gas_prob_chatter():
|
|
v_ego = 10.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(v_ego, desired_accel=0.0, min_accel=-1.0, experimental_mode=False, gas_press_prob=0.5)
|
|
toggles = make_toggles()
|
|
|
|
planner.update(sm, toggles)
|
|
assert planner.model_allow_throttle
|
|
assert planner.allow_throttle
|
|
|
|
sm["modelV2"] = make_model(v_ego, desired_accel=0.0, gas_press_prob=0.37)
|
|
planner.update(sm, toggles)
|
|
assert planner.model_allow_throttle
|
|
assert planner.allow_throttle
|
|
|
|
sm["modelV2"] = make_model(v_ego, desired_accel=0.0, gas_press_prob=0.34)
|
|
planner.update(sm, toggles)
|
|
assert not planner.model_allow_throttle
|
|
assert not planner.allow_throttle
|
|
|
|
sm["modelV2"] = make_model(v_ego, desired_accel=0.0, gas_press_prob=0.43)
|
|
planner.update(sm, toggles)
|
|
assert not planner.model_allow_throttle
|
|
assert not planner.allow_throttle
|
|
|
|
sm["modelV2"] = make_model(v_ego, desired_accel=0.0, gas_press_prob=0.46)
|
|
planner.update(sm, toggles)
|
|
assert planner.model_allow_throttle
|
|
assert planner.allow_throttle
|
|
|
|
|
|
def test_no_throttle_cap_stays_at_coast_limit_until_throttle_returns():
|
|
v_ego = 8.5
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(v_ego, desired_accel=0.0, min_accel=-3.0, experimental_mode=False, gas_press_prob=0.0)
|
|
sm["carControl"].orientationNED = [0.0, 0.1, 0.0]
|
|
toggles = make_toggles()
|
|
|
|
planner.update(sm, toggles)
|
|
|
|
accel_coast = max(get_vehicle_min_accel(CP, v_ego), get_coast_accel(sm["carControl"].orientationNED[1]))
|
|
|
|
assert not planner.allow_throttle
|
|
assert planner.output_a_target == pytest.approx(accel_coast, abs=1e-3)
|
|
|
|
|
|
def test_far_near_speed_follow_keeps_uncertainty_smoothing_active():
|
|
v_ego = 30.0
|
|
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.0,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=78.0, v_lead=29.2, radar=False, model_prob=0.96),
|
|
)
|
|
sm["modelV2"] = make_model(v_ego, desired_accel=0.0, gas_press_prob=1.0, brake_press_prob=0.52)
|
|
toggles = make_toggles()
|
|
|
|
for _ in range(12):
|
|
planner.update(sm, toggles)
|
|
|
|
assert planner.mpc.filter_time_factor > 0.75
|
|
|
|
|
|
def test_near_speed_follow_keeps_some_smoothing_under_high_uncertainty():
|
|
v_ego = 31.0
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=53.0, v_lead=29.8, radar=False, model_prob=0.96)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.0,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=lead,
|
|
brake_press_prob=0.85,
|
|
)
|
|
|
|
for _ in range(16):
|
|
planner.update(sm, make_toggles())
|
|
|
|
assert planner.mpc.filter_time_factor >= 0.24
|
|
|
|
|
|
def test_near_speed_follow_soft_brake_cap_limits_matched_follow_pulse():
|
|
v_ego = 31.4
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=44.0, v_lead=30.1, radar=False, model_prob=0.96)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.0,
|
|
min_accel=-1.0,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=lead,
|
|
brake_press_prob=0.85,
|
|
)
|
|
|
|
for _ in range(16):
|
|
planner.update(sm, make_toggles())
|
|
|
|
assert planner.mpc.filter_time_factor >= 0.24
|
|
assert planner.output_a_target >= -0.33
|
|
|
|
|
|
def test_near_speed_follow_soft_brake_cap_covers_slightly_opening_lead():
|
|
v_ego = 29.58
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=44.44, v_lead=30.65, radar=False, model_prob=0.98)
|
|
|
|
cap = planner.get_matched_follow_brake_cap(lead, v_ego, 1.45)
|
|
|
|
assert cap is not None
|
|
assert cap >= -0.14
|
|
assert cap <= -0.06
|
|
|
|
|
|
def test_near_speed_follow_soft_brake_cap_extends_to_spacious_modest_closing():
|
|
v_ego = 23.69
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=49.48, v_lead=21.64, a_lead=-0.014, radar=False, model_prob=0.998)
|
|
|
|
cap = planner.get_matched_follow_brake_cap(lead, v_ego, 1.45)
|
|
|
|
assert cap is not None
|
|
assert cap >= -0.33
|
|
assert cap <= -0.22
|
|
|
|
|
|
def test_near_speed_follow_soft_brake_cap_uses_raw_vehicle_speed_when_cluster_runs_high():
|
|
v_ego = 23.0073
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
sm = make_sm(
|
|
v_ego,
|
|
desired_accel=0.0,
|
|
min_accel=-0.5,
|
|
experimental_mode=False,
|
|
tracking_lead=True,
|
|
lead_one=make_lead(status=True, d_rel=47.52, v_lead=21.68, a_lead=-0.0646, radar=False, model_prob=0.998),
|
|
)
|
|
sm["carState"].vEgoCluster = 23.6931
|
|
sm["starpilotPlan"].maxAcceleration = 0.61
|
|
|
|
for _ in range(6):
|
|
planner.update(sm, make_toggles())
|
|
|
|
assert not planner.lead_is_matched_follow_window(sm["radarState"].leadOne, sm["carState"].vEgoCluster, 1.45)
|
|
assert planner.output_a_target > -0.35
|
|
assert planner.output_a_target < -0.22
|
|
|
|
|
|
def test_near_speed_follow_soft_brake_cap_rejects_close_gap_even_with_modest_closing():
|
|
v_ego = 37.19
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=34.70, v_lead=35.88, radar=False, model_prob=0.99)
|
|
|
|
cap = planner.get_matched_follow_brake_cap(lead, v_ego, 1.0)
|
|
|
|
assert cap is None
|
|
|
|
|
|
def test_follow_control_lead_prefers_active_lead1_for_matched_follow():
|
|
v_ego = 23.3
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner.lead_one = make_lead(status=True, d_rel=80.0, v_lead=20.0, radar=False, model_prob=0.6)
|
|
planner.lead_two = make_lead(status=True, d_rel=49.9, v_lead=21.9, radar=False, model_prob=0.98)
|
|
planner.mpc.source = "lead1"
|
|
|
|
follow_lead = planner.get_follow_control_lead(True, v_ego, 1.45)
|
|
|
|
assert follow_lead is planner.lead_two
|
|
|
|
|
|
def test_follow_control_lead_keeps_matched_follow_lead_without_tracking_latch():
|
|
v_ego = 27.5
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
planner.lead_one = make_lead(status=True, d_rel=61.99, v_lead=27.63, radar=False, model_prob=0.99)
|
|
|
|
follow_lead = planner.get_follow_control_lead(False, v_ego, 1.45)
|
|
|
|
assert follow_lead is planner.lead_one
|
|
|
|
|
|
def test_far_lead_soft_brake_cap_limits_high_confidence_distant_vision_lead():
|
|
v_ego = 32.37
|
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
|
planner = LongitudinalPlanner(CP, init_v=v_ego)
|
|
lead = make_lead(status=True, d_rel=82.07, v_lead=30.63, a_lead=-0.01, radar=False, model_prob=0.99)
|
|
|
|
cap = planner.get_far_lead_brake_cap(lead, v_ego, 1.70)
|
|
|
|
assert cap is not None
|
|
assert cap > -0.2
|
|
assert cap < -0.05
|