【问题标题】:Python tkinter bouncing balls - energy goes to infinityPython tkinter 弹跳球 - 能量无穷大
【发布时间】:2013-06-16 19:16:29
【问题描述】:

我编写了一个 Python 脚本,它创建一个 Ball 类,然后创建它的实例。球应该从墙上反弹,然后(尚未实施)彼此反弹。请注意,当球碰到墙时,球不仅会反转方向,而是会在球上施加一个力(或加速度)。

不幸的是,球从墙上弹起后,会进入振荡运动,振幅越来越大。

我对 Python 很陌生,但之前写过各种 javascript/canvas 模拟。

这个特殊的 python 脚本使用 Verlet 集成来计算位置和速度。我的脚本基于此:

http://physics.weber.edu/schroeder/software/demos/MolecularDynamics.html

谁能建议我应该如何修改代码,使其在球从墙上弹起时“稳定”?谢谢

from Tkinter import *
import random
window = Tk()
canvas = Canvas(window, width = 400, height = 300)
canvas.pack()
wallStiffness=0.05
dt=0.02


class Ball:

    def __init__(self):
        self.x=0
        self.y=0
        self.xvel=random.random()*2-1
        self.yvel=random.random()*2-1
        self.ax=0
        self.ay=0
        self.rad=20



def computeAccelerations(z):

        if z.x<z.rad:
             z.ax=wallStiffness*(z.rad-z.x)
        else: 
            if z.x>400-z.rad:
                z.ax=wallStiffness*(400-z.rad-z.x) 
        #only have horizontal bounces so far, and no bounces between balls yet


list=[]

for i in range(100):    #0 to 99, make 100 balls
    myball=Ball()
    list.append(myball)

#script to place in a crystal to begin with

current_x=30
current_y=0

for j in list:

        j.x=current_x
        current_x+=30
        j.y=current_y

        if current_x+30>370:
            current_x=30
            current_y+=30            

        j.ball=canvas.create_oval(j.x,j.y,j.x+j.rad,j.y+j.rad,fill="blue")



while True:

    for i in range(10):     #10 steps per frame
        for j in list:
            j.x+=j.xvel*dt+0.5*j.ax*dt*dt
            j.y+=j.yvel*dt+0.5*j.ay*dt*dt
            j.xvel+=0.5*j.ax*dt
            j.yvel+=0.5*j.ay*dt
            computeAccelerations(j)
        for j in list:
            j.xvel+=0.5*j.ax*dt
            j.yvel+=0.5*j.ay*dt

    canvas.after(10)    #duration of frame in ms
    for z in list:
        canvas.move(z.ball,z.xvel, z.yvel)
        canvas.update()

window.mainloop()   #keeps the window open

【问题讨论】:

  • 我在 while 循环的 j.x+= 和 j.y+= 部分中遗漏了一个 *dt。振荡运动已经消失,但粒子正在穿过墙壁!

标签: python tkinter simulation physics particles


【解决方案1】:

我相信您遇到的问题是您忘记在动作循环中添加时间步:

while True:
    for i in range(10):
        for j in list:
            j.x += j.xvel * dt + 0.5 * j.ax * dt * dt
            j.y += j.yvel * dt + 0.5 * j.ay * dt * dt
            j.xvel += 0.5 * j.ax * dt
            j.yvel += 0.5 * j.ay * dt
            compute_accelerations(j)
        for j in list:
            j.xvel += 0.5 * j.ax * dt
            j.yvel += 0.5 * j.ay * dt

另外,虽然您的代码有效,但我认为您的Ball 加速方法可以清理:

# box_width = 400

def compute_accelerations(self):
        if self.x < self.rad:
             self.ax = wallStiffness * (self.rad - self.x)
        elif self.x > box_width - self.rad:
                self.ax = wallStiffness * (box_width - self.rad - self.x) 

在这里您会看到self 用于代替z。这是在 Python 类中创建方法的标准方法。这里self 代表Ball 对象的当前实例。这有意义吗?

你可以这样调用这个新修改的方法:

j.compute_accelerations()

【讨论】:

    【解决方案2】:

    这并不能直接回答您的问题,但您使用 Tkinter 错误。作为一般的经验法则,你不应该调用update,你不应该有一个无限循环,你不应该用一个参数调用after。 Tkinter 已经有一个无限循环在运行——mainloop——那么为什么不利用它呢?通过这样做,您可以消除我列出的所有三个不良做法。

    您应该删除while True 循环的主体并将其放入函数中。然后,您让该函数安排在某个时间间隔再次调用它自己。例如:

    def draw_frame():
        for i in range(10):
            for j in list:
                ...
        for z in list:
            canvas.move(z.ball,z.xvel, z.yvel)
    
        canvas.after(10, draw_frame)
    

    这将绘制一个帧,等待 10 毫秒,然后绘制另一个帧,不断重复。如果您希望能够停止动画,您可以创建一个设置标志变量的按钮(例如:we_should_continue=False),在这种情况下您可以执行以下操作:

        if we_should_continue:
            canvas.after(10, draw_frame)
    

    与您的方案相比,这具有的优势是您的方案具有 10 毫秒的小窗口,其中 GUI 完全冻结,这可能会导致一些非常轻微的卡顿。更大的优势在于 tkinter 就是这样设计的。

    【讨论】:

      猜你喜欢
      • 2020-05-22
      • 1970-01-01
      • 2020-05-23
      • 2020-07-12
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      • 1970-01-01
      相关资源
      最近更新 更多