Skip to content
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
224ea93
use current desired curvature for error instead of plan indexed with …
felsager Sep 9, 2025
4fea166
fix typo
felsager Sep 9, 2025
cf0aa85
correct function call
felsager Sep 9, 2025
f55fb02
test should accomodate change
felsager Sep 9, 2025
8582b92
only give raw desired curvature to torque controlled platforms
felsager Sep 9, 2025
33f9aad
raw desired curvature as input to all controllers
felsager Sep 10, 2025
e841e9c
update ref
felsager Sep 10, 2025
57687aa
.
felsager Sep 10, 2025
faa3d02
keep it clean
felsager Sep 11, 2025
592fb97
keep it clean
felsager Sep 11, 2025
cc66562
keep it clean
felsager Sep 11, 2025
762f012
desired lat accel buffer
felsager Sep 11, 2025
44319b7
update ref
felsager Sep 11, 2025
cafa6b9
use deque and add type annotation for confusing variables
felsager Sep 12, 2025
9d9f94c
use deque and add type annotation for confusing variables
felsager Sep 12, 2025
d5a522b
don't use expected lat accel for friction
felsager Sep 12, 2025
639afc8
update ref
felsager Sep 12, 2025
acf3863
use current expected curvature for low speed factor
felsager Sep 12, 2025
106b3b6
type lat_delay
felsager Sep 12, 2025
f8c2a7d
types in different pr and index from end of array
felsager Sep 16, 2025
69b3ea1
try half delay
felsager Sep 17, 2025
997cfed
try zero delay
felsager Sep 17, 2025
2688663
full lag for testing
felsager Sep 17, 2025
843295d
tweak torqued
felsager Sep 18, 2025
fb93974
only use expected error for pid
felsager Sep 18, 2025
ffadc70
only use expected error for pid
felsager Sep 18, 2025
2fa7e51
only use expected error for pid
felsager Sep 18, 2025
e517c4d
correct low speed compensation
felsager Sep 18, 2025
d18423b
use expected error for the integrator
felsager Sep 18, 2025
176d5fa
activate derivative action
felsager Sep 26, 2025
c1cf773
derivative to compensate large changes
felsager Sep 30, 2025
603fb8a
no kd for now
felsager Sep 30, 2025
3541931
updated opendbc with branch
felsager Sep 30, 2025
32ea793
initialize pid with derivative coefficient
felsager Sep 30, 2025
fd14a2d
set k_d properly
felsager Sep 30, 2025
8cbb481
use expected error
felsager Oct 2, 2025
e00931a
reference derivative feedforward
felsager Oct 2, 2025
6f81ce7
improve pid naming convention and use dt for controls
felsager Oct 3, 2025
1ca982c
only use measurement derivative for derivative compensation
felsager Oct 3, 2025
2b1e5ab
negative meas rate instead of err rate
felsager Oct 5, 2025
0375886
friction based on expected jerk
felsager Oct 7, 2025
d97fd3b
friction based on expected jerk use tanh
felsager Oct 8, 2025
0729235
ff friction
felsager Oct 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion common/pid.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ def set_limits(self, pos_limit, neg_limit):
self.pos_limit = pos_limit
self.neg_limit = neg_limit

def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False, error_expected = None):
self.speed = speed
self.p = float(error) * self.k_p
self.f = feedforward * self.k_f
self.d = error_rate * self.k_d

if not freeze_integrator:
#if error_expected is not None:
# error = error_expected
i = self.i + error * self.k_i * self.i_rate

# Don't allow windup if already clipping
Expand Down
2 changes: 1 addition & 1 deletion opendbc_repo
Submodule opendbc_repo updated 72 files
+3 −1 SConscript
+53 −0 SConstruct
+1 −4 docs/CARS.md
+1 −0 opendbc/car/car.capnp
+2 −3 opendbc/dbc/SConscript
+1 −1 opendbc/safety/__init__.py
+10 −0 opendbc/safety/board/can.h
+1 −5 opendbc/safety/board/can_declarations.h
+19 −0 opendbc/safety/board/drivers/can_common.h
+4 −0 opendbc/safety/board/drivers/can_common_declarations.h
+28 −0 opendbc/safety/board/fake_stm.h
+25 −0 opendbc/safety/board/faults.h
+18 −0 opendbc/safety/board/faults_declarations.h
+47 −0 opendbc/safety/board/utils.h
+5 −40 opendbc/safety/helpers.h
+52 −52 opendbc/safety/lateral.h
+4 −4 opendbc/safety/longitudinal.h
+2 −2 opendbc/safety/modes/body.h
+1 −1 opendbc/safety/modes/chrysler.h
+5 −5 opendbc/safety/modes/defaults.h
+1 −1 opendbc/safety/modes/elm327.h
+2 −2 opendbc/safety/modes/ford.h
+1 −1 opendbc/safety/modes/gm.h
+1 −1 opendbc/safety/modes/honda.h
+1 −1 opendbc/safety/modes/hyundai.h
+1 −1 opendbc/safety/modes/hyundai_canfd.h
+2 −2 opendbc/safety/modes/hyundai_common.h
+2 −2 opendbc/safety/modes/mazda.h
+1 −1 opendbc/safety/modes/nissan.h
+2 −2 opendbc/safety/modes/psa.h
+2 −2 opendbc/safety/modes/rivian.h
+1 −1 opendbc/safety/modes/subaru.h
+1 −1 opendbc/safety/modes/subaru_preglobal.h
+2 −2 opendbc/safety/modes/tesla.h
+3 −3 opendbc/safety/modes/toyota.h
+2 −2 opendbc/safety/modes/volkswagen_mqb.h
+2 −2 opendbc/safety/modes/volkswagen_pq.h
+10 −8 opendbc/safety/safety.h
+0 −0 opendbc/safety/safety_declarations.h
+13 −13 opendbc/safety/tests/common.py
+1 −1 opendbc/safety/tests/hyundai_common.py
+11 −0 opendbc/safety/tests/install_mull.sh
+31 −31 opendbc/safety/tests/libsafety/SConscript
+0 −12 opendbc/safety/tests/libsafety/fake_stm.h
+37 −53 opendbc/safety/tests/libsafety/libsafety_py.py
+9 −200 opendbc/safety/tests/libsafety/safety.c
+192 −0 opendbc/safety/tests/libsafety/safety_helpers.h
+104 −0 opendbc/safety/tests/libsafety/safety_helpers.py
+7 −7 opendbc/safety/tests/misra/main.c
+2 −2 opendbc/safety/tests/misra/test_misra.sh
+3 −3 opendbc/safety/tests/mutation.sh
+1 −1 opendbc/safety/tests/safety_replay/helpers.py
+6 −6 opendbc/safety/tests/test_body.py
+14 −14 opendbc/safety/tests/test_chrysler.py
+2 −2 opendbc/safety/tests/test_defaults.py
+20 −20 opendbc/safety/tests/test_ford.py
+23 −23 opendbc/safety/tests/test_gm.py
+21 −21 opendbc/safety/tests/test_honda.py
+27 −27 opendbc/safety/tests/test_hyundai.py
+20 −20 opendbc/safety/tests/test_hyundai_canfd.py
+11 −11 opendbc/safety/tests/test_mazda.py
+14 −14 opendbc/safety/tests/test_nissan.py
+10 −10 opendbc/safety/tests/test_psa.py
+13 −13 opendbc/safety/tests/test_rivian.py
+14 −14 opendbc/safety/tests/test_subaru.py
+9 −9 opendbc/safety/tests/test_subaru_preglobal.py
+13 −13 opendbc/safety/tests/test_tesla.py
+22 −22 opendbc/safety/tests/test_toyota.py
+14 −14 opendbc/safety/tests/test_volkswagen_mqb.py
+12 −12 opendbc/safety/tests/test_volkswagen_pq.py
+0 −1 pyproject.toml
+0 −9 setup.sh
2 changes: 1 addition & 1 deletion panda
6 changes: 4 additions & 2 deletions selfdrive/controls/controlsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self) -> None:
self.CI = interfaces[self.CP.carFingerprint](self.CP)

self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
'liveCalibration', 'liveDelay', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState')
self.pm = messaging.PubMaster(['carControl', 'controlsState'])

Expand Down Expand Up @@ -118,10 +118,12 @@ def state_control(self):
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)

lat_delay = self.sm["liveDelay"].lateralDelay

actuators.curvature = self.desired_curvature
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
self.steer_limited_by_safety, self.desired_curvature,
curvature_limited) # TODO what if not available
curvature_limited, lat_delay)
actuators.torque = float(steer)
actuators.steeringAngleDeg = float(steeringAngleDeg)
# Ensure no NaNs/Infs
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/controls/lib/drive_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL):
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
return alpha * val + (1 - alpha) * prev_val

def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, bool]:
# This function respects ISO lateral jerk and acceleration limits + a max curvature
v_ego = max(v_ego, MIN_SPEED)
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/controls/lib/latcontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, CP, CI):
self.steer_max = 1.0

@abstractmethod
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay: float):
pass

def reset(self):
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/controls/lib/latcontrol_angle.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, CP, CI):
self.sat_check_min_speed = 5.
self.use_steer_limited_by_safety = CP.brand == "tesla"

def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay: float):
angle_log = log.ControlsState.LateralAngleState.new_message()

if not active:
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/controls/lib/latcontrol_pid.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, CP, CI):
k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.get_steer_feedforward = CI.get_steer_feedforward_function()

def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay: float):
pid_log = log.ControlsState.LateralPIDState.new_message()
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
Expand Down
44 changes: 30 additions & 14 deletions selfdrive/controls/lib/latcontrol_torque.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from collections import deque
import math
import numpy as np

from cereal import log
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.latcontrol import DT_CTRL, LatControl
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.pid import PIDController

# At higher speeds (25+mph) we can assume:
Expand All @@ -21,17 +23,20 @@
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [15, 13, 10, 5]

LATACCEL_REQUEST_BUFFER_SIZE = int(1/DT_CTRL)


class LatControlTorque(LatControl):
def __init__(self, CP, CI):
super().__init__(CP, CI)
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(self.torque_params.kp, self.torque_params.ki,
k_f=self.torque_params.kf)
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, k_d=self.torque_params.kd, k_f=self.torque_params.kf)
self.update_limits()
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
self.requested_lateral_accel_buffer = deque([0.] * LATACCEL_REQUEST_BUFFER_SIZE, maxlen=LATACCEL_REQUEST_BUFFER_SIZE)
self.error_pre = 0.0

def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
self.torque_params.latAccelFactor = latAccelFactor
Expand All @@ -43,7 +48,7 @@ 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 update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay: float):
pid_log = log.ControlsState.LateralTorqueState.new_message()
if not active:
output_torque = 0.0
Expand All @@ -53,27 +58,38 @@ def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvat
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))

desired_lateral_accel = desired_curvature * CS.vEgo ** 2
delay_frames = int(lat_delay / DT_CTRL)
lag_compensated_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
self.requested_lateral_accel_buffer.append(lag_compensated_desired_lateral_accel)
current_expected_lateral_accel = self.requested_lateral_accel_buffer[-delay_frames]
current_expected_curvature = current_expected_lateral_accel / (CS.vEgo ** 2)
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2

low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
# pid error calculated as difference between expected and measured lateral acceleration
setpoint = lag_compensated_desired_lateral_accel + low_speed_factor * desired_curvature
setpoint_expected = current_expected_lateral_accel + low_speed_factor * current_expected_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation

gravity_adjusted_lateral_accel = lag_compensated_desired_lateral_accel - roll_compensation
error_expected = float(setpoint_expected - measurement)
#error = float(setpoint - measurement)
error_rate = (error_expected - self.error_pre) / DT_CTRL
self.error_pre = error_expected
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
pid_log.error = float(setpoint - measurement)
pid_log.error = float(error_expected)
ff = gravity_adjusted_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
ff += get_friction(lag_compensated_desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)

freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
output_lataccel = self.pid.update(pid_log.error,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
error_rate,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator,)
#error_expected=error_expected)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)

pid_log.active = True
Expand All @@ -83,7 +99,7 @@ def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvat
pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(actual_lateral_accel)
pid_log.desiredLateralAccel = float(desired_lateral_accel)
pid_log.desiredLateralAccel = float(lag_compensated_desired_lateral_accel)
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
Expand Down
6 changes: 3 additions & 3 deletions selfdrive/controls/tests/test_latcontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def test_saturation(self, car_name, controller):

# Saturate for curvature limited and controller limited
for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, True)
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, True, 0)
assert lac_log.saturated

for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, False)
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, False, 0)
assert not lac_log.saturated

for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, False)
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, False, 0)
assert lac_log.saturated
2 changes: 1 addition & 1 deletion selfdrive/test/process_replay/ref_commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
afcab1abb62b9d5678342956cced4712f44e909e
4e2926cc457a057980633163eabe062e421e9ff6
Loading