@ -3,7 +3,7 @@ import os 
			
		
	
		
			
				
					import  numpy  as  np  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					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  selfdrive . modeld . constants  import  index_function  
			
		
	
		
			
				
					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 " )  
			
		
	
		
			
				
					JSON_FILE  =  os . path . join ( LONG_MPC_DIR ,  " acados_ocp_long.json " )  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					SOURCES  =  [ ' lead0 ' ,  ' lead1 ' ,  ' cruise ' ]  
			
		
	
		
			
				
					SOURCES  =  [ ' lead0 ' ,  ' lead1 ' ,  ' cruise ' ,  ' e2e ' ]  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					X_DIM  =  3  
			
		
	
		
			
				
					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_DIFFS  =  np . diff ( T_IDXS ,  prepend = [ 0. ] )  
			
		
	
		
			
				
					MIN_ACCEL  =  - 3.5  
			
		
	
		
			
				
					MAX_ACCEL  =  2.0  
			
		
	
		
			
				
					T_FOLLOW  =  1.45  
			
		
	
		
			
				
					COMFORT_BRAKE  =  2.5  
			
		
	
		
			
				
					STOP_DISTANCE  =  6.0  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -190,8 +191,8 @@ def gen_long_ocp(): 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					class  LongitudinalMpc :  
			
		
	
		
			
				
					  def  __init__ ( self ,  e2e = False ) :   
			
		
	
		
			
				
					    self . e2e  =  e2 e  
			
		
	
		
			
				
					  def  __init__ ( self ,  mode = ' acc ' ) :   
			
		
	
		
			
				
					    self . mode  =  mod e  
			
		
	
		
			
				
					    self . solver  =  AcadosOcpSolverCython ( MODEL_NAME ,  ACADOS_SOLVER_TYPE ,  N )   
			
		
	
		
			
				
					    self . reset ( )   
			
		
	
		
			
				
					    self . source  =  SOURCES [ 2 ]   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -225,43 +226,36 @@ class LongitudinalMpc: 
			
		
	
		
			
				
					    self . x0  =  np . zeros ( X_DIM )   
			
		
	
		
			
				
					    self . set_weights ( )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  set_weights ( self ,  prev_accel_constraint = True ) :   
			
		
	
		
			
				
					    if  self . e2e :   
			
		
	
		
			
				
					      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 ] ) )   
			
		
	
		
			
				
					  def  set_cost_weights ( self ,  cost_weights ,  constraint_cost_weights ) :   
			
		
	
		
			
				
					    W  =  np . asfortranarray ( np . diag ( cost_weights ) )   
			
		
	
		
			
				
					    for  i  in  range ( N ) :   
			
		
	
		
			
				
					      # TODO don't hardcode A_CHANGE_COST idx   
			
		
	
		
			
				
					      # 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 )   
			
		
	
		
			
				
					    # Setting the slice without the copy make the array not contiguous,   
			
		
	
		
			
				
					    # causing issues with the C interface.   
			
		
	
		
			
				
					    self . solver . cost_set ( N ,  ' W ' ,  np . copy ( W [ : COST_E_DIM ,  : COST_E_DIM ] ) )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    # 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 ) :   
			
		
	
		
			
				
					      self . solver . cost_set ( i ,  ' Zl ' ,  Zl )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  set_weights_for_xva_policy ( self ) :   
			
		
	
		
			
				
					    W  =  np . asfortranarray ( np . diag ( [ 0. ,  0.2 ,  0.25 ,  1. ,  0.0 ,  .1 ] ) )   
			
		
	
		
			
				
					    for  i  in  range ( N ) :   
			
		
	
		
			
				
					      self . solver . cost_set ( i ,  ' W ' ,  W )   
			
		
	
		
			
				
					    # Setting the slice without the copy make the array not contiguous,   
			
		
	
		
			
				
					    # causing issues with the C interface.   
			
		
	
		
			
				
					    self . solver . cost_set ( N ,  ' W ' ,  np . copy ( W [ : COST_E_DIM ,  : COST_E_DIM ] ) )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    # Set L2 slack cost on lower bound constraints   
			
		
	
		
			
				
					    Zl  =  np . array ( [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ] )   
			
		
	
		
			
				
					    for  i  in  range ( N ) :   
			
		
	
		
			
				
					      self . solver . cost_set ( i ,  ' Zl ' ,  Zl )   
			
		
	
		
			
				
					  def  set_weights ( self ,  prev_accel_constraint = True ) :   
			
		
	
		
			
				
					    if  self . mode  ==  ' acc ' :   
			
		
	
		
			
				
					      a_change_cost  =  A_CHANGE_COST  if  prev_accel_constraint  else  0   
			
		
	
		
			
				
					      cost_weights  =  [ X_EGO_OBSTACLE_COST ,  X_EGO_COST ,  V_EGO_COST ,  A_EGO_COST ,  a_change_cost ,  J_EGO_COST ]   
			
		
	
		
			
				
					      constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  DANGER_ZONE_COST ]   
			
		
	
		
			
				
					    elif  self . mode  ==  ' blended ' :   
			
		
	
		
			
				
					      cost_weights  =  [ 0. ,  1.0 ,  0.0 ,  0.0 ,  0.0 ,  1.0 ]   
			
		
	
		
			
				
					      constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ]   
			
		
	
		
			
				
					    elif  self . mode  ==  ' e2e ' :   
			
		
	
		
			
				
					      cost_weights  =  [ 0. ,  0.2 ,  0.25 ,  1. ,  0.0 ,  .1 ]   
			
		
	
		
			
				
					      constraint_cost_weights  =  [ LIMIT_COST ,  LIMIT_COST ,  LIMIT_COST ,  0.0 ]   
			
		
	
		
			
				
					    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 ) :   
			
		
	
		
			
				
					    v_prev  =  self . x0 [ 1 ]   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -306,23 +300,24 @@ class LongitudinalMpc: 
			
		
	
		
			
				
					    self . cruise_min_a  =  min_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 ]   
			
		
	
		
			
				
					    self . status  =  radarstate . leadOne . status  or  radarstate . leadTwo . status   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    lead_xv_0  =  self . process_lead ( radarstate . leadOne )   
			
		
	
		
			
				
					    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   
			
		
	
		
			
				
					    # 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.   
			
		
	
		
			
				
					    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 ] )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    # Update in ACC mode or ACC/e2e blend   
			
		
	
		
			
				
					    if  self . mode  ==  ' acc ' :   
			
		
	
		
			
				
					      self . params [ : , 0 ]  =  MIN_ACCEL  if  self . status  else  self . cruise_min_a   
			
		
	
		
			
				
					      self . params [ : , 1 ]  =  self . cruise_max_a   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					      # Fake an obstacle for cruise, this ensures smooth acceleration to set speed   
			
		
	
		
			
				
					      # when the leads are no factor.   
			
		
	
		
			
				
					      v_lower  =  v_ego  +  ( T_IDXS  *  self . cruise_min_a  *  1.05 )   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -331,6 +326,36 @@ class LongitudinalMpc: 
			
		
	
		
			
				
					                                 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 ] )   
			
		
	
		
			
				
					    self . source  =  SOURCES [ np . argmin ( x_obstacles [ 0 ] ) ]   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -345,6 +370,10 @@ class LongitudinalMpc: 
			
		
	
		
			
				
					      self . crash_cnt  =  0   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  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   
			
		
	
		
			
				
					    # In >90degree turns, x goes to 0 (and may even be -ve)   
			
		
	
		
			
				
					    # So, we use integral(v) + x[0] to obtain the forward-distance