Compare commits

...

19 Commits

Author SHA1 Message Date
Nayan
924eb1aa57 Merge branch 'master-new' into dynamic-outputs-toggle 2025-07-19 01:03:33 -04:00
discountchubbs
4faeedec57 dynamic outputs toggle 2025-07-16 12:03:35 -07:00
discountchubbs
638a92bffb Rm toggle from refactor 2025-07-16 11:57:30 -07:00
James Vecellio-Grant
ad2fab062e Merge branch 'master-new' into modeld-refactor-july7 2025-07-15 06:38:09 -07:00
discountchubbs
d5b59d2b19 Make most outputs dynamic 2025-07-15 06:34:13 -07:00
discountchubbs
079ce94cb4 dynamic 2025-07-14 16:40:19 -07:00
discountchubbs
f7efd7c641 seems a bit repetitive yea? 2025-07-14 16:34:36 -07:00
discountchubbs
30907c3cf9 Affix to generation, while allowing older models to use this IF param is set. 2025-07-13 20:21:33 -07:00
James Vecellio-Grant
a39bc65883 Even clearer! 2025-07-12 22:26:20 +00:00
James Vecellio-Grant
2cc3755760 Make generation a property for clarity 2025-07-12 22:20:51 +00:00
James Vecellio-Grant
abf836a2b8 Merge branch 'master-new' into modeld-refactor-july7 2025-07-10 07:02:13 -07:00
discountchubbs
3c100e3d92 red diff 2025-07-09 19:37:03 -07:00
James Vecellio-Grant
cda3c90468 Mypy from myphone! 2025-07-08 13:05:15 -07:00
James Vecellio-Grant
2c66b6bb75 Update longitudinal_planner.py 2025-07-08 10:22:48 -07:00
discountchubbs
ee12e4b7b4 Add full conditional 2025-07-08 06:37:41 -07:00
discountchubbs
a7751702b7 This needs to be apart of the conditional else fail 2025-07-08 06:16:45 -07:00
discountchubbs
eaa0b44f71 We can revert this after dev-c3-new testing and ready to merge. 2025-07-07 20:40:46 -07:00
discountchubbs
445b101a8b Clean this up 2025-07-07 20:40:38 -07:00
discountchubbs
64cc311161 Introduce zero inputs for Lead, and plan to conform with new SP model introduced Monday, July 7, 2025 2025-07-07 19:15:51 -07:00
10 changed files with 75 additions and 26 deletions

View File

@@ -151,6 +151,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
// Model Manager params
{"DynamicModeldOutputs", PERSISTENT | BACKUP},
{"ModelManager_ActiveBundle", PERSISTENT},
{"ModelManager_DownloadIndex", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"ModelManager_LastSyncTime", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},

View File

@@ -89,6 +89,14 @@ ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) {
list->addItem(horizontal_line());
// Dynamic Modeld Outputs toggle
dynamicModeldOutputs = new ParamControlSP("DynamicModeldOutputs", tr("Allow Dynamic Model Outputs"),
tr("Enable this to allow potentially smoother Gas and Brake controls on all models produced "
"after September, 2024."),
"../assets/offroad/icon_shell.png");
dynamicModeldOutputs->showDescription();
list->addItem(dynamicModeldOutputs);
// LiveDelay toggle
lagd_toggle_control = new ParamControlSP("LagdToggle", tr("Live Learning Steer Delay"), "", "../assets/offroad/icon_shell.png");
lagd_toggle_control->showDescription();
@@ -304,6 +312,7 @@ void ModelsPanel::updateLabels() {
handleBundleDownloadProgress();
currentModelLblBtn->setEnabled(!is_onroad && !isDownloading());
currentModelLblBtn->setValue(GetActiveModelInternalName());
dynamicModeldOutputs->showDescription();
// Update lagdToggle description with current value
QString desc = tr("Enable this for the car to learn and adapt its steering response time. "

View File

@@ -64,6 +64,7 @@ private:
bool is_onroad = false;
ButtonControlSP *currentModelLblBtn;
ParamControlSP *dynamicModeldOutputs;
ParamControlSP *lagd_toggle_control;
OptionControlSP *delay_control;
QProgressBar *supercomboProgressBar;

View File

@@ -62,7 +62,7 @@ class ModelState:
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
bundle = get_active_bundle()
overrides = {override.key: override.value for override in bundle.overrides}
self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".2"))
self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".0"))
self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0"))
model_paths = get_model_path()

View File

@@ -10,8 +10,8 @@ SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
ConfidenceClass = log.ModelDataV2.ConfidenceClass
def get_curvature_from_output(output, vego, lat_action_t, current_generation=None):
if current_generation != 11:
def get_curvature_from_output(output, vego, lat_action_t, mlsim):
if not mlsim:
if desired_curv := output.get('desired_curvature'): # If the model outputs the desired curvature, use that directly
return float(desired_curv[0, 0])

View File

@@ -54,10 +54,10 @@ class ModelState:
raise
model_bundle = get_active_bundle()
self.generation = model_bundle.generation
self.generation = model_bundle.generation if model_bundle is not None else None
overrides = {override.key: override.value for override in model_bundle.overrides}
self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".2"))
self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".0"))
self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0"))
self.MIN_LAT_CONTROL_SPEED = 0.3
@@ -86,6 +86,10 @@ class ModelState:
self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1,
self.numpy_inputs['desire'].shape[2])
@property
def mlsim(self) -> bool:
return bool(self.generation is not None and self.generation >= 11)
def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray],
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge
@@ -151,7 +155,7 @@ class ModelState:
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
self.full_prev_desired_curv[0,-1,:] = outputs['desired_curvature'][0, :]
self.numpy_inputs[input_name_prev][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
if self.generation == 11:
if self.mlsim:
self.numpy_inputs[input_name_prev][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs]
else:
length = outputs['desired_curvature'][0].size
@@ -165,7 +169,7 @@ class ModelState:
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, self.LONG_SMOOTH_SECONDS)
desired_curvature = get_curvature_from_output(model_output, v_ego, lat_action_t, self.generation)
desired_curvature = get_curvature_from_output(model_output, v_ego, lat_action_t, self.mlsim)
if v_ego > self.MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, self.LAT_SMOOTH_SECONDS)
else:

View File

@@ -1,5 +1,7 @@
import numpy as np
from openpilot.common.params import Params
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
from openpilot.sunnypilot.models.helpers import get_active_bundle
def safe_exp(x, out=None):
@@ -24,6 +26,9 @@ def softmax(x, axis=-1):
class Parser:
def __init__(self, ignore_missing=False):
self.ignore_missing = ignore_missing
self._params = Params()
model_bundle = get_active_bundle()
self.generation = model_bundle.generation if model_bundle is not None else None
def check_missing(self, outs, name):
if name not in outs and not self.ignore_missing:
@@ -88,37 +93,65 @@ class Parser:
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def parse_dynamic_outputs(self, outs: dict[str, np.ndarray]) -> None:
if self._params.get_bool("DynamicModeldOutputs") or (self.generation >= 12):
if 'lead' in outs:
if outs['lead'].shape[1] == 2 * SplitModelConstants.LEAD_MHP_SELECTION *SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH:
self.parse_mdn('lead', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN,SplitModelConstants.LEAD_WIDTH))
else:
self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION,
out_shape=(SplitModelConstants.LEAD_TRAJ_LEN,SplitModelConstants.LEAD_WIDTH))
if 'plan' in outs:
if outs['plan'].shape[1] > 2 * SplitModelConstants.PLAN_WIDTH * SplitModelConstants.IDX_N:
self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION,
out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH))
else:
self.parse_mdn('plan', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH))
else:
if 'lead' in outs:
self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION,
out_shape=(SplitModelConstants.LEAD_TRAJ_LEN,SplitModelConstants.LEAD_WIDTH))
if 'plan' in outs:
self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION,
out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH))
def split_outputs(self, outs: dict[str, np.ndarray]) -> None:
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.DESIRED_CURV_WIDTH,))
if 'desire_pred' in outs:
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(SplitModelConstants.DESIRE_PRED_LEN,SplitModelConstants.DESIRE_PRED_WIDTH))
if 'desire_state' in outs:
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(SplitModelConstants.DESIRE_PRED_WIDTH,))
if 'lane_lines' in outs:
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.NUM_LANE_LINES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
out_shape=(SplitModelConstants.NUM_LANE_LINES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
if 'lane_lines_prob' in outs:
self.parse_binary_crossentropy('lane_lines_prob', outs)
if 'lead_prob' in outs:
self.parse_binary_crossentropy('lead_prob', outs)
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'meta' in outs:
self.parse_binary_crossentropy('meta', outs)
if 'road_edges' in outs:
self.parse_mdn('road_edges', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.NUM_ROAD_EDGES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION,
out_shape=(SplitModelConstants.LEAD_TRAJ_LEN,SplitModelConstants.LEAD_WIDTH))
if 'sim_pose' in outs:
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
out_shape=(SplitModelConstants.NUM_ROAD_EDGES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
if 'sim_pose' in outs:
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
self.parse_dynamic_outputs(outs)
self.split_outputs(outs)
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(SplitModelConstants.DESIRE_PRED_LEN,SplitModelConstants.DESIRE_PRED_WIDTH))
self.parse_binary_crossentropy('meta', outs)
return outs
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION,
out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH))
self.parse_dynamic_outputs(outs)
self.split_outputs(outs)
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.DESIRED_CURV_WIDTH,))
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(SplitModelConstants.DESIRE_PRED_WIDTH,))
return outs
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:

View File

@@ -19,7 +19,7 @@ from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
from pathlib import Path
CURRENT_SELECTOR_VERSION = 6
CURRENT_SELECTOR_VERSION = 7
REQUIRED_MIN_SELECTOR_VERSION = 5
USE_ONNX = os.getenv('USE_ONNX', PC)

View File

@@ -21,7 +21,7 @@ class LongitudinalPlannerSP:
@property
def mlsim(self) -> bool:
return self.generation == 11
return bool(self.generation is not None and self.generation >= 11)
def get_mpc_mode(self) -> str | None:
if not self.dec.active():

View File

@@ -55,6 +55,7 @@ def manager_init() -> None:
("CustomAccShortPressIncrement", "1"),
("DeviceBootMode", "0"),
("DynamicExperimentalControl", "0"),
("DynamicModeldOutputs", "0"),
("HyundaiLongitudinalTuning", "0"),
("InteractivityTimeout", "0"),
("LagdToggle", "1"),