| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- from core.utils import *
- import os
- '''
- base discrete/takahashi PID controller
- '''
- class BasePID:
- def __init__(self, target, clip_min, clip_max, controller_type, kp=0, ki=0, kd=0, dt=1, Kc=0, Ti=0, Td=0, Ts=0, debug=False, type='base', swap_error_fn=False):
- self.Kp = kp # discrete pid kp
- self.Ki = ki # discrete pid ki
- self.Kd = kd # discrete pid kd
- self.T = dt # discrete pid frequency time.
- self.Ti = Ti # takahashi ti
- self.Td = Td # takahashi td
- self.Ts = Ts # takahashi ts
- self.Kc = Kc # takahashi kc
- self.target = target # pid set point, target
- self.prev_feedback = 0
- self.feedback_hist = [0, 0]
- self.output_hist = [0]
- self.error_hist = [0, 0]
- self.debug=debug
- self.clip_min = clip_min
- self.clip_max = clip_max
- self.controller_type = CONTROLLER_TYPE_DISCRETE
- self.swap_error_fn = swap_error_fn
- self.type = type
- def continuous_pid(self, feedback):
- ret = (self.Kp * self.proportional(feedback)) + (self.Ki * self.integral(feedback)) + (self.Kd * self.derivative(feedback))
- self.feedback_hist+=[feedback]
- self.prev_feedback=feedback
- return ret
- def discrete_pid(self, feedback, debug=True):
- k1 = self.Kp + self.Ki + self.Kd
- k2 = -1 * self.Kp - 2 * self.Kd
- k3 = self.Kd
- err = self.proportional(feedback)
- ret = self.output_hist[-1] + k1 * err + k2 * self.error_hist[-1] + k3 * self.error_hist[-2]
- self.error_hist+=[err]
- self.feedback_hist+=[feedback]
- return ret
- def zero_feedback_hist(self):
- count = 0
- length = len(self.feedback_hist)
- for i in range(0,length):
- if self.feedback_hist[length-(i+1)]==0:
- count+=1
- else:
- return count
- return count
- def takahashi(self, feedback, debug=True):
- err = self.proportional(feedback)
- ret = self.output_hist[-1] + self.Kc * (self.feedback_hist[-1] - feedback + self.Ts * err/ self.Ti + self.Td / self.Ts * (2*self.feedback_hist[-1] - feedback - self.feedback_hist[-2]))
- self.error_hist+=[err]
- self.feedback_hist+=[feedback]
- return ret
- def pid_clipped(self, feedback, debug=True):
- pid_value = None
- if self.controller_type == CONTROLLER_TYPE_TAKAHASHI:
- pid_value = self.takahashi(feedback, debug)
- elif self.controller_type == CONTROLLER_TYPE_DISCRETE:
- pid_value = self.discrete_pid(feedback, debug)
- else:
- pid_value = self.continuous_pid(feedback)
- if pid_value <= self.clip_min:
- pid_value = self.clip_min
- if pid_value >= self.clip_max:
- pid_value = self.clip_max
- #if self.integral(feedback) == 0 and len(self.feedback_hist) >=3 and self.feedback_hist[-1] == 0 and self.feedback_hist[-2] == 0 and self.feedback_hist[-3] == 0:
- #pid_value = 0.9**self.zero_feedback_hist()
- self.output_hist+=[pid_value]
- return pid_value
- def error(self, feedback):
- if self.swap_error_fn:
- # maintain positive proportional gains
- return self.target - feedback
- else:
- return feedback - self.target
- def proportional(self, feedback):
- return self.error(feedback)
- def integral(self, feedback):
- return sum(self.feedback_hist[-10:]) + feedback
- def derivative(self, feedback):
- return (self.error(self.prev_feedback) - self.error(feedback)) / self.T
- def write_feedback(self, feedback_hist_file):
- if len(self.feedback_hist)==0:
- return
- buf = ''
- buf+=str(self.feedback_hist[0])
- buf+=','
- for i in self.feedback_hist[1:]:
- buf+=str(i)+','
- with open(feedback_hist_file, "w+") as f:
- f.write(buf)
- def write_fval(self, output_hist_file):
- if len(self.output_hist)==0:
- return
- buf = ''
- buf+=str(self.output_hist[0])
- buf+=','
- for i in self.output_hist[1:]:
- buf+=str(i)+','
- with open(output_hist_file, "w+") as f:
- f.write(buf)
- def write(self, feedback_hist_file='_feedback.hist', output_hist_file='_output.hist'):
- self.write_feedback('log' + os.sep + self.type+feedback_hist_file)
- self.write_fval('log'+ os.sep + self.type+output_hist_file)
- def acc(self, window=ACC_WINDOW):
- windowed_hist = [round(hist, 2) for hist in self.feedback_hist][-1*window:]
- return sum(np.array(windowed_hist)==self.target)/float(len(windowed_hist))
- def acc_percentage(self):
- return self.acc(window=0) * 100
|