mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-03 06:33:50 +08:00
5dd8a73192
Maintain openpilot lateral control when the brake or gas pedals are used. Deactivation occurs only through the 'Cruise Control' button. Lots of credit goes to "pfeiferj"! Couldn't of done it without him! https: //github.com/pfeiferj/openpilot Co-Authored-By: Jacob Pfeifer <jacob@pfeifer.dev>
213 lines
7.4 KiB
Python
Executable File
213 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import time
|
|
|
|
import cereal.messaging as messaging
|
|
|
|
from cereal import car, custom
|
|
|
|
from panda import ALTERNATIVE_EXPERIENCE
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
|
|
|
|
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
|
from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
|
|
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
|
from openpilot.selfdrive.controls.lib.events import Events
|
|
|
|
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_variables import FrogPilotVariables
|
|
|
|
REPLAY = "REPLAY" in os.environ
|
|
|
|
EventName = car.CarEvent.EventName
|
|
|
|
|
|
class Car:
|
|
CI: CarInterfaceBase
|
|
|
|
def __init__(self, CI=None):
|
|
self.can_sock = messaging.sub_sock('can', timeout=20)
|
|
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
|
|
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'frogpilotCarState'])
|
|
|
|
self.can_rcv_cum_timeout_counter = 0
|
|
|
|
self.CC_prev = car.CarControl.new_message()
|
|
self.CS_prev = car.CarState.new_message()
|
|
self.initialized_prev = False
|
|
|
|
self.last_actuators_output = car.CarControl.Actuators.new_message()
|
|
|
|
self.params = Params()
|
|
|
|
if CI is None:
|
|
# wait for one pandaState and one CAN packet
|
|
print("Waiting for CAN messages...")
|
|
get_one_can(self.can_sock)
|
|
|
|
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
|
|
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled")
|
|
self.CI, self.CP = get_car(self.params, self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
|
|
else:
|
|
self.CI, self.CP = CI, CI.CP
|
|
|
|
# set alternative experiences from parameters
|
|
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
|
|
self.CP.alternativeExperience = 0
|
|
if not self.disengage_on_accelerator:
|
|
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
|
|
|
|
# FrogPilot alternative experiences
|
|
if self.params.get_bool("AlwaysOnLateral"):
|
|
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL
|
|
self.params.put_bool("AlwaysOnLateralSet", True)
|
|
|
|
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
|
|
|
|
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
|
|
|
|
self.CP.passive = not controller_available or self.CP.dashcamOnly
|
|
if self.CP.passive:
|
|
safety_config = car.CarParams.SafetyConfig.new_message()
|
|
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
|
|
self.CP.safetyConfigs = [safety_config]
|
|
|
|
# Write previous route's CarParams
|
|
prev_cp = self.params.get("CarParamsPersistent")
|
|
if prev_cp is not None:
|
|
self.params.put("CarParamsPrevRoute", prev_cp)
|
|
|
|
# Write CarParams for controls and radard
|
|
cp_bytes = self.CP.to_bytes()
|
|
self.params.put("CarParams", cp_bytes)
|
|
self.params.put_nonblocking("CarParamsCache", cp_bytes)
|
|
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
|
|
|
|
self.events = Events()
|
|
|
|
# card is driven by can recv, expected at 100Hz
|
|
self.rk = Ratekeeper(100, print_delay_threshold=None)
|
|
|
|
# FrogPilot variables
|
|
self.frogpilot_toggles = FrogPilotVariables.toggles
|
|
|
|
self.update_toggles = False
|
|
|
|
def state_update(self) -> car.CarState:
|
|
"""carState update loop, driven by can"""
|
|
|
|
# Update carState from CAN
|
|
can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
|
|
CS, FPCS = self.CI.update(self.CC_prev, can_strs, self.frogpilot_toggles)
|
|
|
|
self.sm.update(0)
|
|
|
|
can_rcv_valid = len(can_strs) > 0
|
|
|
|
# Check for CAN timeout
|
|
if not can_rcv_valid:
|
|
self.can_rcv_cum_timeout_counter += 1
|
|
|
|
if can_rcv_valid and REPLAY:
|
|
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
|
|
|
|
return CS, FPCS
|
|
|
|
def update_events(self, CS: car.CarState) -> car.CarState:
|
|
self.events.clear()
|
|
|
|
self.events.add_from_msg(CS.events)
|
|
|
|
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
|
|
if (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator) or \
|
|
(CS.brakePressed and (not self.CS_prev.brakePressed or not CS.standstill)) or \
|
|
(CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)):
|
|
self.events.add(EventName.pedalPressed)
|
|
|
|
CS.events = self.events.to_msg()
|
|
|
|
def state_publish(self, CS: car.CarState, FPCS: custom.FrogPilotCarState):
|
|
"""carState and carParams publish loop"""
|
|
|
|
# carParams - logged every 50 seconds (> 1 per segment)
|
|
if self.sm.frame % int(50. / DT_CTRL) == 0:
|
|
cp_send = messaging.new_message('carParams')
|
|
cp_send.valid = True
|
|
cp_send.carParams = self.CP
|
|
self.pm.send('carParams', cp_send)
|
|
|
|
# publish new carOutput
|
|
co_send = messaging.new_message('carOutput')
|
|
co_send.valid = self.sm.all_checks(['carControl'])
|
|
co_send.carOutput.actuatorsOutput = self.last_actuators_output
|
|
self.pm.send('carOutput', co_send)
|
|
|
|
# kick off controlsd step while we actuate the latest carControl packet
|
|
cs_send = messaging.new_message('carState')
|
|
cs_send.valid = CS.canValid
|
|
cs_send.carState = CS
|
|
cs_send.carState.canErrorCounter = self.can_rcv_cum_timeout_counter
|
|
cs_send.carState.cumLagMs = -self.rk.remaining * 1000.
|
|
self.pm.send('carState', cs_send)
|
|
|
|
# frogpilotCarState
|
|
fpcs_send = messaging.new_message('frogpilotCarState')
|
|
fpcs_send.valid = CS.canValid
|
|
fpcs_send.frogpilotCarState = FPCS
|
|
self.pm.send('frogpilotCarState', fpcs_send)
|
|
|
|
def controls_update(self, CS: car.CarState, CC: car.CarControl):
|
|
"""control update loop, driven by carControl"""
|
|
|
|
if not self.initialized_prev:
|
|
# Initialize CarInterface, once controls are ready
|
|
# TODO: this can make us miss at least a few cycles when doing an ECU knockout
|
|
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
|
|
# signal pandad to switch to car safety mode
|
|
self.params.put_bool_nonblocking("ControlsReady", True)
|
|
|
|
if self.sm.all_alive(['carControl']):
|
|
# send car controls over can
|
|
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.frogpilot_toggles)
|
|
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
|
|
|
|
self.CC_prev = CC
|
|
|
|
def step(self):
|
|
CS, FPCS = self.state_update()
|
|
|
|
self.update_events(CS)
|
|
|
|
self.state_publish(CS, FPCS)
|
|
|
|
initialized = (not any(e.name == EventName.controlsInitializing for e in self.sm['onroadEvents']) and
|
|
self.sm.seen['onroadEvents'])
|
|
if not self.CP.passive and initialized:
|
|
self.controls_update(CS, self.sm['carControl'])
|
|
|
|
self.initialized_prev = initialized
|
|
self.CS_prev = CS.as_reader()
|
|
|
|
def card_thread(self):
|
|
while True:
|
|
self.step()
|
|
self.rk.monitor_time()
|
|
|
|
# Update FrogPilot parameters
|
|
if FrogPilotVariables.toggles_updated:
|
|
self.update_toggles = True
|
|
elif self.update_toggles:
|
|
FrogPilotVariables.update_frogpilot_params()
|
|
self.update_toggles = False
|
|
|
|
def main():
|
|
config_realtime_process(4, Priority.CTRL_HIGH)
|
|
car = Car()
|
|
car.card_thread()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|