Compare commits

...

106 Commits

Author SHA1 Message Date
Jason Wen dc6bbb519c Update interface.py 2024-09-16 16:24:10 -04:00
Jason Wen a4290d9676 Merge branch 'controls-custom-stock-long-ford' into dev-c3-cslc-ford
# Conflicts:
#	panda
#	selfdrive/car/hyundai/carcontroller.py
2024-09-16 15:50:47 -04:00
Jason Wen b10fe24d57 implement for ford 2024-09-16 15:46:31 -04:00
Jason Wen 45739d199c fixes 2024-09-16 10:19:54 -04:00
Jason Wen 29c143e6ee unused 2024-09-16 00:16:38 -04:00
Jason Wen 6d51225a9a move to method 2024-09-16 00:12:51 -04:00
Jason Wen 2d130b41c3 decouple and implement for volkswagen (mqb & pq) 2024-09-16 00:04:44 -04:00
Jason Wen cacdb08b05 updated 2024-09-15 23:09:53 -04:00
Jason Wen ed5dbc4dd2 missed 2024-09-15 22:07:34 -04:00
Jason Wen 028a468290 init package 2024-09-15 22:03:11 -04:00
Jason Wen 5b3acfbaf6 Update helpers.py 2024-09-15 18:02:25 -04:00
Jason Wen d54bc4d0b4 Fix 2024-09-15 17:57:22 -04:00
Jason Wen bb1ed2489c no longer needed 2024-09-15 13:47:15 -04:00
Jason Wen aee0a96e02 hkg rename 2024-09-15 10:58:17 -04:00
Jason Wen cc5a6b7fa4 fixup! decouple and implement for honda 2024-09-15 03:54:50 -04:00
Jason Wen 4cb50eeb96 decouple and implement for mazda 2024-09-15 03:47:32 -04:00
Jason Wen 3014e01865 decouple and implement for honda 2024-09-15 03:41:31 -04:00
Jason Wen fbde6d8d07 decouple and implement for fca 2024-09-15 03:30:43 -04:00
Jason Wen 457c82a3b1 todo for future investigation, default for now 2024-09-15 03:06:14 -04:00
Jason Wen e73c5dfc1d fix min set speed 2024-09-15 03:00:58 -04:00
Jason Wen 31accc75f9 more consistent button sends thanks to multikyd! 2024-09-15 02:56:53 -04:00
Jason Wen f34d61b9bd todo for future investigation, default for now 2024-09-15 02:47:05 -04:00
Jason Wen b4b494e6e9 only update hold time when ready 2024-09-15 02:16:07 -04:00
Jason Wen deafc23ea3 must check manual presses this way 2024-09-15 02:10:56 -04:00
Jason Wen f113940993 fix hysteresis 2024-09-15 01:54:03 -04:00
Jason Wen ddcabf9dea introduce random hold time (thanks to multikyd!) 2024-09-15 00:24:46 -04:00
Jason Wen 16354d4f63 initialize car state 2024-09-15 00:13:18 -04:00
Jason Wen 73e620245a add super 2024-09-15 00:11:24 -04:00
Jason Wen 2a62ca2600 set controller in constructor 2024-09-14 23:50:54 -04:00
Jason Wen 2d55dd0d12 flip them 2024-09-14 23:29:25 -04:00
Jason Wen 61b9db6666 explicit return type hint 2024-09-14 22:45:25 -04:00
Jason Wen 5135802760 newer platforms has pause/resume 2024-09-14 22:44:13 -04:00
Jason Wen be46484b6e missing symlink 2024-09-14 22:33:13 -04:00
Jason Wen 12f6278f96 redundant 2024-09-14 22:32:24 -04:00
Jason Wen 53bed607bb check not is ready once 2024-09-14 22:31:41 -04:00
Jason Wen 6aaba7bda4 make sure to immediately apply state change 2024-09-14 21:18:54 -04:00
Jason Wen 8879d158f8 reset count in holding 2024-09-14 21:11:08 -04:00
Jason Wen 3889accb5d move timer to state base 2024-09-14 20:44:30 -04:00
Jason Wen c8bef28d26 less is more 2024-09-14 20:39:07 -04:00
Jason Wen 3798118a76 always reset if not ready 2024-09-14 20:34:55 -04:00
Jason Wen 6e9a2eefeb more explicit inactive and loading 2024-09-14 20:24:45 -04:00
Jason Wen 6efef44bbe keep inactive simple 2024-09-14 20:21:14 -04:00
Jason Wen 4488ad70dd move to openpilot package 2024-09-14 20:00:29 -04:00
Jason Wen 9e3f15245e only 1 instance, so no need for list 2024-09-14 18:37:00 -04:00
Jason Wen 99b61454bd simplify 2024-09-14 18:27:23 -04:00
Jason Wen cc720d2800 move subs to method 2024-09-14 18:21:12 -04:00
Jason Wen 69b43269c1 move speed hysteresis to helpers 2024-09-14 18:18:46 -04:00
Jason Wen e2872e6f32 init list 2024-09-14 17:59:53 -04:00
Jason Wen a3a202fa87 ready state refactor 2024-09-14 17:58:28 -04:00
Jason Wen a5d6fe83ae diff 2024-09-14 17:50:38 -04:00
Jason Wen a70f9a642d callable state classes 2024-09-14 17:42:24 -04:00
Jason Wen c63c7d8bfd fixes 2024-09-14 17:30:29 -04:00
Jason Wen 931e79e801 fix publishing 2024-09-14 17:15:42 -04:00
Jason Wen bc3f0fa6a4 fix typo 2024-09-14 00:24:07 -04:00
Jason Wen 121a863cbf refactor logging 2024-09-14 00:22:13 -04:00
Jason Wen 7f146c5cce less 2024-09-13 23:30:43 -04:00
Jason Wen 3b30419dee add loading state 2024-09-13 23:26:19 -04:00
Jason Wen ddad143b7f decouple even more 2024-09-13 23:01:45 -04:00
Jason Wen efa292ca3f it's ok in init only 2024-09-13 22:31:59 -04:00
Jason Wen 28c85087dc unused 2024-09-13 22:27:47 -04:00
Jason Wen ca0a00b546 updated name 2024-09-13 22:27:29 -04:00
Jason Wen 6da758f53e set directly 2024-09-13 22:26:06 -04:00
Jason Wen cb4ba22ac7 Assign to class var 2024-09-13 11:19:44 -04:00
Jason Wen 78d35cb24e rename base class 2024-09-13 09:25:48 -04:00
Jason Wen 3cc0656961 move to helpers 2024-09-13 09:24:25 -04:00
Jason Wen 675e9957f9 no longer used 2024-09-13 09:21:26 -04:00
Jason Wen f2526cf0d1 entire obj 2024-09-13 09:19:49 -04:00
Jason Wen cc4ebe717b refactor speed checks 2024-09-13 09:13:19 -04:00
Jason Wen b54d7d486e typo 2024-09-13 08:57:45 -04:00
Jason Wen 814b623bfe simplify slc checks 2024-09-13 08:48:42 -04:00
Jason Wen 72b36d5482 use cereal 2024-09-13 08:48:03 -04:00
Jason Wen cc59dabc09 type hint 2024-09-13 08:18:45 -04:00
Jason Wen e5f9885288 typo 2024-09-13 08:18:31 -04:00
Jason Wen 4e81036d6c no longer used 2024-09-13 08:00:34 -04:00
Jason Wen a0b09e7154 hyundai: decouple completely 2024-09-13 07:40:33 -04:00
Jason Wen 3097d451fe implement in card 2024-09-13 00:58:50 -04:00
Jason Wen f90cfc2d0a unused 2024-09-13 00:39:44 -04:00
Jason Wen cfc8878392 not needed 2024-09-13 00:37:34 -04:00
Jason Wen da5a198bd0 new state machine 2024-09-13 00:33:10 -04:00
Jason Wen cecf82e951 set point in base class only 2024-09-13 00:32:30 -04:00
Jason Wen 43b87b53f5 new set speed buttons 2024-09-13 00:30:03 -04:00
Jason Wen 9d84814aba new button mapping 2024-09-13 00:25:15 -04:00
Jason Wen 30718cba8b move to card 2024-09-13 00:11:28 -04:00
Jason Wen 48b7567e38 no need 2024-09-12 23:49:42 -04:00
Jason Wen ddb1c92b2e new cereal name 2024-09-12 23:29:13 -04:00
Jason Wen af5ea57188 pass car instead 2024-09-12 23:12:19 -04:00
Jason Wen 4be8029ead infra for carControlSP 2024-09-12 23:03:01 -04:00
Jason Wen 480da5f8b8 remove from car interface 2024-09-12 22:42:19 -04:00
Jason Wen 8d39d5045d cut it 2024-09-09 19:59:27 -04:00
Jason Wen 455aa3d140 more fix 2024-09-09 19:49:56 -04:00
Jason Wen 47bd75a1b1 fix 2024-09-09 19:48:41 -04:00
Jason Wen c1d08539e7 services 2024-09-09 19:46:55 -04:00
Jason Wen e8b4d7c5a8 cleaner 2024-09-09 19:24:56 -04:00
Jason Wen 659c2ab446 more 2024-09-09 19:20:50 -04:00
Jason Wen 8dff996b8b more simplify 2024-09-09 18:54:36 -04:00
Jason Wen 08ec79db30 simplify 2024-09-09 18:52:14 -04:00
Jason Wen a88412f25a type hints 2024-09-06 03:31:54 -04:00
Jason Wen 73075c8b5a unused 2024-09-06 03:23:03 -04:00
Jason Wen 859b2fb29e implement for hyundai 2024-09-06 02:48:45 -04:00
Jason Wen 59a1122851 type hint 2024-09-06 02:43:00 -04:00
Jason Wen 4a9894d843 logging 2024-09-06 02:33:02 -04:00
Jason Wen 385d8c20d2 add hyundai 2024-09-06 02:26:18 -04:00
Jason Wen 43791061bb abstract out buttons 2024-09-06 02:26:13 -04:00
Jason Wen 8eff79892c init 2024-09-05 02:00:06 -04:00
Kumar 9e6824a69e new api: human like long control v1.2 (#420)
minor tweaks
2024-08-26 18:01:24 -07:00
Kumar caa6f5e7ba Dec v1.1 (#419)
* dec update

* dec

* dec: use anomaly to filter noise

* dec: use "WeightedMovingAverageCalculator" for more responsiveness and for smoohter transtion

* bug fixes
2024-08-26 17:53:46 -07:00
60 changed files with 848 additions and 888 deletions
+26 -2
View File
@@ -35,6 +35,11 @@ enum ModelGeneration {
five @5; five @5;
} }
enum MpcSource {
acc @0;
blended @1;
}
struct ControlsStateSP @0x81c2f05a394cf4af { struct ControlsStateSP @0x81c2f05a394cf4af {
lateralState @0 :Text; lateralState @0 :Text;
personality @8 :LongitudinalPersonalitySP; personality @8 :LongitudinalPersonalitySP;
@@ -92,8 +97,10 @@ struct LongitudinalPlanSP @0xaedffd8f31e7b55d {
desiredTF @13 :Float32; desiredTF @13 :Float32;
notSpeedLimit @14 :Int16; notSpeedLimit @14 :Int16;
e2eX @15 :List(Float32); e2eX @15 :List(Float32);
e2eBlended @18 :Text; e2eBlendedDEPRECATED @18 :Text;
e2eStatus @22 :Bool; e2eStatus @22 :Bool;
mpcSource @23 :MpcSource;
dynamicExperimentalControl @24 :Bool;
distToTurn @7 :Float32; distToTurn @7 :Float32;
turnSpeed @8 :Float32; turnSpeed @8 :Float32;
@@ -205,7 +212,24 @@ struct ModelDataV2SP @0xf98d843bfd7004a3 {
modelCapabilities @4 :UInt32; modelCapabilities @4 :UInt32;
} }
struct CustomReserved7 @0xb86e6369214c01c8 { struct CarControlSP @0xb86e6369214c01c8 {
customStockLongitudinalControl @0 :CustomStockLongitudinalControl;
struct CustomStockLongitudinalControl {
state @0 :ButtonControlState;
cruiseButton @1 :Int16;
finalSpeedKphDEPRECATED @2 :Float32;
vTarget @3 :Float32;
vCruiseCluster @4 :Float32;
enum ButtonControlState {
inactive @0; # No button press or default state
loading @1; # Loading state before transitioning to accelerating or decelerating
accelerating @2; # Increasing speed
decelerating @3; # Decreasing speed
holding @4; # Holding steady speed
}
}
} }
struct CustomReserved8 @0xf416ec09499d9d19 { struct CustomReserved8 @0xf416ec09499d9d19 {
+1 -1
View File
@@ -2388,7 +2388,7 @@ struct Event {
liveMapDataSP @111 :Custom.LiveMapDataSP; liveMapDataSP @111 :Custom.LiveMapDataSP;
e2eLongStateSP @112 :Custom.E2eLongStateSP; e2eLongStateSP @112 :Custom.E2eLongStateSP;
modelV2SP @113 :Custom.ModelDataV2SP; modelV2SP @113 :Custom.ModelDataV2SP;
customReserved7 @114 :Custom.CustomReserved7; carControlSP @114 :Custom.CarControlSP;
customReserved8 @115 :Custom.CustomReserved8; customReserved8 @115 :Custom.CustomReserved8;
customReserved9 @116 :Custom.CustomReserved9; customReserved9 @116 :Custom.CustomReserved9;
+1
View File
@@ -83,6 +83,7 @@ _services: dict[str, tuple] = {
"liveMapDataSP": (True, 0.), "liveMapDataSP": (True, 0.),
"e2eLongStateSP": (True, 0.), "e2eLongStateSP": (True, 0.),
"modelV2SP": (True, 20., 40), "modelV2SP": (True, 20., 40),
"carControlSP": (True, 100., 10),
# debug # debug
"uiDebug": (True, 0., 1), "uiDebug": (True, 0., 1),
+1
View File
@@ -0,0 +1 @@
../sunnypilot/
+1 -1
Submodule panda updated: 55018eafc2...3b4cfcd291
+15 -1
View File
@@ -31,7 +31,7 @@ class Car:
def __init__(self, CI=None): def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20) self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents']) self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput']) self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'carControlSP'])
self.can_rcv_cum_timeout_counter = 0 self.can_rcv_cum_timeout_counter = 0
@@ -70,6 +70,12 @@ class Car:
if self.CP.customStockLongAvailable and self.CP.pcmCruise and self.params.get_bool("CustomStockLong"): if self.CP.customStockLongAvailable and self.CP.pcmCruise and self.params.get_bool("CustomStockLong"):
self.CP.pcmCruiseSpeed = False self.CP.pcmCruiseSpeed = False
services = self.sm.data.keys() | {'longitudinalPlanSP'}
self.sm = messaging.SubMaster(list(services))
cslc_path = f'openpilot.sunnypilot.selfdrive.car.{self.CP.carName}.custom_stock_longitudinal_controller'
CustomStockLongitudinalController = __import__(cslc_path + '.controller', fromlist=['CustomStockLongitudinalController']).CustomStockLongitudinalController
self.custom_stock_longitudinal_controller = CustomStockLongitudinalController(self, self.CI.CC, self.CI.CS, self.CP)
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
@@ -175,8 +181,16 @@ class Car:
# send car controls over can # send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos) self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos)
if not self.CP.pcmCruiseSpeed:
can_sends.extend(self.custom_stock_longitudinal_controller.update(CS, CC))
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid)) self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
cc_send_sp = messaging.new_message('carControlSP')
cc_send_sp.valid = self.sm.all_checks(['carControl'])
if not self.CP.pcmCruiseSpeed:
cc_send_sp.carControlSP.customStockLongitudinalControl = self.custom_stock_longitudinal_controller.state_publish()
self.pm.send('carControlSP', cc_send_sp)
self.CC_prev = CC self.CC_prev = CC
def step(self): def step(self):
+1 -193
View File
@@ -1,14 +1,10 @@
from cereal import car from cereal import car
import cereal.messaging as messaging
from common.conversions import Conversions as CV
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.common.params import Params from openpilot.selfdrive.car import apply_meas_steer_torque_limits
from openpilot.selfdrive.car import DT_CTRL, apply_meas_steer_torque_limits
from openpilot.selfdrive.car.chrysler import chryslercan from openpilot.selfdrive.car.chrysler import chryslercan
from openpilot.selfdrive.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerFlagsSP from openpilot.selfdrive.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerFlagsSP
from openpilot.selfdrive.car.interfaces import CarControllerBase, FORWARD_GEARS from openpilot.selfdrive.car.interfaces import CarControllerBase, FORWARD_GEARS
from openpilot.selfdrive.controls.lib.drive_helpers import FCA_V_CRUISE_MIN
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
@@ -26,64 +22,9 @@ class CarController(CarControllerBase):
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.params = CarControllerParams(CP) self.params = CarControllerParams(CP)
self.sm = messaging.SubMaster(['longitudinalPlanSP'])
self.param_s = Params()
self.last_speed_limit_sign_tap_prev = False
self.speed_limit = 0.
self.speed_limit_offset = 0
self.timer = 0
self.final_speed_kph = 0
self.init_speed = 0
self.current_speed = 0
self.v_set_dis = 0
self.v_cruise_min = 0
self.button_type = 0
self.button_select = 0
self.button_count = 0
self.target_speed = 0
self.t_interval = 7
self.slc_active_stock = False
self.sl_force_active_timer = 0
self.v_tsc_state = 0
self.slc_state = 0
self.m_tsc_state = 0
self.cruise_button = None
self.speed_diff = 0
self.v_tsc = 0
self.m_tsc = 0
self.steady_speed = 0
self.button_frame = 0
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
if not self.CP.pcmCruiseSpeed:
self.sm.update(0)
if self.sm.updated['longitudinalPlanSP']:
self.v_tsc_state = self.sm['longitudinalPlanSP'].visionTurnControllerState
self.slc_state = self.sm['longitudinalPlanSP'].speedLimitControlState
self.m_tsc_state = self.sm['longitudinalPlanSP'].turnSpeedControlState
self.speed_limit = self.sm['longitudinalPlanSP'].speedLimit
self.speed_limit_offset = self.sm['longitudinalPlanSP'].speedLimitOffset
self.v_tsc = self.sm['longitudinalPlanSP'].visionTurnSpeed
self.m_tsc = self.sm['longitudinalPlanSP'].turnSpeed
self.v_cruise_min = FCA_V_CRUISE_MIN[CS.params_list.is_metric] * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1)
can_sends = [] can_sends = []
if not self.CP.pcmCruiseSpeed:
if not self.last_speed_limit_sign_tap_prev and CS.params_list.last_speed_limit_sign_tap:
self.sl_force_active_timer = self.frame
self.param_s.put_bool_nonblocking("LastSpeedLimitSignTap", False)
self.last_speed_limit_sign_tap_prev = CS.params_list.last_speed_limit_sign_tap
sl_force_active = CS.params_list.speed_limit_control_enabled and (self.frame < (self.sl_force_active_timer * DT_CTRL + 2.0))
sl_inactive = not sl_force_active and (not CS.params_list.speed_limit_control_enabled or (True if self.slc_state == 0 else False))
sl_temp_inactive = not sl_force_active and (CS.params_list.speed_limit_control_enabled and (True if self.slc_state == 1 else False))
slc_active = not sl_inactive and not sl_temp_inactive
self.slc_active_stock = slc_active
lkas_active = CC.latActive and CS.madsEnabled lkas_active = CC.latActive and CS.madsEnabled
if self.frame % 10 == 0 and self.CP.carFingerprint not in RAM_CARS: if self.frame % 10 == 0 and self.CP.carFingerprint not in RAM_CARS:
@@ -114,20 +55,6 @@ class CarController(CarControllerBase):
self.last_button_frame = self.frame self.last_button_frame = self.frame
can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, self.CP, resume=True)) can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, self.CP, resume=True))
if not (CC.cruiseControl.cancel or CC.cruiseControl.resume) and not self.CP.pcmCruiseSpeed and CS.out.cruiseState.enabled:
self.button_frame += 1
button_counter_offset = [1, 1, 0, None][self.button_frame % 4]
if ram_cars:
self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise)
elif button_counter_offset is not None:
self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise)
if self.cruise_button is not None:
if ram_cars:
can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter, das_bus, self.CP, buttons=self.cruise_button))
elif button_counter_offset is not None:
can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter + button_counter_offset, das_bus, self.CP, buttons=self.cruise_button))
# HUD alerts # HUD alerts
if self.frame % 25 == 0: if self.frame % 25 == 0:
if CS.lkas_car_model != -1: if CS.lkas_car_model != -1:
@@ -179,122 +106,3 @@ class CarController(CarControllerBase):
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last
return new_actuators, can_sends return new_actuators, can_sends
# multikyd methods, sunnyhaibin logic
def get_cruise_buttons_status(self, CS):
if not CS.out.cruiseState.enabled:
for be in CS.out.buttonEvents:
if be.type in (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.resumeCruise) and be.pressed:
self.timer = 40
elif self.timer:
self.timer -= 1
else:
return 1
return 0
def get_target_speed(self, v_cruise_kph_prev):
v_cruise_kph = v_cruise_kph_prev
if self.slc_state > 1:
v_cruise_kph = (self.speed_limit + self.speed_limit_offset) * CV.MS_TO_KPH
if not self.slc_active_stock:
v_cruise_kph = v_cruise_kph_prev
return v_cruise_kph
def get_button_type(self, button_type):
self.type_status = "type_" + str(button_type)
self.button_picker = getattr(self, self.type_status, lambda: "default")
return self.button_picker()
def reset_button(self):
if self.button_type != 3:
self.button_type = 0
def type_default(self):
self.button_type = 0
return None
def type_0(self):
self.button_count = 0
self.target_speed = self.init_speed
self.speed_diff = self.target_speed - self.v_set_dis
if self.target_speed > self.v_set_dis:
self.button_type = 1
elif self.target_speed < self.v_set_dis and self.v_set_dis > self.v_cruise_min:
self.button_type = 2
return None
def type_1(self):
cruise_button = 1
self.button_count += 1
if self.target_speed <= self.v_set_dis:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_2(self):
cruise_button = 2
self.button_count += 1
if self.target_speed >= self.v_set_dis or self.v_set_dis <= self.v_cruise_min:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_3(self):
cruise_button = None
self.button_count += 1
if self.button_count > self.t_interval:
self.button_type = 0
return cruise_button
def get_curve_speed(self, target_speed_kph, v_cruise_kph_prev):
if self.v_tsc_state != 0:
vision_v_cruise_kph = self.v_tsc * CV.MS_TO_KPH
if int(vision_v_cruise_kph) == int(v_cruise_kph_prev):
vision_v_cruise_kph = 255
else:
vision_v_cruise_kph = 255
if self.m_tsc_state > 1:
map_v_cruise_kph = self.m_tsc * CV.MS_TO_KPH
if int(map_v_cruise_kph) == 0.0:
map_v_cruise_kph = 255
else:
map_v_cruise_kph = 255
curve_speed = self.curve_speed_hysteresis(min(vision_v_cruise_kph, map_v_cruise_kph) + 2 * CV.MPH_TO_KPH)
return min(target_speed_kph, curve_speed)
def get_button_control(self, CS, final_speed, v_cruise_kph_prev):
self.init_speed = round(min(final_speed, v_cruise_kph_prev) * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1))
self.v_set_dis = round(CS.out.cruiseState.speed * (CV.MS_TO_MPH if not CS.params_list.is_metric else CV.MS_TO_KPH))
cruise_button = self.get_button_type(self.button_type)
return cruise_button
def curve_speed_hysteresis(self, cur_speed: float, hyst=(0.75 * CV.MPH_TO_KPH)):
if cur_speed > self.steady_speed:
self.steady_speed = cur_speed
elif cur_speed < self.steady_speed - hyst:
self.steady_speed = cur_speed
return self.steady_speed
def get_cruise_buttons(self, CS, v_cruise_kph_prev):
cruise_button = None
if not self.get_cruise_buttons_status(CS):
pass
elif CS.out.cruiseState.enabled:
set_speed_kph = self.get_target_speed(v_cruise_kph_prev)
if self.slc_state > 1:
target_speed_kph = set_speed_kph
else:
target_speed_kph = min(v_cruise_kph_prev, set_speed_kph)
if self.v_tsc_state != 0 or self.m_tsc_state > 1:
self.final_speed_kph = self.get_curve_speed(target_speed_kph, v_cruise_kph_prev)
else:
self.final_speed_kph = target_speed_kph
cruise_button = self.get_button_control(CS, self.final_speed_kph, v_cruise_kph_prev) # MPH/KPH based button presses
return cruise_button
-5
View File
@@ -65,14 +65,9 @@ def create_lkas_command(packer, CP, apply_steer, lkas_control_bit):
def create_cruise_buttons(packer, frame, bus, CP, cruise_buttons_msg=None, buttons=0, cancel=False, resume=False): def create_cruise_buttons(packer, frame, bus, CP, cruise_buttons_msg=None, buttons=0, cancel=False, resume=False):
acc_accel = 1 if buttons == 1 else 0
acc_decel = 1 if buttons == 2 else 0
values = { values = {
"ACC_Cancel": cancel, "ACC_Cancel": cancel,
"ACC_Resume": resume, "ACC_Resume": resume,
"ACC_Accel": acc_accel,
"ACC_Decel": acc_decel,
"COUNTER": frame % 0x10, "COUNTER": frame % 0x10,
} }
-2
View File
@@ -161,8 +161,6 @@ class CarInterface(CarInterfaceBase):
if self.low_speed_alert: if self.low_speed_alert:
events.add(car.CarEvent.EventName.belowSteerSpeed) events.add(car.CarEvent.EventName.belowSteerSpeed)
ret.customStockLong = self.update_custom_stock_long()
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
+1
View File
@@ -24,6 +24,7 @@ class CarInterface(CarInterfaceBase):
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.customStockLongAvailable = True
if Params().get("DongleId", encoding='utf8') in ("4fde83db16dc0802", "112e4d6e0cad05e1", "e36b272d5679115f", "24574459dd7fb3e0", "83a4e056c7072678"): if Params().get("DongleId", encoding='utf8') in ("4fde83db16dc0802", "112e4d6e0cad05e1", "e36b272d5679115f", "24574459dd7fb3e0", "83a4e056c7072678"):
ret.spFlags |= FordFlagsSP.SP_ENHANCED_LAT_CONTROL.value ret.spFlags |= FordFlagsSP.SP_ENHANCED_LAT_CONTROL.value
-184
View File
@@ -1,16 +1,12 @@
from collections import namedtuple from collections import namedtuple
from cereal import car from cereal import car
import cereal.messaging as messaging
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.common.params import Params
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, rate_limit, make_tester_present_msg, create_gas_interceptor_command from openpilot.selfdrive.car import DT_CTRL, rate_limit, make_tester_present_msg, create_gas_interceptor_command
from openpilot.selfdrive.car.honda import hondacan from openpilot.selfdrive.car.honda import hondacan
from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import HONDA_V_CRUISE_MIN
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@@ -128,48 +124,7 @@ class CarController(CarControllerBase):
self.last_steer = 0.0 self.last_steer = 0.0
self.last_button_frame = 0 self.last_button_frame = 0
self.sm = messaging.SubMaster(['longitudinalPlanSP'])
self.param_s = Params()
self.last_speed_limit_sign_tap_prev = False
self.speed_limit = 0.
self.speed_limit_offset = 0
self.timer = 0
self.final_speed_kph = 0
self.init_speed = 0
self.current_speed = 0
self.v_set_dis = 0
self.v_cruise_min = 0
self.button_type = 0
self.button_select = 0
self.button_count = 0
self.target_speed = 0
self.t_interval = 7
self.slc_active_stock = False
self.sl_force_active_timer = 0
self.v_tsc_state = 0
self.slc_state = 0
self.m_tsc_state = 0
self.cruise_button = None
self.speed_diff = 0
self.v_tsc = 0
self.m_tsc = 0
self.steady_speed = 0
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
if not self.CP.pcmCruiseSpeed:
self.sm.update(0)
if self.sm.updated['longitudinalPlanSP']:
self.v_tsc_state = self.sm['longitudinalPlanSP'].visionTurnControllerState
self.slc_state = self.sm['longitudinalPlanSP'].speedLimitControlState
self.m_tsc_state = self.sm['longitudinalPlanSP'].turnSpeedControlState
self.speed_limit = self.sm['longitudinalPlanSP'].speedLimit
self.speed_limit_offset = self.sm['longitudinalPlanSP'].speedLimitOffset
self.v_tsc = self.sm['longitudinalPlanSP'].visionTurnSpeed
self.m_tsc = self.sm['longitudinalPlanSP'].turnSpeed
self.v_cruise_min = HONDA_V_CRUISE_MIN[CS.params_list.is_metric] * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1)
actuators = CC.actuators actuators = CC.actuators
hud_control = CC.hudControl hud_control = CC.hudControl
conversion = hondacan.get_cruise_speed_conversion(self.CP.carFingerprint, CS.is_metric) conversion = hondacan.get_cruise_speed_conversion(self.CP.carFingerprint, CS.is_metric)
@@ -203,19 +158,6 @@ class CarController(CarControllerBase):
apply_steer = int(interp(-limited_steer * self.params.STEER_MAX, apply_steer = int(interp(-limited_steer * self.params.STEER_MAX,
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V)) self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
if not self.CP.pcmCruiseSpeed:
if not self.last_speed_limit_sign_tap_prev and CS.params_list.last_speed_limit_sign_tap:
self.sl_force_active_timer = self.frame
self.param_s.put_bool_nonblocking("LastSpeedLimitSignTap", False)
self.last_speed_limit_sign_tap_prev = CS.params_list.last_speed_limit_sign_tap
sl_force_active = CS.params_list.speed_limit_control_enabled and (self.frame < (self.sl_force_active_timer * DT_CTRL + 2.0))
sl_inactive = not sl_force_active and (not CS.params_list.speed_limit_control_enabled or (True if self.slc_state == 0 else False))
sl_temp_inactive = not sl_force_active and (CS.params_list.speed_limit_control_enabled and (True if self.slc_state == 1 else False))
slc_active = not sl_inactive and not sl_temp_inactive
self.slc_active_stock = slc_active
# Send CAN commands # Send CAN commands
can_sends = [] can_sends = []
@@ -265,15 +207,6 @@ class CarController(CarControllerBase):
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.CANCEL, self.CP.carFingerprint)) can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume: elif CC.cruiseControl.resume:
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint)) can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint))
elif CS.out.cruiseState.enabled and not self.CP.pcmCruiseSpeed:
self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise)
if self.cruise_button is not None:
send_freq = 1
if not (self.v_tsc_state != 0 or self.m_tsc_state > 1) and abs(self.target_speed - self.v_set_dis) <= 2:
send_freq = 3
if (self.frame - self.last_button_frame) * DT_CTRL > 0.01 * send_freq:
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, self.cruise_button, self.CP.carFingerprint))
self.last_button_frame = self.frame
else: else:
# Send gas and brake commands. # Send gas and brake commands.
@@ -335,120 +268,3 @@ class CarController(CarControllerBase):
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends
# multikyd methods, sunnyhaibin logic
def get_cruise_buttons_status(self, CS):
if not CS.out.cruiseState.enabled or CS.cruise_buttons != 0:
self.timer = 40
elif self.timer:
self.timer -= 1
else:
return 1
return 0
def get_target_speed(self, v_cruise_kph_prev):
v_cruise_kph = v_cruise_kph_prev
if self.slc_state > 1:
v_cruise_kph = (self.speed_limit + self.speed_limit_offset) * CV.MS_TO_KPH
if not self.slc_active_stock:
v_cruise_kph = v_cruise_kph_prev
return v_cruise_kph
def get_button_type(self, button_type):
self.type_status = "type_" + str(button_type)
self.button_picker = getattr(self, self.type_status, lambda: "default")
return self.button_picker()
def reset_button(self):
if self.button_type != 3:
self.button_type = 0
def type_default(self):
self.button_type = 0
return None
def type_0(self):
self.button_count = 0
self.target_speed = self.init_speed
self.speed_diff = self.target_speed - self.v_set_dis
if self.target_speed > self.v_set_dis:
self.button_type = 1
elif self.target_speed < self.v_set_dis and self.v_set_dis > self.v_cruise_min:
self.button_type = 2
return None
def type_1(self):
cruise_button = CruiseButtons.RES_ACCEL
self.button_count += 1
if self.target_speed <= self.v_set_dis:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_2(self):
cruise_button = CruiseButtons.DECEL_SET
self.button_count += 1
if self.target_speed >= self.v_set_dis or self.v_set_dis <= self.v_cruise_min:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_3(self):
cruise_button = None
self.button_count += 1
if self.button_count > self.t_interval:
self.button_type = 0
return cruise_button
def get_curve_speed(self, target_speed_kph, v_cruise_kph_prev):
if self.v_tsc_state != 0:
vision_v_cruise_kph = self.v_tsc * CV.MS_TO_KPH
if int(vision_v_cruise_kph) == int(v_cruise_kph_prev):
vision_v_cruise_kph = 255
else:
vision_v_cruise_kph = 255
if self.m_tsc_state > 1:
map_v_cruise_kph = self.m_tsc * CV.MS_TO_KPH
if int(map_v_cruise_kph) == 0.0:
map_v_cruise_kph = 255
else:
map_v_cruise_kph = 255
curve_speed = self.curve_speed_hysteresis(min(vision_v_cruise_kph, map_v_cruise_kph) + 2 * CV.MPH_TO_KPH)
return min(target_speed_kph, curve_speed)
def get_button_control(self, CS, final_speed, v_cruise_kph_prev):
self.init_speed = round(min(final_speed, v_cruise_kph_prev) * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1))
self.v_set_dis = round(CS.out.cruiseState.speed * (CV.MS_TO_MPH if not CS.params_list.is_metric else CV.MS_TO_KPH))
cruise_button = self.get_button_type(self.button_type)
return cruise_button
def curve_speed_hysteresis(self, cur_speed: float, hyst=(0.75 * CV.MPH_TO_KPH)):
if cur_speed > self.steady_speed:
self.steady_speed = cur_speed
elif cur_speed < self.steady_speed - hyst:
self.steady_speed = cur_speed
return self.steady_speed
def get_cruise_buttons(self, CS, v_cruise_kph_prev):
cruise_button = None
if not self.get_cruise_buttons_status(CS):
pass
elif CS.out.cruiseState.enabled:
set_speed_kph = self.get_target_speed(v_cruise_kph_prev)
if self.slc_state > 1:
target_speed_kph = set_speed_kph
else:
target_speed_kph = min(v_cruise_kph_prev, set_speed_kph)
if self.v_tsc_state != 0 or self.m_tsc_state > 1:
self.final_speed_kph = self.get_curve_speed(target_speed_kph, v_cruise_kph_prev)
else:
self.final_speed_kph = target_speed_kph
cruise_button = self.get_button_control(CS, self.final_speed_kph, v_cruise_kph_prev) # MPH/KPH based button presses
return cruise_button
-2
View File
@@ -329,8 +329,6 @@ class CarInterface(CarInterfaceBase):
if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001: if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001:
events.add(EventName.manualRestart) events.add(EventName.manualRestart)
ret.customStockLong = self.update_custom_stock_long()
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
-2
View File
@@ -296,8 +296,6 @@ class CarInterface(CarInterfaceBase):
if self.CS.params_list.hyundai_radar_tracks_available and not self.CS.params_list.hyundai_radar_tracks_available_cache: if self.CS.params_list.hyundai_radar_tracks_available and not self.CS.params_list.hyundai_radar_tracks_available_cache:
events.add(car.CarEvent.EventName.hyundaiRadarTracksAvailable) events.add(car.CarEvent.EventName.hyundaiRadarTracksAvailable)
ret.customStockLong = self.update_custom_stock_long()
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
-186
View File
@@ -1,14 +1,9 @@
from cereal import car from cereal import car
import cereal.messaging as messaging
from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits from openpilot.selfdrive.car import apply_driver_steer_torque_limits
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.mazda import mazdacan from openpilot.selfdrive.car.mazda import mazdacan
from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons
from openpilot.selfdrive.controls.lib.drive_helpers import MAZDA_V_CRUISE_MIN
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
@@ -21,63 +16,9 @@ class CarController(CarControllerBase):
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.brake_counter = 0 self.brake_counter = 0
self.sm = messaging.SubMaster(['longitudinalPlanSP'])
self.param_s = Params()
self.last_speed_limit_sign_tap_prev = False
self.speed_limit = 0.
self.speed_limit_offset = 0
self.timer = 0
self.final_speed_kph = 0
self.init_speed = 0
self.current_speed = 0
self.v_set_dis = 0
self.v_cruise_min = 0
self.button_type = 0
self.button_select = 0
self.button_count = 0
self.target_speed = 0
self.t_interval = 7
self.slc_active_stock = False
self.sl_force_active_timer = 0
self.v_tsc_state = 0
self.slc_state = 0
self.m_tsc_state = 0
self.cruise_button = None
self.speed_diff = 0
self.v_tsc = 0
self.m_tsc = 0
self.steady_speed = 0
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
if not self.CP.pcmCruiseSpeed:
self.sm.update(0)
if self.sm.updated['longitudinalPlanSP']:
self.v_tsc_state = self.sm['longitudinalPlanSP'].visionTurnControllerState
self.slc_state = self.sm['longitudinalPlanSP'].speedLimitControlState
self.m_tsc_state = self.sm['longitudinalPlanSP'].turnSpeedControlState
self.speed_limit = self.sm['longitudinalPlanSP'].speedLimit
self.speed_limit_offset = self.sm['longitudinalPlanSP'].speedLimitOffset
self.v_tsc = self.sm['longitudinalPlanSP'].visionTurnSpeed
self.m_tsc = self.sm['longitudinalPlanSP'].turnSpeed
self.v_cruise_min = MAZDA_V_CRUISE_MIN[CS.params_list.is_metric] * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1)
can_sends = [] can_sends = []
if not self.CP.pcmCruiseSpeed:
if not self.last_speed_limit_sign_tap_prev and CS.params_list.last_speed_limit_sign_tap:
self.sl_force_active_timer = self.frame
self.param_s.put_bool_nonblocking("LastSpeedLimitSignTap", False)
self.last_speed_limit_sign_tap_prev = CS.params_list.last_speed_limit_sign_tap
sl_force_active = CS.params_list.speed_limit_control_enabled and (self.frame < (self.sl_force_active_timer * DT_CTRL + 2.0))
sl_inactive = not sl_force_active and (not CS.params_list.speed_limit_control_enabled or (True if self.slc_state == 0 else False))
sl_temp_inactive = not sl_force_active and (CS.params_list.speed_limit_control_enabled and (True if self.slc_state == 1 else False))
slc_active = not sl_inactive and not sl_temp_inactive
self.slc_active_stock = slc_active
apply_steer = 0 apply_steer = 0
if CC.latActive: if CC.latActive:
@@ -102,11 +43,6 @@ class CarController(CarControllerBase):
# Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds # Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds
# Send Resume button when planner wants car to move # Send Resume button when planner wants car to move
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.RESUME)) can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.RESUME))
elif CS.out.cruiseState.enabled and not self.CP.pcmCruiseSpeed:
self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise)
if self.cruise_button is not None:
if self.frame % 10 == 0:
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, self.frame // 10, self.cruise_button))
self.apply_steer_last = apply_steer self.apply_steer_last = apply_steer
@@ -128,125 +64,3 @@ class CarController(CarControllerBase):
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends
# multikyd methods, sunnyhaibin logic
def get_cruise_buttons_status(self, CS):
if not CS.out.cruiseState.enabled:
for be in CS.out.buttonEvents:
if be.type in (ButtonType.accelCruise, ButtonType.resumeCruise,
ButtonType.decelCruise, ButtonType.setCruise) and be.pressed:
self.timer = 40
elif self.timer:
self.timer -= 1
else:
return 1
return 0
def get_target_speed(self, v_cruise_kph_prev):
v_cruise_kph = v_cruise_kph_prev
if self.slc_state > 1:
v_cruise_kph = (self.speed_limit + self.speed_limit_offset) * CV.MS_TO_KPH
if not self.slc_active_stock:
v_cruise_kph = v_cruise_kph_prev
return v_cruise_kph
def get_button_type(self, button_type):
self.type_status = "type_" + str(button_type)
self.button_picker = getattr(self, self.type_status, lambda: "default")
return self.button_picker()
def reset_button(self):
if self.button_type != 3:
self.button_type = 0
def type_default(self):
self.button_type = 0
return None
def type_0(self):
self.button_count = 0
self.target_speed = self.init_speed
self.speed_diff = self.target_speed - self.v_set_dis
if self.target_speed > self.v_set_dis:
return Buttons.SET_PLUS
self.button_type = 1
elif self.target_speed < self.v_set_dis and self.v_set_dis > self.v_cruise_min:
return Buttons.SET_MINUS
self.button_type = 2
return None
def type_1(self):
cruise_button = Buttons.SET_PLUS
self.button_count += 1
if self.target_speed <= self.v_set_dis:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_2(self):
cruise_button = Buttons.SET_MINUS
self.button_count += 1
if self.target_speed >= self.v_set_dis or self.v_set_dis <= self.v_cruise_min:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_3(self):
cruise_button = None
self.button_count += 1
if self.button_count > self.t_interval:
self.button_type = 0
return cruise_button
def get_curve_speed(self, target_speed_kph, v_cruise_kph_prev):
if self.v_tsc_state != 0:
vision_v_cruise_kph = self.v_tsc * CV.MS_TO_KPH
if int(vision_v_cruise_kph) == int(v_cruise_kph_prev):
vision_v_cruise_kph = 255
else:
vision_v_cruise_kph = 255
if self.m_tsc_state > 1:
map_v_cruise_kph = self.m_tsc * CV.MS_TO_KPH
if int(map_v_cruise_kph) == 0.0:
map_v_cruise_kph = 255
else:
map_v_cruise_kph = 255
curve_speed = self.curve_speed_hysteresis(min(vision_v_cruise_kph, map_v_cruise_kph) + 2 * CV.MPH_TO_KPH)
return min(target_speed_kph, curve_speed)
def get_button_control(self, CS, final_speed, v_cruise_kph_prev):
self.init_speed = round(min(final_speed, v_cruise_kph_prev) * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1))
self.v_set_dis = round(CS.out.cruiseState.speed * (CV.MS_TO_MPH if not CS.params_list.is_metric else CV.MS_TO_KPH))
cruise_button = self.get_button_type(self.button_type)
return cruise_button
def curve_speed_hysteresis(self, cur_speed: float, hyst=(0.75 * CV.MPH_TO_KPH)):
if cur_speed > self.steady_speed:
self.steady_speed = cur_speed
elif cur_speed < self.steady_speed - hyst:
self.steady_speed = cur_speed
return self.steady_speed
def get_cruise_buttons(self, CS, v_cruise_kph_prev):
cruise_button = None
if not self.get_cruise_buttons_status(CS):
pass
elif CS.out.cruiseState.enabled:
set_speed_kph = self.get_target_speed(v_cruise_kph_prev)
if self.slc_state > 1:
target_speed_kph = set_speed_kph
else:
target_speed_kph = min(v_cruise_kph_prev, set_speed_kph)
if self.v_tsc_state != 0 or self.m_tsc_state > 1:
self.final_speed_kph = self.get_curve_speed(target_speed_kph, v_cruise_kph_prev)
else:
self.final_speed_kph = target_speed_kph
cruise_button = self.get_button_control(CS, self.final_speed_kph, v_cruise_kph_prev) # MPH/KPH based button presses
return cruise_button
-2
View File
@@ -89,8 +89,6 @@ class CarInterface(CarInterfaceBase):
if self.CS.low_speed_alert: if self.CS.low_speed_alert:
events.add(EventName.belowSteerSpeed) events.add(EventName.belowSteerSpeed)
ret.customStockLong = self.update_custom_stock_long()
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
+4 -6
View File
@@ -92,22 +92,20 @@ def create_button_cmd(packer, CP, counter, button):
can = int(button == Buttons.CANCEL) can = int(button == Buttons.CANCEL)
res = int(button == Buttons.RESUME) res = int(button == Buttons.RESUME)
inc = int(button == Buttons.SET_PLUS)
dec = int(button == Buttons.SET_MINUS)
if CP.flags & MazdaFlags.GEN1: if CP.flags & MazdaFlags.GEN1:
values = { values = {
"CAN_OFF": can, "CAN_OFF": can,
"CAN_OFF_INV": (can + 1) % 2, "CAN_OFF_INV": (can + 1) % 2,
"SET_P": inc, "SET_P": 0,
"SET_P_INV": (inc + 1) % 2, "SET_P_INV": 1,
"RES": res, "RES": res,
"RES_INV": (res + 1) % 2, "RES_INV": (res + 1) % 2,
"SET_M": dec, "SET_M": 0,
"SET_M_INV": (dec + 1) % 2, "SET_M_INV": 1,
"DISTANCE_LESS": 0, "DISTANCE_LESS": 0,
"DISTANCE_LESS_INV": 1, "DISTANCE_LESS_INV": 1,
+3 -8
View File
@@ -160,7 +160,7 @@ class CarInterface(CarInterfaceBase):
sp_tss2_long_tune = Params().get_bool("ToyotaTSS2Long") sp_tss2_long_tune = Params().get_bool("ToyotaTSS2Long")
# hand tuned (August 12, 2024) # hand tuned (August 26, 2024)
def custom_tss2_longitudinal_tuning(): def custom_tss2_longitudinal_tuning():
ret.vEgoStopping = 0.25 ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25 ret.vEgoStarting = 0.25
@@ -178,13 +178,8 @@ class CarInterface(CarInterfaceBase):
tune = ret.longitudinalTuning tune = ret.longitudinalTuning
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED: if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
if sp_tss2_long_tune: if sp_tss2_long_tune:
#tune.kiBP = [0., 0.1, 3., 12., 20., 26., 36., 50] tune.kiBP = [0., 3., 8., 12., 20., 27., 36., 50]
#tune.kiV = [0.34, 0.35, 0.2896, 0.2237, 0.174, 0.10, 0.08, 0.06] tune.kiV = [0.322, 0.244, 0.224, 0.202, 0.17, 0.12, 0.08, 0.06]
#smooth
tune.kiBP = [0., 8., 12., 20., 27., 36., 50]
tune.kiV = [0.326, 0.206, 0.20, 0.17, 0.12, 0.08, 0.06]
#tune.kiBP = [0., 2., 5., 12., 16, 20., 27., 36., 50]
#tune.kiV = [0.27, 0.24, 0.2205, 0.20, 0.18, 0.17, 0.12, 0.08, 0.06]
custom_tss2_longitudinal_tuning() custom_tss2_longitudinal_tuning()
else: else:
tune.kpV = [0.0] tune.kpV = [0.0]
-205
View File
@@ -1,14 +1,11 @@
from cereal import car from cereal import car
import cereal.messaging as messaging
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.common.numpy_fast import clip from openpilot.common.numpy_fast import clip
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.volkswagen import mqbcan, pqcan from openpilot.selfdrive.car.volkswagen import mqbcan, pqcan
from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags
from openpilot.selfdrive.controls.lib.drive_helpers import VOLKSWAGEN_V_CRUISE_MIN
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@@ -28,70 +25,12 @@ class CarController(CarControllerBase):
self.eps_timer_soft_disable_alert = False self.eps_timer_soft_disable_alert = False
self.hca_frame_timer_running = 0 self.hca_frame_timer_running = 0
self.hca_frame_same_torque = 0 self.hca_frame_same_torque = 0
self.last_button_frame = 0
self.sm = messaging.SubMaster(['longitudinalPlanSP'])
self.param_s = Params()
self.last_speed_limit_sign_tap_prev = False
self.speed_limit = 0.
self.speed_limit_offset = 0
self.timer = 0
self.final_speed_kph = 0
self.init_speed = 0
self.current_speed = 0
self.v_set_dis = 0
self.v_set_dis_prev = 0
self.v_cruise_min = 0
self.button_type = 0
self.button_select = 0
self.button_count = 0
self.target_speed = 0
self.t_interval = 7
self.slc_active_stock = False
self.sl_force_active_timer = 0
self.v_tsc_state = 0
self.slc_state = 0
self.m_tsc_state = 0
self.cruise_button = None
self.last_cruise_button = None
self.speed_diff = 0
self.v_tsc = 0
self.m_tsc = 0
self.steady_speed = 0
self.acc_type = -1
self.send_count = 0
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
if not self.CP.pcmCruiseSpeed:
self.sm.update(0)
if self.sm.updated['longitudinalPlanSP']:
self.v_tsc_state = self.sm['longitudinalPlanSP'].visionTurnControllerState
self.slc_state = self.sm['longitudinalPlanSP'].speedLimitControlState
self.m_tsc_state = self.sm['longitudinalPlanSP'].turnSpeedControlState
self.speed_limit = self.sm['longitudinalPlanSP'].speedLimit
self.speed_limit_offset = self.sm['longitudinalPlanSP'].speedLimitOffset
self.v_tsc = self.sm['longitudinalPlanSP'].visionTurnSpeed
self.m_tsc = self.sm['longitudinalPlanSP'].turnSpeed
self.v_cruise_min = VOLKSWAGEN_V_CRUISE_MIN[CS.params_list.is_metric] * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1)
actuators = CC.actuators actuators = CC.actuators
hud_control = CC.hudControl hud_control = CC.hudControl
can_sends = [] can_sends = []
if not self.CP.pcmCruiseSpeed:
if not self.last_speed_limit_sign_tap_prev and CS.params_list.last_speed_limit_sign_tap:
self.sl_force_active_timer = self.frame
self.param_s.put_bool_nonblocking("LastSpeedLimitSignTap", False)
self.last_speed_limit_sign_tap_prev = CS.params_list.last_speed_limit_sign_tap
sl_force_active = CS.params_list.speed_limit_control_enabled and (self.frame < (self.sl_force_active_timer * DT_CTRL + 2.0))
sl_inactive = not sl_force_active and (not CS.params_list.speed_limit_control_enabled or (True if self.slc_state == 0 else False))
sl_temp_inactive = not sl_force_active and (CS.params_list.speed_limit_control_enabled and (True if self.slc_state == 1 else False))
slc_active = not sl_inactive and not sl_temp_inactive
self.slc_active_stock = slc_active
# **** Steering Controls ************************************************ # # **** Steering Controls ************************************************ #
if self.frame % self.CCP.STEER_STEP == 0: if self.frame % self.CCP.STEER_STEP == 0:
@@ -171,155 +110,11 @@ class CarController(CarControllerBase):
if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume): if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume):
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values, can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume)) cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
if not (CC.cruiseControl.cancel or CC.cruiseControl.resume) and CS.out.cruiseState.enabled:
if not self.CP.pcmCruiseSpeed:
self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise)
if self.cruise_button is not None:
if self.acc_type == -1:
if self.button_count >= 2 and self.v_set_dis_prev != self.v_set_dis:
self.acc_type = 1 if abs(self.v_set_dis - self.v_set_dis_prev) >= 10 and self.last_cruise_button in (1, 2) else \
0 if abs(self.v_set_dis - self.v_set_dis_prev) < 10 and self.last_cruise_button not in (1, 2) else 1
if self.send_count >= 10 and self.v_set_dis_prev == self.v_set_dis:
self.cruise_button = 3 if self.cruise_button == 1 else 4
if self.acc_type == 0:
self.cruise_button = 1 if self.cruise_button == 1 else 2 # accel, decel
elif self.acc_type == 1:
self.cruise_button = 3 if self.cruise_button == 1 else 4 # resume, set
if self.frame % self.CCP.BTN_STEP == 0:
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, CS.gra_stock_values, frame=(self.frame // self.CCP.BTN_STEP),
buttons=self.cruise_button, custom_stock_long=True))
self.send_count += 1
else:
self.send_count = 0
self.last_cruise_button = self.cruise_button
new_actuators = actuators.as_builder() new_actuators = actuators.as_builder()
new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last
self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"] self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"]
self.v_set_dis_prev = self.v_set_dis
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends
# multikyd methods, sunnyhaibin logic
def get_cruise_buttons_status(self, CS):
if not CS.out.cruiseState.enabled:
for be in CS.out.buttonEvents:
if be.type in (ButtonType.accelCruise, ButtonType.resumeCruise,
ButtonType.decelCruise, ButtonType.setCruise) and be.pressed:
self.timer = 40
elif be.type == ButtonType.gapAdjustCruise and be.pressed:
self.timer = 300
elif self.timer:
self.timer -= 1
else:
return 1
return 0
def get_target_speed(self, v_cruise_kph_prev):
v_cruise_kph = v_cruise_kph_prev
if self.slc_state > 1:
v_cruise_kph = (self.speed_limit + self.speed_limit_offset) * CV.MS_TO_KPH
if not self.slc_active_stock:
v_cruise_kph = v_cruise_kph_prev
return v_cruise_kph
def get_button_type(self, button_type):
self.type_status = "type_" + str(button_type)
self.button_picker = getattr(self, self.type_status, lambda: "default")
return self.button_picker()
def reset_button(self):
if self.button_type != 3:
self.button_type = 0
def type_default(self):
self.button_type = 0
return None
def type_0(self):
self.button_count = 0
self.target_speed = self.init_speed
self.speed_diff = self.target_speed - self.v_set_dis
if self.target_speed > self.v_set_dis:
self.button_type = 1
elif self.target_speed < self.v_set_dis and self.v_set_dis > self.v_cruise_min:
self.button_type = 2
return None
def type_1(self):
cruise_button = 1
self.button_count += 1
if self.target_speed <= self.v_set_dis:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_2(self):
cruise_button = 2
self.button_count += 1
if self.target_speed >= self.v_set_dis or self.v_set_dis <= self.v_cruise_min:
self.button_count = 0
self.button_type = 3
elif self.button_count > 5:
self.button_count = 0
self.button_type = 3
return cruise_button
def type_3(self):
cruise_button = None
self.button_count += 1
if self.button_count > self.t_interval:
self.button_type = 0
return cruise_button
def get_curve_speed(self, target_speed_kph, v_cruise_kph_prev):
if self.v_tsc_state != 0:
vision_v_cruise_kph = self.v_tsc * CV.MS_TO_KPH
if int(vision_v_cruise_kph) == int(v_cruise_kph_prev):
vision_v_cruise_kph = 255
else:
vision_v_cruise_kph = 255
if self.m_tsc_state > 1:
map_v_cruise_kph = self.m_tsc * CV.MS_TO_KPH
if int(map_v_cruise_kph) == 0.0:
map_v_cruise_kph = 255
else:
map_v_cruise_kph = 255
curve_speed = self.curve_speed_hysteresis(min(vision_v_cruise_kph, map_v_cruise_kph) + 2 * CV.MPH_TO_KPH)
return min(target_speed_kph, curve_speed)
def get_button_control(self, CS, final_speed, v_cruise_kph_prev):
self.init_speed = round(min(final_speed, v_cruise_kph_prev) * (CV.KPH_TO_MPH if not CS.params_list.is_metric else 1))
self.v_set_dis = round(CS.out.cruiseState.speed * (CV.MS_TO_MPH if not CS.params_list.is_metric else CV.MS_TO_KPH))
cruise_button = self.get_button_type(self.button_type)
return cruise_button
def curve_speed_hysteresis(self, cur_speed: float, hyst=(0.75 * CV.MPH_TO_KPH)):
if cur_speed > self.steady_speed:
self.steady_speed = cur_speed
elif cur_speed < self.steady_speed - hyst:
self.steady_speed = cur_speed
return self.steady_speed
def get_cruise_buttons(self, CS, v_cruise_kph_prev):
cruise_button = None
if not self.get_cruise_buttons_status(CS):
pass
elif CS.out.cruiseState.enabled:
set_speed_kph = self.get_target_speed(v_cruise_kph_prev)
if self.slc_state > 1:
target_speed_kph = set_speed_kph
else:
target_speed_kph = min(v_cruise_kph_prev, set_speed_kph)
if self.v_tsc_state != 0 or self.m_tsc_state > 1:
self.final_speed_kph = self.get_curve_speed(target_speed_kph, v_cruise_kph_prev)
else:
self.final_speed_kph = target_speed_kph
cruise_button = self.get_button_control(CS, self.final_speed_kph, v_cruise_kph_prev) # MPH/KPH based button presses
return cruise_button
-2
View File
@@ -171,8 +171,6 @@ class CarInterface(CarInterfaceBase):
if self.CC.eps_timer_soft_disable_alert: if self.CC.eps_timer_soft_disable_alert:
events.add(EventName.steerTimeLimit) events.add(EventName.steerTimeLimit)
ret.customStockLong = self.update_custom_stock_long()
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
+3 -11
View File
@@ -49,7 +49,7 @@ def create_lka_hud_control(packer, bus, ldw_stock_values, lat_active, steering_p
return packer.make_can_msg("LDW_02", bus, values) return packer.make_can_msg("LDW_02", bus, values)
def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0, cancel=False, resume=False, custom_stock_long=False): def create_acc_buttons_control(packer, bus, gra_stock_values, cancel=False, resume=False):
values = {s: gra_stock_values[s] for s in [ values = {s: gra_stock_values[s] for s in [
"GRA_Hauptschalter", # ACC button, on/off "GRA_Hauptschalter", # ACC button, on/off
"GRA_Typ_Hauptschalter", # ACC main button type "GRA_Typ_Hauptschalter", # ACC main button type
@@ -58,18 +58,10 @@ def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0
"GRA_ButtonTypeInfo", # unknown related to stalk type "GRA_ButtonTypeInfo", # unknown related to stalk type
]} ]}
accel_cruise = 1 if buttons == 1 else 0
decel_cruise = 1 if buttons == 2 else 0
resume_cruise = 1 if buttons == 3 else 0
set_cruise = 1 if buttons == 4 else 0
values.update({ values.update({
"COUNTER": (frame + 1) % 0x10 if custom_stock_long else (gra_stock_values["COUNTER"] + 1) % 16, "COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
"GRA_Abbrechen": cancel, "GRA_Abbrechen": cancel,
"GRA_Tip_Wiederaufnahme": resume or resume_cruise, "GRA_Tip_Wiederaufnahme": resume,
"GRA_Tip_Setzen": set_cruise,
"GRA_Tip_Runter": decel_cruise,
"GRA_Tip_Hoch": accel_cruise,
}) })
return packer.make_can_msg("GRA_ACC_01", bus, values) return packer.make_can_msg("GRA_ACC_01", bus, values)
+3 -11
View File
@@ -31,7 +31,7 @@ def create_lka_hud_control(packer, bus, ldw_stock_values, lat_active, steering_p
return packer.make_can_msg("LDW_Status", bus, values) return packer.make_can_msg("LDW_Status", bus, values)
def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0, cancel=False, resume=False, custom_stock_long=False): def create_acc_buttons_control(packer, bus, gra_stock_values, cancel=False, resume=False):
values = {s: gra_stock_values[s] for s in [ values = {s: gra_stock_values[s] for s in [
"GRA_Hauptschalt", # ACC button, on/off "GRA_Hauptschalt", # ACC button, on/off
"GRA_Typ_Hauptschalt", # ACC button, momentary vs latching "GRA_Typ_Hauptschalt", # ACC button, momentary vs latching
@@ -39,18 +39,10 @@ def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0
"GRA_Sender", # ACC button, CAN message originator "GRA_Sender", # ACC button, CAN message originator
]} ]}
accel_cruise = 1 if buttons == 1 else 0
decel_cruise = 1 if buttons == 2 else 0
resume_cruise = 1 if buttons == 3 else 0
set_cruise = 1 if buttons == 4 else 0
values.update({ values.update({
"COUNTER": (frame + 1) % 0x10 if custom_stock_long else (gra_stock_values["COUNTER"] + 1) % 16, "COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
"GRA_Abbrechen": cancel, "GRA_Abbrechen": cancel,
"GRA_Recall": resume or resume_cruise, "GRA_Recall": resume,
"GRA_Neu_Setzen": set_cruise,
"GRA_Down_kurz": decel_cruise,
"GRA_Up_kurz": accel_cruise,
}) })
return packer.make_can_msg("GRA_Neu", bus, values) return packer.make_can_msg("GRA_Neu", bus, values)
@@ -92,14 +92,14 @@ def get_dynamic_personality(v_ego, personality=custom.LongitudinalPersonalitySP.
x_vel = [0, 5., 5.01, 20., 27.7] x_vel = [0, 5., 5.01, 20., 27.7]
y_dist = [1.0, 1.0, 1.75, 1.75, 1.83] y_dist = [1.0, 1.0, 1.75, 1.75, 1.83]
elif personality==custom.LongitudinalPersonalitySP.standard: elif personality==custom.LongitudinalPersonalitySP.standard:
x_vel = [0, 5., 5.01, 20., 27.7] x_vel = [0, 20., 27.7]
y_dist = [1.45, 1.45, 1.75, 1.75, 1.70] y_dist = [1.75, 1.75, 1.70]
elif personality==custom.LongitudinalPersonalitySP.moderate: elif personality==custom.LongitudinalPersonalitySP.moderate:
x_vel = [0, 27.69, 27.7] x_vel = [0, 27.69, 27.7]
y_dist = [1.45, 1.45, 1.40] y_dist = [1.45, 1.45, 1.40]
elif personality==custom.LongitudinalPersonalitySP.aggressive: elif personality==custom.LongitudinalPersonalitySP.aggressive:
x_vel = [0, 20.0, 20.01, 27.69, 27.7] x_vel = [0, 20.0, 20.01, 27.69, 27.7]
y_dist = [1.0, 1.0, 1.10, 1.10, 1.20] y_dist = [1.07, 1.07, 1.12, 1.12, 1.20]
else: else:
raise NotImplementedError("Dynamic personality not supported") raise NotImplementedError("Dynamic personality not supported")
return np.interp(v_ego, x_vel, y_dist) return np.interp(v_ego, x_vel, y_dist)
@@ -36,6 +36,7 @@ _A_TOTAL_MAX_V = [1.7, 3.2]
_A_TOTAL_MAX_BP = [20., 40.] _A_TOTAL_MAX_BP = [20., 40.]
MpcSource = custom.MpcSource
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@@ -131,10 +132,13 @@ class LongitudinalPlanner:
self.read_param() self.read_param()
self.param_read_counter += 1 self.param_read_counter += 1
if self.dynamic_experimental_controller.is_enabled() and sm['controlsState'].experimentalMode: if self.dynamic_experimental_controller.is_enabled() and sm['controlsState'].experimentalMode:
self.mpc.mode = self.dynamic_experimental_controller.get_mpc_mode(self.CP.radarUnavailable, sm['carState'], sm['radarState'].leadOne, sm['modelV2'], sm['controlsState'], sm['navInstruction'].maneuverDistance) self.dynamic_experimental_controller.set_mpc_fcw_crash_cnt(self.mpc.crash_cnt)
self.dynamic_experimental_controller.update(self.CP.radarUnavailable, sm['carState'], sm['radarState'].leadOne, sm['modelV2'], sm['controlsState'], sm['navInstruction'].maneuverDistance)
self.mpc.mode = self.dynamic_experimental_controller.get_mpc_mode()
else: else:
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc' self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
v_ego = sm['carState'].vEgo v_ego = sm['carState'].vEgo
v_cruise_kph = min(sm['controlsState'].vCruise, V_CRUISE_MAX) v_cruise_kph = min(sm['controlsState'].vCruise, V_CRUISE_MAX)
v_cruise = v_cruise_kph * CV.KPH_TO_MS v_cruise = v_cruise_kph * CV.KPH_TO_MS
@@ -267,7 +271,8 @@ class LongitudinalPlanner:
longitudinalPlanSP.turnSpeedControlState = self.turn_speed_controller.state longitudinalPlanSP.turnSpeedControlState = self.turn_speed_controller.state
longitudinalPlanSP.turnSpeed = float(self.turn_speed_controller.v_target) longitudinalPlanSP.turnSpeed = float(self.turn_speed_controller.v_target)
longitudinalPlanSP.e2eBlended = self.mpc.mode longitudinalPlanSP.mpcSource = MpcSource.blended if self.mpc.mode == 'blended' else MpcSource.acc
longitudinalPlanSP.dynamicExperimentalControl = self.dynamic_experimental_controller.is_enabled()
pm.send('longitudinalPlanSP', plan_sp_send) pm.send('longitudinalPlanSP', plan_sp_send)
@@ -29,13 +29,13 @@ from openpilot.common.numpy_fast import interp
AccelPersonality = custom.AccelerationPersonality AccelPersonality = custom.AccelerationPersonality
# accel personality by @arne182 modified by cgw and kumar # accel personality by @arne182 modified by cgw and kumar
_DP_CRUISE_MIN_V = [-0.031, -0.031, -0.101, -0.101, -0.39, -0.39, -0.59, -0.59, -0.79, -0.79, -1.0, -1.0] _DP_CRUISE_MIN_V = [-0.031, -0.031, -0.080, -0.080, -0.19, -0.19, -0.59, -0.59, -0.79, -0.79, -1.0, -1.0]
_DP_CRUISE_MIN_V_ECO = [-0.030, -0.030, -0.100, -0.100, -0.38, -0.38, -0.58, -0.58, -0.78, -0.78, -1.0, -1.0] _DP_CRUISE_MIN_V_ECO = [-0.030, -0.030, -0.075, -0.075, -0.18, -0.18, -0.58, -0.58, -0.78, -0.78, -1.0, -1.0]
_DP_CRUISE_MIN_V_SPORT = [-0.102, -0.102, -0.102, -0.102, -0.40, -0.40, -0.60, -0.60, -0.80, -0.80, -1.0, -1.0] _DP_CRUISE_MIN_V_SPORT = [-0.102, -0.102, -0.085, -0.085, -0.20, -0.20, -0.60, -0.60, -0.80, -0.80, -1.0, -1.0]
_DP_CRUISE_MIN_BP = [0., 5.0, 5.01, 10., 10.01, 14., 14.01, 18., 18.01, 22., 22.01, 30.] _DP_CRUISE_MIN_BP = [0., 3.0, 3.01, 10., 10.01, 14., 14.01, 18., 18.01, 22., 22.01, 30.]
_DP_CRUISE_MAX_V = [2.0, 2.0, 1.9, 1.60, 1.11, .73, .55, .38, .17] _DP_CRUISE_MAX_V = [2.0, 2.0, 2.0, 1.70, 1.11, .70, .54, .38, .17]
_DP_CRUISE_MAX_V_ECO = [2.0, 2.0, 1.8, 1.35, 0.86, .53, .43, .32, .09] _DP_CRUISE_MAX_V_ECO = [2.0, 2.0, 1.8, 1.40, 0.90, .53, .43, .32, .09]
_DP_CRUISE_MAX_V_SPORT = [2.0, 2.0, 2.0, 2.00, 1.40, .90, .70, .50, .30] _DP_CRUISE_MAX_V_SPORT = [2.0, 2.0, 2.0, 2.00, 1.40, .90, .70, .50, .30]
_DP_CRUISE_MAX_BP = [0., 4., 6., 8., 11., 20., 25., 30., 40.] _DP_CRUISE_MAX_BP = [0., 4., 6., 8., 11., 20., 25., 30., 40.]
@@ -21,33 +21,39 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
# #
# Version = 2024-1-29 # Version = 2024-8-26
from common.numpy_fast import interp from common.numpy_fast import interp
from openpilot.selfdrive.controls.lib.lateral_planner import TRAJECTORY_SIZE import numpy as np
LEAD_WINDOW_SIZE = 5 # d-e2e, from modeldata.h
TRAJECTORY_SIZE = 33
LEAD_WINDOW_SIZE = 4
LEAD_PROB = 0.6 LEAD_PROB = 0.6
SLOW_DOWN_WINDOW_SIZE = 5 SLOW_DOWN_WINDOW_SIZE = 5
SLOW_DOWN_PROB = 0.6 SLOW_DOWN_PROB = 0.6
SLOW_DOWN_BP = [0., 10., 20., 30., 40., 50., 55., 60.] SLOW_DOWN_BP = [0., 10., 20., 30., 40., 50., 55., 60.]
SLOW_DOWN_DIST = [20, 30., 50., 70., 80., 90., 105., 120.] SLOW_DOWN_DIST = [20, 30., 50., 70., 80., 90., 105., 120.]
SLOWNESS_WINDOW_SIZE = 20 SLOWNESS_WINDOW_SIZE = 12
SLOWNESS_PROB = 0.6 SLOWNESS_PROB = 0.5
SLOWNESS_CRUISE_OFFSET = 1.05 SLOWNESS_CRUISE_OFFSET = 1.05
DANGEROUS_TTC_WINDOW_SIZE = 5 DANGEROUS_TTC_WINDOW_SIZE = 3
DANGEROUS_TTC = 2.0 DANGEROUS_TTC = 2.3
HIGHWAY_CRUISE_KPH = 75 HIGHWAY_CRUISE_KPH = 70
STOP_AND_GO_FRAME = 60 STOP_AND_GO_FRAME = 60
SET_MODE_TIMEOUT = 10 SET_MODE_TIMEOUT = 10
MPC_FCW_WINDOW_SIZE = 5 MPC_FCW_WINDOW_SIZE = 10
MPC_FCW_PROB = 0.6 MPC_FCW_PROB = 0.5
V_ACC_MIN = 9.72
class SNG_State: class SNG_State:
@@ -77,29 +83,50 @@ class GenericMovingAverageCalculator:
self.data = [] self.data = []
self.total = 0 self.total = 0
class WeightedMovingAverageCalculator:
def __init__(self, window_size):
self.window_size = window_size
self.data = []
self.weights = np.linspace(1, 2, window_size) # Linear weights, adjust as needed
def add_data(self, value):
if len(self.data) == self.window_size:
self.data.pop(0)
self.data.append(value)
def get_weighted_average(self):
if len(self.data) == 0:
return None
weighted_sum = np.dot(self.data, self.weights[-len(self.data):])
weight_total = np.sum(self.weights[-len(self.data):])
return weighted_sum / weight_total
def reset_data(self):
self.data = []
class DynamicExperimentalController: class DynamicExperimentalController:
def __init__(self): def __init__(self):
self._is_enabled = False self._is_enabled = False
self._mode = 'acc' self._mode = 'acc'
self._mode_prev = 'acc' self._mode_prev = 'acc'
self._mode_changed = False
self._frame = 0 self._frame = 0
self._lead_gmac = GenericMovingAverageCalculator(window_size=LEAD_WINDOW_SIZE) self._lead_gmac = WeightedMovingAverageCalculator(window_size=LEAD_WINDOW_SIZE)
self._has_lead_filtered = False self._has_lead_filtered = False
self._has_lead_filtered_prev = False self._has_lead_filtered_prev = False
self._slow_down_gmac = GenericMovingAverageCalculator(window_size=SLOW_DOWN_WINDOW_SIZE) self._slow_down_gmac = WeightedMovingAverageCalculator(window_size=SLOW_DOWN_WINDOW_SIZE)
self._has_slow_down = False self._has_slow_down = False
self._has_blinkers = False self._has_blinkers = False
self._slowness_gmac = GenericMovingAverageCalculator(window_size=SLOWNESS_WINDOW_SIZE) self._slowness_gmac = WeightedMovingAverageCalculator(window_size=SLOWNESS_WINDOW_SIZE)
self._has_slowness = False self._has_slowness = False
self._has_nav_instruction = False self._has_nav_instruction = False
self._dangerous_ttc_gmac = GenericMovingAverageCalculator(window_size=DANGEROUS_TTC_WINDOW_SIZE) self._dangerous_ttc_gmac = WeightedMovingAverageCalculator(window_size=DANGEROUS_TTC_WINDOW_SIZE)
self._has_dangerous_ttc = False self._has_dangerous_ttc = False
self._v_ego_kph = 0. self._v_ego_kph = 0.
@@ -113,13 +140,46 @@ class DynamicExperimentalController:
self._sng_transit_frame = 0 self._sng_transit_frame = 0
self._sng_state = SNG_State.off self._sng_state = SNG_State.off
self._mpc_fcw_gmac = GenericMovingAverageCalculator(window_size=MPC_FCW_WINDOW_SIZE) self._mpc_fcw_gmac = WeightedMovingAverageCalculator(window_size=MPC_FCW_WINDOW_SIZE)
self._has_mpc_fcw = False self._has_mpc_fcw = False
self._mpc_fcw_crash_cnt = 0 self._mpc_fcw_crash_cnt = 0
self._set_mode_timeout = 0 self._set_mode_timeout = 0
pass pass
def _adaptive_slowdown_threshold(self):
"""
Adapts the slow down threshold based on vehicle speed and recent behavior.
"""
return interp(self._v_ego_kph, SLOW_DOWN_BP, SLOW_DOWN_DIST) * (1.0 + 0.05 * np.log(1 + len(self._slow_down_gmac.data)))
def _anomaly_detection(self, recent_data, threshold=2.0):
"""
Basic anomaly detection using standard deviation.
"""
if len(recent_data) < 3:
return False
mean = np.mean(recent_data)
std_dev = np.std(recent_data)
anomaly = recent_data[-1] > mean + threshold * std_dev
return anomaly
def _smoothed_lead_detection(self, lead_prob, smoothing_factor=0.2):
"""
Smoothing the lead detection to avoid erratic behavior.
"""
self._has_lead_filtered = (1 - smoothing_factor) * self._has_lead_filtered + smoothing_factor * lead_prob
return self._has_lead_filtered > LEAD_PROB
def _adaptive_lead_prob_threshold(self):
"""
Adapts lead probability threshold based on driving conditions.
"""
if self._v_ego_kph > HIGHWAY_CRUISE_KPH:
return LEAD_PROB + 0.1 # Increase the threshold on highways
return LEAD_PROB
def _update(self, car_state, lead_one, md, controls_state, maneuver_distance): def _update(self, car_state, lead_one, md, controls_state, maneuver_distance):
self._v_ego_kph = car_state.vEgo * 3.6 self._v_ego_kph = car_state.vEgo * 3.6
self._v_cruise_kph = controls_state.vCruise self._v_cruise_kph = controls_state.vCruise
@@ -128,18 +188,27 @@ class DynamicExperimentalController:
# fcw detection # fcw detection
self._mpc_fcw_gmac.add_data(self._mpc_fcw_crash_cnt > 0) self._mpc_fcw_gmac.add_data(self._mpc_fcw_crash_cnt > 0)
self._has_mpc_fcw = self._mpc_fcw_gmac.get_moving_average() >= MPC_FCW_PROB self._has_mpc_fcw = self._mpc_fcw_gmac.get_weighted_average() > MPC_FCW_PROB
# nav enable detection # nav enable detection
self._has_nav_instruction = md.navEnabledDEPRECATED and maneuver_distance / max(car_state.vEgo, 1) < 13 self._has_nav_instruction = md.navEnabledDEPRECATED and maneuver_distance / max(car_state.vEgo, 1) < 13
# lead detection # lead detection with smoothing
self._lead_gmac.add_data(lead_one.status) self._lead_gmac.add_data(lead_one.status)
self._has_lead_filtered = self._lead_gmac.get_moving_average() >= LEAD_PROB self._has_lead_filtered = self._lead_gmac.get_weighted_average() > LEAD_PROB
#lead_prob = self._lead_gmac.get_weighted_average() or 0
#self._has_lead_filtered = self._smoothed_lead_detection(lead_prob)
# slow down detection # adaptive slow down detection
self._slow_down_gmac.add_data(len(md.orientation.x) == len(md.position.x) == TRAJECTORY_SIZE and md.position.x[TRAJECTORY_SIZE - 1] < interp(self._v_ego_kph, SLOW_DOWN_BP, SLOW_DOWN_DIST)) adaptive_threshold = self._adaptive_slowdown_threshold()
self._has_slow_down = self._slow_down_gmac.get_moving_average() >= SLOW_DOWN_PROB slow_down_trigger = len(md.orientation.x) == len(md.position.x) == TRAJECTORY_SIZE and md.position.x[TRAJECTORY_SIZE - 1] < adaptive_threshold
self._slow_down_gmac.add_data(slow_down_trigger)
self._has_slow_down = self._slow_down_gmac.get_weighted_average() > SLOW_DOWN_PROB
# anomaly detection for slow down events
if self._anomaly_detection(self._slow_down_gmac.data):
# Handle anomaly: potentially log it, adjust behavior, or issue a warning
self._has_slow_down = False # Reset slow down if anomaly detected
# blinker detection # blinker detection
self._has_blinkers = car_state.leftBlinker or car_state.rightBlinker self._has_blinkers = car_state.leftBlinker or car_state.rightBlinker
@@ -161,7 +230,7 @@ class DynamicExperimentalController:
# slowness detection # slowness detection
if not self._has_standstill: if not self._has_standstill:
self._slowness_gmac.add_data(self._v_ego_kph <= (self._v_cruise_kph*SLOWNESS_CRUISE_OFFSET)) self._slowness_gmac.add_data(self._v_ego_kph <= (self._v_cruise_kph*SLOWNESS_CRUISE_OFFSET))
self._has_slowness = self._slowness_gmac.get_moving_average() >= SLOWNESS_PROB self._has_slowness = self._slowness_gmac.get_weighted_average() > SLOWNESS_PROB
# dangerous TTC detection # dangerous TTC detection
if not self._has_lead_filtered and self._has_lead_filtered_prev: if not self._has_lead_filtered and self._has_lead_filtered_prev:
@@ -171,14 +240,14 @@ class DynamicExperimentalController:
if self._has_lead and car_state.vEgo >= 0.01: if self._has_lead and car_state.vEgo >= 0.01:
self._dangerous_ttc_gmac.add_data(lead_one.dRel/car_state.vEgo) self._dangerous_ttc_gmac.add_data(lead_one.dRel/car_state.vEgo)
self._has_dangerous_ttc = self._dangerous_ttc_gmac.get_moving_average() is not None and self._dangerous_ttc_gmac.get_moving_average() <= DANGEROUS_TTC self._has_dangerous_ttc = self._dangerous_ttc_gmac.get_weighted_average() is not None and self._dangerous_ttc_gmac.get_weighted_average() <= DANGEROUS_TTC
# keep prev values # keep prev values
self._has_standstill_prev = self._has_standstill self._has_standstill_prev = self._has_standstill
self._has_lead_filtered_prev = self._has_lead_filtered self._has_lead_filtered_prev = self._has_lead_filtered
self._frame += 1 self._frame += 1
def _blended_priority_mode(self): def _radarless_mode(self):
# when mpc fcw crash prob is high # when mpc fcw crash prob is high
# use blended to slow down quickly # use blended to slow down quickly
if self._has_mpc_fcw: if self._has_mpc_fcw:
@@ -190,21 +259,21 @@ class DynamicExperimentalController:
self._set_mode('blended') self._set_mode('blended')
return return
# when blinker is on and speed is driving below highway cruise speed: blended # when blinker is on and speed is driving below V_ACC_MIN: blended
# we dont want it to switch mode at higher speed, blended may trigger hard brake # we dont want it to switch mode at higher speed, blended may trigger hard brake
if self._has_blinkers and self._v_ego_kph < HIGHWAY_CRUISE_KPH: #if self._has_blinkers and self._v_ego_kph < V_ACC_MIN:
self._set_mode('blended') # self._set_mode('blended')
return # return
# when at highway cruise and SNG: blended # when at highway cruise and SNG: blended
# ensuring blended mode is used because acc is bad at catching SNG lead car # ensuring blended mode is used because acc is bad at catching SNG lead car
# especially those who accel very fast and then brake very hard. # especially those who accel very fast and then brake very hard.
if self._sng_state == SNG_State.going and self._v_cruise_kph >= HIGHWAY_CRUISE_KPH: #if self._sng_state == SNG_State.going and self._v_cruise_kph >= V_ACC_MIN:
self._set_mode('blended') # self._set_mode('blended')
return # return
# when standstill: blended # when standstill: blended
# in case of lead car suddenly move away under traffic light, acc mode wont brake at traffic light. # in case of lead car suddenly move away under traffic light, acc mode won't brake at traffic light.
if self._has_standstill: if self._has_standstill:
self._set_mode('blended') self._set_mode('blended')
return return
@@ -226,9 +295,9 @@ class DynamicExperimentalController:
self._set_mode('acc') self._set_mode('acc')
return return
self._set_mode('blended') self._set_mode('acc')
def _acc_priority_mode(self): def _radar_mode(self):
# when mpc fcw crash prob is high # when mpc fcw crash prob is high
# use blended to slow down quickly # use blended to slow down quickly
if self._has_mpc_fcw: if self._has_mpc_fcw:
@@ -236,18 +305,18 @@ class DynamicExperimentalController:
return return
# If there is a filtered lead, the vehicle is not in standstill, and the lead vehicle's yRel meets the condition, # If there is a filtered lead, the vehicle is not in standstill, and the lead vehicle's yRel meets the condition,
#if self._has_lead_filtered and not self._has_standstill: if self._has_lead_filtered and not self._has_standstill:
# self._set_mode('acc') self._set_mode('acc')
# return
# when blinker is on and speed is driving below highway cruise speed: blended
# we dont want it to switch mode at higher speed, blended may trigger hard brake
if self._has_blinkers and self._v_ego_kph < HIGHWAY_CRUISE_KPH:
self._set_mode('blended')
return return
# when blinker is on and speed is driving below V_ACC_MIN: blended
# we dont want it to switch mode at higher speed, blended may trigger hard brake
#if self._has_blinkers and self._v_ego_kph < V_ACC_MIN:
# self._set_mode('blended')
# return
# when standstill: blended # when standstill: blended
# in case of lead car suddenly move away under traffic light, acc mode wont brake at traffic light. # in case of lead car suddenly move away under traffic light, acc mode won't brake at traffic light.
if self._has_standstill: if self._has_standstill:
self._set_mode('blended') self._set_mode('blended')
return return
@@ -270,17 +339,22 @@ class DynamicExperimentalController:
self._set_mode('acc') self._set_mode('acc')
def get_mpc_mode(self, radar_unavailable, car_state, lead_one, md, controls_state, maneuver_distance): def update(self, radar_unavailable, car_state, lead_one, md, controls_state, maneuver_distance):
if self._is_enabled: if self._is_enabled:
self._update(car_state, lead_one, md, controls_state, maneuver_distance) self._update(car_state, lead_one, md, controls_state, maneuver_distance)
if radar_unavailable: if radar_unavailable:
self._blended_priority_mode() self._radarless_mode()
else: else:
self._acc_priority_mode() self._radar_mode()
self._mode_changed = self._mode != self._mode_prev
self._mode_prev = self._mode self._mode_prev = self._mode
def get_mpc_mode(self):
return self._mode return self._mode
def has_changed(self):
return self._mode_changed
def set_enabled(self, enabled): def set_enabled(self, enabled):
self._is_enabled = enabled self._is_enabled = enabled
@@ -297,4 +371,4 @@ class DynamicExperimentalController:
self._set_mode_timeout = SET_MODE_TIMEOUT self._set_mode_timeout = SET_MODE_TIMEOUT
if self._set_mode_timeout > 0: if self._set_mode_timeout > 0:
self._set_mode_timeout -= 1 self._set_mode_timeout -= 1
@@ -320,8 +320,8 @@ void AnnotatedCameraWidgetSP::updateState(const UIStateSP &s) {
// TODO: Add toggle variables to cereal, and parse from cereal // TODO: Add toggle variables to cereal, and parse from cereal
longitudinalPersonality = s.scene.longitudinal_personality; longitudinalPersonality = s.scene.longitudinal_personality;
dynamicLaneProfile = s.scene.dynamic_lane_profile; dynamicLaneProfile = s.scene.dynamic_lane_profile;
mpcMode = QString::fromStdString(lp_sp.getE2eBlended()); const auto mpc_source = lp_sp.getMpcSource();
mpcMode = (mpcMode == "blended") ? mpcMode.replace(0, 1, mpcMode[0].toUpper()) : mpcMode.toUpper(); mpcSource = mpc_source == cereal::MpcSource::BLENDED ? QString(tr("blended")) : QString(tr("acc"));
static int reverse_delay = 0; static int reverse_delay = 0;
bool reverse_allowed = false; bool reverse_allowed = false;
@@ -1061,7 +1061,7 @@ void AnnotatedCameraWidgetSP::drawFeatureStatusText(QPainter &p, int x, int y) {
p.setBrush(dec_color); p.setBrush(dec_color);
p.drawEllipse(dec_btn); p.drawEllipse(dec_btn);
QString dec_status_text; QString dec_status_text;
dec_status_text.sprintf("DEC: %s\n", dynamicExperimentalControlToggle ? (experimentalMode ? QString(mpcMode).toStdString().c_str() : QString("Inactive").toStdString().c_str()) : "OFF"); dec_status_text.sprintf("DEC: %s\n", dynamicExperimentalControlToggle ? (experimentalMode ? QString(mpcSource).toStdString().c_str() : QString("Inactive").toStdString().c_str()) : "OFF");
p.setPen(QPen(shadow_color, 2)); p.setPen(QPen(shadow_color, 2));
p.drawText(x + drop_shadow_size, y + drop_shadow_size, dec_status_text); p.drawText(x + drop_shadow_size, y + drop_shadow_size, dec_status_text);
p.setPen(QPen(text_color, 2)); p.setPen(QPen(text_color, 2));
@@ -1177,6 +1177,10 @@ void AnnotatedCameraWidgetSP::drawLaneLines(QPainter &painter, const UIStateSP *
// paint path // paint path
QLinearGradient bg(0, height(), 0, 0); QLinearGradient bg(0, height(), 0, 0);
const auto long_plan_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP();
bool exp_mode_path = (long_plan_sp.getDynamicExperimentalControl() && long_plan_sp.getMpcSource() == cereal::MpcSource::BLENDED) ||
(!long_plan_sp.getDynamicExperimentalControl() && sm["controlsState"].getControlsState().getExperimentalMode());
if (madsEnabled || car_state.getCruiseState().getEnabled()) { if (madsEnabled || car_state.getCruiseState().getEnabled()) {
if (steerOverride && latActive) { if (steerOverride && latActive) {
bg.setColorAt(0.0, QColor::fromHslF(20 / 360., 0.94, 0.51, 0.17)); bg.setColorAt(0.0, QColor::fromHslF(20 / 360., 0.94, 0.51, 0.17));
@@ -1185,7 +1189,7 @@ void AnnotatedCameraWidgetSP::drawLaneLines(QPainter &painter, const UIStateSP *
} else if (!(latActive || car_state.getCruiseState().getEnabled())) { } else if (!(latActive || car_state.getCruiseState().getEnabled())) {
bg.setColorAt(0, whiteColor()); bg.setColorAt(0, whiteColor());
bg.setColorAt(1, whiteColor(0)); bg.setColorAt(1, whiteColor(0));
} else if (sm["controlsState"].getControlsState().getExperimentalMode()) { } else if (exp_mode_path) {
// The first half of track_vertices are the points for the right side of the path // The first half of track_vertices are the points for the right side of the path
const auto &acceleration = sm["modelV2"].getModelV2().getAcceleration().getX(); const auto &acceleration = sm["modelV2"].getModelV2().getAcceleration().getX();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration.size()); const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration.size());
@@ -192,7 +192,7 @@ private:
cereal::LongitudinalPlanSP::SpeedLimitControlState slcState; cereal::LongitudinalPlanSP::SpeedLimitControlState slcState;
int longitudinalPersonality; int longitudinalPersonality;
int dynamicLaneProfile; int dynamicLaneProfile;
QString mpcMode; QString mpcSource;
int speed_limit_frame; int speed_limit_frame;
bool slcShowSign = true; bool slcShowSign = true;
View File
View File
View File
@@ -0,0 +1,127 @@
from abc import abstractmethod, ABC
from cereal import car, custom
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_controller import ACTIVE_STATES
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.states import InactiveState, \
LoadingState, AcceleratingState, DeceleratingState, HoldingState
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.helpers import get_set_point, \
speed_hysteresis, update_manual_button_timers
ButtonType = car.CarState.ButtonEvent.Type
ButtonControlState = custom.CarControlSP.CustomStockLongitudinalControl.ButtonControlState
SpeedLimitControlState = custom.LongitudinalPlanSP.SpeedLimitControlState
TurnSpeedControlState = custom.LongitudinalPlanSP.SpeedLimitControlState
VisionTurnControllerState = custom.LongitudinalPlanSP.VisionTurnControllerState
SendCan = tuple[int, bytes, int]
class CustomStockLongitudinalControllerBase(ABC):
def __init__(self, car, car_controller, car_state, CP):
self.car = car
self.car_controller = car_controller
self.car_state = car_state
self.CP = CP
self.v_target = 0
self.v_cruise_cluster = 0
self.v_cruise_min = 0
self.cruise_button = None
self.button_state = ButtonControlState.inactive
self.v_tsc_state = VisionTurnControllerState.disabled
self.slc_state = SpeedLimitControlState.inactive
self.m_tsc_state = TurnSpeedControlState.inactive
self.v_tsc = 0
self.speed_limit_offseted = 0
self.m_tsc = 0
self.is_ready = False
self.is_ready_prev = False
self.speed_steady = 0
self.accel_button = None
self.decel_button = None
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0}
self.button_states = {
ButtonControlState.inactive: InactiveState(self),
ButtonControlState.loading: LoadingState(self),
ButtonControlState.accelerating: AcceleratingState(self),
ButtonControlState.decelerating: DeceleratingState(self),
ButtonControlState.holding: HoldingState(self),
}
@abstractmethod
def create_mock_button_messages(self) -> list[SendCan]:
pass
def state_publish(self) -> custom.CarControlSP.CustomStockLongitudinalControl:
customStockLongitudinalControl = custom.CarControlSP.CustomStockLongitudinalControl.new_message()
customStockLongitudinalControl.state = self.button_state
customStockLongitudinalControl.cruiseButton = 0 if self.cruise_button is None else int(self.cruise_button)
customStockLongitudinalControl.vTarget = float(self.v_target)
customStockLongitudinalControl.vCruiseCluster = float(self.v_cruise_cluster)
return customStockLongitudinalControl
def update_msgs(self) -> None:
if self.car.sm.updated['longitudinalPlanSP']:
self.v_tsc_state = self.car.sm['longitudinalPlanSP'].visionTurnControllerState
self.slc_state = self.car.sm['longitudinalPlanSP'].speedLimitControlState
self.m_tsc_state = self.car.sm['longitudinalPlanSP'].turnSpeedControlState
self.v_tsc = self.car.sm['longitudinalPlanSP'].visionTurnSpeed
speed_limit = self.car.sm['longitudinalPlanSP'].speedLimit
speed_limit_offset = self.car.sm['longitudinalPlanSP'].speedLimitOffset
self.speed_limit_offseted = speed_limit + speed_limit_offset
self.m_tsc = self.car.sm['longitudinalPlanSP'].turnSpeed
def update_calculations(self, CS: car.CarState, CC: car.CarControl) -> None:
is_metric = self.car_state.params_list.is_metric
v_cruise_kph = CC.vCruise
v_cruise = v_cruise_kph * CV.KPH_TO_MS
self.v_cruise_min = get_set_point(is_metric)
self.v_cruise_cluster = round(CS.cruiseState.speed * (CV.MS_TO_KPH if is_metric else CV.MS_TO_MPH))
v_targets = {'cruise': CC.vCruise}
if self.v_tsc_state != VisionTurnControllerState.disabled:
v_targets['v_tsc'] = self.v_tsc
if self.slc_state in ACTIVE_STATES:
v_targets['slc'] = self.speed_limit_offseted
if self.m_tsc_state > TurnSpeedControlState.tempInactive:
v_targets['m_tsc'] = self.m_tsc
source = min(v_targets, key=v_targets.get)
v_target = speed_hysteresis(self, v_targets[source], self.speed_steady, 1.5 * (CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS))
v_target = min(v_target, v_cruise)
v_target = round(v_target * (CV.MS_TO_KPH if is_metric else CV.MS_TO_MPH))
self.v_target = v_target
def update_state(self, CS: car.CarState, CC: car.CarControl) -> None:
update_manual_button_timers(CS, self.cruise_buttons)
ready = CS.cruiseState.enabled and not CC.cruiseControl.cancel and not CC.cruiseControl.resume
button_pressed = any(self.cruise_buttons[k] > 0 for k in self.cruise_buttons)
self.is_ready = ready and not button_pressed
self.cruise_button = self.button_states[self.button_state]()
self.is_ready_prev = self.is_ready
def update(self, CS: car.CarState, CC: car.CarControl) -> list[SendCan]:
self.update_msgs()
self.update_calculations(CS, CC)
self.update_state(CS, CC)
can_sends = self.create_mock_button_messages()
return can_sends
@@ -0,0 +1,30 @@
from cereal import car
ButtonType = car.CarState.ButtonEvent.Type
def get_set_point(is_metric: bool) -> float:
return 30 if is_metric else 20
def speed_hysteresis(controller, speed: float, speed_steady: float, hyst: float) -> float:
if speed > speed_steady + hyst:
speed_steady = speed - hyst
elif speed < speed_steady - hyst:
speed_steady = speed + hyst
controller.speed_steady = speed
return speed_steady
def update_manual_button_timers(CS: car.CarState, button_timers: dict[ButtonType, int]) -> None:
# increment timer for buttons still pressed
for k in button_timers:
if button_timers[k] > 0:
button_timers[k] += 1
for b in CS.buttonEvents:
if b.type.raw in button_timers:
# Start/end timer and store current state on change of button pressed
button_timers[b.type.raw] = 1 if b.pressed else 0
@@ -0,0 +1,85 @@
from abc import ABC, abstractmethod
from random import randint
from cereal import custom
ButtonControlState = custom.CarControlSP.CustomStockLongitudinalControl.ButtonControlState
INACTIVE_TIMER = 40
RESET_COUNT = 5
HOLD_TIME = 7
class ButtonStateBase(ABC):
def __init__(self, controller):
self.controller = controller
self.button_count = 0
self.timer = INACTIVE_TIMER
def __call__(self) -> int | None:
if self.controller.is_ready:
# TODO-SP: Validate different hold time intervals for different platforms to prevent temporary cruise fault
pass
else:
if self.controller.is_ready_prev:
self.controller.button_state = ButtonControlState.inactive
return None
return self.handle()
@abstractmethod
def handle(self) -> int | None:
pass
class InactiveState(ButtonStateBase):
def handle(self) -> None:
self.button_count = 0
if not self.controller.is_ready:
self.timer = INACTIVE_TIMER
elif self.timer > 0:
self.timer -= 1
else:
self.controller.button_state = ButtonControlState.loading
return None
class LoadingState(ButtonStateBase):
def handle(self) -> None:
if self.controller.v_target > self.controller.v_cruise_cluster:
self.controller.button_state = ButtonControlState.accelerating
elif self.controller.v_target < self.controller.v_cruise_cluster and self.controller.v_cruise_cluster > self.controller.v_cruise_min:
self.controller.button_state = ButtonControlState.decelerating
else:
self.controller.button_state = ButtonControlState.holding
return None
class AcceleratingState(ButtonStateBase):
def handle(self) -> int | None:
self.button_count += 1
if self.controller.v_target <= self.controller.v_cruise_cluster or self.button_count > RESET_COUNT:
self.button_count = 0
self.controller.button_state = ButtonControlState.holding
return None
return self.controller.accel_button
class DeceleratingState(ButtonStateBase):
def handle(self) -> int | None:
self.button_count += 1
if self.controller.v_target >= self.controller.v_cruise_cluster or self.controller.v_cruise_cluster <= self.controller.v_cruise_min or self.button_count > RESET_COUNT:
self.button_count = 0
self.controller.button_state = ButtonControlState.holding
return None
return self.controller.decel_button
class HoldingState(ButtonStateBase):
def handle(self) -> None:
self.button_count += 1
if self.button_count > HOLD_TIME:
self.button_count = 0
self.controller.button_state = ButtonControlState.loading
return None
View File
@@ -0,0 +1,32 @@
from cereal import car
from openpilot.selfdrive.car.chrysler.values import RAM_CARS
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
from openpilot.sunnypilot.selfdrive.car.chrysler.custom_stock_longitudinal_controller import helpers
ButtonType = car.CarState.ButtonEvent.Type
# TODO-SP: Handle this better in the base class
ACCEL_BUTTON = 1
DECEL_BUTTON = 2
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = ACCEL_BUTTON
self.decel_button = DECEL_BUTTON
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0, ButtonType.resumeCruise: 0}
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
ram_cars = self.CP.carFingerprint in RAM_CARS
das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0
button_counter_offset = [1, 1, 0, None][self.car_controller.frame % 4]
if self.cruise_button is not None:
if ram_cars:
can_sends.append(helpers.create_cruise_buttons(self.car_controller.packer, self.car_state.button_counter, das_bus, self.cruise_button))
elif button_counter_offset is not None:
can_sends.append(helpers.create_cruise_buttons(self.car_controller.packer, self.car_state.button_counter + button_counter_offset, das_bus, self.cruise_button))
return can_sends
@@ -0,0 +1,9 @@
def create_cruise_buttons(packer, frame, bus, buttons=0):
values = {
"ACC_Accel": 1 if buttons == 1 else 0,
"ACC_Decel": 1 if buttons == 2 else 0,
"COUNTER": frame % 0x10,
}
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
@@ -0,0 +1,28 @@
from cereal import car
from openpilot.selfdrive.car.ford.values import CarControllerParams
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
from openpilot.sunnypilot.selfdrive.car.ford.custom_stock_longitudinal_controller import fordcan
ButtonType = car.CarState.ButtonEvent.Type
# TODO-SP: Handle this better in the base class
ACCEL_BUTTON = 1
DECEL_BUTTON = 2
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = ACCEL_BUTTON
self.decel_button = DECEL_BUTTON
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0, ButtonType.gapAdjustCruise: 0,
ButtonType.resumeCruise: 0, ButtonType.setCruise: 0}
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.cruise_button is not None and (self.car_controller.frame % CarControllerParams.BUTTONS_STEP) == 0:
can_sends.append(fordcan.create_button_msg(self.car_controller.packer, self.car_controller.CAN.camera, self.car_state.buttons_stock_values, buttons=self.cruise_button))
can_sends.append(fordcan.create_button_msg(self.car_controller.packer, self.car_controller.CAN.main, self.car_state.buttons_stock_values, buttons=self.cruise_button))
return can_sends
@@ -0,0 +1,48 @@
def create_button_msg(packer, bus: int, stock_values: dict, buttons=0):
"""
Creates a CAN message for the Ford SCCM buttons/switches.
Includes cruise control buttons, turn lights and more.
Frequency is 10Hz.
"""
values = {s: stock_values[s] for s in [
"HeadLghtHiFlash_D_Stat", # SCCM Passthrough the remaining buttons
"TurnLghtSwtch_D_Stat", # SCCM Turn signal switch
"WiprFront_D_Stat",
"LghtAmb_D_Sns",
"AccButtnGapDecPress",
"AccButtnGapIncPress",
"AslButtnOnOffCnclPress",
"AslButtnOnOffPress",
"LaSwtchPos_D_Stat",
"CcAslButtnCnclResPress",
"CcAslButtnDeny_B_Actl",
"CcAslButtnIndxDecPress",
"CcAslButtnIndxIncPress",
"CcAslButtnOffCnclPress",
"CcAslButtnOnOffCncl",
"CcAslButtnOnPress",
"CcAslButtnResDecPress",
"CcAslButtnResIncPress",
"CcAslButtnSetDecPress",
"CcAslButtnSetIncPress",
"CcAslButtnSetPress",
"CcButtnOffPress",
"CcButtnOnOffCnclPress",
"CcButtnOnOffPress",
"CcButtnOnPress",
"HeadLghtHiFlash_D_Actl",
"HeadLghtHiOn_B_StatAhb",
"AhbStat_B_Dsply",
"AccButtnGapTogglePress",
"WiprFrontSwtch_D_Stat",
"HeadLghtHiCtrl_D_RqAhb",
]}
values.update({
"CcAslButtnSetIncPress": 1 if buttons == 1 else 0, # CC increase button
"CcAslButtnSetDecPress": 1 if buttons == 2 else 0, # CC decrease button
})
return packer.make_can_msg("Steering_Data_FD1", bus, values)
@@ -0,0 +1,25 @@
from cereal import car
from openpilot.selfdrive.car.honda import hondacan
from openpilot.selfdrive.car.honda.values import CruiseButtons
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
from openpilot.sunnypilot.selfdrive.car.honda.custom_stock_longitudinal_controller import helpers
ButtonType = car.CarState.ButtonEvent.Type
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = CruiseButtons.RES_ACCEL
self.decel_button = CruiseButtons.DECEL_SET
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0,
ButtonType.altButton3: 0, ButtonType.cancel: 0}
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.cruise_button is not None:
can_sends.append(hondacan.spam_buttons_command(self.car_controller.packer, self.car_controller.CAN, self.cruise_button, self.CP.carFingerprint))
return can_sends
@@ -0,0 +1,58 @@
from random import choices
from cereal import car
from openpilot.common.numpy_fast import interp
from openpilot.selfdrive.car.hyundai import hyundaicanfd
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CANFD_CAR
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
from openpilot.sunnypilot.selfdrive.car.hyundai.custom_stock_longitudinal_controller import helpers
ButtonType = car.CarState.ButtonEvent.Type
BUTTON_COPIES = 2
BUTTON_COPIES_TIME = 7
BUTTON_COPIES_TIME_IMPERIAL = [BUTTON_COPIES_TIME + 3, 70]
BUTTON_COPIES_TIME_METRIC = [BUTTON_COPIES_TIME, 40]
POPULATION = [0, 1]
WEIGHTS = [0.51, 0.49]
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = Buttons.RES_ACCEL
self.decel_button = Buttons.SET_DECEL
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0, ButtonType.gapAdjustCruise: 0}
def create_can_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.cruise_button is not None:
copies_xp = BUTTON_COPIES_TIME_METRIC if self.car_state.params_list.is_metric else BUTTON_COPIES_TIME_IMPERIAL
copies = int(interp(BUTTON_COPIES_TIME, copies_xp, [1, BUTTON_COPIES]))
can_sends.extend([helpers.create_clu11(self.car_controller.packer, self.car_state.clu11, self.cruise_button, self.CP)] * copies)
return can_sends
def create_canfd_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# TODO: resume for alt button cars
pass
else:
if self.cruise_button is not None:
for _ in range(BUTTON_COPIES):
can_sends.append(hyundaicanfd.create_buttons(self.car_controller.packer, self.CP, self.car_controller.CAN,
self.car_state.button_counters + choices(POPULATION, WEIGHTS)[0],
self.cruise_button))
return can_sends
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.CP.carFingerprint in CANFD_CAR:
can_sends.extend(self.create_canfd_mock_button_messages())
else:
can_sends.extend(self.create_can_mock_button_messages())
return can_sends
@@ -0,0 +1,22 @@
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags
def create_clu11(packer, clu11, button, CP):
values = {s: clu11[s] for s in [
"CF_Clu_CruiseSwState",
"CF_Clu_CruiseSwMain",
"CF_Clu_SldMainSW",
"CF_Clu_ParityBit1",
"CF_Clu_VanzDecimal",
"CF_Clu_Vanz",
"CF_Clu_SPEED_UNIT",
"CF_Clu_DetentOut",
"CF_Clu_RheostatLevel",
"CF_Clu_CluInfo",
"CF_Clu_AmpInfo",
"CF_Clu_AliveCnt1",
]}
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = (values["CF_Clu_AliveCnt1"] + 1) % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
return packer.make_can_msg("CLU11", bus, values)
@@ -0,0 +1,25 @@
from cereal import car
from openpilot.selfdrive.car.mazda.values import Buttons
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
from openpilot.sunnypilot.selfdrive.car.mazda.custom_stock_longitudinal_controller import helpers
ButtonType = car.CarState.ButtonEvent.Type
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = Buttons.SET_PLUS
self.decel_button = Buttons.SET_MINUS
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0,
ButtonType.resumeCruise: 0, ButtonType.setCruise: 0}
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
if self.cruise_button is not None:
if self.car_controller.frame % 10 == 0:
can_sends.append(helpers.create_button_cmd(self.car_controller.packer, self.CP, self.car_controller.frame // 10, self.cruise_button))
return can_sends
@@ -0,0 +1,40 @@
def create_button_cmd(packer, CP, counter, button):
can = int(button == Buttons.CANCEL)
res = int(button == Buttons.RESUME)
inc = int(button == Buttons.SET_PLUS)
dec = int(button == Buttons.SET_MINUS)
if CP.flags & MazdaFlags.GEN1:
values = {
"CAN_OFF": can,
"CAN_OFF_INV": (can + 1) % 2,
"SET_P": inc,
"SET_P_INV": (inc + 1) % 2,
"RES": res,
"RES_INV": (res + 1) % 2,
"SET_M": dec,
"SET_M_INV": (dec + 1) % 2,
"DISTANCE_LESS": 0,
"DISTANCE_LESS_INV": 1,
"DISTANCE_MORE": 0,
"DISTANCE_MORE_INV": 1,
"MODE_X": 0,
"MODE_X_INV": 1,
"MODE_Y": 0,
"MODE_Y_INV": 1,
"BIT1": 1,
"BIT2": 1,
"BIT3": 1,
"CTR": (counter + 1) % 16,
}
return packer.make_can_msg("CRZ_BTNS", 0, values)
@@ -0,0 +1,77 @@
from cereal import car, custom
from openpilot.sunnypilot.controls.lib.custom_stock_longitudinal_controller.controllers import CustomStockLongitudinalControllerBase, \
SendCan
ButtonType = car.CarState.ButtonEvent.Type
ButtonControlState = custom.CarControlSP.CustomStockLongitudinalControl.ButtonControlState
# TODO-SP: Handle this better in the base class
ACCEL_BUTTON = 1
DECEL_BUTTON = 2
RESUME_CRUISE = 3
SET_CRUISE = 4
ACC_TYPE_BUTTONS = {
0: {1: ACCEL_BUTTON, 2: DECEL_BUTTON}, # accel, decel
1: {1: RESUME_CRUISE, 2: SET_CRUISE} # resume, set
}
class CustomStockLongitudinalController(CustomStockLongitudinalControllerBase):
def __init__(self, car, car_controller, car_state, CP):
super().__init__(car, car_controller, car_state, CP)
self.accel_button = ACCEL_BUTTON
self.decel_button = DECEL_BUTTON
self.cruise_buttons = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0,
ButtonType.resumeCruise: 0, ButtonType.setCruise: 0}
self.last_v_cruise_cluster = 0
self.last_cruise_button = False
self.acc_type = -1
self.button_frame = 0
self.initial_v_target = 0
self.initial_v_cruise_cluster = 0
def update_checks(self) -> None:
if self.button_state > ButtonControlState.inactive:
self.button_frame += 1
if self.button_frame == 1:
self.initial_v_target = self.v_target
self.initial_v_cruise_cluster = self.v_cruise_cluster
self.set_acc_type()
else:
self.button_frame = 0
self.initial_v_target = 0
self.initial_v_cruise_cluster = 0
def set_acc_type(self):
current_v_cruise_diff = abs(self.v_target - self.v_cruise_cluster)
initial_v_cruise_diff = abs(self.initial_v_target - self.initial_v_cruise_cluster)
if self.last_v_cruise_cluster != self.v_cruise_cluster and current_v_cruise_diff != initial_v_cruise_diff:
if self.acc_type == -1:
v_cruise_diff = abs(self.v_cruise_cluster - self.last_v_cruise_cluster)
if v_cruise_diff >= 10 and self.last_cruise_button in (ACCEL_BUTTON, DECEL_BUTTON):
self.acc_type = 1
else:
self.acc_type = 0
if self.cruise_button is not None:
self.cruise_button = ACC_TYPE_BUTTONS.get(self.acc_type, {}).get(self.cruise_button, self.cruise_button)
def create_mock_button_messages(self) -> list[SendCan]:
can_sends = []
self.update_checks()
if self.cruise_button is not None:
if self.car_state.gra_stock_values["COUNTER"] != self.car_controller.gra_acc_counter_last:
can_sends.append(self.car_controller.CCS.create_acc_buttons_control(self.car_controller.packer_pt, self.car_controller.ext_bus, self.car_state.gra_stock_values, buttons=self.cruise_button))
self.last_cruise_button = self.cruise_button
self.last_v_cruise_cluster = self.v_cruise_cluster
return can_sends
@@ -0,0 +1,18 @@
def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0):
values = {s: gra_stock_values[s] for s in [
"GRA_Hauptschalter", # ACC button, on/off
"GRA_Typ_Hauptschalter", # ACC main button type
"GRA_Codierung", # ACC button configuration/coding
"GRA_Tip_Stufe_2", # unknown related to stalk type
"GRA_ButtonTypeInfo", # unknown related to stalk type
]}
values.update({
"COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
"GRA_Tip_Wiederaufnahme": 1 if buttons == 3 else 0,
"GRA_Tip_Setzen": 1 if buttons == 4 else 0,
"GRA_Tip_Runter": 1 if buttons == 2 else 0,
"GRA_Tip_Hoch": 1 if buttons == 1 else 0,
})
return packer.make_can_msg("GRA_ACC_01", bus, values)
@@ -0,0 +1,17 @@
def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0):
values = {s: gra_stock_values[s] for s in [
"GRA_Hauptschalt", # ACC button, on/off
"GRA_Typ_Hauptschalt", # ACC button, momentary vs latching
"GRA_Kodierinfo", # ACC button, configuration
"GRA_Sender", # ACC button, CAN message originator
]}
values.update({
"COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
"GRA_Recall": 1 if buttons == 3 else 0,
"GRA_Neu_Setzen": 1 if buttons == 4 else 0,
"GRA_Down_kurz": 1 if buttons == 2 else 0,
"GRA_Up_kurz": 1 if buttons == 1 else 0,
})
return packer.make_can_msg("GRA_Neu", bus, values)