示例#1
0
class LatControlPID():
    def __init__(self, CP, CI):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)
        self.get_steer_feedforward = CI.get_steer_feedforward_function()

    def reset(self):
        self.pid.reset()

    def update(self, active, CS, CP, VM, params, last_actuators,
               desired_curvature, desired_curvature_rate):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
        pid_log.steeringRateDeg = float(CS.steeringRateDeg)

        angle_steers_des_no_offset = math.degrees(
            VM.get_steer_from_curvature(-desired_curvature, CS.vEgo,
                                        params.roll))
        angle_steers_des = angle_steers_des_no_offset + params.angleOffsetDeg

        pid_log.steeringAngleDesiredDeg = angle_steers_des
        pid_log.angleError = angle_steers_des - CS.steeringAngleDeg
        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max

            # offset does not contribute to resistive torque
            steer_feedforward = self.get_steer_feedforward(
                angle_steers_des_no_offset, CS.vEgo)

            deadzone = 0.0

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(angle_steers_des,
                                           CS.steeringAngleDeg,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, angle_steers_des, pid_log
示例#2
0
class TiciFanController(BaseFanController):
    def __init__(self) -> None:
        super().__init__()
        cloudlog.info("Setting up TICI fan handler")

        self.last_ignition = False
        self.controller = PIController(k_p=0,
                                       k_i=2e-3,
                                       k_f=1,
                                       neg_limit=-80,
                                       pos_limit=0,
                                       rate=(1 / DT_TRML))

    def update(self, max_cpu_temp: float, ignition: bool) -> int:
        self.controller.neg_limit = -(80 if ignition else 30)
        self.controller.pos_limit = -(30 if ignition else 0)

        if ignition != self.last_ignition:
            self.controller.reset()

        fan_pwr_out = -int(
            self.controller.update(setpoint=75,
                                   measurement=max_cpu_temp,
                                   feedforward=interp(
                                       max_cpu_temp, [60.0, 100.0], [0, -80])))

        self.last_ignition = ignition
        return fan_pwr_out
示例#3
0
class LatControl(object):
  def __init__(self, CP):
    self.pid = PIController((CP.steerKpBP, CP.steerKpV),
                            (CP.steerKiBP, CP.steerKiV),
                            k_f=CP.steerKf, pos_limit=1.0)
    self.last_cloudlog_t = 0.0
    self.angle_steers_des = 0.

  def reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, CP, VM, path_plan):
    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
      # constant for 0.05s.
      #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
      #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
      self.angle_steers_des = path_plan.angleSteers
      steers_max = get_steer_max(CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des   # feedforward desired angle
      if CP.steerControlType == car.CarParams.SteerControlType.torque:
        # TODO: feedforward something based on path_plan.rateSteers
        steer_feedforward -= path_plan.angleOffset   # subtract the offset, since it does not contribute to resistive torque
        steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
      deadzone = 0.0
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override,
                                     feedforward=steer_feedforward, speed=v_ego, deadzone=deadzone)

    self.sat_flag = self.pid.saturated
    return output_steer, float(self.angle_steers_des)
示例#4
0
class LatControl(object):
  def __init__(self, VM):
    self.pid = PIController(VM.CP.steerKp, VM.CP.steerKi, pos_limit=1.0)
    self.setup_mpc()

    self.y_des = -1  # Legacy

  def setup_mpc(self):
    self.libmpc = libmpc_py.libmpc
    self.libmpc.init()

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.mpc_updated = False
    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.last_mpc_ts = 0.0
    self.angle_steers_des = 0

  def reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, d_poly, angle_offset, VM, PL):
    self.mpc_updated = False
    if self.last_mpc_ts + 0.001 < PL.last_md_ts:
      self.last_mpc_ts = PL.last_md_ts

      curvature_factor = VM.curvature_factor(v_ego)

      l_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.l_poly))
      r_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.r_poly))
      p_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.p_poly))

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

      self.libmpc.run_mpc(self.cur_state, self.mpc_solution,
                          l_poly, r_poly, p_poly,
                          PL.PP.l_prob, PL.PP.r_prob, PL.PP.p_prob, curvature_factor, v_ego, PL.PP.lane_width)

      delta_desired = self.mpc_solution[0].delta[1]
      self.cur_state[0].delta = delta_desired

      self.angle_steers_des = math.degrees(delta_desired * VM.CP.sR) + angle_offset
      self.mpc_updated = True

    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      steer_max = get_steer_max(VM.CP, v_ego)
      self.pid.pos_limit = steer_max
      self.pid.neg_limit = -steer_max
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override)

    self.sat_flag = self.pid.saturated
    return output_steer
示例#5
0
class LatControlPID():
    def __init__(self, CP):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.

    def reset(self):
        self.pid.reset()

    def update(self, active, CS, CP, path_plan):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(CS.steeringAngle)
        pid_log.steerRate = float(CS.steeringRate)

        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                _c1, _c2, _c3 = [
                    0.34365576041121065, 12.845373070976711, 51.63304088261174
                ]
                steer_feedforward *= _c1 * CS.vEgo**2 + _c2 * CS.vEgo + _c3
            deadzone = 0.0

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(self.angle_steers_des,
                                           CS.steeringAngle,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
示例#6
0
class LatControlPID():
    def __init__(self, CP):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            ([0.], [CP.lateralTuning.pid.kf]),
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)

    def reset(self):
        self.pid.reset()

    def update(self, active, CS, CP, VM, params, desired_curvature,
               desired_curvature_rate):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
        pid_log.steeringRateDeg = float(CS.steeringRateDeg)

        angle_steers_des_no_offset = math.degrees(
            VM.get_steer_from_curvature(-desired_curvature, CS.vEgo))
        angle_steers_des = angle_steers_des_no_offset + params.angleOffsetDeg

        pid_log.angleError = angle_steers_des - CS.steeringAngleDeg
        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max

            # TODO: feedforward something based on lat_plan.rateSteers
            steer_feedforward = angle_steers_des_no_offset  # offset does not contribute to resistive torque
            steer_feedforward *= CS.vEgo**2  # proportional to realigning tire momentum (~ lateral accel)

            deadzone = 0.0

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(angle_steers_des,
                                           CS.steeringAngleDeg,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, angle_steers_des, pid_log
示例#7
0
class LatControlPID():
    def __init__(self, CP):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.

    def reset(self):
        self.pid.reset()

    def update(self, active, CS, CP, path_plan):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(CS.steeringAngle)
        pid_log.steerRate = float(CS.steeringRate)

        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        elif CS.vEgo < 7:  #7*3.6km
            self.angle_steers_des = path_plan.angleSteers * 0.5
        else:
            self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= CS.vEgo**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = int(Params().get('IgnoreZone')) * 0.1

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(self.angle_steers_des,
                                           CS.steeringAngle,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
示例#8
0
class LatControlPID(object):
    def __init__(self, CP):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0)
        self.angle_steers_des = 0.

    def reset(self):
        self.pid.reset()

    def update(self, active, v_ego, angle_steers, angle_steers_rate,
               steer_override, CP, VM, path_plan):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(angle_steers)
        pid_log.steerRate = float(angle_steers_rate)

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
            # constant for 0.05s.
            #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
            #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
            self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = 0.0
            output_steer = self.pid.update(self.angle_steers_des,
                                           angle_steers,
                                           check_saturation=(v_ego > 10),
                                           override=steer_override,
                                           feedforward=steer_feedforward,
                                           speed=v_ego,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        self.sat_flag = self.pid.saturated
        return output_steer, float(self.angle_steers_des), pid_log
示例#9
0
class LatControl(object):
  def __init__(self, CP):
    self.pid = PIController((CP.steerKpBP, CP.steerKpV),
                            (CP.steerKiBP, CP.steerKiV),
                            k_f=CP.steerKf, pos_limit=1.0)
    self.last_cloudlog_t = 0.0
    self.dampened_angle_steers = 0.
    self.angle_steers_des = 0.
    self.sat_time = 0.0

  def reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, CP, VM, path_plan):
    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
      # constant for 0.05s.
      #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
      #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
      self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

      steers_max = get_steer_max(CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des   # feedforward desired angle
      if CP.steerControlType == car.CarParams.SteerControlType.torque:
        # TODO: feedforward something based on path_plan.rateSteers
        steer_feedforward -= path_plan.angleOffset   # subtract the offset, since it does not contribute to resistive torque
        steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
      deadzone = 0.0
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override,
                                     feedforward=steer_feedforward, speed=v_ego, deadzone=deadzone)

    # Reset sat_flat always, set it only if needed
    self.sat_flag = False

    # If PID is saturated, set time which it was saturated
    if self.pid.saturated and self.sat_time < 0.5:
      self.sat_time = sec_since_boot()

    # To save cycles, nest in sat_time check
    if self.sat_time > 0.5:
      # If its been saturated for 1.5 seconds then set flag
      if (sec_since_boot() - self.sat_time) > 0.7:
        self.sat_flag = True

      # If it is no longer saturated, clear the sat flag and timer
      if not self.pid.saturated:
        self.sat_time = 0.0

    return output_steer, float(self.angle_steers_des)
示例#10
0
class LatControlPID():
    def __init__(self, CP):
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.

    def reset(self):
        self.pid.reset()

    def update(self, active, v_ego, angle_steers, angle_steers_rate,
               eps_torque, steer_override, rate_limited, CP, path_plan):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(angle_steers)
        pid_log.steerRate = float(angle_steers_rate)

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            self.angle_steers_des = path_plan.angleSteers

            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = 0.0

            check_saturation = (v_ego >
                                10) and not rate_limited and not steer_override
            output_steer = self.pid.update(self.angle_steers_des,
                                           angle_steers,
                                           check_saturation=check_saturation,
                                           override=steer_override,
                                           feedforward=steer_feedforward,
                                           speed=v_ego,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        # we only deal with Tesla, so we return two angles, no torque info
        return float(self.angle_steers_des), path_plan.angleSteers, pid_log
示例#11
0
class LatControl(object):
  def __init__(self, CP):
    self.pid = PIController((CP.steerKpBP, CP.steerKpV),
                            (CP.steerKiBP, CP.steerKiV),
                            k_f=CP.steerKf, pos_limit=1.0)
    self.last_cloudlog_t = 0.0
    self.angle_steers_des = 0.
    
    # TODO: add the feedforward parameters to LiveParameters
    self.angle_ff_gain = 2.0
    self.rate_ff_gain = 0.2
    self.angle_ff_bp = [[0.5, 5.0],[0.0, 1.0]]
    
  def reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, CP, VM, path_plan):
    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
      # constant for 0.05s.
      #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
      #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
      self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

      steers_max = get_steer_max(CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des   # feedforward desired angle
      if CP.steerControlType == car.CarParams.SteerControlType.torque:
        angle_feedforward = steer_feedforward - path_plan.angleOffset
        angle_ff_ratio = interp(abs(angle_feedforward), self.angle_ff_bp[0], self.angle_ff_bp[1])
        angle_feedforward *= angle_ff_ratio * self.angle_ff_gain
        rate_feedforward = (1.0 - angle_ff_ratio) * self.rate_ff_gain * path_plan.rateSteers
        steer_feedforward = v_ego**2 * (rate_feedforward + angle_feedforward)
        
      deadzone = 0.0
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override,
                                     feedforward=steer_feedforward, speed=v_ego, deadzone=deadzone)
    
    self.sat_flag = self.pid.saturated
    return output_steer, float(self.angle_steers_des)    
示例#12
0
class LongControl():
    def __init__(self, CP):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController(
            (CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
            (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
            k_f=CP.longitudinalTuning.kf,
            rate=1 / DT_CTRL)
        self.v_pid = 0.0
        self.last_output_accel = 0.0

    def reset(self, v_pid):
        """Reset PID controller and change setpoint"""
        self.pid.reset()
        self.v_pid = v_pid

    def update(self, active, CS, CP, long_plan, accel_limits):
        """Update longitudinal control. This updates the state machine and runs a PID loop"""
        # Interp control trajectory
        # TODO estimate car specific lag, use .15s for now
        speeds = long_plan.speeds
        if len(speeds) == CONTROL_N:
            v_target_lower = interp(CP.longitudinalActuatorDelayLowerBound,
                                    T_IDXS[:CONTROL_N], speeds)
            a_target_lower = 2 * (
                v_target_lower - speeds[0]
            ) / CP.longitudinalActuatorDelayLowerBound - long_plan.accels[0]

            v_target_upper = interp(CP.longitudinalActuatorDelayUpperBound,
                                    T_IDXS[:CONTROL_N], speeds)
            a_target_upper = 2 * (
                v_target_upper - speeds[0]
            ) / CP.longitudinalActuatorDelayUpperBound - long_plan.accels[0]
            a_target = min(a_target_lower, a_target_upper)

            v_target = speeds[0]
            v_target_future = speeds[-1]
        else:
            v_target = 0.0
            v_target_future = 0.0
            a_target = 0.0

        # TODO: This check is not complete and needs to be enforced by MPC
        a_target = clip(a_target, ACCEL_MIN_ISO, ACCEL_MAX_ISO)

        self.pid.neg_limit = accel_limits[0]
        self.pid.pos_limit = accel_limits[1]

        # Update state machine
        output_accel = self.last_output_accel
        self.long_control_state = long_control_state_trans(
            CP, active, self.long_control_state, CS.vEgo, v_target_future,
            CS.brakePressed, CS.cruiseState.standstill)

        if self.long_control_state == LongCtrlState.off or CS.gasPressed:
            self.reset(CS.vEgo)
            output_accel = 0.

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            self.v_pid = v_target

            # Toyota starts braking more when it thinks you want to stop
            # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
            prevent_overshoot = not CP.stoppingControl and CS.vEgo < 1.5 and v_target_future < 0.7 and v_target_future < self.v_pid
            deadzone = interp(CS.vEgo, CP.longitudinalTuning.deadzoneBP,
                              CP.longitudinalTuning.deadzoneV)
            freeze_integrator = prevent_overshoot

            output_accel = self.pid.update(self.v_pid,
                                           CS.vEgo,
                                           speed=CS.vEgo,
                                           deadzone=deadzone,
                                           feedforward=a_target,
                                           freeze_integrator=freeze_integrator)

            if prevent_overshoot:
                output_accel = min(output_accel, 0.0)

        # Intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # Keep applying brakes until the car is stopped
            if not CS.standstill or output_accel > CP.stopAccel:
                output_accel -= CP.stoppingDecelRate * DT_CTRL
            output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
            self.reset(CS.vEgo)

        self.last_output_accel = output_accel
        final_accel = clip(output_accel, accel_limits[0], accel_limits[1])

        return final_accel
示例#13
0
class LongControl():
  def __init__(self, CP, compute_gb):
    self.long_control_state = LongCtrlState.off  # initialized to off
    self.pid = PIController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
                            (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
                            CP.longitudinalTuning.kf,
                            rate=RATE,
                            sat_limit=0.8,
                            convert=compute_gb)
    self.v_pid = 0.0
    self.last_output_gb = 0.0

  def reset(self, v_pid):
    """Reset PID controller and change setpoint"""
    self.pid.reset()
    self.v_pid = v_pid

  def update(self, active, CS, v_target, v_target_future, a_target, CP, radarState = None):
    """Update longitudinal control. This updates the state machine and runs a PID loop"""
    # Actuation limits
    gas_max = interp(CS.vEgo, CP.gasMaxBP, CP.gasMaxV)
    brake_max = interp(CS.vEgo, CP.brakeMaxBP, CP.brakeMaxV)

    # Update state machine
    output_gb = self.last_output_gb

    d_rel = -1.
    if radarState is not None and radarState.leadOne.status:
      lead = radarState.leadOne
      following = lead.dRel < 45.0 and lead.vLeadK > CS.vEgo and lead.aLeadK > 0.0
      d_rel = lead.dRel
    else:
      following = False

    if not following:
      gas_max *= 0.9

    self.long_control_state = long_control_state_trans(active, self.long_control_state, CS.vEgo,
                                                       v_target_future, self.v_pid, output_gb,
                                                       CS.brakePressed, CS.cruiseState.standstill, CP.minSpeedCan, d_rel)

    v_ego_pid = max(CS.vEgo, CP.minSpeedCan)  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

    if self.long_control_state == LongCtrlState.off or CS.gasPressed:
      self.reset(v_ego_pid)
      output_gb = 0.

    # tracking objects and driving
    elif self.long_control_state == LongCtrlState.pid:
      self.v_pid = v_target
      self.pid.pos_limit = gas_max
      self.pid.neg_limit = - brake_max

      # Toyota starts braking more when it thinks you want to stop
      # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
      prevent_overshoot = not CP.stoppingControl and CS.vEgo < 1.5 and v_target_future < 0.7
      deadzone = interp(v_ego_pid, CP.longitudinalTuning.deadzoneBP, CP.longitudinalTuning.deadzoneV)

      if d_rel > 0.:
        a_target *= interp(d_rel, [10., 25., 70.], [1.0, 0.8, 0.6])

      output_gb = self.pid.update(self.v_pid, v_ego_pid, speed=v_ego_pid, deadzone=deadzone, feedforward=a_target, freeze_integrator=prevent_overshoot)

      if prevent_overshoot:
        output_gb = min(output_gb, 0.0)

    # Intention is to stop, switch to a different brake control until we stop
    elif self.long_control_state == LongCtrlState.stopping:
      # Keep applying brakes until the car is stopped
      if not CS.standstill or output_gb > -BRAKE_STOPPING_TARGET:
        output_gb -= CP.stoppingBrakeRate / RATE
      output_gb = clip(output_gb, -brake_max, gas_max)

      self.reset(CS.vEgo)

    # Intention is to move again, release brake fast before handing control to PID
    elif self.long_control_state == LongCtrlState.starting:
      if output_gb < -0.2:
        output_gb += CP.startingBrakeRate / RATE
      self.reset(CS.vEgo)

    self.last_output_gb = output_gb
    final_gas = clip(output_gb, 0., gas_max)
    final_brake = -clip(output_gb, -brake_max, 0.)

    return final_gas, final_brake
示例#14
0
class LatControlPID():
    def __init__(self, CP):
        self.kegman = kegman_conf(CP)
        self.deadzone = float(self.kegman.conf['deadzone'])
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.
        self.mpc_frame = 500

        self.BP0 = 4
        self.steer_Kf1 = [0.00003, 0.00003]
        self.steer_Ki1 = [0.02, 0.04]
        self.steer_Kp1 = [0.18, 0.20]

        self.steer_Kf2 = [0.00005, 0.00005]
        self.steer_Ki2 = [0.04, 0.05]
        self.steer_Kp2 = [0.20, 0.25]

        self.pid_change_flag = 0
        self.pre_pid_change_flag = 0
        self.pid_BP0_time = 0

        self.movAvg = moveavg1.MoveAvg()
        self.v_curvature = 256
        self.model_sum = 0
        self.path_x = np.arange(192)

    def calc_va(self, sm, v_ego):
        md = sm['model']
        if len(md.path.poly):
            path = list(md.path.poly)

            self.l_poly = np.array(md.leftLane.poly)
            self.r_poly = np.array(md.rightLane.poly)
            self.p_poly = np.array(md.path.poly)

            # Curvature of polynomial https://en.wikipedia.org/wiki/Curvature#Curvature_of_the_graph_of_a_function
            # y = a x^3 + b x^2 + c x + d, y' = 3 a x^2 + 2 b x + c, y'' = 6 a x + 2 b
            # k = y'' / (1 + y'^2)^1.5
            # TODO: compute max speed without using a list of points and without numpy
            y_p = 3 * path[0] * self.path_x**2 + 2 * path[
                1] * self.path_x + path[2]
            y_pp = 6 * path[0] * self.path_x + 2 * path[1]
            curv = y_pp / (1. + y_p**2)**1.5

            #print( 'curv={}'.format( curv ) )

            a_y_max = 2.975 - v_ego * 0.0375  # ~1.85 @ 75mph, ~2.6 @ 25mph
            v_curvature = np.sqrt(a_y_max / np.clip(np.abs(curv), 1e-4, None))
            model_speed = np.min(v_curvature)
            model_speed = max(30.0 * CV.KPH_TO_MS,
                              model_speed)  # Don't slow down below 20mph

            model_sum = curv[2] * 1000.  #np.sum( curv, 0 )

            model_speed = model_speed * CV.MS_TO_KPH
            if model_speed > MAX_SPEED:
                model_speed = MAX_SPEED
        else:
            model_speed = MAX_SPEED
            model_sum = 0

        #following = lead_1.status and lead_1.dRel < 45.0 and lead_1.vLeadK > v_ego and lead_1.aLeadK > 0.0

        #following = CS.lead_distance < 100.0
        #accel_limits = [float(x) for x in calc_cruise_accel_limits(v_ego, following)]
        #jerk_limits = [min(-0.1, accel_limits[0]), max(0.1, accel_limits[1])]  # TODO: make a separate lookup for jerk tuning
        #accel_limits_turns = limit_accel_in_turns(v_ego, CS.angle_steers, accel_limits, self.steerRatio, self.wheelbase )

        model_speed = self.movAvg.get_min(model_speed, 10)
        return model_speed, model_sum

    def update_state(self, sm, CS):
        self.v_curvature, self.model_sum = self.calc_va(sm, CS.vEgo)

    def reset(self):
        self.pid.reset()

    def linear2_tune(self, CP, v_ego):  # angle(조향각에 의한 변화)
        cv_angle = abs(self.angle_steers_des)
        cv = [2, 15]  # angle
        # Kp
        fKp1 = [float(self.steer_Kp1[0]), float(self.steer_Kp1[1])]
        fKp2 = [float(self.steer_Kp2[0]), float(self.steer_Kp2[1])]
        self.steerKp1 = interp(cv_angle, cv, fKp1)
        self.steerKp2 = interp(cv_angle, cv, fKp2)
        self.steerKpV = [float(self.steerKp1), float(self.steerKp2)]

        # Ki
        fKi1 = [float(self.steer_Ki1[0]), float(self.steer_Ki1[1])]
        fKi2 = [float(self.steer_Ki2[0]), float(self.steer_Ki2[1])]
        self.steerKi1 = interp(cv_angle, cv, fKi1)
        self.steerKi2 = interp(cv_angle, cv, fKi2)
        self.steerKiV = [float(self.steerKi1), float(self.steerKi2)]

        # kf
        fKf1 = [float(self.steer_Kf1[0]), float(self.steer_Kf1[1])]
        fKf2 = [float(self.steer_Kf2[0]), float(self.steer_Kf2[1])]
        self.steerKf1 = interp(cv_angle, cv, fKf1)
        self.steerKf2 = interp(cv_angle, cv, fKf2)

        xp = CP.lateralTuning.pid.kpBP
        fp = [float(self.steerKf1), float(self.steerKf2)]
        self.steerKfV = interp(v_ego, xp, fp)

        self.pid.gain((CP.lateralTuning.pid.kpBP, self.steerKpV),
                      (CP.lateralTuning.pid.kiBP, self.steerKiV),
                      k_f=self.steerKfV)

    def linear_tune(self, CP, v_ego):  # 곡률에 의한 변화.
        cv_value = self.v_curvature
        cv = [100, 200]  # 곡률
        # Kp
        fKp1 = [float(self.steer_Kp1[1]), float(self.steer_Kp1[0])]
        fKp2 = [float(self.steer_Kp2[1]), float(self.steer_Kp2[0])]
        self.steerKp1 = interp(cv_value, cv, fKp1)
        self.steerKp2 = interp(cv_value, cv, fKp2)
        self.steerKpV = [float(self.steerKp1), float(self.steerKp2)]

        # Ki
        fKi1 = [float(self.steer_Ki1[1]), float(self.steer_Ki1[0])]
        fKi2 = [float(self.steer_Ki2[1]), float(self.steer_Ki2[0])]
        self.steerKi1 = interp(cv_value, cv, fKi1)
        self.steerKi2 = interp(cv_value, cv, fKi2)
        self.steerKiV = [float(self.steerKi1), float(self.steerKi2)]

        # kf
        fKf1 = [float(self.steer_Kf1[1]), float(self.steer_Kf1[0])]
        fKf2 = [float(self.steer_Kf2[1]), float(self.steer_Kf2[0])]
        self.steerKf1 = interp(cv_value, cv, fKf1)
        self.steerKf2 = interp(cv_value, cv, fKf2)

        xp = CP.lateralTuning.pid.kpBP
        fp = [float(self.steerKf1), float(self.steerKf2)]
        self.steerKfV = interp(v_ego, xp, fp)

        self.pid.gain((CP.lateralTuning.pid.kpBP, self.steerKpV),
                      (CP.lateralTuning.pid.kiBP, self.steerKiV),
                      k_f=self.steerKfV)

    def sR_tune(self, CP, v_ego, path_plan):
        kBP0 = 0
        if self.pid_change_flag == 0:
            pass
        elif abs(path_plan.angleSteers) > self.BP0 or self.v_curvature < 200:
            kBP0 = 1
            self.pid_change_flag = 2

            ##
            self.pid_BP0_time = 300
        elif self.pid_BP0_time:
            kBP0 = 1
            self.pid_BP0_time -= 1
        else:
            kBP0 = 0
            self.pid_change_flag = 3

        self.steerKpV = [
            float(self.steer_Kp1[kBP0]),
            float(self.steer_Kp2[kBP0])
        ]
        self.steerKiV = [
            float(self.steer_Ki1[kBP0]),
            float(self.steer_Ki2[kBP0])
        ]

        xp = CP.lateralTuning.pid.kpBP
        fp = [float(self.steer_Kf1[kBP0]), float(self.steer_Kf2[kBP0])]
        self.steerKfV = interp(v_ego, xp, fp)

        if self.pid_change_flag != self.pre_pid_change_flag:
            self.pre_pid_change_flag = self.pid_change_flag
            self.pid.gain((CP.lateralTuning.pid.kpBP, self.steerKpV),
                          (CP.lateralTuning.pid.kiBP, self.steerKiV),
                          k_f=self.steerKfV)

            #self.pid = PIController((CP.lateralTuning.pid.kpBP, self.steerKpV),
            #                        (CP.lateralTuning.pid.kiBP, self.steerKiV),
            #                         k_f=self.steerKfV, pos_limit=1.0)

    def live_tune(self, CP, path_plan, v_ego):
        self.mpc_frame += 1
        if self.mpc_frame % 600 == 0:
            # live tuning through /data/openpilot/tune.py overrides interface.py settings
            self.kegman = kegman_conf()
            if self.kegman.conf['tuneGernby'] == "1":
                self.steerKf = float(self.kegman.conf['Kf'])

                self.BP0 = float(self.kegman.conf['sR_BP0'])
                self.steer_Kp1 = [
                    float(self.kegman.conf['Kp']),
                    float(self.kegman.conf['sR_Kp'])
                ]
                self.steer_Ki1 = [
                    float(self.kegman.conf['Ki']),
                    float(self.kegman.conf['sR_Ki'])
                ]
                self.steer_Kf1 = [
                    float(self.kegman.conf['Kf']),
                    float(self.kegman.conf['sR_Kf'])
                ]

                self.steer_Kp2 = [
                    float(self.kegman.conf['Kp2']),
                    float(self.kegman.conf['sR_Kp2'])
                ]
                self.steer_Ki2 = [
                    float(self.kegman.conf['Ki2']),
                    float(self.kegman.conf['sR_Ki2'])
                ]
                self.steer_Kf2 = [
                    float(self.kegman.conf['Kf2']),
                    float(self.kegman.conf['sR_Kf2'])
                ]

                self.deadzone = float(self.kegman.conf['deadzone'])
                self.mpc_frame = 0
                if not self.pid_change_flag:
                    self.pid_change_flag = 1

        self.linear2_tune(CP, v_ego)

        #self.linear_tune( CP, v_ego )
        #self.sR_tune( CP, v_ego, path_plan )

    def update(self, active, v_ego, angle_steers, angle_steers_rate,
               eps_torque, steer_override, rate_limited, CP, path_plan):

        self.angle_steers_des = path_plan.angleSteers
        self.live_tune(CP, path_plan, v_ego)

        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(angle_steers)
        pid_log.steerRate = float(angle_steers_rate)

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            #self.angle_steers_des = 0.0
            self.pid.reset()
            #self.angle_steers_des = path_plan.angleSteers
        else:
            #self.angle_steers_des = path_plan.angleSteers
            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle

            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)

            if abs(self.angle_steers_des) > self.BP0:
                deadzone = 0
            else:
                deadzone = self.deadzone

            check_saturation = (v_ego >
                                10) and not rate_limited and not steer_override
            output_steer = self.pid.update(self.angle_steers_des,
                                           angle_steers,
                                           check_saturation=check_saturation,
                                           override=steer_override,
                                           feedforward=steer_feedforward,
                                           speed=v_ego,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
class LatControlPID():
    def __init__(self, CP):
        self.trPID = trace1.Loger("pid")
        self.angle_steers_des = 0.

        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            sat_limit=CP.steerLimitTimer)

    def reset(self):
        self.pid.reset()

    def atom_tune(self, v_ego_kph, sr_value, CP):  # 조향각에 따른 변화.
        self.sr_KPH = CP.atomTuning.sRKPH
        self.sr_BPV = CP.atomTuning.sRBPV
        self.sR_pid_KiV = CP.atomTuning.sRpidKiV
        self.sR_pid_KpV = CP.atomTuning.sRpidKpV

        self.Ki = []
        self.Kp = []
        self.Ms = []

        nPos = 0
        for angle in self.sr_BPV:  # angle
            self.Ki.append(interp(sr_value, angle, self.sR_pid_KiV[nPos]))
            self.Kp.append(interp(sr_value, angle, self.sR_pid_KpV[nPos]))
            nPos += 1
            if nPos > 10:
                break

        for kph in self.sr_KPH:
            self.Ms.append(kph * CV.KPH_TO_MS)

        #rt_Ki = interp( v_ego_kph, self.sr_KPH, self.Ki )
        #rt_Kp  = interp( v_ego_kph, self.sr_KPH, self.Kp )

        return self.Ms, self.Ki, self.Kp

    def linear2_tune(self, CS, CP):  # angle(조향각에 의한 변화)
        v_ego_kph = CS.vEgo * CV.MS_TO_KPH
        sr_value = self.angle_steers_des
        MsV, KiV, KpV = self.atom_tune(v_ego_kph, sr_value, CP)
        self.pid.gain((MsV, KpV), (MsV, KiV), k_f=self.steerKf)

    def update(self, active, CS, CP, path_plan):
        self.angle_steers_des = path_plan.angleSteers
        self.linear2_tune(CS, CP)

        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(CS.steeringAngle)
        pid_log.steerRate = float(CS.steeringRate)

        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.reset()
        else:
            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= CS.vEgo**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = self.deadzone  #0.1

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(self.angle_steers_des,
                                           CS.steeringAngle,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
示例#16
0
class LatControlPID():
    def __init__(self, CP):
        self.kegman = kegman_conf(CP)
        self.deadzone = float(self.kegman.conf['deadzone'])
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.

        self.Mid_Smoother = St_Smoother.Steer_Mid_Smoother()

        self.mpc_frame = 0

    def reset(self):
        self.pid.reset()

    def live_tune(self, CP):
        self.mpc_frame += 1
        if self.mpc_frame % 300 == 0:
            # live tuning through /data/openpilot/tune.py overrides interface.py settings
            self.kegman = kegman_conf()
            if self.kegman.conf['tuneGernby'] == "1":
                self.steerKpV = [float(self.kegman.conf['Kp'])]
                self.steerKiV = [float(self.kegman.conf['Ki'])]
                self.steerKf = float(self.kegman.conf['Kf'])
                self.pid = PIController(
                    (CP.lateralTuning.pid.kpBP, self.steerKpV),
                    (CP.lateralTuning.pid.kiBP, self.steerKiV),
                    k_f=self.steerKf,
                    pos_limit=1.0)
                self.deadzone = float(self.kegman.conf['deadzone'])

            self.mpc_frame = 0

    def update(self, active, CS, CP, path_plan):
        self.live_tune(CP)

        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(CS.steeringAngle)
        pid_log.steerRate = float(CS.steeringRate)

        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            if CS.vEgo < 7.0:  # ristrict to 25km
                self.angle_steers_des = self.Mid_Smoother.get_data(
                    path_plan.angleSteers, 0.3)
            elif CS.vEgo < 15.0:  # ristrict to 54km
                self.angle_steers_des = self.Mid_Smoother.get_data(
                    path_plan.angleSteers, 0.45)
            elif CS.vEgo < 20.0:  # ristrict to 72km
                self.angle_steers_des = self.Mid_Smoother.get_data(
                    path_plan.angleSteers, 0.6)
            elif CS.vEgo < 25.0:  # ristrict to 90km
                self.angle_steers_des = self.Mid_Smoother.get_data(
                    path_plan.angleSteers, 0.8)
            elif CS.vEgo < 35.0:  # ristrict to 125km
                self.angle_steers_des = self.Mid_Smoother.get_data(
                    path_plan.angleSteers, 0.9)
            else:
                self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= CS.vEgo**2  # proportional to realigning tire momentum (~ lateral accel)

            deadzone = self.deadzone

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            output_steer = self.pid.update(self.angle_steers_des,
                                           CS.steeringAngle,
                                           check_saturation=check_saturation,
                                           override=CS.steeringPressed,
                                           feedforward=steer_feedforward,
                                           speed=CS.vEgo,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
示例#17
0
class LatControlPID(object):
  def __init__(self, CP):
    self.kegman = kegman_conf(CP)
    self.kegtime_prev = 0
    self.profiler = Profiler(False, 'LaC')
    self.frame = 0
    self.pid = PIController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
                            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
                            k_f=CP.lateralTuning.pid.kf)
    self.angle_steers_des = 0.
    self.polyReact = 1.
    self.poly_damp = 0
    self.poly_factor = CP.lateralTuning.pid.polyFactor
    self.poly_scale = CP.lateralTuning.pid.polyScale
    self.path_error_comp = 0.0
    self.damp_angle_steers = 0.
    self.damp_angle_rate = 0.
    self.damp_steer = 0.1
    self.react_steer = 0.01
    self.react_mpc = 0.0
    self.damp_mpc = 0.25
    self.angle_ff_ratio = 0.0
    self.angle_ff_gain = 1.0
    self.rate_ff_gain = CP.lateralTuning.pid.rateFFGain
    self.angle_ff_bp = [[0.5, 5.0],[0.0, 1.0]]
    self.lateral_offset = 0.0
    self.previous_integral = 0.0
    self.damp_angle_steers= 0.0
    self.damp_rate_steers_des = 0.0
    self.damp_angle_steers_des = 0.0
    self.limited_damp_angle_steers_des = 0.0
    self.old_plan_count = 0
    self.last_plan_time = 0
    self.lane_change_adjustment = 1.0
    self.angle_index = 0.
    self.avg_plan_age = 0.
    self.min_index = 0
    self.max_index = 0
    self.prev_angle_steers = 0.
    self.c_prob = 0.
    self.deadzone = 0.
    self.starting_angle = 0.
    self.projected_lane_error = 0.
    self.prev_projected_lane_error = 0.
    self.path_index = None #np.arange((30.))*100.0/15.0
    self.angle_rate_des = 0.0    # degrees/sec, rate dynamically limited by accel_limit
    self.fast_angles = [[]]
    self.center_angles = []
    self.live_tune(CP)
    self.react_index = 0.0
    self.next_params_put = 36000
    self.zero_poly_crossed = 0
    self.zero_steer_crossed = 0

    try:
      params = Params()
      lateral_params = params.get("LateralGain")
      lateral_params = json.loads(lateral_params)
      self.angle_ff_gain = max(1.0, float(lateral_params['angle_ff_gain']))
    except:
      self.angle_ff_gain = 1.0

  def live_tune(self, CP):
    if self.frame % 300 == 0:
      (mode, ino, dev, nlink, uid, gid, size, atime, mtime, self.kegtime) = os.stat(os.path.expanduser('~/kegman.json'))
      if self.kegtime != self.kegtime_prev:
        try:
          time.sleep(0.0001)
          self.kegman = kegman_conf() 
        except:
          print("   Kegman error")
        self.pid._k_i = ([0.], [float(self.kegman.conf['Ki'])])
        self.pid._k_p = ([0.], [float(self.kegman.conf['Kp'])])
        self.pid.k_f = (float(self.kegman.conf['Kf']))
        self.damp_steer = (float(self.kegman.conf['dampSteer']))
        self.react_steer = (float(self.kegman.conf['reactSteer']))
        self.react_mpc = (float(self.kegman.conf['reactMPC']))
        self.damp_mpc = (float(self.kegman.conf['dampMPC']))
        self.deadzone = float(self.kegman.conf['deadzone'])
        self.polyReact = min(11, max(0, int(10 * float(self.kegman.conf['polyReact']))))
        self.poly_damp = min(1, max(0, float(self.kegman.conf['polyDamp'])))
        self.poly_factor = max(0.0, float(self.kegman.conf['polyFactor']) * 0.001)
        self.require_blinker = bool(int(self.kegman.conf['requireBlinker']))
        self.require_nudge = bool(int(self.kegman.conf['requireNudge']))
        self.react_center = [max(0, float(self.kegman.conf['reactCenter0'])),max(0, float(self.kegman.conf['reactCenter1'])),max(0, float(self.kegman.conf['reactCenter2'])), 0]
        self.kegtime_prev = self.kegtime

  def update_lane_state(self, angle_steers, driver_opposing_lane, blinker_on, path_plan):
    if self.require_nudge:
      if self.lane_changing > 0.0: # and path_plan.cProb > 0:
        self.lane_changing += 0.01  # max(self.lane_changing + 0.01, 0.005 * abs(path_plan.lPoly[5] + path_plan.rPoly[5]))
        if self.lane_changing > 2.75 or (not blinker_on and self.lane_changing < 1.0 and abs(path_plan.cPoly[5]) < 100 and min(abs(self.starting_angle - angle_steers), abs(self.angle_steers_des - angle_steers)) < 1.5 and path_plan.cPoly[14] * path_plan.cPoly[0] > 0):
          self.lane_changing = 0.0
          self.stage = "4"
        elif 2.25 <= self.lane_changing < 2.5 and path_plan.cPoly[14] * path_plan.cPoly[4] > 0:   # abs(path_plan.lPoly[5] + path_plan.rPoly[5]) < abs(path_plan.cPoly[5]):
          self.lane_changing = 2.5
          self.stage = "3"
        elif 2.0 <= self.lane_changing < 2.25 and path_plan.cPoly[14] * path_plan.cPoly[9] > 0:      # (path_plan.lPoly[5] + path_plan.rPoly[5]) * path_plan.cPoly[0] < 0:
          self.lane_changing = 2.25
          self.stage = "2"
        elif self.lane_changing < 2.0 and path_plan.cPoly[14] * path_plan.cPoly[0] < 0:     #path_plan.laneWidth < 1.2 * abs(path_plan.lPoly[5] + path_plan.rPoly[5]):
          self.lane_changing = 2.0
          self.stage = "1"
        elif self.lane_changing < 1.0 and abs(path_plan.cPoly[14]) > abs(path_plan.cPoly[7]):     #path_plan.laneWidth < 1.2 * abs(path_plan.lPoly[5] + path_plan.rPoly[5]):
          self.lane_changing = 0.98
          self.stage = "0"
        #else:
        #self.lane_changing = max(self.lane_changing + 0.01, 0.005 * abs(path_plan.lPoly[5] + path_plan.rPoly[5]))
        #if blinker_on:
        #  self.lane_change_adjustment = 0.0
        #else:
        self.lane_change_adjustment = interp(self.lane_changing, [0.0, 1.0, 2.0, 2.25, 2.5, 2.75], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
        #print("%0.2f lane_changing  %0.2f adjustment  %0.2f p_poly   %0.2f avg_poly   stage = %s    blinker %d   opposing %d  width_1  %0.2f  width_2  %0.2f  center_1  %0.2f  center_2  %0.2f" % (self.lane_changing, self.lane_change_adjustment, path_plan.cPoly[5], path_plan.lPoly[5] + path_plan.rPoly[5], self.stage, blinker_on, driver_opposing_lane, path_plan.laneWidth, 0.6 * abs(path_plan.lPoly[5] - path_plan.rPoly[5]), path_plan.cPoly[0], path_plan.lPoly[5] + path_plan.rPoly[5]))
      elif (blinker_on or not self.require_blinker) and driver_opposing_lane and path_plan.rProb > 0 and path_plan.lProb > 0 and (abs(path_plan.cPoly[14]) > 100 or min(abs(self.starting_angle - angle_steers), abs(self.angle_steers_des - angle_steers)) > 1.0): # and path_plan.cPoly[14] * path_plan.cPoly[0] > 0:
        print('starting lane change @ %0.2f' % self.lane_changing)
        self.lane_changing = 0.01 
        self.lane_change_adjustment = 1.0
      else:
        if self.lane_changing != 0: print('terminating lane change @ %0.2f' % self.lane_changing)
        self.lane_changing = 0
        self.stage = "0"
        self.starting_angle = angle_steers
        self.lane_change_adjustment = 1.0
    elif blinker_on:
      self.lane_change_adjustment = 0.0
    else:
      self.lane_change_adjustment = 1.0
      
  def reset(self):
    self.pid.reset()

  def adjust_angle_gain(self):
    if (self.pid.f > 0) == (self.pid.i > 0) and abs(self.pid.i) >= abs(self.previous_integral):
      if not abs(self.pid.f + self.pid.i) > 1: self.angle_ff_gain *= 1.0001
    elif self.angle_ff_gain > 1.0:
      self.angle_ff_gain *= 0.9999
    self.previous_integral = self.pid.i

  def update(self, active, brake_pressed, v_ego, angle_steers, angle_steers_rate, steer_override, CP, path_plan, canTime, blinker_on):
    self.profiler.checkpoint('controlsd')
    pid_log = car.CarState.LateralPIDState.new_message()
    path_age = (time.time() * 1000 - path_plan.sysTime) * 1e-3
    if (angle_steers - path_plan.angleOffset >= 0) == (self.prev_angle_steers < 0):
      self.zero_steer_crossed = time.time()
    self.prev_angle_steers = angle_steers - path_plan.angleOffset

    if path_plan.canTime != self.last_plan_time and len(path_plan.fastAngles) > 1:
      time.sleep(0.00001)
      if path_age > 0.23: self.old_plan_count += 1
      if self.path_index is None:
        self.avg_plan_age = path_age
        self.path_index = np.arange((len(path_plan.fastAngles)))*100.0/15.0
      self.last_plan_time = path_plan.canTime
      self.avg_plan_age += 0.01 * (path_age - self.avg_plan_age)
      self.c_prob = path_plan.cProb
      self.projected_lane_error = float(min(0.5, max(-0.5, self.c_prob * self.poly_factor * sum(np.array(path_plan.cPoly)))))
      self.center_angles.append(float(self.projected_lane_error))
      if len(self.center_angles) > 15: self.center_angles.pop(0)
      if (self.projected_lane_error >= 0) == (self.prev_projected_lane_error < 0):
        self.zero_poly_crossed = time.time()
      self.prev_projected_lane_error = self.projected_lane_error
      if time.time() - min(self.zero_poly_crossed, self.zero_steer_crossed) < 4:
        self.projected_lane_error -= (float(self.c_prob * self.poly_damp * self.center_angles[0]))
      self.fast_angles = np.array(path_plan.fastAngles)
      self.profiler.checkpoint('path_plan')

    if path_plan.paramsValid: self.angle_index = max(0., 100. * (self.react_mpc + path_age))
    self.min_index = min(self.min_index, self.angle_index)
    self.max_index = max(self.max_index, self.angle_index)

    if self.frame % 300 == 0 and self.frame > 0:
      print("old plans:  %d  avg plan age:  %0.3f   min index:  %d  max_index:  %d   center_steer:  %0.2f" % (self.old_plan_count, self.avg_plan_age, self.min_index, self.max_index, self.path_error_comp))
      self.min_index = 100
      self.max_index = 0

    self.profiler.checkpoint('update')
    self.frame += 1
    self.live_tune(CP)
    self.profiler.checkpoint('live_tune')

    if v_ego < 0.3 or not path_plan.paramsValid:
      if self.frame > self.next_params_put and v_ego == 0 and brake_pressed:
        self.next_params_put = self.frame + 36000
        put_nonblocking("LateralGain", json.dumps({'angle_ff_gain': self.angle_ff_gain}))
        self.profiler.checkpoint('params_put')
      output_steer = 0.0
      self.stage = "0"
      self.lane_changing = 0.0
      self.previous_integral = 0.0
      self.previous_lane_error = 0.0
      self.path_error_comp = 0.0
      self.damp_angle_steers= 0.0
      self.damp_rate_steers_des = 0.0 
      self.damp_angle_steers_des = 0.0
      pid_log.active = False
      self.pid.reset()
      self.profiler.checkpoint('pid_reset')
    else:
      try:
        pid_log.active = True
        if False and blinker_on and steer_override:
          self.path_error_comp *= 0.9
          self.damp_angle_steers = angle_steers
          self.angle_steers_des = angle_steers
          self.damp_angle_steers_des = angle_steers
          self.limited_damp_angle_steers_des = angle_steers
          self.angle_rate_des = 0
        else:
          react_steer = self.react_steer + self.react_center[min(len(self.react_center)-1, int(abs(angle_steers - path_plan.angleOffset)))]
          self.damp_angle_steers += (angle_steers + angle_steers_rate * (self.damp_steer + float(react_steer)) - self.damp_angle_steers) / max(1.0, self.damp_steer * 100.)
          self.angle_steers_des = interp(self.angle_index, self.path_index, self.fast_angles[min(len(self.fast_angles)-1, int(self.polyReact))])
          self.damp_angle_steers_des += (self.angle_steers_des - self.damp_angle_steers_des + self.projected_lane_error) / max(1.0, self.damp_mpc * 100.)
          if (self.damp_angle_steers - self.damp_angle_steers_des) * (angle_steers - self.damp_angle_steers_des) < 0:
            self.damp_angle_steers = self.damp_angle_steers_des

        angle_feedforward = float(self.damp_angle_steers_des - path_plan.angleOffset) + float(self.path_error_comp)
        self.angle_ff_ratio = float(gernterp(abs(angle_feedforward), self.angle_ff_bp[0], self.angle_ff_bp[1]))
        rate_feedforward = (1.0 - self.angle_ff_ratio) * self.rate_ff_gain * self.angle_rate_des
        steer_feedforward = float(v_ego)**2 * (rate_feedforward + angle_feedforward * self.angle_ff_ratio * self.angle_ff_gain)

        if not steer_override and v_ego > 10.0:
          if abs(angle_steers) > (self.angle_ff_bp[0][1] / 2.0):
            self.adjust_angle_gain()
          else:
            self.previous_integral = self.pid.i

        if path_plan.cProb == 0 or (angle_feedforward > 0) == (self.pid.p > 0) or (path_plan.cPoly[-1] > 0) == (self.pid.p > 0):
          p_scale = 1.0 
        else:
          p_scale = max(0.2, min(1.0, 1 / abs(angle_feedforward)))
        self.profiler.checkpoint('pre-pid')

        output_steer = self.pid.update(self.damp_angle_steers_des, self.damp_angle_steers, check_saturation=(v_ego > 10), override=steer_override, p_scale=p_scale,
                                      add_error=0, feedforward=steer_feedforward, speed=v_ego, deadzone=self.deadzone if abs(angle_feedforward) < 1 else 0.0)
        self.profiler.checkpoint('pid_update')

      except:
        output_steer = 0
        print("  angle error!")
        pass
    
      #driver_opposing_op = steer_override and (angle_steers - self.prev_angle_steers) * output_steer < 0
      #self.update_lane_state(angle_steers, driver_opposing_op, blinker_on, path_plan)
      #self.profiler.checkpoint('lane_change')

    output_factor = self.lane_change_adjustment if pid_log.active else 0

    if self.lane_change_adjustment < 1 and self.lane_changing > 0:
      self.damp_angle_steers_des = self.angle_steers_des
      self.limit_damp_angle_steers_des = self.angle_steers_des

    self.prev_override = steer_override
    self.pid.f *= output_factor
    self.pid.i *= output_factor
    self.pid.p *= output_factor
    output_steer *= output_factor
    pid_log.p = float(self.pid.p)
    pid_log.i = float(self.pid.i)
    pid_log.f = float(self.pid.f)
    pid_log.output = float(output_steer)
    pid_log.p2 = float(self.projected_lane_error)
    pid_log.saturated = bool(self.pid.saturated)
    pid_log.angleFFRatio = self.angle_ff_ratio
    pid_log.steerAngle = float(self.damp_angle_steers)
    pid_log.steerAngleDes = float(self.damp_angle_steers_des)
    self.sat_flag = self.pid.saturated
    self.profiler.checkpoint('post_update')

    if self.frame % 5000 == 1000 and self.profiler.enabled:
      self.profiler.display()
      self.profiler.reset(True)

    return output_steer, float(self.angle_steers_des), pid_log
示例#18
0
class LongControl():
    def __init__(self, CP):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController(
            (CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
            (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
            rate=RATE,
            sat_limit=0.8)
        self.v_pid = 0.0
        self.last_output_accel = 0.0

    def reset(self, v_pid):
        """Reset PID controller and change setpoint"""
        self.pid.reset()
        self.v_pid = v_pid

    def update(self, active, CS, CP, long_plan, accel_limits):
        """Update longitudinal control. This updates the state machine and runs a PID loop"""
        # Interp control trajectory
        # TODO estimate car specific lag, use .15s for now
        if len(long_plan.speeds) == CONTROL_N:
            v_target = interp(DEFAULT_LONG_LAG, T_IDXS[:CONTROL_N],
                              long_plan.speeds)
            v_target_future = long_plan.speeds[-1]
            a_target = 2 * (v_target - long_plan.speeds[0]
                            ) / DEFAULT_LONG_LAG - long_plan.accels[0]
        else:
            v_target = 0.0
            v_target_future = 0.0
            a_target = 0.0

        self.pid.neg_limit = accel_limits[0]
        self.pid.pos_limit = accel_limits[1]

        # Update state machine
        output_accel = self.last_output_accel
        self.long_control_state = long_control_state_trans(
            active, self.long_control_state, CS.vEgo, v_target_future,
            self.v_pid, output_accel, CS.brakePressed,
            CS.cruiseState.standstill, CP.minSpeedCan)

        v_ego_pid = max(
            CS.vEgo, CP.minSpeedCan
        )  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

        if self.long_control_state == LongCtrlState.off or CS.gasPressed:
            self.reset(v_ego_pid)
            output_accel = 0.

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            self.v_pid = v_target

            # Toyota starts braking more when it thinks you want to stop
            # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
            prevent_overshoot = not CP.stoppingControl and CS.vEgo < 1.5 and v_target_future < 0.7
            deadzone = interp(v_ego_pid, CP.longitudinalTuning.deadzoneBP,
                              CP.longitudinalTuning.deadzoneV)
            freeze_integrator = prevent_overshoot

            output_accel = self.pid.update(self.v_pid,
                                           v_ego_pid,
                                           speed=v_ego_pid,
                                           deadzone=deadzone,
                                           feedforward=a_target,
                                           freeze_integrator=freeze_integrator)

            if prevent_overshoot:
                output_accel = min(output_accel, 0.0)

        # Intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # Keep applying brakes until the car is stopped
            if not CS.standstill or output_accel > -DECEL_STOPPING_TARGET:
                output_accel -= CP.stoppingDecelRate / RATE
            output_accel = clip(output_accel, accel_limits[0], accel_limits[1])

            self.reset(CS.vEgo)

        # Intention is to move again, release brake fast before handing control to PID
        elif self.long_control_state == LongCtrlState.starting:
            if output_accel < -DECEL_THRESHOLD_TO_PID:
                output_accel += CP.startingAccelRate / RATE
            self.reset(CS.vEgo)

        self.last_output_accel = output_accel
        final_accel = clip(output_accel, accel_limits[0], accel_limits[1])

        return final_accel, v_target, a_target
示例#19
0
class LongControl(object):
    def __init__(self, CP, compute_gb):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController((CP.longitudinalKpBP, CP.longitudinalKpV),
                                (CP.longitudinalKiBP, CP.longitudinalKiV),
                                rate=100.0,
                                sat_limit=0.8,
                                convert=compute_gb)
        self.v_pid = 0.0
        self.last_output_gb = 0.0

    def reset(self, v_pid):
        self.pid.reset()
        self.v_pid = v_pid

    def update(self, active, v_ego, brake_pressed, standstill,
               cruise_standstill, v_cruise, v_target, v_target_future,
               a_target, CP, lead_1):
        # actuation limits
        gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV)
        brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV)

        output_gb = self.last_output_gb
        rate = 100.0
        self.long_control_state = long_control_state_trans(
            active, self.long_control_state, v_ego, v_target_future,
            self.v_pid, output_gb, brake_pressed, cruise_standstill)

        v_ego_pid = max(
            v_ego, MIN_CAN_SPEED
        )  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

        # *** long control behavior based on state
        if self.long_control_state == LongCtrlState.off:
            self.v_pid = v_ego_pid  # do nothing
            output_gb = 0.
            self.pid.reset()

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            prevent_overshoot = not CP.stoppingControl and v_ego < 1.5 and v_target_future < 0.7

            self.v_pid = v_target
            self.pid.pos_limit = gas_max
            self.pid.neg_limit = -brake_max
            deadzone = interp(v_ego_pid, CP.longPidDeadzoneBP,
                              CP.longPidDeadzoneV)
            output_gb = self.pid.update(self.v_pid,
                                        v_ego_pid,
                                        speed=v_ego_pid,
                                        deadzone=deadzone,
                                        feedforward=a_target,
                                        freeze_integrator=prevent_overshoot)
            if prevent_overshoot:
                output_gb = min(output_gb, 0.0)

        # intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # TODO: use the standstill bit from CAN to detect movements, usually more accurate than looking at v_ego
            if not standstill or output_gb > -brake_stopping_target:
                output_gb -= stopping_brake_rate / rate
            output_gb = clip(output_gb, -brake_max, gas_max)

            self.v_pid = v_ego
            self.pid.reset()

        # intention is to move again, release brake fast before handling control to PID
        elif self.long_control_state == LongCtrlState.starting:
            if output_gb < -0.2:
                output_gb += starting_brake_rate / rate
            self.v_pid = v_ego
            self.pid.reset()

        self.last_output_gb = output_gb
        final_gas = clip(output_gb, 0., gas_max)
        final_brake = -clip(output_gb, -brake_max, 0.)

        return final_gas, final_brake
示例#20
0
class LatControl(object):
  def __init__(self, VM):
    self.pid = PIController((VM.CP.steerKpBP, VM.CP.steerKpV),
                            (VM.CP.steerKiBP, VM.CP.steerKiV),
                            k_f=VM.CP.steerKf, pos_limit=1.0)
    self.last_cloudlog_t = 0.0
    self.setup_mpc(VM.CP.steerRateCost)

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

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.mpc_updated = False
    self.mpc_nans = False
    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.last_mpc_ts = 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
    self.blindspot_blink_counter_left_check = 0
    self.blindspot_blink_counter_right_check = 0
    

  def reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, d_poly, angle_offset, VM, PL,blindspot,leftBlinker,rightBlinker):
    cur_time = sec_since_boot()
    self.mpc_updated = False
    # TODO: this creates issues in replay when rewinding time: mpc won't run
    if self.last_mpc_ts < PL.last_md_ts:
      self.last_mpc_ts = PL.last_md_ts
      self.angle_steers_des_prev = self.angle_steers_des_mpc

      curvature_factor = VM.curvature_factor(v_ego)

      l_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.l_poly))
      r_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.r_poly))
      p_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.p_poly))

      # account for actuation delay
      self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers, curvature_factor, VM.CP.steerRatio, VM.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,
                          l_poly, r_poly, p_poly,
                          PL.PP.l_prob, PL.PP.r_prob, PL.PP.p_prob, curvature_factor, v_ego_mpc, PL.PP.lane_width)

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

      self.cur_state[0].delta = delta_desired

      self.angle_steers_des_mpc = float(math.degrees(delta_desired * VM.CP.steerRatio) + angle_offset)
      if leftBlinker:
        if self.blindspot_blink_counter_left_check > 150:
          self.angle_steers_des_mpc += 0#15
      if rightBlinker:
        if self.blindspot_blink_counter_right_check > 150:
          self.angle_steers_des_mpc -= 0#15
      self.angle_steers_des_time = cur_time
      self.mpc_updated = True

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

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

    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
      # constant for 0.05s.
      #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
      #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
      self.angle_steers_des = self.angle_steers_des_mpc
      steers_max = get_steer_max(VM.CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des   # feedforward desired angle
      if VM.CP.steerControlType == car.CarParams.SteerControlType.torque:
        steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
      deadzone = 0.0
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override, feedforward=steer_feedforward, speed=v_ego, deadzone=deadzone)
      if rightBlinker:
        if blindspot:
          self.blindspot_blink_counter_right_check = 0
          print "debug: blindspot detected"
        self.blindspot_blink_counter_right_check += 1
        if self.blindspot_blink_counter_right_check > 150:
          self.angle_steers_des -= 0#15

      else:
        self.blindspot_blink_counter_right_check = 0
      
      if leftBlinker:
        if blindspot:
          self.blindspot_blink_counter_left_check = 0
          print "debug: blindspot detected"
        self.blindspot_blink_counter_left_check += 1
        if self.blindspot_blink_counter_left_check > 150:
          self.angle_steers_des += 0#15
      else:
        self.blindspot_blink_counter_left_check = 0

    self.sat_flag = self.pid.saturated
    return output_steer, float(self.angle_steers_des)
示例#21
0
class LatControl(object):
  def __init__(self, VM):
    self.pid = PIController(VM.CP.steerKp, VM.CP.steerKi, k_f=VM.CP.steerKf, pos_limit=1.0)
    self.last_cloudlog_t = 0.0
    self.setup_mpc(VM.CP.steerRateCost)

  def setup_mpc(self, steer_rate_cost):
    self.libmpc = libmpc_py.libmpc
    self.libmpc.init(steer_rate_cost)

    self.mpc_solution = libmpc_py.ffi.new("log_t *")
    self.cur_state = libmpc_py.ffi.new("state_t *")
    self.mpc_updated = False
    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.last_mpc_ts = 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 reset(self):
    self.pid.reset()

  def update(self, active, v_ego, angle_steers, steer_override, d_poly, angle_offset, VM, PL):
    cur_time = sec_since_boot()
    self.mpc_updated = False
    if self.last_mpc_ts < PL.last_md_ts:
      self.last_mpc_ts = PL.last_md_ts
      self.angle_steers_des_prev = self.angle_steers_des_mpc

      curvature_factor = VM.curvature_factor(v_ego)

      l_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.l_poly))
      r_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.r_poly))
      p_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.p_poly))

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

      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,
                          l_poly, r_poly, p_poly,
                          PL.PP.l_prob, PL.PP.r_prob, PL.PP.p_prob, curvature_factor, v_ego_mpc, PL.PP.lane_width)

      delta_desired = self.mpc_solution[0].delta[1]
      self.cur_state[0].delta = delta_desired

      self.angle_steers_des_mpc = float(math.degrees(delta_desired * VM.CP.steerRatio) + angle_offset)
      self.angle_steers_des_time = cur_time
      self.mpc_updated = True

      #  Check for infeasable MPC solution
      nans = np.any(np.isnan(list(self.mpc_solution[0].delta)))
      t = sec_since_boot()
      if nans:
        self.libmpc.init(VM.CP.steerRateCost)
        self.cur_state[0].delta = math.radians(angle_steers) / VM.CP.steerRatio

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

    if v_ego < 0.3 or not active:
      output_steer = 0.0
      self.pid.reset()
    else:
      # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
      # constant for 0.05s.
      #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
      #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
      self.angle_steers_des = self.angle_steers_des_mpc
      steers_max = get_steer_max(VM.CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des * v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override, feedforward=steer_feedforward)

    self.sat_flag = self.pid.saturated
    return output_steer
示例#22
0
class LongControl(object):
    def __init__(self, CP, compute_gb):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController(
            (CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
            (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
            k_f=1.0,
            rate=RATE,
            sat_limit=0.8,
            convert=compute_gb)
        self.v_pid = 0.0
        self.last_output_gb = 0.0
        self.lastdecelForTurn = False
        self.last_lead = None
        self.freeze = False
        self.none_count = 0

    def reset(self, v_pid):
        """Reset PID controller and change setpoint"""
        self.pid.reset()
        self.v_pid = v_pid

    def dynamic_gas(self, v_ego, gas_interceptor, gas_button_status):
        dynamic = False
        if gas_interceptor:
            if gas_button_status == 0:
                dynamic = True
                x = [
                    0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781,
                    8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703,
                    24.46614, 29.05805, 32.71015, 35.76326
                ]
                y = [
                    0.2, 0.20443, 0.21592, 0.23334, 0.25734, 0.27916, 0.3229,
                    0.34784, 0.36765, 0.38, 0.396, 0.409, 0.425, 0.478, 0.55,
                    0.621, 0.7
                ]
            elif gas_button_status == 1:
                y = [0.25, 0.9, 0.9]
            elif gas_button_status == 2:
                y = [0.2, 0.5, 0.7]
        else:
            if gas_button_status == 0:
                dynamic = True
                x = [
                    0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781,
                    8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703,
                    24.46614, 29.05805, 32.71015, 35.76326
                ]
                y = [
                    0.35, 0.47, 0.43, 0.35, 0.3, 0.3, 0.3229, 0.34784, 0.36765,
                    0.38, 0.396, 0.409, 0.425, 0.478, 0.55, 0.621, 0.7
                ]
            elif gas_button_status == 1:
                y = [0.9, 0.95, 0.99]
            elif gas_button_status == 2:
                y = [0.25, 0.2, 0.2]

        if not dynamic:
            x = [0., 9., 35.]  # default BP values

        accel = interp(v_ego, x, y)

        if self.none_count < 10 and self.last_lead is not None and self.last_lead.status:  # if returned nones is less than 10, last lead is not none, and last lead's status is true assume lead
            v_rel = self.last_lead.vRel
            #a_lead = self.last_lead.aLeadK  # to use later
            #x_lead = self.last_lead.dRel
        else:
            v_rel = None
            #a_lead = None
            #x_lead = None

        if dynamic and v_rel is not None:  # dynamic gas profile specific operations, and if lead
            if v_ego < 6.7056:  # if under 15 mph
                x = [1.61479, 1.99067, 2.28537, 2.49888, 2.6312, 2.68224]
                y = [
                    -accel, -(accel / 1.06), -(accel / 1.2), -(accel / 1.8),
                    -(accel / 4.4), 0
                ]  # array that matches current chosen accel value
                accel += interp(v_rel, x, y)
            else:
                x = [-0.89408, 0, 0.89408, 4.4704]
                y = [-.15, -.05, .005, .05]
                accel += interp(v_rel, x, y)

        min_return = 0.0
        max_return = 1.0
        return round(max(min(accel, max_return), min_return),
                     5)  # ensure we return a value between range

    def update(self, active, v_ego, brake_pressed, standstill,
               cruise_standstill, v_cruise, v_target, v_target_future,
               a_target, CP, gas_interceptor, gas_button_status, decelForTurn,
               longitudinalPlanSource, lead_one, gas_pressed):
        """Update longitudinal control. This updates the state machine and runs a PID loop"""
        # Actuation limits
        if lead_one is not None:
            self.last_lead = lead_one
            self.none_count = 0
        else:
            self.none_count = clip(self.none_count + 1, 0, 10)

        #gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV)
        gas_max = self.dynamic_gas(v_ego, gas_interceptor, gas_button_status)
        brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV)

        # Update state machine
        output_gb = self.last_output_gb
        self.long_control_state = long_control_state_trans(
            active, self.long_control_state, v_ego, v_target_future,
            self.v_pid, output_gb, brake_pressed, cruise_standstill)

        v_ego_pid = max(
            v_ego, MIN_CAN_SPEED
        )  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

        if self.long_control_state == LongCtrlState.off:
            self.v_pid = v_ego_pid
            self.pid.reset()
            output_gb = 0.

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            self.v_pid = v_target
            self.pid.pos_limit = gas_max
            self.pid.neg_limit = -brake_max

            # Toyota starts braking more when it thinks you want to stop
            # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
            prevent_overshoot = not CP.stoppingControl and v_ego < 1.5 and v_target_future < 0.7
            deadzone = interp(v_ego_pid, CP.longitudinalTuning.deadzoneBP,
                              CP.longitudinalTuning.deadzoneV)

            if longitudinalPlanSource == 'cruise':
                if decelForTurn and not self.lastdecelForTurn:
                    self.lastdecelForTurn = True
                    self.pid._k_p = (CP.longitudinalTuning.kpBP, [
                        x * 0 for x in CP.longitudinalTuning.kpV
                    ])
                    self.pid._k_i = (CP.longitudinalTuning.kiBP, [
                        x * 0 for x in CP.longitudinalTuning.kiV
                    ])
                    self.pid.i = 0.0
                    self.pid.k_f = 1.0
                if self.lastdecelForTurn and not decelForTurn:
                    self.lastdecelForTurn = False
                    self.pid._k_p = (CP.longitudinalTuning.kpBP,
                                     CP.longitudinalTuning.kpV)
                    self.pid._k_i = (CP.longitudinalTuning.kiBP,
                                     CP.longitudinalTuning.kiV)
                    self.pid.k_f = 1.0
            else:
                self.lastdecelForTurn = False
                self.pid._k_p = (CP.longitudinalTuning.kpBP,
                                 CP.longitudinalTuning.kpV)
                self.pid._k_i = (CP.longitudinalTuning.kiBP,
                                 CP.longitudinalTuning.kiV)
                self.pid.k_f = 1.0
            if gas_pressed or brake_pressed:
                if not self.freeze:
                    self.pid.i = 0.0
                    self.freeze = True
            else:
                if self.freeze:
                    self.freeze = False

            output_gb = self.pid.update(self.v_pid,
                                        v_ego_pid,
                                        speed=v_ego_pid,
                                        deadzone=deadzone,
                                        feedforward=a_target,
                                        freeze_integrator=(prevent_overshoot
                                                           or gas_pressed
                                                           or brake_pressed))

            if prevent_overshoot:
                output_gb = min(output_gb, 0.0)

        # Intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # Keep applying brakes until the car is stopped
            if not standstill or output_gb > -BRAKE_STOPPING_TARGET:
                output_gb -= STOPPING_BRAKE_RATE / RATE
            output_gb = clip(output_gb, -brake_max, gas_max)

            self.v_pid = v_ego
            self.pid.reset()

        # Intention is to move again, release brake fast before handing control to PID
        elif self.long_control_state == LongCtrlState.starting:
            if output_gb < -0.2:
                output_gb += STARTING_BRAKE_RATE / RATE
            self.v_pid = v_ego
            self.pid.reset()

        self.last_output_gb = output_gb
        final_gas = clip(output_gb, 0., gas_max)
        final_brake = -clip(output_gb, -brake_max, 0.)

        return final_gas, final_brake
示例#23
0
class LatControl(object):
    def __init__(self, CP):
        self.pid = PIController((CP.steerKpBP, CP.steerKpV),
                                (CP.steerKiBP, CP.steerKiV),
                                k_f=CP.steerKf,
                                pos_limit=1.0)
        self.last_cloudlog_t = 0.0
        self.setup_mpc(CP.steerRateCost)

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

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.mpc_updated = False
        self.mpc_nans = False
        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.last_mpc_ts = 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

        # For Variable Steering Ratio
        self.lowSteerRatio = 9.0  # Set the lowest possible steering ratio allowed
        self.vsrWindowLow = 0.1  # Set the tire/car angle low-end used for VSR (vsrWindowLow - is same as lowSteerRatio)
        self.vsrWindowHigh = 0.65  # Set the tire/car angle high-end (vsrWindowHigh + is same as CP.steerRatio / interface.py)
        self.manual_Steering_Offset = 0.0  # Set a steering wheel offset. (Should this be * steering ratio to get the steering wheel angle?)
        self.variableSteerRatio = 0.0  # Used to store the calculated steering ratio
        self.angle_Check = 0.0  # Used for desired tire/car angle
        self.vsrSlope = 0.0  # Used for slope intercept formula
        self.vsrYIntercept = 0.0  # Used for slope intercept formula

    def reset(self):
        self.pid.reset()

    def update(self, active, v_ego, angle_steers, steer_override, d_poly,
               angle_offset, CP, VM, PL):
        cur_time = sec_since_boot()
        self.mpc_updated = False
        # TODO: this creates issues in replay when rewinding time: mpc won't run
        if self.last_mpc_ts < PL.last_md_ts:
            self.last_mpc_ts = PL.last_md_ts
            self.angle_steers_des_prev = self.angle_steers_des_mpc

            curvature_factor = VM.curvature_factor(v_ego)

            l_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.l_poly))
            r_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.r_poly))
            p_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.p_poly))

            # Prius (and Prime) appears to have a variable steering ratio. Try to account for that
            # Random maths:
            #  https://www.desmos.com/calculator
            #  https://www.calculator.net/slope-calculator.html
            #  (steering wheel angle / steering ratio) = tire angle ..
            # So, if the calculation below is determining the tire angle, look for values under about 1.5 degrees
            self.angle_Check = angle_steers - angle_offset
            if abs(self.angle_Check
                   ) < self.vsrWindowLow:  # 0.3 degrees, for example
                self.variableSteerRatio = self.lowSteerRatio  # Use the lower ratio
            elif self.vsrWindowLow < abs(
                    self.angle_Check
            ) < self.vsrWindowHigh:  # The VSR transition zone
                # Begin the _variable_ part
                # Find the slope of the line from the start of the VSR window to the end of the window - ( m = (y1 - y) / (x1 - x) )
                self.vsrSlope = (self.lowSteerRatio - CP.steerRatio) / (
                    self.vsrWindowLow - self.vsrWindowHigh)
                # Solve for b (y-intercept) - (b = y - mx)
                self.vsrYIntercept = (CP.steerRatio -
                                      self.vsrSlope) * self.vsrWindowHigh
                # Use b to find y - (y = mx + b)
                self.variableSteerRatio = (
                    self.vsrSlope * self.angle_Check) + self.vsrYIntercept
                if not self.lowSteerRatio <= self.variableSteerRatio <= CP.steerRatio:  # Sanity/safety check
                    if self.variableSteerRatio < self.lowSteerRatio:
                        self.variableSteerRatio = self.lowSteerRatio  # Reset to the low ratio
                    elif self.variableSteerRatio > CP.steerRatio:
                        self.variableSteerRatio = CP.steerRatio  # Reset to steerRatio from interface.py
            else:  # The angle is in the quick zone so do nothing
                self.variableSteerRatio = CP.steerRatio  # Use steerRatio from interface.py

            # account for actuation delay
            self.cur_state = calc_states_after_delay(self.cur_state, v_ego,
                                                     angle_steers,
                                                     curvature_factor,
                                                     self.variableSteerRatio,
                                                     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, l_poly,
                                r_poly, p_poly, PL.PP.l_prob, PL.PP.r_prob,
                                PL.PP.p_prob, curvature_factor, v_ego_mpc,
                                PL.PP.lane_width)

            # reset to current steer angle if not active or overriding
            if active:
                delta_desired = self.mpc_solution[0].delta[1]
            else:  # Add a steering offset vs recalibrating steering sensor so it reads near 0
                delta_desired = math.radians(
                    self.angle_Check) / self.variableSteerRatio
            self.cur_state[0].delta = delta_desired

            self.angle_steers_des_mpc = float(
                math.degrees(delta_desired * self.variableSteerRatio) +
                angle_offset + self.manual_Steering_Offset)
            self.angle_steers_des_time = cur_time
            self.mpc_updated = True

            #  Check for infeasable MPC solution
            self.mpc_nans = np.any(np.isnan(list(self.mpc_solution[0].delta)))
            t = sec_since_boot()
            if self.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) / self.variableSteerRatio

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

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            self.pid.reset()
        else:
            # TODO: ideally we should interp, but for tuning reasons we keep the mpc solution
            # constant for 0.05s.
            #dt = min(cur_time - self.angle_steers_des_time, _DT_MPC + _DT) + _DT  # no greater than dt mpc + dt, to prevent too high extraps
            #self.angle_steers_des = self.angle_steers_des_prev + (dt / _DT_MPC) * (self.angle_steers_des_mpc - self.angle_steers_des_prev)
            self.angle_steers_des = self.angle_steers_des_mpc
            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = 0.0
            output_steer = self.pid.update(self.angle_steers_des,
                                           angle_steers,
                                           check_saturation=(v_ego > 10),
                                           override=steer_override,
                                           feedforward=steer_feedforward,
                                           speed=v_ego,
                                           deadzone=deadzone)

        self.sat_flag = self.pid.saturated
        return output_steer, float(self.angle_steers_des)
示例#24
0
class LatControlPID():
    def __init__(self, CP):
        self.factor = 1.

        self.lowpid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            sat_limit=CP.steerLimitTimer)

        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            neg_limit=-1.0,
            sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.
        self.increasing = False
        self.dualpids = False

    def reset(self):
        self.pid.reset()
        self.lowpid.reset()
        self.increasing = False

    def dualPIDinit(self, CP):
        self.factor = CP.lateralParams.torqueBP[1] / CP.lateralParams.torqueBP[
            -1]

        self.highkpV = CP.lateralTuning.pid.kpV[1]
        self.highkiV = CP.lateralTuning.pid.kiV[1]

        self.lowpid = PIController(
            (CP.lateralTuning.pid.kpBP, [CP.lateralTuning.pid.kpV[0]]),
            (CP.lateralTuning.pid.kiBP, [CP.lateralTuning.pid.kiV[0]]),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0,
            sat_limit=CP.steerLimitTimer)

        self.pid = PIController((CP.lateralTuning.pid.kpBP, [self.highkpV]),
                                (CP.lateralTuning.pid.kiBP, [self.highkiV]),
                                k_f=CP.lateralTuning.pid.kf,
                                pos_limit=1.0,
                                sat_limit=CP.steerLimitTimer)
        self.angle_steers_des = 0.
        self.increasing = False
        self.dualpids = True

    def pidset(self, pid, descontrol, setpoint, measurement, feedforward):
        error = float(setpoint - measurement)
        p = error * pid._k_p[1][0]
        f = feedforward * pid.k_f
        i = descontrol - p - f
        pid.p = p
        pid.i = i
        pid.f = f
        pid.sat_count = 0.0
        pid.saturated = False
        pid.control = descontrol

    def update(self, active, CS, CP, path_plan):
        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(CS.steeringAngle)
        pid_log.steerRate = float(CS.steeringRate)

        if CS.vEgo < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            if len(CP.lateralTuning.pid.kpV) > 1 and not self.dualpids:
                self.dualPIDinit(CP)
            self.pid.reset()
            self.lowpid.reset()
            self.increasing = False
        else:
            self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, CS.vEgo)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max

            self.lowpid.pos_limit = steers_max
            self.lowpid.neg_limit = -steers_max

            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= CS.vEgo**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = 0.0

            check_saturation = (
                CS.vEgo >
                10) and not CS.steeringRateLimited and not CS.steeringPressed
            #output_steer = self.pid.update(self.angle_steers_des, CS.steeringAngle, check_saturation=check_saturation, override=CS.steeringPressed,
            #                                feedforward=steer_feedforward, speed=CS.vEgo, deadzone=deadzone)

            output_steer = 0.0
            if not self.dualpids:
                output_steer = self.pid.update(
                    self.angle_steers_des,
                    CS.steeringAngle,
                    check_saturation=check_saturation,
                    override=CS.steeringPressed,
                    feedforward=steer_feedforward,
                    speed=CS.vEgo,
                    deadzone=deadzone)
            else:
                if not self.increasing:
                    raw_low_output_steer = self.lowpid.update(
                        self.angle_steers_des,
                        CS.steeringAngle,
                        check_saturation=check_saturation,
                        override=CS.steeringPressed,
                        feedforward=steer_feedforward,
                        speed=CS.vEgo,
                        deadzone=deadzone)
                    output_steer = raw_low_output_steer * self.factor
                    #print("Lo - " + str(output_steer))
                    if abs(output_steer) > (self.factor * 0.99):
                        self.pidset(self.pid, output_steer,
                                    self.angle_steers_des, CS.steeringAngle,
                                    steer_feedforward)
                        #self.pid.p = self.lowpid.p * self.factor
                        #self.pid.i = self.lowpid.i * self.factor
                        #self.pid.f = self.lowpid.f * self.factor
                        #self.pid.sat_count = 0.0
                        #self.pid.saturated = False
                        #self.pid.control = self.lowpid.control * self.factor

                        self.increasing = True
                else:
                    output_steer = self.pid.update(
                        self.angle_steers_des,
                        CS.steeringAngle,
                        check_saturation=check_saturation,
                        override=CS.steeringPressed,
                        feedforward=steer_feedforward,
                        speed=CS.vEgo,
                        deadzone=deadzone)
                    #print("Hi - " + str(output_steer))
                    if abs(output_steer) < (self.factor * 0.1) and abs(
                            self.pid.i) < (self.factor * 0.2):
                        self.pidset(self.lowpid, (output_steer / self.factor),
                                    self.angle_steers_des, CS.steeringAngle,
                                    steer_feedforward)
                        #self.lowpid.p = self.pid.p / self.factor
                        #self.lowpid.i = 0
                        #self.lowpid.f = self.pid.f / self.factor
                        #self.lowpid.sat_count = 0.0
                        #self.lowpid.saturated = False
                        #self.lowpid.control = self.pid.control / self.factor

                        self.increasing = False
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        return output_steer, float(self.angle_steers_des), pid_log
示例#25
0
class LatControl(object):
    def __init__(self, CP):
        self.pid = PIController((CP.steerKpBP, CP.steerKpV),
                                (CP.steerKiBP, CP.steerKiV),
                                k_f=CP.steerKf,
                                pos_limit=1.0)
        self.last_cloudlog_t = 0.0
        self.setup_mpc(CP.steerRateCost)
        self.smooth_factor = 2.0 * CP.steerActuatorDelay / _DT  # Multiplier for inductive component (feed forward)
        self.projection_factor = 5.0 * _DT  #  Mutiplier for reactive component (PI)
        self.accel_limit = 2.0  # Desired acceleration limit to prevent "whip steer" (resistive component)
        self.ff_angle_factor = 0.5  # Kf multiplier for angle-based feed forward
        self.ff_rate_factor = 5.0  # Kf multiplier for rate-based feed forward
        self.ratioDelayExp = 2.0  # Exponential coefficient for variable steering ratio (delay)
        self.ratioDelayScale = 0.0  # Multiplier for variable steering ratio (delay)
        self.ratioScale = 6.0  # Multiplier for variable steering ratio
        self.ratioExp = 2.0  # Exponential coefficient for variable steering assist (torque)
        self.ratioAdjust = 0.85  # Fudge factor to preserve existing tuning parameters
        self.prev_angle_rate = 0
        self.feed_forward = 0.0
        self.steerActuatorDelay = CP.steerActuatorDelay
        self.angle_rate_desired = 0.0
        self.last_mpc_ts = 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
        self.avg_angle_steers = 0.0
        self.last_y = 0.0
        self.new_y = 0.0
        self.angle_sample_count = 0.0
        self.projected_angle_steers = 0.0
        self.lateral_error = 0.0

        # variables for dashboarding
        self.context = zmq.Context()
        self.steerpub = self.context.socket(zmq.PUB)
        self.steerpub.bind("tcp://*:8594")
        self.influxString = 'steerData3,testName=none,active=%s,ff_type=%s ff_type_a=%s,ff_type_r=%s,steer_status=%s,steer_torque_motor=%s,' \
                        'steering_control_active=%s,steer_parameter1=%s,steer_parameter2=%s,steer_parameter3=%s,steer_parameter4=%s,steer_parameter5=%s,' \
                        'steer_parameter6=%s,steer_stock_torque=%s,steer_stock_torque_request=%s,x=%s,y=%s,lateral_error=%s,y0=%s,y1=%s,y2=%s,y3=%s,y4=%s,psi=%s,delta=%s,t=%s,' \
                        'curvature_factor=%s,slip_factor=%s,resonant_period=%s,accel_limit=%s,restricted_steer_rate=%s,ff_angle_factor=%s,ff_rate_factor=%s,' \
                        'pCost=%s,lCost=%s,rCost=%s,hCost=%s,srCost=%s,torque_motor=%s,driver_torque=%s,angle_rate_count=%s,angle_rate_desired=%s,' \
                        'avg_angle_rate=%s,future_angle_steers=%s,angle_rate=%s,steer_zero_crossing=%s,center_angle=%s,angle_steers=%s,angle_steers_des=%s,' \
                        'angle_offset=%s,self.angle_steers_des_mpc=%s,steerRatio=%s,steerKf=%s,steerKpV[0]=%s,steerKiV[0]=%s,steerRateCost=%s,l_prob=%s,' \
                        'r_prob=%s,c_prob=%s,p_prob=%s,l_poly[0]=%s,l_poly[1]=%s,l_poly[2]=%s,l_poly[3]=%s,r_poly[0]=%s,r_poly[1]=%s,r_poly[2]=%s,r_poly[3]=%s,' \
                        'p_poly[0]=%s,p_poly[1]=%s,p_poly[2]=%s,p_poly[3]=%s,c_poly[0]=%s,c_poly[1]=%s,c_poly[2]=%s,c_poly[3]=%s,d_poly[0]=%s,d_poly[1]=%s,' \
                        'd_poly[2]=%s,lane_width=%s,lane_width_estimate=%s,lane_width_certainty=%s,v_ego=%s,p=%s,i=%s,f=%s %s\n~'
        self.steerdata = self.influxString
        self.frames = 0
        self.curvature_factor = 0.0
        self.slip_factor = 0.0
        self.isActive = 0

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

        self.mpc_solution = libmpc_py.ffi.new("log_t *")
        self.cur_state = libmpc_py.ffi.new("state_t *")
        self.mpc_updated = False
        self.mpc_nans = False
        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

    def reset(self):
        self.pid.reset()

    def update(self, active, v_ego, angle_steers, angle_rate, steer_override,
               d_poly, angle_offset, CP, VM, PL):
        cur_time = sec_since_boot()
        self.mpc_updated = False
        # TODO: this creates issues in replay when rewinding time: mpc won't run
        if self.last_mpc_ts < PL.last_md_ts:
            self.last_mpc_ts = PL.last_md_ts
            self.angle_steers_des_prev = self.angle_steers_des_mpc

            # Use the model's solve time instead of cur_time
            self.angle_steers_des_time = float(self.last_mpc_ts / 1000000000.0)
            self.curvature_factor = VM.curvature_factor(v_ego)

            # This is currently disabled, but it is used to compensate for variable steering rate
            ratioDelayFactor = 1. + self.ratioDelayScale * abs(
                angle_steers / 100.)**self.ratioDelayExp

            # Determine a proper delay time that includes the model's processing time, which is variable
            plan_age = _DT_MPC + cur_time - float(
                self.last_mpc_ts / 1000000000.0)
            total_delay = ratioDelayFactor * CP.steerActuatorDelay + plan_age

            # Use steering rate from the last 2 samples to estimate acceleration for a more realistic future steering rate
            accelerated_angle_rate = 2.0 * angle_rate - self.prev_angle_rate

            # Project the future steering angle for the actuator delay only (not model delay)
            self.projected_angle_steers = ratioDelayFactor * CP.steerActuatorDelay * accelerated_angle_rate + angle_steers

            self.l_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.l_poly))
            self.r_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.r_poly))
            self.p_poly = libmpc_py.ffi.new("double[4]", list(PL.PP.p_poly))

            # account for actuation delay and the age of the plan
            self.cur_state = calc_states_after_delay(
                self.cur_state, v_ego, self.projected_angle_steers,
                self.curvature_factor, CP.steerRatio, total_delay)

            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, self.l_poly,
                                self.r_poly, self.p_poly, PL.PP.l_prob,
                                PL.PP.r_prob, PL.PP.p_prob,
                                self.curvature_factor, v_ego_mpc,
                                PL.PP.lane_width)

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

            self.cur_state[0].delta = delta_desired

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

            # Use last 2 desired angles to determine the model's desired steer rate
            self.angle_rate_desired = (self.angle_steers_des_mpc -
                                       self.angle_steers_des_prev) / _DT_MPC
            self.mpc_updated = True

            #  Check for infeasable MPC solution
            self.mpc_nans = np.any(np.isnan(list(self.mpc_solution[0].delta)))
            t = sec_since_boot()
            if self.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) / CP.steerRatio

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

        elif self.frames > 20:
            self.steerpub.send(self.steerdata)
            self.frames = 0
            self.steerdata = self.influxString

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            self.feed_forward = 0.0
            self.pid.reset()
            ff_type = "r"
            projected_angle_steers_des = 0.0
            projected_angle_steers = 0.0
            restricted_steer_rate = 0.0
        else:
            # Interpolate desired angle between MPC updates
            self.angle_steers_des = self.angle_steers_des_prev + self.angle_rate_desired * (
                cur_time - self.angle_steers_des_time)
            self.avg_angle_steers = (4.0 * self.avg_angle_steers +
                                     angle_steers) / 5.0

            # Determine the target steer rate for desired angle, but prevent the acceleration limit from being exceeded
            # Restricting the steer rate creates the resistive component needed for resonance
            restricted_steer_rate = np.clip(
                self.angle_steers_des - float(angle_steers),
                float(angle_rate) - self.accel_limit,
                float(angle_rate) + self.accel_limit)

            # Determine projected desired angle that is within the acceleration limit (prevent the steering wheel from jerking)
            projected_angle_steers_des = self.angle_steers_des + self.projection_factor * restricted_steer_rate

            # Determine project angle steers using current steer rate
            projected_angle_steers = float(
                angle_steers) + self.projection_factor * float(angle_rate)

            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max

            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # Decide which feed forward mode should be used (angle or rate).  Use more dominant mode, and only if conditions are met
                # Spread feed forward out over a period of time to make it more inductive (for resonance)
                if abs(self.ff_rate_factor * float(restricted_steer_rate)) > abs(self.ff_angle_factor * float(self.angle_steers_des) - float(angle_offset)) - 0.5 \
                    and (abs(float(restricted_steer_rate)) > abs(angle_rate) or (float(restricted_steer_rate) < 0) != (angle_rate < 0)) \
                    and (float(restricted_steer_rate) < 0) == (float(self.angle_steers_des) - float(angle_offset) - 0.5 < 0):
                    ff_type = "r"
                    self.feed_forward = (
                        ((self.smooth_factor - 1.) * self.feed_forward) +
                        self.ff_rate_factor * v_ego**2 *
                        float(restricted_steer_rate)) / self.smooth_factor
                elif abs(self.angle_steers_des - float(angle_offset)) > 0.5:
                    ff_type = "a"
                    self.feed_forward = ((
                        (self.smooth_factor - 1.) * self.feed_forward
                    ) + self.ff_angle_factor * v_ego**2 * float(
                        apply_deadzone(
                            float(self.angle_steers_des) - float(angle_offset),
                            0.5))) / self.smooth_factor
                else:
                    ff_type = "r"
                    self.feed_forward = ((
                        (self.smooth_factor - 1.) * self.feed_forward) +
                                         0.0) / self.smooth_factor
            else:
                self.feed_forward = self.angle_steers_des  # feedforward desired angle
            deadzone = 0.0

            # Use projected desired and actual angles instead of "current" values, in order to make PI more reactive (for resonance)
            output_steer = self.pid.update(projected_angle_steers_des,
                                           projected_angle_steers,
                                           check_saturation=(v_ego > 10),
                                           override=steer_override,
                                           feedforward=self.feed_forward,
                                           speed=v_ego,
                                           deadzone=deadzone)

            # All but the last 3 lines after here are for real-time dashboarding
            self.pCost = 0.0
            self.lCost = 0.0
            self.rCost = 0.0
            self.hCost = 0.0
            self.srCost = 0.0
            self.last_ff_a = 0.0
            self.last_ff_r = 0.0
            self.center_angle = 0.0
            self.steer_zero_crossing = 0.0
            self.steer_rate_cost = 0.0
            self.avg_angle_rate = 0.0
            self.angle_rate_count = 0.0
            steer_motor = 0.0
            self.frames += 1
            steer_parameter1 = 0.0
            steer_parameter2 = 0.0
            steer_parameter3 = 0.0
            steer_parameter4 = 0.0
            steer_parameter5 = 0.0
            steer_parameter6 = 0.0
            steering_control_active = 0.0
            steer_torque_motor = 0.0
            driver_torque = 0.0
            steer_status = 0.0
            steer_stock_torque = 0.0
            steer_stock_torque_request = 0.0

            self.steerdata += ("%d,%s,%d,%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d|" % (self.isActive, \
            ff_type, 1 if ff_type == "a" else 0, 1 if ff_type == "r" else 0, steer_status, steer_torque_motor, steering_control_active, steer_parameter1, steer_parameter2, steer_parameter3, steer_parameter4, steer_parameter5, steer_parameter6, steer_stock_torque, steer_stock_torque_request, self.cur_state[0].x, self.cur_state[0].y, self.lateral_error, self.mpc_solution[0].y[0], self.mpc_solution[0].y[1], self.mpc_solution[0].y[2], self.mpc_solution[0].y[3], self.mpc_solution[0].y[4], self.cur_state[0].psi, self.cur_state[0].delta, self.cur_state[0].t, self.curvature_factor, self.slip_factor ,self.smooth_factor, self.accel_limit, float(restricted_steer_rate) ,self.ff_angle_factor, self.ff_rate_factor, self.pCost, self.lCost, self.rCost, self.hCost, self.srCost, steer_motor, float(driver_torque), \
            self.angle_rate_count, self.angle_rate_desired, self.avg_angle_rate, projected_angle_steers, float(angle_rate), self.steer_zero_crossing, self.center_angle, angle_steers, self.angle_steers_des, angle_offset, \
            self.angle_steers_des_mpc, CP.steerRatio, CP.steerKf, CP.steerKpV[0], CP.steerKiV[0], CP.steerRateCost, PL.PP.l_prob, \
            PL.PP.r_prob, PL.PP.c_prob, PL.PP.p_prob, self.l_poly[0], self.l_poly[1], self.l_poly[2], self.l_poly[3], self.r_poly[0], self.r_poly[1], self.r_poly[2], self.r_poly[3], \
            self.p_poly[0], self.p_poly[1], self.p_poly[2], self.p_poly[3], PL.PP.c_poly[0], PL.PP.c_poly[1], PL.PP.c_poly[2], PL.PP.c_poly[3], PL.PP.d_poly[0], PL.PP.d_poly[1], \
            PL.PP.d_poly[2], PL.PP.lane_width, PL.PP.lane_width_estimate, PL.PP.lane_width_certainty, v_ego, self.pid.p, self.pid.i, self.pid.f, int(time.time() * 100) * 10000000))

        self.sat_flag = self.pid.saturated
        self.prev_angle_rate = angle_rate
        return output_steer, float(self.angle_steers_des)
示例#26
0
class ALCAController(object):
    def __init__(self, carcontroller, alcaEnabled, steerByAngle):
        #import settings
        self.CC = carcontroller  # added to start, will see if we need it actually
        # variables for lane change
        self.alcaEnabled = alcaEnabled
        self.laneChange_strStartFactor = 2.
        self.laneChange_strStartMultiplier = 1.5
        self.laneChange_steerByAngle = steerByAngle  # steer only by angle; do not call PID
        self.laneChange_last_actuator_angle = 0.
        self.laneChange_last_actuator_delta = 0.
        self.laneChange_last_sent_angle = 0.
        self.laneChange_over_the_line = 0  # did we cross the line?
        self.laneChange_avg_angle = 0.  # used if we do average entry angle over x frames
        self.laneChange_avg_count = 0.  # used if we do average entry angle over x frames
        self.laneChange_enabled = 1  # set to zero for no lane change
        self.laneChange_counter = 0  # used to count frames during lane change
        self.laneChange_min_duration = 1.  # min time to wait before looking for next lane
        self.laneChange_duration = 5  # how many max seconds to actually do the move; if lane not found after this then send error
        self.laneChange_after_lane_duration_mult = 1.  # multiplier for time after we cross the line before we let OP take over; multiplied with CL_TIMEA_T
        self.laneChange_wait = 2  # how many seconds to wait before it starts the change
        self.laneChange_lw = 3.0  # lane width in meters
        self.laneChange_angle = 0.  # saves the last angle from actuators before lane change starts
        self.laneChange_angled = 0.  # angle delta
        self.laneChange_steerr = 16.00  # steer ratio for lane change
        self.laneChange_direction = 0  # direction of the lane change
        self.prev_right_blinker_on = False  # local variable for prev position
        self.prev_left_blinker_on = False  # local variable for prev position
        self.keep_angle = False  #local variable to keep certain angle delta vs. actuator
        #self.blindspot_blink_counter = 0
        self.pid = None
        self.last10delta = []

    def last10delta_reset(self):
        self.last10delta = []
        for i in range(0, 10):
            self.last10delta.append(0.)

    def last10delta_add(self, angle):
        self.last10delta.pop(0)
        self.last10delta.append(angle)
        n = 0
        a = 0.
        for i in self.last10delta:
            if i != 0:
                n += 1
                a += i
        return n, a

    def last10delta_correct(self, c):
        for i in self.last10delta:
            j = self.last10delta.index(i)
            n = self.last10delta.pop(j)
            n += c
            self.last10delta.insert(j, n)

    def update_status(self, alcaEnabled):
        self.alcaEnabled = alcaEnabled

    def set_pid(self, CS):
        self.laneChange_steerr = CS.CP.steerRatio
        self.pid = PIController((CS.CP.steerKpBP, CS.CP.steerKpV),
                                (CS.CP.steerKiBP, CS.CP.steerKiV),
                                k_f=CS.CP.steerKf,
                                pos_limit=1.0)

    def update_angle(self, enabled, CS, frame, actuators):
        alca_m1 = 1.
        alca_m2 = 1.
        alca_m3 = 1.
        if CS.alcaMode == 1:
            alca_m1 = 0.95
            alca_m2 = 1.
            alca_m3 = 0.5
        if CS.alcaMode == 2:
            alca_m1 = 0.9
            alca_m2 = 1.7
            alca_m3 = 0.5
        # speed variable parameters
        cl_max_angle_delta = interp(CS.v_ego, CS.CL_MAX_ANGLE_DELTA_BP,
                                    CS.CL_MAX_ANGLE_DELTA) * alca_m1
        cl_maxd_a = interp(CS.v_ego, CS.CL_MAXD_BP, CS.CL_MAXD_A) * alca_m3
        cl_lane_pass_time = interp(CS.v_ego, CS.CL_LANE_PASS_BP,
                                   CS.CL_LANE_PASS_TIME) * alca_m2
        cl_timea_t = interp(CS.v_ego, CS.CL_TIMEA_BP, CS.CL_TIMEA_T) * alca_m2
        cl_lane_detect_factor = interp(CS.v_ego, CS.CL_LANE_DETECT_BP,
                                       CS.CL_LANE_DETECT_FACTOR)
        cl_max_a = interp(CS.v_ego, CS.CL_MAX_A_BP, CS.CL_MAX_A)
        cl_adjust_factor = interp(CS.v_ego, CS.CL_ADJUST_FACTOR_BP,
                                  CS.CL_ADJUST_FACTOR)
        cl_reentry_angle = interp(CS.v_ego, CS.CL_REENTRY_ANGLE_BP,
                                  CS.CL_REENTRY_ANGLE)
        cl_correction_factor = interp(CS.v_ego, CS.CL_CORRECTION_FACTOR_BP,
                                      CS.CL_CORRECTION_FACTOR)
        cl_min_v = CS.CL_MIN_V
        self.laneChange_wait = CS.CL_WAIT_BEFORE_START

        # Basic highway lane change logic
        blindspot = CS.blind_spot_on
        #if changing_lanes:
        #if blindspot:
        #self.blindspot_blink_counter = 0
        # print "debug: blindspot detected in alca"
        #CS.UE.custom_alert_message(3,"Auto Lane Change Canceled! (s)",200,5)
        #self.laneChange_enabled = 1
        #  self.laneChange_counter = 0
        #self.laneChange_direction = 0
        # self.blindspot_blink_counter += 1

        #if self.blindspot_blink_counter < 150:
        #self.laneChange_enabled = 0
        #self.laneChange_counter = 0
        #else:
        #self.laneChange_enabled = 1
        #self.laneChange_counter = self.laneChange_counter

        #self.laneChange_steerr = CS.CP.steerRatio
        actuator_delta = 0.
        laneChange_angle = 0.
        turn_signal_needed = 0  # send 1 for left, 2 for right 0 for not needed

        if (not CS.right_blinker_on) and (not CS.left_blinker_on) and \
        (self.laneChange_enabled ==7):
            self.laneChange_enabled = 1
            self.laneChange_counter = 0
            self.laneChange_direction = 0
            CS.UE.custom_alert_message(-1, "", 0)

        if (not CS.right_blinker_on) and (not CS.left_blinker_on) and \
          (self.laneChange_enabled > 1):
            # no blinkers on but we are still changing lane, so we need to send blinker command
            if self.laneChange_direction == -1:
                turn_signal_needed = 1
            elif self.laneChange_direction == 1:
                turn_signal_needed = 2
            else:
                turn_signal_needed = 0

        if (CS.cstm_btns.get_button_status("alca") >
                0) and self.alcaEnabled and (self.laneChange_enabled == 1):
            if ((CS.v_ego < cl_min_v) or (abs(actuators.steerAngle) >= cl_max_a) or \
            (abs(CS.angle_steers)>= cl_max_a)  or (not enabled)):
                CS.cstm_btns.set_button_status("alca", 9)
            else:
                CS.cstm_btns.set_button_status("alca", 1)

        if self.alcaEnabled and enabled and (((not self.prev_right_blinker_on) and CS.right_blinker_on) or \
          ((not self.prev_left_blinker_on) and CS.left_blinker_on)) and \
          ((CS.v_ego < cl_min_v) or (abs(actuators.steerAngle) >= cl_max_a) or (abs(CS.angle_steers) >=cl_max_a)):
            # something is not right, the speed or angle is limitting
            CS.UE.custom_alert_message(3, "Auto Lane Change Unavailable!", 500,
                                       3)
            CS.cstm_btns.set_button_status("alca", 9)


        if self.alcaEnabled and enabled and (((not self.prev_right_blinker_on) and CS.right_blinker_on) or \
          ((not self.prev_left_blinker_on) and CS.left_blinker_on))  and \
          (CS.v_ego >= cl_min_v) and (abs(actuators.steerAngle) < cl_max_a):
            # start blinker, speed and angle is within limits, let's go
            laneChange_direction = 1
            # changing lanes
            if CS.left_blinker_on:
                laneChange_direction = -1

            if (self.laneChange_enabled > 1) and (self.laneChange_direction <>
                                                  laneChange_direction):
                # something is not right; signal in oposite direction; cancel
                CS.UE.custom_alert_message(3, "Auto Lane Change Canceled! (s)",
                                           200, 5)
                self.laneChange_enabled = 1
                self.laneChange_counter = 0
                self.laneChange_direction = 0
                CS.cstm_btns.set_button_status("alca", 1)
            elif (self.laneChange_enabled == 1):
                # compute angle delta for lane change
                CS.UE.custom_alert_message(2, "Auto Lane Change Engaged! (1)",
                                           100)
                self.laneChange_enabled = 5
                self.laneChange_counter = 1
                self.laneChange_direction = laneChange_direction
                self.laneChange_avg_angle = 0.
                self.laneChange_avg_count = 0.
                self.laneChange_angled = self.laneChange_direction * self.laneChange_steerr * cl_maxd_a
                self.laneChange_last_actuator_angle = 0.
                self.laneChange_last_actuator_delta = 0.
                self.laneChange_over_the_line = 0
                CS.cstm_btns.set_button_status("alca", 2)
                # reset PID for torque
                self.pid.reset()

        if (not self.alcaEnabled) and self.laneChange_enabled > 1:
            self.laneChange_enabled = 1
            self.laneChange_counter = 0
            self.laneChange_direction = 0

        # lane change in process
        if self.laneChange_enabled > 1:
            if (CS.steer_override or (CS.v_ego < cl_min_v)
                    or (not (CS.right_blinker_on or CS.left_blinker_on))):
                CS.UE.custom_alert_message(4, "Auto Lane Change Canceled! (u)",
                                           200, 3)
                # if any steer override cancel process or if speed less than min speed
                self.laneChange_counter = 0
                self.laneChange_enabled = 1
                self.laneChange_direction = 0
                CS.cstm_btns.set_button_status("alca", 1)
            if blindspot:
                CS.UE.custom_alert_message(4, "Auto Lane Change Held! (b)",
                                           200, 3)
                # if blindspot detected cancel process
                self.laneChange_counter = 0
                self.laneChange_enabled = 5
                #CS.cstm_btns.set_button_status("alca",1)

            # now that we crossed the line, we need to get far enough from the line and then engage OP
            #     1. we wait 0.05s to ensure we don't have back and forth adjustments
            #     2. we check to see if the delta between our steering and the actuator continues to decrease or not
            #     3. we check to see if the delta between our steering and the actuator is less than 5 deg
            #     4. we disengage after 0.5s and let OP take over
            if self.laneChange_enabled == 2:
                laneChange_angle = self.laneChange_angled
                if self.laneChange_counter == 1:
                    CS.UE.custom_alert_message(
                        2, "Auto Lane Change Engaged! (5)", 800)
                self.laneChange_counter += 1
                # check if angle continues to decrease
                current_delta = abs(self.laneChange_angle + laneChange_angle +
                                    (-actuators.steerAngle))
                previous_delta = abs(self.laneChange_last_sent_angle -
                                     self.laneChange_last_actuator_angle)
                if (self.laneChange_counter > cl_lane_pass_time):
                    # continue to half the angle between our angle and actuator
                    delta_angle = (-actuators.steerAngle -
                                   self.laneChange_angle -
                                   self.laneChange_angled) / cl_adjust_factor
                    self.laneChange_angle += delta_angle
                # wait 0.05 sec before starting to check if angle increases or if we are within X deg of actuator.angleSteer
                if ((current_delta > previous_delta) or
                    (current_delta <= cl_reentry_angle)) and (
                        self.laneChange_counter > cl_lane_pass_time):
                    self.laneChange_enabled = 7
                    self.laneChange_counter = 1
                    self.laneChange_direction = 0
                # we crossed the line, so  x sec later give control back to OP
                laneChange_after_lane_duration = cl_timea_t * self.laneChange_after_lane_duration_mult
                if (self.laneChange_counter >
                        laneChange_after_lane_duration * 100):
                    self.laneChange_enabled = 7
                    self.laneChange_counter = 1
                    self.laneChange_direction = 0
            # this is the main stage once we start turning
            # we have to detect when to let go control back to OP or raise alarm if max timer passed
            # there are three conditions we look for:
            #     1. we can detect when we cross the lane, and then we let go control to OP
            #     2. we passed the min timer to cross the line and the delta between actuator and our angle
            #       is less than the release angle, then we let go control to OP
            #     3. the delta between our angle and the actuator is higher than the previous one
            #       (we cross the optimal path), then we let go control to OP
            #     4. max time is achieved: alert and disengage
            # CONTROL: during this time we use ALCA angle to steer (ALCA Control)
            if self.laneChange_enabled == 3:
                if self.laneChange_counter == 1:
                    CS.UE.custom_alert_message(
                        2, "Auto Lane Change Engaged! (4)", 800)
                    self.laneChange_over_the_line = 0
                    self.last10delta_reset()
                    self.keep_angle = False
                self.laneChange_counter += 1
                laneChange_angle = self.laneChange_angled
                if (self.laneChange_over_the_line == 0):
                    # we didn't cross the line, so keep computing the actuator delta until it flips
                    actuator_delta = self.laneChange_direction * (
                        -actuators.steerAngle -
                        self.laneChange_last_actuator_angle)
                    actuator_ratio = (-actuators.steerAngle
                                      ) / self.laneChange_last_actuator_angle
                if (actuator_ratio < 1) and (abs(actuator_delta) >
                                             0.5 * cl_lane_detect_factor):
                    # sudden change in actuator angle or sign means we are on the other side of the line
                    self.laneChange_over_the_line = 1
                    self.laneChange_enabled = 2
                    self.laneChange_counter = 1

                    # didn't change the lane yet, check that we are not eversteering or understeering based on road curvature
                """
        # code for v3.1 designed to turn more if lane moves away
        n,a = self.last10delta_add(actuator_delta)
        c = 5.
        if a == 0:
          a = 0.00001
        if (abs(a) < CL_MIN_ACTUATOR_DELTA) and (self.keep_angle == False):
          c = (a/abs(a)) * (CL_MIN_ACTUATOR_DELTA - abs(a)) / 10
          self.laneChange_angle = self.laneChange_angle -self.laneChange_direction * CL_CORRECTION_FACTOR * c * 10 
          self.last10delta_correct(c)
        #print a, c, actuator_delta, self.laneChange_angle
        """
                #print a, c, actuator_delta, self.laneChange_angle
                # code for v3.2 and above
                a_delta = self.laneChange_direction * (self.laneChange_angle +
                                                       laneChange_angle -
                                                       (-actuators.steerAngle))
                if (self.laneChange_over_the_line == 0) and (
                    (abs(a_delta) > cl_max_angle_delta *
                     self.laneChange_steerr) or (self.keep_angle)):
                    #steering more than what we wanted, need to adjust
                    self.keep_angle = True
                    self.laneChange_angle = -actuators.steerAngle + self.laneChange_direction * cl_max_angle_delta * self.laneChange_steerr - laneChange_angle
                    self.laneChange_angle = self.laneChange_angle * (
                        1 - self.laneChange_direction *
                        (1 - cl_correction_factor))
                if self.laneChange_counter > (self.laneChange_duration) * 100:
                    self.laneChange_enabled = 1
                    self.laneChange_counter = 0
                    CS.UE.custom_alert_message(
                        4, "Auto Lane Change Canceled! (t)", 200, 5)
                    self.laneChange_counter = 0
                    CS.cstm_btns.set_button_status("alca", 1)
            # this is the critical start of the turn
            # here we will detect the angle to move; it is based on a speed determined angle but we have to also
            # take in consideration what's happening with the delta of consecutive actuators
            # if they go in the same direction with our turn we have to reset our turn angle because the actuator either
            # is compensating for a turn in the road OR for same lane correction for the car
            # CONTROL: during this time we use ALCA angle to steer (ALCA Control)

            # TODO: - when actuator moves in the same direction with lane change, correct starting angle
            if self.laneChange_enabled == 4:
                if self.laneChange_counter == 1:
                    self.laneChange_angle = -actuators.steerAngle
                    CS.UE.custom_alert_message(
                        2, "Auto Lane Change Engaged! (3)", 100)
                    self.laneChange_angled = self.laneChange_direction * self.laneChange_steerr * cl_maxd_a
                    # if angle more than max angle allowed cancel; last chance to cancel on road curvature
                if self.laneChange_counter == 2:
                    CS.UE.custom_alert_message(
                        2, "Auto Lane Change     Engaged! (3)", 100)
                if (abs(self.laneChange_angle) >
                        cl_max_a) and (self.laneChange_counter == 1):
                    CS.UE.custom_alert_message(
                        4, "Auto Lane Change Canceled! (a)", 200, 5)
                    self.laneChange_enabled = 1
                    self.laneChange_counter = 0
                    self.laneChange_direction = 0
                    CS.cstm_btns.set_button_status("alca", 1)
                laneChange_angle = self.laneChange_strStartFactor * self.laneChange_angled * self.laneChange_counter / 50
                self.laneChange_counter += 1
                #laneChange_angle = self.laneChange_strStartFactor * self.laneChange_angled *  self.laneChange_counter / 50
                #delta_change = abs(self.laneChange_angle+ laneChange_angle + actuators.steerAngle) - self.laneChange_strStartMultiplier * abs(self.laneChange_angled)
                laneChange_angle = self.laneChange_strStartFactor * self.laneChange_angled * self.laneChange_counter / 100
                delta_change = abs(
                    self.laneChange_angle + laneChange_angle + actuators.
                    steerAngle) - cl_max_angle_delta * self.laneChange_steerr
                if (self.laneChange_counter == 100) or (delta_change >= 0):
                    if (delta_change < 0):
                        # didn't achieve desired angle yet, so repeat
                        self.laneChange_counter = 2
                        self.laneChange_angle += laneChange_angle
                        laneChange_angle = 0.
                    else:
                        self.laneChange_enabled = 3
                        self.laneChange_counter = 1
                        self.laneChange_angled = laneChange_angle
            # this is the first stage of the ALCAS
            # here we wait for x seconds before we start the turn
            # if at any point we detect hand on wheel, we let go of control and notify user
            # the same test for hand on wheel is done at ANY stage throughout the lane change process
            # CONTROL: during this stage we use the actuator angle to steer (OP Control)
            if self.laneChange_enabled == 5:
                if self.laneChange_counter == 1:
                    CS.UE.custom_alert_message(
                        2, "Auto Lane Change Engaged! (2)",
                        self.laneChange_wait * 100)
                self.laneChange_counter += 1
                if self.laneChange_counter > (self.laneChange_wait - 1) * 100:
                    self.laneChange_avg_angle += -actuators.steerAngle
                    self.laneChange_avg_count += 1
                if self.laneChange_counter == self.laneChange_wait * 100:
                    self.laneChange_enabled = 4
                    self.laneChange_counter = 1
                if self.laneChange_counter == 0:
                    if not blindspot:
                        if CS.right_blinker_on or CS.left_blinker_on:
                            self.laneChange_counter += 1

            # this is the final stage of the ALCAS
            # this just shows a message that we completed the lane change
            # CONTROL: during this time we use the actuator angle to steer (OP Control)
            if self.laneChange_enabled == 7:
                if self.laneChange_counter == 1:
                    CS.UE.custom_alert_message(2, "Auto Lane Change Complete!",
                                               300, 4)
                    CS.cstm_btns.set_button_status("alca", 1)
                self.laneChange_counter += 1
        alca_enabled = (self.laneChange_enabled > 1)
        apply_angle = 0.
        # save position of blinker stalk
        self.prev_right_blinker_on = CS.right_blinker_on
        self.prev_left_blinker_on = CS.left_blinker_on
        # Angle if 0 we need to save it as a very small nonzero with the same sign as the prev one
        self.laneChange_last_actuator_delta = -actuators.steerAngle - self.laneChange_last_actuator_angle
        last_angle_sign = 1
        if (self.laneChange_last_actuator_angle <> 0):
            last_angle_sign = self.laneChange_last_actuator_angle / abs(
                self.laneChange_last_actuator_angle)
        if actuators.steerAngle == 0:
            self.laneChange_last_actuator_angle = last_angle_sign * 0.00001
        else:
            self.laneChange_last_actuator_angle = -actuators.steerAngle
        # determine what angle to send and send it
        if (self.laneChange_enabled > 1) and (self.laneChange_enabled < 5):
            apply_angle = self.laneChange_angle + laneChange_angle
        else:
            apply_angle = -actuators.steerAngle
        self.laneChange_last_sent_angle = apply_angle
        return [-apply_angle, alca_enabled, turn_signal_needed]

    def update(self, enabled, CS, frame, actuators):
        if self.alcaEnabled:
            # ALCA enabled
            new_angle = 0.
            new_ALCA_Enabled = False
            new_turn_signal = 0
            new_angle, new_ALCA_Enabled, new_turn_signal = self.update_angle(
                enabled, CS, frame, actuators)
            output_steer = 0.
            if new_ALCA_Enabled and (self.laneChange_enabled <
                                     5) and not self.laneChange_steerByAngle:
                #self.angle_steers_des = self.angle_steers_des_mpc # not declared
                steers_max = interp(CS.v_ego, CS.CP.steerMaxBP,
                                    CS.CP.steerMaxV)
                self.pid.pos_limit = steers_max
                self.pid.neg_limit = -steers_max
                output_steer = self.pid.update(
                    new_angle,
                    CS.angle_steers,
                    check_saturation=(CS.v_ego > 10),
                    override=CS.steer_override,
                    feedforward=new_angle * (CS.v_ego**2),
                    speed=CS.v_ego,
                    deadzone=0.0)
            else:
                output_steer = actuators.steer
            return [new_angle, output_steer, new_ALCA_Enabled, new_turn_signal]
        else:
            # ALCA disabled
            return [actuators.steerAngle, actuators.steer, False, 0]
示例#27
0
class LongControl(object):
    def __init__(self, compute_gb):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController((_KP_BP, _KP_V), (_KI_BP, _KI_V),
                                rate=100.0,
                                sat_limit=0.8,
                                convert=compute_gb)
        self.v_pid = 0.0
        self.last_output_gb = 0.0

    def reset(self, v_pid):
        self.pid.reset()
        self.v_pid = v_pid

    def update(self, active, v_ego, brake_pressed, standstill, v_cruise,
               v_target_lead, a_target, jerk_factor, CP):

        # actuation limits
        gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV)
        brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV)

        overshoot_allowance = 2.0  # overshoot allowed when changing accel sign

        output_gb = self.last_output_gb

        # limit max target speed based on cruise setting
        v_target = min(v_target_lead, v_cruise * CV.KPH_TO_MS)
        rate = 100.0
        max_speed_delta_up = a_target[1] * 1.0 / rate
        max_speed_delta_down = a_target[0] * 1.0 / rate

        self.long_control_state = long_control_state_trans(active, self.long_control_state, v_ego,\
                                                           v_target, self.v_pid, output_gb, brake_pressed)

        v_ego_pid = max(
            v_ego, 0.3
        )  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

        # *** long control behavior based on state
        if self.long_control_state == LongCtrlState.off:
            self.v_pid = v_ego_pid  # do nothing
            output_gb = 0.
            self.pid.reset()

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            #reset v_pid close to v_ego if it was too far and new v_target is closer to v_ego
            if ((self.v_pid > v_ego + overshoot_allowance)
                    and (v_target < self.v_pid)):
                self.v_pid = max(v_target, v_ego + overshoot_allowance)
            elif ((self.v_pid < v_ego - overshoot_allowance)
                  and (v_target > self.v_pid)):
                self.v_pid = min(v_target, v_ego - overshoot_allowance)

            # move v_pid no faster than allowed accel limits
            if (v_target > self.v_pid + max_speed_delta_up):
                self.v_pid += max_speed_delta_up
            elif (v_target < self.v_pid + max_speed_delta_down):
                self.v_pid += max_speed_delta_down
            else:
                self.v_pid = v_target

            # to avoid too much wind up on acceleration, limit positive speed error
            if CP.enableGas:
                max_speed_error = interp(v_ego, _MAX_SPEED_ERROR_BP,
                                         _MAX_SPEED_ERROR_V)
                self.v_pid = min(self.v_pid, v_ego + max_speed_error)

            self.pid.pos_limit = gas_max
            self.pid.neg_limit = -brake_max
            deadzone = interp(v_ego_pid, CP.longPidDeadzoneBP,
                              CP.longPidDeadzoneV)
            output_gb = self.pid.update(self.v_pid,
                                        v_ego_pid,
                                        speed=v_ego_pid,
                                        jerk_factor=jerk_factor,
                                        deadzone=deadzone)

        # intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # TODO: use the standstill bit from CAN to detect movements, usually more accurate than looking at v_ego
            if not standstill or output_gb > -brake_stopping_target:
                output_gb -= stopping_brake_rate / rate
            output_gb = clip(output_gb, -brake_max, gas_max)

            self.v_pid = v_ego
            self.pid.reset()

        # intention is to move again, release brake fast before handling control to PID
        elif self.long_control_state == LongCtrlState.starting:
            if output_gb < -0.2:
                output_gb += starting_brake_rate / rate
            self.v_pid = v_ego
            self.pid.reset()
            self.pid.i = starting_Ui

        self.last_output_gb = output_gb
        final_gas = clip(output_gb, 0., gas_max)
        final_brake = -clip(output_gb, -brake_max, 0.)

        return final_gas, final_brake
示例#28
0
class LatControlPID(object):
  def __init__(self, CP):
    self.pid = PIController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
                            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
                            k_f=CP.lateralTuning.pid.kf, pos_limit=1.0)
    self.angle_steers_des = 0.
    self.angle_ff_ratio = 0.0
    self.angle_ff_gain = 1.0
    self.rate_ff_gain = 0.01
    self.angle_ff_bp = [[0.5, 5.0],[0.0, 1.0]]
    self.sat_time = 0.0
    
  def reset(self):
    self.pid.reset()
    
  def adjust_angle_gain(self):
    if (self.pid.f > 0) == (self.pid.i > 0) and abs(self.pid.i) >= abs(self.previous_integral):
      self.angle_ff_gain *= 1.0001
    else:
      self.angle_ff_gain *= 0.9999
    self.angle_ff_gain = max(1.0, self.angle_ff_gain)
    self.previous_integral = self.pid.i

  def update(self, active, v_ego, angle_steers, angle_steers_rate, steer_override, CP, VM, path_plan):
    pid_log = log.Live100Data.LateralPIDState.new_message()
    pid_log.steerAngle = float(angle_steers)
    pid_log.steerRate = float(angle_steers_rate)

    if v_ego < 0.3 or not active:
      output_steer = 0.0
      pid_log.active = False
      self.pid.reset()
      self.previous_integral = 0.0
    else:
      self.angle_steers_des = interp(sec_since_boot(), path_plan.mpcTimes, path_plan.mpcAngles)
      
      steers_max = get_steer_max(CP, v_ego)
      self.pid.pos_limit = steers_max
      self.pid.neg_limit = -steers_max
      steer_feedforward = self.angle_steers_des   # feedforward desired angle
      if CP.steerControlType == car.CarParams.SteerControlType.torque:
        angle_feedforward = steer_feedforward - path_plan.angleOffset
        self.angle_ff_ratio = interp(abs(angle_feedforward), self.angle_ff_bp[0], self.angle_ff_bp[1])
        angle_feedforward *= self.angle_ff_ratio * self.angle_ff_gain
        rate_feedforward = (1.0 - self.angle_ff_ratio) * self.rate_ff_gain * path_plan.rateSteers
        steer_feedforward = v_ego**2 * (rate_feedforward + angle_feedforward)
        
        if v_ego > 10.0:
          if abs(angle_steers) > (self.angle_ff_bp[0][1] / 2.0):
            self.adjust_angle_gain()
          else:
            self.previous_integral = self.pid.i
        
      deadzone = 0.0
      output_steer = self.pid.update(self.angle_steers_des, angle_steers, check_saturation=(v_ego > 10), override=steer_override,
                                     feedforward=steer_feedforward, speed=v_ego, deadzone=deadzone)
      pid_log.active = True
      pid_log.p = self.pid.p
      pid_log.i = self.pid.i
      pid_log.f = self.pid.f
      pid_log.output = output_steer
      pid_log.saturated = bool(self.pid.saturated)

    # Reset sat_flat always, set it only if needed
    self.sat_flag = False

    # If PID is saturated, set time which it was saturated
    if self.pid.saturated and self.sat_time < 0.5:
      self.sat_time = sec_since_boot()

    # To save cycles, nest in sat_time check
    if self.sat_time > 0.5:
      # If its been saturated for 0.7 seconds then set flag
      if (sec_since_boot() - self.sat_time) > 0.7:
        self.sat_flag = True

      # If it is no longer saturated, clear the sat flag and timer
      if not self.pid.saturated:
        self.sat_time = 0.0

    if CP.steerControlType == car.CarParams.SteerControlType.torque:
      return output_steer, path_plan.angleSteers, pid_log
    else:
      return self.angle_steers_des, path_plan.angleSteers
示例#29
0
class LongControl(object):
    def __init__(self, CP, compute_gb):
        self.long_control_state = LongCtrlState.off  # initialized to off
        self.pid = PIController(
            (CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
            (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
            rate=RATE,
            sat_limit=0.8,
            convert=compute_gb)
        self.v_pid = 0.0
        self.last_output_gb = 0.0

    def reset(self, v_pid):
        """Reset PID controller and change setpoint"""
        self.pid.reset()
        self.v_pid = v_pid

    def update(self, active, v_ego, brake_pressed, standstill,
               cruise_standstill, v_cruise, v_target, v_target_future,
               a_target, CP):
        """Update longitudinal control. This updates the state machine and runs a PID loop"""
        # Actuation limits
        gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV)
        brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV)

        # Update state machine
        output_gb = self.last_output_gb
        self.long_control_state = long_control_state_trans(
            active, self.long_control_state, v_ego, v_target_future,
            self.v_pid, output_gb, brake_pressed, cruise_standstill)

        v_ego_pid = max(
            v_ego, MIN_CAN_SPEED
        )  # Without this we get jumps, CAN bus reports 0 when speed < 0.3

        if self.long_control_state == LongCtrlState.off:
            self.v_pid = v_ego_pid
            self.pid.reset()
            output_gb = 0.

        # tracking objects and driving
        elif self.long_control_state == LongCtrlState.pid:
            self.v_pid = v_target
            self.pid.pos_limit = gas_max
            self.pid.neg_limit = -brake_max

            # Toyota starts braking more when it thinks you want to stop
            # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
            prevent_overshoot = not CP.stoppingControl and v_ego < 1.5 and v_target_future < 0.7
            deadzone = interp(v_ego_pid, CP.longitudinalTuning.deadzoneBP,
                              CP.longitudinalTuning.deadzoneV)

            output_gb = self.pid.update(self.v_pid,
                                        v_ego_pid,
                                        speed=v_ego_pid,
                                        deadzone=deadzone,
                                        feedforward=a_target,
                                        freeze_integrator=prevent_overshoot)

            if prevent_overshoot:
                output_gb = min(output_gb, 0.0)

        # Intention is to stop, switch to a different brake control until we stop
        elif self.long_control_state == LongCtrlState.stopping:
            # Keep applying brakes until the car is stopped
            if not standstill or output_gb > -BRAKE_STOPPING_TARGET:
                output_gb -= STOPPING_BRAKE_RATE / RATE
            output_gb = clip(output_gb, -brake_max, gas_max)

            self.v_pid = v_ego
            self.pid.reset()

        # Intention is to move again, release brake fast before handing control to PID
        elif self.long_control_state == LongCtrlState.starting:
            if output_gb < -0.2:
                output_gb += STARTING_BRAKE_RATE / RATE
            self.v_pid = v_ego
            self.pid.reset()

        self.last_output_gb = output_gb
        final_gas = clip(output_gb, 0., gas_max)
        final_brake = -clip(output_gb, -brake_max, 0.)

        return final_gas, final_brake
示例#30
0
class LatControlPID(object):
    def __init__(self, CP):
        kegman_conf(CP)
        self.pid = PIController(
            (CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
            (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
            k_f=CP.lateralTuning.pid.kf,
            pos_limit=1.0)
        self.angle_steers_des = 0.
        self.mpc_frame = 0

    def reset(self):
        self.pid.reset()

    def live_tune(self, CP):
        self.mpc_frame += 1
        if self.mpc_frame % 300 == 0:
            # live tuning through /data/openpilot/tune.py overrides interface.py settings
            kegman = kegman_conf()
            if kegman.conf['tuneGernby'] == "1":
                self.steerKpV = [float(kegman.conf['Kp'])]
                self.steerKiV = [float(kegman.conf['Ki'])]
                self.pid = PIController(
                    (CP.lateralTuning.pid.kpBP, self.steerKpV),
                    (CP.lateralTuning.pid.kiBP, self.steerKiV),
                    k_f=CP.lateralTuning.pid.kf,
                    pos_limit=1.0)
            self.mpc_frame = 0

    def update(self, active, v_ego, angle_steers, angle_steers_rate,
               eps_torque, steer_override, CP, VM, path_plan):
        self.live_tune(CP)

        pid_log = log.ControlsState.LateralPIDState.new_message()
        pid_log.steerAngle = float(angle_steers)
        pid_log.steerRate = float(angle_steers_rate)

        if v_ego < 0.3 or not active:
            output_steer = 0.0
            pid_log.active = False
            self.pid.reset()
        else:
            self.angle_steers_des = path_plan.angleSteers  # get from MPC/PathPlanner

            steers_max = get_steer_max(CP, v_ego)
            self.pid.pos_limit = steers_max
            self.pid.neg_limit = -steers_max
            steer_feedforward = self.angle_steers_des  # feedforward desired angle
            if CP.steerControlType == car.CarParams.SteerControlType.torque:
                # TODO: feedforward something based on path_plan.rateSteers
                steer_feedforward -= path_plan.angleOffset  # subtract the offset, since it does not contribute to resistive torque
                steer_feedforward *= v_ego**2  # proportional to realigning tire momentum (~ lateral accel)
            deadzone = 0.0
            output_steer = self.pid.update(self.angle_steers_des,
                                           angle_steers,
                                           check_saturation=(v_ego > 10),
                                           override=steer_override,
                                           feedforward=steer_feedforward,
                                           speed=v_ego,
                                           deadzone=deadzone)
            pid_log.active = True
            pid_log.p = self.pid.p
            pid_log.i = self.pid.i
            pid_log.f = self.pid.f
            pid_log.output = output_steer
            pid_log.saturated = bool(self.pid.saturated)

        self.sat_flag = self.pid.saturated
        return output_steer, float(self.angle_steers_des), pid_log