我想用scipy_ivp求解常微分方程一个球在x方向的初始速度为1,y方向的初始速度为0,重力加速度为g = 9.82,当球撞击地面时,假设其速度改变符号并乘以0.9。但是,使用事件参数,我发现它不能正常工作。这是我的代码和结果:
from scipy.integrate import solve_ivp
def f_derivs_ivp(t, vars, g = 9.82):
dxdt = vars[2]
dydt = vars[3]
dvxdt = 0
dvydt = -g
return dxdt, dydt, dvxdt, dvydt
def bounce(t, y, g = 9.82):
if y[1] <= 0:
y[3] = -0.9*y[3]
#print(y[1])
return y[1]
#bounce.terminal = True
#bounce.direction = -1
vars0 = np.array([0, 10, 1, 0])
sol = solve_ivp(f_derivs_ivp, [0, 7], vars0, max_step=0.01, events = bounce)
plt.plot(sol.y[0], sol.y[1], "ko")
print(sol.y_events)
print(sol.t_events)
使用scipy.ivp()之外的另一个方法,结果应该如下所示:
我做错了什么?events参数是如何工作的?还要注意,如果我在bounce函数中写入return y[1] - 10
,什么也不会改变。如果我在bounce函数的if语句中写入y[3] = 10
,它会上下跳动,但不是它应该的样子。
2条答案
按热度按时间iklwldmw1#
Scipy常微分方程求解器不具有用户自定义动作的事件-动作机制,事件函数只是轨迹上的一个标量函数,其零点(如果设置了交叉方向)是事件。这意味着事件函数用于搜索事件(首先检测符号变化,然后使用布伦特方法进行细化)。每个时间步长将调用多次,与时间步长是否包含事件无关。
内置的操作是“注册”(默认)和“终止”。
你将不得不在一次反弹时中断积分,并以反射状态作为初始条件重新开始。将各部分分开绘制或使用连接得到一个大的解数组。
vwkv1x7d2#
实际上,您可以使用solve_ivp的事件处理程序,通过在y〈=0时触发模拟终止来检测球撞击地面时的冲击。
终止后,安全最后一个时间步长的所有状态,降低速度并反转y方向(在您的情况下为-0.9)。然后将数组作为下一次运行的初始条件传递。循环直到您到达定义的模拟结束时间。要完全退出模拟循环,请添加名为“end_of_simulation”的第二个事件检测。如果累计模拟时间超过t_end = 7,模拟完全终止。
注意,时间向量以及所有状态向量需要在每个循环中堆叠在一起。
此外,当球的速度低于某个阈值(例如0.1m/s)时,请关闭速度和重力。否则,球将“坠落”穿过地面。如果有人有更好的主意来处理这种情况,请告诉我。
总而言之,它看起来是这样的: