Modelling two particles under gravity - python

I have some code and I think it mostly works. I am supposed to be modelling the way two particles interact under gravity in 2D. It works until the second loop over the update function, so I think there is something wrong with the update function but I can't see it.
from __future__ import print_function, division
import numpy as np # imports the scientific computing package
G = 10 # of course G is not really 10 but this make it easier to track the accuracy of calculations.
t=5 # defines the timestep
class Particle:
def __init__(self,mass):
self.mass = mass # gives the particle mass
self.position = np.array([0,0],int) # gives the particle a position defined by a vector. Therefore all answers will now be given as vectors.
def calculateForce(self,other):
'''
this function calculates the force acting on the particles
'''
force = (G*self.mass*other.mass)/(other.position - self.position)**2
print('The force acting on the particles is', force)
return(force)
def calculateAcceleration(self,force):
'''
calculates the acceleration of the first particle
'''
acceleration = (force/self.mass)
print('Acceleration of particle is', acceleration)
return acceleration
def calculateAcceleration1(other, force):
'''
calculates the acceleration of the second particle
'''
acceleration1 = (force/other.mass)
print('Acceleration of particle1 is', acceleration1)
return acceleration1
def calculateVelocity(self, acceleration):
'''
calculates the velocity of the first particle
'''
velocity = (acceleration*t)
print('Velocity of particle is', velocity)
return velocity
def calculateVelocity1(self, acceleration1):
'''
calculates the velocity of the second particle
'''
velocity1 = (acceleration1*t)
print('Velocity of particle1 is', velocity1)
return velocity1
def calculatePosition(self, velocity):
'''
calculates the position of the first particle
'''
position = (velocity*t)
print('Position of particle is', position)
return position
def calculatePosition1(self,velocity1):
'''
calculates the position of the second particle
'''
position1 = (velocity1*t)
print('Position of particle1 is', position1)
return position1
def update(self,other):
# for x in range(0,1):
self.position = position
other.position = position1
print(self.position)
print(other.position)
return self.position
return other.position
def repeat(self,other):
for x in range(0,5):
force = p.calculateForce(p1)
acceleration = p.calculateAcceleration(force)
acceleration1 = p1.calculateAcceleration1(force)
velocity = p.calculateVelocity(acceleration)
velocity1 = p1.calculateVelocity1(acceleration1)
position = p.calculatePosition(velocity)
position1 = p1.calculatePosition1(velocity1)
p.update(p1)
p = Particle(10) # gives first particle a mass of 10
p1 = Particle(20) # gives second particle a mass of 20
p1.position[0] = 5 # x coordinate of second particle is 5
p1.position[1] = 5 # y coordinate of second particle is 5
print(p1.position)
force = p.calculateForce(p1)
acceleration = p.calculateAcceleration(force)
acceleration1 = p1.calculateAcceleration1(force)
velocity = p.calculateVelocity(acceleration)
velocity1 = p1.calculateVelocity1(acceleration1)
position = p.calculatePosition(velocity)
position1 = p1.calculatePosition1(velocity1)
p.update(p1)
p.repeat(p1)

Your second return is unreachable in update:
def update(self,other):
# for x in range(0,1):
self.position = position
other.position = position1
print(self.position)
print(other.position)
return self.position
return other.position <- unreachable
If you wanted to return two values you could return a tuple of both and change your code appropriately to use the values:
return self.position, other.position
If either one or the other should be returned then you need to add some logic.

Related

Does the results indicate that the list(position) is not updated or there is something wrong with the equation?

I am writing a code to update a position of a ball after it being kicked at a given angle and velocity after a certain time passed. Does the results indicate that the list(position) is not updated or there is something wrong with the equation?
import numpy as np
class Ball():
def __init__(self, theta, v):
self.position = [0, 0] # Position at ground is (0,0)
self.theta = 0
self.v = 0
def step(self, delta_t = .1):
ball.position[0] = ball.v*np.cos(ball.theta)*t
ball.position[1] = (ball.v**2*np.sin(ball.theta))/9.81
return ball.position
ball = Ball(theta = 30, v = 100)
for t in range(200):
ball.step(delta_t = 0.05)
print(f'Ball is at x={ball.position[0]:.2f}m, y={ball.position[1]:.2f}m') # Check position
Output =
Ball is at x=0.00m, y=0.00m
There were a few bugs in your code, I've altered it to do what I believe you intend it to do below:
import numpy as np
class Ball():
def __init__(self, theta, v):
self.position = [0, 0] # Position at ground is (0,0)
self.theta = theta
self.v = v
def step(self, delta_t = .1):
self.position[0] = self.v*np.cos(self.theta)*delta_t
self.position[1] = (self.v**2*np.sin(self.theta))/9.81
return self.position
ball = Ball(theta = 30, v = 100)
for t in range(200):
ball.step(delta_t = 0.05)
print(f'Ball is at x={ball.position[0]:.2f}m, y={ball.position[1]:.2f}m') # Check position
With the result:
Ball is at x=0.77m, y=-1007.17m
Explanation
First, your __init__ method simply set self.theta and self.v to 0, instead of setting them equal to the values provided when initialising the ball object.
Second, your step method relied upon something called t, instead of delta_t which is what you actually rely upon in your defintion of step.
Finally, in your step method, you seem to be relying on ball.XYZ instead of self.XYZ.
Correcting all of these, I believe, makes the class function as you wish it to - whether the calculation is correct, I'll leave to you to determine.
Note
If what you're trying to do is plot the trajectory of a ball in free fall, you should probably update self.v and self.theta with every call of self.step(). Currently, self.step() returns where the ball will be after a given interval of time from its initial condition. If you're trying to step incrementally - with every call of self.step(), the current self.v and current self.theta of the ball will be different after every call of self.step() - but your self.step() definition currently takes no account of this.

2D Pathfinding with variable movement speed?

Hi I'm pretty new to the path-finding field and I have searched all around the interwebs for an answer to my question so I figured its time to ask the experts:
My environment consists of a 2D rectangular map with walls and units to traverse the environment
(no collision between units but they can't go through walls)
Units in the environment move in a turn-based fashion in a straight line but they can move at a speed of
0 to unit.movespeed (e.g. 5)(you have control over the movespeed for every movement). (map size is range is from 20,20 to 200,200 cells but you can move to floating-point locations add walls can be at a location like eg wall[(x=10.75,y=9.34),(x=33.56,y=62.43])
In summery every tick, you tell a unit to move to a destination (x,y) of type float
I have tried A* and goal-based vector pathfinding algorithms but the problem I keep running into is figuring out how fast they should move because obviously, the optimal movespeed should be the maximum speed yet if they always move at the maximum speed they are prone to hit a wall because these algorithms don't take into account variable movement speed.
Any Ideas?
image of movespeed issue with a* star and goal-based vector pathfinding problem
Code:
class Cell:
def __init__(self,coords,vector=None,distance=None,obstacle=False):
self.coords = coords
self.vector = vector
self.distance = distance
self.obstacle = obstacle
class VectorMap:
def __init__(self,unit, dest, map=HardCoded._make_map()):
self.size = Coords(len(map), len(map[0]))
self.map = map
VectorMap.map_converter()
self.unit = unit
self.dest = dest
def _create_vector_map(self):
return self._cell_def(1,self.map[self.dest])
def _cell_def(self,dist,current_cell):
neighbors = [Coords(0, 1), Coords(0, -1), Coords(1, 0), Coords(-1, 0), Coords(1, 1), Coords(1, -1),
Coords(-1, 1), Coords(-1, -1)]
for neighbor in neighbors:
#check if out of range of arr then return map
if current_cell.coords.y + neighbor.y < self.size[1] and current_cell.coords.x + neighbor.x < self.size[0]:
neighbor_cell = self.map[current_cell.coords.x + neighbor.x][current_cell.coords.y + neighbor.y]
if neighbor_cell.obstacle:
continue
neighbor_cell.distance = dist
#neighbor_cell.vector = current_cell
return self._cell_def(self.map,dist+1,neighbor_cell)
def map_converter(self):
nmap = []
for x,element in enumerate(self.map):
tmap = []
for y,value in enumerate(element):
tmap.append(Cell(Coords(x,y),vector=None,distance=None,obstacle=False if value == 0 else True))
nmap.append(tmap)
self.map = nmap
self._create_vector_map()
for x,c in enumerate(self.map):
for y,cell in enumerate(c):
cell.vector = Coords(0,0)
right_tile_distance = (self.map[x+1][y].distance if x+1 < self.size.x else self.map[x][y].distance)
up_tile_distance = (self.map[x][y+1].distance if y+1 < self.size.y else self.map[x][y].distance)
cell.vector.x = (self.map[x-1][y].distance if x is not 0 else self.map[x][y].distance) - right_tile_distance
cell.vector.y = up_tile_distance - (self.map[x][y-1].distance if y is not 0 else self.map[x][y].distance)
if cell.vector.x == 0 and right_tile_distance < self.map[x][y].distance:
cell.vector.x = 1
if cell.vector.y == 0 and up_tile_distance < self.map[x][y].distance:
cell.vector.y = 1
cell.vector = Util.normalize(cell.vector)
def vetor_move(self):
vector = self.map[self.unit.coords.x][self.unit.coords.y].vector
movespeed = self.desired_movespeed()
dest = Coords(vector.x * movespeed,vector.y * movespeed)
return Action(mechanic='move', params=[dest], report='Moving With Maths')
def desired_movespeed(self):
pass
class Util:
#staticmethod
def normalize(vector):
norm=np.linalg.norm(vector,ord=1)
if norm == 0:
norm = np.finfo(vector.dtype).eps
return Coords(*(vector/norm))

Plotting a function in python matplotlib

Here is my code:
import random
import numpy as np
import matplotlib.pyplot as plt
import agent
deer = agent.Agent()
print(deer.x,deer.y)
deer.step()
print(deer.x,deer.y)
def walk(length):
for i in range(length):
#while deer.x <= i:
deer.step()
countx= deer.x + 1
county=deer.y +1
#print(countx,county)
x= countx
y= county
return deer.step()
walk(100)
walker = walk(100)
plt.plot(walk[0],walk[1],label= 'Random walk')
plt.scatter(walk[0],walk[1],s=50,c=range(101))
plt.show()
I'm trying to get the count step of x and y to be graphed.But I don't understand why it wont recognize the x and y values within the function. The goal is to plot the 100 random steps taken.
After there is a plot from the 100 steps taken, I'm trying to create a second Method for taking a step. Instead of choosing a random new angle and moving, the current orientation of the agent should change by a random amount chosen from a normal distribution with mean 0 and standard deviation Sigma=1 (self.orientation = self.orientation + np.random.normal(0,Sigma)).
It's also suppose to walk 100 steps, and show the trajectory using matplolib, and show the distance between the agent and it's starting point.
I used the class Agent:
class Agent:
# initializer with default argument value
def __init__(self,sigma=0.5):
self.x = 0.0 # agent's x position
self.y = 0.0 # agent's y position
self.o = 0.0 # agent's orientation
self.v = 1.0 # agent's velocity
self.sigma = sigma
def step(self):
self.o = random.random()*2*np.pi
self.x +=self.v*np.cos(self.o)
self.y += self.v*np.sin(self.o)
def turn(self):
self.o += np.random.normal(0,self.sigma)
self.x += self.v*np.cos(self.o)
self.y += self.v*np.sin(self.o)
def distance(self):
return np.sqrt(self.x**2 + self.y**2)

Calculating direction after a 2d circle collision

I know how to calculate the scalar of the velocity vector after a collision with 2 circles
(as per this link: https://gamedevelopment.tutsplus.com/tutorials/how-to-create-a-custom-2d-physics-engine-the-basics-and-impulse-resolution--gamedev-6331)
These circles cannot rotate and do not have friction but can have different masses, however I cannot seem to find out any way to find the unit vector that I need to multiply the scalar of velocity by to get the new velocity of the particles after the collision.
I also know how to check if 2 circles are colliding.
Also, I am only dealing with this in a purely "maths-sense" (ie. the circles have a center and a radius), and would like to know how I can represent these circles on the screen in python 3.0.
The vector class:
class Vector():
def __init__(self,x,y):
self.x = x
self.y = y
def add(self, newVector):
return Vector(self.x+newVector.x, self.y+newVector.y)
def subtract(self,newVector):
return Vector(self.x-newVector.x, self.y-newVector.y)
def equals(self, newVector):
return Vector(newVector.x,newVector.y)
def scalarMult(self, scalar):
return Vector(self.x*scalar, self.y*scalar)
def dotProduct(self, newVector):
return (self.x*newVector.x)+(self.y*newVector.y
def distance(self):
return math.sqrt((self.x)**2 +(self.y)**2)
The circle class:
class Particles():
def __init__(self,currentPos, oldPos, accel, dt,mass, center, radius):
self.currentPos = currentPos
self.oldPos = oldPos
self.accel = accel
self.dt = dt
self.mass = mass
self.center = center
self.radius = radius
def doVerletPosition(currentPos, oldPos, accel, dt):
a = currentPos.subtract(oldPos)
b = currentPos.add(a)
c = accel.scalarMult(dt)
d = c.scalarMult(dt)
return d.add(b)
def doVerletVelocity(currentPos, oldPos, dt):
deltaD = (currentPos.subtract(oldPos))
return deltaD.scalarMult(1/dt)
def collisionDetection(self, center, radius):
xCenter = (self.radius).xComponent()
yCenter = (self.radius).yComponent()
xOther = radius.xComponent()
yOther = radius.yComponent()
if ((xCenter - xOther)**2 + (yCenter-yOther)**2 < (self.radius + radius)**2):
return True
else:
return False
I do know about AABBs, but I am only using around 10 particles for now, and AABBs are not necessary now.
You know that the force transmitted between the two discs has to go along the "normal vector" for this collision, which is easy to get - it's just the vector along the line connecting the centers of the two discs.
You have four constraints: conservation of momentum (which counts for two constraints since it applies in x and y), conservation of energy, and this "force along normal" constraint. And you have four unknowns, namely the x and y components of the final velocities. Four equations and four unknowns, you can solve for your answer. Due to my background in physics, I've written this out in terms of momentum instead of velocity, but hopefully that's not too hard to parse. (Note, for instance, that kinetic energy is equal to p**2/2m or 1/2 mv**2.)
## conservation of momentum
p_1_x_i + p_2_x_i = p_1_x_f + p_2_x_f ## p_1_x_i := momentum of disc _1_ in _x_ axis intially _i
p_1_y_i + p_2_x_i = p_1_y_f + p_2_y_f
## conservation of energy
(p_1_x_i**2 + p_1_y_i**2)/(2*m_1) + (p_2_x_i**2 + p_2_y_i**2)/(2*m_2) = (p_1_x_f**2 + p_1_y_f**2)/(2*m_1) + (p_2_x_f**2 + p_2_y_f**2)/(2*m_2)
## impulse/force goes along the normal vector
tan(th) := (x_2-x_1)/(y_2-y_1) # tangent of the angle of the collision
j_1_x := p_1_x_i - p_1_x_f # change in momentum aka impulse
j_1_y := p_1_y_i - p_1_y_f
tan(th) = -j_1_x/j_1_y
(I hope the notation is clear. It would be much clearer if I could use latex, but stackoverflow doesn't support it.)
Hope this helps!

new position of creature without using a library

Given the position of the creature, and the position of it's target, I'm trying to calculate the path to follow, limited by movement and rotation speed.
The creature has a look-orientation, and can only move forward along that orientation. It can not sidestep.
I am able to calculate the angle of difference between the creature's look-orientation and the target's position with the following code
def calculateAngleDifference(self):
deltaX = self.position_x - self.target[0]
deltaY = self.position_y - self.target[1]
try:
#atan is inverse tan
delta = degrees(atan(deltaY / deltaX)) - self.orientation
except:
delta = degrees(atan(deltaY / 1)) - self.orientation
return delta//1
this gives me a correct result, and stops when the creature is looking directly to the target.
However, I would like to move it during the same time, and thus the creature should walk a curve. It is impossible to have a target that is more than 45° different.
for the position calculation I use
def pointOnCircle(origin, distance=50, degree = 45, method=cos):
return origin + distance * method(radians(degree))
newX = pointOnCircle(self.position_x, distance=1, degree = 1, method=cos)
newY = pointOnCircle(self.position_y, distance=1, degree = 1, method=sin)
# input()
self.position_x = newX
self.position_y = newY
However, this never gets me to the target.

Categories