我正在尝试使用scipy中的ODEINT和MINIMIZE在时域中进行参数估计。 我尝试了2种整合策略。首先,在单个时间步内调用ODEINT,然后在整个持续时间内调用ODEINT。
集成函数时,结果匹配。
优化时,结果会有所不同。
代码和结果如下:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from scipy import optimize
from scipy.interpolate import interp1d
def FOPDTmodel(output_value, t, params, t_vector, input_vector, output0):
"""First order model with time delay. Also called
First order plus dead time (FOPDT)
Args:
output_value (float): the output (see equation above) for the step (delta t)
t (float): time value to be used to calculate dy/dt
params (list): parameters of the model:
params[0] (float): Kp - gain
params[1] (float): tau - time constant
params[2] (float): theta - dead time
t_vector (np.array of float): time vector
input_vector (np.array of float): the input vector matching the time vector
output0 (float): initial value for the output at time zero
Returns:
float: dy/dy calculated for the conditions
"""
#ideas from: https://apmonitor.com/pdc/index.php/Main/FirstOrderOptimization
Kp = params[0]
tau = params[1]
theta = params[2]
input0 = input_vector[0]
input_interp = interp1d(t_vector, input_vector)
if ((t-theta)<= 0) or (t<0.0):
input_value = input_interp(0.0)
elif ((t-theta)> t_vector[-1]) or (t>t_vector[-1]):
input_value = input_vector[-1]
else:
input_value = input_interp(t-theta)
dydt = (-(output_value-output0) + Kp * (input_value-input0))/tau
return dydt
def integrate_model(model, step_output_value, params, t_vector, input_vector, output_vector):
"""Integrator function - this function's intent is to return the integral of dy/dt
over the whole time vector, using "odeint" over ONE time step at a time.
Args:
model (function object): the function that returns dy/dt (the model)
step_output_value (float): the output for the first step
params (list): parameters of the model:
params[0] (float): Kp - gain
params[1] (float): tau - time constant
params[2] (float): theta - dead time
t_vector (np.array of float): time vector
input_vector (np.array of float): the input vector matching the time vector
output0 (float): initial value for the output at time zero
Returns:
np.array of floats: for each time in time vector, return the model output, integrated
"""
# define the collector for each integration step
model_output = np.zeros(len(t_vector))
for i in range(len(t_vector)-1):
ts = [t_vector[i], t_vector[i+1]]
step_args_list = [list(params), t_vector, input_vector, output_vector[0]]
step_args = tuple(step_args_list)
step_output = odeint(model, step_output_value, ts, args=step_args)
model_output[i+1] = step_output[1]
step_output_value = step_output[1]
return model_output
def integrate_model2(model, step_output_value, params, t_vector, input_vector, output_vector):
"""Same idea, but now "odeint" integrates all interval by itself
"""
step_args_list = [list(params), t_vector, input_vector, output_vector[0]]
step_args = tuple(step_args_list)
model_output = odeint(model, step_output_value, t_vector, args=step_args)
return model_output
# unit test
# lets try a 3-2-1-1 input
input_shape = [2,3,2,1,1,5]
input_amplitudes = [0,1,-1,1,-1,0]
time_step = 0.1
u = 0
previous_t = 0
current_t = 0
# generate the input vector
for i in range(len(input_shape)):
current_t = current_t + input_shape[i]
u = np.append(u, np.ones((int((current_t-previous_t)/time_step)))*input_amplitudes[i])
previous_t = previous_t + input_shape[i]
# we need a time vector of the same length
t_vector = np.linspace(0, sum(input_shape), len(u))
print('time vector length: {}, input vector length: {}'.format(len(t_vector), len(u)))
input_vector = u
output_vector = u*2 #for now
Kp_model = 2
tau_model = 1
theta_model = 1
params = [Kp_model, tau_model, theta_model]
init_cond = output_vector[0]
sim_data = integrate_model(FOPDTmodel, init_cond, params, t_vector, input_vector, output_vector)
sim_data2 = integrate_model2(FOPDTmodel, init_cond, params, t_vector, input_vector, output_vector)
plt.figure(6)
plt.plot(t_vector,sim_data,'r--', label='Output')
plt.plot(t_vector,sim_data2,'g--', label='Output2')
plt.plot(t_vector, input_vector, 'b-', label='Input')
plt.legend(loc='best')
plt.show()
# Cost function definition: squares of the errors
def lq_functional(est_param, model, init_condition, t_vector, input_vector, output_vector):
"""Cost function - returns the squared of the differences between model and measured output data
Args:
est_params (list): parameters of the model:
params[0] (float): Kp - gain
params[1] (float): tau - time constant
params[2] (float): theta - dead time
model (function object): the function that returns dy/dt (the model)
init_condition (float): the output for the first step (initial output at time zero)
t_vector (np.array of float): time vector
input_vector (np.array of float): the input vector matching the time vector
output_vector (np.array of float): measured output vector
Returns:
float: sum of the squared difference sum(model - measured output)**2
"""
Kp_est = est_param[0]
tau_est = est_param[1]
theta_est = est_param[2]
params = [Kp_est, tau_est, theta_est]
calc_output = integrate_model(model, init_condition, params, t_vector, input_vector, output_vector)
err = 0.0
for i in range(len(t_vector)-1):
err = err + (calc_output[i] - output_vector[i])**2
return err
def lq_functional2(est_param, model, init_condition, t_vector, input_vector, output_vector):
"""Same thing, but using integrate_model2
"""
Kp_est = est_param[0]
tau_est = est_param[1]
theta_est = est_param[2]
params = [Kp_est, tau_est, theta_est]
calc_output = integrate_model2(model, init_condition, params, t_vector, input_vector, output_vector)
err = 0.0
for i in range(len(t_vector)-1):
err = err + (calc_output[i] - output_vector[i])**2
return float(err)
# unit test - cost function
# so now I can 'guess' some values and calculate the error
Kp_est = 2
tau_est = 1
theta_est = 0
init_condition = sim_data[0]
est_param = [Kp_est, tau_est, theta_est]
cost = lq_functional(est_param, FOPDTmodel, init_condition, t_vector, input_vector, sim_data)
print('Calulated cost function, no noise: {}'.format(cost))
# Add noise to data so that we can check if we can identify the parameters
sim_data_corrupted = np.random.normal(sim_data,0.5)
cost = lq_functional(est_param, FOPDTmodel, init_condition, t_vector, input_vector, sim_data_corrupted)
cost2 = lq_functional2(est_param, FOPDTmodel, init_condition, t_vector, input_vector, sim_data_corrupted)
print('Calulated cost function, with noise: {}'.format(cost))
print('Calulated cost2 function, with noise: {}'.format(cost2))
# OPTIMIZATION
# Parameter Identification - minimize the functional
x0 = [Kp_est, tau_est, theta_est]
res = optimize.minimize(lq_functional, x0, args=(FOPDTmodel, init_condition, t_vector, input_vector, sim_data))
res2 = optimize.minimize(lq_functional2, x0, args=(FOPDTmodel, init_condition, t_vector, input_vector, sim_data2))
print()
print('results with piecewise integration')
print(res)
print()
print('results with whole interval integration')
print(res2)
我得到:
time vector length: 141, input vector length: 141
Calulated cost function, no noise: 101.11625776819666
Calulated cost function, with noise: 145.64124970748537
Calulated cost2 function, with noise: 145.64125759948672
results with piecewise integration
fun: 4.388434235295435e-14
hess_inv: array([[ 0.04835563, 0.03152493, -0.00713219],
[ 0.03152493, 0.03621244, -0.00812973],
[-0.00713219, -0.00812973, 0.00470696]])
jac: array([ -7.11831968e-07, 1.93578035e-06, 5.74649344e-06])
message: 'Optimization terminated successfully.'
nfev: 93
nit: 12
njev: 18
status: 0
success: True
x: array([ 1.99999996, 0.99999998, 1.00000001])
results with whole interval integration
fun: 13.884320943542953
hess_inv: array([[ 0.13204289, -0.1932133 , 0.23462904],
[-0.1932133 , 0.37258444, -0.44843942],
[ 0.23462904, -0.44843942, 0.54146373]])
jac: array([-13.79523265, 52.3177942 , 54.71900034])
message: 'Desired error not necessarily achieved due to precision loss.'
nfev: 247
nit: 2
njev: 47
status: 2
success: False
x: array([ 2.09032193, 1.87545011, 0.6178844 ])
有人会为我为什么获得不同的结果提供线索吗?
谢谢