Related
This is the following question after appyling comments from: Using GEKKO for Moving Horizon Estimation online
I have studied example from estimation iterative example on the Dynamic Optimization course website and revised my code as follows:
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
import matplotlib; matplotlib.use('TkAgg')
class Observer():
def __init__(self, window_size, r_init, alpha_init):
self.m = GEKKO(remote=False)
self.dt = 0.05
self.m.time = [i*self.dt for i in range(window_size)]
#Parameters
self.m.u = self.m.MV()
#Variables
self.m.r = self.m.CV(lb=0) # value=r_init) #ub=20 can be over 20
self.m.alpha = self.m.CV() # value=alpha_init) #ub lb for angle?
#Equations
self.m.Equation(self.m.r.dt()== -self.m.cos(self.m.alpha))
self.m.Equation(self.m.alpha.dt()== self.m.sin(self.m.alpha)/self.m.r - self.m.u) # differential equation
#Options
self.m.options.MV_STEP_HOR = 2
self.m.options.IMODE = 5 # dynamic estimation
self.m.options.EV_TYPE = 2 #Default 1: absolute error form 2: squared error form
self.m.options.DIAGLEVEL = 0 #diagnostic level
self.m.options.NODES = 5 #nodes # collocation nodes default:2
self.m.options.SOLVER = 3 #solver_num
# STATUS = 0, optimizer doesn't adjust value
# STATUS = 1, optimizer can adjust
self.m.u.STATUS = 0
self.m.r.STATUS = 1
self.m.alpha.STATUS = 1
# FSTATUS = 0, no measurement
# FSTATUS = 1, measurement used to update model
self.m.u.FSTATUS = 1 #default
self.m.r.FSTATUS = 1
self.m.alpha.FSTATUS = 1
self.m.r.TR_INIT = 0
self.m.alpha.TR_INIT = 0
self.count = 0
def MHE(self, observed_state, u_data):
self.count =+ 1
self.m.u.MEAS = u_data
self.m.r.MEAS = observed_state[0]
self.m.alpha.MEAS = observed_state[1]
self.m.solve(disp=False)
return self.m.r.MODEL, self.m.alpha.MODEL
if __name__=="__main__":
FILE_PATH00 = '/home/shane16/Project/model_guard/uav_paper/adversarial/SA_PPO/src/DATA/4end_estimation_results_r.csv'
FILE_PATH01 = '/home/shane16/Project/model_guard/uav_paper/adversarial/SA_PPO/src/DATA/4end_estimation_results_alpha.csv'
FILE_PATH02 = '/home/shane16/Project/model_guard/uav_paper/adversarial/SA_PPO/src/DATA/4end_action_buffer_eps0.0_sig0.0.csv'
cycles = 55
x = np.arange(cycles) # 1...300
matrix00 = np.loadtxt(FILE_PATH00, delimiter=',')
matrix01 = np.loadtxt(FILE_PATH01, delimiter=',')
matrix02 = np.loadtxt(FILE_PATH02, delimiter=',')
vanilla_action_sigma_0 = matrix02
vanilla_estimation_matrix_r = np.zeros(cycles)
vanilla_estimation_matrix_alpha = np.zeros(cycles)
# sigma = 0.0
# vanilla model true/observed states
r_vanilla_sigma_0_true = matrix00[0, 3:] # from step 1
r_vanilla_sigma_0_observed = matrix00[1, 3:] # from step1
alpha_vanilla_sigma_0_true = matrix01[0, 3:]
alpha_vanilla_sigma_0_observed = matrix01[1, 3:]
# initialize estimator
sigma = 0.0 #1.0
solver_num = 3
nodes = 5
# for window_size in [5, 10, 20, 30, 40, 50]:
window_size = 5
observer = Observer(window_size, r_vanilla_sigma_0_observed[0], alpha_vanilla_sigma_0_observed[0])
for i in range(cycles):
if i % 100 == 0:
print('cylcle: {}'.format(i))
vanilla_observed_states = np.hstack((r_vanilla_sigma_0_observed[i], alpha_vanilla_sigma_0_observed[i])) # from current observed state
r_hat, alpha_hat = observer.MHE(vanilla_observed_states, vanilla_action_sigma_0[i]) # and current action -> estimate current state
vanilla_estimation_matrix_r[i] = r_hat
vanilla_estimation_matrix_alpha[i] = alpha_hat
#plot vanilla
plt.figure()
plt.subplot(3,1,1)
plt.title('Vanilla model_sig{}'.format(sigma))
plt.plot(x, vanilla_action_sigma_0[:cycles],'b:',label='action (w)')
plt.legend()
plt.subplot(3,1,2)
plt.ylabel('r')
plt.plot(x, r_vanilla_sigma_0_true[:cycles], 'k-', label='true_r')
plt.plot(x, r_vanilla_sigma_0_observed[:cycles], 'gx', label='observed_r')
plt.plot(x, vanilla_estimation_matrix_r, 'r--', label='time window: 10')
# plt.legend()
plt.subplot(3,1,3)
plt.xlabel('time steps')
plt.ylabel('alpha')
plt.plot(x, alpha_vanilla_sigma_0_true[:cycles], 'k-', label='true_alpha')
plt.plot(x, alpha_vanilla_sigma_0_observed[:cycles], 'gx', label='observed_alpha')
plt.plot(x, vanilla_estimation_matrix_alpha, 'r--', label='time window: {}'.format(window_size))
plt.legend()
plt.savefig('plot/revision/4estimated_STATES_vanilla_sig{}_window{}_cycles{}_solver{}_nodes{}.png'.format(sigma, window_size,cycles, solver_num, nodes))
plt.show()
csv files: https://drive.google.com/drive/folders/1jW_6zBCdbJHB7yU3HmCIhamEyOT1LJqD?usp=sharing
The code works when initialized with values specified at line 15,16 (m.r, m.alpha).
However, if I try with no initial value,(as same condition in example), solution is not found.
terminal output:
cylcle: 0 Traceback (most recent call last): File
"4observer_mhe.py", line 86, in
r_hat, alpha_hat = observer.MHE(vanilla_observed_states, vanilla_action_sigma_0[i]) # and current action -> estimate current
state File "4observer_mhe.py", line 49, in MHE
self.m.solve(disp=False) File "/home/shane16/Project/model_guard/LipSDP/lipenv/lib/python3.7/site-packages/gekko/gekko.py",
line 2140, in solve
raise Exception(apm_error) Exception: #error: Solution Not Found
What could be the solution to this problem?
I have tried below strategies, but couldn't find the solution.
Reduce the number of decision variables by using m.FV() or m.MV() with m.options.MV_STEP_HOR=2+ to reduce the degrees of freedom for the solver for the unknown parameters.
Try other solvers with m.options.SOLVER=1 or m.options.SOLVER=2.
I expect to see estimation results that follow the true state well.
But I guess I'm doing something wrong.
Could anyone help me please?
Thank you.
Solvers sometimes need good initial guess values or constraints (lower and upper bounds) on the degrees of freedom (MV or FV) to find the optimal solution.
One of the equations may be the source of the problem:
self.m.alpha.dt() == self.m.sin(self.m.alpha)/self.m.r - self.m.u
The initial value of r is zero (default) because no initial value is provided when it is declared as self.m.r = self.m.CV(lb=0). A comment suggests that it was formerly initialized with value r_init. The zero value creates a divide-by-zero for that equation. Try rearranging the equation into an equivalent form that avoids the potential for divide-by-zero either with the initial guess or when the solver is iterating.
self.m.r*self.m.alpha.dt() == self.m.sin(self.m.alpha) - self.m.r*self.m.u
There may be other things that are also causing the model to not converge. When the solution does not converge then the infeasibilities.txt file can be a source to troubleshoot the specific equations that are having trouble. Here are instructions to retrieve the infeasibilities.txt file: How to retrieve the 'infeasibilities.txt' from the gekko
I have a data.table x, it has 2 columns a, b.
I want calculate a c column.
library(data.table)
x = data.table(a = c(1:5), b = c(1,0,2,3,6), c = NA)
x$a[1] = NA
x$b[1] = NA
x
#> a b c
#> <int> <num> <lgcl>
#> 1: NA NA NA
#> 2: 2 0 NA
#> 3: 3 2 NA
#> 4: 4 3 NA
#> 5: 5 6 NA
The algorithm is:
c[i] = ifelse(a[i] < b[i] & c[i-1] < b[i], a[i], b[i])
I don't want to use for loop, because it's too slow.
I want to use data.table functions, or a fast method like this:
x$c = fifelse(x$a < x$b & lag(x$c) < x$b, x$a, x$b)
But it's not working, because x$c calculation is in progress right now.
Any solution for this?
Thanks for help
Janos
You could use Reduce with accumulate=T option :
library(data.table)
x = data.table(a = c(1:5), b = c(1,0,2,3,6), c = NA)
x$a[1] = NA
x$b[1] = NA
x[,c:=Reduce(f = function(prev,val) ifelse((val$a < val$b & prev<val$b),val$a,val$b),
x = split(.SD[-1],seq_len(.N-1)), init = NA
,accumulate = T)][]
#> a b c
#> <int> <num> <num>
#> 1: NA NA NA
#> 2: 2 0 0
#> 3: 3 2 2
#> 4: 4 3 3
#> 5: 5 6 5
Reduce passes the result of the previous row calculation to calculate the next row.
accumulate=T returns the intermediate results instead of only the last row.
everyone and Professor John
We are using gekko to do MPC on tclab simulation model. We try to emulate the situation that on site the actuator deviates from MV calculated by gekko because of the problems of actuator.
If the deviation is in the fixed pattern, for example a quite big constant deviation happens for a long time and may come back then work well for a long time. We can deal with it by extra logic to detect deviation and add the deviation value to the mv calculated by gekko.
one day, I noticed that there could be meas for MV when fstatus = 1. So I gave it a try. I hope gekko could deal with the deviation by itself. for example, if mv from gekko is 10 and the measurement is 5 and the pattern continues, gekko may spit out a higher MV value than 10, for example 15 and measurement is 10.
In the simulation, when I set MV's fstatus=1, the MV's curve becomes to :
q1a is the q1 with manual deviation. In the above pic, q1a == q1. It looks like gekko takes one more step thinking about the MV's effect.
In the below pic, there are two times range, one with "q1a == q1+20" and the other with "q1a == q1 -20". q1a's value is fed to tclab and mv(q1)'s meas.
I do not understand why the q1 calculated by gekko is going up or going down when meas deviates despite the t1 is going far away from sp.
Edit: Example Code
See the screen shot below from "normal" HMI. The sluggish MV disappeared, so it maybe caused by bug in my code. But the up-going or down-going could still be seen.
See my code below:
from random import random
from random import randrange
import tclab
from tclab import labtime
from tclab import TCLabModel
import numpy as np
import time
import matplotlib.pyplot as plt
from gekko import GEKKO
import json
from tclab import TCLabModel
make_mp4 = True
if make_mp4:
import imageio # required to make animation
import os
try:
os.mkdir('./figures')
except:
pass
class tclab_heaterpipe():
def __init__(self,d1,d2,model):
if(d1 >= 1 and d2 >=1):
self.delay_q1_step = int(d1)
self.delay_q2_step = int(d2)
self.q1_buffer = [0] * self.delay_q1_step
self.q2_buffer = [0] * self.delay_q2_step
self.m = model
else:
self.delay_q1_step =0
self.delay_q2_step =0
return
def Q1_delay(self,q1):
if(self.delay_q1_step == 0):
self.m.Q1(q1)
self.q1_buffer.insert(0,q1)
self.m.Q1(self.q1_buffer.pop())
def Q2_delay(self,q2):
if(self.delay_q2_step == 0):
self.m.Q1(q2)
self.q2_buffer.insert(0,q2)
self.m.Q2(self.q2_buffer.pop())
# Connect to Arduino
connected = False
theta1 = 1
theta2 = 1
T = tclab.setup(connected)
a = T()
tclab_delay = tclab_heaterpipe(theta1,theta2,a)
# Turn LED on
print('LED On')
a.LED(100)
# Simulate a time delay
# Run time in minutes
run_time = 80.0
# Number of cycles
loops = int(60.0*run_time)
# Temperature (K)
t1sp = 45.0
t2sp = 35.0
#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc',remote=False)
m.time = np.linspace(0,400,41)
step = 10
T1 = np.ones(int(loops/step)+1) * a.T1 # temperature (degC)
T2 = np.ones(int(loops/step)+1) * a.T2 # temperature (degC)
Tsp1 = np.ones(int(loops/step)+1) * t1sp # set point (degC)
Tsp2 = np.ones(int(loops/step)+1) * t2sp # set point (degC)
# heater values
Q1s = np.ones(int(loops/step)+1) * 0.0
Q2s = np.ones(int(loops/step)+1) * 0.0
# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Q2_ss = m.Param(value=0)
TC2_ss = m.Param(value=a.T2)
Kp1 = m.Param(value= 0.7)
tau1 = m.Param(value=160.0)
Kp2 = m.Param(value=0.05)
tau2 = m.Param(value=160.0)
Kp3= m.Param(value=0.05)
tau3 = m.Param(value=160.0)
Kp4 = m.Param(value=0.4)
tau4 = m.Param(value=200.0)
sp1 = m.Param(value=a.T1)
sp2 = m.Param(value=a.T2)
# Manipulated variable
Q1 = m.MV(value=0, name='q1')
Q1.STATUS = 1 # use to control temperature
Q1.FSTATUS = 1 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 10.0
Q1.DCOST = 5.0
Q2 = m.MV(value=0, name='q2')
Q2.STATUS = 1 # use to control temperature
Q2.FSTATUS = 1 # no feedback measurement
Q2.LOWER = 0.0
Q2.UPPER = 100.0
Q2.DMAX = 10.0
Q2.DCOST = 5.0
# Controlled variable
TC1 = m.CV(value=a.T1, name='tc1')
TC1.STATUS = 1 # minimize error with setpoint range
TC1.FSTATUS = 1 # receive measurement
TC1.TR_INIT = 2 # reference trajectory
# TC1.COST = 0.1
TC1.WSPHI = 20
TC1.WSPLO = 20
TC1.TAU = 50 # time constant for response
#TC1.TR_OPEN = 3
TC2 = m.CV(value=a.T2, name='tc2')
TC2.STATUS = 1 # minimize error with setpoint range
TC2.FSTATUS = 1 # receive measurement
TC2.TR_INIT = 2 # reference trajectory
# TC2.COST = 0.1
TC2.WSPHI = 20
TC2.WSPLO = 20
TC2.TAU = 30 # time constant for response
#kTC2.TR_OPEN = 3
# 添加延时
Q1d=m.Var()
m.delay(Q1, Q1d, theta1)
Q2d=m.Var()
m.delay(Q2, Q2d, theta2)
# Equation
#m.Equation(tau1 * TC1.dt() + (TC1 - TC1_ss) == Kp1 * (Q1d - Q1_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp2 * (Q1d - Q1_ss))
# m.Equation(tau3 * TC1.dt() + (TC1 - TC1_ss) == Kp3 * (Q2d - Q2_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp4 * (Q2d - Q2_ss))
m.Equation(0.5 * (tau1 * TC1.dt() + (TC1 - TC1_ss) + tau3 * TC1.dt() + (TC1 - TC1_ss)) == Kp1 * (Q1d - Q1_ss) + Kp3 * (Q2d -Q2_ss))
m.Equation(0.5 * (tau2 * TC2.dt() + (TC2 - TC2_ss) + tau4 * TC2.dt() + (TC2 - TC2_ss)) == Kp4 * (Q2d - Q2_ss) + Kp2 * (Q1d - Q1_ss))
# Steady-state initializations
m.options.IMODE = 1
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
m.solve()
sp1.VALUE = 45
sp2.VALUE = 35
# Global Options
m.options.IMODE = 6 # MPC
m.options.CV_TYPE = 3 # Objective type
m.options.NODES = 2 # Collocation nodes
m.options.MAX_TIME = 10
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
#m.options.CV_WGT_START = 2*theta
#m.options.CV_WGT_SLOPE = theta
# m.options.MV_STEP_HOR = 5
##################################################################
# Create plot
plt.figure()
plt.ion()
plt.show()
# Main Loop
a.Q1(0)
a.Q2(0)
Q2s[0:] = 0
start_time = time.time()
tm = np.linspace(1,loops,int(loops/step)+1)
j=0
try:
time_start = time.time()
labtime_start = labtime.time()
if(not connected):
labtime.set_rate(10)
for i in tclab.clock(loops,adaptive=False):
i = int(i)
if(i == 0):
continue
print("-----------------------")
t_real = time.time() - time_start
t_lab = labtime.time() - labtime_start
print("real time = {0:4.1f} lab time = {1:4.1f} m.time = {1:4.1f}".format(t_real, t_lab,m.time))
#print("real time = {0:4.1f} m.time = {1:4.1f}".format(t_real, m.time))
if(i%step != 0):
continue
j = i/step
j = int(j)
print(j)
T1[j:] = a.T1
T2[j:] = a.T2
tm[j] = i
###############################
### MPC CONTROLLER ###
###############################
TC1.MEAS = T1[j]
TC2.MEAS = T2[j]
print("T1 meas:{0:4.1f} ".format(a.T1))
print("T2 meas:{0:4.1f} ".format(a.T2))
# input setpoint with deadband +/- DT
DT =0.5
TC1.SPHI = Tsp1[j] +DT
TC1.SPLO = Tsp1[j] -DT
TC2.SPHI = Tsp2[j] +DT
TC2.SPLO = Tsp2[j] -DT
try:
# stop model time to solve MPC in cast the solver takes too much time
if(not connected):
labtime.stop()
m.solve(disp=False)
#start model time
if(not connected):
labtime.start()
except Exception as e:
if(not connected):
if(not labtime.running):
labtime.start()
print("sovle's exception:")
print(e)
if(j != 0):
Q1s[j] = Q1s[j-1]
Q2s[j] = Q2s[j-1]
continue
# test for successful solution
if (m.options.APPSTATUS==1):
# retrieve the first Q value
Q1s[j:] = np.ones(len(Q1s)-j) * Q1.NEWVAL
Q2s[j:] = np.ones(len(Q2s)-j) * Q2.NEWVAL
#a.Q1(Q1.NEWVAL)
#a.Q2(Q2.NEWVAL)
print("Q1 applied with delay: {0:4.1f} ".format(Q1.NEWVAL))
print("Q2 applied with delay: {0:4.1f} ".format(Q2.NEWVAL))
with open(m.path+'//results.json') as f:
results = json.load(f)
else:
# not successful, set heater to zero
print("APPSTATUS is not 1,set Q to 0")
#Q1s[j] = 0
#Q2s[j] = 0
if i> 300 and i < 600:
Q1s[j] = Q1s[j] - 20
Q2s[j] = Q2s[j] - 20
if i>= 600:
Q1s[j] = Q1s[j] + 20
Q2s[j] = Q2s[j] + 20
Q1.meas= Q1s[j]
Q2.meas= Q2s[j]
tclab_delay.Q1_delay(Q1s[j])
tclab_delay.Q2_delay(Q2s[j])
print("calc:"+str(Q1s[j]))
print("calc:"+str(Q2s[j]))
#apply disturbance on 50s, 200s,
#if(i == 600):
# Q2s[j] = 100
#if(i == 1400):
# Q2s[j] = 0
#Q2s[j] = 20 - randrange(20)
#Q2s[j:] = np.ones(len(Q2s)-j) * Q2s[j]
#restore Q2 to 0
#if(i == 300):
#Q2s[j:] = 0
#a.Q2(Q2s[j])
#tclab_delay.Q2_delay(Q2s[j])
#take Q2 to FV
#Q2.MEAS = Q2s[j]
if(not connected):
labtime.stop()
# Plot
try:
plt.clf()
ax=plt.subplot(2,1,1)
ax.grid()
plt.plot(tm[0:j],T1[0:j],'ro',markersize=3,label=r'$T_1$')
plt.plot(tm[0:j],Tsp1[0:j],'r-',markersize=3,label=r'$T_1 Setpoint$')
plt.plot(tm[0:j],T2[0:j],'bo',markersize=3,label=r'$T_2$')
plt.plot(tm[0:j],Tsp2[0:j],'b-',markersize=3,label=r'$T_2 Setpoint$')
plt.plot(tm[j]+m.time,results['tc1.bcv'],'r-.',markersize=1,\
label=r'$T_1$ predicted',linewidth=1)
plt.plot(tm[j]+m.time,results['tc2.bcv'],'b-.',markersize=1,\
label=r'$T_2$ predicted',linewidth=1)
plt.plot(tm[j]+m.time,results['tc1.tr_hi'],'k--',\
label=r'$T_1$ trajectory')
plt.plot(tm[j]+m.time,results['tc1.tr_lo'],'k--')
plt.plot(tm[j]+m.time,results['tc2.tr_hi'],'k--',\
label=r'$T_2$ trajectory')
plt.plot(tm[j]+m.time,results['tc2.tr_lo'],'k--')
plt.ylabel('Temperature (degC)')
plt.legend(loc='best')
ax=plt.subplot(2,1,2)
ax.grid()
plt.plot(tm[0:j],Q1s[0:j],'r-',linewidth=3,label=r'$Q_1$')
plt.plot(tm[0:j],Q2s[0:j],'b-',linewidth=3,label=r'$Q_2$')
plt.plot(tm[j]+m.time,Q1.value,'r-.',\
label=r'$Q_1$ plan',linewidth=1)
plt.plot(tm[j]+m.time,Q2.value,'b-.',\
label=r'$Q_2$ plan',linewidth=1)
#plt.plot(tm[0:i],Q2s[0:i],'b:',LineWidth=3,label=r'$Q_2$')
plt.ylabel('Heaters')
plt.xlabel('Time (sec)')
plt.legend(loc='best')
plt.draw()
plt.pause(0.05)
if make_mp4:
filename='./figures/plot_'+str(j+10000)+'.png'
plt.savefig(filename)
except Exception as e:
print(e)
pass
if(not connected):
labtime.start()
# Turn off heaters
a.Q1(0)
a.Q2(0)
print('Shutting down')
input("Press Enter to continue...")
a.close()
# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Shutting down')
a.close()
if make_mp4:
images = []
iset = 0
for i in range(1,int(loops/step)+1):
filename='./figures/plot_'+str(i+10000)+'.png'
if os.path.exists(filename):
images.append(imageio.imread(filename))
if ((i+1)%350)==0:
imageio.mimsave('results_'+str(iset)+'.mp4', images)
iset += 1
images = []
if images!=[]:
imageio.mimsave('results_'+str(iset)+'.mp4', images)
# Make sure serial connection still closes when there's an error
except:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Error: Shutting down')
a.close()
raise
Regards
Tibalt
Is the FSTATUS also ON for the CVs such as t1.FSTATUS=1? If you update the measurement such as:
t1.MEAS = lab.T1
t2.MEAS = lab.T2
then this updates the BIAS for t1 and t2 (BIAS documentation). This should take care of any process / model mismatch that you are introducing by arbitrarily increasing or decreasing the heater by 20%. If t1.FSTATUS is OFF (0) then it is not able to compensate for the mismatch.
Another thing to try is to adjust the reference trajectory. The controller can appear sluggish if TAU is too high. Here is an example application with MPC and a linear model.
One additional way to compensate for the mismatch is to use Moving Horizon Estimation as shown here.
It looks like you have created a nice interface!
Response to Edit
Thanks for adding the code. The problem is that Q1.DMAX=10 and Q2.DMAX=10. When the Q1 and Q2 values are shifted up by 20 each cycle, the most that the controller can shift down is 20-10=10 so the controller appears that it is ramping in the wrong direction. Changing to DMAX=100 fixes the problem. There is still offset from the setpoint because the recommended Q1 and Q2 are shifted each cycle. The true recommended values are never implemented. Another thing to try is to impose an offset on the measured values such as TC1.MEAS = T1[j] + 20. The model bias will remove the offset in this case.
from random import random
from random import randrange
import tclab
from tclab import labtime
from tclab import TCLabModel
import numpy as np
import time
import matplotlib.pyplot as plt
from gekko import GEKKO
import json
from tclab import TCLabModel
make_gif = True
make_mp4 = True
if make_gif or make_mp4:
# pip install imageio-ffmpeg with imageio to make MP4
import imageio # required to make animation
import os
try:
os.mkdir('./figures')
except:
pass
class tclab_heaterpipe():
def __init__(self,d1,d2,model):
if(d1 >= 1 and d2 >=1):
self.delay_q1_step = int(d1)
self.delay_q2_step = int(d2)
self.q1_buffer = [0] * self.delay_q1_step
self.q2_buffer = [0] * self.delay_q2_step
self.m = model
else:
self.delay_q1_step =0
self.delay_q2_step =0
return
def Q1_delay(self,q1):
if(self.delay_q1_step == 0):
self.m.Q1(q1)
self.q1_buffer.insert(0,q1)
self.m.Q1(self.q1_buffer.pop())
def Q2_delay(self,q2):
if(self.delay_q2_step == 0):
self.m.Q1(q2)
self.q2_buffer.insert(0,q2)
self.m.Q2(self.q2_buffer.pop())
# Connect to Arduino
connected = False # switch to connected=True with physical hardware
theta1 = 1
theta2 = 1
T = tclab.setup(connected)
a = T()
tclab_delay = tclab_heaterpipe(theta1,theta2,a)
# Turn LED on
print('LED On')
a.LED(100)
# Simulate a time delay
# Run time in minutes
run_time = 20.0
# Number of cycles
loops = int(60.0*run_time)
# Temperature (K)
t1sp = 45.0
t2sp = 35.0
#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc',remote=False)
m.time = np.linspace(0,400,41)
step = 10
T1 = np.ones(int(loops/step)+1) * a.T1 # temperature (degC)
T2 = np.ones(int(loops/step)+1) * a.T2 # temperature (degC)
Tsp1 = np.ones(int(loops/step)+1) * t1sp # set point (degC)
Tsp2 = np.ones(int(loops/step)+1) * t2sp # set point (degC)
# heater values
Q1s = np.ones(int(loops/step)+1) * 0.0
Q2s = np.ones(int(loops/step)+1) * 0.0
# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Q2_ss = m.Param(value=0)
TC2_ss = m.Param(value=a.T2)
Kp1 = m.Param(value= 0.7)
tau1 = m.Param(value=160.0)
Kp2 = m.Param(value=0.05)
tau2 = m.Param(value=160.0)
Kp3= m.Param(value=0.05)
tau3 = m.Param(value=160.0)
Kp4 = m.Param(value=0.4)
tau4 = m.Param(value=200.0)
sp1 = m.Param(value=a.T1)
sp2 = m.Param(value=a.T2)
# Manipulated variable
Q1 = m.MV(value=0, name='q1')
Q1.STATUS = 1 # use to control temperature
Q1.FSTATUS = 1 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 100.0
Q1.DCOST = 1e-3
Q2 = m.MV(value=0, name='q2')
Q2.STATUS = 1 # use to control temperature
Q2.FSTATUS = 1 # no feedback measurement
Q2.LOWER = 0.0
Q2.UPPER = 100.0
Q2.DMAX = 100.0
Q2.DCOST = 1e-3
# Controlled variable
TC1 = m.CV(value=a.T1, name='tc1')
TC1.STATUS = 1 # minimize error with setpoint range
TC1.FSTATUS = 1 # receive measurement
TC1.TR_INIT = 2 # reference trajectory
# TC1.COST = 0.1
TC1.WSPHI = 20
TC1.WSPLO = 20
TC1.TAU = 50 # time constant for response
#TC1.TR_OPEN = 3
TC2 = m.CV(value=a.T2, name='tc2')
TC2.STATUS = 1 # minimize error with setpoint range
TC2.FSTATUS = 1 # receive measurement
TC2.TR_INIT = 2 # reference trajectory
# TC2.COST = 0.1
TC2.WSPHI = 20
TC2.WSPLO = 20
TC2.TAU = 30 # time constant for response
#kTC2.TR_OPEN = 3
# 添加延时
Q1d=m.Var()
m.delay(Q1, Q1d, theta1)
Q2d=m.Var()
m.delay(Q2, Q2d, theta2)
# Equation
#m.Equation(tau1 * TC1.dt() + (TC1 - TC1_ss) == Kp1 * (Q1d - Q1_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp2 * (Q1d - Q1_ss))
# m.Equation(tau3 * TC1.dt() + (TC1 - TC1_ss) == Kp3 * (Q2d - Q2_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp4 * (Q2d - Q2_ss))
m.Equation(0.5 * (tau1 * TC1.dt() + (TC1 - TC1_ss) + tau3 * TC1.dt() + (TC1 - TC1_ss)) == Kp1 * (Q1d - Q1_ss) + Kp3 * (Q2d -Q2_ss))
m.Equation(0.5 * (tau2 * TC2.dt() + (TC2 - TC2_ss) + tau4 * TC2.dt() + (TC2 - TC2_ss)) == Kp4 * (Q2d - Q2_ss) + Kp2 * (Q1d - Q1_ss))
# Steady-state initializations
m.options.IMODE = 1
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
m.solve()
sp1.VALUE = 45
sp2.VALUE = 35
# Global Options
m.options.IMODE = 6 # MPC
m.options.CV_TYPE = 3 # Objective type
m.options.NODES = 2 # Collocation nodes
m.options.MAX_TIME = 10
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
#m.options.CV_WGT_START = 2*theta
#m.options.CV_WGT_SLOPE = theta
# m.options.MV_STEP_HOR = 5
##################################################################
# Create plot
plt.figure(figsize=(12,8))
plt.ion()
plt.show()
# Main Loop
a.Q1(0)
a.Q2(0)
Q2s[0:] = 0
start_time = time.time()
tm = np.linspace(1,loops,int(loops/step)+1)
j=0
try:
time_start = time.time()
labtime_start = labtime.time()
if(not connected):
labtime.set_rate(10)
for i in tclab.clock(loops,adaptive=False):
i = int(i)
if(i == 0):
continue
print("-----------------------")
t_real = time.time() - time_start
t_lab = labtime.time() - labtime_start
print("real time = {0:4.1f} lab time = {1:4.1f} m.time = {1:4.1f}".format(t_real, t_lab,m.time))
#print("real time = {0:4.1f} m.time = {1:4.1f}".format(t_real, m.time))
if(i%step != 0):
continue
j = i/step
j = int(j)
print(j)
T1[j:] = a.T1
T2[j:] = a.T2
tm[j] = i
###############################
### MPC CONTROLLER ###
###############################
TC1.MEAS = T1[j]
TC2.MEAS = T2[j]
print("T1 meas:{0:4.1f} ".format(a.T1))
print("T2 meas:{0:4.1f} ".format(a.T2))
# input setpoint with deadband +/- DT
DT =0.5
TC1.SPHI = Tsp1[j] +DT
TC1.SPLO = Tsp1[j] -DT
TC2.SPHI = Tsp2[j] +DT
TC2.SPLO = Tsp2[j] -DT
try:
# stop model time to solve MPC in cast the solver takes too much time
if(not connected):
labtime.stop()
m.solve(disp=False)
#start model time
if(not connected):
labtime.start()
except Exception as e:
if(not connected):
if(not labtime.running):
labtime.start()
print("sovle's exception:")
print(e)
if(j != 0):
Q1s[j] = Q1s[j-1]
Q2s[j] = Q2s[j-1]
continue
# test for successful solution
if (m.options.APPSTATUS==1):
# retrieve the first Q value
Q1s[j:] = np.ones(len(Q1s)-j) * Q1.NEWVAL
Q2s[j:] = np.ones(len(Q2s)-j) * Q2.NEWVAL
#a.Q1(Q1.NEWVAL)
#a.Q2(Q2.NEWVAL)
print("Q1 applied with delay: {0:4.1f} ".format(Q1.NEWVAL))
print("Q2 applied with delay: {0:4.1f} ".format(Q2.NEWVAL))
with open(m.path+'//results.json') as f:
results = json.load(f)
else:
# not successful, set heater to zero
print("APPSTATUS is not 1,set Q to 0")
#Q1s[j] = 0
#Q2s[j] = 0
if i> 300 and i < 600:
Q1s[j] = max(0,Q1s[j] - 20)
Q2s[j] = max(0,Q2s[j] - 20)
if i>= 600:
Q1s[j] = min(100,Q1s[j] + 20)
Q2s[j] = min(100,Q2s[j] + 20)
Q1.meas= Q1s[j]
Q2.meas= Q2s[j]
tclab_delay.Q1_delay(Q1s[j])
tclab_delay.Q2_delay(Q2s[j])
print("calc:"+str(Q1s[j]))
print("calc:"+str(Q2s[j]))
if(not connected):
labtime.stop()
# Plot
try:
plt.clf()
ax=plt.subplot(2,1,1)
ax.grid()
plt.plot(tm[0:j],T1[0:j],'ro',markersize=3,label=r'$T_1$')
plt.plot(tm[0:j],Tsp1[0:j],'r-',markersize=3,label=r'$T_1 Setpoint$')
plt.plot(tm[0:j],T2[0:j],'bo',markersize=3,label=r'$T_2$')
plt.plot(tm[0:j],Tsp2[0:j],'b-',markersize=3,label=r'$T_2 Setpoint$')
plt.plot(tm[j]+m.time,results['tc1.bcv'],'r-.',markersize=1,\
label=r'$T_1$ predicted',linewidth=1)
plt.plot(tm[j]+m.time,results['tc2.bcv'],'b-.',markersize=1,\
label=r'$T_2$ predicted',linewidth=1)
plt.plot(tm[j]+m.time,results['tc1.tr_hi'],'k--',\
label=r'$T_1$ trajectory')
plt.plot(tm[j]+m.time,results['tc1.tr_lo'],'k--')
plt.plot(tm[j]+m.time,results['tc2.tr_hi'],'k--',\
label=r'$T_2$ trajectory')
plt.plot(tm[j]+m.time,results['tc2.tr_lo'],'k--')
plt.ylabel('Temperature (degC)')
plt.legend(loc=1)
ax=plt.subplot(2,1,2)
ax.grid()
plt.plot(tm[0:j],Q1s[0:j],'r-',linewidth=3,label=r'$Q_1$')
plt.plot(tm[0:j],Q2s[0:j],'b-',linewidth=3,label=r'$Q_2$')
plt.plot(tm[j]+m.time,Q1.value,'r-.',\
label=r'$Q_1$ plan',linewidth=1)
plt.plot(tm[j]+m.time,Q2.value,'b-.',\
label=r'$Q_2$ plan',linewidth=1)
#plt.plot(tm[0:i],Q2s[0:i],'b:',LineWidth=3,label=r'$Q_2$')
plt.ylabel('Heaters')
plt.xlabel('Time (sec)')
plt.legend(loc=1)
plt.draw()
plt.pause(0.05)
if make_mp4:
filename='./figures/plot_'+str(j+10000)+'.png'
plt.savefig(filename)
except Exception as e:
print(e)
pass
if(not connected):
labtime.start()
# Turn off heaters
a.Q1(0)
a.Q2(0)
print('Shutting down')
input("Press Enter to continue...")
a.close()
# make gif
if make_gif:
images = []
iset = 0
for i in range(1,int(loops/step)+1):
filename='./figures/plot_'+str(i+10000)+'.png'
if os.path.exists(filename):
images.append(imageio.imread(filename))
if ((i+1)%350)==0:
imageio.mimsave('results_'+str(iset)+'.gif', images)
iset += 1
images = []
if images!=[]:
imageio.mimsave('results_'+str(iset)+'.gif', images)
if make_mp4:
images = []
iset = 0
for i in range(1,int(loops/step)+1):
filename='./figures/plot_'+str(i+10000)+'.png'
if os.path.exists(filename):
images.append(imageio.imread(filename))
if ((i+1)%350)==0:
imageio.mimsave('results_'+str(iset)+'.gif', images)
iset += 1
images = []
if images!=[]:
imageio.mimsave('results_'+str(iset)+'.gif', images)
# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Shutting down')
a.close()
if make_gif:
images = []
iset = 0
for i in range(1,int(loops/step)+1):
filename='./figures/plot_'+str(i+10000)+'.png'
if os.path.exists(filename):
images.append(imageio.imread(filename))
if ((i+1)%350)==0:
imageio.mimsave('results_'+str(iset)+'.gif', images)
iset += 1
images = []
if images!=[]:
imageio.mimsave('results_'+str(iset)+'.gif', images)
if make_mp4:
images = []
iset = 0
for i in range(1,int(loops/step)+1):
filename='./figures/plot_'+str(i+10000)+'.png'
if os.path.exists(filename):
images.append(imageio.imread(filename))
if ((i+1)%350)==0:
imageio.mimsave('results_'+str(iset)+'.mp4', images)
iset += 1
images = []
if images!=[]:
imageio.mimsave('results_'+str(iset)+'.mp4', images)
# Make sure serial connection still closes when there's an error
except:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Error: Shutting down')
a.close()
raise
I am trying to implement a lateral controller for an autonomous vehicle defined by a lateral dynamic model.Well, my problem is that the CVs don't reach the desired reference or target point set by SP. I am using the following equations of motion and objective function. I am using a semi empirical formula (pacejka) to calculate tire forces donated by Fyf Fyr. Here are the equations of motion and objective function. Thanks in advance.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
import time
import math
#%% NMPC model
T = 5
nt = 51
m = GEKKO(remote=False)
m.time = np.linspace(0,T,nt)
#Model Parameters
X_speed = m.Param(value=10.0)
mass=m.Param(value=1611.0)
c=m.Param(value=1.351)
b=m.Param(value=1.5242)
Iz=m.Param(value=3048.1)
Cyf=m.Param(value=1.30)
Dyf=m.Param(value=3449.94238709)
Byf=m.Param(value=0.223771457713)
Eyf=m.Param(value=-0.6077272729)
Cyr=m.Param(value=1.30)
Dyr=m.Param(value=3846.47835351)
Byr=m.Param(value=0.207969093485)
Eyr=m.Param(value=-0.7755647971)
#Variables
slip_angle_front_tire = m.Var(value=0.0, lb=-10.0, ub=14.0 )
slip_angle_rear_tire = m.Var(value=0.0, lb=-10.0, ub=14.0 )
phi_f = m.Var(value=0.0)
phi_r = m.Var(value=0.0)
maxF = 5000
Ffy = m.Var(value=0.0, lb=-.0*maxF, ub=maxF )
Fry = m.Var(value=0.0, lb=-1.0*maxF, ub=maxF )
xpos = m.Var(value=0.0)
dy = m.Var(value=0.0)
dpsi = m.Var(value=0.0)
#MV
steering = m.MV(value=0, lb=-0.40, ub=0.40 )
#CV
ypos = m.CV(value=0.0 ,lb =-200.0,ub=200.0 )
psipos = m.CV(value=0.0,lb=-3.5,ub=3.5)
#Equations
m.Equation(ypos.dt() == dy)
m.Equation(psipos.dt() == dpsi)
m.Equation(slip_angle_front_tire == steering - m.atan( (dy+b*dpsi)/X_speed ) )
m.Equation(slip_angle_rear_tire == -1.0*m.atan( (dy-c*dpsi) / X_speed))
m.Equation(phi_f == (1-Eyf)*(slip_angle_front_tire) + (Eyf/Byf)*(m.atan(Byf*slip_angle_front_tire) ) )
m.Equation(phi_r == (1-Eyr)*(slip_angle_rear_tire) + (Eyr/Byr)*(m.atan(Byr*slip_angle_rear_tire) ) )
m.Equation(Ffy == (Dyf*( m.sin(Cyf*m.atan(Byf*phi_f ) ) ) ) *2.0 )
m.Equation(Fry == (Dyr*( m.sin(Cyr*m.atan(Byr*phi_r ) ) ) ) *2.0 )
m.Equation(mass*dy.dt() == (Ffy*m.cos(steering) ) + (Fry) - (X_speed*dpsi*mass) )
m.Equation(dpsi.dt()*Iz == ( b*Ffy*m.cos(steering) ) - ( c*Fry) )
#Global options
m.options.IMODE = 6 #MPC
m.options.CV_TYPE = 2
m.options.MV_TYPE = 0
#MV tuning
steering.STATUS = 1
steering.DCOST = 0.01
#CV Tuning
ypos.STATUS = 1
psipos.STATUS = 1
ypos.TR_INIT = 2
psipos.TR_INIT = 2
ypos.WSP = 100
psipos.WSP = 10
ypos.SP = 9.2
psipos.SP = 1.5
print('Solver starts ...')
t = time.time()
m.solve(disp=True)
print('Solver took ', time.time() - t, 'seconds')
plt.figure()
plt.subplot(4,1,1)
plt.plot(m.time,steering.value,'b-',LineWidth=2)
plt.ylabel('steering wheel')
plt.subplot(4,1,2)
plt.plot(m.time,ypos.value,'r--',LineWidth=2)
plt.ylabel('y-point')
plt.subplot(4,1,3)
plt.plot(m.time,psipos.value,'r--',LineWidth=2)
plt.ylabel('yaw angle')
plt.xlabel('time')
plt.show()
For a reference trajectory, you need to include the time constant TAU for how fast to reach the setpoint.
ypos.TAU = 1.5
psipos.TAU = 1.5
There is additional information on tuning an MPC application in the Dynamic Optimization exercises.
One other correction that you need is the -1.0 in Ffy = m.Var(value=0.0, lb=-1.0*maxF, ub=maxF). Otherwise, it can never reach the setpoint. It appears that both setpoints cannot be reached so it preferentially tries to meet the ypos setpoint that has a higher weight. You may need another MV to control both ypos and psipos. Otherwise, you may consider opening the steering bounds to see if it can find a better solution with fewer restrictions. I also set the end time to 10 with 101 points because it needed additional time to stabilize to the new setpoint.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
import time
import math
#%% NMPC model
T = 10
nt = 101
m = GEKKO(remote=False)
m.time = np.linspace(0,T,nt)
#Model Parameters
X_speed = m.Param(value=10.0)
mass=m.Param(value=1611.0)
c=m.Param(value=1.351)
b=m.Param(value=1.5242)
Iz=m.Param(value=3048.1)
Cyf=m.Param(value=1.30)
Dyf=m.Param(value=3449.94238709)
Byf=m.Param(value=0.223771457713)
Eyf=m.Param(value=-0.6077272729)
Cyr=m.Param(value=1.30)
Dyr=m.Param(value=3846.47835351)
Byr=m.Param(value=0.207969093485)
Eyr=m.Param(value=-0.7755647971)
#Variables
slip_angle_front_tire = m.Var(value=0.0, lb=-10.0, ub=14.0 )
slip_angle_rear_tire = m.Var(value=0.0, lb=-10.0, ub=14.0 )
phi_f = m.Var(value=0.0)
phi_r = m.Var(value=0.0)
maxF = 5000
Ffy = m.Var(value=0.0, lb=-1.0*maxF, ub=maxF )
Fry = m.Var(value=0.0, lb=-1.0*maxF, ub=maxF )
xpos = m.Var(value=0.0)
dy = m.Var(value=0.0)
dpsi = m.Var(value=0.0)
#MV
steering = m.MV(value=0, lb=-0.4, ub=0.4 )
#CV
ypos = m.CV(value=0.0 ,lb =-200.0,ub=200.0 )
psipos = m.CV(value=0.0,lb=-3.5,ub=3.5)
#Equations
m.Equation(ypos.dt() == dy)
m.Equation(psipos.dt() == dpsi)
m.Equation(slip_angle_front_tire == steering - m.atan( (dy+b*dpsi)/X_speed ) )
m.Equation(slip_angle_rear_tire == -1.0*m.atan( (dy-c*dpsi) / X_speed))
m.Equation(phi_f == (1-Eyf)*(slip_angle_front_tire) + (Eyf/Byf)*(m.atan(Byf*slip_angle_front_tire) ) )
m.Equation(phi_r == (1-Eyr)*(slip_angle_rear_tire) + (Eyr/Byr)*(m.atan(Byr*slip_angle_rear_tire) ) )
m.Equation(Ffy == (Dyf*( m.sin(Cyf*m.atan(Byf*phi_f ) ) ) ) *2.0 )
m.Equation(Fry == (Dyr*( m.sin(Cyr*m.atan(Byr*phi_r ) ) ) ) *2.0 )
m.Equation(mass*dy.dt() == (Ffy*m.cos(steering) ) + (Fry) - (X_speed*dpsi*mass) )
m.Equation(dpsi.dt()*Iz == ( b*Ffy*m.cos(steering) ) - ( c*Fry) )
#Global options
m.options.IMODE = 6 #MPC
m.options.CV_TYPE = 2
m.options.MV_TYPE = 1
#MV tuning
steering.STATUS = 1
steering.DCOST = 0.1
#CV Tuning
ypos.STATUS = 1
psipos.STATUS = 1
ypos.TR_INIT = 2
psipos.TR_INIT = 2
ypos.WSP = 100
psipos.WSP = 10
ypos.SP = 9.2
psipos.SP = 1.5
ypos.TAU = 1.5
psipos.TAU = 1.5
print('Solver starts ...')
t = time.time()
m.solve(disp=True)
print('Solver took ', time.time() - t, 'seconds')
plt.figure()
plt.subplot(3,1,1)
plt.plot(m.time,steering.value,'b-',LineWidth=2)
plt.ylabel('steering wheel')
plt.subplot(3,1,2)
plt.plot([0,10],[9.2,9.2],'k-')
plt.plot(m.time,ypos.value,'r--',LineWidth=2)
plt.ylabel('y-point')
plt.subplot(3,1,3)
plt.plot([0,10],[1.5,1.5],'k-')
plt.plot(m.time,psipos.value,'g:',LineWidth=2)
plt.ylabel('yaw angle')
plt.xlabel('time')
plt.show()
I defined a custom measure that allows to transform the prediction$data with an external function before evaluating standard measures such as rmse. If I try to tune params without parallelization everything goes smooth but if I start a parallelized session it seems not to find the external function anymore, although it's declared in the global environment.
library(compiler)
library(mlr)
library(parallelMap)
library(parallel)
# define function
inverse_fun = function(x){x^2}
inverse_fun = Vectorize(inverse_fun)
inverse_fun = cmpfun(inverse_fun, options=list(suppressUndefined=T))
assign('inverse_fun', inverse_fun, envir = .GlobalEnv)
tuning_criterion = 'rmse'
# define a new measure that applies inverse_fun to prediction and evaluates rmse
original_measure = eval(parse(text=tuning_criterion))
transf_measure_fun = function(task, model, pred, feats, extra.args){
# transform back to original value
pred$data$truth = inverse_fun(pred$data$truth)
pred$data$response = inverse_fun(pred$data$response)
return(original_measure$fun(task, model, pred, feats, extra.args))
}
transf_measure = makeMeasure(
id = 'ii', name = 'ccc',
properties = original_measure$properties,
minimize = original_measure$minimize, best = original_measure$best, worst = original_measure$worst,
fun = transf_measure_fun)
transf_measure = setAggregation(transf_measure, original_measure$aggr)
aggregated_measure = list(transf_measure, setAggregation(transf_measure, test.sd), setAggregation(transf_measure, train.mean), setAggregation(transf_measure, train.sd))
# train and predict
lrn.lm = makeLearner("regr.ksvm")
mod.lm = train(lrn.lm, bh.task)
task.pred.lm = predict(mod.lm, task = bh.task)
# inverse function on prediction
inv_pred = task.pred.lm
inv_pred$data$truth = inverse_fun(inv_pred$data$truth)
inv_pred$data$response = inverse_fun(inv_pred$data$response)
# check for performance match
performance(task.pred.lm, transf_measure)
performance(inv_pred, rmse)
# tuning
discrete_ps = makeParamSet(
makeDiscreteParam("C", values = c(0.5, 1.0, 1.5, 2.0)),
makeDiscreteParam("sigma", values = c(0.5, 1.0, 1.5, 2.0))
)
ctrl = makeTuneControlGrid()
rdesc = makeResampleDesc("CV", iters = 3L)
# this works
res = tuneParams(lrn.lm, task = bh.task, resampling = rdesc,
par.set = discrete_ps, control = ctrl, measures = transf_measure)
# try with parallelization - doesn't work
current_os = Sys.info()[['sysname']] # detect OS
if (current_os == "Windows"){
set.seed(1, "L'Ecuyer-CMRG")
parallelStart(mode = "socket", cpus = detectCores(), show.info = F)
parallel::clusterSetRNGStream(iseed = 1)
} else if (current_os == "Linux"){
set.seed(1, "L'Ecuyer-CMRG")
parallelStart(mode = "multicore", cpus = detectCores(), show.info = F)
} else {
cat('\n\n#### OS not recognized, check parallelization init\n\n')
}
res = tuneParams(lrn.lm, task = bh.task, resampling = rdesc,
par.set = discrete_ps, control = ctrl, measures = transf_measure)
parallelStop()
getting the following error:
Error in stopWithJobErrorMessages(inds, vcapply(result.list[inds], as.character)) :
Errors occurred in 16 slave jobs, displaying at most 10 of them:
00001: Error in inverse_fun(pred$data$truth) :
cannot find "inverse_fun"
I tried to pass the function with extra.args but I get an error
original_measure = eval(parse(text=tuning_criterion))
transf_measure_fun = function(task, model, pred, feats, extra.args){
# transform back to original value
pred$data$truth = extra.args$inv_fun(pred$data$truth)
pred$data$response = extra.args$inv_fun(pred$data$response)
return(original_measure$fun(task, model, pred, feats, extra.args))
}
transf_measure = makeMeasure(
id = 'ii', name = 'ccc',
properties = original_measure$properties,
minimize = original_measure$minimize, best = original_measure$best, worst = original_measure$worst,
fun = transf_measure_fun(extra.args = list(inv_fun = inverse_fun))
)
and I get
Error in FUN(X[[i]], ...) : argument "pred" is missing, with no default
Thanks in advance
You need to export your custom objects using parallelMap::parallelExport().
library(mlr)
#> Loading required package: ParamHelpers
library(parallelMap)
library(compiler)
# define function
inverse_fun = function(x){x^2}
inverse_fun = Vectorize(inverse_fun)
inverse_fun = cmpfun(inverse_fun, options=list(suppressUndefined=T))
assign('inverse_fun', inverse_fun, envir = .GlobalEnv)
tuning_criterion = 'rmse'
# define a new measure that applies inverse_fun to prediction and evaluates rmse
original_measure = eval(parse(text=tuning_criterion))
transf_measure_fun = function(task, model, pred, feats, extra.args){
# transform back to original value
pred$data$truth = inverse_fun(pred$data$truth)
pred$data$response = inverse_fun(pred$data$response)
return(original_measure$fun(task, model, pred, feats, extra.args))
}
transf_measure = makeMeasure(
id = 'ii', name = 'ccc',
properties = original_measure$properties,
minimize = original_measure$minimize, best = original_measure$best, worst = original_measure$worst,
fun = transf_measure_fun)
transf_measure = setAggregation(transf_measure, original_measure$aggr)
# tuning
discrete_ps = makeParamSet(
makeDiscreteParam("C", values = c(0.5, 1.0, 1.5, 2.0)),
makeDiscreteParam("sigma", values = c(0.5, 1.0, 1.5, 2.0))
)
ctrl = makeTuneControlGrid()
rdesc = makeResampleDesc("CV", iters = 3L)
lrn.lm = makeLearner("regr.ksvm")
set.seed(1, "L'Ecuyer-CMRG")
parallelStart(mode = "socket", cpus = 2, show.info = F)
parallelExport("inverse_fun", "original_measure")
res = tuneParams(lrn.lm, task = bh.task, resampling = rdesc,
par.set = discrete_ps, control = ctrl, measures = transf_measure)
#> [Tune] Started tuning learner regr.ksvm for parameter set:
#> Type len Def Constr Req Tunable Trafo
#> C discrete - - 0.5,1,1.5,2 - TRUE -
#> sigma discrete - - 0.5,1,1.5,2 - TRUE -
#> With control class: TuneControlGrid
#> Imputation value: Inf
#> [Tune] Result: C=2; sigma=0.5 : ii.test.rmse=270.8008465
parallelStop()
Created on 2019-10-08 by the reprex package (v0.3.0)
Session info
devtools::session_info()
#> ─ Session info ──────────────────────────────────────────────────────────
#> setting value
#> version R version 3.6.1 (2019-07-05)
#> os Arch Linux
#> system x86_64, linux-gnu
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz Europe/Berlin
#> date 2019-10-08
#>
#> ─ Packages ──────────────────────────────────────────────────────────────
#> ! package * version date lib
#> assertthat 0.2.1 2019-03-21 [1]
#> backports 1.1.5 2019-10-02 [1]
#> BBmisc 1.11 2017-03-10 [1]
#> callr 3.3.2 2019-09-22 [1]
#> checkmate 1.9.4 2019-07-04 [1]
#> cli 1.1.0 2019-03-19 [1]
#> colorspace 1.4-1 2019-03-18 [1]
#> crayon 1.3.4 2017-09-16 [1]
#> data.table 1.12.4 2019-10-03 [1]
#> desc 1.2.0 2018-05-01 [1]
#> devtools 2.2.1 2019-09-24 [1]
#> digest 0.6.21 2019-09-20 [1]
#> dplyr 0.8.3 2019-07-04 [1]
#> ellipsis 0.3.0 2019-09-20 [1]
#> evaluate 0.14 2019-05-28 [1]
#> fastmatch 1.1-0 2017-01-28 [1]
#> fs 1.3.1 2019-05-06 [1]
#> ggplot2 3.2.1 2019-08-10 [1]
#> glue 1.3.1 2019-03-12 [1]
#> gtable 0.3.0 2019-03-25 [1]
#> highr 0.8 2019-03-20 [1]
#> htmltools 0.4.0 2019-10-04 [1]
#> kernlab 0.9-27 2018-08-10 [1]
#> knitr 1.25 2019-09-18 [1]
#> lattice 0.20-38 2018-11-04 [1]
#> lazyeval 0.2.2 2019-03-15 [1]
#> magrittr 1.5 2014-11-22 [1]
#> Matrix 1.2-17 2019-03-22 [1]
#> memoise 1.1.0 2017-04-21 [1]
#> mlr * 2.15.0.9000 2019-10-08 [1]
#> munsell 0.5.0 2018-06-12 [1]
#> parallelMap * 1.4 2019-05-17 [1]
#> ParamHelpers * 1.12 2019-01-18 [1]
#> pillar 1.4.2 2019-06-29 [1]
#> pkgbuild 1.0.5 2019-08-26 [1]
#> pkgconfig 2.0.3 2019-09-22 [1]
#> pkgload 1.0.2 2018-10-29 [1]
#> prettyunits 1.0.2 2015-07-13 [1]
#> processx 3.4.1 2019-07-18 [1]
#> ps 1.3.0 2018-12-21 [1]
#> purrr 0.3.2 2019-03-15 [1]
#> R6 2.4.0 2019-02-14 [1]
#> Rcpp 1.0.2 2019-07-25 [1]
#> remotes 2.1.0 2019-06-24 [1]
#> rlang 0.4.0 2019-06-25 [1]
#> rmarkdown 1.16 2019-10-01 [1]
#> rprojroot 1.3-2 2018-01-03 [1]
#> scales 1.0.0 2018-08-09 [1]
#> sessioninfo 1.1.1 2018-11-05 [1]
#> stringi 1.4.3 2019-03-12 [1]
#> stringr 1.4.0 2019-02-10 [1]
#> R survival 2.44-1.1 <NA> [2]
#> testthat 2.2.1 2019-07-25 [1]
#> tibble 2.1.3 2019-06-06 [1]
#> tidyselect 0.2.5 2018-10-11 [1]
#> usethis 1.5.1.9000 2019-10-07 [1]
#> withr 2.1.2 2018-03-15 [1]
#> xfun 0.10 2019-10-01 [1]
#> XML 3.98-1.20 2019-06-06 [1]
#> yaml 2.2.0 2018-07-25 [1]
#> source
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> local
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> <NA>
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#> Github (r-lib/usethis#3015465)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.1)
#> CRAN (R 3.6.0)
#> CRAN (R 3.6.0)
#>
#> [1] /home/pjs/R/x86_64-pc-linux-gnu-library/3.6
#> [2] /usr/lib/R/library
#>
#> R ── Package was removed from disk.