├── README.md └── PID_Video.py /README.md: -------------------------------------------------------------------------------- 1 | # PIDtest 2 | 3 | Note: this was created in one 3 hour session and uploaded to youtube (video link below) 4 | Thanks to those who have contributed so far :) 5 | 6 |

I MADE AN IMPROVED VERSION OF THIS CODE https://github.com/Jmack66/TVC-Sim

7 | 8 | a Toy implementation of a PID controller in Python using turtle for graphics 9 | video found here https://youtu.be/ZMI_kpNUgJM 10 | -------------------------------------------------------------------------------- /PID_Video.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import turtle 4 | import time 5 | 6 | #GLOBAL PARAMS 7 | TIMER = 0 8 | TIME_STEP = 0.001 9 | SETPOINT = 10 10 | SIM_TIME = 100 11 | INITIAL_X = 0 12 | INITIAL_Y = -100 13 | MASS = 1 #kg 14 | MAX_THRUST = 15 #Newtons 15 | g = -9.81 #Gravitational constant 16 | V_i = 0 #initial velocity 17 | Y_i = 0 #initial height 18 | #------------ 19 | #---PID GAINS--- 20 | #ku = 0.6 21 | #Tu = 18 ms 22 | KP = 0.36 23 | KI = 40.0 24 | KD = 0.0008099999999999997 25 | #KD = 0.00128 for higher setpoints 26 | antiWindup = True 27 | # KP = 0.6 28 | # KI = 0.0 29 | # KD = 0.0 30 | #--------------- 31 | 32 | class Simulation(object): 33 | def __init__(self): 34 | self.Insight = Rocket() 35 | self.pid = PID(KP,KI,KD,SETPOINT) 36 | self.screen = turtle.Screen() 37 | self.screen.setup(800,600) 38 | self.marker = turtle.Turtle() 39 | self.marker.penup() 40 | self.marker.left(180) 41 | self.marker.goto(15,SETPOINT) 42 | self.marker.color('red') 43 | self.sim = True 44 | self.timer = 0 45 | self.poses = np.array([]) 46 | self.times = np.array([]) 47 | self.kpe = np.array([]) 48 | self.kde = np.array([]) 49 | self.kie = np.array([]) 50 | self.thrst = np.array([]) 51 | def cycle(self): 52 | while(self.sim): 53 | thrust = self.pid.compute(self.Insight.get_y()) 54 | print(thrust) 55 | self.Insight.set_ddy(thrust) 56 | self.Insight.set_dy() 57 | self.Insight.set_y() 58 | time.sleep(TIME_STEP) 59 | self.timer += 1 60 | if self.timer > SIM_TIME: 61 | print("SIM ENDED") 62 | self.sim = False 63 | elif self.Insight.get_y() > 700: 64 | print("OUT OF BOUNDS") 65 | self.sim = False 66 | elif self.Insight.get_y() < -700: 67 | print("OUT OF BOUNDS") 68 | self.sim = False 69 | self.poses = np.append(self.poses,self.Insight.get_y()) 70 | self.times = np.append(self.times,self.timer) 71 | self.kpe = np.append(self.kpe,self.pid.get_kpe()) 72 | self.kde = np.append(self.kde,self.pid.get_kde()) 73 | self.kie = np.append(self.kie,self.pid.get_kie()) 74 | self.thrst = np.append(self.thrst,thrust) 75 | graph(self.times,self.poses,self.kpe,self.kde,self.kie,self.thrst) 76 | 77 | def graph(x,y1,y2,y3,y4,y5): 78 | fig, (ax1, ax2,ax3,ax4,ax5) = plt.subplots(5, sharex=True) 79 | #fig.suptitle('antiwindup') 80 | ax1.set(ylabel='rocket \nHeight') 81 | ax1.plot(x,y1) 82 | ax2.set(ylabel='KP_error') 83 | ax2.plot(x,y2,'tab:red') 84 | ax3.set(ylabel='KD_error') 85 | ax3.plot(x,y3,'tab:orange') 86 | ax4.set(ylabel='KI_error') 87 | ax4.plot(x,y4,'tab:pink') 88 | ax5.set(ylabel='rocket \nThrust') 89 | ax5.plot(x,y5,'tab:brown') 90 | plt.show() 91 | 92 | class Rocket(object): 93 | def __init__(self): 94 | global Rocket 95 | self.Rocket = turtle.Turtle() 96 | self.Rocket.shape('square') 97 | self.Rocket.color('black') 98 | self.Rocket.penup() 99 | self.Rocket.goto(INITIAL_X,INITIAL_Y) 100 | self.Rocket.speed(0) 101 | #physics 102 | self.ddy = 0 103 | self.dy = V_i 104 | self.y = INITIAL_Y 105 | def set_ddy(self,thrust): 106 | self.ddy = g + thrust / MASS 107 | def get_ddy(self): 108 | return self.ddy 109 | def set_dy(self): 110 | self.dy += self.ddy * TIME_STEP 111 | def get_dy(self): 112 | return self.dy 113 | def set_y(self): 114 | self.Rocket.sety(self.y + self.dy * TIME_STEP) 115 | def get_y(self): 116 | self.y = self.Rocket.ycor() 117 | return self.y 118 | 119 | class PID(object): 120 | def __init__(self,KP,KI,KD,target): 121 | self.kp = KP 122 | self.ki = KI 123 | self.kd = KD 124 | self.setpoint = target 125 | self.error = 0 126 | self.integral_error = 0 127 | self.error_last = 0 128 | self.derivative_error = 0 129 | self.output = 0 130 | def compute(self, pos): 131 | self.error = self.setpoint - pos 132 | #self.integral_error += self.error * TIME_STEP 133 | self.derivative_error = (self.error - self.error_last) / TIME_STEP 134 | self.error_last = self.error 135 | self.output = self.kp*self.error + self.ki*self.integral_error + self.kd*self.derivative_error 136 | if(abs(self.output)>= MAX_THRUST and (((self.error>=0) and (self.integral_error>=0))or((self.error<0) and (self.integral_error<0)))): 137 | if(antiWindup): 138 | #no integration 139 | self.integral_error = self.integral_error 140 | else: 141 | #if no antiWindup rectangular integration 142 | self.integral_error += self.error * TIME_STEP 143 | else: 144 | #rectangular integration 145 | self.integral_error += self.error * TIME_STEP 146 | if self.output >= MAX_THRUST: 147 | self.output = MAX_THRUST 148 | elif self.output <= 0: 149 | self.output = 0 150 | return self.output 151 | 152 | def get_kpe(self): 153 | return self.kp*self.error 154 | def get_kde(self): 155 | return self.kd*self.derivative_error 156 | def get_kie(self): 157 | return self.ki*self.integral_error 158 | 159 | def main(): 160 | sim = Simulation() 161 | sim.cycle() 162 | 163 | main() 164 | --------------------------------------------------------------------------------