Exemple #1
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False
        self.pre_auto_LCA_timer = 0.0
        self.lane_change_Blocked = LaneChangeBlocked.off
        self.prev_torque_applied = False

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        lca_left = sm['carState'].leftBlindspot
        lca_right = sm['carState'].rightBlindspot

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        VM.update_params(sm['liveParameters'].stiffnessFactor,
                         sm['liveParameters'].steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        left_BlindSpot = sm['carState'].leftBlindspot
        right_BlindSpot = sm['carState'].rightBlindspot

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not one_blinker) or (not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            if sm['carState'].leftBlinker:
                self.lane_change_direction = LaneChangeDirection.left
            elif sm['carState'].rightBlinker:
                self.lane_change_direction = LaneChangeDirection.right

            if self.lane_change_direction == LaneChangeDirection.left:
                torque_applied = sm['carState'].steeringTorque > 0 and sm[
                    'carState'].steeringPressed
                if CP.autoLcaEnabled and 2.5 > self.pre_auto_LCA_timer > 2.0 and not left_BlindSpot:
                    torque_applied = True  # Enable auto LCA only once after 2 sec
            else:
                torque_applied = sm['carState'].steeringTorque < 0 and sm[
                    'carState'].steeringPressed
                if CP.autoLcaEnabled and 2.5 > self.pre_auto_LCA_timer > 2.0 and not right_BlindSpot:
                    torque_applied = True  # Enable auto LCA only once after 2 sec

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                    self.lane_change_Blocked = LaneChangeBlocked.off
                elif torque_applied:
                    if self.prev_torque_applied or self.lane_change_direction == LaneChangeDirection.left and not left_BlindSpot or \
                            self.lane_change_direction == LaneChangeDirection.right and not right_BlindSpot:
                        self.lane_change_state = LaneChangeState.laneChangeStarting
                        self.lane_change_Blocked = LaneChangeBlocked.off
                    else:
                        if not self.prev_torque_applied:
                            if left_BlindSpot:
                                self.lane_change_Blocked = LaneChangeBlocked.left
                            elif right_BlindSpot:
                                self.lane_change_Blocked = LaneChangeBlocked.right
                        if self.pre_auto_LCA_timer < 10.:
                            self.pre_auto_LCA_timer = 10.
                else:
                    if not (left_BlindSpot or right_BlindSpot):
                        self.lane_change_Blocked = LaneChangeBlocked.off
                    if self.pre_auto_LCA_timer > 10.3:
                        self.prev_torque_applied = True

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - 2 * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.preLaneChange
                elif self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        if self.lane_change_state == LaneChangeState.off:
            self.pre_auto_LCA_timer = 0.0
            self.prev_torque_applied = False
        elif not (3. < self.pre_auto_LCA_timer <
                  10.):  # stop afer 3 sec resume from 10 when torque applied
            self.pre_auto_LCA_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob

        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 2

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        plan_send.pathPlan.laneChangeBlocked = self.lane_change_Blocked

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #2
0
class PathPlanner():
  def __init__(self, CP):
    self.LP = LanePlanner()

    self.last_cloudlog_t = 0
    self.steer_rate_cost = CP.steerRateCost

    self.setup_mpc()
    self.solution_invalid_cnt = 0
    self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
    self.path_offset_i = 0.0

    self.mpc_frame = 0
    self.sR_delay_counter = 0
    self.steerRatio_new = 0.0
    self.sR_time = 1

    kegman = kegman_conf(CP)
    if kegman.conf['steerRatio'] == "-1":
      self.steerRatio = CP.steerRatio
    else:
      self.steerRatio = float(kegman.conf['steerRatio'])

    if kegman.conf['steerRateCost'] == "-1":
      self.steerRateCost = CP.steerRateCost
    else:
      self.steerRateCost = float(kegman.conf['steerRateCost'])

    self.sR = [float(kegman.conf['steerRatio']), (float(kegman.conf['steerRatio']) + float(kegman.conf['sR_boost']))]
    self.sRBP = [float(kegman.conf['sR_BP0']), float(kegman.conf['sR_BP1'])]

    self.steerRateCost_prev = self.steerRateCost
    self.setup_mpc()

    self.alc_nudge_less = bool(int(kegman.conf['ALCnudgeLess']))
    self.alc_min_speed = float(kegman.conf['ALCminSpeed'])
    self.alc_timer = float(kegman.conf['ALCtimer'])

    self.lane_change_state = LaneChangeState.off
    self.lane_change_direction = LaneChangeDirection.none
    self.lane_change_timer = 0.0
    self.lane_change_ll_prob = 1.0
    self.prev_one_blinker = False



  def setup_mpc(self):
    self.libmpc = libmpc_py.libmpc
    self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.cur_state[0].x = 0.0
    self.cur_state[0].y = 0.0
    self.cur_state[0].psi = 0.0
    self.cur_state[0].delta = 0.0

    self.angle_steers_des = 0.0
    self.angle_steers_des_mpc = 0.0
    self.angle_steers_des_prev = 0.0
    self.angle_steers_des_time = 0.0


  def update(self, sm, pm, CP, VM):

    v_ego = sm['carState'].vEgo
    angle_steers = sm['carState'].steeringAngle
    active = sm['controlsState'].active

    angle_offset = sm['liveParameters'].angleOffset

    # Run MPC
    self.angle_steers_des_prev = self.angle_steers_des_mpc

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

    curvature_factor = VM.curvature_factor(v_ego)

    # Get steerRatio and steerRateCost from kegman.json every x seconds
    self.mpc_frame += 1
    if self.mpc_frame % 500 == 0:
      # live tuning through /data/openpilot/tune.py overrides interface.py settings
      kegman = kegman_conf()
      if kegman.conf['tuneGernby'] == "1":
        self.steerRateCost = float(kegman.conf['steerRateCost'])
        if self.steerRateCost != self.steerRateCost_prev:
          self.setup_mpc()
          self.steerRateCost_prev = self.steerRateCost

        self.sR = [float(kegman.conf['steerRatio']), (float(kegman.conf['steerRatio']) + float(kegman.conf['sR_boost']))]
        self.sRBP = [float(kegman.conf['sR_BP0']), float(kegman.conf['sR_BP1'])]
        self.sR_time = int(float(kegman.conf['sR_time'])) * 100

      self.mpc_frame = 0

    if v_ego > 11.111:
      # boost steerRatio by boost amount if desired steer angle is high
      self.steerRatio_new = interp(abs(angle_steers), self.sRBP, self.sR)

      self.sR_delay_counter += 1
      if self.sR_delay_counter % self.sR_time != 0:
        if self.steerRatio_new > self.steerRatio:
          self.steerRatio = self.steerRatio_new
      else:
        self.steerRatio = self.steerRatio_new
        self.sR_delay_counter = 0
    else:
      self.steerRatio = self.sR[0]

    #print("steerRatio = ", self.steerRatio)

    self.LP.parse_model(sm['model'])

    # Lane change logic
    one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
    below_lane_change_speed = v_ego < self.alc_min_speed

    if sm['carState'].leftBlinker:
      self.lane_change_direction = LaneChangeDirection.left
    elif sm['carState'].rightBlinker:
      self.lane_change_direction = LaneChangeDirection.right

    if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (not self.lane_change_enabled):
      self.lane_change_state = LaneChangeState.off
      self.lane_change_direction = LaneChangeDirection.none
    else:
      torque_applied = sm['carState'].steeringPressed and \
                       ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                        (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

      blindspot_detected = ((sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or
                            (sm['carState'].rightBlindspot and self.lane_change_direction == LaneChangeDirection.right))

      lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

      # State transitions
      # off

      if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
        self.lane_change_state = LaneChangeState.preLaneChange
        self.lane_change_ll_prob = 1.0

      # pre
      elif self.lane_change_state == LaneChangeState.preLaneChange:
        if not one_blinker or below_lane_change_speed:
          self.lane_change_state = LaneChangeState.off
        elif torque_applied and not blindspot_detected:
          self.lane_change_state = LaneChangeState.laneChangeStarting

      # starting
      elif self.lane_change_state == LaneChangeState.laneChangeStarting:
        # fade out over .5s
        self.lane_change_ll_prob = max(self.lane_change_ll_prob - 2*DT_MDL, 0.0)
        # 98% certainty
        if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
          self.lane_change_state = LaneChangeState.laneChangeFinishing

      # finishing
      elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
        # fade in laneline over 1s
        self.lane_change_ll_prob = min(self.lane_change_ll_prob + DT_MDL, 1.0)
        if one_blinker and self.lane_change_ll_prob > 0.99:
          self.lane_change_state = LaneChangeState.preLaneChange
        elif self.lane_change_ll_prob > 0.99:
          self.lane_change_state = LaneChangeState.off

    if self.lane_change_state in [LaneChangeState.off, LaneChangeState.preLaneChange]:
      self.lane_change_timer = 0.0
    else:
      self.lane_change_timer += DT_MDL

    self.prev_one_blinker = one_blinker

    desire = DESIRES[self.lane_change_direction][self.lane_change_state]

    # Turn off lanes during lane change
    if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
      self.LP.l_prob *= self.lane_change_ll_prob
      self.LP.r_prob *= self.lane_change_ll_prob
      self.libmpc.init_weights(MPC_COST_LAT.PATH / 3.0, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)
    else:
      self.libmpc.init_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)

    self.LP.update_d_poly(v_ego)


    # TODO: Check for active, override, and saturation
    # if active:
    #   self.path_offset_i += self.LP.d_poly[3] / (60.0 * 20.0)
    #   self.path_offset_i = clip(self.path_offset_i, -0.5,  0.5)
    #   self.LP.d_poly[3] += self.path_offset_i
    # else:
    #   self.path_offset_i = 0.0

    # account for actuation delay
    self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, VM.sR, CP.steerActuatorDelay)

    v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
    self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                        list(self.LP.l_poly), list(self.LP.r_poly), list(self.LP.d_poly),
                        self.LP.l_prob, self.LP.r_prob, curvature_factor, v_ego_mpc, self.LP.lane_width)

    # reset to current steer angle if not active or overriding
    if active:
      delta_desired = self.mpc_solution[0].delta[1]
      rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
    else:
      delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
      rate_desired = 0.0

    self.cur_state[0].delta = delta_desired

    self.angle_steers_des_mpc = float(math.degrees(delta_desired * VM.sR) + angle_offset)

    #  Check for infeasable MPC solution
    mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
    t = sec_since_boot()
    if mpc_nans:
      self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steerRateCost)
      self.cur_state[0].delta = math.radians(angle_steers - angle_offset) / VM.sR

      if t > self.last_cloudlog_t + 5.0:
        self.last_cloudlog_t = t
        cloudlog.warning("Lateral mpc - nan: True")

    if self.mpc_solution[0].cost > 20000. or mpc_nans:   # TODO: find a better way to detect when MPC did not converge
      self.solution_invalid_cnt += 1
    else:
      self.solution_invalid_cnt = 0
    plan_solution_valid = self.solution_invalid_cnt < 2

    plan_send = messaging.new_message('pathPlan')
    plan_send.valid = sm.all_alive_and_valid(service_list=['carState', 'controlsState', 'liveParameters', 'model'])
    plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
    plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
    plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
    plan_send.pathPlan.lProb = float(self.LP.l_prob)
    plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
    plan_send.pathPlan.rProb = float(self.LP.r_prob)

    plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
    plan_send.pathPlan.rateSteers = float(rate_desired)
    plan_send.pathPlan.angleOffset = float(sm['liveParameters'].angleOffsetAverage)
    plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
    plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

    plan_send.pathPlan.desire = desire
    plan_send.pathPlan.laneChangeState = self.lane_change_state
    plan_send.pathPlan.laneChangeDirection = self.lane_change_direction

    pm.send('pathPlan', plan_send)

    if LOG_MPC:
      dat = messaging.new_message('liveMpc')
      dat.liveMpc.x = list(self.mpc_solution[0].x)
      dat.liveMpc.y = list(self.mpc_solution[0].y)
      dat.liveMpc.psi = list(self.mpc_solution[0].psi)
      dat.liveMpc.delta = list(self.mpc_solution[0].delta)
      dat.liveMpc.cost = self.mpc_solution[0].cost
      pm.send('liveMpc', dat)
Exemple #3
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.path_offset_i = 0.0
        self.lane_change_state = LaneChangeState.off
        self.lane_change_timer = 0.0
        self.prev_one_blinker = False

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        VM.update_params(sm['liveParameters'].stiffnessFactor,
                         sm['liveParameters'].steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        lane_change_direction = LaneChangeDirection.none
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker

        if not active or self.lane_change_timer > 10.0:
            self.lane_change_state = LaneChangeState.off
        else:
            if sm['carState'].leftBlinker:
                lane_change_direction = LaneChangeDirection.left
            elif sm['carState'].rightBlinker:
                lane_change_direction = LaneChangeDirection.right

            if lane_change_direction == LaneChangeDirection.left:
                torque_applied = sm['carState'].steeringTorque > 0 and sm[
                    'carState'].steeringPressed
            else:
                torque_applied = sm['carState'].steeringTorque < 0 and sm[
                    'carState'].steeringPressed

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if False:  # self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker:
                self.lane_change_state = LaneChangeState.preLaneChange

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange and not one_blinker:
                self.lane_change_state = LaneChangeState.off
            elif self.lane_change_state == LaneChangeState.preLaneChange and torque_applied:
                self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting and lane_change_prob > 0.5:
                self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing and lane_change_prob < 0.2:
                self.lane_change_state = LaneChangeState.preLaneChange

            # Don't allow starting lane change below 45 mph
            if (v_ego < 45 * CV.MPH_TO_MS) and (
                    self.lane_change_state == LaneChangeState.preLaneChange):
                self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob = 0.
            self.LP.r_prob = 0.
            self.libmpc.init_weights(MPC_COST_LAT.PATH / 10.0,
                                     MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING,
                                     self.steer_rate_cost)
        else:
            self.libmpc.init_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                                     MPC_COST_LAT.HEADING,
                                     self.steer_rate_cost)

        self.LP.update_d_poly(v_ego)

        # TODO: Check for active, override, and saturation
        # if active:
        #   self.path_offset_i += self.LP.d_poly[3] / (60.0 * 20.0)
        #   self.path_offset_i = clip(self.path_offset_i, -0.5,  0.5)
        #   self.LP.d_poly[3] += self.path_offset_i
        # else:
        #   self.path_offset_i = 0.0

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 2

        plan_send = messaging.new_message()
        plan_send.init('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)
        plan_send.pathPlan.sensorValid = bool(sm['liveParameters'].sensorValid)
        plan_send.pathPlan.posenetValid = bool(
            sm['liveParameters'].posenetValid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = lane_change_direction

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message()
            dat.init('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #4
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'

        if int(Params().get('OpkrAutoLaneChangeDelay')) == 0:
            self.lane_change_auto_delay = 0.0
        elif int(Params().get('OpkrAutoLaneChangeDelay')) == 1:
            self.lane_change_auto_delay = 0.5
        elif int(Params().get('OpkrAutoLaneChangeDelay')) == 2:
            self.lane_change_auto_delay = 1.0
        elif int(Params().get('OpkrAutoLaneChangeDelay')) == 3:
            self.lane_change_auto_delay = 1.5
        elif int(Params().get('OpkrAutoLaneChangeDelay')) == 4:
            self.lane_change_auto_delay = 2.0

        self.lane_change_wait_timer = 0.0
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        self.mpc_frame = 0

        self.lane_change_adjust = [0.2, 1.3]
        self.lane_change_adjust_vel = [16, 30]
        self.lane_change_adjust_new = 0.0

        self.new_steerRatio = CP.steerRatio

        self.angle_offset_select = int(Params().get('OpkrAngleOffsetSelect'))

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        #saturated_steering = sm['controlsState'].steerSaturated
        #live_steerratio = sm['liveParameters'].steerRatio
        mode_select = sm['carState'].cruiseState.modeSel
        lateral_control_method = sm['controlsState'].lateralControlMethod
        if lateral_control_method == 0:
            output_scale = sm[
                'controlsState'].lateralControlState.pidState.output
        elif lateral_control_method == 1:
            output_scale = sm[
                'controlsState'].lateralControlState.indiState.output
        elif lateral_control_method == 2:
            output_scale = sm[
                'controlsState'].lateralControlState.lqrState.output
        angle_offset = sm['liveParameters'].angleOffset

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc

        if abs(output_scale) >= 1 and v_ego > 8:
            self.mpc_frame += 1
            if self.mpc_frame % 5 == 0:
                self.new_steerRatio += (round(v_ego, 1) * 0.01)
                if self.new_steerRatio >= 18.0:
                    self.new_steerRatio = 18.0
                self.mpc_frame = 0
        else:
            self.mpc_frame += 1
            if self.mpc_frame % 5 == 0:
                self.new_steerRatio -= 0.2
                if self.new_steerRatio <= CP.steerRatio:
                    self.new_steerRatio = CP.steerRatio
                self.mpc_frame = 0

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

        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not one_blinker) or (not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                              (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (sm['carState'].leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (sm['carState'].rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                self.lane_change_wait_timer += DT_MDL
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif not blindspot_detected and (
                        torque_applied or (self.lane_change_auto_delay
                                           and self.lane_change_wait_timer >
                                           self.lane_change_auto_delay)):
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                self.lane_change_adjust_new = interp(
                    v_ego, self.lane_change_adjust_vel,
                    self.lane_change_adjust)
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob -
                    self.lane_change_adjust_new * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.laneChangeDone

            # done
            elif self.lane_change_state == LaneChangeState.laneChangeDone:
                if not one_blinker:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego, sm)

        # account for actuation delay
        if mode_select == 3:
            self.cur_state = calc_states_after_delay(
                self.cur_state, v_ego, angle_steers - angle_offset,
                curvature_factor, VM.sR, (CP.steerActuatorDelay + 0.05))
        else:
            self.cur_state = calc_states_after_delay(
                self.cur_state, v_ego, angle_steers - angle_offset,
                curvature_factor, VM.sR, CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        if self.angle_offset_select == 0:
            plan_send.pathPlan.angleOffset = float(
                sm['liveParameters'].angleOffsetAverage)
        else:
            plan_send.pathPlan.angleOffset = float(
                sm['liveParameters'].angleOffset)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        plan_send.pathPlan.steerRatio = VM.sR
        plan_send.pathPlan.steerActuatorDelay = CP.steerActuatorDelay
        plan_send.pathPlan.outputScale = output_scale

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #5
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost
        self.steerRatio = CP.steerRatio

        self.setup_mpc()
        self.solution_invalid_cnt = 0

        self.steerRatio_last = 0

        self.params = Params()

        # Lane change
        self.lane_change_enabled = self.params.get('LaneChangeEnabled') == b'1'
        self.lane_change_auto_delay = self.params.get_OpkrAutoLanechangedelay(
        )  #int( self.params.get('OpkrAutoLanechangedelay') )

        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_run_timer = 0.0
        self.lane_change_wait_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        # atom
        self.trPATH = trace1.Loger("path")
        self.trLearner = trace1.Loger("Learner")
        self.trpathPlan = trace1.Loger("pathPlan")

        self.atom_timer_cnt = 0
        self.atom_steer_ratio = None
        self.atom_sr_boost_bp = [0., 0.]
        self.atom_sr_boost_range = [0., 0.]

        self.carParams_valid = False

        self.m_avg = ma.MoveAvg()

    def limit_ctrl(self, value, limit, offset):
        p_limit = offset + limit
        m_limit = offset - limit
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def limit_ctrl1(self, value, limit1, limit2, offset):
        p_limit = offset + limit1
        m_limit = offset - limit2
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def atom_tune(self, v_ego_kph, sr_value, atomTuning):  # 조향각에 따른 변화.
        self.sr_KPH = atomTuning.sRKPH
        self.sr_BPV = atomTuning.sRBPV
        self.sr_steerRatioV = atomTuning.sRsteerRatioV
        self.sr_SteerRatio = []

        nPos = 0
        for steerRatio in self.sr_BPV:  # steerRatio
            self.sr_SteerRatio.append(
                interp(sr_value, steerRatio, self.sr_steerRatioV[nPos]))
            nPos += 1
            if nPos > 20:
                break

        steerRatio = interp(v_ego_kph, self.sr_KPH, self.sr_SteerRatio)

        return steerRatio

    def atom_actuatorDelay(self, v_ego_kph, sr_value, atomTuning):
        self.sr_KPH = atomTuning.sRKPH
        self.sr_BPV = atomTuning.sRBPV
        self.sr_ActuatorDelayV = atomTuning.sRsteerActuatorDelayV
        self.sr_ActuatorDelay = []

        nPos = 0
        for steerRatio in self.sr_BPV:
            self.sr_ActuatorDelay.append(
                interp(sr_value, steerRatio, self.sr_ActuatorDelayV[nPos]))
            nPos += 1
            if nPos > 10:
                break

        actuatorDelay = interp(v_ego_kph, self.sr_KPH, self.sr_ActuatorDelay)

        return actuatorDelay

    def atom_steer(self, sr_value, sr_up, sr_dn):
        delta = sr_value - self.steerRatio_last

        sr_up = min(abs(delta), sr_up)
        sr_dn = min(abs(delta), sr_dn)
        steerRatio = self.steerRatio_last
        if delta > 0:
            steerRatio += sr_up
        elif delta < 0:
            steerRatio -= sr_dn

        self.steerRatio_last = steerRatio
        return steerRatio

    def update(self, sm, pm, CP, VM):
        self.atom_timer_cnt += 1
        if self.atom_timer_cnt > 1000:
            self.atom_timer_cnt = 0

        cruiseState = sm['carState'].cruiseState
        leftBlindspot = sm['carState'].leftBlindspot
        rightBlindspot = sm['carState'].rightBlindspot

        lateralsRatom = CP.lateralsRatom
        atomTuning = CP.atomTuning

        #if atomTuning is None or lateralsRatom is None:
        #print('carparams={} steerRatio={}  carParams_valid={}'.format(sm.updated['carParams'], sm['carParams'].steerRatio, self.carParams_valid ) )

        if not self.carParams_valid and sm[
                'carParams'].steerRatio:  # sm.updated['carParams']:
            self.carParams_valid = True

        if self.carParams_valid:
            lateralsRatom = sm['carParams'].lateralsRatom
            atomTuning = sm['carParams'].atomTuning

        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        steeringPressed = sm['carState'].steeringPressed
        steeringTorque = sm['carState'].steeringTorque
        active = sm['controlsState'].active
        model_sum = sm['controlsState'].modelSum

        v_ego_kph = v_ego * CV.MS_TO_KPH

        self.steerRatio = sm['liveParameters'].steerRatio
        angle_offset = sm['liveParameters'].angleOffset
        angleOffsetAverage = sm['liveParameters'].angleOffsetAverage
        stiffnessFactor = sm['liveParameters'].stiffnessFactor

        #if (self.atom_timer_cnt % 100) == 0:
        #  str_log3 = 'angleOffset={:.1f} angleOffsetAverage={:.3f} steerRatio={:.2f} stiffnessFactor={:.3f} '.format( angle_offset, angleOffsetAverage, self.steerRatio, stiffnessFactor )
        #  self.trLearner.add( 'LearnerParam {}  carParams={}'.format( str_log3, self.carParams_valid ) )

        if lateralsRatom.learnerParams:
            pass
        else:
            # atom
            if self.carParams_valid:
                self.steer_rate_cost = sm['carParams'].steerRateCost
                self.steerRatio = sm['carParams'].steerRatio
            else:
                self.steer_rate_cost = CP.steerRateCost
                self.steerRatio = CP.steerRatio

            #xp = [-5,0,5]
            #fp = [0.4, 0.7, 0.4]
            #self.steer_rate_cost = interp( angle_steers, xp, fp )
            steerRatio = self.atom_tune(v_ego_kph, angle_steers, atomTuning)
            self.steerRatio = self.atom_steer(steerRatio, 2, 1)

        #actuatorDelay = CP.steerActuatorDelay
        steerActuatorDelay = self.atom_actuatorDelay(v_ego_kph, angle_steers,
                                                     atomTuning)

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        VM.update_params(stiffnessFactor, self.steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_run_timer >
                            LANE_CHANGE_TIME_MAX) or (not one_blinker) or (
                                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            l_poly = self.LP.l_poly[3]
            r_poly = self.LP.r_poly[3]
            c_prob = l_poly + r_poly
            torque_applied = steeringPressed and \
                              ((steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or \
                                (steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if cruiseState.cruiseSwState == Buttons.CANCEL:
                self.lane_change_state = LaneChangeState.off
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            elif self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                self.lane_change_wait_timer += DT_MDL

                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif not blindspot_detected and (
                        torque_applied or (self.lane_change_auto_delay
                                           and self.lane_change_wait_timer >
                                           self.lane_change_auto_delay)):
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                xp = [40, 50, 60, 70]
                fp2 = [0.2, 0.6, 1.2, 1.5]
                lane_time = interp(v_ego_kph, xp, fp2)
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - lane_time * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if self.lane_change_ll_prob > 0.99 and abs(c_prob) < 0.3:
                    self.lane_change_state = LaneChangeState.laneChangeDone

            # done
            elif self.lane_change_state == LaneChangeState.laneChangeDone:
                if not one_blinker:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_run_timer = 0.0
        else:
            self.lane_change_run_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego, lateralsRatom.cameraOffset)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)
        org_angle_steers_des = self.angle_steers_des_mpc

        # atom
        if steeringPressed:
            delta_steer = org_angle_steers_des - angle_steers
            xp = [-255, 0, 255]
            fp2 = [5, 0, 5]
            limit_steers = interp(steeringTorque, xp, fp2)
            if steeringTorque < 0:  # right
                if delta_steer > 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)
            elif steeringTorque > 0:  # left
                if delta_steer < 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)

        elif v_ego_kph < 15:  # 30
            xp = [3, 10, 15]
            fp2 = [3, 5, 7]
            limit_steers = interp(v_ego_kph, xp, fp2)
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, limit_steers, angle_steers)
        elif v_ego_kph > 60:
            pass
        elif abs(angle_steers) > 10:  # angle steer > 10
            # 2.
            xp = [-10, -5, 0, 5, 10]  # 5  10=>28 15=>35, 30=>52
            fp1 = [3, 8, 10, 20, 10]  # +
            fp2 = [10, 20, 10, 8, 3]  # -
            limit_steers1 = interp(model_sum, xp, fp1)  # +
            limit_steers2 = interp(model_sum, xp, fp2)  # -
            self.angle_steers_des_mpc = self.limit_ctrl1(
                org_angle_steers_des, limit_steers1, limit_steers2,
                angle_steers)

        delta_steer = self.angle_steers_des_mpc - angle_steers
        ANGLE_LIMIT = 8
        if delta_steer > ANGLE_LIMIT:
            p_angle_steers = angle_steers + ANGLE_LIMIT
            self.angle_steers_des_mpc = p_angle_steers
        elif delta_steer < -ANGLE_LIMIT:
            m_angle_steers = angle_steers - ANGLE_LIMIT
            self.angle_steers_des_mpc = m_angle_steers

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, self.steer_rate_cost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        #self.trPATH.add( 'mpc_nans ={}  libmpc  steer_rate_cost={}  delta={}   angle_steers={}'.format( mpc_nans, self.steer_rate_cost, self.cur_state[0].delta, angle_steers ) )

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        plan_send.pathPlan.steerRatio = self.steerRatio
        plan_send.pathPlan.steerActuatorDelay = steerActuatorDelay
        pm.send('pathPlan', plan_send)

        #if self.solution_invalid_cnt > 0:
        #  str_log3 = 'v_ego_kph={:.1f} angle_steers_des_mpc={:.1f} angle_steers={:.1f} solution_invalid_cnt={:.0f} mpc_solution={:.1f}/{:.0f}'.format( v_ego_kph, self.angle_steers_des_mpc, angle_steers, self.solution_invalid_cnt, self.mpc_solution[0].cost, mpc_nans )
        #  self.trpathPlan.add( 'pathPlan {}  LOG_MPC={}'.format( str_log3, LOG_MPC ) )

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #6
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        # dragonpilot
        self.params = Params()
        self.dragon_auto_lc_enabled = False
        self.dragon_auto_lc_allowed = False
        self.dragon_auto_lc_timer = None
        self.dragon_assisted_lc_min_mph = LANE_CHANGE_SPEED_MIN
        self.dragon_auto_lc_min_mph = 60 * CV.MPH_TO_MS
        self.dragon_auto_lc_delay = 2.
        self.last_ts = 0.
        self.dp_last_modified = None
        self.dp_enable_sr_boost = False

        self.dp_steer_ratio = 0.
        self.dp_sr_boost_bp = None
        self.dp_sr_boost_range = None

    def limit_ctrl(self, value, limit, offset):
        p_limit = offset + limit
        m_limit = offset - limit
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        # dragonpilot
        cur_time = sec_since_boot()
        if cur_time - self.last_ts >= 5.:
            modified = get_last_modified()
            if self.dp_last_modified != modified:
                self.lane_change_enabled = True if self.params.get(
                    "LaneChangeEnabled", encoding='utf8') == "1" else False
                if self.lane_change_enabled:
                    self.dragon_auto_lc_enabled = True if self.params.get(
                        "DragonEnableAutoLC",
                        encoding='utf8') == "1" else False
                    # adjustable assisted lc min speed
                    try:
                        self.dragon_assisted_lc_min_mph = float(
                            self.params.get("DragonAssistedLCMinMPH",
                                            encoding='utf8'))
                    except (TypeError, ValueError):
                        self.dragon_assisted_lc_min_mph = 45
                    self.dragon_assisted_lc_min_mph *= CV.MPH_TO_MS
                    if self.dragon_assisted_lc_min_mph < 0:
                        self.dragon_assisted_lc_min_mph = 0
                    if self.dragon_auto_lc_enabled:
                        # adjustable auto lc min speed
                        try:
                            self.dragon_auto_lc_min_mph = float(
                                self.params.get("DragonAutoLCMinMPH",
                                                encoding='utf8'))
                        except (TypeError, ValueError):
                            self.dragon_auto_lc_min_mph = 60
                        self.dragon_auto_lc_min_mph *= CV.MPH_TO_MS
                        if self.dragon_auto_lc_min_mph < 0:
                            self.dragon_auto_lc_min_mph = 0
                        # when auto lc is smaller than assisted lc, we set assisted lc to the same speed as auto lc
                        if self.dragon_auto_lc_min_mph < self.dragon_assisted_lc_min_mph:
                            self.dragon_assisted_lc_min_mph = self.dragon_auto_lc_min_mph
                        # adjustable auto lc delay
                        try:
                            self.dragon_auto_lc_delay = float(
                                self.params.get("DragonAutoLCDelay",
                                                encoding='utf8'))
                        except (TypeError, ValueError):
                            self.dragon_auto_lc_delay = 2.
                        if self.dragon_auto_lc_delay < 0:
                            self.dragon_auto_lc_delay = 0
                else:
                    self.dragon_auto_lc_enabled = False

                self.dp_enable_sr_boost = True if self.params.get(
                    "DragonEnableSteerBoost",
                    encoding='utf8') == "1" else False
                if self.dp_enable_sr_boost:
                    try:
                        sr_boost_min = float(
                            self.params.get("DragonSteerBoostMin",
                                            encoding='utf8'))
                        sr_boost_Max = float(
                            self.params.get("DragonSteerBoostMax",
                                            encoding='utf8'))
                        self.dp_sr_boost_range = [sr_boost_min, sr_boost_Max]

                        boost_min_at = float(
                            self.params.get("DragonSteerBoostMinAt",
                                            encoding='utf8'))
                        boost_max_at = float(
                            self.params.get("DragonSteerBoostMaxAt",
                                            encoding='utf8'))
                        self.dp_sr_boost_bp = [boost_min_at, boost_max_at]
                    except (TypeError, ValueError):
                        put_nonblocking("DragonEnableSteerBoost", '0')
                        self.dp_enable_sr_boost = False
                if not self.dp_enable_sr_boost:
                    self.dp_sr_boost_range = [0., 0.]
                    self.dp_sr_boost_bp = [0., 0.]

                self.dp_last_modified = modified
            self.last_ts = cur_time

        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        # atom
        v_ego_kph = v_ego * CV.MS_TO_KPH

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        VM.update_params(sm['liveParameters'].stiffnessFactor,
                         sm['liveParameters'].steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)
        boost_rate = (1 + (interp(abs(angle_steers), self.dp_sr_boost_bp,
                                  self.dp_sr_boost_range) /
                           100)) if self.dp_enable_sr_boost else 1
        self.dp_steer_ratio = VM.sR * boost_rate

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < self.dragon_assisted_lc_min_mph

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not one_blinker) or (not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or \
                              (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # dragonpilot auto lc
            if not below_lane_change_speed and self.dragon_auto_lc_enabled and v_ego >= self.dragon_auto_lc_min_mph:
                # we allow auto lc when speed reached dragon_auto_lc_min_mph
                self.dragon_auto_lc_allowed = True

                if self.dragon_auto_lc_timer is None:
                    # we only set timer when in preLaneChange state, dragon_auto_lc_delay delay
                    if self.lane_change_state == LaneChangeState.preLaneChange:
                        self.dragon_auto_lc_timer = cur_time + self.dragon_auto_lc_delay
                elif cur_time >= self.dragon_auto_lc_timer:
                    # if timer is up, we set torque_applied to True to fake user input
                    torque_applied = True
            else:
                # if too slow, we reset all the variables
                self.dragon_auto_lc_allowed = False
                self.dragon_auto_lc_timer = None

            # we reset the timers when torque is applied regardless
            if torque_applied:
                self.dragon_auto_lc_timer = None

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif torque_applied:
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out lanelines over .5s
                #self.lane_change_ll_prob = max(self.lane_change_ll_prob - 2*DT_MDL, 0.0)

                # fade out over .2s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - DT_MDL / 5, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.preLaneChange
                elif self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.off

                # when finishing, we reset timer to none.
                self.dragon_auto_lc_timer = None

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor,
                                                 self.dp_steer_ratio,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] *
                                        self.dp_steer_ratio)
        else:
            delta_desired = math.radians(angle_steers -
                                         angle_offset) / self.dp_steer_ratio
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        #self.angle_steers_des_mpc = float(math.degrees(delta_desired * self.dp_steer_ratio) + angle_offset)
        #old_angle_steers_des = self.angle_steers_des_mpc
        org_angle_steers_des = float(
            math.degrees(delta_desired * self.steerRatio) + angle_offset)
        self.angle_steers_des_mpc = org_angle_steers_des

        # atom
        if v_ego_kph < 40:
            xp = [0, 5, 20, 40]
            fp2 = [0.3, 0.5, 1, 1.5]
            limit_steers = interp(v_ego_kph, xp, fp2)
            angle_steers_des = self.limit_ctrl(org_angle_steers_des,
                                               limit_steers, angle_steers)
            self.angle_steers_des_mpc = angle_steers_des
        elif self.lane_change_state != LaneChangeState.off:
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, 10, angle_steers)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(
                angle_steers - angle_offset) / self.dp_steer_ratio

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 2

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)
        plan_send.pathPlan.sensorValid = bool(sm['liveParameters'].sensorValid)
        plan_send.pathPlan.posenetValid = bool(
            sm['liveParameters'].posenetValid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        plan_send.pathPlan.alcAllowed = self.dragon_auto_lc_allowed

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #7
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost
        self.setup_mpc()
        self.solution_invalid_cnt = 0

        self.params = Params()
        kyd = kyd_conf(CP)
        if kyd.conf['steerRatio'] == "-1":
            self.steerRatio = CP.steerRatio
        else:
            self.steerRatio = float(kyd.conf['steerRatio'])

        if kyd.conf['steerRateCost'] == "-1":
            self.steer_rate_cost = CP.steerRateCost
        else:
            self.steer_rate_cost = float(kyd.conf['steerRateCost'])

        self.kyd_steerRatio = None
        self.sRBP = [0., 0.]
        self.sRBoost = [0., 0.]

        # Lane change
        self.lane_change_enabled = self.params.get('LaneChangeEnabled') == b'1'
        self.lane_change_auto_delay = self.params.get_OpkrAutoLanechangedelay(
        )  #int( self.params.get('OpkrAutoLanechangedelay') )

        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_run_timer = 0.0
        self.lane_change_wait_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        self.param_OpkrEnableLearner = int(
            self.params.get('OpkrEnableLearner'))

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):

        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        if not self.param_OpkrEnableLearner:
            kyd = kyd_conf()
            #self.steer_rate_cost = float(kyd.conf['steerRateCost'])
            self.sRBP = kyd.conf['sR_BP']
            self.sRBoost = kyd.conf['sR_Boost']
            boost_rate = interp(abs(angle_steers), self.sRBP, self.sRBoost)
            self.kyd_steerRatio = self.steerRatio + boost_rate

            self.sR_Cost = [
                1.0, 0.90, 0.81, 0.73, 0.66, 0.60, 0.54, 0.48, 0.36, 0.275,
                0.20, 0.175, 0.15, 0.11, 0.05
            ]
            #self.sR_Cost = [0.75,0.67,0.60,0.54,0.48,0.425,0.37,0.32,0.24,0.19,0.15,0.14,0.13,0.11,0.05]
            #self.sR_Cost = [0.50,0.46,0.425,0.395,0.37,0.34,0.315,0.29,0.23,0.185,0.15,0.14,0.13,0.11,0.05]
            #steerRatio = 10.0
            #"sR_BP": [0.0,2.0,4.0,6.0,8.0,10.0,12.0,14.0,20.0,27.0,35.0,40.0,45.0,60.0,100],
            #"sR_Boost": [0.0,0.7,1.3,1.9,2.5,3.05,3.55,4.0,5.0,5.7,6.2,6.35,6.4,6.5,8.0],
            self.steer_rate_cost = interp(abs(angle_steers), self.sRBP,
                                          self.sR_Cost)

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc

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

        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_run_timer >
                            LANE_CHANGE_TIME_MAX) or (not one_blinker) or (
                                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                              (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (sm['carState'].leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (sm['carState'].rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                self.lane_change_wait_timer += DT_MDL

                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif not blindspot_detected and (
                        torque_applied or (self.lane_change_auto_delay
                                           and self.lane_change_wait_timer >
                                           self.lane_change_auto_delay)):
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - 1.5 * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.laneChangeDone

            # done
            elif self.lane_change_state == LaneChangeState.laneChangeDone:
                if not one_blinker:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_run_timer = 0.0
        else:
            self.lane_change_run_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        if self.param_OpkrEnableLearner:
            self.cur_state = calc_states_after_delay(
                self.cur_state, v_ego, angle_steers - angle_offset,
                curvature_factor, VM.sR, CP.steerActuatorDelay)
        else:
            self.cur_state = calc_states_after_delay(
                self.cur_state, v_ego, angle_steers - angle_offset,
                curvature_factor, self.kyd_steerRatio, CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            if self.param_OpkrEnableLearner:
                rate_desired = math.degrees(self.mpc_solution[0].rate[0] *
                                            VM.sR)
            else:
                rate_desired = math.degrees(self.mpc_solution[0].rate[0] *
                                            self.kyd_steerRatio)
        else:
            if self.param_OpkrEnableLearner:
                delta_desired = math.radians(angle_steers -
                                             angle_offset) / VM.sR
            else:
                delta_desired = math.radians(
                    angle_steers - angle_offset) / self.kyd_steerRatio
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired
        if self.param_OpkrEnableLearner:
            self.angle_steers_des_mpc = float(
                math.degrees(delta_desired * VM.sR) + angle_offset)
        else:
            self.angle_steers_des_mpc = float(
                math.degrees(delta_desired * self.kyd_steerRatio) +
                angle_offset)
        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, self.steer_rate_cost)
            if self.param_OpkrEnableLearner:
                self.cur_state[0].delta = math.radians(angle_steers -
                                                       angle_offset) / VM.sR
            else:
                self.cur_state[0].delta = math.radians(
                    angle_steers - angle_offset) / self.kyd_steerRatio

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction

        if not self.param_OpkrEnableLearner:
            plan_send.pathPlan.steerRatio = float(self.kyd_steerRatio)

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #8
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.steerRatio = CP.steerRatio
        self.steerActuatorDelay = CP.steerActuatorDelay

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        self.steerRatio_last = 0
        self.params = Params()
        self.lane_change_auto_delay = 0
        self.lane_change_run_timer = 0.0
        self.lane_change_wait_timer = 0.0

        # atom
        self.trPATH = trace1.Loger("path")
        self.trLearner = trace1.Loger("Learner")
        self.trpathPlan = trace1.Loger("pathPlan")

        self.atom_timer_cnt = 0
        self.atom_steer_ratio = None
        self.atom_sr_boost_bp = [0., 0.]
        self.atom_sr_boost_range = [0., 0.]

        self.carParams_valid = False

        self.m_avg = ma.MoveAvg()

    def limit_ctrl(self, value, limit, offset):
        p_limit = offset + limit
        m_limit = offset - limit
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def limit_ctrl1(self, value, limit1, limit2, offset):
        p_limit = offset + limit1
        m_limit = offset - limit2
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def atom_tune(self, v_ego_kph, sr_value, atomTuning):
        self.sr_KPH = atomTuning.sRKPH
        self.sr_BPV = atomTuning.sRBPV
        self.sr_steerRatioV = atomTuning.sRsteerRatioV
        self.sr_SteerRatio = []

        nPos = 0
        for steerRatio in self.sr_BPV:  # steerRatio
            self.sr_SteerRatio.append(
                interp(sr_value, steerRatio, self.sr_steerRatioV[nPos]))
            nPos += 1
            if nPos > 20:
                break

        steerRatio = interp(v_ego_kph, self.sr_KPH, self.sr_SteerRatio)

        return steerRatio

    def atom_actuatorDelay(self, v_ego_kph, sr_value, atomTuning):
        self.sr_KPH = atomTuning.sRKPH
        self.sr_BPV = atomTuning.sRBPV
        self.sr_ActuatorDelayV = atomTuning.sRsteerActuatorDelayV
        self.sr_ActuatorDelay = []

        nPos = 0
        for steerRatio in self.sr_BPV:
            self.sr_ActuatorDelay.append(
                interp(sr_value, steerRatio, self.sr_ActuatorDelayV[nPos]))
            nPos += 1
            if nPos > 10:
                break

        actuatorDelay = interp(v_ego_kph, self.sr_KPH, self.sr_ActuatorDelay)

        return actuatorDelay

    def atom_steer(self, sr_value, sr_up, sr_dn):
        delta = sr_value - self.steerRatio_last

        sr_up = min(abs(delta), sr_up)
        sr_dn = min(abs(delta), sr_dn)
        steerRatio = self.steerRatio_last
        if delta > 0:
            steerRatio += sr_up
        elif delta < 0:
            steerRatio -= sr_dn

        self.steerRatio_last = steerRatio
        return steerRatio

    def update(self, sm, pm, CP, VM):
        atomTuning = CP.atomTuning
        lateralsRatom = CP.lateralsRatom

        laneLineProbs = sm['modelV2'].laneLineProbs

        leftLaneProb = False  # laneLineProbs[0] < 0.01
        rightLaneProb = False  # laneLineProbs[3] < 0.01

        #if laneLineProbs[0] < 0.01:
        #  leftLaneProb =  True

        #if laneLineProbs[3] < 0.01:
        #  rightLaneProb =  True

        cruiseState = sm['carState'].cruiseState
        leftBlindspot = sm['carState'].leftBlindspot
        rightBlindspot = sm['carState'].rightBlindspot

        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        steeringPressed = sm['carState'].steeringPressed
        steeringTorque = sm['carState'].steeringTorque
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        v_ego_kph = v_ego * CV.MS_TO_KPH
        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc

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

        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:


            torque_applied = steeringPressed and \
                             ((steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                              (steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            laneLineProbs_detected = (
                (leftLaneProb
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (rightLaneProb
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if cruiseState.cruiseSwState == Buttons.CANCEL:
                self.lane_change_state = LaneChangeState.off
                self.lane_change_ll_prob = 1.0

            elif self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif torque_applied and not blindspot_detected and not laneLineProbs_detected:
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                xp = [40, 70]
                fp2 = [1, 2]
                lane_time = interp(v_ego_kph, xp, fp2)
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - lane_time * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.preLaneChange
                elif self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego, lateralsRatom.cameraOffset)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)

        # atom
        org_angle_steers_des = self.angle_steers_des_mpc
        if self.lane_change_state == LaneChangeState.laneChangeStarting:
            xp = [40, 70]
            fp2 = [5, 8]
            limit_steers = interp(v_ego_kph, xp, fp2)
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, limit_steers, angle_steers)
        elif steeringPressed:
            delta_steer = org_angle_steers_des - angle_steers
            if angle_steers > 10 and steeringTorque > 0:
                delta_steer = max(delta_steer, 0)
                delta_steer = min(delta_steer, DST_ANGLE_LIMIT)
                self.angle_steers_des_mpc = angle_steers + delta_steer
            elif angle_steers < -10 and steeringTorque < 0:
                delta_steer = min(delta_steer, 0)
                delta_steer = max(delta_steer, -DST_ANGLE_LIMIT)
                self.angle_steers_des_mpc = angle_steers + delta_steer
            else:
                if steeringTorque < 0:  # right
                    if delta_steer > 0:
                        self.angle_steers_des_mpc = self.limit_ctrl(
                            org_angle_steers_des, DST_ANGLE_LIMIT,
                            angle_steers)
                elif steeringTorque > 0:  # left
                    if delta_steer < 0:
                        self.angle_steers_des_mpc = self.limit_ctrl(
                            org_angle_steers_des, DST_ANGLE_LIMIT,
                            angle_steers)

        elif v_ego_kph < 30:  # 30
            xp = [3, 10, 30]
            fp2 = [1, 3, 5]
            limit_steers = interp(v_ego_kph, xp, fp2)
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, limit_steers, angle_steers)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #9
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        # dp
        self.dragon_auto_lc_allowed = False
        self.dragon_auto_lc_timer = None
        self.dragon_auto_lc_delay = 2.
        self.dp_continuous_auto_lc = False
        self.dp_did_auto_lc = False

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc

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

        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < (
            sm['dragonConf'].dpAssistedLcMinMph * CV.MPH_TO_MS)

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                              (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (sm['carState'].leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (sm['carState'].rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # dp alc
            cur_time = sec_since_boot()
            if not below_lane_change_speed and sm[
                    'dragonConf'].dpAutoLc and v_ego >= (
                        sm['dragonConf'].dpAutoLcMinMph * CV.MPH_TO_MS):
                # we allow auto lc when speed reached dragon_auto_lc_min_mph
                self.dragon_auto_lc_allowed = True
            else:
                # if too slow, we reset all the variables
                self.dragon_auto_lc_allowed = False
                self.dragon_auto_lc_timer = None

            # disable auto lc when continuous is off and already did auto lc once
            if self.dragon_auto_lc_allowed and not sm[
                    'dragonConf'].dpAutoLcCont and self.dp_did_auto_lc:
                self.dragon_auto_lc_allowed = False

            if self.dragon_auto_lc_allowed:
                if self.dragon_auto_lc_timer is None:
                    # we only set timer when in preLaneChange state, dragon_auto_lc_delay delay
                    if self.lane_change_state == LaneChangeState.preLaneChange:
                        self.dragon_auto_lc_timer = cur_time + sm[
                            'dragonConf'].dpAutoLcDelay
                elif cur_time >= self.dragon_auto_lc_timer:
                    # if timer is up, we set torque_applied to True to fake user input
                    torque_applied = True
                    self.dp_did_auto_lc = True

            # we reset the timers when torque is applied regardless
            if torque_applied:
                self.dragon_auto_lc_timer = None

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif torque_applied and not blindspot_detected:
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - 2 * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.preLaneChange
                elif self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.off

                # dp when finishing, we reset timer to none.
                self.dragon_auto_lc_timer = None

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        if self.prev_one_blinker and not one_blinker:
            self.dp_did_auto_lc = False

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 2

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model',
            'dragonConf'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        plan_send.pathPlan.dpALCAllowed = self.dragon_auto_lc_allowed

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #10
0
class PathPlanner():
  def __init__(self, CP):
    self.LP = LanePlanner()

    self.last_cloudlog_t = 0
    self.steer_rate_cost = CP.steerRateCost

    self.setup_mpc()
    self.solution_invalid_cnt = 0
    self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
    self.path_offset_i = 0.0

    self.mpc_frame = 0
    self.sR_delay_counter = 0
    self.steerRatio_new = 0.0
    self.sR_time = 1

    kegman = kegman_conf(CP)
    if kegman.conf['steerRatio'] == "-1":
      self.steerRatio = CP.steerRatio
    else:
      self.steerRatio = float(kegman.conf['steerRatio'])

    if kegman.conf['steerRateCost'] == "-1":
      self.steerRateCost = CP.steerRateCost
    else:
      self.steerRateCost = float(kegman.conf['steerRateCost'])

    self.sR = [float(kegman.conf['steerRatio']), (float(kegman.conf['steerRatio']) + float(kegman.conf['sR_boost']))]
    self.sRBP = [float(kegman.conf['sR_BP0']), float(kegman.conf['sR_BP1'])]

    self.steerRateCost_prev = self.steerRateCost
    self.setup_mpc()

    self.lane_change_state = LaneChangeState.off
    self.lane_change_direction = LaneChangeDirection.none
    self.lane_change_timer = 0.0
    self.prev_one_blinker = False
    self.pre_auto_LCA_timer = 0.0
    self.lane_change_BSM = LaneChangeBSM.off
    self.prev_torque_applied = False

  def setup_mpc(self):
    self.libmpc = libmpc_py.libmpc
    self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.cur_state[0].x = 0.0
    self.cur_state[0].y = 0.0
    self.cur_state[0].psi = 0.0
    self.cur_state[0].delta = 0.0

    self.angle_steers_des = 0.0
    self.angle_steers_des_mpc = 0.0
    self.angle_steers_des_prev = 0.0
    self.angle_steers_des_time = 0.0

  def update(self, sm, pm, CP, VM):
    v_ego = sm['carState'].vEgo
    angle_steers = sm['carState'].steeringAngle
    active = sm['controlsState'].active

    angle_offset = sm['liveParameters'].angleOffset

    lca_left = sm['carState'].lcaLeft
    lca_right = sm['carState'].lcaRight

    # Run MPC
    self.angle_steers_des_prev = self.angle_steers_des_mpc
    VM.update_params(sm['liveParameters'].stiffnessFactor, sm['liveParameters'].steerRatio)
    curvature_factor = VM.curvature_factor(v_ego)

    # Get steerRatio and steerRateCost from kegman.json every x seconds
    self.mpc_frame += 1
    if self.mpc_frame % 500 == 0:
      # live tuning through /data/openpilot/tune.py overrides interface.py settings
      kegman = kegman_conf()
      if kegman.conf['tuneGernby'] == "1":
        self.steerRateCost = float(kegman.conf['steerRateCost'])
        if self.steerRateCost != self.steerRateCost_prev:
          self.setup_mpc()
          self.steerRateCost_prev = self.steerRateCost

        self.sR = [float(kegman.conf['steerRatio']), (float(kegman.conf['steerRatio']) + float(kegman.conf['sR_boost']))]
        self.sRBP = [float(kegman.conf['sR_BP0']), float(kegman.conf['sR_BP1'])]
        self.sR_time = int(float(kegman.conf['sR_time'])) * 100

      self.mpc_frame = 0

    if v_ego > 11.111:
      # boost steerRatio by boost amount if desired steer angle is high
      self.steerRatio_new = interp(abs(angle_steers), self.sRBP, self.sR)

      self.sR_delay_counter += 1
      if self.sR_delay_counter % self.sR_time != 0:
        if self.steerRatio_new > self.steerRatio:
          self.steerRatio = self.steerRatio_new
      else:
        self.steerRatio = self.steerRatio_new
        self.sR_delay_counter = 0
    else:
      self.steerRatio = self.sR[0]

    print("steerRatio = ", self.steerRatio)

    self.LP.parse_model(sm['model'])

    # Lane change logic
    one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
    below_lane_change_speed = v_ego < 60 * CV.KPH_TO_MS

    if sm['carState'].leftBlinker:
      self.lane_change_direction = LaneChangeDirection.left
    elif sm['carState'].rightBlinker:
      self.lane_change_direction = LaneChangeDirection.right

    # cancel LCA when driver takeover during lane change 2020-03-11
    # if (not active) or (self.lane_change_timer > 10.0) or (not one_blinker) or (not self.lane_change_enabled):
    if (not active) or (self.lane_change_timer > 10.0) or (not one_blinker) or (not self.lane_change_enabled) or (sm['carState'].steeringPressed and ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.right) or (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.left))):
      self.lane_change_state = LaneChangeState.off
      self.lane_change_direction = LaneChangeDirection.none
    else:
      if sm['carState'].leftBlinker:
        self.lane_change_direction = LaneChangeDirection.left
      elif sm['carState'].rightBlinker:
        self.lane_change_direction = LaneChangeDirection.right

      if self.lane_change_direction == LaneChangeDirection.left:
        torque_applied = sm['carState'].steeringTorque > 0 and sm['carState'].steeringPressed
        if CP.autoLcaEnabled and 0.6 > self.pre_auto_LCA_timer > 0.1 and not lca_left:
          torque_applied = True # Enable auto LCA only once after 1 sec 
      else:
        torque_applied = sm['carState'].steeringTorque < 0 and sm['carState'].steeringPressed
        if CP.autoLcaEnabled and 0.6 > self.pre_auto_LCA_timer > 0.1 and not lca_right:
          torque_applied = True # Enable auto LCA only once after 1 sec 

      lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

      if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
        self.lane_change_state = LaneChangeState.preLaneChange

      # pre
      elif self.lane_change_state == LaneChangeState.preLaneChange:
        if not one_blinker or below_lane_change_speed:
          self.lane_change_state = LaneChangeState.off   
        elif torque_applied:
          if self.prev_torque_applied or self.lane_change_direction == LaneChangeDirection.left and not lca_left or \
                  self.lane_change_direction == LaneChangeDirection.right and not lca_right:
            self.lane_change_state = LaneChangeState.laneChangeStarting
          else:
            if self.pre_auto_LCA_timer < 10.:
              self.pre_auto_LCA_timer = 10.
        else:
          if self.pre_auto_LCA_timer > 10.3:
            self.prev_torque_applied = True

      # bsm
      elif self.lane_change_state == LaneChangeState.laneChangeStarting:
        if lca_left and self.lane_change_direction == LaneChangeDirection.left and not self.prev_torque_applied:
          self.lane_change_BSM = LaneChangeBSM.left
          self.lane_change_state = LaneChangeState.preLaneChange
        elif lca_right and self.lane_change_direction == LaneChangeDirection.right and not self.prev_torque_applied:
          self.lane_change_BSM = LaneChangeBSM.right
          self.lane_change_state = LaneChangeState.preLaneChange
        else:
          # starting
          self.lane_change_BSM = LaneChangeBSM.off
          if self.lane_change_state == LaneChangeState.laneChangeStarting and lane_change_prob > 0.5:
            self.lane_change_state = LaneChangeState.laneChangeFinishing

      # starting
      #elif self.lane_change_state == LaneChangeState.laneChangeStarting and lane_change_prob > 0.5:
        #self.lane_change_state = LaneChangeState.laneChangeFinishing

      # finishing
      elif self.lane_change_state == LaneChangeState.laneChangeFinishing and lane_change_prob < 0.2:
        if one_blinker:
          self.lane_change_state = LaneChangeState.preLaneChange
        else:
          self.lane_change_state = LaneChangeState.off

    if self.lane_change_state in [LaneChangeState.off, LaneChangeState.preLaneChange]:
      self.lane_change_timer = 0.0
      if self.lane_change_BSM == LaneChangeBSM.right:
        if not lca_right:
          self.lane_change_BSM = LaneChangeBSM.off
      if self.lane_change_BSM == LaneChangeBSM.left:
        if not lca_left:
          self.lane_change_BSM = LaneChangeBSM.off
    else:
      self.lane_change_timer += DT_MDL

    if self.lane_change_state == LaneChangeState.off:
      self.pre_auto_LCA_timer = 0.0
      self.prev_torque_applied = False
    elif not (3. < self.pre_auto_LCA_timer < 10.): # stop afer 3 sec resume from 10 when torque applied
      self.pre_auto_LCA_timer += DT_MDL

    self.prev_one_blinker = one_blinker

    desire = DESIRES[self.lane_change_direction][self.lane_change_state]

    # Turn off lanes during lane change
    if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
      self.LP.l_prob = 0.
      self.LP.r_prob = 0.
      self.libmpc.init_weights(MPC_COST_LAT.PATH / 10.0, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)
    else:
      self.libmpc.init_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)

    self.LP.update_d_poly(v_ego)


    # TODO: Check for active, override, and saturation
    # if active:
    #   self.path_offset_i += self.LP.d_poly[3] / (60.0 * 20.0)
    #   self.path_offset_i = clip(self.path_offset_i, -0.5,  0.5)
    #   self.LP.d_poly[3] += self.path_offset_i
    # else:
    #   self.path_offset_i = 0.0

    # account for actuation delay
    self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, self.steerRatio, CP.steerActuatorDelay)

    v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
    self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                        list(self.LP.l_poly), list(self.LP.r_poly), list(self.LP.d_poly),
                        self.LP.l_prob, self.LP.r_prob, curvature_factor, v_ego_mpc, self.LP.lane_width)

    # reset to current steer angle if not active or overriding
    if active:
      delta_desired = self.mpc_solution[0].delta[1]
      rate_desired = math.degrees(self.mpc_solution[0].rate[0] * self.steerRatio)
    else:
      delta_desired = math.radians(angle_steers - angle_offset) / self.steerRatio
      rate_desired = 0.0

    self.cur_state[0].delta = delta_desired

    self.angle_steers_des_mpc = float(math.degrees(delta_desired * self.steerRatio) + angle_offset)

    #  Check for infeasable MPC solution
    mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
    t = sec_since_boot()
    if mpc_nans:
      self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steerRateCost)
      self.cur_state[0].delta = math.radians(angle_steers - angle_offset) / self.steerRatio

      if t > self.last_cloudlog_t + 5.0:
        self.last_cloudlog_t = t
        cloudlog.warning("Lateral mpc - nan: True")

    if self.mpc_solution[0].cost > 20000. or mpc_nans:   # TODO: find a better way to detect when MPC did not converge
      self.solution_invalid_cnt += 1
    else:
      self.solution_invalid_cnt = 0
    plan_solution_valid = self.solution_invalid_cnt < 2

    plan_send = messaging.new_message()
    plan_send.init('pathPlan')
    plan_send.valid = sm.all_alive_and_valid(service_list=['carState', 'controlsState', 'liveParameters', 'model'])
    plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
    plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
    plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
    plan_send.pathPlan.lProb = float(self.LP.l_prob)
    plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
    plan_send.pathPlan.rProb = float(self.LP.r_prob)

    plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
    plan_send.pathPlan.rateSteers = float(rate_desired)
    plan_send.pathPlan.angleOffset = float(sm['liveParameters'].angleOffsetAverage)
    plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
    plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)
    plan_send.pathPlan.sensorValid = bool(sm['liveParameters'].sensorValid)
    plan_send.pathPlan.posenetValid = bool(sm['liveParameters'].posenetValid)

    plan_send.pathPlan.desire = desire
    plan_send.pathPlan.laneChangeState = self.lane_change_state
    plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
    plan_send.pathPlan.laneChangeBSM = self.lane_change_BSM

    pm.send('pathPlan', plan_send)

    if LOG_MPC:
      dat = messaging.new_message()
      dat.init('liveMpc')
      dat.liveMpc.x = list(self.mpc_solution[0].x)
      dat.liveMpc.y = list(self.mpc_solution[0].y)
      dat.liveMpc.psi = list(self.mpc_solution[0].psi)
      dat.liveMpc.delta = list(self.mpc_solution[0].delta)
      dat.liveMpc.cost = self.mpc_solution[0].cost
      pm.send('liveMpc', dat)
Exemple #11
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost
        self.steerRatio = CP.steerRatio

        self.setup_mpc()
        self.solution_invalid_cnt = 0

        self.params = Params()

        # Lane change
        self.lane_change_enabled = self.params.get('LaneChangeEnabled') == b'1'
        self.lane_change_auto_delay = self.params.get_OpkrAutoLanechangedelay(
        )  #int( self.params.get('OpkrAutoLanechangedelay') )

        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_run_timer = 0.0
        self.lane_change_wait_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

        # atom
        self.trPATH = trace1.Loger("path")
        self.trLearner = trace1.Loger("Learner")
        self.trpathPlan = trace1.Loger("pathPlan")

        self.atom_timer_cnt = 0
        self.atom_steer_ratio = None
        self.atom_sr_boost_bp = [0., 0.]
        self.atom_sr_boost_range = [0., 0.]

    def limit_ctrl(self, value, limit, offset):
        p_limit = offset + limit
        m_limit = offset - limit
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        self.atom_timer_cnt += 1
        if self.atom_timer_cnt > 1000:
            self.atom_timer_cnt = 0

        leftBlindspot = sm['carState'].leftBlindspot
        rightBlindspot = sm['carState'].rightBlindspot

        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        steeringPressed = sm['carState'].steeringPressed
        steeringTorque = sm['carState'].steeringTorque
        active = sm['controlsState'].active
        v_ego_kph = v_ego * CV.MS_TO_KPH

        self.steerRatio = sm['liveParameters'].steerRatio
        angle_offset = sm['liveParameters'].angleOffset
        angleOffsetAverage = sm['liveParameters'].angleOffsetAverage
        stiffnessFactor = sm['liveParameters'].stiffnessFactor

        if (self.atom_timer_cnt % 100) == 0:
            str_log3 = 'angleOffset={:.1f} angleOffsetAverage={:.3f} steerRatio={:.2f} stiffnessFactor={:.3f} '.format(
                angle_offset, angleOffsetAverage, self.steerRatio,
                stiffnessFactor)
            self.trLearner.add('LearnerParam {}'.format(str_log3))

        if CP.lateralsRatom.learnerParams:
            pass
        else:
            #angle_offset = 0
            #angleOffsetAverage = 0

            # atom
            self.steer_rate_cost = sm['carParams'].steerRateCost
            self.steerRatio = sm['carParams'].steerRatio
            if self.steer_rate_cost == 0:
                self.steer_rate_cost = CP.steerRateCost

            if self.steerRatio == 0:
                self.steerRatio = CP.steerRatio

            self.steerRatio = interp(angle_steers, CP.atomTuning.sRBPV,
                                     CP.atomTuning.sRsteerRatioV)

            str_log1 = 'steerRatio={:.1f}/{:.1f} bp={} range={}'.format(
                self.steerRatio, CP.steerRatio, CP.atomTuning.sRBPV,
                CP.atomTuning.sRsteerRatioV)
            str_log2 = 'steerRateCost={:.2f}'.format(self.steer_rate_cost)
            self.trPATH.add('{} {}'.format(str_log1, str_log2))

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        VM.update_params(stiffnessFactor,
                         self.steerRatio)  # sm['liveParameters'].steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_run_timer >
                            LANE_CHANGE_TIME_MAX) or (not one_blinker) or (
                                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = steeringPressed and \
                              ((steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or \
                                (steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (sm['carState'].leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (sm['carState'].rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                self.lane_change_wait_timer += DT_MDL

                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif not blindspot_detected and (
                        torque_applied or (self.lane_change_auto_delay
                                           and self.lane_change_wait_timer >
                                           self.lane_change_auto_delay)):
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - 1.5 * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.laneChangeDone

            # done
            elif self.lane_change_state == LaneChangeState.laneChangeDone:
                if not one_blinker:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_run_timer = 0.0
        else:
            self.lane_change_run_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)
        org_angle_steers_des = self.angle_steers_des_mpc

        # atom
        if steeringPressed:
            delta_steer = self.angle_steers_des_mpc - angle_steers
            xp = [-255, 0, 255]
            fp2 = [5, 0, 5]
            limit_steers = interp(steeringTorque, xp, fp2)
            if steeringTorque < 0:  # right
                if delta_steer > 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)
            elif steeringTorque > 0:  # left
                if delta_steer < 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)

        elif v_ego_kph < 30:
            xp = [5, 15, 30]
            fp2 = [3, 5, 9]
            limit_steers = interp(v_ego_kph, xp, fp2)
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, limit_steers, angle_steers)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, self.steer_rate_cost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
        pm.send('pathPlan', plan_send)

        if self.solution_invalid_cnt > 0:
            str_log3 = 'v_ego_kph={:.1f} angle_steers_des_mpc={:.1f} angle_steers={:.1f} solution_invalid_cnt={:.0f} mpc_solution={:.1f}/{:.0f}'.format(
                v_ego_kph, self.angle_steers_des_mpc, angle_steers,
                self.solution_invalid_cnt, self.mpc_solution[0].cost, mpc_nans)
            self.trpathPlan.add('pathPlan {}'.format(str_log3))

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)
Exemple #12
0
class PathPlanner():
    def __init__(self, CP):
        self.LP = LanePlanner()
        if not travis:
            self.arne_sm = messaging_arne.SubMaster(['arne182Status'])
            self.arne_pm = messaging_arne.PubMaster(['latControl'])
        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost
        self.blindspotwait = 30
        self.setup_mpc()
        self.solution_invalid_cnt = 0
        self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'
        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False
        self.blindspotTrueCounterleft = 0
        self.blindspotTrueCounterright = 0
        self.posenetValid = True
        self.op_params = opParams()
        self.alca_nudge_required = self.op_params.get('alca_nudge_required',
                                                      default=True)
        self.alca_min_speed = self.op_params.get('alca_min_speed',
                                                 default=20.0)

        self.mpc_frame = 0
        self.sR_delay_counter = 0
        self.steerRatio_new = 0.0
        self.sR_time = 1
        self.sR_Boost = [3.5, 2.5]
        self.sR_BoostBP = [10., 22.2]
        self.sR_Boost_new = 0.0

        self.steerRatio = self.op_params.get('steer_ratio', default=12.0)
        self.sR = [self.steerRatio, self.steerRatio + self.sR_Boost_new]
        self.sRBP = [2.5, 10.0]

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        if not travis:
            self.arne_sm.update(0)
            gas_button_status = self.arne_sm['arne182Status'].gasbuttonstatus
            if gas_button_status == 1:
                self.blindspotwait = 10
            elif gas_button_status == 2:
                self.blindspotwait = 30
            else:
                self.blindspotwait = 20
            if self.arne_sm['arne182Status'].rightBlindspot:
                self.blindspotTrueCounterright = 0
            else:
                self.blindspotTrueCounterright = self.blindspotTrueCounterright + 1
            if self.arne_sm['arne182Status'].leftBlindspot:
                self.blindspotTrueCounterleft = 0
            else:
                self.blindspotTrueCounterleft = self.blindspotTrueCounterleft + 1
        else:
            self.blindspotwait = 10
            self.blindspotTrueCounterleft = 0
            self.blindspotTrueCounterright = 0
        v_ego = sm['carState'].vEgo
        angle_steers = sm['carState'].steeringAngle
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc
        #VM.update_params(sm['liveParameters'].stiffnessFactor, sm['liveParameters'].steerRatio)
        #VM.update_params(sm['liveParameters'].stiffnessFactor, CP.steerRatio)
        VM.update_params(sm['liveParameters'].stiffnessFactor, self.steerRatio)
        curvature_factor = VM.curvature_factor(v_ego)

        # Get steerRatio and steerRateCost from kegman.json every x seconds
        self.mpc_frame += 1
        if self.mpc_frame % 500 == 0:
            # live tuning through /data/openpilot/tune.py overrides interface.py settings
            self.sR_Boost_new = interp(v_ego, self.sR_BoostBP, self.sR_Boost)
            self.steerRatio = self.op_params.get('steer_ratio', default=12.0)
            self.sR = [self.steerRatio, self.steerRatio + self.sR_Boost_new]
            self.sR_time = 100

            self.mpc_frame = 0

        if v_ego > 5.0:
            # boost steerRatio by boost amount if desired steer angle is high
            self.steerRatio_new = interp(abs(angle_steers), self.sRBP, self.sR)

            self.sR_delay_counter += 1
            if self.sR_delay_counter % self.sR_time != 0:
                if self.steerRatio_new > self.steerRatio:
                    self.steerRatio = self.steerRatio_new
            else:
                self.steerRatio = self.steerRatio_new
                self.sR_delay_counter = 0
        else:
            self.steerRatio = self.sR[0]

        print("steerRatio = ", self.steerRatio)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < self.alca_min_speed * CV.MPH_TO_MS

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (
                not one_blinker
        ) or (not self.lane_change_enabled) or (
                sm['carState'].steeringPressed and
            ((sm['carState'].steeringTorque > 0
              and self.lane_change_direction == LaneChangeDirection.right) or
             (sm['carState'].steeringTorque < 0
              and self.lane_change_direction == LaneChangeDirection.left))):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            if sm['carState'].leftBlinker:
                lane_change_direction = LaneChangeDirection.left
            elif sm['carState'].rightBlinker:
                lane_change_direction = LaneChangeDirection.right

            #if self.alca_nudge_required:
            torque_applied = (sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and lane_change_direction == LaneChangeDirection.left and not sm['carState'].leftBlindspot) or \
                              (sm['carState'].steeringTorque < 0 and lane_change_direction == LaneChangeDirection.right and not sm['carState'].rightBlindspot))) or \
                             (not self.alca_nudge_required and self.blindspotTrueCounterleft > self.blindspotwait and lane_change_direction == LaneChangeDirection.left) or \
                             (not self.alca_nudge_required and self.blindspotTrueCounterright > self.blindspotwait and lane_change_direction == LaneChangeDirection.right)
            #else:
            #  torque_applied = True

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.blindspotTrueCounterleft = 0
                self.blindspotTrueCounterright = 0
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                    self.blindspotTrueCounterleft = 0
                    self.blindspotTrueCounterright = 0
                elif torque_applied:
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .2s
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - 2 * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing
                if (self.arne_sm['arne182Status'].rightBlindspot
                        and lane_change_direction == LaneChangeDirection.right
                    ) or (self.arne_sm['arne182Status'].leftBlindspot and
                          lane_change_direction == LaneChangeDirection.left):
                    self.lane_change_state = LaneChangeState.preLaneChange
                    self.blindspotTrueCounterleft = 0
                    self.blindspotTrueCounterright = 0

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + 0.5 * DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.preLaneChange
                    self.blindspotTrueCounterleft = 0
                    self.blindspotTrueCounterright = 0
                elif self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.off
                    self.blindspotTrueCounterright = 0
                    self.blindspotTrueCounterleft = 0

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_timer = 0.0
        else:
            self.lane_change_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob

        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor,
                                                 self.steerRatio,
                                                 CP.steerActuatorDelay)

        #if abs(angle_steers - angle_offset) > 4 and CP.lateralTuning.which() == 'pid': #check if this causes laggy steering
        #  self.cur_state[0].delta = math.radians(angle_steers - angle_offset) / VM.sR

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] *
                                        self.steerRatio)
        else:
            delta_desired = math.radians(angle_steers -
                                         angle_offset) / self.steerRatio
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired

        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * self.steerRatio) + angle_offset)

        #  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, CP.steerRateCost)
            self.cur_state[0].delta = math.radians(
                angle_steers - angle_offset) / self.steerRatio

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 2

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)
        plan_send.pathPlan.sensorValid = bool(sm['liveParameters'].sensorValid)
        plan_send.pathPlan.posenetValid = bool(
            sm['liveParameters'].posenetValid) or self.posenetValid
        self.posenetValid = bool(sm['liveParameters'].posenetValid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction

        pm.send('pathPlan', plan_send)

        dat = messaging.new_message('liveMpc')
        dat.liveMpc.x = list(self.mpc_solution[0].x)
        dat.liveMpc.y = list(self.mpc_solution[0].y)
        dat.liveMpc.psi = list(self.mpc_solution[0].psi)
        dat.liveMpc.delta = list(self.mpc_solution[0].delta)
        dat.liveMpc.cost = self.mpc_solution[0].cost
        pm.send('liveMpc', dat)

        msg = messaging_arne.new_message('latControl')
        msg.latControl.anglelater = math.degrees(
            list(self.mpc_solution[0].delta)[-1])
        if not travis:
            self.arne_pm.send('latControl', msg)
Exemple #13
0
class PathPlanner():
  def __init__(self, CP):
    self.LP = LanePlanner()

    self.last_cloudlog_t = 0
    self.steer_rate_cost = CP.steerRateCost

    self.setup_mpc()
    self.solution_invalid_cnt = 0
    self.lane_change_enabled = Params().get('LaneChangeEnabled') == b'1'

    if int(Params().get('OpkrAutoLaneChangeDelay')) == 0:
      self.lane_change_auto_delay = 0.0
    elif int(Params().get('OpkrAutoLaneChangeDelay')) == 1:
      self.lane_change_auto_delay = 0.5
    elif int(Params().get('OpkrAutoLaneChangeDelay')) == 2:
      self.lane_change_auto_delay = 1.0
    elif int(Params().get('OpkrAutoLaneChangeDelay')) == 3:
      self.lane_change_auto_delay = 1.5
    elif int(Params().get('OpkrAutoLaneChangeDelay')) == 4:
      self.lane_change_auto_delay = 2.0
    self.trRapidCurv = trace1.Loger("079_OPKR_RapidCurv")   
    self.lane_change_wait_timer = 0.0
    self.lane_change_state = LaneChangeState.off
    self.lane_change_direction = LaneChangeDirection.none
    self.lane_change_timer = 0.0
    self.lane_change_ll_prob = 1.0
    self.prev_one_blinker = False

    self.mpc_frame = 0

    self.lane_change_adjust = [0.2, 1.3]
    self.lane_change_adjust_vel = [16, 30]
    self.lane_change_adjust_new = 0.0

    self.new_steerRatio = CP.steerRatio

  def limit_ctrl(self, value, limit, offset ):
      p_limit = offset + limit
      m_limit = offset - limit
      if value > p_limit:
          value = p_limit
      elif  value < m_limit:
          value = m_limit
      return value

  def limit_ctrl1(self, value, limit1, limit2, offset ):
      p_limit = offset + limit1
      m_limit = offset - limit2
      if value > p_limit:
          value = p_limit
      elif  value < m_limit:
          value = m_limit
      return value     

  def setup_mpc(self):
    self.libmpc = libmpc_py.libmpc
    self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, self.steer_rate_cost)

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.cur_state[0].x = 0.0
    self.cur_state[0].y = 0.0
    self.cur_state[0].psi = 0.0
    self.cur_state[0].delta = 0.0

    self.angle_steers_des = 0.0
    self.angle_steers_des_mpc = 0.0
    self.angle_steers_des_prev = 0.0
    self.angle_steers_des_time = 0.0

  def update(self, sm, pm, CP, VM):
    limit_steers1 = 0
    limit_steers2 = 0
    debug_status = 0
    v_ego = sm['carState'].vEgo
    angle_steers = sm['carState'].steeringAngle
    active = sm['controlsState'].active
    steeringPressed = sm['carState'].steeringPressed 
    saturated_steering = sm['controlsState'].steerSaturated
    live_steerratio = sm['liveParameters'].steerRatio
    mode_select = sm['carState'].cruiseState.modeSel
    steeringTorque = sm['carState'].steeringTorque    
    angle_offset = sm['liveParameters'].angleOffset
    model_sum = sm['controlsState'].modelSum
    v_ego_kph = v_ego * CV.MS_TO_KPH    

    # Run MPC
    self.angle_steers_des_prev = self.angle_steers_des_mpc

    if saturated_steering:
      self.mpc_frame += 1
      if self.mpc_frame % 10 == 0:
        self.new_steerRatio += 0.1
        if self.new_steerRatio >= 16.5:
          self.new_steerRatio = 16.5
        self.mpc_frame = 0
    else:
      self.mpc_frame += 1
      if self.mpc_frame % 20 == 0:
        self.new_steerRatio -= 0.1
        if self.new_steerRatio <= CP.steerRatio:
          self.new_steerRatio = CP.steerRatio
        self.mpc_frame = 0

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

    curvature_factor = VM.curvature_factor(v_ego)

    self.LP.parse_model(sm['model'])

    # Lane change logic
    one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
    below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

    if sm['carState'].leftBlinker:
      self.lane_change_direction = LaneChangeDirection.left
    elif sm['carState'].rightBlinker:
      self.lane_change_direction = LaneChangeDirection.right

    if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX) or (not one_blinker) or (not self.lane_change_enabled):
      self.lane_change_state = LaneChangeState.off
      self.lane_change_direction = LaneChangeDirection.none
    else:
      torque_applied = sm['carState'].steeringPressed and \
                       ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                        (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

      blindspot_detected = ((sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or
                            (sm['carState'].rightBlindspot and self.lane_change_direction == LaneChangeDirection.right))

      lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

      # State transitions
      # off
      if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
        self.lane_change_state = LaneChangeState.preLaneChange
        self.lane_change_ll_prob = 1.0
        self.lane_change_wait_timer = 0

      # pre
      elif self.lane_change_state == LaneChangeState.preLaneChange:
        self.lane_change_wait_timer += DT_MDL
        if not one_blinker or below_lane_change_speed:
          self.lane_change_state = LaneChangeState.off
        elif not blindspot_detected and (torque_applied or (self.lane_change_auto_delay and self.lane_change_wait_timer > self.lane_change_auto_delay)):
          self.lane_change_state = LaneChangeState.laneChangeStarting

      # starting
      elif self.lane_change_state == LaneChangeState.laneChangeStarting:
        # fade out over .5s
        self.lane_change_adjust_new = interp(v_ego, self.lane_change_adjust_vel, self.lane_change_adjust)
        self.lane_change_ll_prob = max(self.lane_change_ll_prob - self.lane_change_adjust_new*DT_MDL, 0.0)
        # 98% certainty
        if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
          self.lane_change_state = LaneChangeState.laneChangeFinishing

      # finishing
      elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
        # fade in laneline over 1s
        self.lane_change_ll_prob = min(self.lane_change_ll_prob + DT_MDL, 1.0)
        if one_blinker and self.lane_change_ll_prob > 0.99:
          self.lane_change_state = LaneChangeState.laneChangeDone

      # done
      elif self.lane_change_state == LaneChangeState.laneChangeDone:
        if not one_blinker:
          self.lane_change_state = LaneChangeState.off

    if self.lane_change_state in [LaneChangeState.off, LaneChangeState.preLaneChange]:
      self.lane_change_timer = 0.0
    else:
      self.lane_change_timer += DT_MDL

    self.prev_one_blinker = one_blinker

    desire = DESIRES[self.lane_change_direction][self.lane_change_state]

    # Turn off lanes during lane change
    if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
      self.LP.l_prob *= self.lane_change_ll_prob
      self.LP.r_prob *= self.lane_change_ll_prob
    self.LP.update_d_poly(v_ego, sm)

    # account for actuation delay
    if mode_select == 3:
      self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, VM.sR, (CP.steerActuatorDelay + 0.05))
    else:
      self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, VM.sR, CP.steerActuatorDelay)

    v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
    self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                        list(self.LP.l_poly), list(self.LP.r_poly), list(self.LP.d_poly),
                        self.LP.l_prob, self.LP.r_prob, curvature_factor, v_ego_mpc, self.LP.lane_width)

    # reset to current steer angle if not active or overriding
    if active:
      delta_desired = self.mpc_solution[0].delta[1]
      rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
    else:
      delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
      rate_desired = 0.0

    self.cur_state[0].delta = delta_desired

    self.angle_steers_des_mpc = float(math.degrees(delta_desired * VM.sR) + angle_offset)

    # Atom 
    # org_angle_steers_des = self.angle_steers_des_mpc
    # delta_steer = org_angle_steers_des - angle_steers

    # if self.lane_change_state == LaneChangeState.laneChangeStarting:
    #   debug_status = 0
    #   xp = [40,70]
    #   fp2 = [5,10]
    #   limit_steers = interp( v_ego_kph, xp, fp2 )
    #   self.angle_steers_des_mpc = self.limit_ctrl( org_angle_steers_des, limit_steers, angle_steers )      
    # elif steeringPressed:
    #   debug_status = 1
    #   if angle_steers > 10 and steeringTorque > 0:
    #     delta_steer = max( delta_steer, 0 )
    #     delta_steer = min( delta_steer, DST_ANGLE_LIMIT )
    #     self.angle_steers_des_mpc = angle_steers + delta_steer
    #   elif angle_steers < -10  and steeringTorque < 0:
    #     delta_steer = min( delta_steer, 0 )
    #     delta_steer = max( delta_steer, -DST_ANGLE_LIMIT )        
    #     self.angle_steers_des_mpc = angle_steers + delta_steer
    #   else:
    #     debug_status = 2
    #     if steeringTorque < 0:  # right
    #       if delta_steer > 0:
    #         self.angle_steers_des_mpc = self.limit_ctrl( org_angle_steers_des, DST_ANGLE_LIMIT, angle_steers )
    #     elif steeringTorque > 0:  # left
    #       if delta_steer < 0:
    #         self.angle_steers_des_mpc = self.limit_ctrl( org_angle_steers_des, DST_ANGLE_LIMIT, angle_steers )
    # elif v_ego_kph < 20 : #and abs(angle_steers) < 5.0 : 
    #   debug_status = 3
    # # 저속 와리가리 제어.  
    #   xp = [5,10,20]
    #   fp2 = [1,3,7]
    #   limit_steers = interp( v_ego_kph, xp, fp2 )
    #   self.angle_steers_des_mpc = self.limit_ctrl( org_angle_steers_des, limit_steers, angle_steers )
    # elif v_ego_kph > 85: 
    #   debug_status = 4
    #   pass
    # elif abs(angle_steers) > 25: 
    # # #최대 허용 조향각 제어 로직 1.  
    #   debug_status = 5
    #   xp = [-30,-20,-10,-5,0,5,10,20,30]    # 5=>약12도, 10=>28 15=>35, 30=>52
    #   fp1 = [ 3, 5, 7, 9,11,13,15,13,11,9]    # +
    #   fp2 = [ 9,11,13,15,13,11, 9, 7, 5,3]    # -
    #   # fp1 = [ 5, 7, 9,11,13,15,17,15,12]    # +
    #   # fp2 = [12,15,17,15,13,11, 9, 7, 5]    # -
    #   # xp = [-40,-30,-20,-10,-5,0,5,10,20,30,40]    # 5=>약12도, 10=>28 15=>35, 30=>52
    #   # fp1 = [ 3, 5, 7, 9,11,13,15,17,15,12,10]    # +
    #   # fp2 = [10,12,15,17,15,13,11, 9, 7, 5, 3]    # -      
    #   limit_steers1 = interp( model_sum, xp, fp1 )  # +
    #   limit_steers2 = interp( model_sum, xp, fp2 )  # -
    #   self.angle_steers_des_mpc = self.limit_ctrl1( org_angle_steers_des, limit_steers1, limit_steers2, angle_steers )
      
    # str1 = '#/{} CVs/{} LS1/{} LS2/{} Ang/{} oDES/{} delta1/{} fDES/{} '.format(   
    #           debug_status, model_sum, limit_steers1, limit_steers2, angle_steers, org_angle_steers_des, delta_steer, self.angle_steers_des_mpc)

    # # Conan : 최대 허용 조향각 제어 로직 2.  
    # delta_steer2 = self.angle_steers_des_mpc - angle_steers
    # if delta_steer2 > DST_ANGLE_LIMIT:   
    #   p_angle_steers = angle_steers + DST_ANGLE_LIMIT
    #   self.angle_steers_des_mpc = p_angle_steers
    # elif delta_steer2 < -DST_ANGLE_LIMIT:
    #   m_angle_steers = angle_steers - DST_ANGLE_LIMIT
    #   self.angle_steers_des_mpc = m_angle_steers

    # str2 = 'delta2/{} fDES2/{}'.format(   
    #         delta_steer2, self.angle_steers_des_mpc)
    # self.trRapidCurv.add( str1 + str2 )        

    # Hoya : 가변 sR rate_cost 
    # self.sr_boost_bp = [ 10.0, 15.0, 20.0, 30.0]
    # self.sR_Cost     = [ 1.00, 0.75, 0.60, 0.30] 
    # steerRateCost  = interp(abs(angle_steers), self.sr_boost_bp, self.sR_Cost)

    #  Check for infeasable MPC solution
    mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
    t = sec_since_boot()
    if mpc_nans:
      self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE, MPC_COST_LAT.HEADING, CP.steerRateCost) # CP.steerRateCost < steerRateCost
      self.cur_state[0].delta = math.radians(angle_steers - angle_offset) / VM.sR

      if t > self.last_cloudlog_t + 5.0:
        self.last_cloudlog_t = t
        cloudlog.warning("Lateral mpc - nan: True")

    if self.mpc_solution[0].cost > 20000. or mpc_nans:   # TODO: find a better way to detect when MPC did not converge
      self.solution_invalid_cnt += 1
    else:
      self.solution_invalid_cnt = 0
    plan_solution_valid = self.solution_invalid_cnt < 3

    plan_send = messaging.new_message('pathPlan')
    plan_send.valid = sm.all_alive_and_valid(service_list=['carState', 'controlsState', 'liveParameters', 'model'])
    plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
    plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
    plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
    plan_send.pathPlan.lProb = float(self.LP.l_prob)
    plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
    plan_send.pathPlan.rProb = float(self.LP.r_prob)

    plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
    plan_send.pathPlan.rateSteers = float(rate_desired)
    plan_send.pathPlan.angleOffset = float(sm['liveParameters'].angleOffset)
    #plan_send.pathPlan.angleOffset = float(sm['liveParameters'].angleOffsetAverage)    
    plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
    plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

    plan_send.pathPlan.desire = desire
    plan_send.pathPlan.laneChangeState = self.lane_change_state
    plan_send.pathPlan.laneChangeDirection = self.lane_change_direction
    plan_send.pathPlan.steerRatio = VM.sR
    plan_send.pathPlan.steerActuatorDelay = CP.steerActuatorDelay    

    pm.send('pathPlan', plan_send)

    if LOG_MPC:
      dat = messaging.new_message('liveMpc')
      dat.liveMpc.x = list(self.mpc_solution[0].x)
      dat.liveMpc.y = list(self.mpc_solution[0].y)
      dat.liveMpc.psi = list(self.mpc_solution[0].psi)
      dat.liveMpc.delta = list(self.mpc_solution[0].delta)
      dat.liveMpc.cost = self.mpc_solution[0].cost
      pm.send('liveMpc', dat)
Exemple #14
0
class PathPlanner():
    def __init__(self, CP):
        self.PathPlan = trace1.Loger("077_R3_LQR_parhplan")

        self.LP = LanePlanner()

        self.last_cloudlog_t = 0
        self.steer_rate_cost = CP.steerRateCost

        self.setup_mpc()
        self.solution_invalid_cnt = 0

        self.params = Params()

        # Lane change
        self.lane_change_enabled = self.params.get('LaneChangeEnabled') == b'1'
        self.lane_change_auto_delay = self.params.get_OpkrAutoLanechangedelay()

        self.lane_change_state = LaneChangeState.off
        self.lane_change_direction = LaneChangeDirection.none
        self.lane_change_run_timer = 0.0
        self.lane_change_wait_timer = 0.0
        self.lane_change_ll_prob = 1.0
        self.prev_one_blinker = False

    def limit_ctrl(self, value, limit, offset):
        p_limit = offset + limit
        m_limit = offset - limit
        if value > p_limit:
            value = p_limit
        elif value < m_limit:
            value = m_limit
        return value

    def setup_mpc(self):
        self.libmpc = libmpc_py.libmpc
        self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                         MPC_COST_LAT.HEADING, self.steer_rate_cost)

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.cur_state[0].x = 0.0
        self.cur_state[0].y = 0.0
        self.cur_state[0].psi = 0.0
        self.cur_state[0].delta = 0.0

        self.angle_steers_des = 0.0
        self.angle_steers_des_mpc = 0.0
        self.angle_steers_des_prev = 0.0
        self.angle_steers_des_time = 0.0

    def update(self, sm, pm, CP, VM):
        v_ego = sm['carState'].vEgo
        v_ego_kph = v_ego * 3.61
        angle_steers = sm['carState'].steeringAngle
        steeringTorque = sm['carState'].steeringTorque
        steeringPressed = sm['carState'].steeringPressed
        active = sm['controlsState'].active

        angle_offset = sm['liveParameters'].angleOffset

        # Run MPC
        self.angle_steers_des_prev = self.angle_steers_des_mpc

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

        curvature_factor = VM.curvature_factor(v_ego)

        self.LP.parse_model(sm['model'])

        # Lane change logic
        one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
        below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN

        if sm['carState'].leftBlinker:
            self.lane_change_direction = LaneChangeDirection.left
        elif sm['carState'].rightBlinker:
            self.lane_change_direction = LaneChangeDirection.right

        if (not active) or (self.lane_change_run_timer >
                            LANE_CHANGE_TIME_MAX) or (not one_blinker) or (
                                not self.lane_change_enabled):
            self.lane_change_state = LaneChangeState.off
            self.lane_change_direction = LaneChangeDirection.none
        else:
            torque_applied = sm['carState'].steeringPressed and \
                             ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
                              (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))

            blindspot_detected = (
                (sm['carState'].leftBlindspot
                 and self.lane_change_direction == LaneChangeDirection.left) or
                (sm['carState'].rightBlindspot
                 and self.lane_change_direction == LaneChangeDirection.right))

            lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob

            # State transitions
            # off
            if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
                self.lane_change_state = LaneChangeState.preLaneChange
                self.lane_change_ll_prob = 1.0
                self.lane_change_wait_timer = 0

            # pre
            elif self.lane_change_state == LaneChangeState.preLaneChange:
                self.lane_change_wait_timer += DT_MDL

                if not one_blinker or below_lane_change_speed:
                    self.lane_change_state = LaneChangeState.off
                elif not blindspot_detected and (
                        torque_applied or (self.lane_change_auto_delay
                                           and self.lane_change_wait_timer >
                                           self.lane_change_auto_delay)):
                    self.lane_change_state = LaneChangeState.laneChangeStarting

            # starting
            elif self.lane_change_state == LaneChangeState.laneChangeStarting:
                # fade out over .5s
                # ATOM logic add
                xp = [40, 60, 70, 80]
                fp2 = [0.5, 1, 1.5, 2]
                lane_time = interp(v_ego_kph, xp, fp2)
                # <==
                self.lane_change_ll_prob = max(
                    self.lane_change_ll_prob - lane_time * DT_MDL, 0.0)
                # 98% certainty
                if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
                    self.lane_change_state = LaneChangeState.laneChangeFinishing

            # finishing
            elif self.lane_change_state == LaneChangeState.laneChangeFinishing:
                # fade in laneline over 1s
                self.lane_change_ll_prob = min(
                    self.lane_change_ll_prob + DT_MDL, 1.0)
                if one_blinker and self.lane_change_ll_prob > 0.99:
                    self.lane_change_state = LaneChangeState.laneChangeDone

            # done
            elif self.lane_change_state == LaneChangeState.laneChangeDone:
                if not one_blinker:
                    self.lane_change_state = LaneChangeState.off

        if self.lane_change_state in [
                LaneChangeState.off, LaneChangeState.preLaneChange
        ]:
            self.lane_change_run_timer = 0.0
        else:
            self.lane_change_run_timer += DT_MDL

        self.prev_one_blinker = one_blinker

        desire = DESIRES[self.lane_change_direction][self.lane_change_state]

        # Turn off lanes during lane change
        if desire == log.PathPlan.Desire.laneChangeRight or desire == log.PathPlan.Desire.laneChangeLeft:
            self.LP.l_prob *= self.lane_change_ll_prob
            self.LP.r_prob *= self.lane_change_ll_prob
        self.LP.update_d_poly(v_ego)

        # account for actuation delay
        self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                 angle_steers - angle_offset,
                                                 curvature_factor, VM.sR,
                                                 CP.steerActuatorDelay)

        v_ego_mpc = max(v_ego, 5.0)  # avoid mpc roughness due to low speed
        self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                            list(self.LP.l_poly), list(self.LP.r_poly),
                            list(self.LP.d_poly), self.LP.l_prob,
                            self.LP.r_prob, curvature_factor, v_ego_mpc,
                            self.LP.lane_width)

        # reset to current steer angle if not active or overriding
        if active:
            delta_desired = self.mpc_solution[0].delta[1]
            rate_desired = math.degrees(self.mpc_solution[0].rate[0] * VM.sR)
        else:
            delta_desired = math.radians(angle_steers - angle_offset) / VM.sR
            rate_desired = 0.0

        self.cur_state[0].delta = delta_desired
        self.angle_steers_des_mpc = float(
            math.degrees(delta_desired * VM.sR) + angle_offset)
        org_angle_steers_des = self.angle_steers_des_mpc

        # atom
        if steeringPressed:
            delta_steer = self.angle_steers_des_mpc - angle_steers
            xp = [-450, 0, 450]
            fp2 = [5, 0, 5]
            limit_steers = interp(steeringTorque, xp, fp2)
            if steeringTorque < 0:  # right
                if delta_steer > 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)
            elif steeringTorque > 0:  # left
                if delta_steer < 0:
                    self.angle_steers_des_mpc = self.limit_ctrl(
                        org_angle_steers_des, limit_steers, angle_steers)

        elif v_ego_kph < 30:  # 30
            xp = [5, 15, 30]
            fp2 = [1, 3, 5]
            limit_steers = interp(v_ego_kph, xp, fp2)
            self.angle_steers_des_mpc = self.limit_ctrl(
                org_angle_steers_des, limit_steers, angle_steers)
        # 가변 sR rate_cost
        # self.atom_sr_boost_bp = [ 1.5,  5.0, 10.0, 15.0, 20.0, 30.0, 50.0, 60.0, 100.0, 300.0]
        # self.sR_Cost          = [0.50, 0.41, 0.34, 0.28, 0.24, 0.18, 0.12, 0.10,  0.05,  0.01]
        # self.steer_rate_cost  = interp(abs(angle_steers), self.atom_sr_boost_bp, self.sR_Cost)


# # 설정값 분석을 위한 임시 코드

#     self.scale = CP.lateralTuning.lqr.scale
#     self.ki = CP.lateralTuning.lqr.ki
#     self.dc_gain = CP.lateralTuning.lqr.dcGain
#     self.steerRatio = VM.sR
#     self.laneWidth = float(self.LP.lane_width)
#     self.dPoly = [float(x) for x in self.LP.d_poly]
#     self.lPoly = [float(x) for x in self.LP.l_poly]
#     self.lProb = float(self.LP.l_prob)
#     self.rPoly = [float(x) for x in self.LP.r_poly]
#     self.rProb = float(self.LP.r_prob)

#     str2 = '/{} /{} /{} /{} /{} /{} /{} /{} /{} /{} /{} /{} /{} /{} /{}'.format(
#               v_ego_kph, angle_steers, self.angle_steers_des_mpc, angle_offset, steeringTorque, self.scale, self.ki, self.dc_gain, self.steerRatio, self.laneWidth, self.dPoly, self.lPoly, self.lProb, self.rPoly, self.rProb )
#     self.PathPlan.add( str2 )
# ###############################

#  Check for infeasable MPC solution
        mpc_nans = any(math.isnan(x) for x in self.mpc_solution[0].delta)
        t = sec_since_boot()
        if mpc_nans:
            self.libmpc.init(MPC_COST_LAT.PATH, MPC_COST_LAT.LANE,
                             MPC_COST_LAT.HEADING, self.steer_rate_cost)
            self.cur_state[0].delta = math.radians(angle_steers -
                                                   angle_offset) / VM.sR

            if t > self.last_cloudlog_t + 5.0:
                self.last_cloudlog_t = t
                cloudlog.warning("Lateral mpc - nan: True")

        if self.mpc_solution[
                0].cost > 20000. or mpc_nans:  # TODO: find a better way to detect when MPC did not converge
            self.solution_invalid_cnt += 1
        else:
            self.solution_invalid_cnt = 0
        plan_solution_valid = self.solution_invalid_cnt < 3

        plan_send = messaging.new_message('pathPlan')
        plan_send.valid = sm.all_alive_and_valid(service_list=[
            'carState', 'controlsState', 'liveParameters', 'model'
        ])
        plan_send.pathPlan.laneWidth = float(self.LP.lane_width)
        plan_send.pathPlan.dPoly = [float(x) for x in self.LP.d_poly]
        plan_send.pathPlan.lPoly = [float(x) for x in self.LP.l_poly]
        plan_send.pathPlan.lProb = float(self.LP.l_prob)
        plan_send.pathPlan.rPoly = [float(x) for x in self.LP.r_poly]
        plan_send.pathPlan.rProb = float(self.LP.r_prob)

        plan_send.pathPlan.angleSteers = float(self.angle_steers_des_mpc)
        plan_send.pathPlan.rateSteers = float(rate_desired)
        plan_send.pathPlan.angleOffset = float(
            sm['liveParameters'].angleOffsetAverage)
        plan_send.pathPlan.mpcSolutionValid = bool(plan_solution_valid)
        plan_send.pathPlan.paramsValid = bool(sm['liveParameters'].valid)

        plan_send.pathPlan.desire = desire
        plan_send.pathPlan.laneChangeState = self.lane_change_state
        plan_send.pathPlan.laneChangeDirection = self.lane_change_direction

        pm.send('pathPlan', plan_send)

        if LOG_MPC:
            dat = messaging.new_message('liveMpc')
            dat.liveMpc.x = list(self.mpc_solution[0].x)
            dat.liveMpc.y = list(self.mpc_solution[0].y)
            dat.liveMpc.psi = list(self.mpc_solution[0].psi)
            dat.liveMpc.delta = list(self.mpc_solution[0].delta)
            dat.liveMpc.cost = self.mpc_solution[0].cost
            pm.send('liveMpc', dat)