I'm trying to create a lookAt function in 2 dimensions using Python, so here's my code right now.
from math import *
def lookAt(segment, originPoint):
segmentCenterPoint = getSegmentCenter(segment)
for i in range(2):
vtx = getVertex(segment, i)
x, y = getVertexCoord(vtx)
# Calculate the rotation angle already applied on the polygon
offsetAngle = atan2(y - segmentCenterPoint.y, x - segmentCenterPoint.x)
# Calculate the rotation angle to orient the polygon to an origin point
orientAngle = atan2(segmentCenterPoint.y - originPoint.y, segmentCenterPoint.x - originPoint.x)
# Get the final angle
finalAngle = orientAngle - (pi / 2)
if offsetAngle >= pi:
offsetAngle -= pi
elif offsetAngle < 0:
offsetAngle += pi
finalAngle += offsetAngle
# Temporary move the point to have its rotation pivot to (0,0)
tempX = x - segmentCenterPoint.x
tempY = y - segmentCenterPoint.y
# Calculate coords of the point with the rotation applied
s = sin(finalAngle)
c = cos(finalAngle)
newX = tempX * c - tempY * s
newY = tempX * s + tempY * c
# Move the point to the initial pivot
x = newX + segmentCenterPoint.x
y = newY + segmentCenterPoint.y
# Apply new coords to the vertex
setVertexCoord(vtx, x, y)
I tried some examples manually and worked well, but when I tried to apply the function on thousands of segments, it seems some segment are not well oriented.
I probably missed something but i don't know what it is. Also, maybe there's a faster way to calculate it ?
Thank you for your help.
EDIT
Here is a visualization to understand better the goal of the lookAt.
The goal is to find A' and B' coordinates, assuming we already know O, A and B ones. ([AB] is the segment we need to orient perpendicularly to the point O)
To find positions of A' and B', you don't need rotate points (and dealt with angles at all).
Find vector OC = segmentCenterPoint - originPoint
Make normalized (unit) vector oc = OC / Length(OC)
Make perpendicular vector P = (-oc.Y, oc.X)
Find CB length lCB
Find A' = C + lCB * P
B' = C - lCB * P
Related
Iv'e been trying lately to calculate a point an ellipse
The desired point is the green point , knowing the red dots
and the ellipse equation.
I've used numpy linspace to create an array on points
and iterate them using zip(x axis , y axis)
between the red points , and using the ellipse
equation figure which of the points is the closest to 1.
(which is the outcome of the ellipse equation ).
this concept works most of the time , but in some location
of the red outer dot , this method doesn't seem to give good outcome
long story short, any idea how to calculate the green dot in python?
p.s - ellipse might have angle, both of hes axis are known.
I end up using the ellipse equation from this answer:
and created an in_ellipse function
then Iv'e used the Intermediate value theorem , to get a good estimation
of the point
def in_ellipse(point, ellipse):
return true if point in ellipse
return false
dot_a = ellipse_center
dot_b = dot
for i in range(20):
center_point = ((dot_b.y - dot_a.y)/2, (dot_b.x - dot_a.x)/2)
if in_ellipse(center_point):
dot_a = center_point
else:
dot_b = center_point
return center_point
this system gives the point in 7 (2^20) digits resolution after decimal point
you can increase the range for better resolution.
Let ellipse center is (0,0) (otherwise just subtract center coordinates), semi-axes are a, b and rotation angle is theta. We can build affine tranformation to transform ellipse into circle and apply the same transform to point P.
1) Rotate by -theta
px1 = px * Cos(theta) + py * Sin(theta)
py1 = -px * Sin(theta) + py * Cos(theta)
2) Extend (or shrink) along OY axis by a/b times
px2 = px1
py2 = py1 * a / b
3) Find intersection point
plen = hypot(px2, py2) (length of p2 vector)
if (a > plen), then segment doesn't intersect ellipse - it fully lies inside
ix = a * px2 / plen
iy = a * py2 / plen
4) Make backward shrinking
ix2 = ix
iy2 = iy * b / a
5) Make backward rotation
ixfinal = ix2 * Cos(theta) - iy2 * Sin(theta)
iyfinal = ix2 * Sin(theta) + iy2 * Cos(theta)
I am trying to plot a curved path for a robot to follow using the following as a guide: http://rossum.sourceforge.net/papers/CalculationsForRobotics/CirclePath.htm
The code i have does not create a path that ends at the destination. I am expecting the path to curve left or right depending on the quadrant the destination is in (+x+y,+x-y,-x+y,-x-y)
import math
start = [400,500]
dest = [200,300]
speed = 10
startangle = 0
rc =0
rotv =0
rads =0
def getPos(t):
ang = (rotv*t)+rads
x = start[0] - rc * math.sin(rads) + rc * math.sin(rotv*(t)+rads)
y = start[1] + rc * math.cos(rads) - rc * math.cos(rotv*(t)+rads)
return (int(x),int(y), ang)
dx = dest[0] - start[0]
dy = dest[1] - start[1]
rads = math.atan2(-dy,dx)
rads %= 2*math.pi
distance = (dx**2 + dy**2)**.5 #rg
bangle = 2*rads
rc = distance /(2 * math.sin(rads))
if rads > (math.pi/2):
bangle = 2*(rads-math.pi)
rc= -rc
if rads < -(math.pi/2):
bangle = 2*(rads+math.pi)
rc= -rc
pathlength = rc * bangle
xc = start[0] - rc * math.sin(rads)
yc = start[1] + rc * math.cos(rads)
rotcenter = [xc,yc]
traveltime = pathlength/speed
rotv = bangle/traveltime
for p in range(int(traveltime)):
pos = getPos(p)
Start: Blue, End: Red, Rotation Point: Purple
UPDATE:
I have added code to allow positive and negative x/y values. I have updated the image.
To answer your question I first read through the article you linked. I think it is very interesting and explains the ideas behind the formulas pretty well, althought it lacks the formulas for when the starting position is not at the origin and when the starting angle is not 0.
It took a little while to come up with these formulas, but now it works for every case I could think of. To be able to use the formulas given in the linked article, I used the names of the variables given there. Notice that I also used the notation with t_0 as the starting time, which you just ignored. You can easily remove any instance of t_0 or set t_0 = 0.
The last part of the following code is used for testing and creates a little red turtle that traces the path of the computed arc in the specified direction. The black turtle indicates the goal position. Both turtles are close to each other at the end of the animation, but they are not directly above each other, because I am only iterating over integers and it is possible that t_1 is not an integer.
from math import pi, hypot, sin, cos, atan2, degrees
def norm_angle(a):
# Normalize the angle to be between -pi and pi
return (a+pi)%(2*pi) - pi
# Given values
# named just like in http://rossum.sourceforge.net/papers/CalculationsForRobotics/CirclePath.htm
x_0, y_0 = [400,500] # initial position of robot
theta_0 = -pi/2 # initial orientation of robot
s = 10 # speed of robot
x_1, y_1 = [200,300] # goal position of robot
t_0 = 0 # starting time
# To be computed:
r_G = hypot(x_1 - x_0, y_1 - y_0) # relative polar coordinates of the goal
phi_G = atan2(y_1 - y_0, x_1 - x_0)
phi = 2*norm_angle(phi_G - theta_0) # angle and
r_C = r_G/(2*sin(phi_G - theta_0)) # radius (sometimes negative) of the arc
L = r_C*phi # length of the arc
if phi > pi:
phi -= 2*pi
L = -r_C*phi
elif phi < -pi:
phi += 2*pi
L = -r_C*phi
t_1 = L/s + t_0 # time at which the robot finishes the arc
omega = phi/(t_1 - t_0) # angular velocity
x_C = x_0 - r_C*sin(theta_0) # center of rotation
y_C = y_0 + r_C*cos(theta_0)
def position(t):
x = x_C + r_C*sin(omega*(t - t_0) + theta_0)
y = y_C - r_C*cos(omega*(t - t_0) + theta_0)
return x, y
def orientation(t):
return omega*(t - t_0) + theta_0
#--------------------------------------------
# Just used for debugging
#--------------------------------------------
import turtle
screen = turtle.Screen()
screen.setup(600, 600)
screen.setworldcoordinates(0, 0, 600, 600)
turtle.hideturtle()
turtle.shape("turtle")
turtle.penup()
turtle.goto(x_1, y_1)
turtle.setheading(degrees(orientation(t_1)))
turtle.stamp()
turtle.goto(x_0, y_0)
turtle.color("red")
turtle.showturtle()
turtle.pendown()
for t in range(t_0, int(t_1)+1):
turtle.goto(*position(t))
turtle.setheading(degrees(orientation(t)))
I am not sure at which point your code failed, but I hope this works for you. If you intend to use this snippet multiple times in you code consider encapsulating it in a function that takes in the given values as parameters and returns the position function (and if you like rotation function as well).
I am trying to generate random x and y coordinates within a ring, which has an outer radius of 3.5 and an inner radius of 2. Therefor the following must be true for x and y:
x**2 + y**2 < 12.25 and x**2 + y**2 > 4
I wrote the following function:
def meteorites():
circle = False
while circle == False:
r = np.array([uniform(-6., 6.), uniform(-6., 6.)])
# we will regenerate random numbers untill the coordinates
# are within the ring x^2+y^2 < 3,5^2 and x^2+y^2 > 2^2
if (r[0]**2+r[1]**2 < 12.25) and (r[0]**2+r[1]**2 > 4.):
circle = True
else :
circle = False
return r[0], r[1]
x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
x[i] = meteorites()[0]
y[i] = meteorites()[1]
plt.scatter(x,y)
plt.show()
When I plot the resulting coordinates I get a square from -3.5 to 3.5. I can't seem to find the problem. I'm also not sure if it's a coding error, or some dum math problem. Since you guys are usually good at both, can you see what I'm doing wrong here?
To get uniform distribution of random point in the ring, one should take relative areas of thin circular regions into account. How it works for the circle
For your case generate uniform distribution of SquaredR in range of squared inner and outer radii. Pseudocode:
Fi = RandomUniform(0, 2 * Pi)
SquaredR = RandomUniform(inner*inner, outer*outer)
R = Sqrt(SquaredR)
x,y = R * Cos(Fi), R * Sin(Fi)
Take a random angle and a random distance between the two constraints; you'll need to produce a uniform distribution in a circle:
from math import sin, cos, radians, pi, sqrt
def meteorites():
angle = uniform(0, 2 * pi) # in radians
distance = sqrt(uniform(4, 12.25))
return distance * cos(angle), distance * sin(angle)
You're getting random points that don't fall on your ring because these two lines don't do what you want:
x[i] = meteorites()[0]
y[i] = meteorites()[1]
These assign an x value from one point on the ring to x[i], and the y value from a different point on the ring to y[i]. You get coordinates from different points because you're calling meteorites() twice.
Instead, you probably want to call the function once, and then assign to each coordinate, or do an assignment with iterable-unpacking where both targets are on the left side of the equals sign:
x[i], y[i] = meteorites()
Your implementation will also work if you correct one line: insstead of calling meteorites() twice, call just once.
x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
x[i], y[i] = meteorites()
plt.scatter(x,y)
plt.show()
I would also rather run through a loop that picks a random angle and a random distance within your ring range. Then calculate the coords from that.
But in your code the first problem is see is that should write:
x[i],y[i] = meteorites()
instead of
x[i] = meteorites()[0]
y[i] = meteorites()[1]
In your example, you're called meteorites() twice resulting in the x and y two different meteorites.
as #Martijn Pieters suggested, simply draw the polar coordinates uniformly in the range you require.
theta = uniform(0,2*np.pi)
r = uniform(2.,3.5)
x = r*np.cos(theta)
y = r*np.sin(theta)
EDIT: There will be equal probability for every point in the ring to occur.
But practically there will be less pixels for a given theta the closer r is to the lower limit. So "meteorites" with smaller r will occur with larger probability.
I believe this effect is negligeble.
EDIT 2: MBo's answer is better. Code:
theta = uniform(0, 2 * np.pi)
r = np.sqrt(uniform(2.0 ** 2, 3.5 ** 2)) # draw from sqrt distribution
x = r * np.cos(theta)
y = r * np.sin(theta)
You could try the following to generate 1000 samples using numpy:
import numpy
n = 1000
phi = numpy.random.uniform(0, 2*numpy.pi, n)
r = numpy.random.uniform(2, 3.5, n)
Then x, y coordinates can be constructed as follows using the transformation from radial to cartesian coordinates:
x = r * numpy.cos(phi)
y = r * numpy.sin(phi)
This shows the power of numpy, as x and y are now arrays without needing to iterate over n.
I have generated a random point named y0=(a,b) in xy-plane , How can I generate another random point (x,y) 10 steps apart from y0?
note: by 10 steps apart from the firt point I don't mean the Euclidean distance. I mean the number of steps on lattice between the two point (a,b) and (x,y) which is given by |x-a|+|y-b|=10
My attempt(sometimes gives wrong result).
import random
y0=(random.randint(0,50),random.randint(0,50))# here I generated the first point.
y=random.randint(0,50)
# I used the formula |x-a|+|y-b|=10.
x=(10 -abs(y-y0[1]))+y0[0] or x=-(10 -abs(y-y0[1]))+y0[0]
x0=(x,y)
Let's say you have a point (x, y)
create another random point anywhere on the plane: (x1, y2) = (random(), random())
take the vector from your point to the new point: (vx, vy) = (x1-x, y1-y)
get the length l of the vector: l = sqrt(vx * vx + vy * vy)
use l to normalise the vector (so it has a length of 1): (vx, vy) = (vx / l, vy / l)
make the vector 10 steps long: (vx, vy) = (vx * 10, vy * 10)
add it to your original point to get to the desired point: (x1, y2) = (x + vx, y + vy)
voilá :)
from random import random
from math import sqrt
# Deviation
dev = 50
# Required distance between points
l = 10
if __name__ == '__main__':
# First random point
x0, y0 = dev*random(), dev*random()
# Second point
x1 = dev*random()
y1 = y0 + sqrt(l**2 - (x1 - x0)**2)
# Output
print "First point (%s, %s)" % (x0, y0)
print "Second point (%s, %s)" % (x1, y1)
print "Distance: %s" % (sqrt((x1 - x0)**2 + (y1 - y0)**2))
Let's say that your new point (x, y) is on a cercle of radius 10 and center (x0, y0). The random component is the angle.
import math as m
# radius of the circle
r = 10
# create random angle and compute coordinates of the new point
theta = 2*m.pi*random.random()
x = x0 + r*m.cos(theta)
y = y0 + r*m.sin(theta)
# test if the point created is in the domain [[0,50], [0, 50]] (see comments of PM2Ring)
while not ( 0<=x<=50 and 0<=y<=50 ) :
# update theta: add pi/2 until the new point is in the domain (see HumanCatfood's comment)
theta += 0.5*m.pi
x = x0 + r*m.cos(theta)
y = y0 + r*m.sin(theta)
So, you got the formula d=d1+d2=|x-x0|+|y-y0| , for d=10
Let's examine what's going on with this formula:
Let's say we generate a random point P at (0,0)
Let's say we generate y=random.randint(0,50) and let's imagine the value is 50.
What does this mean?
d1=|x-p[0]|=50 and your original formula is d=d1+d2=|x-x0|+|y-y0|, so
that means d2=|y-y0|=10-50 and d2=|y-y0|=-40. Is this possible? Absolutely not! An absolute value |y-y0| will always be positive, that's why your formula won't work for certain random points, you need to make sure (d-d1)>0, otherwise your equation won't have solution.
If you wanted to consider Euclidean distance you just need to generate random points in a circle where your original point will be the center, something like this will do:
import random
import math
def random_point(p, r=10):
theta = 2 * math.pi * random.random()
return (p[0] + r * math.cos(theta), p[1] + r * math.sin(theta))
If you draw a few random points you'll see more and more how the circle shape is created, let's try with N=10, N=50, N=1000:
Now, it seems you need the generated circle to be constrained at certain area region. One possible choice (not the most optimal though) would be generating random points till they meet those constraints, something like this would do:
def random_constrained_point(p, r=10, x_limit=50, y_limit=50):
i = 0
MAX_ITERATIONS = 100
while True:
x0, y0 = random_point(p, r)
if (0 <= x0 <= x_limit and 0 <= y0 <= y_limit):
return (x0, y0)
if i == MAX_ITERATIONS:
return p
i += 1
Once you got this, it's interesting to check what shape is created when you increase more and more the circle radius (10,20,50):
As you can see, your generated random constrained points will form a well_defined subarc.
this code generate a random point xy-plane named y0 then generate another point x0 10 steps apart from y0 in taxi distance .
------- begining of the code--------
import random
y0=(random.randint(0,50),random.randint(0,50))
while True:
y=random.randint(0,50)
x=(10 -abs(y-y0[1]))+y0[0]
if (abs(x-y0[0])+abs(y-y0[1]))==10:
x0=(x,y)
break
abs(x)+abs(y)=10 defines a square, so all you need to do is pick a random value along the perimeter of the square (40 units long), and map that random distance back to your x,y coordinate pair.
Something like (untested):
x = random.randint(-10,9)
y = 10 - abs(x)
if (random.randint(0,1) == 0):
x = -x
y = -y
x = x + y0[0]
y = y + y0[1]
x0=(x,y)
Clipping the x range that way ensures that all points are picked uniformly. Otherwise you can end up with (-10,0) and (10,0) having twice the chance of being picked compared to any other coordinate.
I have a list of x,y ideal points, and a second list of x,y measured points. The latter has some offset and some noise.
I am trying to "fit" the latter to the former. So, extract the x,y offset of the latter relative to the former.
I'm following some examples of scipy.optimize.leastsq, but having trouble getting it working. Here is my code:
import random
import numpy as np
from scipy import optimize
# Generate fake data. Goal: Get back dx=0.1, dy=0.2 at the end of this exercise
dx = 0.1
dy = 0.2
# "Actual" (ideal) data.
xa = np.array([0,0,0,1,1,1])
ya = np.array([0,1,2,0,1,2])
# "Measured" (non-ideal) data. Add the offset and some randomness.
xm = map(lambda x: x + dx + random.uniform(0,0.01), xa)
ym = map(lambda y: y + dy + random.uniform(0,0.01), ya)
# Plot each
plt.figure()
plt.plot(xa, ya, 'b.', xm, ym, 'r.')
# The error function.
#
# Args:
# translations: A list of xy tuples, each xy tuple holding the xy offset
# between 'coords' and the ideal positions.
# coords: A list of xy tuples, each xy tuple holding the measured (non-ideal)
# coordinates.
def errfunc(translations, coords):
sum = 0
for t, xy in zip(translations, coords):
dx = t[0] + xy[0]
dy = t[1] + xy[1]
sum += np.sqrt(dx**2 + dy**2)
return sum
translations, coords = [], []
for xxa, yya, xxm, yym in zip(xa, ya, xm, ym):
t = (xxm-xxa, yym-yya)
c = (xxm, yym)
translations.append(t)
coords.append(c)
translation_guess = [0.05, 0.1]
out = optimize.leastsq(errfunc, translation_guess, args=(translations, coords), full_output=1)
print out
I get the error:
errfunc() takes exactly 2 arguments (3 given)"
I'm not sure why it says 3 arguments as I only gave it two. Can anyone help?
====
ANSWER:
I was thinking about this wrong. All I have to do is to take the average of the dx and dy's -- that gives the correct result.
n = xa.shape[0]
dx = -np.sum(xa - xm) / n
dy = -np.sum(ya - ym) / n
print dx, dy
The scipy.optimize.leastsq assumes that the function you are using already has one input, x0, the initial guess. Any other additional inputs are then listed in args.
So you are sending three arguments: translation_guess, transactions, and coords.
Note that here it specifies that args are "extra arguments."
Okay, I think I understand now. You have the actual locations and the measured locations and you want to figure out the constant offset, but there is noise on each pair. Correct me if I'm wrong:
xy = tuple with coordinates of measured point
t = tuple with measured offset (constant + noise)
The actual coordinates of a point are (xy - t) then?
If so, then we think it should be measured at (xy - t + guess).
If so, then our error is (xy - t + guess - xy) = (guess - t)
Where it is measured doesn't even matter! We just want to find the guess that is closest to all of the measured translations:
def errfunc(guess, translations):
errx = 0
erry = 0
for t in translations:
errx += guess[0] - t[0]
erry += guess[1] - t[1]
return errx,erry
What do you think? Does that make sense or did I miss something?