I am trying to move a rectangle to a specific point with a specific speed.
however it only works properly if x,y (the point i am trying to move it to) are the same. otherwise, if x was bigger than y it would move at a 45 degree angle until self.location[1]==y(it reached where it needed to be for y), then it would move in a straight for x and vise versa.
i know that i would have to change speed_y so that it was slower. how do i work out what speed i need y to be at in order to get the rectangle to move to location in a straight line no matter what location is?
full function:
def move(self,x,y,speed):
if speed==0:
self.location=[x,y]
else:
speed_x = speed
speed_y = speed
if x > 0 and not self.location[0]==x: # x is positive and not already where it needs to be
if not x == y:
speed_x = something # need to slow down the speed so that it gets to y as the same time as it gets to x
self.speed_x=speed_x
else: self.speed_x=0
if y > 0 and not self.location[1]==y: # same for y
if not x == y:
speed_y = something # need to slow down the speed so that it gets to y as the same time as it gets to x
self.speed_y=speed_y
else: self.speed_y=0
You should set your speeds to a ratio of required distance for each axis.
e.g. if distance to x is half distance to y then speed_x should be half speed_y.
Further example as requested:
distance_x = x-self.location[0]
distance_y = y-self.location[1]
if abs(distance_x) < abs(distance_y):
ratio = distance_x/abs(distance_y)
speed_x = ratio * speed
edit: Reworked the directions of example.
I don't quite get what you are asking for, but see if this helps you:
speed_x = speed*(cos(atan2((y-self.location[1]), (x-self.location[0]))))
speed_y = speed*(sin(atan2((y-self.location[1]), (x-self.location[0]))))
That would "slow" the speed you are given by splitting it in the values needed to get to where you want your box to be at the same time.
Pardon any errors in my english/python, im not native on either one :)
You need mathematical calculations to get new position in every move.
Related
I'm simulating the projectile motions with python on Spyder, one with air resistance and one without it. I started off by following this exercise.
It has guided me to do the projectile motion with air resistance. In order to make a comparison, I tried to plot a projectile motion without air resistance in the same graph with the same parameters on my own. However, the resulted graph has come as a surprise for me as it looks a bit off. There's a big difference between this two lines in terms of the range as the other similar graphs I've come across online only show a slight difference.
My graph:
(the smaller one is the one with drag while the bigger one is the one without it)
My questions are :
What caused this? Is it because the drag force I've put only includes drag coefficient and velocity, which made the graph smaller than the one that also has cross sectional area and rho included?
Is the way I'm doing it very complicated and how would you modify it to make it more efficient and neat?
It would be awesome if you're also willing to point out any errors I've made
import numpy as np
import matplotlib.pyplot as plt
# Model parameters
M = 1.0 # Mass of projectile in kg
g = 9.8 # Acceleration due to gravity (m/s^2)
V = 80 # Initial velocity in m/s
ang = 60.0 # Angle of initial velocity in degree
Cd = 0.005 # Drag coefficient
dt = 0.5 # time step in s
# Set up the lists to store variables
# Start by putting the initial velocities at t=0
t = [0] # list to keep track of time
vx = [V*np.cos(ang/180*np.pi)] # list for velocity x and y components
vy = [V*np.sin(ang/180*np.pi)]
# parameters for the projectile motion without drag force
t1=0
vx_nodrag=V*np.cos(ang/180*np.pi)
vy_nodrag=V*np.sin(ang/180*np.pi)
while (t1 < 100):
x_nodrag=vx_nodrag*t1
y_nodrag=vy_nodrag*t1+(0.5*-9.8*t1**2)
plt.ylim([0,500])
plt.xlim([0,570])
plt.scatter(x_nodrag, y_nodrag)
print(x_nodrag,y_nodrag)
t1=t1+dt
# Drag force
drag = Cd*V**2 # drag force
# Create the lists for acceleration components
ax = [-(drag*np.cos(ang/180*np.pi))/M]
ay = [-g-(drag*np.sin(ang/180*np.pi)/M)]
# Use Euler method to update variables
counter = 0
while (counter < 100):
t.append(t[counter]+dt) # increment by dt and add to the list of time
vx.append(vx[counter]+dt*ax[counter])
vy.append(vy[counter]+dt*ay[counter])
# With the new velocity calculate the drag force
vel = np.sqrt(vx[counter+1]**2 + vy[counter+1]**2)
drag = Cd*vel**2
ax.append(-(drag*np.cos(ang/180*np.pi))/M)
ay.append(-g-(drag*np.sin(ang/180*np.pi)/M))
# Increment the counter by 1
counter = counter +1
x=[0]#creating a list for x
y=[0]#creating a list for y
counter1=0
while (counter1<50):
#t.append(t[counter1]+dt),t already has a list.
x.append(x[counter1]+dt*vx[counter1])
y.append(y[counter1]+dt*vy[counter1])
plt.ylim([0,500])
plt.xlim([0,570])
plt.plot(x,y)
#print(x,y)
counter1=1+counter1
# Let's plot the trajectory
plt.plot(x,y,'ro')
plt.ylabel("height")
plt.xlabel("range")
print("Range of projectile is {:3.1f} m".format(x[counter]))
Yes you definitely have to include all parameters of the projectile as the total distance traveled is very sensitive to that.
Generally try to work with functions you can then call in for loops. This makes your code easier to read and more reusable. Implement for example a function which calculates a new time step. Also work with numpy arrays as vectors instead of having vx and vy separately.
Additionally there is an error in your calculations. When calculating the new velocities you also have to compute an updated angle. This is because in every step you have new angle (i.e. in the end of the flight the drag force in y direction has an opposite sign than gravity)
Do for example:
vel = np.sqrt(vx[counter+1]**2 + vy[counter+1]**2)
drag = Cd*vel**2
# calculate new angle for force separation
a, v = np.array([1,0]), np.array([vx[counter+1], vy[counter+1]])
if vy[counter+1] > 0:
ang = np.arccos( np.dot(a,v) / (1*np.linalg.norm(v)) )
else:
ang = 2*np.pi - np.arccos( np.dot(a,v) / (1*np.linalg.norm(v)) )
ax.append(-(drag*np.cos(ang))/M)
ay.append(-g-(drag*np.sin(ang)/M))
So I'm making a game and I've got some help from another post to make bullets fly towards the mouse cursor. The original person who showed me this did explain it to me and I have a rough idea what it does but I didn't quite understand it. So I'm posting here for further explanation.
def Shoot(self):
pos = self.rect.centerx, self.rect.centery
mpos = py.mouse.get_pos()
direction = py.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])
direction.scale_to_length(10)
return Bullet(pos[0], pos[1], round(direction[0]), round(direction[1]))
Edit: well I know what it does I just don't how I do it. I know It allows for projectiles to a fly towards the mouse even on diagonals but I don't know how it does it.
Whats happening is your getting the position of the cube/player with pos.
mpos is the mouse position on the screen
direction gets the direction between the player and the mouse. for example it the direction could be 10 pixels down and 100 pixels to the right.
The next line scales the direction down to 10, so instead of moving 100 pixels right and 10 down, its close to about 1 down and 10 right (not exactly but pretty close)
The last line creates the bullet with the x position, y position, x speed, y speed. rounding the speed as i said above, its not exactly 1 down and 10 right, it will be some decimal so to make it a nice number, you round it
I've tried to explain that in the answer to your previous question (Im currently making a game with pygame and Ive run into an Issue.), but I'll try it again.
The instruction
direction = py.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])
Computes the distance from the point pos (A) to the point mpos (B) along the x-axis and y-axis. Such a tuple of axis aligned distances is called Vector:
At this point the Euclidean distance from point A to point B is unknown.
In the following the vector is scaled to a length of 10, by the operation pygame.math.Vector2.scale_to_length:
direction.scale_to_length(10)
That means that the x and y component of the vector is changed in that way (xd, yd), that the Euclidean length of the vector is 10 (d = 10):
If the components of the vector are added to the components of the point A, once per frame, then the point A steps towards the point B (A1, A2, ...):
I've been trying to make a code with pygame to simulate simple gravity. At the moment, there is only one object (HOM) which is orbiting the sun. However, for reasons unknown to me, whenever I run the code, HOM travels round the sun in an orbit at the start, but then accelerates away from the sun when it reaches ~135 degrees from vertical.
Does anyone know why this is happening and how I can fix it? I have been printing some variables to try and source the problem, but have had no luck so far.
Code:
import pygame,sys,time
from math import *
screen=pygame.display.set_mode((800,600))
G = 5
class Object: #Just an object, like a moon or planet
def __init__(self,mass,init_cds,init_vel,orbit_obj='Sun'):
self.mass = mass
self.cds = init_cds
self.velocity = init_vel
self.accel = [0,0]
self.angle = 0
self.orb_obj = orbit_obj
def display(self):
int_cds = (round(self.cds[0]),round(self.cds[1]))#Stores its co-ordinates as floats, has to convert to integers for draw function
pygame.draw.circle(screen,(255,0,0),int_cds,10)
def calc_gravity(self):
if self.orb_obj == 'Sun':
c_x,c_y = 400,300
c_mass = 10000
else:
c_x,c_y = self.orb_obj.cds
c_mass = self.orb_obj.mass
d_x = self.cds[0]-c_x
d_y = self.cds[1]-c_y
dist = sqrt(d_x**2+d_y**2) #Find direct distance
angle = atan(d_x/d_y) #Find angle
print(d_x,d_y)
print(dist,degrees(angle))
if dist == 0:
acc = 0
else:
acc = G*c_mass/(dist**2) #F=G(Mm)/r^2, a=F/m -> a=GM/r^2
print(acc)
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
self.accel = [acc_x,acc_y]
print(self.accel)
self.velocity = [self.velocity[0]+self.accel[0],self.velocity[1]+self.accel[1]] #Add acceleration to velocity
print(self.velocity)
self.cds = (self.cds[0]+self.velocity[0],self.cds[1]+self.velocity[1]) #Change co-ordinates by velocity
print(self.cds)
print('-------------------') #For seperating each run of the function when printing variables
HOM = Object(1000000,(400,100),[10,0]) #The problem planet
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
pygame.draw.circle(screen,(255,255,0),(400,300),25)
HOM.display()
HOM.calc_gravity()
clock.tick(30)
pygame.display.flip()
Your main issue has to do with this line:
angle = atan(d_x/d_y) #Find angle
The atan function is very limited in its ability to compute angles because it can't tell the signs of the coordinates you combined in your division. For instance, it will give the same result for atan(1/1) and atan(-1/-1), since both divisions compute the same slope (1).
Instead you should use atan2, and pass the coordinates separately. Since this will let the code see both coordinates, it can pick an angle in the right quadrant of the circle every time.
But there's an even better fix. Instead of computing an angle and then immediately converting it back to a unit vector (by calling sin and cos on it), why not compute the unit vector directly? You already have the original vector's length! Instead of:
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
Use:
acc_x = acc * d_x / distance
acc_y = acc * d_y / distance
The d_x / distance and d_y / distance values are the same as the sin and cos values you were getting before (for the angles when they were working correctly), but there's no need for the trigonometry. You can get rid of the line I quoted up top completely!
Note that you might need to reverse the way you're computing d_x and d_y, so that you get a vector that points from the orbiting object towards the object it's orbiting around (instead of pointing the other way, from the center of the orbit towards the orbiting object). I'm not sure if I'm reading your code correctly, but it looks to me like you have it the other way around right now. That means that you were actually getting the wrong results from atan in the cases where your current code was working the way you expected, and the bad behavior (flying off into nowhere) is the code working "correctly" (from a mathematical point of view). Alternatively, you could compute acc to be negative, rather than positive.
As several commenters mentioned, you may have other issues related to your choice of integration algorithm, but those errors are not going to be as large as the main issue with the acceleration angle. They'll crop up as you run your simulation over longer time periods (and try to use larger time steps to make the simulation go faster). Your current algorithm is good enough for an orbit or two, but if you're simulating dozens or hundreds of orbits, you'll start seeing errors accumulate and so you should pick a better integrator.
I need to decrease the amount of red in a pixel by 50% on the top half of the image and then increase bottom half by 50%
def changeRed():
setMediaPath("/Users/addison/Downloads/Cmpt101_Pics/Learjet31A.jpg")
filename1 = "/Users/addison/Downloads/Cmpt101_Pics/Learjet31A.jpg"
source = makePicture(filename1)
halfWidth = getWidth(source)/2
for y in range(0,getHeight(source)):
for x in range(0, halfWidth):
pixel = getPixel(source, x, y)
value = getRed(pixel)
setRed(pixel, value-127.5)
show(source)
This is what I have right now, I'm not sure how to change it to make it halved on the X axis instead of the Y axis which it currently does right now..I have attempted to look everywhere for the solution but can't seem to find anything...Help Please
All you need to do is swap the x and y in the for loops. For example
for x in range(0, getWidth(source)):
for y in range(0, halfHeight):
# Do stuff
I'm working on a particle filter for an autonomous robot right now, and am having trouble producing expected distance measurements by which to filter the particles. I have an image that I'm using as a map. Each pixel represents a certain scaled area in the enviroment. Space the robot can occupy is white, walls are black, and areas that are exterior to the enviroment are grey.
If you are unfamiliar with what a particle filter is, my python code will create a predetermined number of random guesses as to where it might be (x,y,theta) in the white space. It will then measure the distance to the nearest wall with ultrasonic sensors at several angles. The script will compare these measurements with the measurements that would have been expected at each angle for each guessed location/orientation. Those that most closely match the actual measurements will survive while guesses that are less likely to be right will be eliminated.
My problem is finding the nearest wall AT a given angle. Say the sensor is measuring at 60°. For each guess, I need to adjust the angle to account for the guessed robot orientation, and then measure the distance to the wall at that angle. It's easy enough find the nearest wall in the x direction:
from PIL import Image
#from matplotlib._png import read_png
from matplotlib.pyplot import *
mapp = Image.open("Map.png")
pixels = mapp.load()
width = mapp.size[0]
height = mapp.size[1]
imshow(mapp)
pixelWidth = 5
for x in range(width):
if mapp.getpixel((x, 100)) == (0,0,0,255): #Identify the first black pixel
distance = x*pixelWidth self.x
The problem is that I can't tell the script to search one pixel at a time going at a 60°, or 23°, or whatever angle. Right now the best thing I can think of is to go in the x direction first, find a black pixel, and then use the tangent of the angle to determine how many pixels I need to move up or down, but there are obvious problems with this, mostly having to do with corners, and I can't imagine how many if statements it's going to take to work around it. Is there another solution?
Okay, I think I found a good approximation of what I'm trying to do, though I'd still like to hear if anyone else has a better solution. By checking the tangent of the angle I've actually traveled so far between each pixel move, I can decide whether to move one pixel in the x-direction, or in the y-direction.
for i in range(len(angles)):
angle = self.orientation+angles[i]
if angle > 360:
angle -= 360
x = self.x
y = self.y
x1 = x
y1 = y
xtoy_ratio = tan(angle*math.pi/180)
if angle < 90:
xadd = 1
yadd = 1
elif 90 < angle < 180:
xadd = -1
yadd = 1
elif 180 < angle < 270:
xadd = -1
yadd = -1
else:
xadd = 1
yadd = -1
while mapp.getpixel(x,y) != (0,0,0,255):
if (y-y1)/(x-x1) < xtoy_ratio:
y += yadd
else:
x += xadd
distance = sqrt((y-y1)^2+(x-x1)^2)*pixel_width
The accuracy of this method of course depends a great deal on the actual length represented by each pixel. As long as pixel_width is small, accuracy will be pretty good, but if not, it will generally go pretty far before correcting itself.
As I said, I welcome other answers.
Thanks