Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding gravity to a bouncing ball using vectors

I have a gravity vector (in the form [r, theta]) which I add to my ball's velocity vector. For some reason, the ball doesn't return to the same height after bouncing, but instead slowly loses height sporadically. I am guessing there's some rounding error or something in a calculation I'm using, but I can't isolate the issue.

Here is my code. You need both files and pygame to run it. Sorry if it's a little confusing. I can comment anything some more if you want.

I added a marker whenever the ball reaches its max height so you guys what I mean. I want the ball to return to exactly the same height every time it bounces.

I took a little bit of unnecessary code out. The full program is under the pastebin links.


https://pastebin.com/FyejMCmg - PhysicsSim

import pygame, sys, math, tools, random, time
from pygame.locals import *

clock = pygame.time.Clock()
lines = []

class Particle:
    def __init__(self,screen,colour, mass, loc, vel):
        self.screen = screen
        self.colour = colour

        self.mass = mass

        self.x = loc[0]
        self.y = loc[1]
        self.location = self.x,self.y
        self.speed = vel[0]
        self.angle = vel[1]


    def update(self):
        global lines

        # add gravity
        self.speed,self.angle = tools.add_vectors2([self.speed,self.angle], tools.GRAVITY)

        # update position
        dt = clock.tick(60)
        self.x += self.speed * tools.SCALE * math.cos(self.angle) * dt
        self.y -= self.speed * tools.SCALE * math.sin(self.angle) * dt
        self.location = int(self.x),int(self.y)

        # border checking
        do = False
        n=[]
        if ((self.y+self.mass) > tools.SCREEN_HEIGHT):
            self.y = tools.SCREEN_HEIGHT-self.mass
            n = [0,1]
            do = True

        # adds position to array so max height so max height can be recorded
        if (self.speed==0):
            lines.append([self.screen, self.location, self.mass])

        # bounce
        if do:
            #init, convert everything to cartesian
            v = tools.polarToCartesian([self.speed, self.angle])

            #final -> initial minus twice the projection onto n, where n is the normal to the surface
            a = tools.scalarP(2*abs(tools.dotP(v,n)),n) #vector to be added to v
            v = tools.add_vectors(v,a)

            self.angle = tools.cartesianToPolar(v)[1] # does not set magnitude


        # drawing
        pygame.draw.circle(self.screen, self.colour, self.location, self.mass, 0)

# draws max height line
def draw_line(l):
    screen = l[0]
    location = l[1]
    radius = l[2]
    pygame.draw.line(screen, tools.BLACK, [location[0] + 15, location[1]-radius],[location[0] - 15, location[1]-radius])

def main():
    pygame.init()

    DISPLAY = pygame.display.set_mode(tools.SCREEN_SIZE,0,32)
    DISPLAY.fill(tools.WHITE)

    particles = []
    particles.append(Particle(DISPLAY, tools.GREEN, 10, [100,100], [0,0]))

    done = False
    while not done:
        global lines
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        DISPLAY.fill(tools.WHITE)
        for i in particles:
            i.update()

        for l in lines:
            draw_line(l)

        pygame.display.update()

main()

https://pastebin.com/Epgqka31 - tools

import math

#colours
WHITE =     (255, 255, 255)
BLUE =      (  0,   0, 255)
GREEN =     (  0, 255,   0)
RED =       ( 255,  0,   0)
BLACK =     (   0,  0,   0)

COLOURS = [WHITE,BLUE,GREEN,RED,BLACK]

#screen
SCREEN_SIZE = SCREEN_WIDTH,SCREEN_HEIGHT = 1000,700

#vectors
GRAVITY = [5.0, 3*math.pi/2] # not 9.8 because it seems too high
SCALE = 0.01

# converts polar coordinates to cartesian coordinates in R2
def polarToCartesian(v):
    return [v[0]*math.cos(v[1]), v[0]*math.sin(v[1])]

# converts cartesian coordinates to polar coordinates in R2
def cartesianToPolar(v):
    return [math.sqrt(v[0]**2 + v[1]**2), math.atan2(v[1],v[0])]

# dots two cartesian vectors in R2
def dotP(v1, v2):
    return v1[0]*v2[0] + v1[1]*v2[1]

# multiplies cartesian vector v by scalar s in Rn
def scalarP(s,v):
    v_=[]
    for i in v:
        v_.append(s*i)
    return v_

# returns the sum of two cartesian vectors in R2
def add_vectors(v1, v2):
    return [v1[0]+v2[0], v1[1]+v2[1]]

# returns the sum of two polar vectors in R2, equations from https://math.stackexchange.com/questions/1365622/adding-two-polar-vectors
def add_vectors2(v1,v2):
    r1,r2,t1,t2 = v1[0],v2[0],v1[1],v2[1]
    return [math.sqrt(r1**2 + r2**2 + 2*r1*r2*math.cos(t2-t1)), t1 + math.atan2(r2*math.sin(t2 - t1), r1 + r2*math.cos(t2 - t1))]
like image 526
Alexander Avatar asked May 01 '17 23:05

Alexander


People also ask

How does gravity affect the bounce of a ball?

When a ball is dropped gravity pulls the ball toward the ground, slowing the ball down so that each bounce is shorter and shorter, until eventually the ball stops bouncing. The force of the ball hitting the hard ground puts an equal force back onto the ball, meaning it bounces back up.

What forces are acting on a bouncing ball?

The forces acting on a spinning ball during impact are the force of gravity, the normal force, and the force of friction (which has in general both a 'translational' and a 'rotational' component).

Does a bouncing ball have gravitational potential energy?

Now, this energy can be released in your muscles as you lift the ball up, with the energy still around as the gravitational potential energy of the ball. When we drop the ball, the gravitational potential energy of the ball is transformed into kinetic energy (movement energy) on the way down.


Video Answer


1 Answers

Your time interval, dt = clock.tick(60), is not a constant. If you change it to dt = 60 your program runs as expected.

Have a look a the Verlet Algorithm and implement it in your code. You are on the right track!

like image 136
tpvasconcelos Avatar answered Oct 20 '22 08:10

tpvasconcelos