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

Python 如何使用scipy.integrate.ode.set_f_params()进行与时间相关的参数更改?,python,scipy,numeric,ode,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

下面的脚本演示了
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
这正是我所期望的。我认为选项2应该给出相同的输出,但它给出了以下内容:

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

如果有人能解释一下,我将不胜感激。

通常不需要使用
设置参数。在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
def(t,ys):
归还
r=ode(f)。设置初始值(0,0)
a=1
s=0
当r.successful()和s<5时:
r、 整合(r.t+1)
打印r.t,r.y,a
a+=1
s+=1

通常不需要使用
设置参数
。在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
def(t,ys):
归还
r=ode(f)。设置初始值(0,0)
a=1
s=0
当r.successful()和s<5时:
r、 整合(r.t+1)
打印r.t,r.y,a
a+=1
s+=1

获得不同输出的原因是,在选项2中,scipy积分器决定不需要经常调用函数“f”。(事实上,在选项2中,在t=1和t=3之间,即使只调用一次您的“f”也不麻烦。)

要看到这一点,您可以修改函数f,如下所示:

def f(t,ys,a):
    print "f(%.17f) is returning %f" % (t, a)
    return a
然后,选项2在这里产生输出:(注意,积分器是如何聪明地将它探测到的“t”的值跳跃10倍。这会导致它一直跳到3.45。因此,直到达到t 4.0,积分器才注意到f将返回不同的值)

相比之下,选项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]

得到不同输出的原因是,在选项2中,scipy积分器决定不需要经常调用函数“f”。(事实上,在选项2中,在t=1和t=3之间,即使只调用一次您的“f”也不麻烦。)

要看到这一点,您可以修改函数f,如下所示:

def f(t,ys,a):
    print "f(%.17f) is returning %f" % (t, a)
    return a
然后,选项2在这里产生输出:(注意,积分器是如何聪明地将它探测到的“t”的值跳跃10倍。这会导致它一直跳到3.45。因此,直到达到t 4.0,积分器才注意到f将返回不同的值)

相比之下,选项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]

我也遇到过同样的问题,试图用传感器来模拟控制问题,传感器进行测量,执行器定期施加力。我找到了一个适用于我们两个应用程序的解决方案诀窍是将最大步长设置为与时间步长相同。这将强制解算器在每个时间步长进行计算,这意味着您可以在while循环中设置_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()
从numpy导入*
将matplotlib.pyplot作为plt导入
从scipy.integrate导入ode
def传播(t、状态、k、t、扭矩列表、积分器):
积分器附加(t)
扭矩表。附加(T)
返回数组([state[1],-k*state[1]+T])
k=0.5
#状态是角度和角速率
istate=数组([0,2*pi])
dt=.1
扭矩列表=[]
积分器_t=[]
解算器=ode(传播)
解算器。设置积分器('dopri5',最大步长=dt)
解算器。设置初始值(istate,0)
解算器。设置参数(k,0,扭矩列表,积分器)
newstate=[]
t=[]
θ=π/4
当solver.successful()和solver.t<14时:
newstate.append(solver.y)
t、 追加(solver.t)
solver.integrate(solver.t+dt)
T=-2*(solver.y[0]-theta_目标)-solver.y[1]
解算器。设置参数(k、T、扭矩列表、积分器)
扭矩列表=vstack(扭矩列表)
积分器t=vstack(积分器t)
plt.图()
plt.plot(t,newstate)
plt.title(‘状态与时间’)
plt.xlabel('Time[s]'))
plt.ylabel('角度,角度速率[rad],[rad/s])
plt.图例(['角度','角速率])
#plt.savefig('states.png'))
plt.图()
plt.绘图(积分器、扭矩表)
plt.title(‘命令扭矩与时间’)
plt.xlabel('Time[s]'))
plt.ylabel('指令扭矩[Nm]”)
#plt.savefig('torges.png')
plt.show()


我在尝试使用传感器模拟控制问题时遇到了同样的问题,传感器进行测量,执行器定期施加力。我找到了一个适用于我们两个应用程序的解决方案诀窍是将最大步长设置为与时间步长相同。这将强制解算器在每个时间步长进行计算,这意味着您可以在while循环中设置_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()
从numpy导入*
将matplotlib.pyplot作为plt导入
从scipy.integrate导入ode
def传播(t、状态、k、t、扭矩列表、积分器):
积分器附加(t)
扭矩表。附加(T)
返回数组([state[1],-k*state[1]+T])
k=0.5
#状态是角度和角速率
istate=数组([0,2*pi])
dt=.1
扭矩列表=[]
积分器_t=[]
解算器=ode(传播)
解算器。设置积分器('dopri5',最大步长=dt)
解算器。设置初始值(istate,0)
解算器。设置参数(k,0,扭矩列表,积分器)
newstate=[]
t=[]
θ=π/4
当solver.successful()和solver.t<14时:
newstate.append(solver.y)
t、 追加(solver.t)
solver.integrate(solver.t+dt)
T=-2*(solver.y[0]-theta_目标)-solver.y[1]
解算器。设置参数(k、T、扭矩列表、积分器)
扭矩列表=vstack(扭矩列表)
积分器t=vstack(积分器t)
plt.图()
plt.plot(t,newstate)
plt.title(‘状态与时间’)
plt.xlabel('Time[s]'))
plt.ylabel('角度,角度速率[rad],[rad/s])
plt.图例(['角度','角速率])
#plt.savefig('states.png'))
菲古