Bolt EUV: Non-linear torque function (#27696)

* add erf based ff

* silly bug; diff of nonlinear != nonlinear of diff

* add sigmoid based ff, ensure slope at 0 > 1

* reduce steer down limit and increase driver allowance

* rebase panda

* atry without friction, and with tanh nonlinear

* finalize the nonlinear function

* do not disable friction compensation in the ff

* bump panda

* bump panda

* update refs

* update refs

* resolve comments

* Add type hints

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
pull/27738/head^2
Vivek Aithal 2 years ago committed by GitHub
parent 8e3ed8f10b
commit 15b880c0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      panda
  2. 28
      selfdrive/car/gm/interface.py
  3. 4
      selfdrive/car/gm/values.py
  4. 16
      selfdrive/car/interfaces.py
  5. 10
      selfdrive/controls/lib/drive_helpers.py
  6. 6
      selfdrive/controls/lib/latcontrol_torque.py
  7. 2
      selfdrive/test/process_replay/ref_commit

@ -1 +1 @@
Subproject commit db6c50cd09f773c231f962fc0e31b4612c572b08 Subproject commit a1e1451d1c2f23b953d82eea7691a657c9a8a75a

@ -1,13 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from math import fabs from math import fabs, exp
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config
from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD
from selfdrive.controls.lib.drive_helpers import get_friction
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -44,6 +45,29 @@ class CarInterface(CarInterfaceBase):
else: else:
return CarInterfaceBase.get_steer_feedforward_default return CarInterfaceBase.get_steer_feedforward_default
@staticmethod
def torque_from_lateral_accel_bolt(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
def sig(val):
return 1 / (1 + exp(-val)) - 0.5
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
# This has big effect on the stability about 0 (noise when going straight)
# ToDo: To generalize to other GMs, explore tanh function as the nonlinear
a, b, c, _ = [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178] # weights computed offline
steer_torque = (sig(lateral_accel_value * a) * b) + (lateral_accel_value * c)
return float(steer_torque) + friction
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
if self.CP.carFingerprint == CAR.BOLT_EUV:
return self.torque_from_lateral_accel_bolt
else:
return self.torque_from_lateral_accel_linear
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret.carName = "gm" ret.carName = "gm"

@ -14,8 +14,8 @@ class CarControllerParams:
STEER_STEP = 3 # Active control frames per command (~33hz) STEER_STEP = 3 # Active control frames per command (~33hz)
INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz) INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz)
STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness
STEER_DELTA_DOWN = 25 STEER_DELTA_DOWN = 15
STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_ALLOWANCE = 65
STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_MULTIPLIER = 4
STEER_DRIVER_FACTOR = 100 STEER_DRIVER_FACTOR = 100
NEAR_STOP_BRAKE_PHASE = 0.5 # m/s NEAR_STOP_BRAKE_PHASE = 0.5 # m/s

@ -8,10 +8,10 @@ from cereal import car
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from common.numpy_fast import clip, interp from common.numpy_fast import clip
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, apply_center_deadzone from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, get_friction
from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
@ -131,15 +131,11 @@ class CarInterfaceBase(ABC):
return self.get_steer_feedforward_default return self.get_steer_feedforward_default
@staticmethod @staticmethod
def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation): def torque_from_lateral_accel_linear(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
friction_interp = interp( friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), return (lateral_accel_value / float(torque_params.latAccelFactor)) + friction
[-FRICTION_THRESHOLD, FRICTION_THRESHOLD],
[-torque_params.friction, torque_params.friction]
)
friction = friction_interp if friction_compensation else 0.0
return (lateral_accel_value / torque_params.latAccelFactor) + friction
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType: def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
return self.torque_from_lateral_accel_linear return self.torque_from_lateral_accel_linear

@ -190,3 +190,13 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates):
current_curvature_desired + max_curvature_rate * DT_MDL) current_curvature_desired + max_curvature_rate * DT_MDL)
return safe_desired_curvature, safe_desired_curvature_rate return safe_desired_curvature, safe_desired_curvature_rate
def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float, torque_params: car.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float:
friction_interp = interp(
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
[-friction_threshold, friction_threshold],
[-torque_params.friction, torque_params.friction]
)
friction = float(friction_interp) if friction_compensation else 0.0
return friction

@ -61,10 +61,12 @@ class LatControlTorque(LatControl):
low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2 low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature measurement = actual_lateral_accel + low_speed_factor * actual_curvature
error = setpoint - measurement
gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY
pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error, torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params, setpoint,
lateral_accel_deadzone, friction_compensation=False) lateral_accel_deadzone, friction_compensation=False)
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params, measurement,
lateral_accel_deadzone, friction_compensation=False)
pid_log.error = torque_from_setpoint - torque_from_measurement
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params, ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params,
desired_lateral_accel - actual_lateral_accel, desired_lateral_accel - actual_lateral_accel,
lateral_accel_deadzone, friction_compensation=True) lateral_accel_deadzone, friction_compensation=True)

@ -1 +1 @@
50f1e873095fe2462d2aadb9c401bda76759c01c 1da098f096cee9f2871dafd2788dc9ddac85eeab
Loading…
Cancel
Save