Нечетный эффект замедления для анимации с двумя маятниками с помощью python tkinter

Я только начал работать с python и попытался создать анимацию с двойным маятником, используя tkinter. Я осознаю, что из-за моей неопытности я, вероятно, выбрал запутанный подход.

У меня ошибка, которую я не ожидал. Кажется, что маятник странно тормозит местами. Со временем энергия системы, кажется, падает, что не должно происходить, поскольку я не учел никакого трения.

Я не думаю, что я допустил ошибку в своих формулах для theta1_dotdot и theta2_dotdot, потому что замедление происходит, даже когда я использую более простые (нефизические) формулы.

Вот моя программа:

from tkinter import*
from random import*
from math import*

gui = Tk()
gui.title("Double Pendulum")
canvas = Canvas(gui, width=300, height=300)
canvas.pack()

r1,r2,m1,m2 = 75,75,10,10
g = 9.81
t=0
delt=0.001
theta1 = random()*2*pi
theta2 = random()*2*pi
theta1_dot,theta2_dot = 0,0
dt = 0.1
t = 0

while t < 1000000:
    num1 = (-g*(2*m1+m2)*sin(theta1))
    num2 = -m2*g*sin(theta1-2*theta2)
    num3 = (-2*sin(theta1-theta2)*m2*            (theta2_dot**2*r2+theta1_dot**2*r1*cos(theta1-theta2)))
    denom1 = r1*(2*m1+m2-m2*cos(2*theta1-2*theta2))
    theta1_dotdot = (num1 + num2 + num3)/denom1

    num4 = 2*sin(theta1-theta2)
    num5 = (theta1_dot**2*r1*(m1+m2))
    num6 = g*(m1+m2)*cos(theta1)
    num7 = theta2_dot**2*r2*m2*cos(theta1-theta2)
    denom2 = r2*(2*m1+m2-m2*cos(2*theta1-2*theta2))
    theta2_dotdot = (num4*(num5+num6+num7))/denom2

    theta1_dot += theta1_dotdot * dt
    theta2_dot += theta2_dotdot * dt
    theta1 += theta1_dot * dt
    theta2 += theta2_dot * dt

    x1 = r1*sin(theta1)
    y1 = r1*cos(theta1)

    x2 = x1 + r2*sin(theta2)
    y2 = y1 + r2*cos(theta2)

    trace = canvas.create_oval(150 + x2, 60 + y2, 150 + x2, 60 + y2, fill='black', outline='black')
    lin1 = canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink')
    lin2 = canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink')
    ov1 = canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink')
    ov2 = canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink')
    t += .1
    canvas.after(1)
    canvas.update()
    canvas.delete(ov1)
    canvas.delete(ov2)
    canvas.delete(lin1)
    canvas.delete(lin2)

gui.mainloop()

1 ответ

Решение

Я переписал ваш код, чтобы удалить общий импорт (импорт *), использовать структуру классов, а не глобальные переменные и функции, и чтобы фактически использовать mainloop tkinter и after правильно:

import tkinter as tk
import random
from math import pi, sin, cos

r1,r2,m1,m2 = 75,75,10,10
g = 9.81

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Double Pendulum")
        self.canvas = tk.Canvas(self, width=300, height=300)
        self.canvas.pack()
        self.delt=0.001
        self.theta1 = random.random()*2*pi
        self.theta2 = random.random()*2*pi
        self.theta1_dot,self.theta2_dot = 0,0
        self.dt = 0.1
        self.t = 0

        self.after(1, self.do_after)

    def do_after(self):
        self.canvas.delete('pendulum')

        num1 = (-g*(2*m1+m2)*sin(self.theta1))
        num2 = -m2*g*sin(self.theta1-2*self.theta2)
        num3 = (-2*sin(self.theta1-self.theta2)*m2*(self.theta2_dot**2*r2+self.theta1_dot**2*r1*cos(self.theta1-self.theta2)))
        denom1 = r1*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta1_dotdot = (num1 + num2 + num3)/denom1

        num4 = 2*sin(self.theta1-self.theta2)
        num5 = (self.theta1_dot**2*r1*(m1+m2))
        num6 = g*(m1+m2)*cos(self.theta1)
        num7 = self.theta2_dot**2*r2*m2*cos(self.theta1-self.theta2)
        denom2 = r2*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta2_dotdot = (num4*(num5+num6+num7))/denom2

        self.theta1_dot += theta1_dotdot * self.dt
        self.theta2_dot += theta2_dotdot * self.dt
        self.theta1 += self.theta1_dot * self.dt
        self.theta2 += self.theta2_dot * self.dt

        x1 = r1*sin(self.theta1)
        y1 = r1*cos(self.theta1)

        x2 = x1 + r2*sin(self.theta2)
        y2 = y1 + r2*cos(self.theta2)

        self.canvas.create_oval(150 + x2, 60 + y2, 150 + x2, 60 + y2, fill='black', outline='black', tag='trace')
        self.canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink', tags='pendulum')
        self.canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink', tags='pendulum')
        self.canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink', tags='pendulum')
        self.canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink', tags='pendulum')
        self.t += .1

        self.after(1, self.do_after)

if __name__ == '__main__':
    app = App()
    app.mainloop()

в вашем коде вы принудительно звоните update на каждой итерации цикла вместо того, чтобы позволить tkinter обрабатывать, когда нужно обновить, вы также вызываете after без обратного вызова, который AFAICT на самом деле ничего не делает.
Я также добавил теги в части маятника, чтобы вы могли удалить их все одним вызовом, вместо того, чтобы каждый раз сохранять их идентификаторы.

при дальнейшем тестировании реальная проблема заключается в том, что вы создаете тысячи объектов на холсте, которые tkinter пытается отобразить. чтобы сохранить трассу, вы можете сохранить список координат и вместо этого вывести его в виде линии:

import tkinter as tk
import random
from math import pi, sin, cos

r1,r2,m1,m2 = 75,75,10,10
g = 9.81

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Double Pendulum")
        self.canvas = tk.Canvas(self, width=300, height=300)
        self.canvas.pack()
        self.delt=0.001
        self.theta1 = random.random()*2*pi
        self.theta2 = random.random()*2*pi
        self.theta1_dot,self.theta2_dot = 0,0
        self.dt = 0.1
        self.t = 0

        self.trace_coords = []

        self.after(1, self.do_after)

    def do_after(self):
        self.canvas.delete('trace')
        self.canvas.delete('pendulum')

        num1 = (-g*(2*m1+m2)*sin(self.theta1))
        num2 = -m2*g*sin(self.theta1-2*self.theta2)
        num3 = (-2*sin(self.theta1-self.theta2)*m2*(self.theta2_dot**2*r2+self.theta1_dot**2*r1*cos(self.theta1-self.theta2)))
        denom1 = r1*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta1_dotdot = (num1 + num2 + num3)/denom1

        num4 = 2*sin(self.theta1-self.theta2)
        num5 = (self.theta1_dot**2*r1*(m1+m2))
        num6 = g*(m1+m2)*cos(self.theta1)
        num7 = self.theta2_dot**2*r2*m2*cos(self.theta1-self.theta2)
        denom2 = r2*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta2_dotdot = (num4*(num5+num6+num7))/denom2

        self.theta1_dot += theta1_dotdot * self.dt
        self.theta2_dot += theta2_dotdot * self.dt
        self.theta1 += self.theta1_dot * self.dt
        self.theta2 += self.theta2_dot * self.dt

        x1 = r1*sin(self.theta1)
        y1 = r1*cos(self.theta1)

        x2 = x1 + r2*sin(self.theta2)
        y2 = y1 + r2*cos(self.theta2)

        self.trace_coords.append((150 + x2, 60 + y2, 150 + x2, 60 + y2))
        self.canvas.create_line(self.trace_coords, fill='black', tag='trace')
        self.canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink', tags='pendulum')
        self.canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink', tags='pendulum')
        self.canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink', tags='pendulum')
        self.canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink', tags='pendulum')
        self.t += .1

        self.after(1, self.do_after)

if __name__ == '__main__':
    app = App()
    app.mainloop()
Другие вопросы по тегам