如何使用scipy.integrate.ode.set_f_params()来进行时间相关的参数更改?

时间:2014-01-24 12:30:31

标签: python scipy numeric ode

以下脚本说明了scipy.integrate.ode.set_f_params()使我感到困惑的一些行为。

from scipy.integrate import ode

def f(t,ys,a):
    return a

p = [1]
r = ode(f).set_initial_value(0,0).set_f_params(*p)

s = 0
while r.successful() and s<5:
    r.integrate(r.t+1)
    print r.t, r.y, p

    p[0] += 1
    r = ode(f).set_initial_value(r.y,r.t).set_f_params(*p) ### Option 1
    # r = r.set_f_params(*p) ### Option 2

    s += 1

使用选项1的输出是:

1.0 [ 1.] [1]
2.0 [ 3.] [2]
3.0 [ 6.] [3]
4.0 [10.] [4]
5.0 [ 15.] [5]

这是我的期望。我认为选项2应该给出相同的输出,但它给出了以下内容:

1.0 [ 1.] [1]
2.0 [ 2.] [2]
3.0 [ 3.] [3]
4.0 [ 5.64491239] [4]
5.0 [ 9.64491239] [5]

如果有人能够对此有所了解,我会非常感激。

3 个答案:

答案 0 :(得分:3)

你获得不同输出的原因是因为在选项2中,scipy积分器决定它不需要经常调用你的函数'f'。 (事实上​​,在选项2中,在t = 1和t = 3之间,它甚至不会打扰你的'f'。)

要看到这一点,你可以像这样修改你的函数:

def f(t,ys,a):
    print "f(%.17f) is returning %f" % (t, a)
    return a

然后选项2在这里产生输出:(注意积分器,如果是聪明的,如何将它为't'探测的值跳过10倍。这导致它一直跳过.345到3.45。所以,f将会返回不同的事实,积分器不会注意到它达到t 4.0)

f(0.00000000000000000) is returning 1.000000
f(0.00000000000014901) is returning 1.000000
f(0.00000000000038602) is returning 1.000000
f(0.00000000000031065) is returning 1.000000
f(0.00000000310683694) is returning 1.000000
f(0.00000003417209978) is returning 1.000000
f(0.00000034482472820) is returning 1.000000
f(0.00000345135101245) is returning 1.000000
f(0.00003451661385491) is returning 1.000000
f(0.00034516924227954) is returning 1.000000
f(0.00345169552652583) is returning 1.000000
f(0.03451695836898876) is returning 1.000000
f(0.34516958679361798) is returning 1.000000
f(3.45169587103990994) is returning 1.000000
1.0 [ 1.] [1]
2.0 [ 2.] [2]
3.0 [ 3.] [3]
f(34.51695871350283085) is returning 4.000000
f(34.51695871350283085) is returning 4.000000
f(6.55822215528620234) is returning 4.000000
f(6.55822215528620234) is returning 4.000000
f(3.76234849946453931) is returning 4.000000
f(3.76234849946453931) is returning 4.000000
f(3.45169587103990994) is returning 4.000000
f(3.48276113388237274) is returning 4.000000
f(3.51382639672483554) is returning 4.000000
f(3.82447902514946492) is returning 4.000000
f(6.93100530939575776) is returning 4.000000
4.0 [ 5.64491239] [4]
5.0 [ 9.64491239] [5]

相比之下,选项1产生了这个:

f(0.00000000000000000) is returning 1.000000
f(0.00000000000014901) is returning 1.000000
f(0.00000000000038602) is returning 1.000000
f(0.00000000000031065) is returning 1.000000
f(0.00000000310683694) is returning 1.000000
f(0.00000003417209978) is returning 1.000000
f(0.00000034482472820) is returning 1.000000
f(0.00000345135101245) is returning 1.000000
f(0.00003451661385491) is returning 1.000000
f(0.00034516924227954) is returning 1.000000
f(0.00345169552652583) is returning 1.000000
f(0.03451695836898876) is returning 1.000000
f(0.34516958679361798) is returning 1.000000
f(3.45169587103990994) is returning 1.000000
1.0 [ 1.] [1]
f(1.00000000000000000) is returning 2.000000
f(1.00000004712160906) is returning 2.000000
f(1.00004853947319172) is returning 2.000000
f(1.00002426973659575) is returning 2.000000
f(1.24272163569515759) is returning 2.000000
f(3.66969529528077576) is returning 2.000000
2.0 [ 3.] [2]
f(2.00000000000000000) is returning 3.000000
f(2.00000008161702114) is returning 3.000000
f(2.00009034213922021) is returning 3.000000
f(2.00004517106961011) is returning 3.000000
f(2.45175586717085858) is returning 3.000000
f(6.96886282818334202) is returning 3.000000
3.0 [ 6.] [3]
f(3.00000000000000000) is returning 4.000000
f(3.00000009424321812) is returning 4.000000
f(3.00009707894638256) is returning 4.000000
f(3.00004853947319150) is returning 4.000000
f(3.48544327138667454) is returning 4.000000
f(8.33939059052150533) is returning 4.000000
4.0 [10.] [4]
f(4.00000000000000000) is returning 5.000000
f(4.00000010536712125) is returning 5.000000
f(4.00010264848819030) is returning 5.000000
f(4.00005132424409471) is returning 5.000000
f(4.51329376519484793) is returning 5.000000
f(9.64571817470238457) is returning 5.000000
5.0 [ 15.] [5]

答案 1 :(得分:1)

通常无需使用set_f_params。在Python中,您可以使用外部作用域中的变量:

def f(t, ys):
    return a

r = ode(f).set_initial_value(0,0)

a = 1
s = 0

while r.successful() and s < 5:
    r.integrate(r.t+1)
    print r.t, r.y, a
    a += 1
    s += 1

答案 2 :(得分:0)

我一直在尝试使用进行测量的传感器和按固定间隔施加力的执行器来模拟控制问题时遇到相同的问题。我找到了一个适用于我们两个应用程序的解决方案。 诀窍是将最大步长设置为与您的时间步相同。这将强制求解器在每个时间步进行计算,这意味着您可以在while循环中设置set_f_params()并知道您的更改将立即生效。这是一个带有控制器的阻尼摆系统的小例子。

这是我想出的最好的解决方案,欢迎发表评论。

from numpy import *
import matplotlib.pyplot as plt
from scipy.integrate import ode

def propagate(t, state, k, T, torque_list, integrator_t):
    integrator_t.append(t)
    torque_list.append(T)
    return array([state[1], -k*state[1] + T])


k = .5
#state is angle and angular rate
istate = array([0, 2*pi])
dt = .1

torque_list = []
integrator_t = []
solver = ode(propagate)
solver.set_integrator('dopri5',max_step = dt)
solver.set_initial_value(istate, 0)
solver.set_f_params(k, 0, torque_list, integrator_t )


newstate = []
t = []
theta_target = pi/4
while solver.successful() and solver.t < 14:
    newstate.append(solver.y)
    t.append(solver.t)

    solver.integrate(solver.t + dt)

    T = -2*(solver.y[0]-theta_target) - solver.y[1]
    solver.set_f_params(k, T, torque_list, integrator_t)

torque_list = vstack(torque_list)
integrator_t = vstack(integrator_t)

plt.figure()
plt.plot(t, newstate)
plt.title('States vs Time')
plt.xlabel('Time [s]')
plt.ylabel('Angle, Angle Rate [rad], [rad/s]')
plt.legend(['Angle','Angular Rate'])
#plt.savefig('states.png')

plt.figure()
plt.plot(integrator_t, torque_list)
plt.title('Command Torques vs Time')
plt.xlabel('Time [s]')
plt.ylabel('Command Torque [Nm]')
#plt.savefig('torques.png')
plt.show()

A plot of the systems states of a controlled pendulum. The angle is controlled to pi/4 and the angular rate is controlled to zero.

A plot of the command torques over time. Notice that they regularly update every 0.1 seconds, same as my timestep.