与odeint集成时,Scipy最小化给出不同的结果

时间:2019-01-28 17:05:33

标签: python scipy minimize odeint

我正在尝试使用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 ])

有人会为我为什么获得不同的结果提供线索吗?

谢谢

0 个答案:

没有答案