Getting the last error for a PID controller - python

I'm trying to build a PID controller on Python. Below is so far my implementation. Although the syntax is coherent, this is still pseudocode.
def PID(self, Kp, Ki, Kd, reference_velocity, vehicle_velocity, current_time, last_update_time):
desired_velocity = 0
Kp = 0
Ki = 0
Kd = 0
error = reference_velocity - vehicle_velocity
delta_time = current_time - last_update_time
proportional = Kp * error
integral = Ki * error
derivative = Kd * ((error - last_error) / (delta_time))
desired_velocity = proportional + integral + derivative
return desired_velocity
I am trying to figure out the last_error through what I already have given here but I couldn't figure it out.

Store the errors or the last error in a container in the function's outer scope.
errors = []
def PID(...):
...
errors.append(error)
return desired_velocity
Then in the function that last error can be retrieved with.
...
last_error = errors[-1]
Or you could make your controller a class whose instances are initialized with constants and starting parameters, and keeps records (a container like a list) of past errors. The calculation for the current/next output control signal would be a method that could be called with current parameter values.
Using your algorithm - something like this.
class PID:
def __init__(self,setpoint,t_now,Kp=0,Ki=0,Kd=0):
self.setpoint = setpoint
self.t_last = t_now
self.Kp = Kp
self.Ki = Ki
self.Kd = Kd
self.last_error = 0
def adjust(self,process,t_now):
error = process - self.setpoint
delta_time = t_now - self.t_last
proportional = self.Kp * error
integral = self.Ki * error
derivative = self.Kd * ((error - self.last_error) / (delta_time))
desired_process = proportional + integral + derivative
self.t_last = t_now
self.last_error = error
return desired_process
Usage:
v_controller = PID(setpoint=66, t_now=0, Kp=.2, Kd=.1, Ki=1)
# or
#parameters = {'setpoint':66, 't_now':0, 'Kp':.2, 'Kd':.1, 'Ki':1}
#v_controller = PID(**parameters)
print(v_controller.adjust(66.1,1))
print(v_controller.adjust(66.2,2))
print(v_controller.adjust(65.9,3))
print('-------------')
parameters = {'setpoint':66, 't_now':0, 'Kp':.2, 'Kd':.1, 'Ki':1.5}
v_controller = PID(**parameters)
print(v_controller.adjust(66.1,1))
print(v_controller.adjust(66.2,2))
print(v_controller.adjust(65.9,3))
print('-------------')
parameters = {'setpoint':66, 't_now':0, 'Kp':1.25, 'Kd':.2, 'Ki':.5}
v_controller = PID(**parameters)
print(v_controller.adjust(66.1,1))
print(v_controller.adjust(66.2,2))
print(v_controller.adjust(65.9,3))
Result
>>>
0.12999999999999262
0.2500000000000043
-0.1499999999999929
-------------
0.17999999999998975
0.3500000000000057
-0.19999999999999005
-------------
0.1949999999999889
0.37000000000000666
-0.2349999999999895
>>>

Related

How to solve non-numeric type in numeric context error in pyomo python

This is the code. I think the solver could be glpk instead of gurobi. I got the error before it tries to solve the problem.
from pyomo.environ import *
from pyomo.opt import SolverFactory, SolverStatus
PrintSolverOutput = False ###
model = ConcreteModel()
model.dual = Suffix(direction =Suffix.IMPORT)
model.X = Var(I,T, within = NonNegativeReals)
model.In = Var(I,T, within = NonNegativeReals)
model.S = Var(T, within = NonNegativeReals)
def Objetivo(model):
return (sum(C[i]*model.X[i,t]*((1+tau[i])**(t-1))+Ch[i]*model.In[i,t]
for i in I for t in T)
+sum(Cs*model.S[t]
for t in T)
)
model.Total_Cost = Objective(rule = Objetivo, sense= minimize)
def Balance(model,i,t):
if t==1:
return model.X[i,t]+I0[i]-model.In[i,t]==D[i,t]
elif t>=2:
return model.X[i,t]+model.IN[i,t-1]-model.In[i,t]==D[i,t]
def Horas(model, t):
return sum(r[i]*model.X[i,t] for i in I) <= H+model.S[t]
def Limite(model,t):
return model.S[T]<=LS
model.RBalance = Constraint(I,T,rule=Balance)
model.RHoras = Constraint(T,rule=Horas)
model.RLimiteoras = Constraint(T,rule=Limite)
opt = SolverFactory("gurobi")
This is the data
I forgot to put the data before.
T = [1,2,3,4,5,6]
I = ['A','B']
D = {('A',1):300,
('A',2):400,
('A',3):500,
('A',4):600,
('A',5):800,
('A',6):700,
('B',1):700,
('B',2):600,
('B',3):500,
('B',4):400,
('B',5):300,
('B',6):400}
tau = {'A':0.02,'B':0.01}
r = {'A':5,'B':3}
H = 3520
LS = 800
C = {'A':150,'B':120}
Ch = {'A':8,'B':4}
Cs = 6
I0 = {'A':100,'B':250}
error
The code is from a youtube tutorial and it worked for him but not for me. Why?
Aside from fixing two typos in your code, this model computes and solves for me without that error. Make these fixes, run it again, re-post the exact error with line number and the exact code that produces the error if stuck...

How to apply dead-time from FOPDT model to a Manipulated Variable in MPC gekko

I'm trying to build a MPC model with previously estimated values of k, tau and theta in a FOPDT equation. I implemented this sugestion to estimate the dead-time using cspline: How to estimate theta value in FOPDT equation using gekko?.
However, I can't do the same thing with MPC because I can't use a MV as the second argument of cspline in order to apply the dead-time. If I use a gekko variable for this second argument instead, the MV's don't change because they are not present in the equation. I have two MV's, one CV and two disturbance variables. How can I apply the dead-time in this case? Thank you
import numpy as np
import time
import plotly.express as px
from gekko import GEKKO
import json
from datetime import datetime
start_time_count = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("start_time: ", start_time_count)
dist1 = d19jcSliceMPC['Cond_PID_SP'].values
mv1 = d19jcSliceMPC['Front_PID_PV'].values
mv2 = d19jcSliceMPC['Rear_PID_PV'].values
dist2 = d19jcSliceMPC['Pull_rolling'].values
cv1 = d19jcSliceMPC['Cond_Center_Btm_TC'].values
run_time = 3.0 * 60.0
n = int(0.2*run_time)
Tsp1 = 1163.0 #setpoint
m = GEKKO(name='MPCbtmTC',remote=False)
m.time = np.linspace(0,n-1,n)
time_uc = m.Param(m.time)
# MV
Front = m.MV(value=mv1)
Front_ss = m.Param(value=mv1[0])
KpFront = m.Param(value=1.685312)
tauFront = m.Param(value=5.770839)
thetaFront = m.Param(value=0.114705)
t1 = np.linspace(-1,n-1,n)
ucFront = m.Var(); tm1 = m.Var(); m.Equation(tm1==time_uc-thetaFront)
m.cspline(tm1,ucFront,t1,np.array(Front),bound_x=False)
Rear = m.MV(value=mv2)
Rear_ss = m.Param(value=mv2[0])
KpRear = m.Param(value=0.1)
tauRear = m.Param(value=36.0)
thetaRear = m.Param(value=3.779397)
t2 = np.linspace(-4,n-1,n)
ucRear = m.Var(); tm2 = m.Var(); m.Equation(tm2==time_uc-thetaRear)
m.cspline(tm2,ucRear,t2,np.array(Rear),bound_x=False)
Front.STATUS = 1 # use to control temperature
Front.FSTATUS = 0 # no feedback measurement
Front.LOWER = 1160.0
Front.UPPER = 1200.0
Front.DMAX = 2.0
Front.COST = 0.0
Front.DCOST = 1.0e-4
Rear.STATUS = 1 # use to control temperature
Rear.FSTATUS = 0 # no feedback measurement
Rear.LOWER = 1180.0
Rear.UPPER = 1220.0
Rear.DMAX = 2.0
Rear.COST = 0.0
Rear.DCOST = 1.0e-4
# Parameters (disturbance)
CondSP = m.Param(value=dist1)
CondSP_ss = m.Param(value=dist1[0])
KpCondSP = m.Param(value=4.990293)
tauCondSP = m.Param(value=29.272660)
thetaCondSP = m.Param(value=2.554230)
t3 = np.linspace(-3,n-1,n)
ucCondSP = m.Var(); tm3 = m.Var(); m.Equation(tm3==time_uc-thetaCondSP)
m.cspline(tm3,ucCondSP,t3,dist1,bound_x=False)
Pull = m.Param(value=dist2)
Pull_ss = m.Param(value=dist2[0])
KpPull = m.Param(value=0.151304)
tauPull = m.Param(value=4.128567)
thetaPull = m.Param(value=0.0)
t4 = np.linspace(-0,n-1,n)
ucPull = m.Var(); tm4 = m.Var(); m.Equation(tm4==time_uc-thetaPull)
m.cspline(tm4,ucPull,t4,dist2,bound_x=False)
# Controlled variable
TC1_ss = m.Param(value=cv1[0])
TC1 = m.CV(value=TC1_ss.value)
TC1.STATUS = 1 # minimize error with setpoint range
TC1.FSTATUS = 1 # receive measurement
TC1.TR_INIT = 2 # reference trajectory
TC1.TAU = 2 # time constant for response
# Equation
m.Equation(TC1.dt()==(KpFront*(ucFront-Front_ss)-(TC1-TC1_ss))/tauFront + (KpRear*(ucRear-Rear_ss)-(TC1-TC1_ss))/tauRear+
(KpCondSP*(ucCondSP-CondSP_ss)-(TC1-TC1_ss))/tauCondSP + (KpPull*(ucPull-Pull_ss)-(TC1-TC1_ss))/tauPull)
# Global Options
m.options.IMODE = 6 # MPC
m.options.CV_TYPE = 1 # Objective type
m.options.NODES = 3 # Collocation nodes
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
TC1.MEAS = cv1[0]
# input setpoint with deadband +/- DT
DT = 0.1
TC1.SPHI = Tsp1 + DT
TC1.SPLO = Tsp1 - DT
# solve MPC
m.solve(disp=False)
# get additional solution information
with open(m.path+'//results.json') as f:
results = json.load(f)
finish_time_count = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("end_time: ", finish_time_count)
df_plot = pd.DataFrame({'DateTime' : d19jcSliceMPC.time,
'CV (TC1)' : results['v9.bcv'],
'SPHI' : results['v9.sp_hi'],
'SPLO' : results['v9.sp_lo'],
'Front' : Front,
'Rear' : Rear})
figGekko = px.line(df_plot,
x='DateTime',
y=['CV (TC1)','SPHI','SPLO','Front','Rear'],
labels={"value": "Degrees Celsius"},
title = "MPC")
figGekko.update_layout(legend_title_text='')
figGekko.show()
Edit:
As suggested, i changed to
ucFront = m.Var(); m.Equation(ucFront==Front)
tm1 = m.Var(); m.Equation(tm1==time_uc-thetaFront)
m.cspline(tm1,ucFront,t1,np.array(Front),bound_x=False)
but I get this error:
Error: Exception: Access Violation
At line 359 of file ./f90/cqp.f90
Traceback: not available, compile with -ftrace=frame or -ftrace=full
Error: 'results.json' not found. Check above for additional error details
If I leave just the MV as the unshifted input I get the same error as before
TypeError: y_data must be a python list or numpy array
As Junho Park correctly observed, you can create another variable x such as:
MV = m.MV() # use MV in MPC application
x = m.Var() # use x with cspline function
m.Equation(x==MV) # connect x and MV
The cspline object complains about the MV because it wants a calculated variable instead of a type that is by default fixed and consumes a degree of freedom.

If else statement returning TypeError:object of type 'int' has no len() - Not sure why

So, I am trying to solve an optimization problem. What I am trying to figure out is that when I run the code, my function call "to_fp_Cx" throws an error and I don't understand why.
The traceback keeps pointing to the functions I have defined. I tested these functions independently by calling it with different values and that worked as expected. So, I am not sure what is happening.
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-41-3f717a4f07e1> in <module>
37 # intermediate variables with explicit equations
38
---> 39 if(fload_c3_1 < 0.1):
40 alt_fload_c3_1 = m.Intermediate(0)
41 else:
~\AppData\Local\Continuum\anaconda3\lib\site-packages\gekko\gk_operators.py in __len__(self)
23 return self.name
24 def __len__(self):
---> 25 return len(self.value)
26 def __getitem__(self,key):
27 return self.value[key]
~\AppData\Local\Continuum\anaconda3\lib\site-packages\gekko\gk_operators.py in __len__(self)
142
143 def __len__(self):
--> 144 return len(self.value)
145
146 def __getitem__(self,key):
TypeError: object of type 'int' has no len()
As the name suggests, I am a python noob and I am bamboozled. Any help would be appreciated. Thanks
import numpy as np
# import gekko, pip install if needed
from gekko import GEKKO
# Compressor Performance curves
# Fraction capacity to Fractional power conversion
# Compressor C3
def to_fp_c3(fc):
a = 5.16102738
b = -16.25992208
c = 18.52731113
d = -8.859480201
e = 2.096698885
f = 0.334319989
if (fc < 0.1):
fp = 0.0
else:
fp = (a*fc**5)+(b*fc**4)+(c*fc**3)+(d*fc**2)+(e*fc**1)+(f*fc**0)
return fp
...
### Optimization Model ####
# create new model
m = GEKKO(remote = False)
# Solver option - 1: APOPT, 2: BPOPT, 3: IPOPT 0:Benchmark all available
m.options.SOLVER = 3
# declare model parameters
maxcap_c3_1 = m.Param(value = 900)
maxcap_c3_2 = m.Param(value = 900)
load = m.Param(value = 1500)
## Model variables
# load distribution
fload_c3_1 = m.Var(value=0.50,lb=0.0,ub=1.0, integer = False)
fload_c3_2 = m.Var(value=0.50,lb=0.0,ub=1.0, integer = False)
# declare variables and initial guesses
#totalpowerdraw = m.Var()
# intermediate variables with explicit equations
if(fload_c3_1 < 0.1):
alt_fload_c3_1 = m.Intermediate(0)
else:
alt_fload_c3_1 = m.Intermediate(fload_c3_1)
if(fload_c3_2 < 0.1):
alt_fload_c3_2 = m.Intermediate(0)
else:
alt_fload_c3_2 = m.Intermediate(fload_c3_2)
assignedload_c3_1 = m.Intermediate(alt_fload_c3_1 * maxcap_c3_1)
assignedload_c3_2 = m.Intermediate(alt_fload_c3_2 * maxcap_c3_2)
powerdraw_c3_1 = m.Intermediate(to_fp_c3(alt_fload_c3_1) * maxcap_c3_1)
powerdraw_c3_2 = m.Intermediate(to_fp_c3(alt_fload_c3_2) * maxcap_c3_2)
totalpowerdraw = m.Intermediate(powerdraw_c3_1 + powerdraw_c3_2)
# implicit equations
m.Equation(load == assignedload_c3_1 + assignedload_c3_2 )
# minimize weight1
m.Obj(totalpowerdraw)
# solve optimization
m.solve() # remote=False for local solve
print ('')
print ('--- Results of the Optimization Problem ---')
print (alt_fload_c3_1.value, powerdraw_c3_1.value)
print (alt_fload_c3_1.value, powerdraw_c3_2.value)
Try the m.if3() (or m.if2()) function from Gekko to make the conditional statement switch based on a Gekko variable. There is more information on conditional statements in Question about the conditional statement ('m.if3') in the GEKKO
# use gekko if3 (or if2)
alt_fload_c3_1 = m.if3(fload_c3_1-0.1,0,fload_c3_1)
alt_fload_c3_2 = m.if3(fload_c3_2-0.1,0,fload_c3_2)
Here is a version of your program that gives a successful solution.
import numpy as np
from gekko import GEKKO
# Compressor Performance curves
# Fraction capacity to Fractional power conversion
# Compressor C3
def to_fp_c3(fc):
a = 5.16102738
b = -16.25992208
c = 18.52731113
d = -8.859480201
e = 2.096698885
f = 0.334319989
fp = m.if3(fc-0.1,0,(a*fc**5)+(b*fc**4)+(c*fc**3)\
+(d*fc**2)+(e*fc**1)+(f*fc**0))
return fp
### Optimization Model ####
# create new model
m = GEKKO(remote = False)
# declare model parameters
maxcap_c3_1 = m.Param(value = 900)
maxcap_c3_2 = m.Param(value = 900)
load = m.Param(value = 1500)
## Model variables
# load distribution
fload_c3_1 = m.Var(value=0.50,lb=0.0,ub=1.0, integer = False)
fload_c3_2 = m.Var(value=0.50,lb=0.0,ub=1.0, integer = False)
# use gekko if3 (or if2)
alt_fload_c3_1 = m.if3(fload_c3_1-0.1,0,fload_c3_1)
alt_fload_c3_2 = m.if3(fload_c3_2-0.1,0,fload_c3_2)
assignedload_c3_1 = m.Intermediate(alt_fload_c3_1 * maxcap_c3_1)
assignedload_c3_2 = m.Intermediate(alt_fload_c3_2 * maxcap_c3_2)
powerdraw_c3_1 = m.Intermediate(to_fp_c3(alt_fload_c3_1) * maxcap_c3_1)
powerdraw_c3_2 = m.Intermediate(to_fp_c3(alt_fload_c3_2) * maxcap_c3_2)
totalpowerdraw = m.Intermediate(powerdraw_c3_1 + powerdraw_c3_2)
# implicit equations
m.Equation(load == assignedload_c3_1 + assignedload_c3_2 )
# minimize weight1
m.Obj(totalpowerdraw)
# solve optimization
m.solve() # remote=False for local solve
print ('')
print ('--- Results of the Optimization Problem ---')
print (alt_fload_c3_1.value, powerdraw_c3_1.value)
print (alt_fload_c3_1.value, powerdraw_c3_2.value)
with the solution:
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 0.0313 sec
Objective : 1576.7914326000025
Successful solution
---------------------------------------------------
--- Results of the Optimization Problem ---
[0.66761123885] [677.4476587]
[0.66761123885] [899.3437739]
You have to use this
If(fload_c3_1.value < 0.1):

Why doesn't Scipy root method converge to the same solution that in Matlab?

I have the following function from which I would like to obtain its solution. I'm trying to use Scipy to get them but I observe that the solution being given by Scipy is quite different from that of Matlab, using same settings.
What am I doing wrong?
def WandG(GW,Zin):
G,W = GW # define real variables to be used for the function
R = (G**2)*(Zin.real - 0.1164) +(( 70e-12 * ( W/100 ) )**2)*( 2.9036155068866304e+16*(Zin.real - 0.1164) ) - G
I = (G**2)*( Zin.imag - 18.743998408378143 * (1-W/200) ) + (((W/100)*70e-12)**2)*( 2.9036155068866304e+16*(Zin.imag - 18.743998408378143 * (1-W/200)) ) + 170399985.53071037*(70e-12*(W/100) )
return R,I
sol = optimize.root(WandG, [0.136879496, 47.04],(12.652884410928804+14.632724423763523j), jac=False,tol=1e-14, method='lm',options={'maxiter':100*400,'epsfcn':1e-14,'xtol':1e-14})
The solution using Scipy would be
sol.x[0] = 0.0795283512113496 # G
sol.x[1] = 36.53727146377749 # W
Whereas with MATLAB for the same initial conditions, solver and Zin, it returns, which I trust more since it resembles better to the final result of W which is W=50
G = 0.0794
W = 44.5234
EDIT : added Matlab code
% configuration of fsolve
options = optimoptions('fsolve','Display','iter-detailed','PlotFcn',#optimplotfirstorderopt);
options.StepTolerance = 1e-14;
%options.OptimalityTolerance = 1e-14
options.FunctionTolerance = 1e-14;
options.MaxIterations = 100000;
options.MaxFunctionEvaluations = 400;
options.Algorithm = 'levenberg-marquardt';%'trust-region'%
fun= #WandG;
x0 = [0.136879496', 47.04'];
% Solve the function fun
gw =fsolve(fun,x0,options);
% Function to be solved by fsolve
function F = WandG(x)
F(:,1) = (x(:,1).^2).*(real(Zin) - 0.1164) +(( 70e-12 .* ( x(:,2)./100 ) ).^2).*( w.^2.*(real(Zin) - 0.1164) ) - x(:,1);
F(:,2) = (x(:,1).^2).*( img(Zin) - 18.743998408378143 * (1-x(:,2)./200) ) + (((x(:,2)./100).*70e-12).^2).*( 2.9036155068866304e+16*(img(Zin) - 18.743998408378143 .* (1-x(:,2)./200)) ) + 170399985.53071037.*(70e-12.*(x(:,2)./100) );
end

Give out value of a function variable

I have a code for a VoiceActivityDetector and want to give out the value speech_ratio which is in a function
I tried to set up a new function to print out the value
def __init__(self, wave_input_filename):
self._read_wav(wave_input_filename)._convert_to_mono()
self.sample_window = 0.02 #20 ms
self.sample_overlap = 0.01 #10ms
self.speech_window = 0.5 #half a second
self.speech_energy_threshold = 0.6 #60% of energy in voice band
self.speech_start_band = 300
self.speech_end_band = 3000
#self.speech_ratio = 0
def detect_speech(self):
""" Detects speech regions based on ratio between speech band energy
and total energy.
Output is array of window numbers and speech flags (1 - speech, 0 - nonspeech).
"""
detected_windows = np.array([])
sample_window = int(self.rate * self.sample_window)
sample_overlap = int(self.rate * self.sample_overlap)
data = self.data
sample_start = 0
start_band = self.speech_start_band
end_band = self.speech_end_band
while (sample_start < (len(data) - sample_window)):
sample_end = sample_start + sample_window
if sample_end>=len(data): sample_end = len(data)-1
data_window = data[sample_start:sample_end]
energy_freq = self._calculate_normalized_energy(data_window)
sum_voice_energy = self._sum_energy_in_band(energy_freq, start_band, end_band)
sum_full_energy = sum(energy_freq.values())
speech_ratio = sum_voice_energy/sum_full_energy
#self.speech_ratio2 = speech_ratio
# Hipothesis is that when there is a speech sequence we have ratio of energies more than Threshold
speech_ratio = speech_ratio>self.speech_energy_threshold
detected_windows = np.append(detected_windows,[sample_start, speech_ratio])
sample_start += sample_overlap
detected_windows = detected_windows.reshape(int(len(detected_windows)/2),2)
detected_windows[:,1] = self._smooth_speech_detection(detected_windows)
return detected_windows
def printing(self):
print(self.speech_ratio)
return self.speech_ratio
When I set speech_ratio as a variable in the init it does not change the variable later on in the detect_speech function.
If I do not initialize speech_ratio in the init function it wont be a attribute of my object at all.
You use self.speech_ratio to try and print the value; you should use the same expression to assign to it.

Categories

Resources