various fixes after upstream sync

This commit is contained in:
infiniteCable2
2026-07-25 18:39:58 +02:00
parent 3c983e5cdd
commit 09f191d0a0
21 changed files with 87 additions and 85 deletions
+5 -10
View File
@@ -60,16 +60,11 @@ class PIDController:
class MultiplicativeUnwindPID:
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100, min_cmd=1e-10, ki_red_time=1.0):
if isinstance(k_p, Number):
k_p = [[0], [k_p]]
if isinstance(k_i, Number):
k_i = [[0], [k_i]]
if isinstance(k_d, Number):
k_d = [[0], [k_d]]
self._k_p = k_p
self._k_i = k_i
self._k_d = k_d
def __init__(self, k_p: Gain, k_i: Gain, k_f=0., k_d: Gain = 0., pos_limit=1e308, neg_limit=-1e308,
rate=100, min_cmd=1e-10, ki_red_time=1.0):
self._k_p = ([0], [k_p]) if isinstance(k_p, (int, float)) else k_p
self._k_i = ([0], [k_i]) if isinstance(k_i, (int, float)) else k_i
self._k_d = ([0], [k_d]) if isinstance(k_d, (int, float)) else k_d
self.k_f = float(k_f)
self.pos_limit = pos_limit
self.neg_limit = neg_limit
+7 -7
View File
@@ -34,7 +34,7 @@ class PT2Filter:
=> Zeitbereich: y[k] = -a1*y[k-1] - a2*y[k-2] + b0*u[k] + b1*u[k-1] + b2*u[k-2].
"""
Ts = dt
wd = w0
alpha = 2.0 / Ts
@@ -66,14 +66,14 @@ class PT2Filter:
return (a1d, a2d, b0d, b1d, b2d)
def sync(self, target: float):
steps = compute_saturation_steps(self.w0, self.zeta, self.dt)
for i in range(1, steps + 1):
update(target)
steps = self.compute_saturation_steps(self.w0, self.zeta, self.dt)
for _ in range(steps):
self.update(target)
def compute_saturation_steps(self, w0: float, zeta: float, dt: float) -> int:
"""
Berechnet eine Abschätzung der Schritte, bis der Filter (95% des Endwerts) erreicht ist.
Wir nutzen hier die Abschätzung:
T_s = 4 / (zeta * w0)
und setzen N = T_s / dt.
@@ -103,10 +103,10 @@ class PT2Filter:
+ self.b1 * self.u1
+ self.b2 * self.u2
)
self.y2 = self.y1
self.y1 = y
self.u2 = self.u1
self.u1 = u
return y
+1 -3
View File
@@ -19,13 +19,11 @@ from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
from openpilot.selfdrive.controls.lib.latcontrol_curvature import LatControlCurvature
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from openpilot.selfdrive.controls.lib.latcontrol_curvature import LatControlCurvature
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import get_T_FOLLOW
from openpilot.common.pt2 import PT2Filter
from openpilot.common.realtime import DT_CTRL
from openpilot.sunnypilot.selfdrive.controls.controlsd_ext import ControlsExt
@@ -119,7 +117,7 @@ class Controls(ControlsExt):
self.force_rhd_for_bsm = self.params.get_bool("ForceRHDForBSM")
self.enable_long_comfort_mode = self.params.get_bool("EnableLongComfortMode")
self.disable_car_steer_alerts = self.params.get_bool("DisableCarSteerAlerts")
def state_control(self):
CS = self.sm['carState']
@@ -1,5 +1,4 @@
import math
import numpy as np
from openpilot.cereal import log
from openpilot.common.pid import MultiplicativeUnwindPID
@@ -25,7 +24,7 @@ class LatControlCurvature(LatControl):
else:
self.pid = None
self.kf = 1.
def set_pid_enabled(self, enabled: bool) -> None:
self.enable_pid = enabled
@@ -19,7 +19,7 @@
# def reset(self):
# super().reset()
# self.pid.reset()
#
#
# def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
# pid_log = log.ControlsState.LateralCurvatureState.new_message()
# if not active:
@@ -35,10 +35,10 @@
# actual_curvature = np.interp(CS.vEgo, [2.0, 5.0], [actual_curvature_vm, actual_curvature_pose])
#
# desired_curvature_corr = desired_curvature - roll_compensation
#
#
# pid_log.error = float(desired_curvature - actual_curvature)
# freeze_integrator = steer_limited_by_safety or CS.vEgo < 5 or CS.steeringPressed
#
#
# pid_curvature = self.pid.update(pid_log.error, feedforward=desired_curvature_corr, speed=CS.vEgo,
# freeze_integrator=freeze_integrator, override=CS.steeringPressed)
#
@@ -32,7 +32,7 @@ def get_max_accel(v_ego):
def get_coast_accel(pitch):
return np.sin(pitch) * -5.65 - 0.3 # fitted from data using xx/projects/allow_throttle/compute_coast_accel.py
def get_lead_distance(radarState):
if radarState.leadOne.present and (not radarState.leadTwo.present or radarState.leadOne.dRel < radarState.leadTwo.dRel):
@@ -40,7 +40,7 @@ def get_lead_distance(radarState):
if radarState.leadTwo.present:
return radarState.leadTwo.dRel
return 0
def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
"""
@@ -232,7 +232,7 @@ class TestCurvatureDController:
def counting(*args, **kwargs):
call_count["n"] += 1
return original(*args, **kwargs)
CurvatureDLookup.interp_curve_value = counting
CurvatureDLookup.interp_curve_value = counting # ty: ignore[invalid-assignment]
try:
# First call: cache miss, calls interp_curve_value once
first = controller.get_correction(32e-6, v_ego)
+7 -7
View File
@@ -550,7 +550,7 @@ class CurvatureEstimator(CurvatureDLookup):
self._restore_cached_params()
self.update_use_params(force=True)
cloudlog.info(f"curvatured init brand={self.CP.brand} fingerprint={self.CP.carFingerprint} "
cloudlog.info(f"curvatured init brand={self.CP.brand} fingerprint={self.CP.carFingerprint} " +
f"steerControlType={self.CP.steerControlType} history={HISTORY:.2f}s")
@staticmethod
@@ -596,8 +596,8 @@ class CurvatureEstimator(CurvatureDLookup):
self.use_params = self.enable_curvatured and self.CP.brand in ALLOWED_CARS and \
self.CP.steerControlType == car.CarParams.SteerControlType.curvature
if self.prev_use_params != self.use_params:
cloudlog.info(f"curvatured use_params={self.use_params} toggle={self.enable_curvatured} "
f"brand={self.CP.brand} allowed={self.CP.brand in ALLOWED_CARS} "
cloudlog.info(f"curvatured use_params={self.use_params} toggle={self.enable_curvatured} " +
f"brand={self.CP.brand} allowed={self.CP.brand in ALLOWED_CARS} " +
f"steerControlType={self.CP.steerControlType}")
self.prev_use_params = self.use_params
if not self.use_params:
@@ -960,10 +960,10 @@ class CurvatureEstimator(CurvatureDLookup):
self.last_status_log_t = t
checks = sm.all_checks(tracked_services) if valid is None else valid
cloudlog.info(f"curvatured status use_params={self.use_params} checks={checks} "
f"lag={self.lag:.3f} total_points={int(round(float(self.counts.sum())))} "
f"bucket={self.current_bucket} bucket_points={self.current_bucket_points} "
f"corr={self.current_correction:.8f} cal={self.calibration_percent(self.counts)} "
cloudlog.info(f"curvatured status use_params={self.use_params} checks={checks} " +
f"lag={self.lag:.3f} total_points={int(round(float(self.counts.sum())))} " +
f"bucket={self.current_bucket} bucket_points={self.current_bucket_points} " +
f"corr={self.current_correction:.8f} cal={self.calibration_percent(self.counts)} " +
f"invalid={invalid} not_alive={not_alive}")
+1 -1
View File
@@ -276,4 +276,4 @@ def main():
if __name__ == "__main__":
main()
main()
@@ -1,5 +1,6 @@
import numpy as np
from openpilot.cereal import custom
from opendbc.car.structs import car
from opendbc.car.volkswagen.values import CAR
@@ -233,7 +234,7 @@ class TestCurvatureEstimator:
speed_idx = len(CurvatureDLookup.SPEED_ANCHORS) - 1
v_ego = float(CurvatureDLookup.SPEED_ANCHORS[speed_idx])
required = CurvatureDLookup.required_support_bucket_count(speed_idx)
selected_indices = list(range(0, required - 1)) + [required]
selected_indices = list(range(required - 1)) + [required]
for bucket_idx in selected_indices:
desired_curvature = float(CurvatureDLookup.CURVATURE_BUCKET_CENTERS[bucket_idx])
@@ -262,7 +263,6 @@ class TestCurvatureEstimator:
def test_outer_learned_buckets_stay_invalid_for_apply(self):
speed_idx = len(CurvatureDLookup.SPEED_ANCHORS) - 1
v_ego = float(CurvatureDLookup.SPEED_ANCHORS[speed_idx])
outer_idx = len(CurvatureDLookup.CURVATURE_BUCKET_CENTERS) - 1
counts = np.zeros(CurvatureDLookup.bucket_shape(), dtype=np.float32)
bias = np.zeros(CurvatureDLookup.bucket_shape(), dtype=np.float32)
+1 -1
View File
@@ -289,4 +289,4 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Process the --demo argument.')
parser.add_argument('--demo', action='store_true', help='A boolean for demo mode.')
args = parser.parse_args()
main(demo=args.demo)
main(demo=args.demo)
@@ -1,4 +1,3 @@
from openpilot.cereal import log
from openpilot.common.params import Params, UnknownKeyName
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.list_view import toggle_item
@@ -16,7 +15,8 @@ DESCRIPTIONS = {
"Enables curvature PID post-processing additionally to QFK curvature offset"
),
"EnableCurvatureD": tr_noop(
"Learns speed- and curvature-dependent steering corrections around center for dynamic steering behavior. Experimental and only used on curvature-based steering paths."
"Learns speed- and curvature-dependent steering corrections around center for dynamic steering behavior. " +
"Experimental and only used on curvature-based steering paths."
),
"ShowDynamicSteeringLearnerGraph": tr_noop(
"Display the current dynamic steering learner fit, marker, and status information in the onroad UI."
@@ -100,15 +100,15 @@ class SoftwareLayout(Widget):
self._version_item.action_item.set_text(current_desc)
self._version_item.set_description(current_release_notes)
# Update download button visibility and state
self._download_btn.set_visible(ui_state.is_offroad())
self._force_download_btn.set_visible(ui_state.is_offroad())
updater_state = ui_state.params.get("UpdaterState") or "idle"
failed_count = ui_state.params.get("UpdateFailedCount") or 0
fetch_available = ui_state.params.get_bool("UpdaterFetchAvailable")
update_available = ui_state.params.get_bool("UpdateAvailable")
# Update download button visibility and state
self._download_btn.set_visible(ui_state.is_offroad())
self._force_download_btn.set_visible(ui_state.is_offroad() and not update_available)
if updater_state != "idle":
# Updater responded
self._waiting_for_updater = False
@@ -189,7 +189,7 @@ class SoftwareLayout(Widget):
self._force_download_btn.action_item.set_enabled(False)
self._waiting_for_updater = True
self._waiting_start_ts = time.monotonic()
os.system("pkill -SIGUSR2 -f system.updated.updated")
subprocess.run("pkill -SIGUSR2 -f openpilot.system.updated.updated", shell=True)
def _on_select_branch(self):
# Get available branches and order
@@ -1,9 +1,6 @@
from openpilot.cereal import log
from openpilot.system.ui.widgets.scroller import NavScroller
from openpilot.selfdrive.ui.mici.widgets.button import BigParamControl, BigMultiParamToggle
from openpilot.selfdrive.ui.mici.widgets.button import BigParamControl
from openpilot.system.ui.lib.application import gui_app
from openpilot.selfdrive.ui.layouts.settings.common import restart_needed_callback
from openpilot.selfdrive.ui.ui_state import ui_state
@@ -25,7 +22,7 @@ class ICTogglesLayoutMici(NavScroller):
enable_accel_bar = BigParamControl("Enable Accel Bar", "ShowAccelBar")
enable_curvatured = BigParamControl("Enable Dynamic Steering Learner", "EnableCurvatureD")
show_curvatured_graph = BigParamControl("Show Dynamic Steering Learner Graph", "ShowDynamicSteeringLearnerGraph")
self._scroller.add_widgets([
enable_curvature_correction,
enable_long_comfort_mode,
@@ -24,11 +24,11 @@ class SettingsLayout(NavScroller):
toggles_panel = TogglesLayoutMici()
toggles_btn = SettingsBigButton("toggles", "", gui_app.texture("icons_mici/settings.png", 64, 64))
toggles_btn.set_click_callback(lambda: gui_app.push_widget(toggles_panel))
ictoggles_panel = ICTogglesLayoutMici()
ictoggles_btn = SettingsBigButton("ictoggles", "", gui_app.texture("icons_mici/settings.png", 64, 64))
ictoggles_btn.set_click_callback(lambda: gui_app.push_widget(ictoggles_panel))
network_panel = NetworkLayoutMici()
network_btn = SettingsBigButton("network", "", gui_app.texture("icons_mici/settings/network/wifi_strength_full.png", 76, 56))
network_btn.set_click_callback(lambda: gui_app.push_widget(network_panel))
@@ -124,7 +124,6 @@ class DynamicSteeringLearnerGraphMici(Widget):
lcp = sm["liveCurvatureParameters"]
lcp_frame = sm.recv_frame["liveCurvatureParameters"]
car_state = sm["carState"]
controls_state = sm["controlsState"]
fit_corrections = np.zeros(CurvatureDLookup.bucket_shape(), dtype=np.float32)
fit_valid = np.zeros(CurvatureDLookup.bucket_shape(), dtype=bool)
@@ -194,7 +193,8 @@ class DynamicSteeringLearnerGraphMici(Widget):
0.0, 1.0,
))
marker_x = plot_rect.x + marker_alpha * plot_rect.width
marker_correction = float(np.interp(abs(desired_curvature), np.abs(self._plot_x), self._cached_fit_curve))
center_idx = len(self._plot_x) // 2
marker_correction = float(np.interp(abs(desired_curvature), self._plot_x[center_idx:], corrections[center_idx:]))
marker_y = self._map_y(plot_rect, marker_correction, min_y, max_y)
rl.draw_circle(int(marker_x), int(marker_y), 5, self._marker_glow_color)
rl.draw_circle(int(marker_x), int(marker_y), 3, self._marker_color)
@@ -1,7 +1,8 @@
import pyray as rl
import time
from dataclasses import dataclass
import pyray as rl
from openpilot.common.params import Params
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import gui_app, FontWeight
@@ -16,19 +17,21 @@ class BatteryPanelConfig:
line_height: int = 48 # Basis-Zeilenhöhe
label_width: int = 320
text_margin: int = 25 # Abstand Label → Wert
CONFIG = BatteryPanelConfig()
class BatteryDetails(Widget):
def __init__(self) -> None:
super().__init__()
self._params = Params()
self._capacity: float = 0.0
self._charge: float = 0.0
self._soc: float = 0.0
self._temperature: float = 0.0
self._heaterActive: bool = False
self._heater_active: bool = False
self._voltage: float = 0.0
self._current: float = 0.0
self._power: float = 0.0
@@ -37,21 +40,23 @@ class BatteryDetails(Widget):
self._panel_bg: rl.Color = rl.Color(0, 0, 0, 128)
self._label_color: rl.Color = rl.Color(220, 220, 220, 255)
self._value_color: rl.Color = rl.Color(255, 255, 255, 255)
self._display_enabled: bool = False
self._data_valid: bool = False
self._param_update_time: float = 0.0
self._update_params()
def _update_state(self) -> None:
if time.monotonic() - self._param_update_time > 2.0:
self._update_params()
if not self._display_enabled:
return
sm = ui_state.sm
if sm.recv_frame["carState"] < ui_state.started_frame:
if (sm.recv_frame["carStateIC"] < ui_state.started_frame or
not sm.valid["carStateIC"] or not sm.alive["carStateIC"]):
self._reset_values()
return
@@ -66,11 +71,12 @@ class BatteryDetails(Widget):
self._voltage = float(battery_data.voltage)
self._current = float(battery_data.current)
self._power = float(battery_data.power)
self._data_valid = True
def _update_params(self) -> None:
self._param_update_time = time.monotonic()
self._display_enabled = self._params.get_bool("BatteryDetails")
def _reset_values(self) -> None:
self._capacity = 0.0
self._charge = 0.0
@@ -80,9 +86,10 @@ class BatteryDetails(Widget):
self._voltage = 0.0
self._current = 0.0
self._power = 0.0
self._data_valid = False
def _render(self, rect: rl.Rectangle) -> None:
if not self._display_enabled:
if not self._display_enabled or not self._data_valid:
return
scale = CONFIG.scale_factor
@@ -103,7 +110,6 @@ class BatteryDetails(Widget):
label_width = CONFIG.label_width
text_margin = CONFIG.text_margin
column_spacing = panel_width // 2 - 40
value_width = column_spacing - label_width - text_margin
labels = [
"Capacity:", "Charge:", "SoC:", "Temperature:",
@@ -121,9 +127,7 @@ class BatteryDetails(Widget):
f"{self._power:.2f} kW",
]
rl.draw_text_ex
for i, (label, value) in enumerate(zip(labels, values)):
for i, (label, value) in enumerate(zip(labels, values, strict=True)):
column = i // 4
row = i % 4
@@ -133,7 +133,6 @@ class DynamicSteeringLearnerGraph(Widget):
rl.draw_rectangle_rounded(graph_rect, 0.08, 8, self._panel_bg)
lcp_frame = sm.recv_frame["liveCurvatureParameters"]
controls_state = sm["controlsState"]
car_state = sm["carState"]
fit_corrections = np.zeros(CurvatureDLookup.bucket_shape(), dtype=np.float32)
@@ -276,17 +275,17 @@ class DynamicSteeringLearnerGraph(Widget):
rl.draw_text_ex(self._font_bold, title, rl.Vector2(text_x, title_y), title_size, 0, self._text_color)
status_text = (
f"live={payload_valid} transport={transport_valid} cal={int(getattr(lcp, 'calPerc', 0))}% "
f"live={payload_valid} transport={transport_valid} cal={int(getattr(lcp, 'calPerc', 0))}% " +
f"points={int(getattr(lcp, 'totalBucketPoints', 0))}"
)
rl.draw_text_ex(self._font_medium, status_text, rl.Vector2(text_x, status_y), status_size, 0, self._muted_text_color)
speed_mix = (
f"v={v_ego * 3.6:.0f} km/h mix={CurvatureDLookup.SPEED_ANCHORS[low_idx] * 3.6:.0f}/"
f"v={v_ego * 3.6:.0f} km/h mix={CurvatureDLookup.SPEED_ANCHORS[low_idx] * 3.6:.0f}/" +
f"{CurvatureDLookup.SPEED_ANCHORS[high_idx] * 3.6:.0f} alpha={alpha:.2f}"
)
marker_info = (
f"k={desired_curvature:.2e} corr={display_correction:.2e} "
f"k={desired_curvature:.2e} corr={display_correction:.2e} " +
f"bucket=({int(getattr(lcp, 'bucketSpeed', -1))}, {int(getattr(lcp, 'bucketCurvature', -1))})"
)
rl.draw_text_ex(self._font_medium, speed_mix, rl.Vector2(text_x, footer_y1), footer_size, 0, self._muted_text_color)
@@ -120,7 +120,9 @@ class SteeringLayout(Widget):
def _update_state(self):
super()._update_state()
torque_allowed = ui_state.CP is not None and ui_state.CP.steerControlType not in (car.CarParams.SteerControlType.angle, car.CarParams.SteerControlType.curvature)
torque_allowed = (ui_state.CP is not None and
ui_state.CP.steerControlType not in (car.CarParams.SteerControlType.angle,
car.CarParams.SteerControlType.curvature))
if ui_state.CP is not None:
mads_main_desc = self._mads_limited_desc if self._mads_settings_layout._mads_limited_settings() else self._mads_full_desc
self._mads_toggle.set_description(f"<b>{mads_main_desc}</b><br><br>{self._mads_base_desc}")
@@ -381,6 +381,14 @@ def build_mici_script(pm: PubMaster, main_layout, script: Script) -> None:
def build_tizi_script(pm: PubMaster, main_layout, script: Script) -> None:
"""Build the replay script for the tizi layout."""
def select_settings_panel(panel_name: str) -> None:
"""Select a settings panel without relying on stock or sunnypilot sidebar coordinates."""
from openpilot.selfdrive.ui.layouts.main import MainState
settings_layout = main_layout._layouts[MainState.SETTINGS]
panel_type = next(panel_type for panel_type in settings_layout._panels if panel_type.name == panel_name)
settings_layout.set_current_panel(panel_type)
def make_home_refresh_setup(fn: Callable) -> Callable:
"""Return setup function that calls the given function to modify state and forces an immediate refresh on the home layout."""
from openpilot.selfdrive.ui.layouts.main import MainState
@@ -457,7 +465,7 @@ def build_tizi_script(pm: PubMaster, main_layout, script: Script) -> None:
script.click(2000, 970) # OK
# === Settings - Network ===
script.click(278, 450)
script.setup(lambda: select_settings_panel("NETWORK"))
# TODO: mock networks
script.click(1880, 100) # advanced network settings
@@ -473,12 +481,12 @@ def build_tizi_script(pm: PubMaster, main_layout, script: Script) -> None:
script.click(630, 80) # back from advanced network
# === Settings - Toggles ===
script.click(278, 600)
script.setup(lambda: select_settings_panel("TOGGLES"))
script.click(1200, 280) # expand experimental mode description
# === Settings - Software ===
script.setup(lambda: setup_update_available(False), wait_after=0) # start with no update available
script.click(278, 720) # software
script.setup(lambda: select_settings_panel("SOFTWARE"))
for _ in range(2):
script.click(720, 120) # toggle current release notes
script.setup(setup_update_available) # set update available
@@ -491,11 +499,11 @@ def build_tizi_script(pm: PubMaster, main_layout, script: Script) -> None:
script.click(650, 750) # cancel uninstall
# === Settings - Firehose ===
script.click(278, 845)
script.setup(lambda: select_settings_panel("FIREHOSE"))
# === Settings - Developer (set CarParamsPersistent first) ===
script.setup(setup_developer_params, wait_after=0)
script.click(278, 950)
script.setup(lambda: select_settings_panel("DEVELOPER"))
script.click(1930, 470) # SSH keys (keyboard)
script.click(1930, 115) # click cancel on keyboard
script.click(2000, 960) # toggle alpha long
+3 -3
View File
@@ -99,7 +99,7 @@ class UIState(UIStateSP):
self.is_body: bool | None = None
self.CP: car.CarParams | None = None
self.light_sensor: float = -1.0
self.dark_mode: bool = False
self.onroad_screen_timeout: bool = False
self.enable_accel_bar: bool = False
@@ -190,11 +190,11 @@ class UIState(UIStateSP):
self.status = UIStatus.OVERRIDE
else:
self.status = UIStatus.ENGAGED if ss.enabled else UIStatus.DISENGAGED
# detect status change
self.has_status_change = True if self.status != self._status_prev else False
self._status_prev = self.status
# check for alert
self.has_alert = True if ss.alertSize != 0 else False