From b9ae966de77b5d3bb4aac442bda273698409cbb4 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:59:56 -0600 Subject: [PATCH] User adjustable offsets --- common/params.cc | 2 ++ frogpilot/common/frogpilot_variables.py | 3 +++ frogpilot/ui/qt/offroad/frogpilot_settings.cc | 9 +++++++++ frogpilot/ui/qt/offroad/frogpilot_settings.h | 1 + frogpilot/ui/qt/offroad/lateral_settings.cc | 19 ++++++++++++++++++ frogpilot/ui/qt/offroad/lateral_settings.h | 4 +++- selfdrive/car/gm/interface.py | 20 +++++++++++++++---- 7 files changed, 53 insertions(+), 5 deletions(-) diff --git a/common/params.cc b/common/params.cc index d3ad9edb9..4f10520b5 100644 --- a/common/params.cc +++ b/common/params.cc @@ -535,6 +535,8 @@ std::unordered_map keys = { {"SteerDelayStock", PERSISTENT}, {"SteerFriction", PERSISTENT}, {"SteerFrictionStock", PERSISTENT}, + {"SteerOffset", PERSISTENT}, + {"SteerOffsetStock", PERSISTENT}, {"SteerLatAccel", PERSISTENT}, {"SteerLatAccelStock", PERSISTENT}, {"SteerKP", PERSISTENT}, diff --git a/frogpilot/common/frogpilot_variables.py b/frogpilot/common/frogpilot_variables.py index 7374a2be2..d70505826 100644 --- a/frogpilot/common/frogpilot_variables.py +++ b/frogpilot/common/frogpilot_variables.py @@ -409,6 +409,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [ ("SteerDelayStock", "", 3, ""), ("SteerFriction", "", 3, ""), ("SteerFrictionStock", "", 3, ""), + ("SteerOffset", "", 3, ""), + ("SteerOffsetStock", "", 3, ""), ("SteerKP", "", 3, ""), ("SteerKPStock", "", 3, ""), ("SteerLatAccel", "", 3, ""), @@ -615,6 +617,7 @@ class FrogPilotVariables: toggle.steerActuatorDelay = np.clip(params.get_float("SteerDelay"), 0.01, 1.0) if advanced_lateral_tuning and tuning_level >= level["SteerDelay"] else steerActuatorDelay toggle.use_custom_steerActuatorDelay = bool(round(toggle.steerActuatorDelay, 2) != round(steerActuatorDelay, 2)) toggle.friction = np.clip(params.get_float("SteerFriction"), 0, 0.5) if advanced_lateral_tuning and tuning_level >= level["SteerFriction"] else friction + toggle.steer_offset = np.clip(params.get_float("SteerOffset"), -0.2, 0.2) if advanced_lateral_tuning and tuning_level >= level["SteerOffset"] and toggle.car_make == "gm" else 0.0 toggle.use_custom_friction = bool(round(toggle.friction, 2) != round(friction, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off toggle.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]] toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.5, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor diff --git a/frogpilot/ui/qt/offroad/frogpilot_settings.cc b/frogpilot/ui/qt/offroad/frogpilot_settings.cc index c4e8ec270..81a996bc5 100644 --- a/frogpilot/ui/qt/offroad/frogpilot_settings.cc +++ b/frogpilot/ui/qt/offroad/frogpilot_settings.cc @@ -286,6 +286,7 @@ void FrogPilotSettingsWindow::updateVariables() { longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay(); startAccel = CP.getStartAccel(); steerActuatorDelay = CP.getSteerActuatorDelay(); + steerOffset = 0.0f; steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6; steerRatio = CP.getSteerRatio(); stopAccel = CP.getStopAccel(); @@ -297,6 +298,7 @@ void FrogPilotSettingsWindow::updateVariables() { float currentDelayStock = params.getFloat("SteerDelayStock"); float currentFrictionStock = params.getFloat("SteerFrictionStock"); + float currentSteerOffsetStock = params.getFloat("SteerOffsetStock"); float currentKPStock = params.getFloat("SteerKPStock"); float currentLatAccelStock = params.getFloat("SteerLatAccelStock"); float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock"); @@ -321,6 +323,13 @@ void FrogPilotSettingsWindow::updateVariables() { params.putFloat("SteerFrictionStock", friction); } + if (currentSteerOffsetStock != steerOffset) { + if (params.getFloat("SteerOffset") == currentSteerOffsetStock) { + params.putFloat("SteerOffset", steerOffset); + } + params.putFloat("SteerOffsetStock", steerOffset); + } + if (currentKPStock != steerKp && steerKp != 0) { if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) { params.putFloat("SteerKP", steerKp); diff --git a/frogpilot/ui/qt/offroad/frogpilot_settings.h b/frogpilot/ui/qt/offroad/frogpilot_settings.h index aa4db8d8f..4c516aaed 100644 --- a/frogpilot/ui/qt/offroad/frogpilot_settings.h +++ b/frogpilot/ui/qt/offroad/frogpilot_settings.h @@ -47,6 +47,7 @@ public: float longitudinalActuatorDelay; float startAccel; float steerActuatorDelay; + float steerOffset; float steerKp; float steerRatio; float stopAccel; diff --git a/frogpilot/ui/qt/offroad/lateral_settings.cc b/frogpilot/ui/qt/offroad/lateral_settings.cc index 63ef4e695..244c30e7d 100644 --- a/frogpilot/ui/qt/offroad/lateral_settings.cc +++ b/frogpilot/ui/qt/offroad/lateral_settings.cc @@ -41,6 +41,7 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : {"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced steering control changes to fine-tune how openpilot drives."), "../../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"}, {"SteerDelay", parent->steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("The time between openpilot's steering command and the vehicle's response. Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""}, {"SteerFriction", parent->friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)) : tr("Friction"), tr("Compensates for steering friction. Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""}, + {"SteerOffset", parent->steerOffset != 0 ? QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)) : tr("Steer Offset"), tr("Offsets steering torque to help compensate for alignment or tire issues. More negative pulls the car right; more positive pulls it left. Most users should not need to touch this."), ""}, {"SteerKP", parent->steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)) : tr("Kp Factor"), tr("How strongly openpilot corrects lane position. Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""}, {"SteerLatAccel", parent->latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("Maps steering torque to turning response. Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""}, {"SteerRatio", parent->steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("The relationship between steering wheel rotation and road wheel angle. Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""}, @@ -87,6 +88,9 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : } else if (param == "SteerFriction") { std::vector steerFrictionButton{"Reset"}; lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.5, QString(), std::map(), 0.01, false, {}, steerFrictionButton, false, false); + } else if (param == "SteerOffset") { + std::vector steerOffsetButton{"Reset"}; + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, -0.2, 0.2, QString(), std::map(), 0.005, false, {}, steerOffsetButton, false, false); } else if (param == "SteerKP") { std::vector steerKPButton{"Reset"}; lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerKp * 0.5, parent->steerKp * 1.5, QString(), std::map(), 0.01, false, {}, steerKPButton, false, false); @@ -216,6 +220,14 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : } }); + steerOffsetToggle = static_cast(toggles["SteerOffset"]); + QObject::connect(steerOffsetToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Reset Steer Offset to its default value?"), this)) { + params.putFloat("SteerOffset", parent->steerOffset); + steerOffsetToggle->refresh(); + } + }); + steerKPToggle = static_cast(toggles["SteerKP"]); QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() { if (FrogPilotConfirmationDialog::yesorno(tr("Reset Kp Factor to its default value?"), this)) { @@ -255,6 +267,7 @@ void FrogPilotLateralPanel::showEvent(QShowEvent *event) { steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2))); steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2))); + steerOffsetToggle->setTitle(QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3))); steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2))); steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5); steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2))); @@ -405,6 +418,12 @@ void FrogPilotLateralPanel::updateToggles() { setVisible &= !usingNNFF; } + else if (key == "SteerOffset") { + setVisible &= parent->isGM; + setVisible &= parent->isTorqueCar || forcingTorqueController; + setVisible &= !usingNNFF; + } + else if (key == "SteerKP") { setVisible &= parent->steerKp != 0; setVisible &= !parent->isAngleCar; diff --git a/frogpilot/ui/qt/offroad/lateral_settings.h b/frogpilot/ui/qt/offroad/lateral_settings.h index e414ccf94..235697ce0 100644 --- a/frogpilot/ui/qt/offroad/lateral_settings.h +++ b/frogpilot/ui/qt/offroad/lateral_settings.h @@ -31,6 +31,7 @@ private: float friction; float latAccelFactor; float steerActuatorDelay; + float steerOffset; float steerKp; float steerRatio; @@ -38,7 +39,7 @@ private: std::map toggles; - QSet advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio"}; + QSet advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerOffset", "SteerLatAccel", "SteerKP", "SteerRatio"}; QSet aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"}; QSet laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"}; QSet lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"}; @@ -48,6 +49,7 @@ private: FrogPilotParamValueButtonControl *steerDelayToggle; FrogPilotParamValueButtonControl *steerFrictionToggle; + FrogPilotParamValueButtonControl *steerOffsetToggle; FrogPilotParamValueButtonControl *steerLatAccelToggle; FrogPilotParamValueButtonControl *steerKPToggle; FrogPilotParamValueButtonControl *steerRatioToggle; diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 4ed6d4432..48ec05601 100644 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -47,6 +47,10 @@ NON_LINEAR_TORQUE_PARAMS = { class CarInterface(CarInterfaceBase): + def __init__(self, CP, FPCP, CarController, CarState): + super().__init__(CP, FPCP, CarController, CarState) + self.steer_offset = 0.0 + @staticmethod def get_pid_accel_limits(CP, current_speed, cruise_speed): return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX @@ -90,20 +94,28 @@ class CarInterface(CarInterfaceBase): torque_values, lataccel_values = self.get_lataccel_torque_siglin() def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning): - return np.interp(lateral_acceleration, lataccel_values, torque_values) + return float(np.interp(lateral_acceleration, lataccel_values, torque_values) + self.steer_offset) return torque_from_lateral_accel_siglin else: - return self.torque_from_lateral_accel_linear + def torque_from_lateral_accel_linear(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning): + return self.torque_from_lateral_accel_linear(lateral_acceleration, torque_params) + self.steer_offset + return torque_from_lateral_accel_linear def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType: if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS: torque_values, lataccel_values = self.get_lataccel_torque_siglin() def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning): - return np.interp(torque, torque_values, lataccel_values) + return np.interp(torque - self.steer_offset, torque_values, lataccel_values) return lateral_accel_from_torque_siglin else: - return self.lateral_accel_from_torque_linear + def lateral_accel_from_torque_linear(torque: float, torque_params: car.CarParams.LateralTorqueTuning): + return self.lateral_accel_from_torque_linear(torque - self.steer_offset, torque_params) + return lateral_accel_from_torque_linear + + def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState: + self.steer_offset = float(getattr(frogpilot_toggles, "steer_offset", 0.0)) + return super().update(c, can_strings, frogpilot_toggles) @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):