射弹的Python动画给出直线 [英] Python Animation of projectile giving straight line

查看:117
本文介绍了射弹的Python动画给出直线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标是对喷头进行动画处理/模拟.基本思想是在一个实例上创建多个液滴,每个液滴以不同的角度离开喷头. 但是,我没有直线移动,而是得到了一条静态直线.我只用1个或2个下降点/点尝试了相同的代码,但是我仍然得到相同的直线.起初我以为是因为我将拖拽包含在动画中(在以前的试验中,当我包含拖拽时我得到了奇怪/意外的结果,所以我希望这是同样的问题)

The goal is to animate/simulate a sprinkler. The basic idea is to create multiple droplets at an instance, each exiting the sprinkler at a different angle. However, instead of having a point that moves along, I'm getting a straight, static line. I've tried the same code with only one or 2 drops/points, but I still get the same straight line. At first I thought it was because I included drag into the animation (in previous trials I got strange/unexpected results when I included the drag, so I was hoping it was the same problem)

下面是我编写的代码.

Below is the code I wrote.

dragx和dragy不再设置为相同的值.初始速度是在更新函数之外计算的.

dragx and dragy are no longer set to the same values. The initial velocities are calculated outside the update function.

由于阻力取决于速度,因此在每次增加索引时都会重新计算阻力.

Drag forces are recalculated at each index increase, since the drag depends on the velocity.

现在也包括进口.

我现在也尝试使用散点图而不是直线,这在原点给了我一个点.从那时起我回到了那条线上

I have also now tried to use a scatter plot instead of a line, which gave me a single dot at the origin. I reverted back to the line since then

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from mpl_toolkits.mplot3d import Axes3D 
import matplotlib.animation as animation
from math import sin, cos
import matplotlib.axes as axess

#pressure in system
def pressure(pump):
    return 10.197*pump*10**5/998.2

#Initial velocity based on the pressure of the system
def vinit(init_head):
    return (2*9.81*init_head)**0.5
# Number of droplets per iteration
n = 2
v0 = vinit(pressure(5.8))
cd = 0.5                                             #drag coefficient of a sphere;   
rho = 1.225                                          #density of air;                 kg/m^3

fps = 20
runtime = 1*20
numframes = fps*runtime
time = np.linspace(0,runtime,numframes)

#Initialize droplets

droplets = np.zeros(numframes, dtype = [('position', float, 2),
                                ('velocity', float, 2),
                                ('force', float, 2),
                                ('angle',float, 1),
                                ('radius', float, 1)])

droplets['radius'] = np.random.normal(0.0045, 0.001, numframes)
A = 4*np.pi*droplets['radius']**2
mass = ((4*np.pi*droplets['radius']**3)/3)*1000     #mass of waterdroplet
drag = np.zeros(numframes,dtype = [('dragx', float, 1),('dragy', float, 1)])

rads = np.radians(np.random.normal(37,1,numframes))
droplets['angle'] = rads
droplets['force'][:,1] = -9.8*mass

#Initialize velocity
for i in range(0, numframes):
    droplets['velocity'][i,0] = v0*cos(droplets['angle'][i])
    droplets['velocity'][i,1] = v0*sin(droplets['angle'][i])    

#Initialize the drag
drag['dragx'] = -0.5*rho*cd*A*droplets['velocity'][:,0]
drag['dragy'] = -0.5*rho*cd*A*droplets['velocity'][:,1]


droplets['position'][:,0] = 0


#Initialize the figure
fig,ax = plt.subplots()
axess.Axes.set_frame_on(ax,True)
ax.autoscale(True)
ax.set_xlim(0)
ax.set_ylim(0)

line = ax.plot(droplets['position'][0, 0], droplets['position'][0, 1],'b.')[0]
line = ax.plot(droplets['position'][:, 0], droplets['position'][:, 1],'b.')[0]
xdata = [droplets['position'][0, 0]]
ydata = [droplets['position'][0, 1]]
ln = plt.plot([],[],'b')[0]

def initfunc():
    droplets['position'][0,:] = 0
    ln.set_data(droplets['position'][0, 0], droplets['position'][0, 1])
    return ln

def update(framenum):
    index = framenum
    cd = 0.5                                          #drag coefficient of a sphere;   
    rho = 1.225                                       #density of air;                 kg/m^3
    A = 4*np.pi*droplets['radius']**2                 #surface area of droplet;        m^2
    mass = ((4*np.pi*droplets['radius']**3)/3)*1000   #mass of waterdroplet

    #Update the drag force on the droplet
    drag['dragx'] = -0.5*rho*cd*A*droplets['velocity'][:,0]
    drag['dragy'] = -0.5*rho*cd*A*droplets['velocity'][:,1]

    droplets['force'][index,0] += drag['dragx'][index]
    droplets['force'][index,1] += drag['dragy'][index]
    #droplets['position'][0] = [0,0]
    droplets['velocity'][index,0] = droplets['velocity'][index,0] + (droplets['force'][index,0]/mass[index])*index
    droplets['velocity'][index,1] = droplets['velocity'][index,1] + (droplets['force'][index,1]/mass[index])*index
    droplets['position'][index,0] = droplets['position'][index,0] + (droplets['velocity'][index,0])*index  
    droplets['position'][index,1] = droplets['position'][index,1] + (droplets['velocity'][index,1])*index  

    xdata.append(droplets['position'][index,0])
    ydata.append(droplets['position'][index,1])
    ln.set_data(xdata,ydata)

    line.set_data(droplets['position'][:,0], droplets['position'][:,1])

    return ln



sprink = animation.FuncAnimation(fig, update,init_func = initfunc,interval= 200,  frames = numframes)
plt.show()

sprink.save('Sprinkler.mp4', fps = fps)

推荐答案

除了 @JohanC ,则使用的是表面积(4 * pi * r^2),而不是水滴(近似为球体)的横截面积(pi * r^2).此外,您似乎正在尝试使用假设为真空的运动方程式,当添加空气阻力或其他非恒定力时,这些方程式将失效.

In addition to the mistakes pointed out by @JohanC, you are using the surface area (4 * pi * r^2) instead of the cross-sectional area (pi * r^2) of the water droplets (approximated as spheres). Furthermore, you appear to be trying to using equations of motion which assume a vacuum, those equations break down when air resistance or other nonconstant forces are added.

我建议您阅读

I would recommend you read this answer for a more complete explanation of calculating ballistic projectile motion with air resistance, but here is a simplified version.

计算粒子在介质中移动的位置本质上是解决一阶ODE ,有许多方法可以用数值方法求解这样的方程式.最简单(尽管最不可靠)的方法是将问题分成小段,并近似估计从当前点到下一个点的变化.对于此问题,假设您使用了足够小的时间步长,则这种方法效果很好.

Calculating the position of a particle moving through a medium is essentially solving a first order ODE, there are many ways to solve such an equation numerically. The simplest (albeit least robust) way is to break the problem into small segments and approximate the change from the current point to the next point. For this problem such a method works quite well, assuming you use a small enough time step.

在您的情况下,我们可以做这样的事情

In your case we could do something like this

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
from math import sin, cos

g = 9.81
rho = 1.225
v0 = 50.0    # This can be initialized as desired, I chose 50 m/s for demonstration
C = 0.5

nframes = 100
ndrops = 10
tend = 8.0
time = np.linspace(0.1, tend, nframes)   # Runtime of 4s chosen arbitrarily
dt = time[1] - time[0]                    # change in time between steps
maxs = [0.0, 0.0]
r = [0.001, 0.0045]                       # This stores the range of radii to be used

# I have implemented a class to store the drops for brevity
class drop:

    def __init__(self, pos, vel, r):
        self.pos = pos
        self.vel = vel
        self.r = r

class sprinkler:
# I implemented this as a class to simplify updating the scatter plot
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.drops = [None]*ndrops
        self.maxt = 0.0
        ## This step is simply to figure out how far the water drops can travel
        ## in order to set the bounds of the plot accordingly
        angles = np.linspace(0, np.pi/2, 90)    # We only need to sample from 0 to pi/2
        for angle in angles:
            m = [drop([0.0, 0.1], [v0*cos(angle), v0*sin(angle)], 0.001),
                 drop([0.0, 0.1], [v0*cos(angle), v0*sin(angle)], 0.0045)]
            for d in m:
                t = 0.0
                coef = - 0.5*C*np.pi*d.r**2*rho
                mass = 4/3*np.pi*d.r**3*1000
                while d.pos[1] > 0:
                    a = np.power(d.vel, 2) * coef * np.sign(d.vel)/mass
                    a[1] -= g
                    d.pos += (d.vel + a * dt) * dt
                    d.vel += a * dt
                    t += dt
                    if d.pos[1] > maxs[1]:
                        maxs[1] = d.pos[1]
                    if d.pos[0] > maxs[0]:
                        maxs[0] = d.pos[0]
                    if d.pos[1] < 0.0:
                        if t > self.maxt:
                            self.maxt = t
                        break
        for ii in range(ndrops):    # Make some randomly distributed water drops
            self.drops[ii] = drop([0.0, 0.0], [cos(np.random.random()*np.pi)*v0,
                             sin(np.random.random()*np.pi)*v0],
                             np.random.random()*(r[1]-r[0]) + r[0])

        anim = animation.FuncAnimation(self.fig, self.update, init_func=self.setup,
                                       interval=200, frames=nframes)
        anim.save('Sprinkler.gif', fps = 20, writer='imagemagick')

    def setup(self):
        self.scat = self.ax.scatter([d.pos[0] for d in self.drops],
                               [d.pos[1] for d in self.drops], marker='.', color='k')
        self.ax.set_xlim([-maxs[0]*1.15, maxs[0]*1.15])
        self.ax.set_ylim([0, maxs[1]*1.15])

        return self.scat,

    def update(self,frame):  # Use set_offsets to move the water drops
        self.step()          # Advance to the next 'step'
        self.scat.set_offsets(np.stack([[d.pos[0] for d in self.drops],
                                       [d.pos[1] for d in self.drops]], 1))

        return self.scat,

    def step(self):
        for ii in range(ndrops):
            coef = - 0.5*C*np.pi*self.drops[ii].r**2*rho    # Aggregated coefficient
            mass = 4/3*np.pi*self.drops[ii].r**3*1000
            a = np.power(self.drops[ii].vel, 2)*coef * np.sign(self.drops[ii].vel)/mass
            a[1] -= g
            # Approximate how much the position and velocity would change if we assume
            # a(t) does not change between t and t+dt
            self.drops[ii].pos += np.array(self.drops[ii].vel) * dt + 0.5 * a * dt**2
            self.drops[ii].vel += a * dt
            if self.drops[ii].pos[1] < 0.0:     # Check if the drop has hit the ground
                self.drops[ii].pos[1] = 0.0
                self.drops[ii].vel = [0.0, 0.0]

sprinkler()

这可能无法完全产生您想要的效果,但是可以正确地模拟水滴的运动.

This may not produce exactly what you are after but it correctly models the motion of the water droplets.

将drop作为类实现并将其存储在列表中,通过简单地修改update函数(为简单起见添加了create方法),就可以轻松创建更多的drop来模拟上述的许多迭代". ):

Implementing the drops as a class and storing them in a list as I have makes it easy to create more droplets to simulate many 'iterations' of the above by simply modifying the update function (create method added for simplicity):

    def update(self,frame):  # Use set_offsets to move the water drops
        # Create between 0 and ndrops new drops, but only until tend-maxt
        if time[frame] < tend - self.maxt*1.1:
            self.create(np.random.randint(0, ndrops))
        self.step()          # Advance to the next 'step'
        self.scat.set_offsets(np.stack([[d.pos[0] for d in self.drops],
                                        [d.pos[1] for d in self.drops]], 1))

        return self.scat,

    def create(self, i):
        for ii in range(i):
            self.drops.append(drop([0.0, 0.0], [cos(np.random.random()*np.pi)*v0,
                             sin(np.random.random()*np.pi)*v0],
                             np.random.random()*(r[1]-r[0]) + r[0]))

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
from math import sin, cos

g = 9.81
rho = 1.225
v0 = 50.0    # This can be initialized as desired, I chose 50 m/s for demonstration
C = 0.5

nframes = 100
ndrops = 10
tend = 8.0
time = np.linspace(0.1, tend, nframes)   # Runtime of 4s chosen arbitrarily
dt = time[1] - time[0]                    # change in time between steps
maxs = [0.0, 0.0]
r = [0.001, 0.0045]                       # This stores the range of radii to be used

# I have implemented a class to store the drops for brevity
class drop:

    def __init__(self, pos, vel, r):
        self.pos = pos
        self.vel = vel
        self.r = r

class sprinkler:
# I implemented this as a class to simplify updating the scatter plot
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.drops = [None]*ndrops
        self.maxt = 0.0
        ## This step is simply to figure out how far the water drops can travel
        ## in order to set the bounds of the plot accordingly
        angles = np.linspace(0, np.pi/2, 90)    # We only need to sample from 0 to pi/2
        for angle in angles:
            m = [drop([0.0, 0.1], [v0*cos(angle), v0*sin(angle)], 0.001),
                 drop([0.0, 0.1], [v0*cos(angle), v0*sin(angle)], 0.0045)]
            for d in m:
                t = 0.0
                coef = - 0.5*C*np.pi*d.r**2*rho
                mass = 4/3*np.pi*d.r**3*1000
                while d.pos[1] > 0:
                    a = np.power(d.vel, 2) * coef * np.sign(d.vel)/mass
                    a[1] -= g
                    d.pos += (d.vel + a * dt) * dt
                    d.vel += a * dt
                    t += dt
                    if d.pos[1] > maxs[1]:
                        maxs[1] = d.pos[1]
                    if d.pos[0] > maxs[0]:
                        maxs[0] = d.pos[0]
                    if d.pos[1] < 0.0:
                        if t > self.maxt:
                            self.maxt = t
                        break
        for ii in range(ndrops):    # Make some randomly distributed water drops
            self.drops[ii] = drop([0.0, 0.0], [cos(np.random.random()*np.pi)*v0,
                             sin(np.random.random()*np.pi)*v0],
                             np.random.random()*(r[1]-r[0]) + r[0])

        anim = animation.FuncAnimation(self.fig, self.update, init_func=self.setup,
                                       interval=200, frames=nframes)
        anim.save('Sprinkler.gif', fps = 20, writer='imagemagick')

    def setup(self):
        self.scat = self.ax.scatter([d.pos[0] for d in self.drops],
                                    [d.pos[1] for d in self.drops], marker='.', color='k')
        self.ax.set_xlim([-maxs[0]*1.15, maxs[0]*1.15])
        self.ax.set_ylim([0, maxs[1]*1.15])

        return self.scat,

    def update(self,frame):  # Use set_offsets to move the water drops
        # Create between 0 and ndrops new drops, but only until tend-maxt
        if time[frame] < tend - self.maxt*1.1:
            self.create(np.random.randint(0, ndrops))
        self.step()          # Advance to the next 'step'
        self.scat.set_offsets(np.stack([[d.pos[0] for d in self.drops],
                                        [d.pos[1] for d in self.drops]], 1))

        return self.scat,

    def create(self, i):
        for ii in range(i):
            self.drops.append(drop([0.0, 0.0], [cos(np.random.random()*np.pi)*v0,
                             sin(np.random.random()*np.pi)*v0],
                             np.random.random()*(r[1]-r[0]) + r[0]))
    def step(self):
        for ii in range(len(self.drops)):
            coef = - 0.5*C*np.pi*self.drops[ii].r**2*rho    # Aggregated coefficient
            mass = 4/3*np.pi*self.drops[ii].r**3*1000
            a = np.power(self.drops[ii].vel, 2) * coef * np.sign(self.drops[ii].vel)/mass
            a[1] -= g
            # Approximate how much the position and velocity would change if we assume
            # a(t) does not change between t and t+dt
            self.drops[ii].pos += np.array(self.drops[ii].vel) * dt + 0.5 * a * dt**2
            self.drops[ii].vel += a * dt
            if self.drops[ii].pos[1] < 0.0:     # Check if the drop has hit the ground
                self.drops[ii].pos[1] = 0.0
                self.drops[ii].vel = [0.0, 0.0]

sprinkler()

这篇关于射弹的Python动画给出直线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆