摆锤模拟

时间:2013-03-19 21:03:10

标签: python pygame physics physics-engine

我正在尝试在Pygame中编写简单的钟摆模拟。关键在于我试图直接模拟摆锤上的力(重力和张力),而不是求解描述运动的微分方程。首先,我编写了一个函数,它获取一个向量,将轴系统旋转一定角度,并在新的旋转轴系统中返回该向量的组件;这个函数的代码很好,它按预期工作。

模拟的每个刻度我将重力矢量旋转到摆锤和绳索之间的角度,并获得新的组件 - 一个在绳索的方向上,一个与绳索正交。绳索方向上的张力和分量相互抵消,因此只有正交分量是重要的。在我计算之后,我将加速度矢量旋转回正常坐标系,并进行积分。但是,产生的行为不符合预期。可能是什么原因?

这是代码:

from __future__ import division
import copy
import pygame
import random
import math
import numpy as np
import time

clock = pygame.time.Clock()
pygame.init()
size = (width, height) = (600,500)
screen = pygame.display.set_mode(size)

def rotate(vector,theta):
    #rotate the vector by theta radians around the x-axis
    Vx,Vy = vector[0],vector[1]
    cos,sin = math.cos(theta),math.sin(theta)
    newX,newY = Vx*cos-Vy*sin, Vy*cos+Vx*sin #the newX axis is the result of rotating x axis by theta
    return [newX,newY]

class pendulum:
    def __init__(self,x,y,x0,y0):
        self.x = x
        self.y = y
        self.x0 = x0
        self.y0 = y0
        self.velocity = [0,0]
        self.a= [0,0]
        self.angle = 0
    def CalcForce(self):
        self.angle = math.atan2(-(self.y-self.y0),self.x-self.x0)
        gravity = rotate(g,self.angle)
        self.a[1]=gravity[1]
        self.a[0] = 0 #This component is cancelled by the tension
        self.a = rotate(self.a,-self.angle)
    def move(self):
       #print pylab.dot(self.velocity,[self.x-self.x0,self.y-self.y0])
        self.velocity[0]+=self.a[0]
        self.velocity[1]+=self.a[1]
        self.x+=self.velocity[0]
        self.y+=self.velocity[1]
    def draw(self):
        pygame.draw.circle(screen, (0,0,0), (self.x0,self.y0), 5)
        pygame.draw.line(screen, (0,0,0), (self.x0,self.y0), (int(self.x), int(self.y)),3)
        pygame.draw.circle(screen, (0,0,255), (int(self.x),int(self.y)), 14,0)
g = [0,0.4]
p = pendulum(350,100,300,20)

while 1:
    screen.fill((255,255,255))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    p.CalcForce()
    p.move()
    p.draw()
    clock.tick(60)
    pygame.display.flip()

谢谢。

2 个答案:

答案 0 :(得分:2)

这里有很多问题。我会解决几个问题并为你留下一些。

我修正的是:1)因为你已经导入了numpy,你应该使用它,并根据向量写东西; 2)写一切并让它立即起作用是对你自己的无理要求;所以你需要绘制中间结果等,就像我在这里绘制a一样,你可以看出它是否有意义; 3)你的整个“轮换”方式令人困惑;改为考虑组成部分;我在这里直接计算(它更短,更容易阅读和理解等); 4)在你使用时间步的所有模拟中,你应该明确地使用dt,这样你就可以改变时间步而不改变其他参数。

现在,如果你看它,你会发现它看起来几乎是合理的。请注意,虽然加速度从不向上,所以球在振荡时会下降。这样做的原因是你没有将绳索的张力包含在球上的力量中。我会把那部分留给你。

import pygame
import math
import numpy as np

clock = pygame.time.Clock()
pygame.init()
size = (width, height) = (600,500)
screen = pygame.display.set_mode(size)


class pendulum:
    def __init__(self,x,y,x0,y0):
        self.x0 = np.array((x0, y0))
        self.x = np.array((x, y), dtype=float)
        self.v = np.zeros((2,), dtype=float)
        self.a = np.zeros((2,), dtype=float)
    def CalcForce(self):
        dx = self.x0 - self.x
        angle = math.atan2(-dx[0], dx[1])
        a = g[1]*math.sin(angle)  # tangential accelation due to gravity
        self.a[0] = at*math.cos(angle)
        self.a[1] = at*math.sin(angle)
    def move(self):
        #print np.dot(self.a, self.x-self.x0) #is a perp to string?
        self.x += dt*self.v
        self.v += dt*self.a
    def draw(self):
        pygame.draw.circle(screen, (0,0,0), self.x0, 5)
        pygame.draw.line(screen, (0,0,0), self.x0, self.x.astype(int),3)
        pygame.draw.circle(screen, (0,0,255), self.x.astype(int), 14,0)
        pygame.draw.line(screen, (255, 0, 0), (self.x+200*self.a).astype(int), self.x.astype(int), 4)
dt = .001
g = [0,0.4]
p = pendulum(350,100,300,20)

while 1:
    screen.fill((255,255,255))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    for i in range(100): # don't plot every timestep
        p.CalcForce()
        p.move()
        p.draw()
    clock.tick(60)
    pygame.display.flip()

答案 1 :(得分:1)

如果你想进行模拟,我认为你正在努力做到这一点。我从动作方程开始,参见Equation 20,这里。点意味着相对于时间的区别 - 所以方程是d^2/dt^2 \theta = ...那么你应该在时间方向上实现有限差分方案,并逐步实现。在每个步骤(带有i的标签),您可以根据摆锤的长度和\theta_i计算出摆锤的x和y坐标。查看有限差异的wiki article