Example #1
0
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(3, Priority.CTRL_HIGH)
        self.op_params = opParams()

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster([
                'deviceState', 'pandaState', 'modelV2', 'liveCalibration',
                'driverMonitoringState', 'longitudinalPlan', 'lateralPlan',
                'liveLocationKalman', 'roadCameraState', 'driverCameraState',
                'managerState', 'liveParameters', 'radarState'
            ],
                                          ignore_alive=ignore)

        self.sm_smiskol = messaging.SubMaster([
            'radarState', 'dynamicFollowData', 'liveTracks',
            'dynamicFollowButton', 'laneSpeed', 'dynamicCameraOffset',
            'modelLongButton'
        ])

        self.op_params = opParams()
        self.df_manager = dfManager()
        self.support_white_panda = self.op_params.get('support_white_panda')
        self.last_model_long = False

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        # wait for one pandaState and one CAN packet
        panda_type = messaging.recv_one(
            self.sm.sock['pandaState']).pandaState.pandaType
        has_relay = panda_type in [
            PandaType.blackPanda, PandaType.uno, PandaType.dos
        ]
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP, candidate = get_car(self.can_sock,
                                              self.pm.sock['sendcan'],
                                              has_relay)
        threading.Thread(target=log_fingerprint, args=[candidate]).start()

        # read params
        params = Params()
        self.is_metric = params.get("IsMetric", encoding='utf8') == "1"
        self.is_ldw_enabled = params.get("IsLdwEnabled",
                                         encoding='utf8') == "1"
        community_feature_toggle = params.get("CommunityFeaturesToggle",
                                              encoding='utf8') == "1"
        openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                              encoding='utf8') == "1"
        passive = params.get(
            "Passive", encoding='utf8') == "1" or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'
        # If stock camera is disconnected, we loaded car controls and it's not dashcam mode
        controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard and boardd safety mode
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb, candidate)
        self.VM = VehicleModel(self.CP)

        if self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = False

        self.sm['liveCalibration'].calStatus = Calibration.CALIBRATED
        self.sm['deviceState'].freeSpacePercent = 100
        self.sm['driverMonitoringState'].events = []
        self.sm['driverMonitoringState'].awarenessStatus = 1.
        self.sm['driverMonitoringState'].faceDetected = False

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if community_feature_disallowed:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default

        self.lead_rel_speed = 255
        self.lead_long_dist = 255
Example #2
0
def controlsd_thread(gctx=None):
    gc.disable()

    # start the loop
    set_realtime_priority(3)

    params = Params()

    # Pub Sockets
    sendcan = messaging.pub_sock(service_list['sendcan'].port)
    controlsstate = messaging.pub_sock(service_list['controlsState'].port)
    carstate = messaging.pub_sock(service_list['carState'].port)
    carcontrol = messaging.pub_sock(service_list['carControl'].port)
    carevents = messaging.pub_sock(service_list['carEvents'].port)
    carparams = messaging.pub_sock(service_list['carParams'].port)

    is_metric = params.get("IsMetric") == "1"
    passive = params.get("Passive") != "0"

    sm = messaging.SubMaster([
        'thermal', 'health', 'liveCalibration', 'driverMonitoring', 'plan',
        'pathPlan'
    ])

    logcan = messaging.sub_sock(service_list['can'].port)

    # wait for health and CAN packets
    hw_type = messaging.recv_one(sm.sock['health']).health.hwType
    is_panda_black = hw_type == log.HealthData.HwType.blackPanda
    wait_for_can(logcan)

    CI, CP = get_car(logcan, sendcan, is_panda_black)
    logcan.close()

    # TODO: Use the logcan socket from above, but that will currenly break the tests
    can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 100
    can_sock = messaging.sub_sock(service_list['can'].port,
                                  timeout=can_timeout)

    car_recognized = CP.carName != 'mock'
    # If stock camera is disconnected, we loaded car controls and it's not chffrplus
    controller_available = CP.enableCamera and CI.CC is not None and not passive
    read_only = not car_recognized or not controller_available
    if read_only:
        CP.safetyModel = car.CarParams.SafetyModel.elm327  # diagnostic only

    # Write CarParams for radard and boardd safety mode
    params.put("CarParams", CP.to_bytes())
    params.put("LongitudinalControl",
               "1" if CP.openpilotLongitudinalControl else "0")

    CC = car.CarControl.new_message()
    AM = AlertManager()

    startup_alert = get_startup_alert(car_recognized, controller_available)
    AM.add(sm.frame, startup_alert, False)

    LoC = LongControl(CP, CI.compute_gb)
    VM = VehicleModel(CP)

    if CP.lateralTuning.which() == 'pid':
        LaC = LatControlPID(CP)
    elif CP.lateralTuning.which() == 'indi':
        LaC = LatControlINDI(CP)
    elif CP.lateralTuning.which() == 'lqr':
        LaC = LatControlLQR(CP)

    driver_status = DriverStatus()

    state = State.disabled
    soft_disable_timer = 0
    v_cruise_kph = 255
    v_cruise_kph_last = 0
    overtemp = False
    free_space = False
    cal_status = Calibration.INVALID
    cal_perc = 0
    mismatch_counter = 0
    low_battery = False
    events_prev = []

    sm['pathPlan'].sensorValid = True
    sm['pathPlan'].posenetValid = True

    # detect sound card presence
    sounds_available = not os.path.isfile('/EON') or (
        os.path.isdir('/proc/asound/card0')
        and open('/proc/asound/card0/state').read().strip() == 'ONLINE')

    # controlsd is driven by can recv, expected at 100Hz
    rk = Ratekeeper(100, print_delay_threshold=None)

    prof = Profiler(False)  # off by default

    while True:
        start_time = sec_since_boot()
        prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data and compute car events
        CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter =\
          data_sample(CI, CC, sm, can_sock, cal_status, cal_perc, overtemp, free_space, low_battery,
                      driver_status, state, mismatch_counter, params)
        prof.checkpoint("Sample")

        # Create alerts
        if not sm.all_alive_and_valid():
            events.append(
                create_event('commIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if not sm['pathPlan'].mpcSolutionValid:
            events.append(
                create_event('plannerError',
                             [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
        if not sm['pathPlan'].sensorValid:
            events.append(
                create_event('sensorDataInvalid', [ET.NO_ENTRY, ET.PERMANENT]))
        if not sm['pathPlan'].paramsValid:
            events.append(create_event('vehicleModelInvalid', [ET.WARNING]))
        if not sm['pathPlan'].posenetValid:
            events.append(
                create_event('posenetInvalid', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if not sm['plan'].radarValid:
            events.append(
                create_event('radarFault', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if sm['plan'].radarCanError:
            events.append(
                create_event('radarCanError', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if not CS.canValid:
            events.append(
                create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
        if not sounds_available:
            events.append(
                create_event('soundsUnavailable', [ET.NO_ENTRY, ET.PERMANENT]))

        # Only allow engagement with brake pressed when stopped behind another stopped car
        if CS.brakePressed and sm[
                'plan'].vTargetFuture >= STARTING_TARGET_SPEED and not CP.radarOffCan and CS.vEgo < 0.3:
            events.append(
                create_event('noTarget', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))

        if not read_only:
            # update control state
            state, soft_disable_timer, v_cruise_kph, v_cruise_kph_last = \
              state_transition(sm.frame, CS, CP, state, events, soft_disable_timer, v_cruise_kph, AM)
            prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, v_cruise_kph, driver_status, v_acc, a_acc, lac_log = \
          state_control(sm.frame, sm.rcv_frame, sm['plan'], sm['pathPlan'], CS, CP, state, events, v_cruise_kph, v_cruise_kph_last, AM, rk,
                        driver_status, LaC, LoC, VM, read_only, is_metric, cal_perc)

        prof.checkpoint("State Control")

        # Publish data
        CC, events_prev = data_send(sm, CS, CI, CP, VM, state, events,
                                    actuators, v_cruise_kph, rk, carstate,
                                    carcontrol, carevents, carparams,
                                    controlsstate, sendcan, AM, driver_status,
                                    LaC, LoC, read_only, start_time, v_acc,
                                    a_acc, lac_log, events_prev)
        prof.checkpoint("Sent")

        rk.monitor_time()
        prof.display()
Example #3
0
def controlsd_thread(gctx, rate=100):  #rate in Hz
    # *** log ***
    context = zmq.Context()
    live100 = messaging.pub_sock(context, service_list['live100'].port)

    thermal = messaging.sub_sock(context, service_list['thermal'].port)
    live20 = messaging.sub_sock(context, service_list['live20'].port)
    model = messaging.sub_sock(context, service_list['model'].port)
    health = messaging.sub_sock(context, service_list['health'].port)

    # connects to can and sendcan
    CI = CarInterface()
    VP = CI.getVehicleParams()

    PP = PathPlanner(model)
    AC = AdaptiveCruise(live20)

    AM = AlertManager()

    LoC = LongControl()
    LaC = LatControl()

    # controls enabled state
    enabled = False
    last_enable_request = 0

    # learned angle offset
    angle_offset = 0

    # rear view camera state
    rear_view_toggle = False

    v_cruise_kph = 255

    # 0.0 - 1.0
    awareness_status = 0.0

    soft_disable_timer = None

    # Is cpu temp too high to enable?
    overtemp = False
    free_space = 1.0

    # start the loop
    set_realtime_priority(2)

    rk = Ratekeeper(rate, print_delay_threshold=2. / 1000)
    while 1:
        prof = Profiler()
        cur_time = sec_since_boot()

        # read CAN
        CS = CI.update()

        prof.checkpoint("CarInterface")

        # did it request to enable?
        enable_request, enable_condition = False, False

        if enabled:
            # gives the user 6 minutes
            awareness_status -= 1.0 / (100 * 60 * 6)
            if awareness_status <= 0.:
                AM.add("driverDistracted", enabled)

        # reset awareness status on steering
        if CS.steeringPressed:
            awareness_status = 1.0

        # handle button presses
        for b in CS.buttonEvents:
            print b

            # reset awareness on any user action
            awareness_status = 1.0

            # button presses for rear view
            if b.type == "leftBlinker" or b.type == "rightBlinker":
                if b.pressed:
                    rear_view_toggle = True
                else:
                    rear_view_toggle = False

            if b.type == "altButton1" and b.pressed:
                rear_view_toggle = not rear_view_toggle

            if not VP.brake_only and enabled and not b.pressed:
                if b.type == "accelCruise":
                    v_cruise_kph = v_cruise_kph - (
                        v_cruise_kph % V_CRUISE_DELTA) + V_CRUISE_DELTA
                elif b.type == "decelCruise":
                    v_cruise_kph = v_cruise_kph - (
                        v_cruise_kph % V_CRUISE_DELTA) - V_CRUISE_DELTA
                v_cruise_kph = clip(v_cruise_kph, V_CRUISE_MIN, V_CRUISE_MAX)

            if not enabled and b.type in ["accelCruise", "decelCruise"
                                          ] and not b.pressed:
                enable_request = True

            # do disable on button down
            if b.type == "cancel" and b.pressed:
                AM.add("disable", enabled)

        prof.checkpoint("Buttons")

        # *** health checking logic ***
        hh = messaging.recv_sock(health)
        if hh is not None:
            # if the board isn't allowing controls but somehow we are enabled!
            if not hh.health.controlsAllowed and enabled:
                AM.add("controlsMismatch", enabled)

        # *** thermal checking logic ***

        # thermal data, checked every second
        td = messaging.recv_sock(thermal)
        if td is not None:
            # Check temperature.
            overtemp = any(t > 950 for t in (td.thermal.cpu0, td.thermal.cpu1,
                                             td.thermal.cpu2, td.thermal.cpu3,
                                             td.thermal.mem, td.thermal.gpu))
            # under 15% of space free
            free_space = td.thermal.freeSpace

        prof.checkpoint("Health")

        # *** getting model logic ***
        PP.update(cur_time, CS.vEgo)

        if rk.frame % 5 == 2:
            # *** run this at 20hz again ***
            angle_offset = learn_angle_offset(enabled, CS.vEgo, angle_offset,
                                              np.asarray(PP.d_poly), LaC.y_des,
                                              CS.steeringPressed)

        # disable if the pedals are pressed while engaged, this is a user disable
        if enabled:
            if CS.gasPressed or CS.brakePressed:
                AM.add("disable", enabled)

        if enable_request:
            # check for pressed pedals
            if CS.gasPressed or CS.brakePressed:
                AM.add("pedalPressed", enabled)
                enable_request = False
            else:
                print "enabled pressed at", cur_time
                last_enable_request = cur_time

            # don't engage with less than 15% free
            if free_space < 0.15:
                AM.add("outOfSpace", enabled)
                enable_request = False

        if VP.brake_only:
            enable_condition = ((cur_time - last_enable_request) <
                                0.2) and CS.cruiseState.enabled
        else:
            enable_condition = enable_request

        if enable_request or enable_condition or enabled:
            # add all alerts from car
            for alert in CS.errors:
                AM.add(alert, enabled)

            if AC.dead:
                AM.add("radarCommIssue", enabled)

            if PP.dead:
                AM.add("modelCommIssue", enabled)

            if overtemp:
                AM.add("overheat", enabled)

        prof.checkpoint("Model")

        if enable_condition and not enabled and not AM.alertPresent():
            print "*** enabling controls"

            # beep for enabling
            AM.add("enable", enabled)

            # enable both lateral and longitudinal controls
            enabled = True

            # on activation, let's always set v_cruise from where we are, even if PCM ACC is active
            v_cruise_kph = int(
                round(
                    max(CS.vEgo * CV.MS_TO_KPH * VP.ui_speed_fudge,
                        V_CRUISE_ENABLE_MIN)))

            # 6 minutes driver you're on
            awareness_status = 1.0

            # reset the PID loops
            LaC.reset()
            # start long control at actual speed
            LoC.reset(v_pid=CS.vEgo)

        if VP.brake_only and CS.cruiseState.enabled:
            v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH

        # *** put the adaptive in adaptive cruise control ***
        AC.update(cur_time, CS.vEgo, CS.steeringAngle, LoC.v_pid,
                  awareness_status, VP)

        prof.checkpoint("AdaptiveCruise")

        # *** gas/brake PID loop ***
        final_gas, final_brake = LoC.update(enabled, CS.vEgo, v_cruise_kph,
                                            AC.v_target_lead, AC.a_target,
                                            AC.jerk_factor, VP)

        # *** steering PID loop ***
        final_steer, sat_flag = LaC.update(enabled, CS.vEgo, CS.steeringAngle,
                                           CS.steeringPressed, PP.d_poly,
                                           angle_offset, VP)

        prof.checkpoint("PID")

        # ***** handle alerts ****
        # send a "steering required alert" if saturation count has reached the limit
        if sat_flag:
            AM.add("steerSaturated", enabled)

        if enabled and AM.alertShouldDisable():
            print "DISABLING IMMEDIATELY ON ALERT"
            enabled = False

        if enabled and AM.alertShouldSoftDisable():
            if soft_disable_timer is None:
                soft_disable_timer = 3 * rate
            elif soft_disable_timer == 0:
                print "SOFT DISABLING ON ALERT"
                enabled = False
            else:
                soft_disable_timer -= 1
        else:
            soft_disable_timer = None

        # *** push the alerts to current ***
        alert_text_1, alert_text_2, visual_alert, audible_alert = AM.process_alerts(
            cur_time)

        # ***** control the car *****
        CC = car.CarControl.new_message()

        CC.enabled = enabled

        CC.gas = float(final_gas)
        CC.brake = float(final_brake)
        CC.steeringTorque = float(final_steer)

        CC.cruiseControl.override = True
        CC.cruiseControl.cancel = bool(
            (not VP.brake_only)
            or (not enabled and CS.cruiseState.enabled
                ))  # always cancel if we have an interceptor
        CC.cruiseControl.speedOverride = float((LoC.v_pid - .3) if (
            VP.brake_only and final_brake == 0.) else 0.0)
        CC.cruiseControl.accelOverride = float(AC.a_pcm)

        CC.hudControl.setSpeed = float(v_cruise_kph * CV.KPH_TO_MS)
        CC.hudControl.speedVisible = enabled
        CC.hudControl.lanesVisible = enabled
        CC.hudControl.leadVisible = bool(AC.has_lead)

        CC.hudControl.visualAlert = visual_alert
        CC.hudControl.audibleAlert = audible_alert

        # this alert will apply next controls cycle
        if not CI.apply(CC):
            AM.add("controlsFailed", enabled)

        prof.checkpoint("CarControl")

        # ***** publish state to logger *****

        # publish controls state at 100Hz
        dat = messaging.new_message()
        dat.init('live100')

        # show rear view camera on phone if in reverse gear or when button is pressed
        dat.live100.rearViewCam = ('reverseGear'
                                   in CS.errors) or rear_view_toggle
        dat.live100.alertText1 = alert_text_1
        dat.live100.alertText2 = alert_text_2
        dat.live100.awarenessStatus = max(awareness_status,
                                          0.0) if enabled else 0.0

        # what packets were used to process
        dat.live100.canMonoTimes = list(CS.canMonoTimes)
        dat.live100.mdMonoTime = PP.logMonoTime
        dat.live100.l20MonoTime = AC.logMonoTime

        # if controls is enabled
        dat.live100.enabled = enabled

        # car state
        dat.live100.vEgo = CS.vEgo
        dat.live100.angleSteers = CS.steeringAngle
        dat.live100.steerOverride = CS.steeringPressed

        # longitudinal control state
        dat.live100.vPid = float(LoC.v_pid)
        dat.live100.vCruise = float(v_cruise_kph)
        dat.live100.upAccelCmd = float(LoC.Up_accel_cmd)
        dat.live100.uiAccelCmd = float(LoC.Ui_accel_cmd)

        # lateral control state
        dat.live100.yActual = float(LaC.y_actual)
        dat.live100.yDes = float(LaC.y_des)
        dat.live100.upSteer = float(LaC.Up_steer)
        dat.live100.uiSteer = float(LaC.Ui_steer)

        # processed radar state, should add a_pcm?
        dat.live100.vTargetLead = float(AC.v_target_lead)
        dat.live100.aTargetMin = float(AC.a_target[0])
        dat.live100.aTargetMax = float(AC.a_target[1])
        dat.live100.jerkFactor = float(AC.jerk_factor)

        # lag
        dat.live100.cumLagMs = -rk.remaining * 1000.

        live100.send(dat.to_bytes())

        prof.checkpoint("Live100")

        # *** run loop at fixed rate ***
        if rk.keep_time():
            prof.display()
Example #4
0
class Controls:
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(3, Priority.CTRL_HIGH)
        self.op_params = opParams()

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster([
                'deviceState', 'pandaState', 'modelV2', 'liveCalibration',
                'driverMonitoringState', 'longitudinalPlan', 'lateralPlan',
                'liveLocationKalman', 'roadCameraState', 'driverCameraState',
                'managerState', 'liveParameters', 'radarState'
            ],
                                          ignore_alive=ignore)

        self.sm_smiskol = messaging.SubMaster([
            'radarState', 'dynamicFollowData', 'liveTracks',
            'dynamicFollowButton', 'laneSpeed', 'dynamicCameraOffset',
            'modelLongButton'
        ])

        self.op_params = opParams()
        self.df_manager = dfManager()
        self.support_white_panda = self.op_params.get('support_white_panda')
        self.last_model_long = False

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        # wait for one pandaState and one CAN packet
        panda_type = messaging.recv_one(
            self.sm.sock['pandaState']).pandaState.pandaType
        has_relay = panda_type in [
            PandaType.blackPanda, PandaType.uno, PandaType.dos
        ]
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP, candidate = get_car(self.can_sock,
                                              self.pm.sock['sendcan'],
                                              has_relay)
        threading.Thread(target=log_fingerprint, args=[candidate]).start()

        # read params
        params = Params()
        self.is_metric = params.get("IsMetric", encoding='utf8') == "1"
        self.is_ldw_enabled = params.get("IsLdwEnabled",
                                         encoding='utf8') == "1"
        community_feature_toggle = params.get("CommunityFeaturesToggle",
                                              encoding='utf8') == "1"
        openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                              encoding='utf8') == "1"
        passive = params.get(
            "Passive", encoding='utf8') == "1" or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'
        # If stock camera is disconnected, we loaded car controls and it's not dashcam mode
        controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard and boardd safety mode
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb, candidate)
        self.VM = VehicleModel(self.CP)

        if self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = False

        self.sm['liveCalibration'].calStatus = Calibration.CALIBRATED
        self.sm['deviceState'].freeSpacePercent = 100
        self.sm['driverMonitoringState'].events = []
        self.sm['driverMonitoringState'].awarenessStatus = 1.
        self.sm['driverMonitoringState'].faceDetected = False

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if community_feature_disallowed:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default

        self.lead_rel_speed = 255
        self.lead_long_dist = 255

    def update_events(self, CS):
        """Compute carEvents from carState"""

        self.events.clear()
        self.events.add_from_msg(CS.events)
        self.events.add_from_msg(self.sm['driverMonitoringState'].events)

        # Handle startup event
        if self.startup_event is not None:
            self.events.add(self.startup_event)
            self.startup_event = None

        # Create events for battery, temperature, disk space, and memory
        if self.sm['deviceState'].batteryPercent < 1 and self.sm[
                'deviceState'].chargingError:
            # at zero percent battery, while discharging, OP should not allowed
            self.events.add(EventName.lowBattery)
        if self.sm['deviceState'].thermalStatus >= ThermalStatus.red:
            self.events.add(EventName.overheat)
        if self.sm['deviceState'].freeSpacePercent < 7:
            # under 7% of space free no enable allowed
            self.events.add(EventName.outOfSpace)
        if self.sm['deviceState'].memoryUsagePercent > 90:
            self.events.add(EventName.lowMemory)

        # Alert if fan isn't spinning for 5 seconds
        if self.sm['pandaState'].pandaType in [PandaType.uno, PandaType.dos]:
            if self.sm['pandaState'].fanSpeedRpm == 0 and self.sm[
                    'deviceState'].fanSpeedPercentDesired > 50:
                if (self.sm.frame -
                        self.last_functional_fan_frame) * DT_CTRL > 5.0:
                    self.events.add(EventName.fanMalfunction)
            else:
                self.last_functional_fan_frame = self.sm.frame

        # Handle calibration status
        cal_status = self.sm['liveCalibration'].calStatus
        if cal_status != Calibration.CALIBRATED:
            if cal_status == Calibration.UNCALIBRATED:
                self.events.add(EventName.calibrationIncomplete)
            else:
                self.events.add(EventName.calibrationInvalid)

        # Handle lane change
        if self.sm[
                'lateralPlan'].laneChangeState == LaneChangeState.preLaneChange:
            direction = self.sm['lateralPlan'].laneChangeDirection
            if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
               (CS.rightBlindspot and direction == LaneChangeDirection.right):
                self.events.add(EventName.laneChangeBlocked)
            else:
                if direction == LaneChangeDirection.left:
                    self.events.add(EventName.preLaneChangeLeft)
                else:
                    self.events.add(EventName.preLaneChangeRight)
        elif self.sm['lateralPlan'].laneChangeState in [
                LaneChangeState.laneChangeStarting,
                LaneChangeState.laneChangeFinishing
        ]:
            self.events.add(EventName.laneChange)

        if self.can_rcv_error or (not CS.canValid
                                  and self.sm.frame > 5 / DT_CTRL):
            self.events.add(EventName.canError)
        if (self.sm['pandaState'].safetyModel != self.CP.safetyModel and self.sm.frame > 2 / DT_CTRL) or \
          self.mismatch_counter >= 200:
            self.events.add(EventName.controlsMismatch)

        if len(self.sm['radarState'].radarErrors):
            self.events.add(EventName.radarFault)
        elif not self.sm.valid['liveParameters']:
            self.events.add(EventName.vehicleModelInvalid)
        elif not self.sm.all_alive_and_valid():
            self.events.add(EventName.commIssue)
            if not self.logged_comm_issue:
                cloudlog.error(
                    f"commIssue - valid: {self.sm.valid} - alive: {self.sm.alive}"
                )
                self.logged_comm_issue = True
        else:
            self.logged_comm_issue = False

        if not self.sm['lateralPlan'].mpcSolutionValid:
            self.events.add(EventName.plannerError)
        if not self.sm['liveLocationKalman'].sensorsOK and not NOSENSOR:
            if self.sm.frame > 5 / DT_CTRL:  # Give locationd some time to receive all the inputs
                self.events.add(EventName.sensorDataInvalid)
        if not self.sm['liveLocationKalman'].posenetOK:
            self.events.add(EventName.posenetInvalid)
        if not self.sm['liveLocationKalman'].deviceStable:
            self.events.add(EventName.deviceFalling)
        if log.PandaState.FaultType.relayMalfunction in self.sm[
                'pandaState'].faults:
            self.events.add(EventName.relayMalfunction)
        if self.sm['longitudinalPlan'].fcw:
            self.events.add(EventName.fcw)

            # TODO: fix simulator
            # if not SIMULATION:
            #   if not NOSENSOR and not self.support_white_panda:
            #     if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled > 1000) and not TICI:
            #       # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes
            #       self.events.add(EventName.noGps)
            if not self.sm.all_alive(['roadCameraState', 'driverCameraState'
                                      ]) and (self.sm.frame > 5 / DT_CTRL):
                self.events.add(EventName.cameraMalfunction)
            if self.sm['modelV2'].frameDropPerc > 20:
                self.events.add(EventName.modeldLagging)

            # Check if all manager processes are running
            not_running = set(p.name for p in self.sm['managerState'].processes
                              if not p.running)
            if self.sm.rcv_frame['managerState'] and (not_running -
                                                      IGNORE_PROCESSES):
                self.events.add(EventName.processNotRunning)

        # Only allow engagement with brake pressed when stopped behind another stopped car
        if CS.brakePressed and self.sm['longitudinalPlan'].vTargetFuture >= STARTING_TARGET_SPEED \
          and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3 and not self.last_model_long:
            self.events.add(EventName.noTarget)

        self.add_stock_additions_alerts(CS)

        # vision-only fcw, can be disabled if radar is present
        if self.sm.updated['radarState']:
            self.lead_rel_speed = self.sm['radarState'].leadOne.vRel
            self.lead_long_dist = self.sm['radarState'].leadOne.dRel
        #if CS.cruiseState.enabled and self.lead_long_dist > 5 and self.lead_long_dist < 100 and self.lead_rel_speed <= -0.5 and CS.vEgo >= 5 and \
        #        ((self.lead_long_dist/abs(self.lead_rel_speed) < 2.) or (self.lead_long_dist/abs(self.lead_rel_speed) < 4. and self.lead_rel_speed < -10) or \
        #         (self.lead_long_dist/abs(self.lead_rel_speed) < 5. and self.lead_long_dist/CS.vEgo < 1.5)):
        #  self.events.add(EventName.fcw)

    def add_stock_additions_alerts(self, CS):
        self.AM.SA_set_frame(self.sm.frame)
        self.AM.SA_set_enabled(self.enabled)
        # alert priority is defined by code location, keeping is highest, then lane speed alert, then auto-df alert
        if self.sm_smiskol['modelLongButton'].enabled != self.last_model_long:
            extra_text_1 = 'disabled!' if self.last_model_long else 'enabled!'
            extra_text_2 = '' if self.last_model_long else ', model may behave unexpectedly'
            self.AM.SA_add('modelLongAlert',
                           extra_text_1=extra_text_1,
                           extra_text_2=extra_text_2)
            return

        if self.sm_smiskol['dynamicCameraOffset'].keepingLeft:
            self.AM.SA_add('laneSpeedKeeping',
                           extra_text_1='LEFT',
                           extra_text_2='Oncoming traffic in right lane')
            return
        elif self.sm_smiskol['dynamicCameraOffset'].keepingRight:
            self.AM.SA_add('laneSpeedKeeping',
                           extra_text_1='RIGHT',
                           extra_text_2='Oncoming traffic in left lane')
            return

        ls_state = self.sm_smiskol['laneSpeed'].state
        if ls_state != '':
            self.AM.SA_add('lsButtonAlert', extra_text_1=ls_state)
            return

        faster_lane = self.sm_smiskol['laneSpeed'].fastestLane
        if faster_lane in ['left', 'right']:
            ls_alert = 'laneSpeedAlert'
            if not self.sm_smiskol['laneSpeed'].new:
                ls_alert += 'Silent'
            self.AM.SA_add(
                ls_alert,
                extra_text_1='{} lane faster'.format(faster_lane).upper(),
                extra_text_2='Change lanes to faster {} lane'.format(
                    faster_lane))
            return

        df_out = self.df_manager.update()
        if df_out.changed:
            df_alert = 'dfButtonAlert'
            if df_out.is_auto and df_out.last_is_auto:
                # only show auto alert if engaged, not hiding auto, and time since lane speed alert not showing
                if CS.cruiseState.enabled and not self.op_params.get(
                        'hide_auto_df_alerts'):
                    df_alert += 'Silent'
                    self.AM.SA_add(df_alert,
                                   extra_text_1=df_out.model_profile_text +
                                   ' (auto)')
                    return
            else:
                self.AM.SA_add(
                    df_alert,
                    extra_text_1=df_out.user_profile_text,
                    extra_text_2='Dynamic follow: {} profile active'.format(
                        df_out.user_profile_text))
                return

    def data_sample(self):
        """Receive data from sockets and update carState"""

        # Update carState from CAN
        can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
        CS = self.CI.update(self.CC, can_strs)

        self.sm.update(0)
        self.sm_smiskol.update(0)

        # Check for CAN timeout
        if not can_strs:
            self.can_error_counter += 1
            self.can_rcv_error = True
        else:
            self.can_rcv_error = False

        # When the panda and controlsd do not agree on controls_allowed
        # we want to disengage openpilot. However the status from the panda goes through
        # another socket other than the CAN messages and one can arrive earlier than the other.
        # Therefore we allow a mismatch for two samples, then we trigger the disengagement.
        if not self.enabled:
            self.mismatch_counter = 0

        if not self.sm['pandaState'].controlsAllowed and self.enabled:
            self.mismatch_counter += 1

        self.distance_traveled += CS.vEgo * DT_CTRL

        return CS

    def state_transition(self, CS):
        """Compute conditional state transitions and execute actions on state transitions"""

        self.v_cruise_kph_last = self.v_cruise_kph

        # if stock cruise is completely disabled, then we can use our own set speed logic
        if not self.CP.enableCruise:
            self.v_cruise_kph = update_v_cruise(self.v_cruise_kph,
                                                CS.buttonEvents, self.enabled)
        elif self.CP.enableCruise and CS.cruiseState.enabled:
            self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH

        # decrease the soft disable timer at every step, as it's reset on
        # entrance in SOFT_DISABLING state
        self.soft_disable_timer = max(0, self.soft_disable_timer - 1)

        self.current_alert_types = [ET.PERMANENT]

        # ENABLED, PRE ENABLING, SOFT DISABLING
        if self.state != State.disabled:
            # user and immediate disable always have priority in a non-disabled state
            if self.events.any(ET.USER_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.USER_DISABLE)

            elif self.events.any(ET.IMMEDIATE_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.IMMEDIATE_DISABLE)

            else:
                # ENABLED
                if self.state == State.enabled:
                    if self.events.any(ET.SOFT_DISABLE):
                        self.state = State.softDisabling
                        self.soft_disable_timer = 300  # 3s
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                # SOFT DISABLING
                elif self.state == State.softDisabling:
                    if not self.events.any(ET.SOFT_DISABLE):
                        # no more soft disabling condition, so go back to ENABLED
                        self.state = State.enabled

                    elif self.events.any(
                            ET.SOFT_DISABLE) and self.soft_disable_timer > 0:
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                    elif self.soft_disable_timer <= 0:
                        self.state = State.disabled

                # PRE ENABLING
                elif self.state == State.preEnabled:
                    if not self.events.any(ET.PRE_ENABLE):
                        self.state = State.enabled
                    else:
                        self.current_alert_types.append(ET.PRE_ENABLE)

        # DISABLED
        elif self.state == State.disabled:
            if self.events.any(ET.ENABLE):
                if self.events.any(ET.NO_ENTRY):
                    self.current_alert_types.append(ET.NO_ENTRY)

                else:
                    if self.events.any(ET.PRE_ENABLE):
                        self.state = State.preEnabled
                    else:
                        self.state = State.enabled
                    self.current_alert_types.append(ET.ENABLE)
                    self.v_cruise_kph = initialize_v_cruise(
                        CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)

        # Check if actuators are enabled
        self.active = self.state == State.enabled or self.state == State.softDisabling
        if self.active:
            self.current_alert_types.append(ET.WARNING)

        # Check if openpilot is engaged
        self.enabled = self.active or self.state == State.preEnabled

    def state_control(self, CS):
        """Given the state, this function returns an actuators packet"""

        lat_plan = self.sm['lateralPlan']
        long_plan = self.sm['longitudinalPlan']

        actuators = car.CarControl.Actuators.new_message()

        if CS.leftBlinker or CS.rightBlinker:
            self.last_blinker_frame = self.sm.frame

        # State specific actions

        if not self.active:
            self.LaC.reset()
            self.LoC.reset(v_pid=CS.vEgo)

        long_plan_age = DT_CTRL * (self.sm.frame -
                                   self.sm.rcv_frame['longitudinalPlan'])
        # no greater than dt mpc + dt, to prevent too high extraps
        dt = min(long_plan_age, LON_MPC_STEP + DT_CTRL) + DT_CTRL

        a_acc_sol = long_plan.aStart + (dt / LON_MPC_STEP) * (
            long_plan.aTarget - long_plan.aStart)
        v_acc_sol = long_plan.vStart + dt * (a_acc_sol +
                                             long_plan.aStart) / 2.0

        extras_loc = {
            'lead_one': self.sm_smiskol['radarState'].leadOne,
            'mpc_TR': self.sm_smiskol['dynamicFollowData'].mpcTR,
            'live_tracks': self.sm_smiskol['liveTracks'],
            'has_lead': long_plan.hasLead
        }

        # Gas/Brake PID loop
        actuators.gas, actuators.brake = self.LoC.update(
            self.active, CS, v_acc_sol, long_plan.vTargetFuture, a_acc_sol,
            self.CP, extras_loc)
        # Steering PID loop and lateral MPC
        actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(
            self.active, CS, self.CP, lat_plan)

        # Check for difference between desired angle and angle for angle based control
        angle_control_saturated = self.CP.steerControlType == car.CarParams.SteerControlType.angle and \
          abs(actuators.steeringAngleDeg - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD

        if angle_control_saturated and not CS.steeringPressed and self.active:
            self.saturated_count += 1
        else:
            self.saturated_count = 0

        # Send a "steering required alert" if saturation count has reached the limit
        if (lac_log.saturated and not CS.steeringPressed) or \
           (self.saturated_count > STEER_ANGLE_SATURATION_TIMEOUT):
            # Check if we deviated from the path
            left_deviation = actuators.steer > 0 and lat_plan.dPathPoints[
                0] < -0.1
            right_deviation = actuators.steer < 0 and lat_plan.dPathPoints[
                0] > 0.1

            if left_deviation or right_deviation:
                self.events.add(EventName.steerSaturated)

        return actuators, v_acc_sol, a_acc_sol, lac_log

    def publish_logs(self, CS, start_time, actuators, v_acc, a_acc, lac_log):
        """Send actuators and hud commands to the car, send controlsstate and MPC logging"""

        CC = car.CarControl.new_message()
        CC.enabled = self.enabled
        CC.actuators = actuators

        CC.cruiseControl.override = True
        CC.cruiseControl.cancel = not self.CP.enableCruise or (
            not self.enabled and CS.cruiseState.enabled)

        # Some override values for Honda
        # brake discount removes a sharp nonlinearity
        brake_discount = (1.0 - clip(actuators.brake * 3., 0.0, 1.0))
        speed_override = max(0.0,
                             (self.LoC.v_pid + CS.cruiseState.speedOffset) *
                             brake_discount)
        CC.cruiseControl.speedOverride = float(
            speed_override if self.CP.enableCruise else 0.0)
        CC.cruiseControl.accelOverride = self.CI.calc_accel_override(
            CS.aEgo, self.sm['longitudinalPlan'].aTarget, CS.vEgo,
            self.sm['longitudinalPlan'].vTarget)

        CC.hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS)
        CC.hudControl.speedVisible = self.enabled
        CC.hudControl.lanesVisible = self.enabled
        CC.hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead

        right_lane_visible = self.sm['lateralPlan'].rProb > 0.5
        left_lane_visible = self.sm['lateralPlan'].lProb > 0.5
        CC.hudControl.rightLaneVisible = bool(right_lane_visible)
        CC.hudControl.leftLaneVisible = bool(left_lane_visible)

        recent_blinker = (self.sm.frame - self.last_blinker_frame
                          ) * DT_CTRL < 5.0  # 5s blinker cooldown
        ldw_allowed = self.is_ldw_enabled and CS.vEgo > LDW_MIN_SPEED and not recent_blinker \
                        and (not self.active or CS.epsDisabled == True) and self.sm['liveCalibration'].calStatus == Calibration.CALIBRATED

        meta = self.sm['modelV2'].meta
        if len(meta.desirePrediction) and ldw_allowed:
            l_lane_change_prob = meta.desirePrediction[Desire.laneChangeLeft -
                                                       1]
            r_lane_change_prob = meta.desirePrediction[Desire.laneChangeRight -
                                                       1]
            CAMERA_OFFSET = self.sm['lateralPlan'].cameraOffset
            ldw_average_car_width = 1.750483672001016  # from sedans, suvs, and minivans (todo: find from all openpilot Toyotas instead)
            ldw_m_from_wheel = 0.15
            ldw_threshold = ldw_average_car_width / 2 + ldw_m_from_wheel
            l_lane_close = left_lane_visible and (
                self.sm['modelV2'].laneLines[1].y[0] >
                -(ldw_threshold + CAMERA_OFFSET))
            r_lane_close = right_lane_visible and (
                self.sm['modelV2'].laneLines[2].y[0] <
                (ldw_threshold - CAMERA_OFFSET))

            CC.hudControl.leftLaneDepart = bool(
                l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
            CC.hudControl.rightLaneDepart = bool(
                r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)

        if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
            self.events.add(EventName.ldw)

        clear_event = ET.WARNING if ET.WARNING not in self.current_alert_types else None
        alerts = self.events.create_alerts(self.current_alert_types,
                                           [self.CP, self.sm, self.is_metric])
        self.AM.add_many(self.sm.frame, alerts, self.enabled)

        self.last_model_long = self.sm_smiskol['modelLongButton'].enabled

        self.AM.process_alerts(self.sm.frame, clear_event)
        CC.hudControl.visualAlert = self.AM.visual_alert

        if not self.read_only:
            # send car controls over can
            can_sends = self.CI.apply(CC)
            self.pm.send(
                'sendcan',
                can_list_to_can_capnp(can_sends,
                                      msgtype='sendcan',
                                      valid=CS.canValid))

        force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \
                      (self.state == State.softDisabling)

        steer_angle_rad = (
            CS.steeringAngleDeg -
            self.sm['lateralPlan'].angleOffsetDeg) * CV.DEG_TO_RAD

        # controlsState
        dat = messaging.new_message('controlsState')
        dat.valid = CS.canValid
        controlsState = dat.controlsState
        controlsState.alertText1 = self.AM.alert_text_1
        controlsState.alertText2 = self.AM.alert_text_2
        controlsState.alertSize = self.AM.alert_size
        controlsState.alertStatus = self.AM.alert_status
        controlsState.alertBlinkingRate = self.AM.alert_rate
        controlsState.alertType = self.AM.alert_type
        controlsState.alertSound = self.AM.audible_alert
        controlsState.canMonoTimes = list(CS.canMonoTimes)
        controlsState.longitudinalPlanMonoTime = self.sm.logMonoTime[
            'longitudinalPlan']
        controlsState.lateralPlanMonoTime = self.sm.logMonoTime['lateralPlan']
        controlsState.enabled = self.enabled
        controlsState.active = self.active
        controlsState.curvature = self.VM.calc_curvature(
            steer_angle_rad, CS.vEgo)
        controlsState.state = self.state
        controlsState.engageable = not self.events.any(ET.NO_ENTRY)
        controlsState.longControlState = self.LoC.long_control_state
        controlsState.vPid = float(self.LoC.v_pid)
        controlsState.vCruise = float(self.v_cruise_kph)
        controlsState.upAccelCmd = float(self.LoC.pid.p)
        controlsState.uiAccelCmd = float(self.LoC.pid.id)
        controlsState.ufAccelCmd = float(self.LoC.pid.f)
        controlsState.steeringAngleDesiredDeg = float(
            self.LaC.angle_steers_des)
        controlsState.vTargetLead = float(v_acc)
        controlsState.aTarget = float(a_acc)
        controlsState.cumLagMs = -self.rk.remaining * 1000.
        controlsState.startMonoTime = int(start_time * 1e9)
        controlsState.forceDecel = bool(force_decel)
        controlsState.canErrorCounter = self.can_error_counter

        if self.CP.lateralTuning.which() == 'pid':
            controlsState.lateralControlState.pidState = lac_log
        elif self.CP.lateralTuning.which() == 'lqr':
            controlsState.lateralControlState.lqrState = lac_log
        elif self.CP.lateralTuning.which() == 'indi':
            controlsState.lateralControlState.indiState = lac_log
        self.pm.send('controlsState', dat)

        # carState
        car_events = self.events.to_msg()
        cs_send = messaging.new_message('carState')
        cs_send.valid = CS.canValid
        cs_send.carState = CS
        cs_send.carState.events = car_events
        self.pm.send('carState', cs_send)

        # carEvents - logged every second or on change
        if (self.sm.frame % int(1. / DT_CTRL)
                == 0) or (self.events.names != self.events_prev):
            ce_send = messaging.new_message('carEvents', len(self.events))
            ce_send.carEvents = car_events
            self.pm.send('carEvents', ce_send)
        self.events_prev = self.events.names.copy()

        # 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.carParams = self.CP
            self.pm.send('carParams', cp_send)

        # carControl
        cc_send = messaging.new_message('carControl')
        cc_send.valid = CS.canValid
        cc_send.carControl = CC
        self.pm.send('carControl', cc_send)

        # copy CarControl to pass to CarInterface on the next iteration
        self.CC = CC

    def step(self):
        start_time = sec_since_boot()
        self.prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data from sockets and get a carState
        CS = self.data_sample()
        self.prof.checkpoint("Sample")

        self.update_events(CS)

        if not self.read_only:
            # Update control state
            self.state_transition(CS)
            self.prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, v_acc, a_acc, lac_log = self.state_control(CS)

        self.prof.checkpoint("State Control")

        # Publish data
        self.publish_logs(CS, start_time, actuators, v_acc, a_acc, lac_log)
        self.prof.checkpoint("Sent")

    def controlsd_thread(self):
        while True:
            self.step()
            self.rk.monitor_time()
            self.prof.display()
Example #5
0
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(3, Priority.CTRL_HIGH)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.sm = sm
        if self.sm is None:
            self.sm = messaging.SubMaster([
                'thermal', 'health', 'model', 'liveCalibration', 'frontFrame',
                'dMonitoringState', 'plan', 'pathPlan', 'liveLocationKalman'
            ])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        # wait for one health and one CAN packet
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])

        # read params
        params = Params()
        self.is_metric = params.get("IsMetric", encoding='utf8') == "1"
        self.is_ldw_enabled = params.get("IsLdwEnabled",
                                         encoding='utf8') == "1"
        internet_needed = (params.get("Offroad_ConnectivityNeeded",
                                      encoding='utf8') is not None) and (
                                          params.get("DisableUpdates") != b"1")
        community_feature_toggle = params.get("CommunityFeaturesToggle",
                                              encoding='utf8') == "1"
        openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                              encoding='utf8') == "1"
        passive = params.get("Passive", encoding='utf8') == "1" or \
                  internet_needed or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'
        # If stock camera is disconnected, we loaded car controls and it's not dashcam mode
        controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard and boardd safety mode
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb)
        self.VM = VehicleModel(self.CP)

        if self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]

        self.sm['liveCalibration'].calStatus = Calibration.CALIBRATED
        self.sm['thermal'].freeSpace = 1.
        self.sm['dMonitoringState'].events = []
        self.sm['dMonitoringState'].awarenessStatus = 1.
        self.sm['dMonitoringState'].faceDetected = False

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if internet_needed:
            self.events.add(EventName.internetConnectivityNeeded, static=True)
        if community_feature_disallowed:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default
Example #6
0
def radard_thread(gctx=None):
    set_realtime_priority(1)

    # wait for stats about the car to come in from controls
    cloudlog.info("radard is waiting for CarParams")
    CP = car.CarParams.from_bytes(Params().get("CarParams", block=True))
    cloudlog.info("radard got CarParams")

    # import the radar from the fingerprint
    cloudlog.info("radard is importing %s", CP.radarName)
    exec('from selfdrive.radar.' + CP.radarName +
         '.interface import RadarInterface')

    context = zmq.Context()

    # *** subscribe to features and model from visiond
    model = messaging.sub_sock(context, service_list['model'].port)
    live100 = messaging.sub_sock(context, service_list['live100'].port)

    PP = PathPlanner()
    RI = RadarInterface()

    last_md_ts = 0
    last_l100_ts = 0

    # *** publish live20 and liveTracks
    live20 = messaging.pub_sock(context, service_list['live20'].port)
    liveTracks = messaging.pub_sock(context, service_list['liveTracks'].port)

    path_x = np.arange(0.0, 140.0, 0.1)  # 140 meters is max

    # Time-alignment
    rate = 20.  # model and radar are both at 20Hz
    tsv = 1. / rate
    rdr_delay = 0.10  # radar data delay in s
    v_len = 20  # how many speed data points to remember for t alignment with rdr data

    enabled = 0
    steer_angle = 0.

    tracks = defaultdict(dict)

    # Kalman filter stuff:
    ekfv = EKFV1D()
    speedSensorV = SimpleSensor(XV, 1, 2)

    # v_ego
    v_ego = None
    v_ego_array = np.zeros([2, v_len])
    v_ego_t_aligned = 0.

    rk = Ratekeeper(rate, print_delay_threshold=np.inf)
    while 1:
        rr = RI.update()

        ar_pts = {}
        for pt in rr.points:
            ar_pts[pt.trackId] = [
                pt.dRel + RDR_TO_LDR, pt.yRel, pt.vRel, pt.aRel, None, False,
                None
            ]

        # receive the live100s
        l100 = messaging.recv_sock(live100)
        if l100 is not None:
            enabled = l100.live100.enabled
            v_ego = l100.live100.vEgo
            steer_angle = l100.live100.angleSteers

            v_ego_array = np.append(v_ego_array,
                                    [[v_ego], [float(rk.frame) / rate]], 1)
            v_ego_array = v_ego_array[:, 1:]

            last_l100_ts = l100.logMonoTime

        if v_ego is None:
            continue

        md = messaging.recv_sock(model)
        if md is not None:
            last_md_ts = md.logMonoTime

        # *** get path prediction from the model ***
        PP.update(sec_since_boot(), v_ego, md)

        # run kalman filter only if prob is high enough
        if PP.lead_prob > 0.7:
            ekfv.update(speedSensorV.read(PP.lead_dist, covar=PP.lead_var))
            ekfv.predict(tsv)
            ar_pts[VISION_POINT] = (float(ekfv.state[XV]),
                                    np.polyval(PP.d_poly,
                                               float(ekfv.state[XV])),
                                    float(ekfv.state[SPEEDV]), np.nan,
                                    last_md_ts, np.nan, sec_since_boot())
        else:
            ekfv.state[XV] = PP.lead_dist
            ekfv.covar = (np.diag([PP.lead_var, ekfv.var_init]))
            ekfv.state[SPEEDV] = 0.
            if VISION_POINT in ar_pts:
                del ar_pts[VISION_POINT]

        # *** compute the likely path_y ***
        if enabled:  # use path from model path_poly
            path_y = np.polyval(PP.d_poly, path_x)
        else:  # use path from steer, set angle_offset to 0 since calibration does not exactly report the physical offset
            path_y = calc_lookahead_offset(v_ego,
                                           steer_angle,
                                           path_x,
                                           CP,
                                           angle_offset=0)[0]

        # *** remove missing points from meta data ***
        for ids in tracks.keys():
            if ids not in ar_pts:
                tracks.pop(ids, None)

        # *** compute the tracks ***
        for ids in ar_pts:
            # ignore the vision point for now
            if ids == VISION_POINT and not VISION_ONLY:
                continue
            elif ids != VISION_POINT and VISION_ONLY:
                continue
            rpt = ar_pts[ids]

            # align v_ego by a fixed time to align it with the radar measurement
            cur_time = float(rk.frame) / rate
            v_ego_t_aligned = np.interp(cur_time - rdr_delay, v_ego_array[1],
                                        v_ego_array[0])
            d_path = np.sqrt(
                np.amin((path_x - rpt[0])**2 + (path_y - rpt[1])**2))

            # create the track if it doesn't exist or it's a new track
            if ids not in tracks or rpt[5] == 1:
                tracks[ids] = Track()
            tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, v_ego_t_aligned)

            # allow the vision model to remove the stationary flag if distance and rel speed roughly match
            if VISION_POINT in ar_pts:
                dist_to_vision = np.sqrt(
                    (0.5 * (ar_pts[VISION_POINT][0] - rpt[0]))**2 +
                    (2 * (ar_pts[VISION_POINT][1] - rpt[1]))**2)
                rel_speed_diff = abs(ar_pts[VISION_POINT][2] - rpt[2])
                tracks[ids].mix_vision(dist_to_vision, rel_speed_diff)

        # publish tracks (debugging)
        dat = messaging.new_message()
        dat.init('liveTracks', len(tracks))
        for cnt, ids in enumerate(tracks.keys()):
            dat.liveTracks[cnt].trackId = ids
            dat.liveTracks[cnt].dRel = float(tracks[ids].dRel)
            dat.liveTracks[cnt].yRel = float(tracks[ids].yRel)
            dat.liveTracks[cnt].vRel = float(tracks[ids].vRel)
            dat.liveTracks[cnt].aRel = float(tracks[ids].aRel)
            dat.liveTracks[cnt].stationary = tracks[ids].stationary
            dat.liveTracks[cnt].oncoming = tracks[ids].oncoming
        liveTracks.send(dat.to_bytes())

        idens = tracks.keys()
        track_pts = np.array(
            [tracks[iden].get_key_for_cluster() for iden in idens])

        # If we have multiple points, cluster them
        if len(track_pts) > 1:
            link = linkage_vector(track_pts, method='centroid')
            cluster_idxs = fcluster(link, 2.5, criterion='distance')
            clusters = [None] * max(cluster_idxs)

            for idx in xrange(len(track_pts)):
                cluster_i = cluster_idxs[idx] - 1

                if clusters[cluster_i] == None:
                    clusters[cluster_i] = Cluster()
                clusters[cluster_i].add(tracks[idens[idx]])
        elif len(track_pts) == 1:
            # TODO: why do we need this?
            clusters = [Cluster()]
            clusters[0].add(tracks[idens[0]])
        else:
            clusters = []

        # *** extract the lead car ***
        lead_clusters = [c for c in clusters if c.is_potential_lead(v_ego)]
        lead_clusters.sort(key=lambda x: x.dRel)
        lead_len = len(lead_clusters)

        # *** extract the second lead from the whole set of leads ***
        lead2_clusters = [
            c for c in lead_clusters if c.is_potential_lead2(lead_clusters)
        ]
        lead2_clusters.sort(key=lambda x: x.dRel)
        lead2_len = len(lead2_clusters)

        # *** publish live20 ***
        dat = messaging.new_message()
        dat.init('live20')
        dat.live20.mdMonoTime = last_md_ts
        dat.live20.canMonoTimes = list(rr.canMonoTimes)
        dat.live20.l100MonoTime = last_l100_ts
        if lead_len > 0:
            lead_clusters[0].toLive20(dat.live20.leadOne)
            if lead2_len > 0:
                lead2_clusters[0].toLive20(dat.live20.leadTwo)
            else:
                dat.live20.leadTwo.status = False
        else:
            dat.live20.leadOne.status = False

        dat.live20.cumLagMs = -rk.remaining * 1000.
        live20.send(dat.to_bytes())

        rk.monitor_time()
Example #7
0
def controlsd_thread(gctx=None, rate=100, default_bias=0.):
  # start the loop
  set_realtime_priority(3)

  context = zmq.Context()

  params = Params()

  # pub
  live100 = messaging.pub_sock(context, service_list['live100'].port)
  carstate = messaging.pub_sock(context, service_list['carState'].port)
  carcontrol = messaging.pub_sock(context, service_list['carControl'].port)
  livempc = messaging.pub_sock(context, service_list['liveMpc'].port)

  passive = params.get("Passive") != "0"
  if not passive:
    sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
  else:
    sendcan = None

  # sub
  poller = zmq.Poller()
  thermal = messaging.sub_sock(context, service_list['thermal'].port, conflate=True, poller=poller)
  health = messaging.sub_sock(context, service_list['health'].port, conflate=True, poller=poller)
  cal = messaging.sub_sock(context, service_list['liveCalibration'].port, conflate=True, poller=poller)

  logcan = messaging.sub_sock(context, service_list['can'].port)

  CC = car.CarControl.new_message()

  CI, CP = get_car(logcan, sendcan, 1.0 if passive else None)

  if CI is None:
    raise Exception("unsupported car")

  # if stock camera is connected, then force passive behavior
  if not CP.enableCamera:
    passive = True
    sendcan = None

  if passive:
    CP.safetyModel = car.CarParams.SafetyModels.honda

  fcw_enabled = params.get("IsFcwEnabled") == "1"

  PL = Planner(CP, fcw_enabled)
  LoC = LongControl(CP, CI.compute_gb)
  VM = VehicleModel(CP)
  LaC = LatControl(VM)
  AM = AlertManager()

  if not passive:
    AM.add("startup", False)

  # write CarParams
  params.put("CarParams", CP.to_bytes())

  state = State.disabled
  soft_disable_timer = 0
  v_cruise_kph = 255
  overtemp = False
  free_space = False
  cal_status = Calibration.UNCALIBRATED
  rear_view_toggle = False
  rear_view_allowed = params.get("IsRearViewMirror") == "1"

  # 0.0 - 1.0
  awareness_status = 1.
  v_cruise_kph_last = 0

  rk = Ratekeeper(rate, print_delay_threshold=2./1000)

  # learned angle offset
  angle_offset = default_bias
  calibration_params = params.get("CalibrationParams")
  if calibration_params:
    try:
      calibration_params = json.loads(calibration_params)
      angle_offset = calibration_params["angle_offset2"]
    except (ValueError, KeyError):
      pass

  prof = Profiler(False)  # off by default

  while 1:

    prof.checkpoint("Ratekeeper", ignore=True)

    # sample data and compute car events
    CS, events, cal_status, overtemp, free_space = data_sample(CI, CC, thermal, cal, health, poller, cal_status,
                                                               overtemp, free_space)
    prof.checkpoint("Sample")

    # define plan
    plan, plan_ts = calc_plan(CS, CP, events, PL, LaC, LoC, v_cruise_kph, awareness_status)
    prof.checkpoint("Plan")

    if not passive:
      # update control state
      state, soft_disable_timer, v_cruise_kph, v_cruise_kph_last = state_transition(CS, CP, state, events, soft_disable_timer,
                                                                                    v_cruise_kph, AM)
      prof.checkpoint("State transition")

    # compute actuators
    actuators, v_cruise_kph, awareness_status, angle_offset, rear_view_toggle = state_control(plan, CS, CP, state, events, v_cruise_kph,
                                                                            v_cruise_kph_last, AM, rk, awareness_status, PL, LaC, LoC, VM,
                                                                            angle_offset, rear_view_allowed, rear_view_toggle, passive)
    prof.checkpoint("State Control")

    # publish data
    CC = data_send(plan, plan_ts, CS, CI, CP, VM, state, events, actuators, v_cruise_kph,
                   rk, carstate, carcontrol, live100, livempc, AM, rear_view_allowed,
                   rear_view_toggle, awareness_status, LaC, LoC, angle_offset, passive)
    prof.checkpoint("Sent")

    # *** run loop at fixed rate ***
    rk.keep_time()

    prof.display()
Example #8
0
class Plant(object):
    messaging_initialized = False

    def __init__(self,
                 lead_relevancy=False,
                 rate=100,
                 speed=0.0,
                 distance_lead=2.0):
        self.rate = rate
        self.brake_only = False

        if not Plant.messaging_initialized:
            context = zmq.Context()
            Plant.logcan = messaging.pub_sock(context,
                                              service_list['can'].port)
            Plant.sendcan = messaging.sub_sock(context,
                                               service_list['sendcan'].port)
            Plant.model = messaging.pub_sock(context,
                                             service_list['model'].port)
            Plant.cal = messaging.pub_sock(
                context, service_list['liveCalibration'].port)
            Plant.live100 = messaging.sub_sock(context,
                                               service_list['live100'].port)
            Plant.plan = messaging.sub_sock(context, service_list['plan'].port)
            Plant.messaging_initialized = True

        self.angle_steer = 0.
        self.gear_choice = 0
        self.speed, self.speed_prev = 0., 0.

        self.esp_disabled = 0
        self.main_on = 1
        self.user_gas = 0
        self.computer_brake, self.user_brake = 0, 0
        self.brake_pressed = 0
        self.angle_steer_rate = 0
        self.distance, self.distance_prev = 0., 0.
        self.speed, self.speed_prev = speed, speed
        self.steer_error, self.brake_error, self.steer_not_allowed = 0, 0, 0
        self.gear_shifter = 8  # D gear
        self.pedal_gas = 0
        self.cruise_setting = 0

        self.seatbelt, self.door_all_closed = True, True
        self.steer_torque, self.v_cruise, self.acc_status = 0, 0, 0  # v_cruise is reported from can, not the one used for controls

        self.lead_relevancy = lead_relevancy

        # lead car
        self.distance_lead, self.distance_lead_prev = distance_lead, distance_lead

        self.rk = Ratekeeper(rate, print_delay_threshold=100)
        self.ts = 1. / rate

        self.cp = get_car_can_parser()

    def close(self):
        Plant.logcan.close()
        Plant.model.close()

    def speed_sensor(self, speed):
        if speed < 0.3:
            return 0
        else:
            return speed * CV.MS_TO_KPH

    def current_time(self):
        return float(self.rk.frame) / self.rate

    def step(self,
             v_lead=0.0,
             cruise_buttons=None,
             grade=0.0,
             publish_model=True):
        gen_dbc, gen_signals, gen_checks = get_can_signals(CP)
        sgs = [s[0] for s in gen_signals]
        msgs = [s[1] for s in gen_signals]
        cks_msgs = set(check[0] for check in gen_checks)
        cks_msgs.add(0x18F)
        cks_msgs.add(0x30C)

        # ******** get messages sent to the car ********
        can_msgs = []
        for a in messaging.drain_sock(Plant.sendcan):
            can_msgs.extend(can_capnp_to_can_list(a.sendcan, [0, 2]))
        self.cp.update_can(can_msgs)

        # ******** get live100 messages for plotting ***
        live100_msgs = []
        for a in messaging.drain_sock(Plant.live100):
            live100_msgs.append(a.live100)

        fcw = None
        for a in messaging.drain_sock(Plant.plan):
            if a.plan.fcw:
                fcw = True

        if self.cp.vl[0x1fa]['COMPUTER_BRAKE_REQUEST']:
            brake = self.cp.vl[0x1fa]['COMPUTER_BRAKE'] * 0.003906248
        else:
            brake = 0.0

        if self.cp.vl[0x200]['GAS_COMMAND'] > 0:
            gas = self.cp.vl[0x200]['GAS_COMMAND'] / 256.0
        else:
            gas = 0.0

        if self.cp.vl[0xe4]['STEER_TORQUE_REQUEST']:
            steer_torque = self.cp.vl[0xe4]['STEER_TORQUE'] * 1.0 / 0xf00
        else:
            steer_torque = 0.0

        distance_lead = self.distance_lead_prev + v_lead * self.ts

        # ******** run the car ********
        speed, acceleration = car_plant(self.distance_prev, self.speed_prev,
                                        grade, gas, brake)
        distance = self.distance_prev + speed * self.ts
        speed = self.speed_prev + self.ts * acceleration
        if speed <= 0:
            speed = 0
            acceleration = 0

        # ******** lateral ********
        self.angle_steer -= (steer_torque / 10.0) * self.ts

        # *** radar model ***
        if self.lead_relevancy:
            d_rel = np.maximum(0., distance_lead - distance)
            v_rel = v_lead - speed
        else:
            d_rel = 200.
            v_rel = 0.
        lateral_pos_rel = 0.

        # print at 5hz
        if (self.rk.frame % (self.rate / 5)) == 0:
            print "%6.2f m  %6.2f m/s  %6.2f m/s2   %.2f ang   gas: %.2f  brake: %.2f  steer: %5.2f     lead_rel: %6.2f m  %6.2f m/s" % (
                distance, speed, acceleration, self.angle_steer, gas, brake,
                steer_torque, d_rel, v_rel)

        # ******** publish the car ********
        vls = [
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.angle_steer,
            self.angle_steer_rate,
            0,
            0,
            0,
            0,
            0,  # Doors
            0,
            0,  # Blinkers
            self.gear_choice,
            speed != 0,
            self.brake_error,
            self.brake_error,
            not self.seatbelt,
            self.seatbelt,  # Seatbelt
            self.brake_pressed,
            0.,
            cruise_buttons,
            self.esp_disabled,
            0,  # HUD lead
            self.user_brake,
            self.steer_error,
            self.gear_shifter,
            self.pedal_gas,
            self.cruise_setting,
            self.acc_status,
            self.v_cruise,
            0,  # Cruise speed offset
            self.user_gas,
            self.main_on,
            0,  # EPB State
            0,  # Brake hold
            0,  # Interceptor feedback
            # 0,
        ]

        # TODO: publish each message at proper frequency
        can_msgs = []
        for msg in set(msgs):
            msg_struct = {}
            indxs = [i for i, x in enumerate(msgs) if msg == msgs[i]]
            for i in indxs:
                msg_struct[sgs[i]] = vls[i]

            if "COUNTER" in honda.get_signals(msg):
                msg_struct["COUNTER"] = self.rk.frame % 4

            msg = honda.lookup_msg_id(msg)
            msg_data = honda.encode(msg, msg_struct)

            if "CHECKSUM" in honda.get_signals(msg):
                msg_data = fix(msg_data, msg)

            can_msgs.append([msg, 0, msg_data, 0])

        # add the radar message
        # TODO: use the DBC
        if self.rk.frame % 5 == 0:
            radar_state_msg = '\x79\x00\x00\x00\x00\x00\x00\x00'
            radar_msg = to_3_byte(d_rel*16.0) + \
                        to_3_byte(int(lateral_pos_rel*16.0)&0x3ff) + \
                        to_3s_byte(int(v_rel*32.0)) + \
                        "0f00000"
            can_msgs.append([0x400, 0, radar_state_msg, 1])
            can_msgs.append([0x445, 0, radar_msg.decode("hex"), 1])
        Plant.logcan.send(can_list_to_can_capnp(can_msgs).to_bytes())

        # ******** publish a fake model going straight and fake calibration ********
        # note that this is worst case for MPC, since model will delay long mpc by one time step
        if publish_model and self.rk.frame % 5 == 0:
            md = messaging.new_message()
            cal = messaging.new_message()
            md.init('model')
            cal.init('liveCalibration')
            md.model.frameId = 0
            for x in [md.model.path, md.model.leftLane, md.model.rightLane]:
                x.points = [0.0] * 50
                x.prob = 1.0
                x.std = 1.0
            md.model.lead.dist = float(d_rel)
            md.model.lead.prob = 1.
            md.model.lead.std = 0.1
            cal.liveCalibration.calStatus = 1
            cal.liveCalibration.calPerc = 100
            # fake values?
            Plant.model.send(md.to_bytes())
            Plant.cal.send(cal.to_bytes())

        # ******** update prevs ********
        self.speed = speed
        self.distance = distance
        self.distance_lead = distance_lead

        self.speed_prev = speed
        self.distance_prev = distance
        self.distance_lead_prev = distance_lead

        self.rk.keep_time()
        return (distance, speed, acceleration, distance_lead, brake, gas,
                steer_torque, fcw, live100_msgs)
Example #9
0
def controlsd_thread(gctx, rate=100):
  # start the loop
  set_realtime_priority(2)

  context = zmq.Context()

  # pub
  live100 = messaging.pub_sock(context, service_list['live100'].port)
  carstate = messaging.pub_sock(context, service_list['carState'].port)
  carcontrol = messaging.pub_sock(context, service_list['carControl'].port)
  sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
  livempc = messaging.pub_sock(context, service_list['liveMpc'].port)

  # sub
  thermal = messaging.sub_sock(context, service_list['thermal'].port)
  health = messaging.sub_sock(context, service_list['health'].port)
  cal = messaging.sub_sock(context, service_list['liveCalibration'].port)
  logcan = messaging.sub_sock(context, service_list['can'].port)

  CC = car.CarControl.new_message()
  CI, CP = get_car(logcan, sendcan)
  PL = Planner(CP)
  LoC = LongControl(CI.compute_gb)
  VM = VehicleModel(CP)
  LaC = LatControl(VM)
  AM = AlertManager()

  # write CarParams
  params = Params()
  params.put("CarParams", CP.to_bytes())

  state = State.DISABLED
  soft_disable_timer = 0
  v_cruise_kph = 255
  cal_perc = 0
  cal_status = Calibration.UNCALIBRATED
  rear_view_toggle = False
  rear_view_allowed = params.get("IsRearViewMirror") == "1"

  # 0.0 - 1.0
  awareness_status = 0.

  rk = Ratekeeper(rate, print_delay_threshold=2./1000)

  # learned angle offset
  angle_offset = 0.
  calibration_params = params.get("CalibrationParams")
  if calibration_params:
    try:
      calibration_params = json.loads(calibration_params)
      angle_offset = calibration_params["angle_offset"]
    except (ValueError, KeyError):
      pass

  prof = Profiler()

  while 1:

    prof.reset()  # avoid memory leak

    # sample data and compute car events
    CS, events, cal_status, cal_perc = data_sample(CI, CC, thermal, health, cal, cal_status, cal_perc)
    prof.checkpoint("Sample")

    # define plan
    plan, plan_ts = calc_plan(CS, events, PL, LoC)
    prof.checkpoint("Plan")

    # update control state
    state, soft_disable_timer, v_cruise_kph = state_transition(CS, CP, state, events, soft_disable_timer, v_cruise_kph, cal_perc, AM)
    prof.checkpoint("State transition")

    # compute actuators
    actuators, v_cruise_kph, awareness_status, angle_offset, rear_view_toggle = state_control(plan, CS, CP, state, events, v_cruise_kph,
                                                                            AM, rk, awareness_status, PL, LaC, LoC, VM, angle_offset,
                                                                            rear_view_allowed, rear_view_toggle)
    prof.checkpoint("State Control")

    # publish data
    CC = data_send(plan, plan_ts, CS, CI, CP, state, events, actuators, v_cruise_kph,
                   rk, carstate, carcontrol, live100, livempc, AM, rear_view_allowed,
                   rear_view_toggle, awareness_status, LaC, LoC, angle_offset)
    prof.checkpoint("Sent")

    # *** run loop at fixed rate ***
    if rk.keep_time():
      prof.display()
Example #10
0
def radard_thread(gctx=None):
  set_realtime_priority(2)

  # wait for stats about the car to come in from controls
  cloudlog.info("radard is waiting for CarParams")
  CP = car.CarParams.from_bytes(Params().get("CarParams", block=True))
  mocked = CP.carName == "mock" or CP.carName == "tesla"
  VM = VehicleModel(CP)
  cloudlog.info("radard got CarParams")

  # import the radar from the fingerprint
  cloudlog.info("radard is importing %s", CP.carName)
  RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface
  context = zmq.Context()

  # *** subscribe to features and model from visiond
  poller = zmq.Poller()
  model = messaging.sub_sock(context, service_list['model'].port, conflate=True, poller=poller)
  live100 = messaging.sub_sock(context, service_list['live100'].port, conflate=True, poller=poller)

  MP = ModelParser()
  RI = RadarInterface(CP)

  last_md_ts = 0
  last_l100_ts = 0

  # *** publish live20 and liveTracks
  live20 = messaging.pub_sock(context, service_list['live20'].port)
  liveTracks = messaging.pub_sock(context, service_list['liveTracks'].port)

  path_x = np.arange(0.0, 140.0, 0.1)    # 140 meters is max

  # Time-alignment
  rate = 20.   # model and radar are both at 20Hz
  tsv = 1./rate
  v_len = 20         # how many speed data points to remember for t alignment with rdr data

  active = 0
  steer_angle = 0.
  steer_override = False

  tracks = defaultdict(dict)

  # Kalman filter stuff:
  ekfv = EKFV1D()
  speedSensorV = SimpleSensor(XV, 1, 2)

  # v_ego
  v_ego = None
  v_ego_array = np.zeros([2, v_len])
  v_ego_t_aligned = 0.

  rk = Ratekeeper(rate, print_delay_threshold=np.inf)
  while 1:
    rr = RI.update()

    ar_pts = {}
    for pt in rr.points:
      ar_pts[pt.trackId] = [pt.dRel + RDR_TO_LDR, pt.yRel, pt.vRel, pt.measured]

    # receive the live100s
    l100 = None
    md = None

    for socket, event in poller.poll(0):
      if socket is live100:
        l100 = messaging.recv_one(socket)
      elif socket is model:
        md = messaging.recv_one(socket)

    if l100 is not None:
      active = l100.live100.active
      v_ego = l100.live100.vEgo
      steer_angle = l100.live100.angleSteers
      steer_override = l100.live100.steerOverride

      v_ego_array = np.append(v_ego_array, [[v_ego], [float(rk.frame)/rate]], 1)
      v_ego_array = v_ego_array[:, 1:]

      last_l100_ts = l100.logMonoTime

    if v_ego is None:
      continue

    if md is not None:
      last_md_ts = md.logMonoTime

    # *** get path prediction from the model ***
    MP.update(v_ego, md)

    # run kalman filter only if prob is high enough
    if MP.lead_prob > 0.7:
      reading = speedSensorV.read(MP.lead_dist, covar=np.matrix(MP.lead_var))
      ekfv.update_scalar(reading)
      ekfv.predict(tsv)

      # When changing lanes the distance to the lead car can suddenly change,
      # which makes the Kalman filter output large relative acceleration
      if mocked and abs(MP.lead_dist - ekfv.state[XV]) > 2.0:
        ekfv.state[XV] = MP.lead_dist
        ekfv.covar = (np.diag([MP.lead_var, ekfv.var_init]))
        ekfv.state[SPEEDV] = 0.

      ar_pts[VISION_POINT] = (float(ekfv.state[XV]), np.polyval(MP.d_poly, float(ekfv.state[XV])),
                              float(ekfv.state[SPEEDV]), False)
    else:
      ekfv.state[XV] = MP.lead_dist
      ekfv.covar = (np.diag([MP.lead_var, ekfv.var_init]))
      ekfv.state[SPEEDV] = 0.

      if VISION_POINT in ar_pts:
        del ar_pts[VISION_POINT]

    # *** compute the likely path_y ***
    if (active and not steer_override) or mocked:
      # use path from model (always when mocking as steering is too noisy)
      path_y = np.polyval(MP.d_poly, path_x)
    else:
      # use path from steer, set angle_offset to 0 it does not only report the physical offset
      path_y = calc_lookahead_offset(v_ego, steer_angle, path_x, VM, angle_offset=0)[0]

    # *** remove missing points from meta data ***
    for ids in tracks.keys():
      if ids not in ar_pts:
        tracks.pop(ids, None)

    # *** compute the tracks ***
    for ids in ar_pts:
      # ignore standalone vision point, unless we are mocking the radar
      if ids == VISION_POINT and not mocked:
        continue
      rpt = ar_pts[ids]

      # align v_ego by a fixed time to align it with the radar measurement
      cur_time = float(rk.frame)/rate
      v_ego_t_aligned = np.interp(cur_time - RI.delay, v_ego_array[1], v_ego_array[0])
      d_path = np.sqrt(np.amin((path_x - rpt[0]) ** 2 + (path_y - rpt[1]) ** 2))
      # add sign
      d_path *= np.sign(rpt[1] - np.interp(rpt[0], path_x, path_y))

      # create the track if it doesn't exist or it's a new track
      if ids not in tracks:
        tracks[ids] = Track()
      tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, v_ego_t_aligned, rpt[3], steer_override)

    # allow the vision model to remove the stationary flag if distance and rel speed roughly match
    if VISION_POINT in ar_pts:
      fused_id = None
      best_score = NO_FUSION_SCORE
      for ids in tracks:
        dist_to_vision = np.sqrt((0.5*(ar_pts[VISION_POINT][0] - tracks[ids].dRel)) ** 2 + (2*(ar_pts[VISION_POINT][1] - tracks[ids].yRel)) ** 2)
        rel_speed_diff = abs(ar_pts[VISION_POINT][2] - tracks[ids].vRel)
        tracks[ids].update_vision_score(dist_to_vision, rel_speed_diff)
        if best_score > tracks[ids].vision_score:
          fused_id = ids
          best_score = tracks[ids].vision_score

      if fused_id is not None:
        tracks[fused_id].vision_cnt += 1
        tracks[fused_id].update_vision_fusion()

    if DEBUG:
      print("NEW CYCLE")
      if VISION_POINT in ar_pts:
        print("vision", ar_pts[VISION_POINT])

    idens = tracks.keys()
    track_pts = np.array([tracks[iden].get_key_for_cluster() for iden in idens])

    # If we have multiple points, cluster them
    if len(track_pts) > 1:
      link = linkage_vector(track_pts, method='centroid')
      cluster_idxs = fcluster(link, 2.5, criterion='distance')
      clusters = [None]*max(cluster_idxs)

      for idx in xrange(len(track_pts)):
        cluster_i = cluster_idxs[idx]-1

        if clusters[cluster_i] == None:
          clusters[cluster_i] = Cluster()
        clusters[cluster_i].add(tracks[idens[idx]])
    elif len(track_pts) == 1:
      # TODO: why do we need this?
      clusters = [Cluster()]
      clusters[0].add(tracks[idens[0]])
    else:
      clusters = []

    if DEBUG:
      for i in clusters:
        print(i)
    # *** extract the lead car ***
    lead_clusters = [c for c in clusters
                     if c.is_potential_lead(v_ego)]
    lead_clusters.sort(key=lambda x: x.dRel)
    lead_len = len(lead_clusters)

    # *** extract the second lead from the whole set of leads ***
    lead2_clusters = [c for c in lead_clusters
                      if c.is_potential_lead2(lead_clusters)]
    lead2_clusters.sort(key=lambda x: x.dRel)
    lead2_len = len(lead2_clusters)

    # *** publish live20 ***
    dat = messaging.new_message()
    dat.init('live20')
    dat.live20.mdMonoTime = last_md_ts
    dat.live20.canMonoTimes = list(rr.canMonoTimes)
    dat.live20.radarErrors = list(rr.errors)
    dat.live20.l100MonoTime = last_l100_ts
    if lead_len > 0:
      dat.live20.leadOne = lead_clusters[0].toLive20()
      if lead2_len > 0:
        dat.live20.leadTwo = lead2_clusters[0].toLive20()
      else:
        dat.live20.leadTwo.status = False
    else:
      dat.live20.leadOne.status = False

    dat.live20.cumLagMs = -rk.remaining*1000.
    live20.send(dat.to_bytes())

    # *** publish tracks for UI debugging (keep last) ***
    dat = messaging.new_message()
    dat.init('liveTracks', len(tracks))

    for cnt, ids in enumerate(tracks.keys()):
      if DEBUG:
        print("id: %4.0f x:  %4.1f  y: %4.1f  vr: %4.1f d: %4.1f  va: %4.1f  vl: %4.1f  vlk: %4.1f alk: %4.1f  s: %1.0f  v: %1.0f" % \
          (ids, tracks[ids].dRel, tracks[ids].yRel, tracks[ids].vRel,
           tracks[ids].dPath, tracks[ids].vLat,
           tracks[ids].vLead, tracks[ids].vLeadK,
           tracks[ids].aLeadK,
           tracks[ids].stationary,
           tracks[ids].measured))
      dat.liveTracks[cnt] = {
        "trackId": ids,
        "dRel": float(tracks[ids].dRel),
        "yRel": float(tracks[ids].yRel),
        "vRel": float(tracks[ids].vRel),
        "aRel": float(tracks[ids].aRel),
        "stationary": bool(tracks[ids].stationary),
        "oncoming": bool(tracks[ids].oncoming),
      }
    liveTracks.send(dat.to_bytes())

    rk.monitor_time()
Example #11
0
def controlsd_thread(gctx=None, rate=100, default_bias=0.):
  gc.disable()

  # start the loop
  set_realtime_priority(3)

  context = zmq.Context()
  params = Params()

  # pub
  live100 = messaging.pub_sock(context, service_list['live100'].port)
  carstate = messaging.pub_sock(context, service_list['carState'].port)
  carcontrol = messaging.pub_sock(context, service_list['carControl'].port)
  livempc = messaging.pub_sock(context, service_list['liveMpc'].port)

  passive = params.get("Passive") != "0"
  if not passive:
    while 1:
      try:
        sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
        break
      except zmq.error.ZMQError:
        kill_defaultd()
  else:
    sendcan = None

  # sub
  poller = zmq.Poller()
  thermal = messaging.sub_sock(context, service_list['thermal'].port, conflate=True, poller=poller)
  health = messaging.sub_sock(context, service_list['health'].port, conflate=True, poller=poller)
  cal = messaging.sub_sock(context, service_list['liveCalibration'].port, conflate=True, poller=poller)
  driver_monitor = messaging.sub_sock(context, service_list['driverMonitoring'].port, conflate=True, poller=poller)
  gps_location = messaging.sub_sock(context, service_list['gpsLocationExternal'].port, conflate=True, poller=poller)

  logcan = messaging.sub_sock(context, service_list['can'].port)

  CC = car.CarControl.new_message()

  CI, CP = get_car(logcan, sendcan, 1.0 if passive else None)

  if CI is None:
    raise Exception("unsupported car")

  # if stock camera is connected, then force passive behavior
  if not CP.enableCamera:
    passive = True
    sendcan = None

  if passive:
    CP.safetyModel = car.CarParams.SafetyModels.noOutput

  fcw_enabled = params.get("IsFcwEnabled") == "1"
  geofence = None
  try:
    from selfdrive.controls.lib.geofence import Geofence
    geofence = Geofence(params.get("IsGeofenceEnabled") == "1")
  except ImportError:
    # geofence not available
    params.put("IsGeofenceEnabled", "-1")

  PL = Planner(CP, fcw_enabled)
  LoC = LongControl(CP, CI.compute_gb)
  VM = VehicleModel(CP)
  LaC = LatControl(VM)
  AM = AlertManager()
  driver_status = DriverStatus()

  if not passive:
    AM.add("startup", False)

  # write CarParams
  params.put("CarParams", CP.to_bytes())

  state = State.disabled
  soft_disable_timer = 0
  v_cruise_kph = 255
  v_cruise_kph_last = 0
  overtemp = False
  free_space = False
  cal_status = Calibration.UNCALIBRATED
  mismatch_counter = 0

  rk = Ratekeeper(rate, print_delay_threshold=2./1000)

  # learned angle offset
  angle_offset = default_bias
  calibration_params = params.get("CalibrationParams")
  if calibration_params:
    try:
      calibration_params = json.loads(calibration_params)
      angle_offset = calibration_params["angle_offset2"]
    except (ValueError, KeyError):
      pass

  prof = Profiler(False)  # off by default

  while 1:

    prof.checkpoint("Ratekeeper", ignore=True)

    # sample data and compute car events
    CS, events, cal_status, overtemp, free_space, mismatch_counter = data_sample(CI, CC, thermal, cal, health,
      driver_monitor, gps_location, poller, cal_status, overtemp, free_space, driver_status, geofence, state, mismatch_counter, params)
    prof.checkpoint("Sample")

    # define plan
    plan, plan_ts = calc_plan(CS, CP, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence)
    prof.checkpoint("Plan")

    if not passive:
      # update control state
      state, soft_disable_timer, v_cruise_kph, v_cruise_kph_last = \
        state_transition(CS, CP, state, events, soft_disable_timer, v_cruise_kph, AM)
      prof.checkpoint("State transition")

    # compute actuators
    actuators, v_cruise_kph, driver_status, angle_offset = state_control(plan, CS, CP, state, events, v_cruise_kph, 
      v_cruise_kph_last, AM, rk, driver_status, PL, LaC, LoC, VM, angle_offset, passive)
    prof.checkpoint("State Control")

    # publish data
    CC = data_send(PL.perception_state, plan, plan_ts, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, carstate, carcontrol,
      live100, livempc, AM, driver_status, LaC, LoC, angle_offset, passive)
    prof.checkpoint("Sent")

    # *** run loop at fixed rate ***
    rk.keep_time()

    prof.display()
Example #12
0
    def __init__(self, sm=None, pm=None, can_sock=None):
        gc.disable()
        set_realtime_priority(3)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState', \
                                           'carControl', 'carEvents', 'carParams'])

        self.sm = sm
        if self.sm is None:
            self.sm = messaging.SubMaster(['thermal', 'health', 'model', 'liveCalibration', \
                                           'dMonitoringState', 'plan', 'pathPlan', 'liveLocationKalman'])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        # wait for one health and one CAN packet
        hw_type = messaging.recv_one(self.sm.sock['health']).health.hwType
        has_relay = hw_type in [HwType.blackPanda, HwType.uno]
        print("Waiting for CAN messages...")
        messaging.get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'],
                                   has_relay)

        # read params
        params = Params()
        self.is_metric = params.get("IsMetric", encoding='utf8') == "1"
        self.is_ldw_enabled = params.get("IsLdwEnabled",
                                         encoding='utf8') == "1"
        internet_needed = params.get("Offroad_ConnectivityNeeded",
                                     encoding='utf8') is not None
        community_feature_toggle = params.get("CommunityFeaturesToggle",
                                              encoding='utf8') == "1"
        openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                              encoding='utf8') == "1"
        passive = params.get("Passive", encoding='utf8') == "1" or \
                  internet_needed or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = not os.path.isfile('/EON') or (os.path.isfile('/proc/asound/card0/state') \
                                and open('/proc/asound/card0/state').read().strip() == 'ONLINE')

        car_recognized = self.CP.carName != 'mock'
        # If stock camera is disconnected, we loaded car controls and it's not dashcam mode
        controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive
        community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard and boardd safety mode
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)
        put_nonblocking("LongitudinalControl",
                        "1" if self.CP.openpilotLongitudinalControl else "0")

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb)
        self.VM = VehicleModel(self.CP)

        if self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.consecutive_can_error_count = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.events_prev = []
        self.current_alert_types = []

        self.sm['liveCalibration'].calStatus = Calibration.INVALID
        self.sm['thermal'].freeSpace = 1.
        self.sm['dMonitoringState'].events = []
        self.sm['dMonitoringState'].awarenessStatus = 1.
        self.sm['dMonitoringState'].faceDetected = False

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available, hw_type)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if internet_needed:
            self.events.add(EventName.internetConnectivityNeeded, static=True)
        if community_feature_disallowed:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if self.read_only and not passive:
            self.events.add(EventName.carUnrecognized, static=True)
        # if hw_type == HwType.whitePanda:
        # self.events.add(EventName.whitePandaUnsupported, static=True)

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default
Example #13
0
class Controls:
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(4 if TICI else 3, Priority.CTRL_HIGH)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.camera_packets = ["roadCameraState", "driverCameraState"]
        if TICI:
            self.camera_packets.append("wideRoadCameraState")

        params = Params()
        self.joystick_mode = params.get_bool("JoystickDebugMode")
        joystick_packet = ['testJoystick'] if self.joystick_mode else []

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster(
                [
                    'deviceState', 'pandaState', 'modelV2', 'liveCalibration',
                    'driverMonitoringState', 'longitudinalPlan', 'lateralPlan',
                    'liveLocationKalman', 'managerState', 'liveParameters',
                    'radarState'
                ] + self.camera_packets + joystick_packet,
                ignore_alive=ignore,
                ignore_avg_freq=['radarState', 'longitudinalPlan'])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        if TICI:
            self.log_sock = messaging.sub_sock('androidLog')

        # wait for one pandaState and one CAN packet
        panda_type = messaging.recv_one(
            self.sm.sock['pandaState']).pandaState.pandaType
        has_relay = panda_type in [
            PandaType.blackPanda, PandaType.uno, PandaType.dos
        ]
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'],
                                   has_relay)

        # read params
        self.is_metric = params.get_bool("IsMetric")
        self.is_ldw_enabled = params.get_bool("IsLdwEnabled")
        community_feature_toggle = params.get_bool("CommunityFeaturesToggle")
        openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
        passive = params.get_bool("Passive") or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'

        controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature = self.CP.communityFeature or \
                            self.CP.fingerprintSource == car.CarParams.FingerprintSource.can
        community_feature_disallowed = community_feature and (
            not community_feature_toggle)
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb)
        self.VM = VehicleModel(self.CP)

        if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            self.LaC = LatControlAngle(self.CP)
        elif self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.initialized = False
        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.curve_speed_ms = 255.
        self.v_cruise_kph_limit = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = False
        self.v_target = 0.0
        self.a_target = 0.0

        # TODO: no longer necessary, aside from process replay
        self.sm['liveParameters'].valid = True

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available,
                                               self.CP.fuzzyFingerprint,
                                               len(self.CP.carFw) > 0)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if community_feature_disallowed and car_recognized and not self.CP.dashcamOnly:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)
        elif self.read_only:
            self.events.add(EventName.dashcamMode, static=True)
        elif self.joystick_mode:
            self.events.add(EventName.joystickDebug, static=True)
            self.startup_event = None

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default

    def update_events(self, CS):
        """Compute carEvents from carState"""

        self.events.clear()
        self.events.add_from_msg(CS.events)
        self.events.add_from_msg(self.sm['driverMonitoringState'].events)

        # Handle startup event
        if self.startup_event is not None:
            self.events.add(self.startup_event)
            self.startup_event = None

        # Don't add any more events if not initialized
        if not self.initialized:
            self.events.add(EventName.controlsInitializing)
            return

        # Create events for battery, temperature, disk space, and memory
        if self.sm['deviceState'].batteryPercent < 1 and self.sm[
                'deviceState'].chargingError:
            # at zero percent battery, while discharging, OP should not allowed
            self.events.add(EventName.lowBattery)
        if self.sm['deviceState'].thermalStatus >= ThermalStatus.red:
            self.events.add(EventName.overheat)
        if self.sm['deviceState'].freeSpacePercent < 7 and not SIMULATION:
            # under 7% of space free no enable allowed
            self.events.add(EventName.outOfSpace)
        # TODO: make tici threshold the same
        if self.sm['deviceState'].memoryUsagePercent > (90 if TICI else
                                                        65) and not SIMULATION:
            self.events.add(EventName.lowMemory)

        # Alert if fan isn't spinning for 5 seconds
        if self.sm['pandaState'].pandaType in [PandaType.uno, PandaType.dos]:
            if self.sm['pandaState'].fanSpeedRpm == 0 and self.sm[
                    'deviceState'].fanSpeedPercentDesired > 50:
                if (self.sm.frame -
                        self.last_functional_fan_frame) * DT_CTRL > 5.0:
                    self.events.add(EventName.fanMalfunction)
            else:
                self.last_functional_fan_frame = self.sm.frame

        # Handle calibration status
        cal_status = self.sm['liveCalibration'].calStatus
        if cal_status != Calibration.CALIBRATED:
            if cal_status == Calibration.UNCALIBRATED:
                self.events.add(EventName.calibrationIncomplete)
            else:
                self.events.add(EventName.calibrationInvalid)

        # Handle lane change
        if self.sm[
                'lateralPlan'].laneChangeState == LaneChangeState.preLaneChange:
            direction = self.sm['lateralPlan'].laneChangeDirection
            if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
               (CS.rightBlindspot and direction == LaneChangeDirection.right):
                self.events.add(EventName.laneChangeBlocked)
            else:
                if direction == LaneChangeDirection.left:
                    self.events.add(EventName.preLaneChangeLeft)
                else:
                    self.events.add(EventName.preLaneChangeRight)
        elif self.sm['lateralPlan'].laneChangeState in [
                LaneChangeState.laneChangeStarting,
                LaneChangeState.laneChangeFinishing
        ]:
            self.events.add(EventName.laneChange)

        if self.can_rcv_error or not CS.canValid:
            self.events.add(EventName.canError)

        safety_mismatch = self.sm[
            'pandaState'].safetyModel != self.CP.safetyModel or self.sm[
                'pandaState'].safetyParam != self.CP.safetyParam
        if safety_mismatch or self.mismatch_counter >= 200:
            self.events.add(EventName.controlsMismatch)

        if not self.sm['liveParameters'].valid:
            self.events.add(EventName.vehicleModelInvalid)

        if len(self.sm['radarState'].radarErrors):
            self.events.add(EventName.radarFault)
        elif not self.sm.valid["pandaState"]:
            self.events.add(EventName.usbError)
        elif not self.sm.all_alive_and_valid():
            self.events.add(EventName.commIssue)
            if not self.logged_comm_issue:
                invalid = [
                    s for s, valid in self.sm.valid.items() if not valid
                ]
                not_alive = [
                    s for s, alive in self.sm.alive.items() if not alive
                ]
                cloudlog.event("commIssue",
                               invalid=invalid,
                               not_alive=not_alive)
                self.logged_comm_issue = True
        else:
            self.logged_comm_issue = False

        if not self.sm['lateralPlan'].mpcSolutionValid:
            self.events.add(EventName.plannerError)
        if not self.sm['liveLocationKalman'].sensorsOK and not NOSENSOR:
            if self.sm.frame > 5 / DT_CTRL:  # Give locationd some time to receive all the inputs
                self.events.add(EventName.sensorDataInvalid)
        if not self.sm['liveLocationKalman'].posenetOK:
            self.events.add(EventName.posenetInvalid)
        if not self.sm['liveLocationKalman'].deviceStable:
            self.events.add(EventName.deviceFalling)
        if log.PandaState.FaultType.relayMalfunction in self.sm[
                'pandaState'].faults:
            self.events.add(EventName.relayMalfunction)
        if self.sm['longitudinalPlan'].fcw or (
                self.enabled and self.sm['modelV2'].meta.hardBrakePredicted):
            self.events.add(EventName.fcw)

        if TICI:
            logs = messaging.drain_sock(self.log_sock, wait_for_one=False)
            messages = []
            for m in logs:
                try:
                    messages.append(m.androidLog.message)
                except UnicodeDecodeError:
                    pass

            for err in [
                    "ERROR_CRC", "ERROR_ECC", "ERROR_STREAM_UNDERFLOW",
                    "APPLY FAILED"
            ]:
                for m in messages:
                    if err not in m:
                        continue

                    csid = m.split("CSID:")[-1].split(" ")[0]
                    evt = {
                        "0": EventName.roadCameraError,
                        "1": EventName.wideRoadCameraError,
                        "2": EventName.driverCameraError
                    }.get(csid, None)
                    if evt is not None:
                        self.events.add(evt)

        # TODO: fix simulator
        if not SIMULATION:
            #if not NOSENSOR:
            #if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled > 1000):
            # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes
            #self.events.add(EventName.noGps)
            if not self.sm.all_alive(self.camera_packets):
                self.events.add(EventName.cameraMalfunction)
            if self.sm['modelV2'].frameDropPerc > 20:
                self.events.add(EventName.modeldLagging)
            if self.sm['liveLocationKalman'].excessiveResets:
                self.events.add(EventName.localizerMalfunction)

            # Check if all manager processes are running
            not_running = set(p.name for p in self.sm['managerState'].processes
                              if not p.running)
            if self.sm.rcv_frame['managerState'] and (not_running -
                                                      IGNORE_PROCESSES):
                self.events.add(EventName.processNotRunning)

        # Only allow engagement with brake pressed when stopped behind another stopped car
        speeds = self.sm['longitudinalPlan'].speeds
        if len(speeds) > 1:
            v_future = speeds[-1]
        else:
            v_future = 100.0
        #if CS.brakePressed and v_future >= STARTING_TARGET_SPEED \
        #and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3:
        #self.events.add(EventName.noTarget)

    def data_sample(self):
        """Receive data from sockets and update carState"""

        # Update carState from CAN
        can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
        CS = self.CI.update(self.CC, can_strs)

        self.sm.update(0)

        all_valid = CS.canValid and self.sm.all_alive_and_valid()
        if not self.initialized and (all_valid
                                     or self.sm.frame * DT_CTRL > 2.0):
            self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
            self.initialized = True
            Params().put_bool("ControlsReady", True)

        # Check for CAN timeout
        if not can_strs:
            self.can_error_counter += 1
            self.can_rcv_error = True
        else:
            self.can_rcv_error = False

        # When the panda and controlsd do not agree on controls_allowed
        # we want to disengage openpilot. However the status from the panda goes through
        # another socket other than the CAN messages and one can arrive earlier than the other.
        # Therefore we allow a mismatch for two samples, then we trigger the disengagement.
        if not self.enabled:
            self.mismatch_counter = 0

        if not self.sm['pandaState'].controlsAllowed and self.enabled:
            self.mismatch_counter += 1

        self.distance_traveled += CS.vEgo * DT_CTRL

        return CS

    def cal_curve_speed(self, sm, v_ego, frame):

        if frame % 10 == 0:
            md = sm['modelV2']
            if md is not None and len(
                    md.position.x) == TRAJECTORY_SIZE and len(
                        md.position.y) == TRAJECTORY_SIZE:
                x = md.position.x
                y = md.position.y
                dy = np.gradient(y, x)
                d2y = np.gradient(dy, x)
                curv = d2y / (1 + dy**2)**1.5
                curv = curv[5:TRAJECTORY_SIZE - 10]
                a_y_max = 2.975 - v_ego * 0.0375  # ~1.85 @ 75mph, ~2.6 @ 25mph
                v_curvature = np.sqrt(a_y_max /
                                      np.clip(np.abs(curv), 1e-4, None))
                model_speed = np.mean(v_curvature) * 0.95

                if model_speed < v_ego:
                    self.curve_speed_ms = float(
                        max(model_speed, 32. * CV.KPH_TO_MS))
                else:
                    self.curve_speed_ms = 255.

                if np.isnan(self.curve_speed_ms):
                    self.curve_speed_ms = 255.
            else:
                self.curve_speed_ms = 255.

        return self.curve_speed_ms

    def state_transition(self, CS):
        """Compute conditional state transitions and execute actions on state transitions"""

        self.v_cruise_kph_last = self.v_cruise_kph

        # if stock cruise is completely disabled, then we can use our own set speed logic
        if not self.CP.pcmCruise:
            self.v_cruise_kph = update_v_cruise(self.v_cruise_kph,
                                                CS.buttonEvents, self.enabled)
        elif self.CP.pcmCruise and CS.cruiseState.enabled:
            self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH

        # decrease the soft disable timer at every step, as it's reset on
        # entrance in SOFT_DISABLING state
        self.soft_disable_timer = max(0, self.soft_disable_timer - 1)

        self.current_alert_types = [ET.PERMANENT]

        # ENABLED, PRE ENABLING, SOFT DISABLING
        if self.state != State.disabled:
            # user and immediate disable always have priority in a non-disabled state
            if self.events.any(ET.USER_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.USER_DISABLE)

            elif self.events.any(ET.IMMEDIATE_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.IMMEDIATE_DISABLE)

            else:
                # ENABLED
                if self.state == State.enabled:
                    if self.events.any(ET.SOFT_DISABLE):
                        self.state = State.softDisabling
                        self.soft_disable_timer = 300  # 3s
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                # SOFT DISABLING
                elif self.state == State.softDisabling:
                    if not self.events.any(ET.SOFT_DISABLE):
                        # no more soft disabling condition, so go back to ENABLED
                        self.state = State.enabled

                    elif self.events.any(
                            ET.SOFT_DISABLE) and self.soft_disable_timer > 0:
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                    elif self.soft_disable_timer <= 0:
                        self.state = State.disabled

                # PRE ENABLING
                elif self.state == State.preEnabled:
                    if not self.events.any(ET.PRE_ENABLE):
                        self.state = State.enabled
                    else:
                        self.current_alert_types.append(ET.PRE_ENABLE)

        # DISABLED
        elif self.state == State.disabled:
            if self.events.any(ET.ENABLE):
                if self.events.any(ET.NO_ENTRY):
                    self.current_alert_types.append(ET.NO_ENTRY)

                else:
                    if self.events.any(ET.PRE_ENABLE):
                        self.state = State.preEnabled
                    else:
                        self.state = State.enabled
                    self.current_alert_types.append(ET.ENABLE)
                    self.v_cruise_kph = initialize_v_cruise(
                        CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)

        # Check if actuators are enabled
        self.active = self.state == State.enabled or self.state == State.softDisabling
        if self.active:
            self.current_alert_types.append(ET.WARNING)

        # Check if openpilot is engaged
        self.enabled = self.active or self.state == State.preEnabled

    def state_control(self, CS):
        """Given the state, this function returns an actuators packet"""

        # Update VehicleModel
        params = self.sm['liveParameters']
        x = max(params.stiffnessFactor, 0.1)
        sr = max(params.steerRatio, 0.1)
        self.VM.update_params(x, sr)

        lat_plan = self.sm['lateralPlan']
        long_plan = self.sm['longitudinalPlan']

        actuators = car.CarControl.Actuators.new_message()

        if CS.leftBlinker or CS.rightBlinker:
            self.last_blinker_frame = self.sm.frame

        # State specific actions

        if not self.active:
            self.LaC.reset()
            self.LoC.reset(v_pid=CS.vEgo)

        if not self.joystick_mode:
            # Gas/Brake PID loop
            actuators.gas, actuators.brake, self.v_target, self.a_target = self.LoC.update(
                self.active, CS, self.CP, long_plan)

            # Steering PID loop and lateral MPC
            desired_curvature, desired_curvature_rate = get_lag_adjusted_curvature(
                self.CP, CS.vEgo, lat_plan.psis, lat_plan.curvatures,
                lat_plan.curvatureRates)
            actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(
                self.active, CS, self.CP, self.VM, params, desired_curvature,
                desired_curvature_rate)
        else:
            lac_log = log.ControlsState.LateralDebugState.new_message()
            if self.sm.rcv_frame['testJoystick'] > 0 and self.active:
                gb = clip(self.sm['testJoystick'].axes[0], -1, 1)
                actuators.gas, actuators.brake = max(gb, 0), max(-gb, 0)

                steer = clip(self.sm['testJoystick'].axes[1], -1, 1)
                # max angle is 45 for angle-based cars
                actuators.steer, actuators.steeringAngleDeg = steer, steer * 45.

                lac_log.active = True
                lac_log.steeringAngleDeg = CS.steeringAngleDeg
                lac_log.output = steer
                lac_log.saturated = abs(steer) >= 0.9

        # Check for difference between desired angle and angle for angle based control
        angle_control_saturated = self.CP.steerControlType == car.CarParams.SteerControlType.angle and \
          abs(actuators.steeringAngleDeg - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD

        if angle_control_saturated and not CS.steeringPressed and self.active:
            self.saturated_count += 1
        else:
            self.saturated_count = 0

        # Send a "steering required alert" if saturation count has reached the limit
        if (lac_log.saturated and not CS.steeringPressed) or \
           (self.saturated_count > STEER_ANGLE_SATURATION_TIMEOUT):

            if len(lat_plan.dPathPoints):
                # Check if we deviated from the path
                left_deviation = actuators.steer > 0 and lat_plan.dPathPoints[
                    0] < -0.115
                right_deviation = actuators.steer < 0 and lat_plan.dPathPoints[
                    0] > 0.115

                if left_deviation or right_deviation:
                    self.events.add(EventName.steerSaturated)

        return actuators, lac_log

    def publish_logs(self, CS, start_time, actuators, lac_log):
        """Send actuators and hud commands to the car, send controlsstate and MPC logging"""

        CC = car.CarControl.new_message()
        CC.enabled = self.enabled
        CC.actuators = actuators

        CC.cruiseControl.override = True
        CC.cruiseControl.cancel = not self.CP.pcmCruise or (
            not self.enabled and CS.cruiseState.enabled)
        if self.joystick_mode and self.sm.rcv_frame[
                'testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
            CC.cruiseControl.cancel = True

        # TODO remove car specific stuff in controls
        # Some override values for Honda
        # brake discount removes a sharp nonlinearity
        brake_discount = (1.0 - clip(actuators.brake * 3., 0.0, 1.0))
        speed_override = max(0.0,
                             (self.LoC.v_pid + CS.cruiseState.speedOffset) *
                             brake_discount)
        CC.cruiseControl.speedOverride = float(
            speed_override if self.CP.pcmCruise else 0.0)
        CC.cruiseControl.accelOverride = float(
            self.CI.calc_accel_override(CS.aEgo, self.a_target, CS.vEgo,
                                        self.v_target))

        CC.hudControl.setSpeed = float(self.v_cruise_kph_limit * CV.KPH_TO_MS)
        CC.hudControl.speedVisible = self.enabled
        CC.hudControl.lanesVisible = self.enabled
        CC.hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead

        right_lane_visible = self.sm['lateralPlan'].rProb > 0.5
        left_lane_visible = self.sm['lateralPlan'].lProb > 0.5
        CC.hudControl.rightLaneVisible = bool(right_lane_visible)
        CC.hudControl.leftLaneVisible = bool(left_lane_visible)

        recent_blinker = (self.sm.frame - self.last_blinker_frame
                          ) * DT_CTRL < 5.0  # 5s blinker cooldown
        ldw_allowed = self.is_ldw_enabled and CS.vEgo > LDW_MIN_SPEED and not recent_blinker \
                        and not self.active and self.sm['liveCalibration'].calStatus == Calibration.CALIBRATED

        meta = self.sm['modelV2'].meta
        if len(meta.desirePrediction) and ldw_allowed:
            l_lane_change_prob = meta.desirePrediction[Desire.laneChangeLeft -
                                                       1]
            r_lane_change_prob = meta.desirePrediction[Desire.laneChangeRight -
                                                       1]
            l_lane_close = left_lane_visible and (
                self.sm['modelV2'].laneLines[1].y[0] > -(1.08 + CAMERA_OFFSET))
            r_lane_close = right_lane_visible and (
                self.sm['modelV2'].laneLines[2].y[0] < (1.08 - CAMERA_OFFSET))

            CC.hudControl.leftLaneDepart = bool(
                l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
            CC.hudControl.rightLaneDepart = bool(
                r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)

        if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
            self.events.add(EventName.ldw)

        clear_event = ET.WARNING if ET.WARNING not in self.current_alert_types else None
        alerts = self.events.create_alerts(self.current_alert_types,
                                           [self.CP, self.sm, self.is_metric])
        self.AM.add_many(self.sm.frame, alerts, self.enabled)
        self.AM.process_alerts(self.sm.frame, clear_event)
        CC.hudControl.visualAlert = self.AM.visual_alert

        if not self.read_only and self.initialized:
            # send car controls over can
            can_sends = self.CI.apply(CC)
            self.pm.send(
                'sendcan',
                can_list_to_can_capnp(can_sends,
                                      msgtype='sendcan',
                                      valid=CS.canValid))

        force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \
                      (self.state == State.softDisabling)

        # Curvature & Steering angle
        params = self.sm['liveParameters']
        steer_angle_without_offset = math.radians(CS.steeringAngleDeg -
                                                  params.angleOffsetAverageDeg)
        curvature = -self.VM.calc_curvature(steer_angle_without_offset,
                                            CS.vEgo)

        # controlsState
        dat = messaging.new_message('controlsState')
        dat.valid = CS.canValid
        controlsState = dat.controlsState
        controlsState.alertText1 = self.AM.alert_text_1
        controlsState.alertText2 = self.AM.alert_text_2
        controlsState.alertSize = self.AM.alert_size
        controlsState.alertStatus = self.AM.alert_status
        controlsState.alertBlinkingRate = self.AM.alert_rate
        controlsState.alertType = self.AM.alert_type
        controlsState.alertSound = self.AM.audible_alert
        controlsState.canMonoTimes = list(CS.canMonoTimes)
        controlsState.longitudinalPlanMonoTime = self.sm.logMonoTime[
            'longitudinalPlan']
        controlsState.lateralPlanMonoTime = self.sm.logMonoTime['lateralPlan']
        controlsState.enabled = self.enabled
        controlsState.active = self.active
        controlsState.curvature = curvature
        controlsState.state = self.state
        controlsState.engageable = not self.events.any(ET.NO_ENTRY)
        controlsState.longControlState = self.LoC.long_control_state
        controlsState.vPid = float(self.LoC.v_pid)
        controlsState.vCruise = float(self.v_cruise_kph_limit)
        controlsState.upAccelCmd = float(self.LoC.pid.p)
        controlsState.uiAccelCmd = float(self.LoC.pid.i)
        controlsState.ufAccelCmd = float(self.LoC.pid.f)
        controlsState.cumLagMs = -self.rk.remaining * 1000.
        controlsState.startMonoTime = int(start_time * 1e9)
        controlsState.forceDecel = bool(force_decel)
        controlsState.canErrorCounter = self.can_error_counter

        if self.joystick_mode:
            controlsState.lateralControlState.debugState = lac_log
        elif self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            controlsState.lateralControlState.angleState = lac_log
        elif self.CP.lateralTuning.which() == 'pid':
            controlsState.lateralControlState.pidState = lac_log
        elif self.CP.lateralTuning.which() == 'lqr':
            controlsState.lateralControlState.lqrState = lac_log
        elif self.CP.lateralTuning.which() == 'indi':
            controlsState.lateralControlState.indiState = lac_log
        self.pm.send('controlsState', dat)

        # carState
        car_events = self.events.to_msg()
        cs_send = messaging.new_message('carState')
        cs_send.valid = CS.canValid
        cs_send.carState = CS
        cs_send.carState.events = car_events
        self.pm.send('carState', cs_send)

        # carEvents - logged every second or on change
        if (self.sm.frame % int(1. / DT_CTRL)
                == 0) or (self.events.names != self.events_prev):
            ce_send = messaging.new_message('carEvents', len(self.events))
            ce_send.carEvents = car_events
            self.pm.send('carEvents', ce_send)
        self.events_prev = self.events.names.copy()

        # 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.carParams = self.CP
            self.pm.send('carParams', cp_send)

        # carControl
        cc_send = messaging.new_message('carControl')
        cc_send.valid = CS.canValid
        cc_send.carControl = CC
        self.pm.send('carControl', cc_send)

        # copy CarControl to pass to CarInterface on the next iteration
        self.CC = CC

    def step(self):
        start_time = sec_since_boot()
        self.prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data from sockets and get a carState
        CS = self.data_sample()
        self.prof.checkpoint("Sample")

        self.update_events(CS)

        if not self.read_only and self.initialized:
            # Update control state
            self.state_transition(CS)
            self.prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, lac_log = self.state_control(CS)

        self.prof.checkpoint("State Control")

        # Publish data
        self.publish_logs(CS, start_time, actuators, lac_log)
        self.prof.checkpoint("Sent")

    def controlsd_thread(self):
        while True:
            self.step()
            self.rk.monitor_time()
            self.prof.display()
Example #14
0
class Controls:
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(4 if TICI else 3, Priority.CTRL_HIGH)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.camera_packets = ["roadCameraState", "driverCameraState"]
        if TICI:
            self.camera_packets.append("wideRoadCameraState")

        params = Params()
        self.joystick_mode = params.get_bool("JoystickDebugMode")
        joystick_packet = ['testJoystick'] if self.joystick_mode else []

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster(
                [
                    'deviceState', 'pandaStates', 'peripheralState', 'modelV2',
                    'liveCalibration', 'driverMonitoringState',
                    'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
                    'managerState', 'liveParameters', 'radarState'
                ] + self.camera_packets + joystick_packet,
                ignore_alive=ignore,
                ignore_avg_freq=['radarState', 'longitudinalPlan'])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        if TICI:
            self.log_sock = messaging.sub_sock('androidLog')

        # wait for one pandaState and one CAN packet
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])

        # read params
        self.is_metric = params.get_bool("IsMetric")
        self.is_ldw_enabled = params.get_bool("IsLdwEnabled")
        community_feature_toggle = params.get_bool("CommunityFeaturesToggle")
        openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
        passive = params.get_bool("Passive") or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'

        controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature = self.CP.communityFeature or \
                            self.CP.fingerprintSource == car.CarParams.FingerprintSource.can
        community_feature_disallowed = community_feature and (
            not community_feature_toggle)
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            safety_config = car.CarParams.SafetyConfig.new_message()
            safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
            self.CP.safetyConfigs = [safety_config]

        # Write CarParams for radard
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP)
        self.VM = VehicleModel(self.CP)

        if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            self.LaC = LatControlAngle(self.CP)
        elif self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP, self.CI)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.initialized = False
        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.cruise_mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = False
        self.button_timers = {
            ButtonEvent.Type.decelCruise: 0,
            ButtonEvent.Type.accelCruise: 0
        }

        # scc smoother
        self.is_cruise_enabled = False
        self.applyMaxSpeed = 0
        self.apply_accel = 0.
        self.fused_accel = 0.
        self.lead_drel = 0.
        self.aReqValue = 0.
        self.aReqValueMin = 0.
        self.aReqValueMax = 0.
        self.sccStockCamStatus = 0
        self.sccStockCamAct = 0

        self.left_lane_visible = False
        self.right_lane_visible = False

        self.wide_camera = TICI and params.get_bool('EnableWideCamera')
        self.disable_op_fcw = params.get_bool('DisableOpFcw')

        # TODO: no longer necessary, aside from process replay
        self.sm['liveParameters'].valid = True

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available,
                                               len(self.CP.carFw) > 0)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if community_feature_disallowed and car_recognized and not self.CP.dashcamOnly:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)
            if len(self.CP.carFw) > 0:
                set_offroad_alert("Offroad_CarUnrecognized", True)
            else:
                set_offroad_alert("Offroad_NoFirmware", True)
        elif self.read_only:
            self.events.add(EventName.dashcamMode, static=True)
        elif self.joystick_mode:
            self.events.add(EventName.joystickDebug, static=True)
            self.startup_event = None

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default

    def update_events(self, CS):
        """Compute carEvents from carState"""

        self.events.clear()
        self.events.add_from_msg(CS.events)
        self.events.add_from_msg(self.sm['driverMonitoringState'].events)

        # Handle startup event
        if self.startup_event is not None:
            self.events.add(self.startup_event)
            self.startup_event = None

        # Don't add any more events if not initialized
        if not self.initialized:
            self.events.add(EventName.controlsInitializing)
            return

        # Create events for battery, temperature, disk space, and memory
        if EON and (self.sm['peripheralState'].pandaType != PandaType.uno) and \
           self.sm['deviceState'].batteryPercent < 1 and self.sm['deviceState'].chargingError:
            # at zero percent battery, while discharging, OP should not allowed
            self.events.add(EventName.lowBattery)
        if self.sm['deviceState'].thermalStatus >= ThermalStatus.red:
            self.events.add(EventName.overheat)
        if self.sm['deviceState'].freeSpacePercent < 7 and not SIMULATION:
            # under 7% of space free no enable allowed
            self.events.add(EventName.outOfSpace)
        # TODO: make tici threshold the same
        if self.sm['deviceState'].memoryUsagePercent > (90 if TICI else
                                                        65) and not SIMULATION:
            self.events.add(EventName.lowMemory)

        # TODO: enable this once loggerd CPU usage is more reasonable
        #cpus = list(self.sm['deviceState'].cpuUsagePercent)[:(-1 if EON else None)]
        #if max(cpus, default=0) > 95 and not SIMULATION:
        #  self.events.add(EventName.highCpuUsage)

        # Alert if fan isn't spinning for 5 seconds
        if self.sm['peripheralState'].pandaType in [
                PandaType.uno, PandaType.dos
        ]:
            if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm[
                    'deviceState'].fanSpeedPercentDesired > 50:
                if (self.sm.frame -
                        self.last_functional_fan_frame) * DT_CTRL > 5.0:
                    self.events.add(EventName.fanMalfunction)
            else:
                self.last_functional_fan_frame = self.sm.frame

        # Handle calibration status
        cal_status = self.sm['liveCalibration'].calStatus
        if cal_status != Calibration.CALIBRATED:
            if cal_status == Calibration.UNCALIBRATED:
                self.events.add(EventName.calibrationIncomplete)
            else:
                self.events.add(EventName.calibrationInvalid)

        # Handle lane change
        if self.sm[
                'lateralPlan'].laneChangeState == LaneChangeState.preLaneChange:
            direction = self.sm['lateralPlan'].laneChangeDirection
            if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
               (CS.rightBlindspot and direction == LaneChangeDirection.right):
                self.events.add(EventName.laneChangeBlocked)
            elif self.sm['lateralPlan'].autoLaneChangeEnabled and self.sm[
                    'lateralPlan'].autoLaneChangeTimer > 0:
                self.events.add(EventName.autoLaneChange)
            else:
                if direction == LaneChangeDirection.left:
                    self.events.add(EventName.preLaneChangeLeft)
                else:
                    self.events.add(EventName.preLaneChangeRight)
        elif self.sm['lateralPlan'].laneChangeState in [
                LaneChangeState.laneChangeStarting,
                LaneChangeState.laneChangeFinishing
        ]:
            self.events.add(EventName.laneChange)

        if self.can_rcv_error or not CS.canValid:
            self.events.add(EventName.canError)

        for i, pandaState in enumerate(self.sm['pandaStates']):
            # All pandas must match the list of safetyConfigs, and if outside this list, must be silent or noOutput
            if i < len(self.CP.safetyConfigs):
                safety_mismatch = pandaState.safetyModel != self.CP.safetyConfigs[
                    i].safetyModel or pandaState.safetyParam != self.CP.safetyConfigs[
                        i].safetyParam
            else:
                safety_mismatch = pandaState.safetyModel not in IGNORED_SAFETY_MODES
            if safety_mismatch or self.mismatch_counter >= 200:
                self.events.add(EventName.controlsMismatch)

            if log.PandaState.FaultType.relayMalfunction in pandaState.faults:
                self.events.add(EventName.relayMalfunction)

        # Check for HW or system issues
        if len(self.sm['radarState'].radarErrors):
            self.events.add(EventName.radarFault)
        elif not self.sm.valid["pandaStates"]:
            self.events.add(EventName.usbError)
        elif not self.sm.all_alive_and_valid():
            self.events.add(EventName.commIssue)
            if not self.logged_comm_issue:
                invalid = [
                    s for s, valid in self.sm.valid.items() if not valid
                ]
                not_alive = [
                    s for s, alive in self.sm.alive.items() if not alive
                ]
                cloudlog.event("commIssue",
                               invalid=invalid,
                               not_alive=not_alive)
                self.logged_comm_issue = True
        else:
            self.logged_comm_issue = False

        if not self.sm['liveParameters'].valid:
            self.events.add(EventName.vehicleModelInvalid)
        if not self.sm['lateralPlan'].mpcSolutionValid and not (
                EventName.turningIndicatorOn in self.events.names):
            self.events.add(EventName.plannerError)
        if not self.sm['liveLocationKalman'].sensorsOK and not NOSENSOR:
            if self.sm.frame > 5 / DT_CTRL:  # Give locationd some time to receive all the inputs
                self.events.add(EventName.sensorDataInvalid)
        if not self.sm['liveLocationKalman'].posenetOK:
            self.events.add(EventName.posenetInvalid)
        if not self.sm['liveLocationKalman'].deviceStable:
            self.events.add(EventName.deviceFalling)
        for pandaState in self.sm['pandaStates']:
            if log.PandaState.FaultType.relayMalfunction in pandaState.faults:
                self.events.add(EventName.relayMalfunction)

        if not REPLAY:
            # Check for mismatch between openpilot and car's PCM
            cruise_mismatch = CS.cruiseState.enabledAcc and (
                not self.enabled or not self.CP.pcmCruise)
            self.cruise_mismatch_counter = self.cruise_mismatch_counter + 1 if cruise_mismatch else 0
            if self.cruise_mismatch_counter > int(1. / DT_CTRL):
                self.events.add(EventName.cruiseMismatch)

        # Check for FCW
        stock_long_is_braking = self.enabled and not self.CP.openpilotLongitudinalControl and CS.aEgo < -1.5
        model_fcw = self.sm[
            'modelV2'].meta.hardBrakePredicted and not CS.brakePressed and not stock_long_is_braking
        planner_fcw = self.sm['longitudinalPlan'].fcw and self.enabled
        if not self.disable_op_fcw and (planner_fcw or model_fcw):
            self.events.add(EventName.fcw)

        if TICI:
            logs = messaging.drain_sock(self.log_sock, wait_for_one=False)
            messages = []
            for m in logs:
                try:
                    messages.append(m.androidLog.message)
                except UnicodeDecodeError:
                    pass

            for err in [
                    "ERROR_CRC", "ERROR_ECC", "ERROR_STREAM_UNDERFLOW",
                    "APPLY FAILED"
            ]:
                for m in messages:
                    if err not in m:
                        continue

                    csid = m.split("CSID:")[-1].split(" ")[0]
                    evt = {
                        "0": EventName.roadCameraError,
                        "1": EventName.wideRoadCameraError,
                        "2": EventName.driverCameraError
                    }.get(csid, None)
                    if evt is not None:
                        self.events.add(evt)

        # TODO: fix simulator
        if not SIMULATION:
            #if not NOSENSOR:
            #  if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled > 1000):
            #    # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes
            #    self.events.add(EventName.noGps)
            if not self.sm.all_alive(self.camera_packets):
                self.events.add(EventName.cameraMalfunction)
            if self.sm['modelV2'].frameDropPerc > 20:
                self.events.add(EventName.modeldLagging)
            if self.sm['liveLocationKalman'].excessiveResets:
                self.events.add(EventName.localizerMalfunction)

            # Check if all manager processes are running
            not_running = set(p.name for p in self.sm['managerState'].processes
                              if not p.running)
            if self.sm.rcv_frame['managerState'] and (not_running -
                                                      IGNORE_PROCESSES):
                self.events.add(EventName.processNotRunning)

        # Only allow engagement with brake pressed when stopped behind another stopped car
        speeds = self.sm['longitudinalPlan'].speeds
        if len(speeds) > 1:
            v_future = speeds[-1]
        else:
            v_future = 100.0
        #if CS.brakePressed and v_future >= self.CP.vEgoStarting \
        #  and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3:
        #  self.events.add(EventName.noTarget)

    def data_sample(self):
        """Receive data from sockets and update carState"""

        # Update carState from CAN
        can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
        CS = self.CI.update(self.CC, can_strs)

        self.sm.update(0)

        all_valid = CS.canValid and self.sm.all_alive_and_valid()
        if not self.initialized and (all_valid or self.sm.frame * DT_CTRL > 3.5
                                     or SIMULATION):
            if not self.read_only:
                self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
            self.initialized = True
            Params().put_bool("ControlsReady", True)

        # Check for CAN timeout
        if not can_strs:
            self.can_error_counter += 1
            self.can_rcv_error = True
        else:
            self.can_rcv_error = False

        # When the panda and controlsd do not agree on controls_allowed
        # we want to disengage openpilot. However the status from the panda goes through
        # another socket other than the CAN messages and one can arrive earlier than the other.
        # Therefore we allow a mismatch for two samples, then we trigger the disengagement.
        if not self.enabled:
            self.mismatch_counter = 0

        # All pandas not in silent mode must have controlsAllowed when openpilot is enabled
        if any(not ps.controlsAllowed and self.enabled
               for ps in self.sm['pandaStates']
               if ps.safetyModel not in IGNORED_SAFETY_MODES):
            self.mismatch_counter += 1

        self.distance_traveled += CS.vEgo * DT_CTRL

        return CS

    def state_transition(self, CS):
        """Compute conditional state transitions and execute actions on state transitions"""

        self.v_cruise_kph_last = self.v_cruise_kph

        self.CP.pcmCruise = self.CI.CP.pcmCruise

        # if stock cruise is completely disabled, then we can use our own set speed logic
        #if not self.CP.pcmCruise:
        #  self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.buttonEvents, self.button_timers, self.enabled, self.is_metric)
        #elif self.CP.pcmCruise and CS.cruiseState.enabled:
        #  self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH

        SccSmoother.update_cruise_buttons(self, CS,
                                          self.CP.openpilotLongitudinalControl)

        # decrement the soft disable timer at every step, as it's reset on
        # entrance in SOFT_DISABLING state
        self.soft_disable_timer = max(0, self.soft_disable_timer - 1)

        self.current_alert_types = [ET.PERMANENT]

        # ENABLED, PRE ENABLING, SOFT DISABLING
        if self.state != State.disabled:
            # user and immediate disable always have priority in a non-disabled state
            if self.events.any(ET.USER_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.USER_DISABLE)

            elif self.events.any(ET.IMMEDIATE_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.IMMEDIATE_DISABLE)

            else:
                # ENABLED
                if self.state == State.enabled:
                    if self.events.any(ET.SOFT_DISABLE):
                        self.state = State.softDisabling
                        self.soft_disable_timer = int(0.5 / DT_CTRL)
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                # SOFT DISABLING
                elif self.state == State.softDisabling:
                    if not self.events.any(ET.SOFT_DISABLE):
                        # no more soft disabling condition, so go back to ENABLED
                        self.state = State.enabled

                    elif self.events.any(
                            ET.SOFT_DISABLE) and self.soft_disable_timer > 0:
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                    elif self.soft_disable_timer <= 0:
                        self.state = State.disabled

                # PRE ENABLING
                elif self.state == State.preEnabled:
                    if not self.events.any(ET.PRE_ENABLE):
                        self.state = State.enabled
                    else:
                        self.current_alert_types.append(ET.PRE_ENABLE)

        # DISABLED
        elif self.state == State.disabled:
            if self.events.any(ET.ENABLE):
                if self.events.any(ET.NO_ENTRY):
                    self.current_alert_types.append(ET.NO_ENTRY)

                else:
                    if self.events.any(ET.PRE_ENABLE):
                        self.state = State.preEnabled
                    else:
                        self.state = State.enabled
                    self.current_alert_types.append(ET.ENABLE)
                    self.v_cruise_kph = initialize_v_cruise(
                        CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)

        # Check if actuators are enabled
        self.active = self.state == State.enabled or self.state == State.softDisabling
        if self.active:
            self.current_alert_types.append(ET.WARNING)

        # Check if openpilot is engaged
        self.enabled = self.active or self.state == State.preEnabled

    def state_control(self, CS):
        """Given the state, this function returns an actuators packet"""

        # Update VehicleModel
        params = self.sm['liveParameters']
        x = max(params.stiffnessFactor, 0.1)
        #sr = max(params.steerRatio, 0.1)

        if ntune_common_enabled('useLiveSteerRatio'):
            sr = max(params.steerRatio, 0.1)
        else:
            sr = max(ntune_common_get('steerRatio'), 0.1)

        self.VM.update_params(x, sr)

        lat_plan = self.sm['lateralPlan']
        long_plan = self.sm['longitudinalPlan']

        actuators = car.CarControl.Actuators.new_message()
        actuators.longControlState = self.LoC.long_control_state

        if CS.leftBlinker or CS.rightBlinker:
            self.last_blinker_frame = self.sm.frame

        # State specific actions

        if not self.active:
            self.LaC.reset()
            self.LoC.reset(v_pid=CS.vEgo)

        if not CS.cruiseState.enabledAcc:
            self.LoC.reset(v_pid=CS.vEgo)

        if not self.joystick_mode:
            # accel PID loop
            pid_accel_limits = self.CI.get_pid_accel_limits(
                self.CP, CS.vEgo, self.v_cruise_kph * CV.KPH_TO_MS)
            actuators.accel = self.LoC.update(
                self.active and CS.cruiseState.enabledAcc, CS, self.CP,
                long_plan, pid_accel_limits, self.sm['radarState'])

            # Steering PID loop and lateral MPC
            lat_active = self.active and not CS.steerWarning and not CS.steerError and CS.vEgo > self.CP.minSteerSpeed
            desired_curvature, desired_curvature_rate = get_lag_adjusted_curvature(
                self.CP, CS.vEgo, lat_plan.psis, lat_plan.curvatures,
                lat_plan.curvatureRates)
            actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(
                lat_active, CS, self.CP, self.VM, params, desired_curvature,
                desired_curvature_rate)
        else:
            lac_log = log.ControlsState.LateralDebugState.new_message()
            if self.sm.rcv_frame['testJoystick'] > 0 and self.active:
                actuators.accel = 4.0 * clip(self.sm['testJoystick'].axes[0],
                                             -1, 1)

                steer = clip(self.sm['testJoystick'].axes[1], -1, 1)
                # max angle is 45 for angle-based cars
                actuators.steer, actuators.steeringAngleDeg = steer, steer * 45.

                lac_log.active = True
                lac_log.steeringAngleDeg = CS.steeringAngleDeg
                lac_log.output = steer
                lac_log.saturated = abs(steer) >= 0.9

        # Check for difference between desired angle and angle for angle based control
        angle_control_saturated = self.CP.steerControlType == car.CarParams.SteerControlType.angle and \
          abs(actuators.steeringAngleDeg - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD

        if angle_control_saturated and not CS.steeringPressed and self.active:
            self.saturated_count += 1
        else:
            self.saturated_count = 0

        # Send a "steering required alert" if saturation count has reached the limit
        if (lac_log.saturated and not CS.steeringPressed) or \
           (self.saturated_count > STEER_ANGLE_SATURATION_TIMEOUT):

            if len(lat_plan.dPathPoints):
                # Check if we deviated from the path
                # TODO use desired vs actual curvature
                left_deviation = actuators.steer > 0 and lat_plan.dPathPoints[
                    0] < -0.20
                right_deviation = actuators.steer < 0 and lat_plan.dPathPoints[
                    0] > 0.20

                if left_deviation or right_deviation:
                    self.events.add(EventName.steerSaturated)

        # Ensure no NaNs/Infs
        for p in ACTUATOR_FIELDS:
            attr = getattr(actuators, p)
            if not isinstance(attr, Number):
                continue

            if not math.isfinite(attr):
                cloudlog.error(
                    f"actuators.{p} not finite {actuators.to_dict()}")
                setattr(actuators, p, 0.0)

        return actuators, lac_log

    def update_button_timers(self, buttonEvents):
        # increment timer for buttons still pressed
        for k in self.button_timers.keys():
            if self.button_timers[k] > 0:
                self.button_timers[k] += 1

        for b in buttonEvents:
            if b.type.raw in self.button_timers:
                self.button_timers[b.type.raw] = 1 if b.pressed else 0

    def publish_logs(self, CS, start_time, actuators, lac_log):
        """Send actuators and hud commands to the car, send controlsstate and MPC logging"""

        CC = car.CarControl.new_message()
        CC.enabled = self.enabled
        CC.active = self.active
        CC.actuators = actuators

        if len(self.sm['liveLocationKalman'].orientationNED.value) > 2:
            CC.roll = self.sm['liveLocationKalman'].orientationNED.value[0]
            CC.pitch = self.sm['liveLocationKalman'].orientationNED.value[1]

        CC.cruiseControl.cancel = self.CP.pcmCruise and not self.enabled and CS.cruiseState.enabled
        if self.joystick_mode and self.sm.rcv_frame[
                'testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
            CC.cruiseControl.cancel = True

        CC.hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS)
        CC.hudControl.speedVisible = self.enabled
        CC.hudControl.lanesVisible = self.enabled
        CC.hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead

        right_lane_visible = self.sm['lateralPlan'].rProb > 0.5
        left_lane_visible = self.sm['lateralPlan'].lProb > 0.5

        if self.sm.frame % 100 == 0:
            self.right_lane_visible = right_lane_visible
            self.left_lane_visible = left_lane_visible

        CC.hudControl.rightLaneVisible = self.right_lane_visible
        CC.hudControl.leftLaneVisible = self.left_lane_visible

        recent_blinker = (self.sm.frame - self.last_blinker_frame
                          ) * DT_CTRL < 5.0  # 5s blinker cooldown
        ldw_allowed = self.is_ldw_enabled and CS.vEgo > LDW_MIN_SPEED and not recent_blinker \
                        and not self.active and self.sm['liveCalibration'].calStatus == Calibration.CALIBRATED

        meta = self.sm['modelV2'].meta
        if len(meta.desirePrediction) and ldw_allowed:
            l_lane_change_prob = meta.desirePrediction[Desire.laneChangeLeft -
                                                       1]
            r_lane_change_prob = meta.desirePrediction[Desire.laneChangeRight -
                                                       1]
            cameraOffset = ntune_common_get(
                "cameraOffset"
            ) + 0.08 if self.wide_camera else ntune_common_get("cameraOffset")
            l_lane_close = left_lane_visible and (
                self.sm['modelV2'].laneLines[1].y[0] > -(1.08 + cameraOffset))
            r_lane_close = right_lane_visible and (
                self.sm['modelV2'].laneLines[2].y[0] < (1.08 - cameraOffset))

            CC.hudControl.leftLaneDepart = bool(
                l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
            CC.hudControl.rightLaneDepart = bool(
                r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)

        if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
            self.events.add(EventName.ldw)

        clear_event = ET.WARNING if ET.WARNING not in self.current_alert_types else None
        alerts = self.events.create_alerts(self.current_alert_types,
                                           [self.CP, self.sm, self.is_metric])
        self.AM.add_many(self.sm.frame, alerts, self.enabled)
        self.AM.process_alerts(self.sm.frame, clear_event)
        CC.hudControl.visualAlert = self.AM.visual_alert

        if not self.read_only and self.initialized:
            # send car controls over can
            can_sends = self.CI.apply(CC, self)
            self.pm.send(
                'sendcan',
                can_list_to_can_capnp(can_sends,
                                      msgtype='sendcan',
                                      valid=CS.canValid))

        force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \
                      (self.state == State.softDisabling)

        # Curvature & Steering angle
        params = self.sm['liveParameters']
        steer_angle_without_offset = math.radians(CS.steeringAngleDeg -
                                                  params.angleOffsetAverageDeg)
        curvature = -self.VM.calc_curvature(steer_angle_without_offset,
                                            CS.vEgo)

        # controlsState
        dat = messaging.new_message('controlsState')
        dat.valid = CS.canValid
        controlsState = dat.controlsState
        controlsState.alertText1 = self.AM.alert_text_1
        controlsState.alertText2 = self.AM.alert_text_2
        controlsState.alertSize = self.AM.alert_size
        controlsState.alertStatus = self.AM.alert_status
        controlsState.alertBlinkingRate = self.AM.alert_rate
        controlsState.alertType = self.AM.alert_type
        controlsState.alertSound = self.AM.audible_alert
        controlsState.canMonoTimes = list(CS.canMonoTimes)
        controlsState.longitudinalPlanMonoTime = self.sm.logMonoTime[
            'longitudinalPlan']
        controlsState.lateralPlanMonoTime = self.sm.logMonoTime['lateralPlan']
        controlsState.enabled = self.enabled
        controlsState.active = self.active
        controlsState.curvature = curvature
        controlsState.state = self.state
        controlsState.engageable = not self.events.any(ET.NO_ENTRY)
        controlsState.longControlState = self.LoC.long_control_state
        controlsState.vPid = float(self.LoC.v_pid)
        controlsState.vCruise = float(
            self.applyMaxSpeed if self.CP.
            openpilotLongitudinalControl else self.v_cruise_kph)
        controlsState.upAccelCmd = float(self.LoC.pid.p)
        controlsState.uiAccelCmd = float(self.LoC.pid.i)
        controlsState.ufAccelCmd = float(self.LoC.pid.f)
        controlsState.cumLagMs = -self.rk.remaining * 1000.
        controlsState.startMonoTime = int(start_time * 1e9)
        controlsState.forceDecel = bool(force_decel)
        controlsState.canErrorCounter = self.can_error_counter

        controlsState.angleSteers = steer_angle_without_offset * CV.RAD_TO_DEG
        controlsState.applyAccel = self.apply_accel
        controlsState.aReqValue = self.aReqValue
        controlsState.aReqValueMin = self.aReqValueMin
        controlsState.aReqValueMax = self.aReqValueMax
        controlsState.sccStockCamAct = self.sccStockCamAct
        controlsState.sccStockCamStatus = self.sccStockCamStatus

        controlsState.steerRatio = self.VM.sR
        controlsState.steerRateCost = ntune_common_get('steerRateCost')
        controlsState.steerActuatorDelay = ntune_common_get(
            'steerActuatorDelay')

        controlsState.sccGasFactor = ntune_scc_get('sccGasFactor')
        controlsState.sccBrakeFactor = ntune_scc_get('sccBrakeFactor')
        controlsState.sccCurvatureFactor = ntune_scc_get('sccCurvatureFactor')
        controlsState.longitudinalActuatorDelayLowerBound = ntune_scc_get(
            'longitudinalActuatorDelayLowerBound')
        controlsState.longitudinalActuatorDelayUpperBound = ntune_scc_get(
            'longitudinalActuatorDelayUpperBound')

        if self.joystick_mode:
            controlsState.lateralControlState.debugState = lac_log
        elif self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            controlsState.lateralControlState.angleState = lac_log
        elif self.CP.lateralTuning.which() == 'pid':
            controlsState.lateralControlState.pidState = lac_log
        elif self.CP.lateralTuning.which() == 'lqr':
            controlsState.lateralControlState.lqrState = lac_log
        elif self.CP.lateralTuning.which() == 'indi':
            controlsState.lateralControlState.indiState = lac_log
        self.pm.send('controlsState', dat)

        # carState
        car_events = self.events.to_msg()
        cs_send = messaging.new_message('carState')
        cs_send.valid = CS.canValid
        cs_send.carState = CS
        cs_send.carState.events = car_events
        self.pm.send('carState', cs_send)

        # carEvents - logged every second or on change
        if (self.sm.frame % int(1. / DT_CTRL)
                == 0) or (self.events.names != self.events_prev):
            ce_send = messaging.new_message('carEvents', len(self.events))
            ce_send.carEvents = car_events
            self.pm.send('carEvents', ce_send)
        self.events_prev = self.events.names.copy()

        # 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.carParams = self.CP
            self.pm.send('carParams', cp_send)

        # carControl
        cc_send = messaging.new_message('carControl')
        cc_send.valid = CS.canValid
        cc_send.carControl = CC
        self.pm.send('carControl', cc_send)

        # copy CarControl to pass to CarInterface on the next iteration
        self.CC = CC

    def step(self):
        start_time = sec_since_boot()
        self.prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data from sockets and get a carState
        CS = self.data_sample()
        self.prof.checkpoint("Sample")

        self.update_events(CS)

        if not self.read_only and self.initialized:
            # Update control state
            self.state_transition(CS)
            self.prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, lac_log = self.state_control(CS)

        self.prof.checkpoint("State Control")

        # Publish data
        self.publish_logs(CS, start_time, actuators, lac_log)
        self.prof.checkpoint("Sent")

        self.update_button_timers(CS.buttonEvents)

    def controlsd_thread(self):
        while True:
            self.step()
            self.rk.monitor_time()
            self.prof.display()
Example #15
0
class InterBridge:
    publishers = [
        'thermal',
        'controlsState',
        'model',
        'health',
        'carState',
        'carControl',
        'plan',
        'liveLocation',
        'liveMpc',
        'liveLongitudinalMpc',
        'driverState',
        'liveParameters',
        'pathPlan',
        'carParams',
        'dMonitoringState',
        'testJoystick',
    ]

    def __init__(self, sm=None, pm=None, can_sock=None):
        # Initialize received messages queue
        self.msgs_queue = Queue()

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster(['testJoystick'])

        self.sm = sm
        if self.sm is None:
            self.sm = messaging.SubMaster(self.publishers)

        self.rk = Ratekeeper(RATE, print_delay_threshold=None)

    def sock_msg_received(self, client, server, msg):
        self.msgs_queue.put(msg)

    def sock_msg_send(self, msg):
        pass

    def step(self):
        # Send msg from ZMQ to Socket, only if there are connected clients
        if self.count_clients():
            self.sm.update(0)
            send_msg = {}
            for publisher in self.publishers:
                if self.sm.updated[publisher]:
                    send_msg[publisher] = self.sm[publisher].to_dict()
                    send_msg[publisher]['logMonoTime'] = self.sm.logMonoTime[
                        publisher]
                    # Hack, convert known bytes value to hex (bytes are not serializable)
                    if publisher == 'carParams' and send_msg[publisher][
                            'carFw']:
                        for idx, val in enumerate(
                                send_msg[publisher]['carFw']):
                            send_msg[publisher]['carFw'][idx][
                                'fwVersion'] = val['fwVersion'].hex()

            if send_msg:
                self.sock_msg_send(send_msg)

        # Send msg from Socket to ZMQ (only testJoystick!)
        while not self.msgs_queue.empty():
            msg = self.msgs_queue.get()

            if 'opEdit' in msg:
                if 'loadRequest' in msg['opEdit']:
                    try:
                        with open(OP_PARAMS_PATH, 'r') as file:
                            data = file.read()
                            self.sock_msg_send({'opEdit': json.loads(data)})
                    except (FileNotFoundError, PermissionError):
                        self.sock_msg_send(
                            {'error': "File op_params.json not found."})
                else:
                    try:
                        with open(OP_PARAMS_PATH, 'w') as file:
                            file.write(json.dumps(msg['opEdit'], indent=2))
                    except PermissionError:
                        self.sock_msg_send({
                            'error':
                            "Can't write op_params.json, not enough permissions."
                        })

            elif 'testJoystick' in msg:
                dat = messaging.new_message('testJoystick')
                testJoystick = dat.testJoystick
                testJoystick.axes = msg['testJoystick']['axes']
                testJoystick.buttons = msg['testJoystick']['buttons']
                testJoystick.enabled = msg['testJoystick']['enabled']
                testJoystick.axesMode = msg['testJoystick']['axesMode']
                self.pm.send('testJoystick', dat)

    def interbridged_thread(self, count_callback):
        self.count_clients = count_callback
        while True:
            self.step()
            self.rk.keep_time()
Example #16
0
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(4 if TICI else 3, Priority.CTRL_HIGH)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.camera_packets = ["roadCameraState", "driverCameraState"]
        if TICI:
            self.camera_packets.append("wideRoadCameraState")

        params = Params()
        self.joystick_mode = params.get_bool("JoystickDebugMode")
        joystick_packet = ['testJoystick'] if self.joystick_mode else []

        self.sm = sm
        if self.sm is None:
            ignore = ['driverCameraState', 'managerState'
                      ] if SIMULATION else None
            self.sm = messaging.SubMaster(
                [
                    'deviceState', 'pandaStates', 'peripheralState', 'modelV2',
                    'liveCalibration', 'driverMonitoringState',
                    'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
                    'managerState', 'liveParameters', 'radarState'
                ] + self.camera_packets + joystick_packet,
                ignore_alive=ignore,
                ignore_avg_freq=['radarState', 'longitudinalPlan'])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        if TICI:
            self.log_sock = messaging.sub_sock('androidLog')

        # wait for one pandaState and one CAN packet
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])

        # read params
        self.is_metric = params.get_bool("IsMetric")
        self.is_ldw_enabled = params.get_bool("IsLdwEnabled")
        community_feature_toggle = params.get_bool("CommunityFeaturesToggle")
        openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
        passive = params.get_bool("Passive") or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'

        controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature = self.CP.communityFeature or \
                            self.CP.fingerprintSource == car.CarParams.FingerprintSource.can
        community_feature_disallowed = community_feature and (
            not community_feature_toggle)
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            safety_config = car.CarParams.SafetyConfig.new_message()
            safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
            self.CP.safetyConfigs = [safety_config]

        # Write CarParams for radard
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP)
        self.VM = VehicleModel(self.CP)

        if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
            self.LaC = LatControlAngle(self.CP)
        elif self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP, self.CI)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.initialized = False
        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.cruise_mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]
        self.logged_comm_issue = False
        self.button_timers = {
            ButtonEvent.Type.decelCruise: 0,
            ButtonEvent.Type.accelCruise: 0
        }

        # scc smoother
        self.is_cruise_enabled = False
        self.applyMaxSpeed = 0
        self.apply_accel = 0.
        self.fused_accel = 0.
        self.lead_drel = 0.
        self.aReqValue = 0.
        self.aReqValueMin = 0.
        self.aReqValueMax = 0.
        self.sccStockCamStatus = 0
        self.sccStockCamAct = 0

        self.left_lane_visible = False
        self.right_lane_visible = False

        self.wide_camera = TICI and params.get_bool('EnableWideCamera')
        self.disable_op_fcw = params.get_bool('DisableOpFcw')

        # TODO: no longer necessary, aside from process replay
        self.sm['liveParameters'].valid = True

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available,
                                               len(self.CP.carFw) > 0)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if community_feature_disallowed and car_recognized and not self.CP.dashcamOnly:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)
            if len(self.CP.carFw) > 0:
                set_offroad_alert("Offroad_CarUnrecognized", True)
            else:
                set_offroad_alert("Offroad_NoFirmware", True)
        elif self.read_only:
            self.events.add(EventName.dashcamMode, static=True)
        elif self.joystick_mode:
            self.events.add(EventName.joystickDebug, static=True)
            self.startup_event = None

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default
Example #17
0
class Plant():
    messaging_initialized = False

    def __init__(self,
                 lead_relevancy=False,
                 rate=100,
                 speed=0.0,
                 distance_lead=2.0):
        self.rate = rate

        if not Plant.messaging_initialized:
            Plant.logcan = messaging.pub_sock('can')
            Plant.sendcan = messaging.sub_sock('sendcan')
            Plant.model = messaging.pub_sock('model')
            Plant.live_params = messaging.pub_sock('liveParameters')
            Plant.health = messaging.pub_sock('health')
            Plant.thermal = messaging.pub_sock('thermal')
            Plant.driverState = messaging.pub_sock('driverState')
            Plant.cal = messaging.pub_sock('liveCalibration')
            Plant.controls_state = messaging.sub_sock('controlsState')
            Plant.plan = messaging.sub_sock('plan')
            Plant.messaging_initialized = True

        self.frame = 0
        self.angle_steer = 0.
        self.gear_choice = 0
        self.speed, self.speed_prev = 0., 0.

        self.esp_disabled = 0
        self.main_on = 1
        self.user_gas = 0
        self.computer_brake, self.user_brake = 0, 0
        self.brake_pressed = 0
        self.angle_steer_rate = 0
        self.distance, self.distance_prev = 0., 0.
        self.speed, self.speed_prev = speed, speed
        self.steer_error, self.brake_error, self.steer_not_allowed = 0, 0, 0
        self.gear_shifter = 8  # D gear
        self.pedal_gas = 0
        self.cruise_setting = 0

        self.seatbelt, self.door_all_closed = True, True
        self.steer_torque, self.v_cruise, self.acc_status = 0, 0, 0  # v_cruise is reported from can, not the one used for controls

        self.lead_relevancy = lead_relevancy

        # lead car
        self.distance_lead, self.distance_lead_prev = distance_lead, distance_lead

        self.rk = Ratekeeper(rate, print_delay_threshold=100)
        self.ts = 1. / rate

        self.cp = get_car_can_parser()
        self.response_seen = False

        time.sleep(1)
        messaging.drain_sock(Plant.sendcan)
        messaging.drain_sock(Plant.controls_state)

    def close(self):
        Plant.logcan.close()
        Plant.model.close()
        Plant.live_params.close()

    def speed_sensor(self, speed):
        if speed < 0.3:
            return 0
        else:
            return speed * CV.MS_TO_KPH

    def current_time(self):
        return float(self.rk.frame) / self.rate

    def step(self,
             v_lead=0.0,
             cruise_buttons=None,
             grade=0.0,
             publish_model=True):
        gen_signals, gen_checks = get_can_signals(CP)
        sgs = [s[0] for s in gen_signals]
        msgs = [s[1] for s in gen_signals]
        cks_msgs = set(check[0] for check in gen_checks)
        cks_msgs.add(0x18F)
        cks_msgs.add(0x30C)

        # ******** get messages sent to the car ********
        can_strings = messaging.drain_sock_raw(Plant.sendcan,
                                               wait_for_one=self.response_seen)

        # After the first response the car is done fingerprinting, so we can run in lockstep with controlsd
        if can_strings:
            self.response_seen = True

        self.cp.update_strings(can_strings, sendcan=True)

        # ******** get controlsState messages for plotting ***
        controls_state_msgs = []
        for a in messaging.drain_sock(Plant.controls_state,
                                      wait_for_one=self.response_seen):
            controls_state_msgs.append(a.controlsState)

        fcw = None
        for a in messaging.drain_sock(Plant.plan):
            if a.plan.fcw:
                fcw = True

        if self.cp.vl[0x1fa]['COMPUTER_BRAKE_REQUEST']:
            brake = self.cp.vl[0x1fa]['COMPUTER_BRAKE'] * 0.003906248
        else:
            brake = 0.0

        if self.cp.vl[0x200]['GAS_COMMAND'] > 0:
            gas = self.cp.vl[0x200]['GAS_COMMAND'] / 256.0
        else:
            gas = 0.0

        if self.cp.vl[0xe4]['STEER_TORQUE_REQUEST']:
            steer_torque = self.cp.vl[0xe4]['STEER_TORQUE'] * 1.0 / 0xf00
        else:
            steer_torque = 0.0

        distance_lead = self.distance_lead_prev + v_lead * self.ts

        # ******** run the car ********
        speed, acceleration = car_plant(self.distance_prev, self.speed_prev,
                                        grade, gas, brake)
        distance = self.distance_prev + speed * self.ts
        speed = self.speed_prev + self.ts * acceleration
        if speed <= 0:
            speed = 0
            acceleration = 0

        # ******** lateral ********
        self.angle_steer -= (steer_torque / 10.0) * self.ts

        # *** radar model ***
        if self.lead_relevancy:
            d_rel = np.maximum(0., distance_lead - distance)
            v_rel = v_lead - speed
        else:
            d_rel = 200.
            v_rel = 0.
        lateral_pos_rel = 0.

        # print at 5hz
        if (self.frame % (self.rate // 5)) == 0:
            print(
                "%6.2f m  %6.2f m/s  %6.2f m/s2   %.2f ang   gas: %.2f  brake: %.2f  steer: %5.2f     lead_rel: %6.2f m  %6.2f m/s"
                % (distance, speed, acceleration, self.angle_steer, gas, brake,
                   steer_torque, d_rel, v_rel))

        # ******** publish the car ********
        vls_tuple = namedtuple('vls', [
            'XMISSION_SPEED',
            'WHEEL_SPEED_FL',
            'WHEEL_SPEED_FR',
            'WHEEL_SPEED_RL',
            'WHEEL_SPEED_RR',
            'STEER_ANGLE',
            'STEER_ANGLE_RATE',
            'STEER_TORQUE_SENSOR',
            'STEER_TORQUE_MOTOR',
            'LEFT_BLINKER',
            'RIGHT_BLINKER',
            'GEAR',
            'WHEELS_MOVING',
            'BRAKE_ERROR_1',
            'BRAKE_ERROR_2',
            'SEATBELT_DRIVER_LAMP',
            'SEATBELT_DRIVER_LATCHED',
            'BRAKE_PRESSED',
            'BRAKE_SWITCH',
            'CRUISE_BUTTONS',
            'ESP_DISABLED',
            'HUD_LEAD',
            'USER_BRAKE',
            'STEER_STATUS',
            'GEAR_SHIFTER',
            'PEDAL_GAS',
            'CRUISE_SETTING',
            'ACC_STATUS',
            'CRUISE_SPEED_PCM',
            'CRUISE_SPEED_OFFSET',
            'DOOR_OPEN_FL',
            'DOOR_OPEN_FR',
            'DOOR_OPEN_RL',
            'DOOR_OPEN_RR',
            'CAR_GAS',
            'MAIN_ON',
            'EPB_STATE',
            'BRAKE_HOLD_ACTIVE',
            'INTERCEPTOR_GAS',
            'INTERCEPTOR_GAS2',
            'IMPERIAL_UNIT',
            'MOTOR_TORQUE',
        ])
        vls = vls_tuple(
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.speed_sensor(speed),
            self.angle_steer,
            self.angle_steer_rate,
            0,
            0,  # Steer torque sensor
            0,
            0,  # Blinkers
            self.gear_choice,
            speed != 0,
            self.brake_error,
            self.brake_error,
            not self.seatbelt,
            self.seatbelt,  # Seatbelt
            self.brake_pressed,
            0.,  # Brake pressed, Brake switch
            cruise_buttons,
            self.esp_disabled,
            0,  # HUD lead
            self.user_brake,
            self.steer_error,
            self.gear_shifter,
            self.pedal_gas,
            self.cruise_setting,
            self.acc_status,
            self.v_cruise,
            0,  # Cruise speed offset
            0,
            0,
            0,
            0,  # Doors
            self.user_gas,
            self.main_on,
            0,  # EPB State
            0,  # Brake hold
            0,  # Interceptor feedback
            0,  # Interceptor 2 feedback
            False,
            0,
        )

        # TODO: publish each message at proper frequency
        can_msgs = []
        for msg in set(msgs):
            msg_struct = {}
            indxs = [i for i, x in enumerate(msgs) if msg == msgs[i]]
            for i in indxs:
                msg_struct[sgs[i]] = getattr(vls, sgs[i])

            if "COUNTER" in honda.get_signals(msg):
                msg_struct["COUNTER"] = self.frame % 4

            if "COUNTER_PEDAL" in honda.get_signals(msg):
                msg_struct["COUNTER_PEDAL"] = self.frame % 0xf

            msg = honda.lookup_msg_id(msg)
            msg_data = honda.encode(msg, msg_struct)

            if "CHECKSUM" in honda.get_signals(msg):
                msg_data = fix(msg_data, msg)

            if "CHECKSUM_PEDAL" in honda.get_signals(msg):
                msg_struct["CHECKSUM_PEDAL"] = crc8_pedal(msg_data[:-1])
                msg_data = honda.encode(msg, msg_struct)

            can_msgs.append([msg, 0, msg_data, 0])

        # add the radar message
        # TODO: use the DBC
        if self.frame % 5 == 0:
            radar_state_msg = b'\x79\x00\x00\x00\x00\x00\x00\x00'
            radar_msg = to_3_byte(d_rel * 16.0) + \
                        to_3_byte(int(lateral_pos_rel * 16.0) & 0x3ff) + \
                        to_3s_byte(int(v_rel * 32.0)) + \
                        b"0f00000"

            radar_msg = binascii.unhexlify(radar_msg)
            can_msgs.append([0x400, 0, radar_state_msg, 1])
            can_msgs.append([0x445, 0, radar_msg, 1])

        # add camera msg so controlsd thinks it's alive
        msg_struct["COUNTER"] = self.frame % 4
        msg = honda.lookup_msg_id(0xe4)
        msg_data = honda.encode(msg, msg_struct)
        msg_data = fix(msg_data, 0xe4)
        can_msgs.append([0xe4, 0, msg_data, 2])

        # Fake sockets that controlsd subscribes to
        live_parameters = messaging.new_message('liveParameters')
        live_parameters.liveParameters.valid = True
        live_parameters.liveParameters.sensorValid = True
        live_parameters.liveParameters.posenetValid = True
        live_parameters.liveParameters.steerRatio = CP.steerRatio
        live_parameters.liveParameters.stiffnessFactor = 1.0
        Plant.live_params.send(live_parameters.to_bytes())

        driver_state = messaging.new_message('driverState')
        driver_state.driverState.faceOrientation = [0.] * 3
        driver_state.driverState.facePosition = [0.] * 2
        Plant.driverState.send(driver_state.to_bytes())

        health = messaging.new_message('health')
        health.health.controlsAllowed = True
        Plant.health.send(health.to_bytes())

        thermal = messaging.new_message('thermal')
        thermal.thermal.freeSpace = 1.
        thermal.thermal.batteryPercent = 100
        Plant.thermal.send(thermal.to_bytes())

        # ******** publish a fake model going straight and fake calibration ********
        # note that this is worst case for MPC, since model will delay long mpc by one time step
        if publish_model and self.frame % 5 == 0:
            md = messaging.new_message('model')
            cal = messaging.new_message('liveCalibration')
            md.model.frameId = 0
            for x in [md.model.path, md.model.leftLane, md.model.rightLane]:
                x.points = [0.0] * 50
                x.prob = 1.0
                x.std = 1.0

            if self.lead_relevancy:
                d_rel = np.maximum(0., distance_lead - distance)
                v_rel = v_lead - speed
                prob = 1.0
            else:
                d_rel = 200.
                v_rel = 0.
                prob = 0.0

            md.model.lead.dist = float(d_rel)
            md.model.lead.prob = prob
            md.model.lead.relY = 0.0
            md.model.lead.relYStd = 1.
            md.model.lead.relVel = float(v_rel)
            md.model.lead.relVelStd = 1.
            md.model.lead.relA = 0.0
            md.model.lead.relAStd = 10.
            md.model.lead.std = 1.0

            cal.liveCalibration.calStatus = 1
            cal.liveCalibration.calPerc = 100
            cal.liveCalibration.rpyCalib = [0.] * 3
            # fake values?
            Plant.model.send(md.to_bytes())
            Plant.cal.send(cal.to_bytes())

        Plant.logcan.send(can_list_to_can_capnp(can_msgs))

        # ******** update prevs ********
        self.frame += 1

        if self.response_seen:
            self.rk.monitor_time()

            self.speed = speed
            self.distance = distance
            self.distance_lead = distance_lead

            self.speed_prev = speed
            self.distance_prev = distance
            self.distance_lead_prev = distance_lead

        else:
            # Don't advance time when controlsd is not yet ready
            self.rk.keep_time()
            self.rk._frame = 0

        return {
            "distance": distance,
            "speed": speed,
            "acceleration": acceleration,
            "distance_lead": distance_lead,
            "brake": brake,
            "gas": gas,
            "steer_torque": steer_torque,
            "fcw": fcw,
            "controls_state_msgs": controls_state_msgs,
        }
Example #18
0
class Controls:
    def __init__(self, sm=None, pm=None, can_sock=None):
        config_realtime_process(3, Priority.CTRL_HIGH)

        # Setup sockets
        self.pm = pm
        if self.pm is None:
            self.pm = messaging.PubMaster([
                'sendcan', 'controlsState', 'carState', 'carControl',
                'carEvents', 'carParams'
            ])

        self.sm = sm
        if self.sm is None:
            self.sm = messaging.SubMaster([
                'thermal', 'health', 'model', 'liveCalibration', 'frontFrame',
                'dMonitoringState', 'plan', 'pathPlan', 'liveLocationKalman'
            ])

        self.can_sock = can_sock
        if can_sock is None:
            can_timeout = None if os.environ.get('NO_CAN_TIMEOUT',
                                                 False) else 100
            self.can_sock = messaging.sub_sock('can', timeout=can_timeout)

        # wait for one health and one CAN packet
        print("Waiting for CAN messages...")
        get_one_can(self.can_sock)

        self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])

        # read params
        params = Params()
        self.is_metric = params.get("IsMetric", encoding='utf8') == "1"
        self.is_ldw_enabled = params.get("IsLdwEnabled",
                                         encoding='utf8') == "1"
        internet_needed = (params.get("Offroad_ConnectivityNeeded",
                                      encoding='utf8') is not None) and (
                                          params.get("DisableUpdates") != b"1")
        community_feature_toggle = params.get("CommunityFeaturesToggle",
                                              encoding='utf8') == "1"
        openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                              encoding='utf8') == "1"
        passive = params.get("Passive", encoding='utf8') == "1" or \
                  internet_needed or not openpilot_enabled_toggle

        # detect sound card presence and ensure successful init
        sounds_available = HARDWARE.get_sound_card_online()

        car_recognized = self.CP.carName != 'mock'
        # If stock camera is disconnected, we loaded car controls and it's not dashcam mode
        controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive and not self.CP.dashcamOnly
        community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle
        self.read_only = not car_recognized or not controller_available or \
                           self.CP.dashcamOnly or community_feature_disallowed
        if self.read_only:
            self.CP.safetyModel = car.CarParams.SafetyModel.noOutput

        # Write CarParams for radard and boardd safety mode
        cp_bytes = self.CP.to_bytes()
        params.put("CarParams", cp_bytes)
        put_nonblocking("CarParamsCache", cp_bytes)

        self.CC = car.CarControl.new_message()
        self.AM = AlertManager()
        self.events = Events()

        self.LoC = LongControl(self.CP, self.CI.compute_gb)
        self.VM = VehicleModel(self.CP)

        if self.CP.lateralTuning.which() == 'pid':
            self.LaC = LatControlPID(self.CP)
        elif self.CP.lateralTuning.which() == 'indi':
            self.LaC = LatControlINDI(self.CP)
        elif self.CP.lateralTuning.which() == 'lqr':
            self.LaC = LatControlLQR(self.CP)

        self.state = State.disabled
        self.enabled = False
        self.active = False
        self.can_rcv_error = False
        self.soft_disable_timer = 0
        self.v_cruise_kph = 255
        self.v_cruise_kph_last = 0
        self.mismatch_counter = 0
        self.can_error_counter = 0
        self.last_blinker_frame = 0
        self.saturated_count = 0
        self.distance_traveled = 0
        self.last_functional_fan_frame = 0
        self.events_prev = []
        self.current_alert_types = [ET.PERMANENT]

        self.sm['liveCalibration'].calStatus = Calibration.CALIBRATED
        self.sm['thermal'].freeSpace = 1.
        self.sm['dMonitoringState'].events = []
        self.sm['dMonitoringState'].awarenessStatus = 1.
        self.sm['dMonitoringState'].faceDetected = False

        self.startup_event = get_startup_event(car_recognized,
                                               controller_available)

        if not sounds_available:
            self.events.add(EventName.soundsUnavailable, static=True)
        if internet_needed:
            self.events.add(EventName.internetConnectivityNeeded, static=True)
        if community_feature_disallowed:
            self.events.add(EventName.communityFeatureDisallowed, static=True)
        if not car_recognized:
            self.events.add(EventName.carUnrecognized, static=True)

        # controlsd is driven by can recv, expected at 100Hz
        self.rk = Ratekeeper(100, print_delay_threshold=None)
        self.prof = Profiler(False)  # off by default

    def update_events(self, CS):
        """Compute carEvents from carState"""

        self.events.clear()
        self.events.add_from_msg(CS.events)
        self.events.add_from_msg(self.sm['dMonitoringState'].events)

        # Handle startup event
        if self.startup_event is not None:
            self.events.add(self.startup_event)
            self.startup_event = None

        # Create events for battery, temperature, disk space, and memory
        if self.sm['thermal'].batteryPercent < 1 and self.sm[
                'thermal'].chargingError:
            # at zero percent battery, while discharging, OP should not allowed
            self.events.add(EventName.lowBattery)
        if self.sm['thermal'].thermalStatus >= ThermalStatus.red:
            self.events.add(EventName.overheat)
        if self.sm['thermal'].freeSpace < 0.07:
            # under 7% of space free no enable allowed
            self.events.add(EventName.outOfSpace)
        if self.sm['thermal'].memUsedPercent > 90:
            self.events.add(EventName.lowMemory)

        # Alert if fan isn't spinning for 5 seconds
        if self.sm['health'].hwType in [HwType.uno, HwType.dos]:
            if self.sm['health'].fanSpeedRpm == 0 and self.sm[
                    'thermal'].fanSpeed > 50:
                if (self.sm.frame -
                        self.last_functional_fan_frame) * DT_CTRL > 5.0:
                    self.events.add(EventName.fanMalfunction)
            else:
                self.last_functional_fan_frame = self.sm.frame

        # Handle calibration status
        cal_status = self.sm['liveCalibration'].calStatus
        if cal_status != Calibration.CALIBRATED:
            if cal_status == Calibration.UNCALIBRATED:
                self.events.add(EventName.calibrationIncomplete)
            else:
                self.events.add(EventName.calibrationInvalid)

        # Handle lane change
        if self.sm[
                'pathPlan'].laneChangeState == LaneChangeState.preLaneChange:
            direction = self.sm['pathPlan'].laneChangeDirection
            if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
               (CS.rightBlindspot and direction == LaneChangeDirection.right):
                self.events.add(EventName.laneChangeBlocked)
            else:
                if direction == LaneChangeDirection.left:
                    self.events.add(EventName.preLaneChangeLeft)
                else:
                    self.events.add(EventName.preLaneChangeRight)
        elif self.sm['pathPlan'].laneChangeState in [
                LaneChangeState.laneChangeStarting,
                LaneChangeState.laneChangeFinishing
        ]:
            self.events.add(EventName.laneChange)

        if self.can_rcv_error or (not CS.canValid
                                  and self.sm.frame > 5 / DT_CTRL):
            self.events.add(EventName.canError)
        if self.mismatch_counter >= 200:
            self.events.add(EventName.controlsMismatch)
        if not self.sm.alive['plan'] and self.sm.alive['pathPlan']:
            # only plan not being received: radar not communicating
            self.events.add(EventName.radarCommIssue)
        elif not self.sm.all_alive_and_valid():
            self.events.add(EventName.commIssue)
        if not self.sm['pathPlan'].mpcSolutionValid:
            self.events.add(EventName.plannerError)
        if not self.sm['liveLocationKalman'].sensorsOK and not NOSENSOR:
            if self.sm.frame > 5 / DT_CTRL:  # Give locationd some time to receive all the inputs
                self.events.add(EventName.sensorDataInvalid)
        if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled
                                                        > 1000):
            # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes
            if not (SIMULATION or NOSENSOR):  # TODO: send GPS in carla
                self.events.add(EventName.noGps)
        if not self.sm['pathPlan'].paramsValid:
            self.events.add(EventName.vehicleModelInvalid)
        if not self.sm['liveLocationKalman'].posenetOK:
            self.events.add(EventName.posenetInvalid)
        if not self.sm['liveLocationKalman'].deviceStable:
            self.events.add(EventName.deviceFalling)
        if not self.sm['plan'].radarValid:
            self.events.add(EventName.radarFault)
        if self.sm['plan'].radarCanError:
            self.events.add(EventName.radarCanError)
        if log.HealthData.FaultType.relayMalfunction in self.sm[
                'health'].faults:
            self.events.add(EventName.relayMalfunction)
        if self.sm['plan'].fcw:
            self.events.add(EventName.fcw)
        if self.sm['model'].frameDropPerc > 1 and not SIMULATION:
            self.events.add(EventName.modeldLagging)
        if not self.sm.alive['frontFrame'] and (
                self.sm.frame > 5 / DT_CTRL) and not SIMULATION:
            self.events.add(EventName.cameraMalfunction)

        # Only allow engagement with brake pressed when stopped behind another stopped car
        if CS.brakePressed and self.sm['plan'].vTargetFuture >= STARTING_TARGET_SPEED \
          and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3:
            self.events.add(EventName.noTarget)

    def data_sample(self):
        """Receive data from sockets and update carState"""

        # Update carState from CAN
        can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
        CS = self.CI.update(self.CC, can_strs)

        self.sm.update(0)

        # Check for CAN timeout
        if not can_strs:
            self.can_error_counter += 1
            self.can_rcv_error = True
        else:
            self.can_rcv_error = False

        # When the panda and controlsd do not agree on controls_allowed
        # we want to disengage openpilot. However the status from the panda goes through
        # another socket other than the CAN messages and one can arrive earlier than the other.
        # Therefore we allow a mismatch for two samples, then we trigger the disengagement.
        if not self.enabled:
            self.mismatch_counter = 0

        if not self.sm['health'].controlsAllowed and self.enabled:
            self.mismatch_counter += 1

        self.distance_traveled += CS.vEgo * DT_CTRL

        return CS

    def state_transition(self, CS):
        """Compute conditional state transitions and execute actions on state transitions"""

        self.v_cruise_kph_last = self.v_cruise_kph

        # if stock cruise is completely disabled, then we can use our own set speed logic
        if not self.CP.enableCruise:
            self.v_cruise_kph = update_v_cruise(self.v_cruise_kph,
                                                CS.buttonEvents, self.enabled)
        elif self.CP.enableCruise and CS.cruiseState.enabled:
            self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH

        # decrease the soft disable timer at every step, as it's reset on
        # entrance in SOFT_DISABLING state
        self.soft_disable_timer = max(0, self.soft_disable_timer - 1)

        self.current_alert_types = [ET.PERMANENT]

        # ENABLED, PRE ENABLING, SOFT DISABLING
        if self.state != State.disabled:
            # user and immediate disable always have priority in a non-disabled state
            if self.events.any(ET.USER_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.USER_DISABLE)

            elif self.events.any(ET.IMMEDIATE_DISABLE):
                self.state = State.disabled
                self.current_alert_types.append(ET.IMMEDIATE_DISABLE)

            else:
                # ENABLED
                if self.state == State.enabled:
                    if self.events.any(ET.SOFT_DISABLE):
                        self.state = State.softDisabling
                        self.soft_disable_timer = 300  # 3s
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                # SOFT DISABLING
                elif self.state == State.softDisabling:
                    if not self.events.any(ET.SOFT_DISABLE):
                        # no more soft disabling condition, so go back to ENABLED
                        self.state = State.enabled

                    elif self.events.any(
                            ET.SOFT_DISABLE) and self.soft_disable_timer > 0:
                        self.current_alert_types.append(ET.SOFT_DISABLE)

                    elif self.soft_disable_timer <= 0:
                        self.state = State.disabled

                # PRE ENABLING
                elif self.state == State.preEnabled:
                    if not self.events.any(ET.PRE_ENABLE):
                        self.state = State.enabled
                    else:
                        self.current_alert_types.append(ET.PRE_ENABLE)

        # DISABLED
        elif self.state == State.disabled:
            if self.events.any(ET.ENABLE):
                if self.events.any(ET.NO_ENTRY):
                    self.current_alert_types.append(ET.NO_ENTRY)

                else:
                    if self.events.any(ET.PRE_ENABLE):
                        self.state = State.preEnabled
                    else:
                        self.state = State.enabled
                    self.current_alert_types.append(ET.ENABLE)
                    self.v_cruise_kph = initialize_v_cruise(
                        CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)

        # Check if actuators are enabled
        self.active = self.state == State.enabled or self.state == State.softDisabling
        if self.active:
            self.current_alert_types.append(ET.WARNING)

        # Check if openpilot is engaged
        self.enabled = self.active or self.state == State.preEnabled

    def state_control(self, CS):
        """Given the state, this function returns an actuators packet"""

        plan = self.sm['plan']
        path_plan = self.sm['pathPlan']

        actuators = car.CarControl.Actuators.new_message()

        if CS.leftBlinker or CS.rightBlinker:
            self.last_blinker_frame = self.sm.frame

        # State specific actions

        if not self.active:
            self.LaC.reset()
            self.LoC.reset(v_pid=CS.vEgo)

        plan_age = DT_CTRL * (self.sm.frame - self.sm.rcv_frame['plan'])
        # no greater than dt mpc + dt, to prevent too high extraps
        dt = min(plan_age, LON_MPC_STEP + DT_CTRL) + DT_CTRL

        a_acc_sol = plan.aStart + (dt / LON_MPC_STEP) * (plan.aTarget -
                                                         plan.aStart)
        v_acc_sol = plan.vStart + dt * (a_acc_sol + plan.aStart) / 2.0

        # Gas/Brake PID loop
        actuators.gas, actuators.brake = self.LoC.update(
            self.active, CS, v_acc_sol, plan.vTargetFuture, a_acc_sol, self.CP)
        # Steering PID loop and lateral MPC
        actuators.steer, actuators.steerAngle, lac_log = self.LaC.update(
            self.active, CS, self.CP, path_plan)

        # Check for difference between desired angle and angle for angle based control
        angle_control_saturated = self.CP.steerControlType == car.CarParams.SteerControlType.angle and \
          abs(actuators.steerAngle - CS.steeringAngle) > STEER_ANGLE_SATURATION_THRESHOLD

        if angle_control_saturated and not CS.steeringPressed and self.active:
            self.saturated_count += 1
        else:
            self.saturated_count = 0

        # Send a "steering required alert" if saturation count has reached the limit
        if (lac_log.saturated and not CS.steeringPressed) or \
           (self.saturated_count > STEER_ANGLE_SATURATION_TIMEOUT):
            # Check if we deviated from the path
            left_deviation = actuators.steer > 0 and path_plan.dPoly[3] > 0.1
            right_deviation = actuators.steer < 0 and path_plan.dPoly[3] < -0.1

            if left_deviation or right_deviation:
                self.events.add(EventName.steerSaturated)

        return actuators, v_acc_sol, a_acc_sol, lac_log

    def publish_logs(self, CS, start_time, actuators, v_acc, a_acc, lac_log):
        """Send actuators and hud commands to the car, send controlsstate and MPC logging"""

        CC = car.CarControl.new_message()
        CC.enabled = self.enabled
        CC.actuators = actuators

        CC.cruiseControl.override = True
        CC.cruiseControl.cancel = not self.CP.enableCruise or (
            not self.enabled and CS.cruiseState.enabled)

        # Some override values for Honda
        # brake discount removes a sharp nonlinearity
        brake_discount = (1.0 - clip(actuators.brake * 3., 0.0, 1.0))
        speed_override = max(0.0,
                             (self.LoC.v_pid + CS.cruiseState.speedOffset) *
                             brake_discount)
        CC.cruiseControl.speedOverride = float(
            speed_override if self.CP.enableCruise else 0.0)
        CC.cruiseControl.accelOverride = self.CI.calc_accel_override(
            CS.aEgo, self.sm['plan'].aTarget, CS.vEgo, self.sm['plan'].vTarget)

        CC.hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS)
        CC.hudControl.speedVisible = self.enabled
        CC.hudControl.lanesVisible = self.enabled
        CC.hudControl.leadVisible = self.sm['plan'].hasLead

        right_lane_visible = self.sm['pathPlan'].rProb > 0.5
        left_lane_visible = self.sm['pathPlan'].lProb > 0.5
        CC.hudControl.rightLaneVisible = bool(right_lane_visible)
        CC.hudControl.leftLaneVisible = bool(left_lane_visible)

        recent_blinker = (self.sm.frame - self.last_blinker_frame
                          ) * DT_CTRL < 5.0  # 5s blinker cooldown
        ldw_allowed = self.is_ldw_enabled and CS.vEgo > LDW_MIN_SPEED and not recent_blinker \
                        and not self.active and self.sm['liveCalibration'].calStatus == Calibration.CALIBRATED

        meta = self.sm['model'].meta
        if len(meta.desirePrediction) and ldw_allowed:
            l_lane_change_prob = meta.desirePrediction[Desire.laneChangeLeft -
                                                       1]
            r_lane_change_prob = meta.desirePrediction[Desire.laneChangeRight -
                                                       1]
            l_lane_close = left_lane_visible and (self.sm['pathPlan'].lPoly[3]
                                                  < (1.08 - CAMERA_OFFSET))
            r_lane_close = right_lane_visible and (self.sm['pathPlan'].rPoly[3]
                                                   > -(1.08 + CAMERA_OFFSET))

            CC.hudControl.leftLaneDepart = bool(
                l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
            CC.hudControl.rightLaneDepart = bool(
                r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)

        if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
            self.events.add(EventName.ldw)

        clear_event = ET.WARNING if ET.WARNING not in self.current_alert_types else None
        alerts = self.events.create_alerts(self.current_alert_types,
                                           [self.CP, self.sm, self.is_metric])
        self.AM.add_many(self.sm.frame, alerts, self.enabled)
        self.AM.process_alerts(self.sm.frame, clear_event)
        CC.hudControl.visualAlert = self.AM.visual_alert

        if not self.read_only:
            # send car controls over can
            can_sends = self.CI.apply(CC)
            self.pm.send(
                'sendcan',
                can_list_to_can_capnp(can_sends,
                                      msgtype='sendcan',
                                      valid=CS.canValid))

        force_decel = (self.sm['dMonitoringState'].awarenessStatus < 0.) or \
                      (self.state == State.softDisabling)

        steer_angle_rad = (CS.steeringAngle -
                           self.sm['pathPlan'].angleOffset) * CV.DEG_TO_RAD

        # controlsState
        dat = messaging.new_message('controlsState')
        dat.valid = CS.canValid
        controlsState = dat.controlsState
        controlsState.alertText1 = self.AM.alert_text_1
        controlsState.alertText2 = self.AM.alert_text_2
        controlsState.alertSize = self.AM.alert_size
        controlsState.alertStatus = self.AM.alert_status
        controlsState.alertBlinkingRate = self.AM.alert_rate
        controlsState.alertType = self.AM.alert_type
        controlsState.alertSound = self.AM.audible_alert
        controlsState.driverMonitoringOn = self.sm[
            'dMonitoringState'].faceDetected
        controlsState.canMonoTimes = list(CS.canMonoTimes)
        controlsState.planMonoTime = self.sm.logMonoTime['plan']
        controlsState.pathPlanMonoTime = self.sm.logMonoTime['pathPlan']
        controlsState.enabled = self.enabled
        controlsState.active = self.active
        controlsState.vEgo = CS.vEgo
        controlsState.vEgoRaw = CS.vEgoRaw
        controlsState.angleSteers = CS.steeringAngle
        controlsState.curvature = self.VM.calc_curvature(
            steer_angle_rad, CS.vEgo)
        controlsState.steerOverride = CS.steeringPressed
        controlsState.state = self.state
        controlsState.engageable = not self.events.any(ET.NO_ENTRY)
        controlsState.longControlState = self.LoC.long_control_state
        controlsState.vPid = float(self.LoC.v_pid)
        controlsState.vCruise = float(self.v_cruise_kph)
        controlsState.upAccelCmd = float(self.LoC.pid.p)
        controlsState.uiAccelCmd = float(self.LoC.pid.i)
        controlsState.ufAccelCmd = float(self.LoC.pid.f)
        controlsState.angleSteersDes = float(self.LaC.angle_steers_des)
        controlsState.vTargetLead = float(v_acc)
        controlsState.aTarget = float(a_acc)
        controlsState.jerkFactor = float(self.sm['plan'].jerkFactor)
        controlsState.gpsPlannerActive = self.sm['plan'].gpsPlannerActive
        controlsState.vCurvature = self.sm['plan'].vCurvature
        controlsState.decelForModel = self.sm[
            'plan'].longitudinalPlanSource == LongitudinalPlanSource.model
        controlsState.cumLagMs = -self.rk.remaining * 1000.
        controlsState.startMonoTime = int(start_time * 1e9)
        controlsState.mapValid = self.sm['plan'].mapValid
        controlsState.forceDecel = bool(force_decel)
        controlsState.canErrorCounter = self.can_error_counter

        if self.CP.lateralTuning.which() == 'pid':
            controlsState.lateralControlState.pidState = lac_log
        elif self.CP.lateralTuning.which() == 'lqr':
            controlsState.lateralControlState.lqrState = lac_log
        elif self.CP.lateralTuning.which() == 'indi':
            controlsState.lateralControlState.indiState = lac_log
        self.pm.send('controlsState', dat)

        # carState
        car_events = self.events.to_msg()
        cs_send = messaging.new_message('carState')
        cs_send.valid = CS.canValid
        cs_send.carState = CS
        cs_send.carState.events = car_events
        self.pm.send('carState', cs_send)

        # carEvents - logged every second or on change
        if (self.sm.frame % int(1. / DT_CTRL)
                == 0) or (self.events.names != self.events_prev):
            ce_send = messaging.new_message('carEvents', len(self.events))
            ce_send.carEvents = car_events
            self.pm.send('carEvents', ce_send)
        self.events_prev = self.events.names.copy()

        # 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.carParams = self.CP
            self.pm.send('carParams', cp_send)

        # carControl
        cc_send = messaging.new_message('carControl')
        cc_send.valid = CS.canValid
        cc_send.carControl = CC
        self.pm.send('carControl', cc_send)

        # copy CarControl to pass to CarInterface on the next iteration
        self.CC = CC

    def step(self):
        start_time = sec_since_boot()
        self.prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data from sockets and get a carState
        CS = self.data_sample()
        self.prof.checkpoint("Sample")

        self.update_events(CS)

        if not self.read_only:
            # Update control state
            self.state_transition(CS)
            self.prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, v_acc, a_acc, lac_log = self.state_control(CS)

        self.prof.checkpoint("State Control")

        # Publish data
        self.publish_logs(CS, start_time, actuators, v_acc, a_acc, lac_log)
        self.prof.checkpoint("Sent")

    def controlsd_thread(self):
        while True:
            self.step()
            self.rk.monitor_time()
            self.prof.display()
Example #19
0
def controlsd_thread(sm=None, pm=None, can_sock=None):
    gc.disable()

    # start the loop
    set_realtime_priority(3)

    params = Params()

    is_metric = params.get("IsMetric", encoding='utf8') == "1"
    is_ldw_enabled = params.get("IsLdwEnabled", encoding='utf8') == "1"
    passive = params.get("Passive", encoding='utf8') == "1"
    openpilot_enabled_toggle = params.get("OpenpilotEnabledToggle",
                                          encoding='utf8') == "1"
    community_feature_toggle = params.get("CommunityFeaturesToggle",
                                          encoding='utf8') == "1"

    passive = passive or not openpilot_enabled_toggle

    # Pub/Sub Sockets
    if pm is None:
        pm = messaging.PubMaster([
            'sendcan', 'controlsState', 'carState', 'carControl', 'carEvents',
            'carParams'
        ])

    if sm is None:
        sm = messaging.SubMaster(['thermal', 'health', 'liveCalibration', 'dMonitoringState', 'plan', 'pathPlan', \
                                  'model'])

    if can_sock is None:
        can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 100
        can_sock = messaging.sub_sock('can', timeout=can_timeout)

    # wait for health and CAN packets
    hw_type = messaging.recv_one(sm.sock['health']).health.hwType
    has_relay = hw_type in [HwType.blackPanda, HwType.uno]
    print("Waiting for CAN messages...")
    messaging.get_one_can(can_sock)

    CI, CP = get_car(can_sock, pm.sock['sendcan'], has_relay)

    car_recognized = CP.carName != 'mock'
    # If stock camera is disconnected, we loaded car controls and it's not chffrplus
    controller_available = CP.enableCamera and CI.CC is not None and not passive
    community_feature_disallowed = CP.communityFeature and not community_feature_toggle
    read_only = not car_recognized or not controller_available or CP.dashcamOnly or community_feature_disallowed
    if read_only:
        CP.safetyModel = car.CarParams.SafetyModel.noOutput

    # Write CarParams for radard and boardd safety mode
    cp_bytes = CP.to_bytes()
    params.put("CarParams", cp_bytes)
    params.put("CarParamsCache", cp_bytes)
    params.put("LongitudinalControl",
               "1" if CP.openpilotLongitudinalControl else "0")

    CC = car.CarControl.new_message()
    AM = AlertManager()

    startup_alert = get_startup_alert(car_recognized, controller_available)
    AM.add(sm.frame, startup_alert, False)

    LoC = LongControl(CP, CI.compute_gb)
    VM = VehicleModel(CP)

    if CP.lateralTuning.which() == 'pid':
        LaC = LatControlPID(CP)
    elif CP.lateralTuning.which() == 'indi':
        LaC = LatControlINDI(CP)
    elif CP.lateralTuning.which() == 'lqr':
        LaC = LatControlLQR(CP)

    state = State.disabled
    soft_disable_timer = 0
    v_cruise_kph = 255
    v_cruise_kph_last = 0
    mismatch_counter = 0
    can_error_counter = 0
    last_blinker_frame = 0
    events_prev = []

    sm['liveCalibration'].calStatus = Calibration.INVALID
    sm['pathPlan'].sensorValid = True
    sm['pathPlan'].posenetValid = True
    sm['thermal'].freeSpace = 1.
    sm['dMonitoringState'].events = []
    sm['dMonitoringState'].awarenessStatus = 1.
    sm['dMonitoringState'].faceDetected = False

    # detect sound card presence
    sounds_available = not os.path.isfile('/EON') or (
        os.path.isdir('/proc/asound/card0')
        and open('/proc/asound/card0/state').read().strip() == 'ONLINE')

    # controlsd is driven by can recv, expected at 100Hz
    rk = Ratekeeper(100, print_delay_threshold=None)

    internet_needed = params.get("Offroad_ConnectivityNeeded",
                                 encoding='utf8') is not None

    prof = Profiler(False)  # off by default

    while True:
        start_time = sec_since_boot()
        prof.checkpoint("Ratekeeper", ignore=True)

        # Sample data and compute car events
        CS, events, cal_perc, mismatch_counter, can_error_counter = data_sample(
            CI, CC, sm, can_sock, state, mismatch_counter, can_error_counter,
            params)
        prof.checkpoint("Sample")

        # Create alerts
        if not sm.alive['plan'] and sm.alive[
                'pathPlan']:  # only plan not being received: radar not communicating
            events.append(
                create_event('radarCommIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        elif not sm.all_alive_and_valid():
            events.append(
                create_event('commIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if not sm['pathPlan'].mpcSolutionValid:
            events.append(
                create_event('plannerError',
                             [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
        if not sm['pathPlan'].sensorValid:
            events.append(
                create_event('sensorDataInvalid', [ET.NO_ENTRY, ET.PERMANENT]))
        if not sm['pathPlan'].paramsValid:
            events.append(create_event('vehicleModelInvalid', [ET.WARNING]))
        if not sm['pathPlan'].posenetValid:
            events.append(
                create_event('posenetInvalid', [ET.NO_ENTRY, ET.WARNING]))
        if not sm['plan'].radarValid:
            events.append(
                create_event('radarFault', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if sm['plan'].radarCanError:
            events.append(
                create_event('radarCanError', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
        if not CS.canValid:
            events.append(
                create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
        if not sounds_available:
            events.append(
                create_event('soundsUnavailable', [ET.NO_ENTRY, ET.PERMANENT]))
#    if internet_needed:
#      events.append(create_event('internetConnectivityNeeded', [ET.NO_ENTRY, ET.PERMANENT]))
#    if community_feature_disallowed:
#      events.append(create_event('communityFeatureDisallowed', [ET.PERMANENT]))
        if read_only and not passive:
            events.append(create_event('carUnrecognized', [ET.PERMANENT]))

        # Only allow engagement with brake pressed when stopped behind another stopped car


#    if CS.brakePressed and sm['plan'].vTargetFuture >= STARTING_TARGET_SPEED and not CP.radarOffCan and CS.vEgo < 0.3:
#      events.append(create_event('noTarget', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))

        if not read_only:
            # update control state
            state, soft_disable_timer, v_cruise_kph, v_cruise_kph_last = \
              state_transition(sm.frame, CS, CP, state, events, soft_disable_timer, v_cruise_kph, AM)
            prof.checkpoint("State transition")

        # Compute actuators (runs PID loops and lateral MPC)
        actuators, v_cruise_kph, v_acc, a_acc, lac_log, last_blinker_frame = \
          state_control(sm.frame, sm.rcv_frame, sm['plan'], sm['pathPlan'], CS, CP, state, events, v_cruise_kph, v_cruise_kph_last, AM, rk,
                        LaC, LoC, read_only, is_metric, cal_perc, last_blinker_frame)

        prof.checkpoint("State Control")

        # Publish data
        CC, events_prev = data_send(sm, pm, CS, CI, CP, VM, state, events,
                                    actuators, v_cruise_kph, rk, AM, LaC, LoC,
                                    read_only, start_time, v_acc, a_acc,
                                    lac_log, events_prev, last_blinker_frame,
                                    is_ldw_enabled, can_error_counter)
        prof.checkpoint("Sent")

        rk.monitor_time()
        prof.display()
Example #20
0
def radard_thread(sm=None, pm=None, can_sock=None):
    config_realtime_process(5 if TICI else 1 if JETSON else 2,
                            Priority.CTRL_LOW)

    # wait for stats about the car to come in from controls
    cloudlog.info("radard is waiting for CarParams")
    CP = car.CarParams.from_bytes(Params().get("CarParams", block=True))
    cloudlog.info("radard got CarParams")

    # import the radar from the fingerprint
    cloudlog.info("radard is importing %s", CP.carName)
    RadarInterface = importlib.import_module(
        'selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface

    # *** setup messaging
    if can_sock is None:
        can_sock = messaging.sub_sock('can')
    if sm is None:
        sm = messaging.SubMaster(
            ['modelV2', 'carState'], ignore_avg_freq=[
                'modelV2', 'carState'
            ])  # Can't check average frequency, since radar determines timing
    if pm is None:
        pm = messaging.PubMaster(['radarState', 'liveTracks'])

    RI = RadarInterface(CP)

    rk = Ratekeeper(1.0 / CP.radarTimeStep, print_delay_threshold=None)
    RD = RadarD(CP.radarTimeStep, RI.delay)

    # TODO: always log leads once we can hide them conditionally
    enable_lead = True  #CP.openpilotLongitudinalControl or not CP.radarOffCan

    while 1:
        can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)
        rr = RI.update(can_strings)

        if rr is None:
            continue

        sm.update(0)

        dat = RD.update(sm, rr, enable_lead)
        dat.radarState.cumLagMs = -rk.remaining * 1000.

        pm.send('radarState', dat)

        # *** publish tracks for UI debugging (keep last) ***
        tracks = RD.tracks
        dat = messaging.new_message('liveTracks', len(tracks))

        for cnt, ids in enumerate(sorted(tracks.keys())):
            dat.liveTracks[cnt] = {
                "trackId": ids,
                "dRel": float(tracks[ids].dRel),
                "yRel": float(tracks[ids].yRel),
                "vRel": float(tracks[ids].vRel),
            }
        pm.send('liveTracks', dat)

        rk.monitor_time()
Example #21
0
def controlsd_thread(gctx=None, rate=100, default_bias=0.):
  gc.disable()

  # start the loop
  set_realtime_priority(3)

  context = zmq.Context()
  params = Params()

  # Pub Sockets
  live100 = messaging.pub_sock(context, service_list['live100'].port)
  carstate = messaging.pub_sock(context, service_list['carState'].port)
  carcontrol = messaging.pub_sock(context, service_list['carControl'].port)
  livempc = messaging.pub_sock(context, service_list['liveMpc'].port)

  is_metric = params.get("IsMetric") == "1"
  passive = params.get("Passive") != "0"

  # No sendcan if passive
  if not passive:
        sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
  else:
    sendcan = None

  # Sub sockets
  poller = zmq.Poller()
  thermal = messaging.sub_sock(context, service_list['thermal'].port, conflate=True, poller=poller)
  health = messaging.sub_sock(context, service_list['health'].port, conflate=True, poller=poller)
  cal = messaging.sub_sock(context, service_list['liveCalibration'].port, conflate=True, poller=poller)
  driver_monitor = messaging.sub_sock(context, service_list['driverMonitoring'].port, conflate=True, poller=poller)
  gps_location = messaging.sub_sock(context, service_list['gpsLocationExternal'].port, conflate=True, poller=poller)
  logcan = messaging.sub_sock(context, service_list['can'].port)

  CC = car.CarControl.new_message()
  CI, CP = get_car(logcan, sendcan, 1.0 if passive else None)

  if CI is None:
    raise Exception("unsupported car")

  # if stock camera is connected, then force passive behavior
  if not CP.enableCamera:
    passive = True
    sendcan = None

  if passive:
    CP.safetyModel = car.CarParams.SafetyModels.noOutput

  # Get FCW toggle from settings
  fcw_enabled = params.get("IsFcwEnabled") == "1"
  geofence = None

  PL = Planner(CP, fcw_enabled)
  LoC = LongControl(CP, CI.compute_gb)
  VM = VehicleModel(CP)
  LaC = LatControl(CP)
  AM = AlertManager()
  driver_status = DriverStatus()

  if not passive:
    AM.add("startup", False)

  # Write CarParams for radard and boardd safety mode
  params.put("CarParams", CP.to_bytes())

  state = State.disabled
  soft_disable_timer = 0
  v_cruise_kph = 255
  v_cruise_kph_last = 0
  overtemp = False
  free_space = False
  cal_status = Calibration.INVALID
  cal_perc = 0
  mismatch_counter = 0
  low_battery = False

  rk = Ratekeeper(rate, print_delay_threshold=2./1000)

  # Read angle offset from previous drive, fallback to default
  angle_offset = default_bias
  calibration_params = params.get("CalibrationParams")
  if calibration_params:
    try:
      calibration_params = json.loads(calibration_params)
      angle_offset = calibration_params["angle_offset2"]
    except (ValueError, KeyError):
      pass

  prof = Profiler(False)  # off by default

  while True:
    prof.checkpoint("Ratekeeper", ignore=True)

    # Sample data and compute car events
    CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter = data_sample(CI, CC, thermal, cal, health,
      driver_monitor, gps_location, poller, cal_status, cal_perc, overtemp, free_space, low_battery, driver_status, geofence, state, mismatch_counter, params)
    prof.checkpoint("Sample")

    # Define longitudinal plan (MPC)
    plan, plan_ts = calc_plan(CS, CP, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence)
    prof.checkpoint("Plan")

    if not passive:
      # update control state
      state, soft_disable_timer, v_cruise_kph, v_cruise_kph_last = \
        state_transition(CS, CP, state, events, soft_disable_timer, v_cruise_kph, AM)
      prof.checkpoint("State transition")

    # Compute actuators (runs PID loops and lateral MPC)
    actuators, v_cruise_kph, driver_status, angle_offset = state_control(plan, CS, CP, state, events, v_cruise_kph,
      v_cruise_kph_last, AM, rk, driver_status, PL, LaC, LoC, VM, angle_offset, passive, is_metric, cal_perc)
    prof.checkpoint("State Control")

    # Publish data
    CC = data_send(PL.perception_state, plan, plan_ts, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, carstate, carcontrol,
      live100, livempc, AM, driver_status, LaC, LoC, angle_offset, passive)
    prof.checkpoint("Sent")

    rk.keep_time()  # Run at 100Hz
    prof.display()
Example #22
0
def bridge(q):

  # setup CARLA
  client = carla.Client("127.0.0.1", 2000)
  client.set_timeout(10.0)
  world = client.load_world(args.town)

  if args.low_quality:
    world.unload_map_layer(carla.MapLayer.Foliage)
    world.unload_map_layer(carla.MapLayer.Buildings)
    world.unload_map_layer(carla.MapLayer.ParkedVehicles)
    world.unload_map_layer(carla.MapLayer.Particles)
    world.unload_map_layer(carla.MapLayer.Props)
    world.unload_map_layer(carla.MapLayer.StreetLights)

  blueprint_library = world.get_blueprint_library()

  world_map = world.get_map()

  vehicle_bp = blueprint_library.filter('vehicle.tesla.*')[1]
  spawn_points = world_map.get_spawn_points()
  assert len(spawn_points) > args.num_selected_spawn_point, \
    f'''No spawn point {args.num_selected_spawn_point}, try a value between 0 and
    {len(spawn_points)} for this town.'''
  spawn_point = spawn_points[args.num_selected_spawn_point]
  vehicle = world.spawn_actor(vehicle_bp, spawn_point)

  max_steer_angle = vehicle.get_physics_control().wheels[0].max_steer_angle

  # make tires less slippery
  # wheel_control = carla.WheelPhysicsControl(tire_friction=5)
  physics_control = vehicle.get_physics_control()
  physics_control.mass = 2326
  # physics_control.wheels = [wheel_control]*4
  physics_control.torque_curve = [[20.0, 500.0], [5000.0, 500.0]]
  physics_control.gear_switch_time = 0.0
  vehicle.apply_physics_control(physics_control)

  blueprint = blueprint_library.find('sensor.camera.rgb')
  blueprint.set_attribute('image_size_x', str(W))
  blueprint.set_attribute('image_size_y', str(H))
  blueprint.set_attribute('fov', '70')
  blueprint.set_attribute('sensor_tick', '0.05')
  transform = carla.Transform(carla.Location(x=0.8, z=1.13))
  camera = world.spawn_actor(blueprint, transform, attach_to=vehicle)
  camera.listen(cam_callback)

  # reenable IMU
  imu_bp = blueprint_library.find('sensor.other.imu')
  imu = world.spawn_actor(imu_bp, transform, attach_to=vehicle)
  imu.listen(imu_callback)

  def destroy():
    print("clean exit")
    imu.destroy()
    camera.destroy()
    vehicle.destroy()
    print("done")
  atexit.register(destroy)


  vehicle_state = VehicleState()

  # launch fake car threads
  threading.Thread(target=panda_state_function).start()
  threading.Thread(target=fake_driver_monitoring).start()
  threading.Thread(target=fake_gps).start()
  threading.Thread(target=can_function_runner, args=(vehicle_state,)).start()

  # can loop
  rk = Ratekeeper(100, print_delay_threshold=0.05)

  # init
  throttle_ease_out_counter = REPEAT_COUNTER
  brake_ease_out_counter = REPEAT_COUNTER
  steer_ease_out_counter = REPEAT_COUNTER


  vc = carla.VehicleControl(throttle=0, steer=0, brake=0, reverse=False)

  is_openpilot_engaged = False
  throttle_out = steer_out = brake_out = 0
  throttle_op = steer_op = brake_op = 0
  throttle_manual = steer_manual = brake_manual = 0

  old_steer = old_brake = old_throttle = 0
  throttle_manual_multiplier = 0.7 #keyboard signal is always 1
  brake_manual_multiplier = 0.7 #keyboard signal is always 1
  steer_manual_multiplier = 45 * STEER_RATIO  #keyboard signal is always 1


  while 1:
    # 1. Read the throttle, steer and brake from op or manual controls
    # 2. Set instructions in Carla
    # 3. Send current carstate to op via can

    cruise_button = 0
    throttle_out = steer_out = brake_out = 0
    throttle_op = steer_op = brake_op = 0
    throttle_manual = steer_manual = brake_manual = 0

    # --------------Step 1-------------------------------
    if not q.empty():
      message = q.get()
      m = message.split('_')
      if m[0] == "steer":
        steer_manual = float(m[1])
        is_openpilot_engaged = False
      if m[0] == "throttle":
        throttle_manual = float(m[1])
        is_openpilot_engaged = False
      if m[0] == "brake":
        brake_manual = float(m[1])
        is_openpilot_engaged = False
      if m[0] == "reverse":
        #in_reverse = not in_reverse
        cruise_button = CruiseButtons.CANCEL
        is_openpilot_engaged = False
      if m[0] == "cruise":
        if m[1] == "down":
          cruise_button = CruiseButtons.DECEL_SET
          is_openpilot_engaged = True
        if m[1] == "up":
          cruise_button = CruiseButtons.RES_ACCEL
          is_openpilot_engaged = True
        if m[1] == "cancel":
          cruise_button = CruiseButtons.CANCEL
          is_openpilot_engaged = False

      throttle_out = throttle_manual * throttle_manual_multiplier
      steer_out = steer_manual * steer_manual_multiplier
      brake_out = brake_manual * brake_manual_multiplier

      #steer_out = steer_out
      # steer_out = steer_rate_limit(old_steer, steer_out)
      old_steer = steer_out
      old_throttle = throttle_out
      old_brake = brake_out

      # print('message',old_throttle, old_steer, old_brake)

    if is_openpilot_engaged:
      sm.update(0)
      throttle_op = sm['carControl'].actuators.gas #[0,1]
      brake_op = sm['carControl'].actuators.brake #[0,1]
      steer_op = sm['controlsState'].steeringAngleDesiredDeg # degrees [-180,180]

      throttle_out = throttle_op
      steer_out = steer_op
      brake_out = brake_op

      steer_out = steer_rate_limit(old_steer, steer_out)
      old_steer = steer_out

    else:
      if throttle_out==0 and old_throttle>0:
        if throttle_ease_out_counter>0:
          throttle_out = old_throttle
          throttle_ease_out_counter += -1
        else:
          throttle_ease_out_counter = REPEAT_COUNTER
          old_throttle = 0

      if brake_out==0 and old_brake>0:
        if brake_ease_out_counter>0:
          brake_out = old_brake
          brake_ease_out_counter += -1
        else:
          brake_ease_out_counter = REPEAT_COUNTER
          old_brake = 0

      if steer_out==0 and old_steer!=0:
        if steer_ease_out_counter>0:
          steer_out = old_steer
          steer_ease_out_counter += -1
        else:
          steer_ease_out_counter = REPEAT_COUNTER
          old_steer = 0

    # --------------Step 2-------------------------------

    steer_carla = steer_out / (max_steer_angle * STEER_RATIO * -1)

    steer_carla = np.clip(steer_carla, -1,1)
    steer_out = steer_carla * (max_steer_angle * STEER_RATIO * -1)
    old_steer = steer_carla * (max_steer_angle * STEER_RATIO * -1)

    vc.throttle = throttle_out/0.6
    vc.steer = steer_carla
    vc.brake = brake_out
    vehicle.apply_control(vc)

    # --------------Step 3-------------------------------
    vel = vehicle.get_velocity()
    speed = math.sqrt(vel.x**2 + vel.y**2 + vel.z**2) # in m/s
    vehicle_state.speed = speed
    vehicle_state.angle = steer_out
    vehicle_state.cruise_button = cruise_button
    vehicle_state.is_engaged = is_openpilot_engaged

    if rk.frame%PRINT_DECIMATION == 0:
      print("frame: ", "engaged:", is_openpilot_engaged, "; throttle: ", round(vc.throttle, 3), "; steer(c/deg): ", round(vc.steer, 3), round(steer_out, 3), "; brake: ", round(vc.brake, 3))

    rk.keep_time()
Example #23
0
  def _run(self, q: Queue):
    client = connect_carla_client()
    world = client.load_world(self._args.town)

    settings = world.get_settings()
    settings.synchronous_mode = True  # Enables synchronous mode
    settings.fixed_delta_seconds = 0.05
    world.apply_settings(settings)

    world.set_weather(carla.WeatherParameters.ClearSunset)

    if not self._args.high_quality:
      world.unload_map_layer(carla.MapLayer.Foliage)
      world.unload_map_layer(carla.MapLayer.Buildings)
      world.unload_map_layer(carla.MapLayer.ParkedVehicles)
      world.unload_map_layer(carla.MapLayer.Props)
      world.unload_map_layer(carla.MapLayer.StreetLights)
      world.unload_map_layer(carla.MapLayer.Particles)

    blueprint_library = world.get_blueprint_library()

    world_map = world.get_map()

    vehicle_bp = blueprint_library.filter('vehicle.tesla.*')[1]
    spawn_points = world_map.get_spawn_points()
    assert len(spawn_points) > self._args.num_selected_spawn_point, f'''No spawn point {self._args.num_selected_spawn_point}, try a value between 0 and
      {len(spawn_points)} for this town.'''
    spawn_point = spawn_points[self._args.num_selected_spawn_point]
    vehicle = world.spawn_actor(vehicle_bp, spawn_point)
    self._carla_objects.append(vehicle)
    max_steer_angle = vehicle.get_physics_control().wheels[0].max_steer_angle

    # make tires less slippery
    # wheel_control = carla.WheelPhysicsControl(tire_friction=5)
    physics_control = vehicle.get_physics_control()
    physics_control.mass = 2326
    # physics_control.wheels = [wheel_control]*4
    physics_control.torque_curve = [[20.0, 500.0], [5000.0, 500.0]]
    physics_control.gear_switch_time = 0.0
    vehicle.apply_physics_control(physics_control)

    transform = carla.Transform(carla.Location(x=0.8, z=1.13))

    def create_camera(fov, callback):
      blueprint = blueprint_library.find('sensor.camera.rgb')
      blueprint.set_attribute('image_size_x', str(W))
      blueprint.set_attribute('image_size_y', str(H))
      blueprint.set_attribute('fov', str(fov))
      if not self._args.high_quality:
        blueprint.set_attribute('enable_postprocess_effects', 'False')
      camera = world.spawn_actor(blueprint, transform, attach_to=vehicle)
      camera.listen(callback)
      return camera

    self._camerad = Camerad()

    if self._args.dual_camera:
      road_camera = create_camera(fov=40, callback=self._camerad.cam_callback_road)
      self._carla_objects.append(road_camera)

    road_wide_camera = create_camera(fov=120, callback=self._camerad.cam_callback_wide_road)  # fov bigger than 120 shows unwanted artifacts
    self._carla_objects.append(road_wide_camera)

    vehicle_state = VehicleState()

    # reenable IMU
    imu_bp = blueprint_library.find('sensor.other.imu')
    imu = world.spawn_actor(imu_bp, transform, attach_to=vehicle)
    imu.listen(lambda imu: imu_callback(imu, vehicle_state))

    gps_bp = blueprint_library.find('sensor.other.gnss')
    gps = world.spawn_actor(gps_bp, transform, attach_to=vehicle)
    gps.listen(lambda gps: gps_callback(gps, vehicle_state))

    self._carla_objects.extend([imu, gps])
    # launch fake car threads
    self._threads.append(threading.Thread(target=panda_state_function, args=(vehicle_state, self._exit_event,)))
    self._threads.append(threading.Thread(target=peripheral_state_function, args=(self._exit_event,)))
    self._threads.append(threading.Thread(target=fake_driver_monitoring, args=(self._exit_event,)))
    self._threads.append(threading.Thread(target=can_function_runner, args=(vehicle_state, self._exit_event,)))
    for t in self._threads:
      t.start()

    # init
    throttle_ease_out_counter = REPEAT_COUNTER
    brake_ease_out_counter = REPEAT_COUNTER
    steer_ease_out_counter = REPEAT_COUNTER

    vc = carla.VehicleControl(throttle=0, steer=0, brake=0, reverse=False)

    is_openpilot_engaged = False
    throttle_out = steer_out = brake_out = 0.
    throttle_op = steer_op = brake_op = 0.
    throttle_manual = steer_manual = brake_manual = 0.

    old_steer = old_brake = old_throttle = 0.
    throttle_manual_multiplier = 0.7  # keyboard signal is always 1
    brake_manual_multiplier = 0.7  # keyboard signal is always 1
    steer_manual_multiplier = 45 * STEER_RATIO  # keyboard signal is always 1

    # Simulation tends to be slow in the initial steps. This prevents lagging later
    for _ in range(20):
      world.tick()

    # loop
    rk = Ratekeeper(100, print_delay_threshold=0.05)

    while self._keep_alive:
      # 1. Read the throttle, steer and brake from op or manual controls
      # 2. Set instructions in Carla
      # 3. Send current carstate to op via can

      cruise_button = 0
      throttle_out = steer_out = brake_out = 0.0
      throttle_op = steer_op = brake_op = 0.0
      throttle_manual = steer_manual = brake_manual = 0.0

      # --------------Step 1-------------------------------
      if not q.empty():
        message = q.get()
        m = message.split('_')
        if m[0] == "steer":
          steer_manual = float(m[1])
          is_openpilot_engaged = False
        elif m[0] == "throttle":
          throttle_manual = float(m[1])
          is_openpilot_engaged = False
        elif m[0] == "brake":
          brake_manual = float(m[1])
          is_openpilot_engaged = False
        elif m[0] == "reverse":
          cruise_button = CruiseButtons.CANCEL
          is_openpilot_engaged = False
        elif m[0] == "cruise":
          if m[1] == "down":
            cruise_button = CruiseButtons.DECEL_SET
            is_openpilot_engaged = True
          elif m[1] == "up":
            cruise_button = CruiseButtons.RES_ACCEL
            is_openpilot_engaged = True
          elif m[1] == "cancel":
            cruise_button = CruiseButtons.CANCEL
            is_openpilot_engaged = False
        elif m[0] == "ignition":
          vehicle_state.ignition = not vehicle_state.ignition
        elif m[0] == "quit":
          break

        throttle_out = throttle_manual * throttle_manual_multiplier
        steer_out = steer_manual * steer_manual_multiplier
        brake_out = brake_manual * brake_manual_multiplier

        old_steer = steer_out
        old_throttle = throttle_out
        old_brake = brake_out

      if is_openpilot_engaged:
        sm.update(0)

        # TODO gas and brake is deprecated
        throttle_op = clip(sm['carControl'].actuators.accel / 1.6, 0.0, 1.0)
        brake_op = clip(-sm['carControl'].actuators.accel / 4.0, 0.0, 1.0)
        steer_op = sm['carControl'].actuators.steeringAngleDeg

        throttle_out = throttle_op
        steer_out = steer_op
        brake_out = brake_op

        steer_out = steer_rate_limit(old_steer, steer_out)
        old_steer = steer_out

      else:
        if throttle_out == 0 and old_throttle > 0:
          if throttle_ease_out_counter > 0:
            throttle_out = old_throttle
            throttle_ease_out_counter += -1
          else:
            throttle_ease_out_counter = REPEAT_COUNTER
            old_throttle = 0

        if brake_out == 0 and old_brake > 0:
          if brake_ease_out_counter > 0:
            brake_out = old_brake
            brake_ease_out_counter += -1
          else:
            brake_ease_out_counter = REPEAT_COUNTER
            old_brake = 0

        if steer_out == 0 and old_steer != 0:
          if steer_ease_out_counter > 0:
            steer_out = old_steer
            steer_ease_out_counter += -1
          else:
            steer_ease_out_counter = REPEAT_COUNTER
            old_steer = 0

      # --------------Step 2-------------------------------
      steer_carla = steer_out / (max_steer_angle * STEER_RATIO * -1)

      steer_carla = np.clip(steer_carla, -1, 1)
      steer_out = steer_carla * (max_steer_angle * STEER_RATIO * -1)
      old_steer = steer_carla * (max_steer_angle * STEER_RATIO * -1)

      vc.throttle = throttle_out / 0.6
      vc.steer = steer_carla
      vc.brake = brake_out
      vehicle.apply_control(vc)

      # --------------Step 3-------------------------------
      vel = vehicle.get_velocity()
      speed = math.sqrt(vel.x ** 2 + vel.y ** 2 + vel.z ** 2)  # in m/s
      vehicle_state.speed = speed
      vehicle_state.vel = vel
      vehicle_state.angle = steer_out
      vehicle_state.cruise_button = cruise_button
      vehicle_state.is_engaged = is_openpilot_engaged

      if rk.frame % PRINT_DECIMATION == 0:
        print("frame: ", "engaged:", is_openpilot_engaged, "; throttle: ", round(vc.throttle, 3), "; steer(c/deg): ",
              round(vc.steer, 3), round(steer_out, 3), "; brake: ", round(vc.brake, 3))

      if rk.frame % 5 == 0:
        world.tick()
      rk.keep_time()
      self.started = True