import  numpy  as  np 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  numbers  import  Number 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								class  PIDController : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  __init__ ( self ,  k_p ,  k_i ,  k_f = 0. ,  k_d = 0. ,  pos_limit = 1e308 ,  neg_limit = - 1e308 ,  rate = 100 ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _k_p  =  k_p 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _k_i  =  k_i 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _k_d  =  k_d 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . k_f  =  k_f    # feedforward gain 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  isinstance ( self . _k_p ,  Number ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _k_p  =  [ [ 0 ] ,  [ self . _k_p ] ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  isinstance ( self . _k_i ,  Number ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _k_i  =  [ [ 0 ] ,  [ self . _k_i ] ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  isinstance ( self . _k_d ,  Number ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _k_d  =  [ [ 0 ] ,  [ self . _k_d ] ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . pos_limit  =  pos_limit 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . neg_limit  =  neg_limit 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . i_unwind_rate  =  0.3  /  rate 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . i_rate  =  1.0  /  rate 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . speed  =  0.0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . reset ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @property 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  k_p ( self ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  np . interp ( self . speed ,  self . _k_p [ 0 ] ,  self . _k_p [ 1 ] ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @property 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  k_i ( self ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  np . interp ( self . speed ,  self . _k_i [ 0 ] ,  self . _k_i [ 1 ] ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @property 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  k_d ( self ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  np . interp ( self . speed ,  self . _k_d [ 0 ] ,  self . _k_d [ 1 ] ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @property 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  error_integral ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self . i / self . k_i 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  reset ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . p  =  0.0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . i  =  0.0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . d  =  0.0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . f  =  0.0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . control  =  0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  update ( self ,  error ,  error_rate = 0.0 ,  speed = 0.0 ,  override = False ,  feedforward = 0. ,  freeze_integrator = False ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . speed  =  speed 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . p  =  float ( error )  *  self . k_p 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . f  =  feedforward  *  self . k_f 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . d  =  error_rate  *  self . k_d 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  override : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . i  - =  self . i_unwind_rate  *  float ( np . sign ( self . i ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    else : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      if  not  freeze_integrator : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . i  =  self . i  +  error  *  self . k_i  *  self . i_rate 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        # Clip i to prevent exceeding control limits 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        control_no_i  =  self . p  +  self . d  +  self . f 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								        control_no_i  =  np . clip ( control_no_i ,  self . neg_limit ,  self . pos_limit ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . i  =  np . clip ( self . i ,  self . neg_limit  -  control_no_i ,  self . pos_limit  -  control_no_i ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    control  =  self . p  +  self . i  +  self . d  +  self . f 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    self . control  =  np . clip ( control ,  self . neg_limit ,  self . pos_limit ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self . control