diff --git a/common/libcommon.a b/common/libcommon.a index 9639ce54..108532ae 100644 Binary files a/common/libcommon.a and b/common/libcommon.a differ diff --git a/common/params_keys.h b/common/params_keys.h index 9b360004..7b0d81af 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -199,6 +199,14 @@ inline static std::unordered_map keys = { {"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}}, {"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}}, {"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0}}, + {"CustomAccelProfile", {PERSISTENT, BOOL, "0", "0", 3}}, + {"CustomAccelProfile0MPH", {PERSISTENT, FLOAT, "3.0", "3.0", 3}}, + {"CustomAccelProfile11MPH", {PERSISTENT, FLOAT, "2.5", "2.5", 3}}, + {"CustomAccelProfile22MPH", {PERSISTENT, FLOAT, "2.0", "2.0", 3}}, + {"CustomAccelProfile34MPH", {PERSISTENT, FLOAT, "1.5", "1.5", 3}}, + {"CustomAccelProfile45MPH", {PERSISTENT, FLOAT, "1.0", "1.0", 3}}, + {"CustomAccelProfile56MPH", {PERSISTENT, FLOAT, "0.8", "0.8", 3}}, + {"CustomAccelProfile89MPH", {PERSISTENT, FLOAT, "0.6", "0.6", 3}}, {"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}}, {"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}}, {"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}}, diff --git a/common/params_pyx.so b/common/params_pyx.so index 7735b845..9d2d2ea1 100755 Binary files a/common/params_pyx.so and b/common/params_pyx.so differ diff --git a/panda/board/obj/body_h7.bin.signed b/panda/board/obj/body_h7.bin.signed index 910be7b6..cf00ebb6 100644 Binary files a/panda/board/obj/body_h7.bin.signed and b/panda/board/obj/body_h7.bin.signed differ diff --git a/panda/board/obj/body_h7/bootstub.elf b/panda/board/obj/body_h7/bootstub.elf index 876fe6ba..a2f3a042 100755 Binary files a/panda/board/obj/body_h7/bootstub.elf and b/panda/board/obj/body_h7/bootstub.elf differ diff --git a/panda/board/obj/body_h7/main.bin b/panda/board/obj/body_h7/main.bin index d9238496..bdbd436d 100755 Binary files a/panda/board/obj/body_h7/main.bin and b/panda/board/obj/body_h7/main.bin differ diff --git a/panda/board/obj/body_h7/main.elf b/panda/board/obj/body_h7/main.elf index f1744521..63d5fac1 100755 Binary files a/panda/board/obj/body_h7/main.elf and b/panda/board/obj/body_h7/main.elf differ diff --git a/panda/board/obj/bootstub.body_h7.bin b/panda/board/obj/bootstub.body_h7.bin index 32398183..66181e44 100755 Binary files a/panda/board/obj/bootstub.body_h7.bin and b/panda/board/obj/bootstub.body_h7.bin differ diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin index d4df9df0..de522e7a 100755 Binary files a/panda/board/obj/bootstub.panda.bin and b/panda/board/obj/bootstub.panda.bin differ diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin index e69e3213..d9d7bc03 100755 Binary files a/panda/board/obj/bootstub.panda_h7.bin and b/panda/board/obj/bootstub.panda_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_remote.bin b/panda/board/obj/bootstub.panda_h7_remote.bin index e69e3213..d9d7bc03 100755 Binary files a/panda/board/obj/bootstub.panda_h7_remote.bin and b/panda/board/obj/bootstub.panda_h7_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_jungle_h7.bin b/panda/board/obj/bootstub.panda_jungle_h7.bin index 8e622a11..8bec4707 100755 Binary files a/panda/board/obj/bootstub.panda_jungle_h7.bin and b/panda/board/obj/bootstub.panda_jungle_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_remote.bin b/panda/board/obj/bootstub.panda_remote.bin index d4df9df0..de522e7a 100755 Binary files a/panda/board/obj/bootstub.panda_remote.bin and b/panda/board/obj/bootstub.panda_remote.bin differ diff --git a/panda/board/obj/gitversion.h b/panda/board/obj/gitversion.h index b66631f8..f13d9034 100644 --- a/panda/board/obj/gitversion.h +++ b/panda/board/obj/gitversion.h @@ -1,2 +1,2 @@ extern const uint8_t gitversion[19]; -const uint8_t gitversion[19] = "DEV-a695eda0-DEBUG"; +const uint8_t gitversion[19] = "DEV-5f1e0eeb-DEBUG"; diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed index 14567635..9cf6784f 100644 Binary files a/panda/board/obj/panda.bin.signed and b/panda/board/obj/panda.bin.signed differ diff --git a/panda/board/obj/panda/bootstub.elf b/panda/board/obj/panda/bootstub.elf index f00d413f..03753408 100755 Binary files a/panda/board/obj/panda/bootstub.elf and b/panda/board/obj/panda/bootstub.elf differ diff --git a/panda/board/obj/panda/main.bin b/panda/board/obj/panda/main.bin index b7ba3bd5..b2a5c33c 100755 Binary files a/panda/board/obj/panda/main.bin and b/panda/board/obj/panda/main.bin differ diff --git a/panda/board/obj/panda/main.elf b/panda/board/obj/panda/main.elf index cc212590..600c17fd 100755 Binary files a/panda/board/obj/panda/main.elf and b/panda/board/obj/panda/main.elf differ diff --git a/panda/board/obj/panda_h7.bin.signed b/panda/board/obj/panda_h7.bin.signed index c0592f15..94b96287 100644 Binary files a/panda/board/obj/panda_h7.bin.signed and b/panda/board/obj/panda_h7.bin.signed differ diff --git a/panda/board/obj/panda_h7/bootstub.elf b/panda/board/obj/panda_h7/bootstub.elf index e55d474c..78859407 100755 Binary files a/panda/board/obj/panda_h7/bootstub.elf and b/panda/board/obj/panda_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_h7/main.bin b/panda/board/obj/panda_h7/main.bin index 8a771a85..b44cfdd3 100755 Binary files a/panda/board/obj/panda_h7/main.bin and b/panda/board/obj/panda_h7/main.bin differ diff --git a/panda/board/obj/panda_h7/main.elf b/panda/board/obj/panda_h7/main.elf index 3324136c..8a3b3083 100755 Binary files a/panda/board/obj/panda_h7/main.elf and b/panda/board/obj/panda_h7/main.elf differ diff --git a/panda/board/obj/panda_h7_remote.bin.signed b/panda/board/obj/panda_h7_remote.bin.signed index 3695b05c..f4274a82 100644 Binary files a/panda/board/obj/panda_h7_remote.bin.signed and b/panda/board/obj/panda_h7_remote.bin.signed differ diff --git a/panda/board/obj/panda_h7_remote/bootstub.elf b/panda/board/obj/panda_h7_remote/bootstub.elf index 3decfb56..dfceeb8b 100755 Binary files a/panda/board/obj/panda_h7_remote/bootstub.elf and b/panda/board/obj/panda_h7_remote/bootstub.elf differ diff --git a/panda/board/obj/panda_h7_remote/main.bin b/panda/board/obj/panda_h7_remote/main.bin index a48004c3..e7631368 100755 Binary files a/panda/board/obj/panda_h7_remote/main.bin and b/panda/board/obj/panda_h7_remote/main.bin differ diff --git a/panda/board/obj/panda_h7_remote/main.elf b/panda/board/obj/panda_h7_remote/main.elf index 9999b57b..f7fdd7be 100755 Binary files a/panda/board/obj/panda_h7_remote/main.elf and b/panda/board/obj/panda_h7_remote/main.elf differ diff --git a/panda/board/obj/panda_jungle_h7.bin.signed b/panda/board/obj/panda_jungle_h7.bin.signed index 9182d220..590145b2 100644 Binary files a/panda/board/obj/panda_jungle_h7.bin.signed and b/panda/board/obj/panda_jungle_h7.bin.signed differ diff --git a/panda/board/obj/panda_jungle_h7/bootstub.elf b/panda/board/obj/panda_jungle_h7/bootstub.elf index 5f3538ba..136c1d0e 100755 Binary files a/panda/board/obj/panda_jungle_h7/bootstub.elf and b/panda/board/obj/panda_jungle_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_jungle_h7/main.bin b/panda/board/obj/panda_jungle_h7/main.bin index bef71c7d..f6ac3e84 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.bin and b/panda/board/obj/panda_jungle_h7/main.bin differ diff --git a/panda/board/obj/panda_jungle_h7/main.elf b/panda/board/obj/panda_jungle_h7/main.elf index 13d0fadc..66ad33e6 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.elf and b/panda/board/obj/panda_jungle_h7/main.elf differ diff --git a/panda/board/obj/panda_remote.bin.signed b/panda/board/obj/panda_remote.bin.signed index a314cd3a..b332f4c3 100644 Binary files a/panda/board/obj/panda_remote.bin.signed and b/panda/board/obj/panda_remote.bin.signed differ diff --git a/panda/board/obj/panda_remote/bootstub.elf b/panda/board/obj/panda_remote/bootstub.elf index 0cc87824..98f22165 100755 Binary files a/panda/board/obj/panda_remote/bootstub.elf and b/panda/board/obj/panda_remote/bootstub.elf differ diff --git a/panda/board/obj/panda_remote/main.bin b/panda/board/obj/panda_remote/main.bin index d520249a..5e0e2e42 100755 Binary files a/panda/board/obj/panda_remote/main.bin and b/panda/board/obj/panda_remote/main.bin differ diff --git a/panda/board/obj/panda_remote/main.elf b/panda/board/obj/panda_remote/main.elf index a95d4baa..9d8a7db8 100755 Binary files a/panda/board/obj/panda_remote/main.elf and b/panda/board/obj/panda_remote/main.elf differ diff --git a/panda/board/obj/version b/panda/board/obj/version index d79b060b..e5a2ab49 100644 --- a/panda/board/obj/version +++ b/panda/board/obj/version @@ -1 +1 @@ -DEV-a695eda0-DEBUG \ No newline at end of file +DEV-5f1e0eeb-DEBUG \ No newline at end of file diff --git a/selfdrive/pandad/pandad b/selfdrive/pandad/pandad index 61769d1d..8d99b8d3 100755 Binary files a/selfdrive/pandad/pandad and b/selfdrive/pandad/pandad differ diff --git a/selfdrive/ui/ui b/selfdrive/ui/ui index 4dd62137..4a6cbfb1 100755 Binary files a/selfdrive/ui/ui and b/selfdrive/ui/ui differ diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 7fe1d664..e8861caa 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -237,20 +237,26 @@ void Device::updateBrightness(const UIState &s, const StarPilotUIState &fs) { void Device::updateWakefulness(const UIState &s, const StarPilotUIState &fs) { const StarPilotUIScene &starpilot_scene = fs.starpilot_scene; const QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles; + const bool standby_mode = starpilot_toggles.value("standby_mode").toBool(); + const int screen_timeout = starpilot_toggles.value("screen_timeout").toInt(); + const int screen_timeout_onroad = starpilot_toggles.value("screen_timeout_onroad").toInt(); - bool ignition_just_turned_off = !s.scene.ignition && ignition_on; + bool ignition_state_changed = s.scene.ignition != ignition_on; ignition_on = s.scene.ignition; - if (ignition_on && starpilot_toggles.value("standby_mode").toBool()) { - if (starpilot_scene.wake_up_screen) { - resetInteractiveTimeout(starpilot_toggles.value("screen_timeout").toInt(), starpilot_toggles.value("screen_timeout_onroad").toInt()); - } else { + // Standby mode should extend the current wake window for alerts and status + // changes, but it must not clear the timer every frame or manual wakes will + // collapse immediately. + if (ignition_on && standby_mode && starpilot_scene.wake_up_screen) { + resetInteractiveTimeout(screen_timeout, screen_timeout_onroad); + } + + if (ignition_state_changed) { + if (ignition_on && starpilot_toggles.value("screen_brightness_onroad").toInt() == 0 && !standby_mode) { resetInteractiveTimeout(0, 0); + } else { + resetInteractiveTimeout(screen_timeout, screen_timeout_onroad); } - } else if (ignition_just_turned_off) { - resetInteractiveTimeout(starpilot_toggles.value("screen_timeout").toInt(), starpilot_toggles.value("screen_timeout_onroad").toInt()); - } else if (ignition_on && starpilot_toggles.value("screen_brightness_onroad").toInt() == 0) { - resetInteractiveTimeout(0, 0); } else if (interactive_timeout > 0 && --interactive_timeout == 0) { emit interactiveTimeout(); } diff --git a/starpilot/common/accel_profile.py b/starpilot/common/accel_profile.py new file mode 100644 index 00000000..f4569a15 --- /dev/null +++ b/starpilot/common/accel_profile.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +from openpilot.selfdrive.controls.lib.longitudinal_planner import get_max_accel + +ACCELERATION_PROFILES = { + "STANDARD": 0, + "ECO": 1, + "SPORT": 2, + "SPORT_PLUS": 3, +} + +DECELERATION_PROFILES = { + "STANDARD": 0, + "ECO": 1, + "SPORT": 2, +} + +# MPH = [0.0, 11, 22, 34, 45, 56, 89] +A_CRUISE_MAX_BP_CUSTOM = [0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 40.0] + +CUSTOM_ACCEL_PROFILE_PARAM_SPECS = [ + ("CustomAccelProfile0MPH", 0), + ("CustomAccelProfile11MPH", 11), + ("CustomAccelProfile22MPH", 22), + ("CustomAccelProfile34MPH", 34), + ("CustomAccelProfile45MPH", 45), + ("CustomAccelProfile56MPH", 56), + ("CustomAccelProfile89MPH", 89), +] +CUSTOM_ACCEL_PROFILE_PARAM_KEYS = [key for key, _ in CUSTOM_ACCEL_PROFILE_PARAM_SPECS] +CUSTOM_ACCEL_PROFILE_VALUE_MIN = 0.0 +CUSTOM_ACCEL_PROFILE_VALUE_MAX = 6.0 + +A_CRUISE_MAX_VALS_ECO_EV = [1.25, 1.25, 1.25, 1.25, 1.45, 1.50, 2.00] +A_CRUISE_MAX_VALS_STANDARD_EV = [1.35, 1.35, 1.35, 1.35, 1.60, 1.60, 2.10] +A_CRUISE_MAX_VALS_SPORT_EV = [1.55, 1.55, 1.55, 1.55, 1.84, 1.84, 2.42] +A_CRUISE_MAX_VALS_SPORT_PLUS_EV = [1.7825, 1.7825, 1.7825, 1.7825, 2.116, 2.116, 2.783] +A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2] +A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6] +A_CRUISE_MAX_VALS_ECO_TRUCK = [3.00, 1.05, 0.60, 0.50, 0.50, 0.45, 0.35] +A_CRUISE_MAX_VALS_STANDARD_TRUCK = [6.00, 1.10, 0.70, 0.60, 0.55, 0.45, 0.35] +A_CRUISE_MAX_VALS_SPORT_TRUCK = [6.00, 1.15, 0.75, 0.70, 0.60, 0.50, 0.40] +A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK = [6.00, 1.30, 0.90, 0.80, 0.70, 0.60, 0.45] + + +def akima_interp(x, xp, fp): + if x <= xp[0]: + return fp[0] + if x >= xp[-1]: + return fp[-1] + + i = max(0, min(len(xp) - 2, int(next(idx for idx, bp in enumerate(xp[1:], start=1) if bp >= x) - 1))) + t = (x - xp[i]) / float(xp[i + 1] - xp[i]) + t2 = t * t + t3 = t2 * t + t4 = t2 * t2 + return (fp[i] * (1 - 10 * t3 + 15 * t4 - 6 * t3 * t2) + + fp[i + 1] * (10 * t3 - 15 * t4 + 6 * t3 * t2)) + + +def normalize_acceleration_profile(value): + return _normalize_profile(value, ACCELERATION_PROFILES, ACCELERATION_PROFILES["STANDARD"]) + + +def normalize_deceleration_profile(value): + return _normalize_profile(value, DECELERATION_PROFILES, DECELERATION_PROFILES["STANDARD"]) + + +def get_accel_profile_curve_values(acceleration_profile, ev_tuning=True, truck_tuning=False): + profile = normalize_acceleration_profile(acceleration_profile) + if truck_tuning: + ev_tuning = False + + if profile == ACCELERATION_PROFILES["ECO"]: + if ev_tuning: + return list(A_CRUISE_MAX_VALS_ECO_EV) + if truck_tuning: + return list(A_CRUISE_MAX_VALS_ECO_TRUCK) + return list(A_CRUISE_MAX_VALS_ECO_GAS) + + if profile == ACCELERATION_PROFILES["SPORT"]: + if ev_tuning: + return list(A_CRUISE_MAX_VALS_SPORT_EV) + if truck_tuning: + return list(A_CRUISE_MAX_VALS_SPORT_TRUCK) + return list(A_CRUISE_MAX_VALS_SPORT_GAS) + + if profile == ACCELERATION_PROFILES["SPORT_PLUS"]: + if ev_tuning: + return list(A_CRUISE_MAX_VALS_SPORT_PLUS_EV) + if truck_tuning: + return list(A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK) + return [float(akima_interp(v_ego, [0.0, 5.0, 20.0], [4.0, 4.0, 2.0])) for v_ego in A_CRUISE_MAX_BP_CUSTOM] + + if ev_tuning: + return list(A_CRUISE_MAX_VALS_STANDARD_EV) + if truck_tuning: + return list(A_CRUISE_MAX_VALS_STANDARD_TRUCK) + return [float(get_max_accel(v_ego)) for v_ego in A_CRUISE_MAX_BP_CUSTOM] + + +def interpolate_accel_profile(v_ego, curve_values): + return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, curve_values)) + + +def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False): + return interpolate_accel_profile(v_ego, get_accel_profile_curve_values(ACCELERATION_PROFILES["SPORT_PLUS"], ev_tuning, truck_tuning)) + + +def build_custom_accel_profile_defaults(acceleration_profile, ev_tuning=True, truck_tuning=False): + curve_values = get_accel_profile_curve_values(acceleration_profile, ev_tuning, truck_tuning) + return { + key: float(curve_values[idx]) + for idx, key in enumerate(CUSTOM_ACCEL_PROFILE_PARAM_KEYS) + } + + +def coerce_custom_accel_profile_values(raw_values, acceleration_profile, ev_tuning=True, truck_tuning=False): + defaults = get_accel_profile_curve_values(acceleration_profile, ev_tuning, truck_tuning) + values = [] + for idx, default in enumerate(defaults): + try: + value = float(raw_values[idx]) + except (IndexError, TypeError, ValueError): + value = default + values.append(min(CUSTOM_ACCEL_PROFILE_VALUE_MAX, max(CUSTOM_ACCEL_PROFILE_VALUE_MIN, value))) + return values + + +def _normalize_profile(value, profile_map, fallback): + if isinstance(value, bytes): + value = value.decode("utf-8", errors="replace") + + if isinstance(value, str): + normalized = value.strip().upper().replace("+", "_PLUS").replace(" ", "_") + if normalized in profile_map: + return profile_map[normalized] + + try: + return int(float(value)) + except (TypeError, ValueError): + return fallback diff --git a/starpilot/common/safe_mode.py b/starpilot/common/safe_mode.py index 4ce4ce60..1cd18d20 100644 --- a/starpilot/common/safe_mode.py +++ b/starpilot/common/safe_mode.py @@ -48,6 +48,14 @@ SAFE_MODE_MANAGED_KEYS = ( "AdvancedLongitudinalTune", "EVTuning", "TruckTuning", + "CustomAccelProfile", + "CustomAccelProfile0MPH", + "CustomAccelProfile11MPH", + "CustomAccelProfile22MPH", + "CustomAccelProfile34MPH", + "CustomAccelProfile45MPH", + "CustomAccelProfile56MPH", + "CustomAccelProfile89MPH", "LongitudinalActuatorDelay", "MaxDesiredAcceleration", "StartAccel", diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index 537d0ae5..82e4846a 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -10,6 +10,7 @@ from pathlib import Path from types import SimpleNamespace import cereal.messaging as messaging +import numpy as np from cereal import car, custom, log from opendbc.car import gen_empty_fingerprint @@ -25,6 +26,16 @@ from openpilot.common.constants import CV from openpilot.common.params import Params from openpilot.selfdrive.controls.lib.latcontrol_torque import KP from openpilot.selfdrive.modeld.constants import ModelConstants +from openpilot.starpilot.common.accel_profile import ( + ACCELERATION_PROFILES, + CUSTOM_ACCEL_PROFILE_PARAM_KEYS, + CUSTOM_ACCEL_PROFILE_VALUE_MAX, + CUSTOM_ACCEL_PROFILE_VALUE_MIN, + DECELERATION_PROFILES, + build_custom_accel_profile_defaults, + normalize_acceleration_profile, + normalize_deceleration_profile, +) from openpilot.system.hardware import HARDWARE from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.power_monitoring import VBATT_PAUSE_CHARGING @@ -787,8 +798,19 @@ class StarPilotVariables: toggle.lock_doors_timer = self.get_value("LockDoorsTimer", cast=float, condition=(toggle.car_make == "toyota")) longitudinal_tuning = toggle.openpilot_longitudinal and self.get_value("LongitudinalTune") - toggle.acceleration_profile = self.get_value("AccelerationProfile", cast=float, condition=longitudinal_tuning) - toggle.deceleration_profile = self.get_value("DecelerationProfile", cast=float, condition=longitudinal_tuning) + toggle.acceleration_profile = normalize_acceleration_profile( + self.get_value("AccelerationProfile", cast=None, condition=longitudinal_tuning, default=ACCELERATION_PROFILES["SPORT"]) + ) + toggle.deceleration_profile = normalize_deceleration_profile( + self.get_value("DecelerationProfile", cast=None, condition=longitudinal_tuning, default=DECELERATION_PROFILES["ECO"]) + ) + toggle.custom_accel_profile = self.get_value("CustomAccelProfile", condition=longitudinal_tuning) + custom_accel_defaults = build_custom_accel_profile_defaults(toggle.acceleration_profile, toggle.ev_tuning, toggle.truck_tuning) + toggle.custom_accel_profile_values = [ + self.get_value(key, cast=float, condition=longitudinal_tuning, default=custom_accel_defaults[key], + min=CUSTOM_ACCEL_PROFILE_VALUE_MIN, max=CUSTOM_ACCEL_PROFILE_VALUE_MAX) + for key in CUSTOM_ACCEL_PROFILE_PARAM_KEYS + ] toggle.human_acceleration = self.get_value("HumanAcceleration", condition=longitudinal_tuning) toggle.human_following = self.get_value("HumanFollowing", condition=longitudinal_tuning) toggle.human_lane_changes = has_radar and self.get_value("HumanLaneChanges", condition=longitudinal_tuning) diff --git a/starpilot/controls/lib/starpilot_acceleration.py b/starpilot/controls/lib/starpilot_acceleration.py index 041e9d73..ea7274c5 100644 --- a/starpilot/controls/lib/starpilot_acceleration.py +++ b/starpilot/controls/lib/starpilot_acceleration.py @@ -3,6 +3,14 @@ import numpy as np from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel +from openpilot.starpilot.common.accel_profile import ( + ACCELERATION_PROFILES, + DECELERATION_PROFILES, + coerce_custom_accel_profile_values, + get_accel_profile_curve_values, + get_max_allowed_accel as get_profile_max_allowed_accel, + interpolate_accel_profile, +) from openpilot.starpilot.common.starpilot_variables import CITY_SPEED_LIMIT def cubic_interp(x, xp, fp): @@ -45,56 +53,20 @@ def akima_interp(x, xp, fp): A_CRUISE_MIN_ECO = A_CRUISE_MIN / 2 A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2 - # MPH = [0.0, 11, 22, 34, 45, 56, 89] -A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.] -A_CRUISE_MAX_VALS_ECO_EV = [1.25, 1.25, 1.25, 1.25, 1.45, 1.50, 2.00] -A_CRUISE_MAX_VALS_STANDARD_EV = [1.35, 1.35, 1.35, 1.35, 1.60, 1.60, 2.10] -A_CRUISE_MAX_VALS_SPORT_EV = [1.55, 1.55, 1.55, 1.55, 1.84, 1.84, 2.42] -A_CRUISE_MAX_VALS_SPORT_PLUS_EV = [1.7825, 1.7825, 1.7825, 1.7825, 2.116, 2.116, 2.783] -A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2] -A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6] -A_CRUISE_MAX_VALS_ECO_TRUCK = [3.00, 1.05, 0.60, 0.50, 0.50, 0.45, 0.35] -A_CRUISE_MAX_VALS_STANDARD_TRUCK = [6.00, 1.10, 0.70, 0.60, 0.55, 0.45, 0.35] -A_CRUISE_MAX_VALS_SPORT_TRUCK = [6.00, 1.15, 0.75, 0.70, 0.60, 0.50, 0.40] -A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK = [6.00, 1.30, 0.90, 0.80, 0.70, 0.60, 0.45] - -ACCELERATION_PROFILES = { - "STANDARD": 0, - "ECO": 1, - "SPORT": 2, - "SPORT_PLUS": 3 -} - -DECELERATION_PROFILES = { - "STANDARD": 0, - "ECO": 1, - "SPORT": 2 -} - def get_max_accel_eco(v_ego, ev_tuning=True, truck_tuning=False): - if ev_tuning: - cruise_vals = A_CRUISE_MAX_VALS_ECO_EV - elif truck_tuning: - cruise_vals = A_CRUISE_MAX_VALS_ECO_TRUCK - else: - cruise_vals = A_CRUISE_MAX_VALS_ECO_GAS - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals)) + return interpolate_accel_profile(v_ego, get_accel_profile_curve_values(ACCELERATION_PROFILES["ECO"], ev_tuning, truck_tuning)) def get_max_accel_sport(v_ego, ev_tuning=True, truck_tuning=False): - if ev_tuning: - cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV - elif truck_tuning: - cruise_vals = A_CRUISE_MAX_VALS_SPORT_TRUCK - else: - cruise_vals = A_CRUISE_MAX_VALS_SPORT_GAS - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals)) + return interpolate_accel_profile(v_ego, get_accel_profile_curve_values(ACCELERATION_PROFILES["SPORT"], ev_tuning, truck_tuning)) def get_max_accel_standard(v_ego, ev_tuning=True, truck_tuning=False): - if ev_tuning: - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_EV)) - if truck_tuning: - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_TRUCK)) - return get_max_accel(v_ego) + if ev_tuning or truck_tuning: + return interpolate_accel_profile(v_ego, get_accel_profile_curve_values(ACCELERATION_PROFILES["STANDARD"], ev_tuning, truck_tuning)) + return float(get_max_accel(v_ego)) + +def get_max_accel_custom(v_ego, custom_curve, acceleration_profile, ev_tuning=True, truck_tuning=False): + curve_values = coerce_custom_accel_profile_values(custom_curve, acceleration_profile, ev_tuning, truck_tuning) + return interpolate_accel_profile(v_ego, curve_values) def get_max_accel_low_speeds(max_accel, v_cruise): return float(akima_interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel])) @@ -103,11 +75,7 @@ def get_max_accel_ramp_off(max_accel, v_cruise, v_ego): return float(akima_interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.5, 1.0, max_accel])) def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False): - if ev_tuning: - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_EV)) - if truck_tuning: - return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK)) - return float(akima_interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018 + return float(get_profile_max_allowed_accel(v_ego, ev_tuning, truck_tuning)) class StarPilotAcceleration: def __init__(self, StarPilotPlanner): @@ -121,8 +89,12 @@ class StarPilotAcceleration: sport_gear = sm["starpilotCarState"].sportGear ev_tuning = getattr(starpilot_toggles, "ev_tuning", True) truck_tuning = getattr(starpilot_toggles, "truck_tuning", False) + custom_accel_profile = getattr(starpilot_toggles, "custom_accel_profile", False) + custom_accel_profile_values = getattr(starpilot_toggles, "custom_accel_profile_values", []) - if sm["starpilotCarState"].trafficModeEnabled: + if custom_accel_profile: + self.max_accel = get_max_accel_custom(v_ego, custom_accel_profile_values, starpilot_toggles.acceleration_profile, ev_tuning, truck_tuning) + elif sm["starpilotCarState"].trafficModeEnabled: self.max_accel = get_max_accel_standard(v_ego, ev_tuning, truck_tuning) elif starpilot_toggles.map_acceleration and (eco_gear or sport_gear): if eco_gear: diff --git a/starpilot/system/the_pond/assets/components/tools/device_settings.js b/starpilot/system/the_pond/assets/components/tools/device_settings.js index f09b975d..ba4cb601 100644 --- a/starpilot/system/the_pond/assets/components/tools/device_settings.js +++ b/starpilot/system/the_pond/assets/components/tools/device_settings.js @@ -6,6 +6,7 @@ const endpointOptionsInflight = {} // Plain variables — scheduling/routing flags that must NOT be reactive let syncScheduled = false let lastParams = null +const DYNAMIC_DEFAULT_DEP_KEYS = new Set(["AccelerationProfile", "EVTuning", "TruckTuning"]) // Module-level state (persists across route changes) const state = reactive({ @@ -141,6 +142,21 @@ async function fetchDefaultValues() { } } +async function refreshParamsAndDefaults() { + await fetchDefaultValues() + + try { + const valuesRes = await fetch("/api/params/all") + if (valuesRes.ok) { + const data = await valuesRes.json() + state.values = data || {} + } + } catch (e) { + console.error("Failed to refresh param values:", e) + } + scheduleSyncInputs() +} + async function fetchLayoutAndParams() { state.loadingLayout = true state.loadingValues = true @@ -176,14 +192,13 @@ async function fetchLayoutAndParams() { // Pull params once at page load; local state handles subsequent edits. try { - const valuesRes = await fetch("/api/params/all") - - const data = await valuesRes.json() - state.values = data - if (!(await fetchDefaultValues())) { state.defaultValues = {} } + + const valuesRes = await fetch("/api/params/all") + const data = await valuesRes.json() + state.values = data } catch (e) { console.error("Failed to fetch param values:", e) state.defaultValues = {} @@ -520,7 +535,11 @@ async function updateParam(key, elType) { const updated = (data.updated && typeof data.updated === "object") ? data.updated : {} state.values = { ...state.values, [key]: formattedVal, ...updated } showParamSnackbar(data.message || `Parameter '${key}' updated.`) - scheduleSyncInputs() + if (DYNAMIC_DEFAULT_DEP_KEYS.has(key)) { + await refreshParamsAndDefaults() + } else { + scheduleSyncInputs() + } } else { revertInput(key, current, elType) showParamSnackbar(data.error || "Failed to update parameter", "error") diff --git a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json index 746c60b7..75f8784a 100644 --- a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json @@ -254,6 +254,99 @@ "ui_type": "toggle", "parent_key": "AdvancedLongitudinalTune" }, + { + "key": "CustomAccelProfile", + "label": "Custom Accel Profile", + "description": "Replace the built-in acceleration profile with your own per-speed max-acceleration values. Breakpoint speeds stay fixed, and the starting defaults mirror the currently selected acceleration profile plus EV or Truck tuning.", + "data_type": "bool", + "ui_type": "toggle", + "is_parent_toggle": true, + "parent_key": "AdvancedLongitudinalTune" + }, + { + "key": "CustomAccelProfile0MPH", + "label": "0 mph", + "description": "Max acceleration in m/s² at the fixed 0 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile11MPH", + "label": "11 mph", + "description": "Max acceleration in m/s² at the fixed 11 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile22MPH", + "label": "22 mph", + "description": "Max acceleration in m/s² at the fixed 22 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile34MPH", + "label": "34 mph", + "description": "Max acceleration in m/s² at the fixed 34 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile45MPH", + "label": "45 mph", + "description": "Max acceleration in m/s² at the fixed 45 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile56MPH", + "label": "56 mph", + "description": "Max acceleration in m/s² at the fixed 56 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, + { + "key": "CustomAccelProfile89MPH", + "label": "89 mph", + "description": "Max acceleration in m/s² at the fixed 89 mph breakpoint.", + "data_type": "float", + "ui_type": "numeric", + "min": 0.0, + "max": 6.0, + "step": 0.01, + "precision": 2, + "parent_key": "CustomAccelProfile" + }, { "key": "LongitudinalActuatorDelay", "label": "Actuator Delay", diff --git a/starpilot/system/the_pond/the_pond.py b/starpilot/system/the_pond/the_pond.py index 994ab6f4..67247d38 100644 --- a/starpilot/system/the_pond/the_pond.py +++ b/starpilot/system/the_pond/the_pond.py @@ -41,6 +41,11 @@ from openpilot.system.version import get_build_metadata from panda import Panda from openpilot.starpilot.assets.theme_manager import HOLIDAY_THEME_PATH, THEME_COMPONENT_PARAMS +from openpilot.starpilot.common.accel_profile import ( + CUSTOM_ACCEL_PROFILE_PARAM_KEYS, + build_custom_accel_profile_defaults, + normalize_acceleration_profile, +) from openpilot.starpilot.common.maps_catalog import ( MAPS_CATALOG, MAP_SCHEDULE_OPTIONS, @@ -629,6 +634,8 @@ _TROUBLESHOOT_ADVANCED_LONGITUDINAL_KEYS = [ "AdvancedLongitudinalTune", "EVTuning", "TruckTuning", + "CustomAccelProfile", + *CUSTOM_ACCEL_PROFILE_PARAM_KEYS, "LongitudinalActuatorDelay", "StartAccel", "VEgoStarting", @@ -1663,6 +1670,7 @@ def _get_layout_type_overrides(): _cached_allowed_keys = None _cached_param_types = None _cached_default_values = None +_cached_static_default_values = None def _get_param_type_info(): global _cached_allowed_keys, _cached_param_types @@ -1699,15 +1707,18 @@ def _get_param_type_info(): _cached_param_types = types return _cached_allowed_keys, _cached_param_types -def _get_default_param_values(): - global _cached_default_values - if _cached_default_values is None: - _cached_default_values = { +def _get_static_default_param_values(): + global _cached_static_default_values + if _cached_static_default_values is None: + _cached_static_default_values = { key: default_val for key, default_val, _, _ in starpilot_default_params if key not in EXCLUDED_KEYS } - default_values = dict(_cached_default_values) + return _cached_static_default_values + +def _get_default_param_values(): + default_values = dict(_get_static_default_param_values()) default_values.update(_get_runtime_default_param_overrides()) return default_values @@ -1790,6 +1801,7 @@ def _has_runtime_default_value(key, raw_value): def _get_runtime_default_param_overrides(): overrides = {} + static_defaults = _get_static_default_param_values() for key, stock_key in _RUNTIME_DEFAULT_STOCK_KEYS.items(): stock_raw = _safe_params_get_live_raw(stock_key) @@ -1798,40 +1810,60 @@ def _get_runtime_default_param_overrides(): overrides[stock_key] = stock_raw cp_bytes = _safe_params_get_live_raw("CarParamsPersistent") - if not cp_bytes: - return overrides + if cp_bytes: + try: + with car.CarParams.from_bytes(cp_bytes) as cp: + overrides["EVTuning"] = default_ev_tuning_enabled(cp) - try: - with car.CarParams.from_bytes(cp_bytes) as cp: - overrides["EVTuning"] = default_ev_tuning_enabled(cp) + car_param_defaults = { + "SteerDelay": getattr(cp, "steerActuatorDelay", None), + "SteerRatio": getattr(cp, "steerRatio", None), + "LongitudinalActuatorDelay": getattr(cp, "longitudinalActuatorDelay", None), + "StartAccel": getattr(cp, "startAccel", None), + "StopAccel": getattr(cp, "stopAccel", None), + "StoppingDecelRate": getattr(cp, "stoppingDecelRate", None), + "VEgoStarting": getattr(cp, "vEgoStarting", None), + "VEgoStopping": getattr(cp, "vEgoStopping", None), + } - car_param_defaults = { - "SteerDelay": getattr(cp, "steerActuatorDelay", None), - "SteerRatio": getattr(cp, "steerRatio", None), - "LongitudinalActuatorDelay": getattr(cp, "longitudinalActuatorDelay", None), - "StartAccel": getattr(cp, "startAccel", None), - "StopAccel": getattr(cp, "stopAccel", None), - "StoppingDecelRate": getattr(cp, "stoppingDecelRate", None), - "VEgoStarting": getattr(cp, "vEgoStarting", None), - "VEgoStopping": getattr(cp, "vEgoStopping", None), - } + for key, value in car_param_defaults.items(): + if key in overrides or value is None: + continue + if key not in _RUNTIME_DEFAULT_ZERO_OK_KEYS and float(value) == 0.0: + continue + overrides[key] = value + except Exception: + pass - for key, value in car_param_defaults.items(): - if key in overrides or value is None: - continue - if key not in _RUNTIME_DEFAULT_ZERO_OK_KEYS and float(value) == 0.0: - continue - overrides[key] = value - except Exception: - pass + ev_tuning_raw = _safe_params_get_live_raw("EVTuning") + truck_tuning_raw = _safe_params_get_live_raw("TruckTuning") + acceleration_profile_raw = _safe_params_get_live_raw("AccelerationProfile") + + ev_tuning = _coerce_param_value( + ev_tuning_raw if not _is_blank_param_raw(ev_tuning_raw) else overrides.get("EVTuning", static_defaults.get("EVTuning", "0")), + bool, + ) + truck_tuning = _coerce_param_value( + truck_tuning_raw if not _is_blank_param_raw(truck_tuning_raw) else static_defaults.get("TruckTuning", "0"), + bool, + ) + if truck_tuning: + ev_tuning = False + + acceleration_profile = normalize_acceleration_profile( + acceleration_profile_raw if not _is_blank_param_raw(acceleration_profile_raw) else static_defaults.get("AccelerationProfile", "0") + ) + overrides.update(build_custom_accel_profile_defaults(acceleration_profile, ev_tuning, truck_tuning)) return overrides -def _get_current_param_value(key, value_type): - safe_type = value_type or str - if safe_type == bool: - return params.get_bool(key) - return _coerce_param_value(params.get(key), safe_type) +def _get_current_param_value(key, value_type, defaults_lookup=None): + raw_value = _safe_params_get_live_raw(key) + if _is_blank_param_raw(raw_value): + if defaults_lookup is None: + defaults_lookup = _get_default_param_values() + raw_value = defaults_lookup.get(key) + return _coerce_param_value(raw_value, value_type) def _serialize_param_write_value(raw_value): if isinstance(raw_value, bool): @@ -3108,6 +3140,22 @@ def setup(app): if key == "AutomaticUpdates" and params.get_bool("IsOnroad"): return jsonify({"error": "Cannot change Automatic Updates while driving."}), 403 + if key in {"EVTuning", "TruckTuning"}: + enabled = str_val.strip() in ("1", "true", "True") + params.put_bool(key, enabled) + + updated = {key: enabled} + if enabled: + other_key = "TruckTuning" if key == "EVTuning" else "EVTuning" + params.put_bool(other_key, False) + updated[other_key] = False + + update_starpilot_toggles() + return jsonify({ + "message": f"Parameter '{key}' updated successfully.", + "updated": updated, + }), 200 + if key == "CarMake": catalog = _get_fingerprint_catalog() normalized_make = _normalize_fingerprint_make_key(str_val) @@ -3225,25 +3273,13 @@ def setup(app): def get_all_params(): _enforce_cancel_remap_lkas_lock() allowed_keys, types = _get_param_type_info() + defaults_lookup = _get_default_param_values() result = {} for key in allowed_keys: t = types.get(key, str) try: - if t == bool: - result[key] = params.get_bool(key) - else: - raw = params.get(key) - raw_str = raw.decode("utf-8", errors="replace") if isinstance(raw, bytes) else str(raw or "") - - if not raw_str: - result[key] = 0.0 if t == float else (0 if t == int else "") - elif t == float: - result[key] = float(raw_str) - elif t == int: - result[key] = int(float(raw_str)) - else: - result[key] = raw_str + result[key] = _get_current_param_value(key, t, defaults_lookup) except Exception: result[key] = None diff --git a/system/camerad/camerad b/system/camerad/camerad index f8db1756..e0ee9b26 100755 Binary files a/system/camerad/camerad and b/system/camerad/camerad differ diff --git a/system/loggerd/bootlog b/system/loggerd/bootlog index 152cc1c7..802b9299 100755 Binary files a/system/loggerd/bootlog and b/system/loggerd/bootlog differ diff --git a/system/loggerd/encoderd b/system/loggerd/encoderd index 948fc27f..9da8f561 100755 Binary files a/system/loggerd/encoderd and b/system/loggerd/encoderd differ diff --git a/system/loggerd/loggerd b/system/loggerd/loggerd index 2365adf4..02d04f06 100755 Binary files a/system/loggerd/loggerd and b/system/loggerd/loggerd differ