Voacc refinement and non-scc cruise check

This commit is contained in:
firestar5683
2026-04-24 18:19:18 -05:00
parent 031ad89851
commit ca5b641b96
40 changed files with 151 additions and 24 deletions
@@ -141,6 +141,9 @@ class CarInterface(CarInterfaceBase):
# see https://github.com/commaai/opendbc/pull/1137/
ret.dashcamOnly = True
if ret.flags & HyundaiFlags.NON_SCC:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.NON_SCC.value
# Common longitudinal control setup
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
@@ -95,6 +95,7 @@ class HyundaiSafetyFlags(IntFlag):
FCEV_GAS = 256
ALT_LIMITS_2 = 512
CANFD_ANGLE_STEERING = 1024
NON_SCC = 4096
class HyundaiStarPilotSafetyFlags(IntFlag):
+30 -3
View File
@@ -55,6 +55,9 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
#define HYUNDAI_LDA_BUTTON_ADDR_CHECK \
{.msg = {{0x391, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_NON_SCC_CRUISE_ADDR_CHECK \
{.msg = {{0x367, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
static const CanMsg HYUNDAI_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(0)
};
@@ -140,6 +143,11 @@ static void hyundai_rx_hook(const CANPacket_t *msg) {
}
}
if ((msg->addr == 0x367U) && (msg->bus == 0U) && hyundai_non_scc) {
uint8_t cruise_set_speed = msg->data[0];
hyundai_common_cruise_state_check((cruise_set_speed > 0U) && (cruise_set_speed < 255U));
}
if (msg->bus == 0U) {
if (msg->addr == 0x251U) {
int torque_driver_new = (GET_BYTES(msg, 0, 2) & 0x7ffU) - 1024U;
@@ -351,6 +359,17 @@ static safety_config hyundai_init(uint16_t param) {
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_non_scc_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_CRUISE_ADDR_CHECK
};
static RxCheck hyundai_non_scc_rx_checks_lda[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_CRUISE_ADDR_CHECK
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_fcev_rx_checks_lda[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
@@ -366,10 +385,18 @@ static safety_config hyundai_init(uint16_t param) {
SET_RX_CHECKS(hyundai_fcev_rx_checks, ret);
}
} else {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_rx_checks_lda, ret);
if (hyundai_non_scc) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_non_scc_rx_checks_lda, ret);
} else {
SET_RX_CHECKS(hyundai_non_scc_rx_checks, ret);
}
} else {
SET_RX_CHECKS(hyundai_rx_checks, ret);
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_rx_checks_lda, ret);
} else {
SET_RX_CHECKS(hyundai_rx_checks, ret);
}
}
}
}
@@ -48,6 +48,9 @@ bool hyundai_has_lda_button = false;
extern bool hyundai_aol_lkas_on_engage;
bool hyundai_aol_lkas_on_engage = false;
extern bool hyundai_non_scc;
bool hyundai_non_scc = false;
static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button
void hyundai_common_init(uint16_t param) {
@@ -61,6 +64,7 @@ void hyundai_common_init(uint16_t param) {
const int HYUNDAI_PARAM_HAS_LDA_BUTTON = 1024;
const uint16_t HYUNDAI_PARAM_AOL_LKAS_ON_ENGAGE = 2048;
const uint16_t HYUNDAI_PARAM_NON_SCC = 4096;
hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS);
hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS);
@@ -72,6 +76,7 @@ void hyundai_common_init(uint16_t param) {
hyundai_has_lda_button = GET_FLAG(param, HYUNDAI_PARAM_HAS_LDA_BUTTON);
hyundai_aol_lkas_on_engage = GET_FLAG(param, HYUNDAI_PARAM_AOL_LKAS_ON_ENGAGE);
hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC);
hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES;
@@ -7,7 +7,7 @@ from opendbc.car.structs import CarParams
from opendbc.safety.tests.libsafety import libsafety_py
import opendbc.safety.tests.common as common
from opendbc.safety.tests.common import CANPackerSafety
from opendbc.safety.tests.hyundai_common import HyundaiAolLkasOnEngageBase, HyundaiAolLkasOnEngageStockBase, HyundaiButtonBase, HyundaiLongitudinalBase
from opendbc.safety.tests.hyundai_common import Buttons, HyundaiAolLkasOnEngageBase, HyundaiAolLkasOnEngageStockBase, HyundaiButtonBase, HyundaiLongitudinalBase
# 4 bit checkusm used in some hyundai messages
@@ -147,6 +147,27 @@ class TestHyundaiSafetyCameraSCC(TestHyundaiSafety):
self.safety.init_tests()
class TestHyundaiSafetyNonScc(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.NON_SCC)
self.safety.init_tests()
def _pcm_status_msg(self, enable):
values = {"CF_Lvr_CruiseSet": 30 if enable else 0}
return self.packer.make_can_msg_safety("LVR12", 0, values)
def test_non_scc_uses_lvr12_rx_checks(self):
self._rx(self._user_gas_msg(False))
self._rx(self._torque_driver_msg(0))
self._rx(self._speed_msg(0))
self._rx(self._user_brake_msg(False))
self._rx(self._button_msg(Buttons.NONE))
self._rx(self._pcm_status_msg(False))
self.assertTrue(self.safety.safety_config_valid())
class TestHyundaiSafetyFCEV(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-9d2a8050-DEBUG";
const uint8_t gitversion[19] = "DEV-031ad898-DEBUG";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
DEV-9d2a8050-DEBUG
DEV-031ad898-DEBUG
@@ -494,7 +494,7 @@ class LongitudinalMpc:
model_v = getattr(model_lead, "v", ())
if tracking_lead and radar_lead is not None and radar_lead.status:
if model_prob > 0.5 and len(model_x) and len(model_v):
if bool(getattr(radar_lead, "radar", False)) and model_prob > 0.5 and len(model_x) and len(model_v):
x_lead_traj = float(radar_lead.dRel) + (np.asarray(model_x, dtype=np.float64) - float(model_x[0]))
v_lead_traj = float(radar_lead.vLead) + (np.asarray(model_v, dtype=np.float64) - float(model_v[0]))
+37 -15
View File
@@ -22,6 +22,9 @@ _LEAD_ACCEL_TAU = 0.6
# Compare model v against dRel motion over a short window to correct sustained over-prediction.
_BIAS_FD_WINDOW_S = 2.0
_BIAS_EMA_TAU = 0.5
_BIAS_MAX_ABS = 8.0
_BIAS_MIN_PROB = 0.5
_BIAS_RESET_JUMP = 10.0
# radar tracks
SPEED, ACCEL = 0, 1 # Kalman filter states enum
@@ -241,7 +244,7 @@ class RadarD:
self._bias_fd_k = int(round(_BIAS_FD_WINDOW_S / DT_MDL))
self.lead_drel_hists = [deque(maxlen=self._bias_fd_k + 1) for _ in range(2)]
self.vego_hist_bias = deque(maxlen=self._bias_fd_k + 1)
self.vego_hists_bias = [deque(maxlen=self._bias_fd_k + 1) for _ in range(2)]
self.bias_ema_filters = [FirstOrderFilter(0.0, _BIAS_EMA_TAU, DT_MDL) for _ in range(2)]
self.v_ego = 0.0
@@ -256,6 +259,35 @@ class RadarD:
self.starpilot_radar_state = custom.StarPilotRadarState.new_message()
self.starpilot_toggles = get_starpilot_toggles()
def _reset_lead_bias(self, idx: int) -> None:
self.lead_drel_hists[idx].clear()
self.vego_hists_bias[idx].clear()
self.bias_ema_filters[idx].x = 0.0
def _update_lead_bias(self, idx: int, lead_msg: capnp._DynamicStructReader) -> float:
# A newly-acquired or hopped lead does not have enough history for finite-difference biasing.
if len(lead_msg.x) == 0 or len(lead_msg.v) == 0 or float(lead_msg.prob) <= _BIAS_MIN_PROB:
self._reset_lead_bias(idx)
return 0.0
lead_x = float(lead_msg.x[0])
if len(self.lead_drel_hists[idx]) and abs(lead_x - self.lead_drel_hists[idx][-1]) > _BIAS_RESET_JUMP:
self._reset_lead_bias(idx)
self.lead_drel_hists[idx].append(lead_x)
self.vego_hists_bias[idx].append(self.v_ego)
if len(self.lead_drel_hists[idx]) == self._bias_fd_k + 1 and len(self.vego_hists_bias[idx]) == self._bias_fd_k + 1:
dt_win = _BIAS_FD_WINDOW_S
v_ego_hist = self.vego_hists_bias[idx]
v_ego_avg = sum(v_ego_hist) / len(v_ego_hist)
a_ego_win = (v_ego_hist[-1] - v_ego_hist[0]) / dt_win
fd_vlead = v_ego_avg + (self.lead_drel_hists[idx][-1] - self.lead_drel_hists[idx][0]) / dt_win + a_ego_win * dt_win / 2
bias_raw = np.clip(float(lead_msg.v[0]) - fd_vlead, -_BIAS_MAX_ABS, _BIAS_MAX_ABS)
self.bias_ema_filters[idx].update(bias_raw)
return float(np.clip(self.bias_ema_filters[idx].x, -_BIAS_MAX_ABS, _BIAS_MAX_ABS))
def update(self, sm: messaging.SubMaster, rr: car.RadarData):
self.ready = sm.seen['modelV2']
self.current_time = 1e-9 * max(sm.logMonoTime.values())
@@ -299,20 +331,7 @@ class RadarD:
leads_v3 = sm['modelV2'].leadsV3
if len(leads_v3) > 1:
lead_probs = [self.lead_prob_filters[i].update(leads_v3[i].prob) for i in range(2)]
self.vego_hist_bias.append(self.v_ego)
lead_biases = [0.0, 0.0]
for i in range(2):
lead_x = float(leads_v3[i].x[0]) if len(leads_v3[i].x) else 0.0
self.lead_drel_hists[i].append(lead_x)
if len(self.lead_drel_hists[i]) == self._bias_fd_k + 1 and len(self.vego_hist_bias) == self._bias_fd_k + 1:
dt_win = _BIAS_FD_WINDOW_S
v_ego_avg = sum(self.vego_hist_bias) / len(self.vego_hist_bias)
a_ego_win = (self.vego_hist_bias[-1] - self.vego_hist_bias[0]) / dt_win
fd_vlead = v_ego_avg + (self.lead_drel_hists[i][-1] - self.lead_drel_hists[i][0]) / dt_win + a_ego_win * dt_win / 2
bias_raw = float(leads_v3[i].v[0]) - fd_vlead
self.bias_ema_filters[i].update(bias_raw)
lead_biases[i] = self.bias_ema_filters[i].x
lead_biases = [self._update_lead_bias(i, leads_v3[i]) for i in range(2)]
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, lead_probs[0],
sm['modelV2'], sm['carState'].standstill, sm['starpilotPlan'],
@@ -320,6 +339,9 @@ class RadarD:
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, lead_probs[1],
sm['modelV2'], sm['carState'].standstill, sm['starpilotPlan'],
self.starpilot_toggles, lead_bias=lead_biases[1], low_speed_override=False)
else:
for i in range(2):
self._reset_lead_bias(i)
if self.ready and (self.starpilot_toggles.adjacent_lead_tracking or self.starpilot_toggles.human_lane_changes):
self.starpilot_radar_state.leadLeft = get_adjacent_lead(self.tracks, sm['carState'].standstill, sm['modelV2'], left=True)
@@ -8,21 +8,27 @@ import pytest
from cereal import log
from opendbc.car.honda.interface import CarInterface
from opendbc.car.honda.values import CAR
import openpilot.selfdrive.controls.radard as radard_mod
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner, get_vehicle_min_accel
from openpilot.selfdrive.controls.radard import RadarD
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):
def make_lead(*, status: bool, d_rel: float = 200.0, v_lead: float = 0.0, a_lead: float = 0.0,
radar: bool = True, model_prob: float = 0.0, a_lead_tau: float = 0.3):
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.aLeadTau = a_lead_tau
lead.vRel = 0.0
lead.aRel = 0.0
lead.modelProb = 0.0
lead.modelProb = model_prob
lead.radar = radar
return lead
@@ -40,6 +46,13 @@ def fill_model_lead(lead_msg, lead, prob: float):
lead_msg.aStd = [0.5] * len(ModelConstants.LEAD_T_IDXS)
def make_model_lead_msg(*, d_rel: float, v_lead: float, prob: float):
model = log.ModelDataV2.new_message()
model.init('leadsV3', 1)
fill_model_lead(model.leadsV3[0], make_lead(status=True, d_rel=d_rel, v_lead=v_lead), prob)
return model.leadsV3[0]
def make_model(v_ego: float, desired_accel: float, gas_press_prob: float = 1.0, lead_one=None, lead_two=None):
model = log.ModelDataV2.new_message()
t_idxs = ModelConstants.T_IDXS
@@ -246,3 +259,38 @@ def test_allow_throttle_hysteresis_filters_gas_prob_chatter():
planner.update(sm, toggles)
assert planner.model_allow_throttle
assert planner.allow_throttle
def test_radard_bias_resets_across_low_prob_lead_acquisition(monkeypatch):
monkeypatch.setattr(radard_mod, "get_starpilot_toggles", lambda sm=None: SimpleNamespace())
rd = RadarD()
rd.v_ego = 33.0
for _ in range(rd._bias_fd_k + 5):
bias = rd._update_lead_bias(0, make_model_lead_msg(d_rel=120.0, v_lead=33.0, prob=0.01))
assert bias == 0.0
bias = rd._update_lead_bias(0, make_model_lead_msg(d_rel=56.0, v_lead=33.0, prob=0.99))
assert bias == 0.0
assert len(rd.lead_drel_hists[0]) == 1
assert len(rd.vego_hists_bias[0]) == 1
def test_model_trajectory_requires_radar_anchor():
radar_lead = make_lead(status=True, d_rel=60.0, v_lead=20.0, radar=False, model_prob=0.99)
model = make_model(30.0, desired_accel=0.0)
fill_model_lead(model.leadsV3[0], make_lead(status=True, d_rel=60.0, v_lead=32.0), 0.99)
model.leadsV3[0].v = [32.0 + 2.0 * i for i in range(len(ModelConstants.LEAD_T_IDXS))]
vision_mpc = LongitudinalMpc()
vision_mpc.set_cur_state(30.0, 0.0)
vision_mpc.lead_v_filter.x = radar_lead.vLead
vision_traj = vision_mpc.process_lead(model.leadsV3[0], radar_lead, tracking_lead=True)
radar_mpc = LongitudinalMpc()
radar_mpc.set_cur_state(30.0, 0.0)
radar_lead.radar = True
radar_traj = radar_mpc.process_lead(model.leadsV3[0], radar_lead, tracking_lead=True)
assert np.allclose(vision_traj[:, 1], 20.0, atol=1e-3)
assert radar_traj[5, 1] > vision_traj[5, 1] + 1.0