@ -3,7 +3,7 @@ import os 
			
		
	
		
		
			
				
					
					import  numpy  as  np import  numpy  as  np  
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					from  common . realtime  import  sec_since_boot from  common . realtime  import  sec_since_boot  
			
		
	
		
		
			
				
					
					from  common . numpy_fast  import  clip ,  interp from  common . numpy_fast  import  clip  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					from  system . swaglog  import  cloudlog from  system . swaglog  import  cloudlog  
			
		
	
		
		
			
				
					
					from  selfdrive . modeld . constants  import  index_function from  selfdrive . modeld . constants  import  index_function  
			
		
	
		
		
			
				
					
					from  selfdrive . controls . lib . radar_helpers  import  _LEAD_ACCEL_TAU from  selfdrive . controls . lib . radar_helpers  import  _LEAD_ACCEL_TAU  
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
						
					 
					@ -20,7 +20,7 @@ LONG_MPC_DIR = os.path.dirname(os.path.abspath(__file__)) 
			
		
	
		
		
			
				
					
					EXPORT_DIR  =  os . path . join ( LONG_MPC_DIR ,  " c_generated_code " ) EXPORT_DIR  =  os . path . join ( LONG_MPC_DIR ,  " c_generated_code " )  
			
		
	
		
		
			
				
					
					JSON_FILE  =  os . path . join ( LONG_MPC_DIR ,  " acados_ocp_long.json " ) JSON_FILE  =  os . path . join ( LONG_MPC_DIR ,  " acados_ocp_long.json " )  
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					SOURCES  =  [ ' lead0 ' ,  ' lead1 ' ,  ' cruise ' ] SOURCES  =  [ ' lead0 ' ,  ' lead1 ' ,  ' cruise ' ,  ' e2e ' ]  
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					X_DIM  =  3 X_DIM  =  3  
			
		
	
		
		
			
				
					
					U_DIM  =  1 U_DIM  =  1  
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -50,6 +50,7 @@ T_IDXS_LST = [index_function(idx, max_val=MAX_T, max_idx=N) for idx in range(N+1 
			
		
	
		
		
			
				
					
					T_IDXS  =  np . array ( T_IDXS_LST ) T_IDXS  =  np . array ( T_IDXS_LST )  
			
		
	
		
		
			
				
					
					T_DIFFS  =  np . diff ( T_IDXS ,  prepend = [ 0. ] ) T_DIFFS  =  np . diff ( T_IDXS ,  prepend = [ 0. ] )  
			
		
	
		
		
			
				
					
					MIN_ACCEL  =  - 3.5 MIN_ACCEL  =  - 3.5  
			
		
	
		
		
			
				
					
					MAX_ACCEL  =  2.0  
			
		
	
		
		
			
				
					
					T_FOLLOW  =  1.45 T_FOLLOW  =  1.45  
			
		
	
		
		
			
				
					
					COMFORT_BRAKE  =  2.5 COMFORT_BRAKE  =  2.5  
			
		
	
		
		
			
				
					
					STOP_DISTANCE  =  6.0 STOP_DISTANCE  =  6.0  
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -190,8 +191,8 @@ def gen_long_ocp(): 
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					class  LongitudinalMpc : class  LongitudinalMpc :  
			
		
	
		
		
			
				
					
					  def  __init__ ( self ,  e2e = False ) :    def  __init__ ( self ,  mode = ' acc ' ) :   
			
				
				
			
		
	
		
		
			
				
					
					    self . e2e  =  e2 e     self . mode  =  mod e  
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					    self . solver  =  AcadosOcpSolverCython ( MODEL_NAME ,  ACADOS_SOLVER_TYPE ,  N )      self . solver  =  AcadosOcpSolverCython ( MODEL_NAME ,  ACADOS_SOLVER_TYPE ,  N )   
			
		
	
		
		
			
				
					
					    self . reset ( )      self . reset ( )   
			
		
	
		
		
			
				
					
					    self . source  =  SOURCES [ 2 ]      self . source  =  SOURCES [ 2 ]   
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -225,49 +226,42 @@ class LongitudinalMpc: 
			
		
	
		
		
			
				
					
					    self . x0  =  np . zeros ( X_DIM )      self . x0  =  np . zeros ( X_DIM )   
			
		
	
		
		
			
				
					
					    self . set_weights ( )      self . set_weights ( )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  set_weights ( self ,  prev_accel_constraint = True ) :    def  set_cost_weights ( self ,  cost_weights ,  constraint_cost_weights ) :   
			
				
				
			
		
	
		
		
			
				
					
					    if  self . e2e :      W  =  np . asfortranarray ( np . diag ( cost_weights ) )   
			
				
				
			
		
	
		
		
			
				
					
					      self . set_weights_for_xva_policy ( )   
			
		
	
		
		
			
				
					
					      self . params [ : , 0 ]  =  - 10.   
			
		
	
		
		
			
				
					
					      self . params [ : , 1 ]  =  10.   
			
		
	
		
		
			
				
					
					      self . params [ : , 2 ]  =  1e5   
			
		
	
		
		
			
				
					
					    else :   
			
		
	
		
		
			
				
					
					      self . set_weights_for_lead_policy ( prev_accel_constraint )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  set_weights_for_lead_policy ( self ,  prev_accel_constraint = True ) :   
			
		
	
		
		
			
				
					
					    a_change_cost  =  A_CHANGE_COST  if  prev_accel_constraint  else  0   
			
		
	
		
		
			
				
					
					    W  =  np . asfortranarray ( np . diag ( [ X_EGO_OBSTACLE_COST ,  X_EGO_COST ,  V_EGO_COST ,  A_EGO_COST ,  a_change_cost ,  J_EGO_COST ] ) )   
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					    for  i  in  range ( N ) :      for  i  in  range ( N ) :   
			
		
	
		
		
			
				
					
					      # TODO don't hardcode A_CHANGE_COST idx   
			
		
	
		
		
			
				
					
					      # reduce the cost on (a-a_prev) later in the horizon.        # reduce the cost on (a-a_prev) later in the horizon.   
			
		
	
		
		
			
				
					
					      W [ 4 , 4 ]  =  a_change_cost *  np . interp ( T_IDXS [ i ] ,  [ 0.0 ,  1.0 ,  2.0 ] ,  [ 1.0 ,  1.0 ,  0.0 ] )        W [ 4 , 4 ]  =  cost_weights [ 4 ]  *  np . interp ( T_IDXS [ i ] ,  [ 0.0 ,  1.0 ,  2.0 ] ,  [ 1.0 ,  1.0 ,  0.0 ] )   
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					      self . solver . cost_set ( i ,  ' W ' ,  W )        self . solver . cost_set ( i ,  ' W ' ,  W )   
			
		
	
		
		
			
				
					
					    # Setting the slice without the copy make the array not contiguous,      # Setting the slice without the copy make the array not contiguous,   
			
		
	
		
		
			
				
					
					    # causing issues with the C interface.      # causing issues with the C interface.   
			
		
	
		
		
			
				
					
					    self . solver . cost_set ( N ,  ' W ' ,  np . copy ( W [ : COST_E_DIM ,  : COST_E_DIM ] ) )      self . solver . cost_set ( N ,  ' W ' ,  np . copy ( W [ : COST_E_DIM ,  : COST_E_DIM ] ) )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    # Set L2 slack cost on lower bound constraints      # Set L2 slack cost on lower bound constraints   
			
		
	
		
		
			
				
					
					    Zl  =  np . array ( [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  DANGER_ZONE_COST ] )      Zl  =  np . array ( constraint_cost_weights )   
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					    for  i  in  range ( N ) :      for  i  in  range ( N ) :   
			
		
	
		
		
			
				
					
					      self . solver . cost_set ( i ,  ' Zl ' ,  Zl )        self . solver . cost_set ( i ,  ' Zl ' ,  Zl )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  set_weights_for_xva_policy ( self ) :    def  set_weights ( self ,  prev_accel_constraint = True ) :   
			
				
				
			
		
	
		
		
			
				
					
					    W  =  np . asfortranarray ( np . diag ( [ 0. ,  0.2 ,  0.25 ,  1. ,  0.0 ,  .1 ] ) )      if  self . mode  ==  ' acc ' :   
			
				
				
			
		
	
		
		
			
				
					
					    for  i  in  range ( N ) :        a_change_cost  =  A_CHANGE_COST  if  prev_accel_constraint  else  0   
			
				
				
			
		
	
		
		
			
				
					
					      self . solver . cost_set ( i ,  ' W ' ,  W )        cost_weights  =  [ X_EGO_OBSTACLE_COST ,  X_EGO_COST ,  V_EGO_COST ,  A_EGO_COST ,  a_change_cost ,  J_EGO_COST ]   
			
				
				
			
		
	
		
		
			
				
					
					    # Setting the slice without the copy make the array not contiguous,        constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  DANGER_ZONE_COST ]   
			
				
				
			
		
	
		
		
			
				
					
					    # causing issues with the C interface.      elif  self . mode  ==  ' blended ' :   
			
				
				
			
		
	
		
		
			
				
					
					    self . solver . cost_set ( N ,  ' W ' ,  np . copy ( W [ : COST_E_DIM ,  : COST_E_DIM ] ) )        cost_weights  =  [ 0. ,  1.0 ,  0.0 ,  0.0 ,  0.0 ,  1.0 ]   
			
				
				
			
		
	
		
		
			
				
					
					
      constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ]   
			
				
				
			
		
	
		
		
			
				
					
					    # Set L2 slack cost on lower bound constraints      elif  self . mode  ==  ' e2e ' :   
			
				
				
			
		
	
		
		
			
				
					
					    Zl  =  np . array ( [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ] )        cost_weights  =  [ 0. ,  0.2 ,  0.25 ,  1. ,  0.0 ,  .1 ]   
			
				
				
			
		
	
		
		
			
				
					
					    for  i  in  range ( N ) :        constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ]   
			
				
				
			
		
	
		
		
			
				
					
					      self . solver . cost_set ( i ,  ' Zl ' ,  Zl )      else :   
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					      raise  NotImplementedError ( f ' Planner mode  { self . mode }  not recognized ' )   
			
		
	
		
		
			
				
					
					    self . set_cost_weights ( cost_weights ,  constraint_cost_weights )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  set_cur_state ( self ,  v ,  a ) :    def  set_cur_state ( self ,  v ,  a ) :   
			
		
	
		
		
			
				
					
					    v_prev  =  self . x0 [ 1 ]      v_prev  =  self . x0 [ 1 ]   
			
		
	
		
		
			
				
					
					    self . x0 [ 1 ]  =  v      self . x0 [ 1 ]  =  v   
			
		
	
		
		
			
				
					
					    self . x0 [ 2 ]  =  a      self . x0 [ 2 ]  =  a   
			
		
	
		
		
			
				
					
					    if  abs ( v_prev  -  v )  >  2. :  # probably only helps if v < v_prev      if  abs ( v_prev  -  v )  >  2. :    # probably only helps if v < v_prev   
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					      for  i  in  range ( 0 ,  N + 1 ) :        for  i  in  range ( 0 ,  N + 1 ) :   
			
		
	
		
		
			
				
					
					        self . solver . set ( i ,  ' x ' ,  self . x0 )          self . solver . set ( i ,  ' x ' ,  self . x0 )   
			
		
	
		
		
			
				
					
					
 
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -306,31 +300,62 @@ class LongitudinalMpc: 
			
		
	
		
		
			
				
					
					    self . cruise_min_a  =  min_a      self . cruise_min_a  =  min_a   
			
		
	
		
		
			
				
					
					    self . cruise_max_a  =  max_a      self . cruise_max_a  =  max_a   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  update ( self ,  carstate ,  radarstate ,  v_cruise ) :    def  update ( self ,  carstate ,  radarstate ,  v_cruise ,  x ,  v ,  a ,  j ) :   
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					    v_ego  =  self . x0 [ 1 ]      v_ego  =  self . x0 [ 1 ]   
			
		
	
		
		
			
				
					
					    self . status  =  radarstate . leadOne . status  or  radarstate . leadTwo . status      self . status  =  radarstate . leadOne . status  or  radarstate . leadTwo . status   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    lead_xv_0  =  self . process_lead ( radarstate . leadOne )      lead_xv_0  =  self . process_lead ( radarstate . leadOne )   
			
		
	
		
		
			
				
					
					    lead_xv_1  =  self . process_lead ( radarstate . leadTwo )      lead_xv_1  =  self . process_lead ( radarstate . leadTwo )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    # set accel limits in params   
			
		
	
		
		
			
				
					
					    self . params [ : , 0 ]  =  interp ( float ( self . status ) ,  [ 0.0 ,  1.0 ] ,  [ self . cruise_min_a ,  MIN_ACCEL ] )   
			
		
	
		
		
			
				
					
					    self . params [ : , 1 ]  =  self . cruise_max_a   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    # To estimate a safe distance from a moving lead, we calculate how much stopping      # To estimate a safe distance from a moving lead, we calculate how much stopping   
			
		
	
		
		
			
				
					
					    # distance that lead needs as a minimum. We can add that to the current distance      # distance that lead needs as a minimum. We can add that to the current distance   
			
		
	
		
		
			
				
					
					    # and then treat that as a stopped car/obstacle at this new distance.      # and then treat that as a stopped car/obstacle at this new distance.   
			
		
	
		
		
			
				
					
					    lead_0_obstacle  =  lead_xv_0 [ : , 0 ]  +  get_stopped_equivalence_factor ( lead_xv_0 [ : , 1 ] )      lead_0_obstacle  =  lead_xv_0 [ : , 0 ]  +  get_stopped_equivalence_factor ( lead_xv_0 [ : , 1 ] )   
			
		
	
		
		
			
				
					
					    lead_1_obstacle  =  lead_xv_1 [ : , 0 ]  +  get_stopped_equivalence_factor ( lead_xv_1 [ : , 1 ] )      lead_1_obstacle  =  lead_xv_1 [ : , 0 ]  +  get_stopped_equivalence_factor ( lead_xv_1 [ : , 1 ] )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    # Fake an obstacle for cruise, this ensures smooth acceleration to set speed      # Update in ACC mode or ACC/e2e blend   
			
				
				
			
		
	
		
		
			
				
					
					    # when the leads are no factor.      if  self . mode  ==  ' acc ' :   
			
				
				
			
		
	
		
		
			
				
					
					    v_lower  =  v_ego  +  ( T_IDXS  *  self . cruise_min_a  *  1.05 )        self . params [ : , 0 ]  =  MIN_ACCEL  if  self . status  else  self . cruise_min_a   
			
				
				
			
		
	
		
		
			
				
					
					    v_upper  =  v_ego  +  ( T_IDXS  *  self . cruise_max_a  *  1.05 )        self . params [ : , 1 ]  =  self . cruise_max_a   
			
				
				
			
		
	
		
		
			
				
					
					    v_cruise_clipped  =  np . clip ( v_cruise  *  np . ones ( N + 1 ) ,  
 
			
				
				
			
		
	
		
		
			
				
					
					                               v_lower ,        # Fake an obstacle for cruise, this ensures smooth acceleration to set speed   
			
				
				
			
		
	
		
		
			
				
					
					                               v_upper )        # when the leads are no factor.   
			
				
				
			
		
	
		
		
			
				
					
					    cruise_obstacle  =  np . cumsum ( T_DIFFS  *  v_cruise_clipped )  +  get_safe_obstacle_distance ( v_cruise_clipped )        v_lower  =  v_ego  +  ( T_IDXS  *  self . cruise_min_a  *  1.05 )   
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					      v_upper  =  v_ego  +  ( T_IDXS  *  self . cruise_max_a  *  1.05 )   
			
		
	
		
		
			
				
					
					      v_cruise_clipped  =  np . clip ( v_cruise  *  np . ones ( N + 1 ) ,   
			
		
	
		
		
			
				
					
					                                 v_lower ,   
			
		
	
		
		
			
				
					
					                                 v_upper )   
			
		
	
		
		
			
				
					
					      cruise_obstacle  =  np . cumsum ( T_DIFFS  *  v_cruise_clipped )  +  get_safe_obstacle_distance ( v_cruise_clipped )   
			
		
	
		
		
			
				
					
					      x_obstacles  =  np . column_stack ( [ lead_0_obstacle ,  lead_1_obstacle ,  cruise_obstacle ] )   
			
		
	
		
		
			
				
					
					      self . source  =  SOURCES [ np . argmin ( x_obstacles [ 0 ] ) ]   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					      # These are not used in ACC mode   
			
		
	
		
		
			
				
					
					      x [ : ] ,  v [ : ] ,  a [ : ] ,  j [ : ]  =  0.0 ,  0.0 ,  0.0 ,  0.0   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    elif  self . mode  ==  ' blended ' :   
			
		
	
		
		
			
				
					
					      self . params [ : , 0 ]  =  MIN_ACCEL   
			
		
	
		
		
			
				
					
					      self . params [ : , 1 ]  =  MAX_ACCEL   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					      x_obstacles  =  np . column_stack ( [ lead_0_obstacle ,   
			
		
	
		
		
			
				
					
					                                     lead_1_obstacle ] )   
			
		
	
		
		
			
				
					
					      cruise_target  =  T_IDXS  *  v_cruise  +  x [ 0 ]   
			
		
	
		
		
			
				
					
					      xforward  =  ( ( v [ 1 : ]  +  v [ : - 1 ] )  /  2 )  *  ( T_IDXS [ 1 : ]  -  T_IDXS [ : - 1 ] )   
			
		
	
		
		
			
				
					
					      x  =  np . cumsum ( np . insert ( xforward ,  0 ,  x [ 0 ] ) )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					      x_and_cruise  =  np . column_stack ( [ x ,  cruise_target ] )   
			
		
	
		
		
			
				
					
					      x  =  np . min ( x_and_cruise ,  axis = 1 )   
			
		
	
		
		
			
				
					
					      self . source  =  ' e2e '   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    else :   
			
		
	
		
		
			
				
					
					      raise  NotImplementedError ( f ' Planner mode  { self . mode }  not recognized ' )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    self . yref [ : , 1 ]  =  x   
			
		
	
		
		
			
				
					
					    self . yref [ : , 2 ]  =  v   
			
		
	
		
		
			
				
					
					    self . yref [ : , 3 ]  =  a   
			
		
	
		
		
			
				
					
					    self . yref [ : , 5 ]  =  j   
			
		
	
		
		
			
				
					
					    for  i  in  range ( N ) :   
			
		
	
		
		
			
				
					
					      self . solver . set ( i ,  " yref " ,  self . yref [ i ] )   
			
		
	
		
		
			
				
					
					    self . solver . set ( N ,  " yref " ,  self . yref [ N ] [ : COST_E_DIM ] )   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    x_obstacles  =  np . column_stack ( [ lead_0_obstacle ,  lead_1_obstacle ,  cruise_obstacle ] )      x_obstacles  =  np . column_stack ( [ lead_0_obstacle ,  lead_1_obstacle ,  cruise_obstacle ] )   
			
		
	
		
		
			
				
					
					    self . source  =  SOURCES [ np . argmin ( x_obstacles [ 0 ] ) ]      self . source  =  SOURCES [ np . argmin ( x_obstacles [ 0 ] ) ]   
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
						
					 
					@ -345,6 +370,10 @@ class LongitudinalMpc: 
			
		
	
		
		
			
				
					
					      self . crash_cnt  =  0        self . crash_cnt  =  0   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					  def  update_with_xva ( self ,  x ,  v ,  a ) :    def  update_with_xva ( self ,  x ,  v ,  a ) :   
			
		
	
		
		
			
				
					
					    self . params [ : , 0 ]  =  - 10.   
			
		
	
		
		
			
				
					
					    self . params [ : , 1 ]  =  10.   
			
		
	
		
		
			
				
					
					    self . params [ : , 2 ]  =  1e5   
			
		
	
		
		
			
				
					
					
 
			
		
	
		
		
			
				
					
					    # v, and a are in local frame, but x is wrt the x[0] position      # v, and a are in local frame, but x is wrt the x[0] position   
			
		
	
		
		
			
				
					
					    # In >90degree turns, x goes to 0 (and may even be -ve)      # In >90degree turns, x goes to 0 (and may even be -ve)   
			
		
	
		
		
			
				
					
					    # So, we use integral(v) + x[0] to obtain the forward-distance      # So, we use integral(v) + x[0] to obtain the forward-distance