mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-11 15:03:44 +08:00
6c4dde8a07
Exclude HYUNDAI_IONIQ_6 from the recent Hyundai CAN-FD highway friction wrapper, restoring original friction threshold/error passthrough in both controller modes. Preserve all steering limits, ramp rates, startup PID settings, scalar calibration, other vehicles, native assets and dropout repairs. Update regression expectations and add exact Ioniq friction passthrough coverage. Focused suite: 22 passed; 559 repository Python imports match exact parent plus the three intended files. Independent numerical checks: 80,640 comparisons. Protected inventory receipt independently verified. This is the user-requested tuning revert, not a lag fix or on-device qualification.
156 lines
8.8 KiB
Python
156 lines
8.8 KiB
Python
import math
|
|
import numpy as np
|
|
from collections import deque
|
|
|
|
from openpilot.cereal import log
|
|
from opendbc.car import structs
|
|
from opendbc.car.hyundai.values import CAR, HyundaiFlags
|
|
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
|
|
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY, CV
|
|
from openpilot.common.filter_simple import FirstOrderFilter
|
|
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
|
from openpilot.common.pid import PIDController
|
|
from openpilot.starpilot.lateral.controller_selection import ControllerMode, default_selection, policy_for
|
|
from openpilot.starpilot.lateral.torque_extension import apply_turn_assist, create_extension, create_turn_assist
|
|
from openpilot.starpilot.flm import live as flm
|
|
|
|
# At higher speeds (25+mph) we can assume:
|
|
# Lateral acceleration achieved by a specific car correlates to
|
|
# torque applied to the steering rack. It does not correlate to
|
|
# wheel slip, or to speed.
|
|
|
|
# This controller applies torque to achieve desired lateral
|
|
# accelerations. To compensate for the low speed effects the
|
|
# proportional gain is increased at low speeds by the PID controller.
|
|
# Additionally, there is friction in the steering wheel that needs
|
|
# to be overcome to move it at all, this is compensated for too.
|
|
|
|
KP = 0.8
|
|
KI = 0.15
|
|
|
|
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
|
|
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
|
|
|
|
LP_FILTER_CUTOFF_HZ = 1.2
|
|
JERK_LOOKAHEAD_SECONDS = 0.19
|
|
JERK_GAIN = 0.3
|
|
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
|
VERSION = 1
|
|
|
|
class LatControlTorque(LatControl):
|
|
def __init__(self, CP, CI, dt, *, controller_mode: ControllerMode | None = None, turn_assist: bool = False):
|
|
super().__init__(CP, CI, dt)
|
|
self.hkg_canfd_torque = bool(CP.brand == 'hyundai' and CP.flags & HyundaiFlags.CANFD and
|
|
CP.carFingerprint != CAR.HYUNDAI_IONIQ_6 and
|
|
CP.steerControlType == structs.CarParams.SteerControlType.torque and
|
|
not CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and
|
|
not (CP.notCar or CP.passive or CP.dashcamOnly))
|
|
self.controller_mode = default_selection(CP).mode if controller_mode is None else ControllerMode(controller_mode)
|
|
self.controller_policy = policy_for(CP)
|
|
if self.controller_mode == ControllerMode.STARPILOT and self.controller_policy is None:
|
|
raise ValueError('No StarPilot torque policy for CarParams')
|
|
self.torque_params = CP.lateralTuning.torque.as_builder()
|
|
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
|
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
|
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
|
|
self.update_limits()
|
|
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
|
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
|
|
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
|
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
|
|
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
|
self.turn_assist = create_turn_assist(CP, turn_assist)
|
|
self.starpilot_extension = create_extension(self, CP, self.controller_mode, self.controller_policy, turn_assist=turn_assist)
|
|
self.flm_source = flm.GmLiveSource(CP, self.controller_mode)
|
|
self.flm_surface = None
|
|
|
|
def update_torque_parameters(self, latAccelFactor, latAccelOffset, friction):
|
|
if self.starpilot_extension is not None:
|
|
latAccelFactor, latAccelOffset, friction = self.starpilot_extension.transform_torque_parameters(latAccelFactor, latAccelOffset, friction)
|
|
self.torque_params.latAccelFactor = latAccelFactor
|
|
self.torque_params.latAccelOffset = latAccelOffset
|
|
self.torque_params.friction = friction
|
|
self.update_limits()
|
|
|
|
def update_limits(self):
|
|
self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params),
|
|
self.lateral_accel_from_torque(-self.steer_max, self.torque_params))
|
|
|
|
def friction(self, error, deadzone, threshold, CS, setpoint):
|
|
if self.hkg_canfd_torque and not CS.steeringPressed:
|
|
weight = np.interp(CS.vEgo, [50 * CV.MPH_TO_MS, 65 * CV.MPH_TO_MS], [0.0, 1.0])
|
|
weight *= np.interp(abs(setpoint), [0.25, 0.65], [1.0, 0.0])
|
|
threshold += max(np.interp(CS.vEgo, [65 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.65, 0.78]) - threshold, 0.0) * weight
|
|
error = math.copysign(max(abs(error) - 0.04 * weight, 0.0), error)
|
|
return get_friction(error, deadzone, threshold, self.torque_params)
|
|
|
|
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay):
|
|
self.flm_surface = self.flm_source.sample(active=active, speed=CS.vEgo)
|
|
if self.starpilot_extension is not None:
|
|
return self.starpilot_extension.update(active, CS, VM, params, steer_limited_by_safety,
|
|
desired_curvature, curvature_limited, lat_delay)
|
|
return self.update_standard(active, CS, VM, params, steer_limited_by_safety,
|
|
desired_curvature, curvature_limited, lat_delay)
|
|
|
|
def update_standard(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay,
|
|
*, friction_policy=None):
|
|
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
|
pid_log.version = VERSION
|
|
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
|
measurement = measured_curvature * CS.vEgo ** 2
|
|
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
|
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
|
|
|
|
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
|
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg + flm.deadband(self.flm_surface, CS.vEgo)), CS.vEgo, 0.0))
|
|
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
|
|
|
delay_frames = int(np.clip(lat_delay / self.dt + 1, 1, self.lat_accel_request_buffer_len))
|
|
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
|
|
setpoint = expected_lateral_accel
|
|
error = setpoint - measurement
|
|
|
|
lookahead_idx = int(np.clip(-delay_frames + self.lookahead_frames, -self.lat_accel_request_buffer_len+1, -2))
|
|
raw_lateral_jerk = (self.lat_accel_request_buffer[lookahead_idx+1] - self.lat_accel_request_buffer[lookahead_idx-1]) / (2 * self.dt)
|
|
desired_lateral_jerk = self.jerk_filter.update(raw_lateral_jerk)
|
|
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
|
ff = gravity_adjusted_future_lateral_accel
|
|
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
|
ff -= self.torque_params.latAccelOffset
|
|
threshold = flm.base_threshold(self.flm_surface, CS.vEgo, FRICTION_THRESHOLD)
|
|
ff, threshold = flm.stages(self.flm_surface, CS, setpoint, desired_lateral_jerk, ff, threshold)
|
|
if friction_policy is not None:
|
|
threshold = friction_policy.friction_threshold(CS.vEgo, setpoint, desired_lateral_jerk)
|
|
ff += self.friction(error + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, threshold, CS, setpoint)
|
|
|
|
if not active:
|
|
output_torque = 0.0
|
|
pid_log.active = False
|
|
else:
|
|
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
|
pid_log.error = float(error)
|
|
|
|
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
|
output_lataccel = self.pid.update(pid_log.error, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
|
|
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
|
|
|
output_torque = apply_turn_assist(self, CS, VM, params, desired_curvature, output_torque)
|
|
output_torque = flm.angle_assist(self.flm_surface, CS, VM, params, desired_curvature, setpoint, desired_lateral_jerk, output_torque)
|
|
|
|
if friction_policy is not None:
|
|
output_torque *= friction_policy.output_scale(setpoint, CS.vEgo)
|
|
|
|
pid_log.active = True
|
|
pid_log.p = float(self.pid.p)
|
|
pid_log.i = float(self.pid.i)
|
|
pid_log.d = float(self.pid.d)
|
|
pid_log.f = float(self.pid.f)
|
|
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
|
pid_log.actualLateralAccel = float(measurement)
|
|
pid_log.desiredLateralAccel = float(setpoint)
|
|
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
|
|
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
|
|
|
# TODO left is positive in this convention
|
|
return -output_torque, 0.0, pid_log
|