Python中运动粒子之间的弹性碰撞:为什么动能不守恒?

2024-09-28 03:17:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在pygame中编写一个粒子模拟程序,但是在编写粒子之间的碰撞时遇到了困难。所有的碰撞都是弹性的,所以动能应该守恒。然而,我遇到了两个主要问题:

  1. 粒子不断加速直到失去控制
  2. 粒子聚集在一起停止运动

我真的很感激任何有助于解决这些问题的见解。在

我用这个文件(http://www.vobarian.com/collisions/2dcollisions2.pdf)来帮助计算碰撞后粒子的新速度。所有粒子的质量都是一样的,所以我在计算中忽略了它们的质量。 以下是我目前为止的代码:

import pygame
pygame.init()
import random
import numpy

HEIGHT = 500
WIDTH = 500
NUM_BALLS = 2


#setup
win = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Simulation")

class Particle(object):
    def __init__(self, x, y , radius):
        self.x = x
        self.y = y
        self.radius = radius
        self.xvel = random.randint(1,5)
        self.yvel = random.randint(1,5)

    def Draw_circle(self):
        pygame.draw.circle(win, (255,0,0), (self.x, self.y), self.radius)


def redrawGameWindow():
    win.fill((0,0,0))
for ball in balls:
    ball.Draw_circle()

pygame.display.update()


balls = []    
for turn in range(NUM_BALLS):
balls.append(Particle(random.randint(20,WIDTH-20),random.randint(20,HEIGHT-20),20))


run = True
while run:

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        run = False

for ball in balls:
     if (ball.x + ball.radius) <= WIDTH or (ball.x - ball.radius) >= 0:
       ball.x += ball.xvel
     if (ball.x + ball.radius) > WIDTH or (ball.x - ball.radius) < 0:
         ball.xvel *= -1
         ball.x += ball.xvel
     if (ball.y + ball.radius) <= HEIGHT or (ball.y - ball.radius) >= 0:
         ball.y += ball.yvel
     if (ball.y + ball.radius) > HEIGHT or (ball.y - ball.radius) < 0:
         ball.yvel *= -1
         ball.y += ball.yvel

#code for collision between balls
for ball in balls:
    for i in range(len(balls)):
        for j in range (i+1, len(balls)):
            d = (balls[j].x-balls[i].x)**2 + (balls[j].y-balls[i].y)**2
            if d < (balls[j].radius + balls[i].radius)**2:
                print("collision")
                n  = (balls[i].x-balls[j].x, balls[i].y-balls[j].y) #normal vector
                un = n/ numpy.sqrt((balls[i].x-balls[j].x)**2 + (balls[i].y-balls[j].y)**2) #unit normal vector
                ut = numpy.array((-un[1],un[0])) #unit tangent vector
                u1n = numpy.vdot(un, (balls[j].xvel, balls[j].yvel)) #initial velocity in normal direction for first ball
                v1t = numpy.vdot(ut, (balls[j].xvel, balls[j].yvel)) #initial velocity in tangent direction (remains unchanged)
                u2n = numpy.vdot(un, (balls[i].xvel, balls[i].yvel)) #second ball
                v2t = numpy.vdot(ut, (balls[i].xvel, balls[i].yvel))
                v1n = u2n
                v2n = u1n
                v1n *= un
                v2n *= un
                v1t *= ut
                v2t *= ut
                v1 = v1n + v1t #ball 1 final velocity
                v2 = v2n + v2t #ball 2 final velocity
                balls[j].xvel = int(numpy.vdot(v1, (1,0))) #splitting final velocities into x and y components again
                balls[j].yvel = int(numpy.vdot(v1, (0,1)))
                balls[i].xvel = int(numpy.vdot(v2, (1,0)))
                balls[i].yvel = int(numpy.vdot(v2, (0,1)))

redrawGameWindow()
pygame.time.delay(20)

pygame.quit()

Tags: inselfnumpyforif粒子randompygame
2条回答

v1 = v1n + v1t中,v1n是一个向量,而{}是一个标量。在

因此,值v1t被加到v1n向量的两个分量中,这将导致错误。在

要将切向速度添加为向量,因此应该像对法向速度所做的那样:

v1n *= un
v2n *= un
v1t *= ut
v2t *= ut

注意,对两个完全不同的东西使用同一个变量,向量和它的范数,会使代码更难理解,并可能导致错误——就像刚才一样。您可能应该以更一致的方式重命名变量,以便使差异更清楚。在

另外,ut必须是一个numpy数组,但您将其设置为tuple。考虑一下区别:

用元组

^{pr2}$

带着np.数组公司名称:

^{3}$

所以,你必须改变

ut = (-un[1],un[0])

进入

ut = numpy.array((-un[1],un[0]))

物体粘在一起的问题很简单,即使你总是检查碰撞情况,它真正意识到的时候,是当这些圆越过接触,但又稍微相互接触的时候。这些圆现在反弹并移动一点。然而,它们还没有移动到完全不接触的程度,这意味着再次反弹,它会循环。我做了一些代码来修复这个问题,但是你必须改变bounce函数并使用你的代码,因为我的代码不是那么完美,而是功能性的。在

import pygame
pygame.init()
import random
import numpy

HEIGHT = 500
WIDTH = 500
NUM_BALLS = 2


#setup
win = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Simulation")

class Particle(object):
    def __init__(self, x, y , radius):
        self.x = x
        self.y = y
        self.radius = radius
        self.xvel = random.randint(1,5)
        self.yvel = random.randint(1,5)
        self.colideCheck = []
    def Draw_circle(self):
        pygame.draw.circle(win, (255,0,0), (int(self.x), int(self.y)), self.radius)


def redrawGameWindow():
    win.fill((0,0,0))
    for ball in balls:
        ball.Draw_circle()
    pygame.display.update()
def bounce(v1,v2, mass1 = 1, mass2 = 1):




    #i Tried my own bouncing mechanism, but doesn't work completly well, add yours here




    multi1 = mass1/(mass1+mass2)
    multi2 = mass2/(mass1+mass2)
    deltaV2 = (multi1*v1[0]-multi2*v2[0],multi1*v1[1]-multi2*v2[1])
    deltaV1 = (multi2*v2[0]-multi1*v1[0],multi2*v2[1]-multi1*v1[1])
    print("preVelocities:",v1,v2)
    return deltaV1,deltaV2
def checkCollide(circ1Cord,circ1Rad,circ2Cord,circ2Rad):
    tryDist = circ1Rad+circ2Rad
    actualDist = dist(circ1Cord,circ2Cord)
    if dist(circ1Cord,circ2Cord) <= tryDist:
        return True
    return False
def dist(pt1,pt2):
    dX = pt1[0]-pt2[0]
    dY = pt1[1]-pt2[1]
    return (dX**2 + dY**2)**0.5
balls = []  
for turn in range(NUM_BALLS):
    balls.append(Particle(random.randint(60,WIDTH-60),random.randint(60,HEIGHT-60),60))


run = True
while run:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
    for ball in balls:
         if (ball.x + ball.radius) <= WIDTH and (ball.x - ball.radius) >= 0:
             ball.x += ball.xvel
         else:
             ball.xvel *= -1
             ball.x += ball.xvel
         if (ball.y + ball.radius) <= HEIGHT and (ball.y - ball.radius) >= 0:
             ball.y += ball.yvel
         else:
             ball.yvel *= -1
             ball.y += ball.yvel
    for ball in balls:
        for secBallCheck in balls:
            if secBallCheck not in ball.colideCheck and ball!= secBallCheck and checkCollide((ball.x,ball.y),ball.radius,(secBallCheck.x,secBallCheck.y),secBallCheck.radius):
                print("COLLIDE")
                vel1,vel2 = bounce((ball.xvel,ball.yvel),(secBallCheck.xvel,secBallCheck.yvel))
                print(vel1,vel2)
                ball.xvel = vel1[0]
                ball.yvel = vel1[1]
                ball.colideCheck.append(secBallCheck)
                secBallCheck.xvel = vel2[0]
                secBallCheck.yvel = vel2[1]
                secBallCheck.colideCheck.append(ball)
            elif not checkCollide((ball.x,ball.y),ball.radius,(secBallCheck.x,secBallCheck.y),secBallCheck.radius):
                if ball in secBallCheck.colideCheck:
                    secBallCheck.colideCheck.remove(ball)
                    ball.colideCheck.remove(secBallCheck)
    redrawGameWindow()
    pygame.time.delay(20)

pygame.quit()

相关问题 更多 >

    热门问题