python 使用scipy.ivp()实现球在自由落体中的反弹

mbzjlibv  于 2023-03-16  发布在  Python
关注(0)|答案(2)|浏览(113)

我想用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,它会上下跳动,但不是它应该的样子。

iklwldmw

iklwldmw1#

Scipy常微分方程求解器不具有用户自定义动作的事件-动作机制,事件函数只是轨迹上的一个标量函数,其零点(如果设置了交叉方向)是事件。这意味着事件函数用于搜索事件(首先检测符号变化,然后使用布伦特方法进行细化)。每个时间步长将调用多次,与时间步长是否包含事件无关。
内置的操作是“注册”(默认)和“终止”。
你将不得不在一次反弹时中断积分,并以反射状态作为初始条件重新开始。将各部分分开绘制或使用连接得到一个大的解数组。

vwkv1x7d

vwkv1x7d2#

实际上,您可以使用solve_ivp的事件处理程序,通过在y〈=0时触发模拟终止来检测球撞击地面时的冲击。
终止后,安全最后一个时间步长的所有状态,降低速度并反转y方向(在您的情况下为-0.9)。然后将数组作为下一次运行的初始条件传递。循环直到您到达定义的模拟结束时间。要完全退出模拟循环,请添加名为“end_of_simulation”的第二个事件检测。如果累计模拟时间超过t_end = 7,模拟完全终止。
注意,时间向量以及所有状态向量需要在每个循环中堆叠在一起。
此外,当球的速度低于某个阈值(例如0.1m/s)时,请关闭速度和重力。否则,球将“坠落”穿过地面。如果有人有更好的主意来处理这种情况,请告诉我。
总而言之,它看起来是这样的:

from scipy.integrate import solve_ivp
import numpy as np
from matplotlib import pyplot as plt

def falling_obj_rhs(t, Y, *args):
    
    g, _, _ = args
    
    dxdt = Y[2]
    dydt = Y[3]
    
    dvxdt = 0
    dvydt = g
    
    return dxdt, dydt, dvxdt, dvydt

def hit_ground(t, Y, *args): 
    return Y[1]

def end_of_simulation(t, Y, *args):
    _, tend, cum_t_event = args
    
    if tend - (cum_t_event + t) <= 0:
        return 0
    else:
        return 1

def main():

    g       = -9.81
    t_start = 0
    t_end   = 7
    
    # the function y is approaching the x axis from above --> negative slope
    hit_ground.direction       = -1  
    hit_ground.terminal        = True
    end_of_simulation.terminal = True
    
    #       x, y, vx, vy
    ic   = [0, 10, 1, 0]
    t    = np.array([])
    ypos = np.array([])
    
    cum_t_event = 0.0
    while cum_t_event < t_end:
        
        sol = solve_ivp( falling_obj_rhs, 
                         [t_start, t_end], 
                         ic, 
                         max_step = 0.01, 
                         events   = [hit_ground, end_of_simulation], 
                         args     = (g, t_end, cum_t_event), )
        
        if sol.t_events[1].size == 1:
            # end_of_simulation event happened --> terminate
            cum_t_event = t_end
        elif sol.t_events[0].size == 0:
            # no hit_ground did occure --> array is empty
            pass
        else:
            cum_t_event = cum_t_event + sol.t_events[0][-1]
        
        # store solution from last time step as initial condition
        ic = sol.y[:,-1]
        # reduce initial condition of y-velocity (energy loss du to collision)
        # and change sign
        ic[-1] = ic[-1]*-0.9
        # turn off gravity and velocity once the ball is on the ground
        if ic[-1] < 0.1:
            ic[-1] = 0
            g      = 0
        
        t    = np.concatenate([t, sol.y[0]], axis=0)
        ypos = np.concatenate([ypos, sol.y[1]], axis=0)
    
    plt.plot(t, ypos, label='obj. trajectory')
    plt.xlabel('t')
    plt.ylabel('y')
    plt.grid()
    plt.legend()

if __name__ == '__main__':
    main()

相关问题