trouble with rotation in spherical coordinates / python implementation - python

I am trying to implement a function for rotation in spherical coordinates.
In the end I need the implementation in Javascript, but first I am trying to get it to work in python because I am more familiar with the language.
I want a function rotate which takes as parameters:
rotation angle (alpha)
spherical coordinates theta and phi of point to be rotated (theta, phi)
spherical coordinates theta and phi of reference axis through the spheres origin around which the rotation should take place (THETA, PHI)
Using the approach from http://stla.github.io/stlapblog/posts/RotationSphericalCoordinates.html this is what I got so far:
Execution:
rotate(
alpha = 90, # roation angle
theta = 90, # point theta
phi = 0, # point phi
THETA = 0, # axis theta
PHI = 0, # axis phi
degrees = True # angles in degrees
)
This should deliver a new coodinate theta = 90 and phi = 90. What I get is theta = 180 and phi = 90.
Here some ohter inputs and outputs/expected outputs:
The part I am really not sure about is the calculation of theta_ and psi_ in the rotate function. Within the article it says psi_ should be a 2x1 matrix, but what I get is a 2x2 matrix.
Here my implementation attempt:
import numpy as np
from math import cos, sin, atan, pi
from cmath import exp, phase
#####################################################
def rotate(alpha, theta, phi, THETA, PHI, degrees=True):
## DEGREES TO RAD
if degrees:
alpha = pi/180 * alpha
theta = pi/180 * theta
phi = pi/180 * phi
THETA = pi/180 * THETA
PHI = pi/180 * PHI
psi_ = Psi_(alpha, theta, phi, THETA, PHI)
theta_ = 2 * atan(abs(psi_[1][1])/abs(psi_[0][0]))
phi_ = phase(psi_[1][1]) - phase(psi_[0][0])
## RAD TO DEGREES
if degrees:
return theta_ * 180/pi, phi_ * 180/pi
return theta_, phi_
#####################################################
def Psi_(alpha, theta, phi, THETA, PHI):
return Rn(THETA, PHI, alpha) * \
Psi(alpha, theta, phi)
#####################################################
def Psi(alpha, theta, phi):
return np.array([
[cos(theta)/2],
[exp(1j*phi) * sin(theta/2)]
])
#####################################################
def Rn(THETA, PHI, alpha):
return Rz(PHI) * \
Ry(THETA) * \
Rz(alpha) * \
Ry(THETA).conj().T * \
Rz(PHI).conj().T
#####################################################
def Rx(alpha):
return np.array([
[cos(alpha/2), -1j * sin(alpha/2)],
[-1j * sin(alpha/2), cos(alpha/2)]
])
#####################################################
def Ry(alpha):
return np.array([
[cos(alpha/2), -sin(alpha/2)],
[sin(alpha/2), cos(alpha/2)]
])
#####################################################
def Rz(alpha):
return np.array([
[exp(-1j * alpha/2), 0],
[0, exp(1j * alpha/2)]
])
#####################################################
if __name__ == "__main__":
print(rotate(
alpha = 90, # roation angle
theta = 90, # point theta
phi = 0, # point phi
THETA = 0, # axis theta
PHI = 0, # axis phi
degrees = True # angles in degrees
))
Thanks for any help!

As mentioned in my comment, I don't think that using rotations around x, y, and z is the most clever solution if you actually want to rotate around an arbitrary axis. So I'd use Quaternions. This virtually uses x,y, z vectors, but the qubit solution uses all the sine, atan methods as well, so no advantage or disadvantage here
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
class Quaternion( object ):
"""
Simplified Quaternion class for rotation of normalized vectors only!
"""
def __init__( self, q0, qx, qy, qz ):
"""
Internally uses floats to avoid integer division issues.
#param q0: int or float
#param qx: int or float
#param qy: int or float
#param qz: int or float
"""
self._q0 = float( q0 )
self._qx = float( qx )
self._qy = float( qy )
self._qz = float( qz )
"""
Note if interpreted as rotation q0 -> -q0 doesn't make a difference
q0 = cos( w ) so -cos( w ) = cos( w + pi ) and as the rotation
is by twice the angle it is either 2w or 2w + 2pi, the latter being equivalent to the former.
"""
def conjugate(q):
"""
#return Quaternion
"""
conjq = Quaternion( q._q0, -q._qx, -q._qy, -q._qz )
return conjq
def __mul__(q, r):
"""
Non commutative quaternion multiplication.
#return Quaternion
"""
if isinstance(r, Quaternion):
mq0 = q._q0 * r._q0 - q._qx * r._qx - q._qy * r._qy - q._qz * r._qz
mqx = q._q0 * r._qx + q._qx * r._q0 + q._qy * r._qz - q._qz * r._qy
mqy = q._q0 * r._qy - q._qx * r._qz + q._qy * r._q0 + q._qz * r._qx
mqz = q._q0 * r._qz + q._qx * r._qy - q._qy * r._qx + q._qz * r._q0
out = Quaternion(mq0, mqx, mqy, mqz)
else:
raise TypeError
return out
def __getitem__( q, idx ):
"""
#return float
"""
if idx < 0:
idx = 4 + idx
if idx in [ 0, 1, 2, 3 ]:
out = (q._q0, q._qx, q._qy, q._qz)[idx]
else:
raise IndexError
return out
theta, phi = .4, .89
xA, yA, zA = np.sin( theta ) * np.cos( phi ), np.sin( theta ) * np.sin( phi ), np.cos( theta )
Theta, Phi = .5, 1.13
xB, yB, zB = np.sin( Theta ) * np.cos( Phi ), np.sin( Theta ) * np.sin( Phi ), np.cos( Theta )
qB = Quaternion( 0, xB, yB, zB )
cX = [ xB ]
cY = [ yB ]
cZ = [ zB ]
for alpha in np.linspace( 0.1, 6, 20 ):
qA = Quaternion( np.cos( 0.5 * alpha ), xA * np.sin( 0.5 * alpha ), yA * np.sin( 0.5 * alpha ), zA * np.sin( 0.5 * alpha ) )
qAi = qA.conjugate()
qBr = qA * ( qB * qAi )
cX += [ qBr[1] ]
cY += [ qBr[2] ]
cZ += [ qBr[3] ]
print np.arccos( qBr[3] ), np.arctan2( qBr[2], qBr[1] )
fig = plt.figure()
ax = fig.add_subplot( 111, projection='3d' )
u = np.linspace( 0, 2 * np.pi, 50 )
v = np.linspace( 0, np.pi, 25 )
x = .9 * np.outer( np.cos( u ), np.sin( v ) )
y = .9 * np.outer( np.sin( u ), np.sin( v ) )
z = .9 * np.outer( np.ones( np.size( u ) ), np.cos( v ) )
ax.plot_wireframe( x, y, z, color='g', alpha=.3 )
ax.plot( [ 0, xA ], [ 0, yA ],[ 0, zA ], color='r' )
ax.plot( [ 0, xB ], [ 0, yB ],[ 0, zB ], color='b' )
ax.plot( cX, cY, cZ , color='b' )
plt.show()
providing
>> 0.49031916121373825 1.1522714737763464
>> 0.45533365052161895 1.2122741888530444
>> 0.41447110732929837 1.2534150991034823
>> 0.3704040237686721 1.2671812656784103
>> 0.32685242086086375 1.2421569673912964
>> 0.28897751220432055 1.1656787444306542
>> 0.26337170669521853 1.0325160977992986
>> 0.2562961184275642 0.8617797986161756
>> 0.26983294601232743 0.6990291355811976
>> 0.30014342513738007 0.5835103693125616
>> 0.3405035923275427 0.5247781593073798
>> 0.38470682535027323 0.5136174978518265
>> 0.42809208202393517 0.5372807783495164
>> 0.4673177317395864 0.5852787001209924
>> 0.49997646587457817 0.6499418738891971
>> 0.5243409810228178 0.7256665899898235
>> 0.5392333590629659 0.8081372118739611
>> 0.5439681824890205 0.8937546559885136
>> 0.5383320845959003 0.9792306451808166
>> 0.5225792805478816 1.0612632858722035
and

Related

how to create a square on top of a given Vector. This square should be orthogonal to the given vector

Programming in Python (Blender):
I want to create a square and print all vertices (A;B;C;D) into my console on top of a given Vector. The square should be orthogonal to this vector, like this:
def create_verts_around_point(radius, vert):
# given Vector
vec = np.array([vert[0], vert[1], vert[2]])
# side_length of square
side_length = radius
# Vctor x-direction (1,0,0)
x_vec = np.array([1,0,0])
# Vekctor y-direction (0,1,0)
y_vec = np.array([0,1,0])
# Vector z-direction (0,0,1)
z_vec = np.array([0,0,1])
p1 = vec + (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p2 = vec - (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p3 = vec - (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
p4 = vec + (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
But my output looks like this in the end (Square is always parallel to my x-axis and y-axis):
I don't think you're really thinking about this problem in 3D, but see if this is close.
I create a square, perpendicular to the X axis. I then rotate that square based on the angles in x, y, and z. I then position the square at the end of the vector and plot it. I add plot points for the origin and the end of the vector, and I duplicate the last point in the square do it draws all the lines.
import math
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
def create_verts_around_point(sides, vert):
x0, y0, z0 = vert
# Here is the unrotated square.
half = sides/2
square = [
[0, -half,-half],
[0, -half, half],
[0, half, half],
[0, half,-half],
]
# Now find the rotation in each direction.
thetax = math.atan2( z0, y0 )
thetay = math.atan2( z0, x0 )
thetaz = math.atan2( y0, x0 )
# Now rotate the cube, first in x.
cubes = []
txcos = math.cos(thetax)
txsin = math.sin(thetax)
tycos = math.cos(thetay)
tysin = math.sin(thetay)
tzcos = math.cos(thetaz)
tzsin = math.sin(thetaz)
for x,y,z in square:
x,y,z = (x, y * txcos - z * txsin, y * txsin + z * txcos)
x,y,z = (x * txcos - z * txsin, y, x * txsin + z * txcos)
x,y,z = (x * txcos - y * txsin, x * txsin + y * txcos, z)
cubes.append( (x0+x, y0+y, z0+z) )
return cubes
point = (10,10,10)
square = create_verts_around_point(5, point)
points = [(0,0,0),point] + square + [square[0]]
x = [p[0] for p in points]
y = [p[1] for p in points]
z = [p[2] for p in points]
ax = plt.figure().add_subplot(111, projection='3d')
ax.plot( x, y, z )
plt.show()
Output:

Improve an exponential curve fit of nearly exponential data

I have recorded some data about the color of an LED that varies with the 8bit signal sent to the LED driver, the signal can vary between 0 and 255.
Exponential curve fitting seems to work very well to represent the LED's behavior. I have had good results with the following formula:
x * signal ** ex
y * signal ** ey
z * signal ** ez
In Python, I use the following function:
from scipy.optimize import curve_fit
def fit_func_xae(x, a, e):
# Curve fitting function
return a * x**e
# X, Y, Z are real colorimetric values that are measured by a physical instrument
(aX, eX), cov = curve_fit(fit_func3xa, signal, X)
(aY, eY), cov = curve_fit(fit_func3xa, signal, Y)
(aZ, eZ), cov = curve_fit(fit_func3xa, signal, Z)
Note:
In colorimetry, we represent the color of the LED in the CIE XYZ color space, which is a linear space that works in a similar way as a linear RGB color space. Even if it is an aproximation, you can think of XYZ as a synonym of (linear) RGB.
So a color can be represented as a triplet of linear values X, Y, Z.
here is the data behind the curves.
for each 8bit parameter sent to the LED driver, there are 3 measures
Signal
[ 3. 3. 3. 5. 5. 5. 7. 7. 7. 10. 10. 10. 15. 15.
15. 20. 20. 20. 30. 30. 30. 40. 40. 40. 80. 80. 80. 160.
160. 160. 240. 240. 240. 255. 255. 255.]
X, Y, Z
[[9.93295448e-05 8.88955748e-04 6.34978556e-04]
[9.66399391e-05 8.86031926e-04 6.24680520e-04]
[1.06108685e-04 8.99010175e-04 6.41577838e-04]
[1.96407653e-04 1.70210146e-03 1.27178991e-03]
[1.84965943e-04 1.67927596e-03 1.24985475e-03]
[1.83770476e-04 1.67905297e-03 1.24855580e-03]
[3.28537613e-04 2.75382195e-03 2.14639821e-03]
[3.17804246e-04 2.74152647e-03 2.11730825e-03]
[3.19167905e-04 2.74977632e-03 2.11142769e-03]
[5.43770342e-04 4.09314433e-03 3.33793380e-03]
[5.02493149e-04 4.04392581e-03 3.24784452e-03]
[5.00712102e-04 4.03456071e-03 3.26803716e-03]
[1.48001671e-03 1.09367632e-02 9.59283037e-03]
[1.52082180e-03 1.09920985e-02 9.63624777e-03]
[1.50153844e-03 1.09623592e-02 9.61724422e-03]
[3.66206564e-03 2.74730946e-02 2.51982924e-02]
[3.64074861e-03 2.74283157e-02 2.52187294e-02]
[3.68719991e-03 2.75033778e-02 2.51691331e-02]
[1.50905917e-02 1.06056566e-01 1.06534373e-01]
[1.51370269e-02 1.06091182e-01 1.06790424e-01]
[1.51654172e-02 1.06109863e-01 1.06943957e-01]
[3.42912601e-02 2.30854413e-01 2.43427207e-01]
[3.42217124e-02 2.30565972e-01 2.43529454e-01]
[3.41486993e-02 2.30807320e-01 2.43591644e-01]
[1.95905112e-01 1.27409867e+00 1.37490536e+00]
[1.94923951e-01 1.26934278e+00 1.37751808e+00]
[1.95242984e-01 1.26805844e+00 1.37565458e+00]
[1.07931878e+00 6.97822521e+00 7.49602715e+00]
[1.08944832e+00 7.03128378e+00 7.54296884e+00]
[1.07994964e+00 6.96864302e+00 7.44011991e+00]
[2.95296087e+00 1.90746191e+01 1.99164655e+01]
[2.94254973e+00 1.89524517e+01 1.98158118e+01]
[2.95753358e+00 1.90200667e+01 1.98885050e+01]
[3.44049055e+00 2.21221159e+01 2.29667049e+01]
[3.43817829e+00 2.21225393e+01 2.29363833e+01]
[3.43077583e+00 2.21158929e+01 2.29399652e+01]]
_
The Problem:
Here's a scatter plot of some of my LED's XYZ values, together with a plot of the exponential curve fitting obtained with the code above:
It all seems good... until you zoom a bit:
On this zoom we can also see that the curve is fitted on multiple measurements:
At high values, Z values (blue dots) are always higher than Y values (green dots). But at low values, Y values are higher than Z values.
The meaning of this is that the LED changes in color depending on the PWM applied, for some reason (maybe because the temperature rises when more power is applied).
This behavior cannot be represented mathematically by the formula that I have used for the curve fit, however, the curve fit is so good for high values that I am searching for a way to improve it in a simple and elegant way.
Do you have any idea how this could be done? I have tried unsuccessfully to add more parameters, for example I tried to use:
x1 * signal ** ex + x2 * signal ** fx
instead of:
x * signal ** ex
but that causes scipy to overflow.
My idea was that by adding two such elements I could still have a funtion that equals 0 when signal = 0, but that increases faster at low values than a simple exponential.
The data shows two steps in the log-log plot so I used an approach already used here.
Code is as follows:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
signal = np.array( [
3.0, 3.0, 3.0,
5.0, 5.0, 5.0,
7.0, 7.0, 7.0,
10.0, 10., 10.,
15.0, 15., 15.,
20.0, 20., 20.,
30.0, 30., 30.,
40.0, 40., 40.,
80.0, 80., 80.,
160.0, 160., 160.,
240.0, 240., 240.,
255.0, 255., 255.
] )
data = np.array( [
[9.93295448e-05, 8.88955748e-04, 6.34978556e-04],
[9.66399391e-05, 8.86031926e-04, 6.24680520e-04],
[1.06108685e-04, 8.99010175e-04, 6.41577838e-04],
[1.96407653e-04, 1.70210146e-03, 1.27178991e-03],
[1.84965943e-04, 1.67927596e-03, 1.24985475e-03],
[1.83770476e-04, 1.67905297e-03, 1.24855580e-03],
[3.28537613e-04, 2.75382195e-03, 2.14639821e-03],
[3.17804246e-04, 2.74152647e-03, 2.11730825e-03],
[3.19167905e-04, 2.74977632e-03, 2.11142769e-03],
[5.43770342e-04, 4.09314433e-03, 3.33793380e-03],
[5.02493149e-04, 4.04392581e-03, 3.24784452e-03],
[5.00712102e-04, 4.03456071e-03, 3.26803716e-03],
[1.48001671e-03, 1.09367632e-02, 9.59283037e-03],
[1.52082180e-03, 1.09920985e-02, 9.63624777e-03],
[1.50153844e-03, 1.09623592e-02, 9.61724422e-03],
[3.66206564e-03, 2.74730946e-02, 2.51982924e-02],
[3.64074861e-03, 2.74283157e-02, 2.52187294e-02],
[3.68719991e-03, 2.75033778e-02, 2.51691331e-02],
[1.50905917e-02, 1.06056566e-01, 1.06534373e-01],
[1.51370269e-02, 1.06091182e-01, 1.06790424e-01],
[1.51654172e-02, 1.06109863e-01, 1.06943957e-01],
[3.42912601e-02, 2.30854413e-01, 2.43427207e-01],
[3.42217124e-02, 2.30565972e-01, 2.43529454e-01],
[3.41486993e-02, 2.30807320e-01, 2.43591644e-01],
[1.95905112e-01, 1.27409867e+00, 1.37490536e+00],
[1.94923951e-01, 1.26934278e+00, 1.37751808e+00],
[1.95242984e-01, 1.26805844e+00, 1.37565458e+00],
[1.07931878e+00, 6.97822521e+00, 7.49602715e+00],
[1.08944832e+00, 7.03128378e+00, 7.54296884e+00],
[1.07994964e+00, 6.96864302e+00, 7.44011991e+00],
[2.95296087e+00, 1.90746191e+01, 1.99164655e+01],
[2.94254973e+00, 1.89524517e+01, 1.98158118e+01],
[2.95753358e+00, 1.90200667e+01, 1.98885050e+01],
[3.44049055e+00, 2.21221159e+01, 2.29667049e+01],
[3.43817829e+00, 2.21225393e+01, 2.29363833e+01],
[3.43077583e+00, 2.21158929e+01, 2.29399652e+01]
] )
def determine_start_parameters( x , y, edge=9 ):
logx = np.log( x )
logy = np.log( y )
xx = logx[ :edge ]
yy = logy[ :edge ]
(ar1, br1), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
xx = logx[ edge : -edge ]
yy = logy[ edge : -edge]
(ar2, br2), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
xx = logx[ -edge : ]
yy = logy[ -edge : ]
(ar3, br3), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
cross1r = ( br2 - br1 ) / ( ar1 - ar2 )
mr = ar1 * cross1r + br1
cross2r = ( br3 - br2 ) / ( ar2 - ar3 )
xx0r = [ mr, ar1, ar2 , ar3, cross1r, cross2r, 1 ]
return xx0r
def func(
x, b,
m1, m2, m3,
a1, a2,
p
):
"""
continuous approxiation for a two-step function
used to fit the log-log data
p is a sharpness parameter for the transition
"""
out = b - np.log(
( 1 + np.exp( -m1 * ( x - a1 ) )**abs( p ) )
) / p + np.log(
( 1 + np.exp( m2 * ( x - a1 ) )**abs( p ) )
) / p - np.log(
( 1 + np.exp( m3 * ( x - a2 ) )**abs( p ) )
) / abs( p )
return out
def expfunc(
x, b,
m1, m2, m3,
a1, a2,
p
):
"""
remapping to the original data
"""
xi = np.log( x )
eta = func(
xi, b,
m1, m2, m3,
a1, a2,
p)
return np.exp(eta)
def expfunc2(
x, b,
m1, m2, m3,
a1, a2,
p
):
"""
simplified remapping
"""
aa1 = np.exp( a1 )
aa2 = np.exp( a2 )
return (
np.exp( b ) * (
( 1 + ( x / aa1 )**( m2 * p ) ) /
( 1 + ( x / aa2 )**( m3 * p ) ) /
( 1 + ( aa1 / x )**( m1 * p ) )
)**( 1 / p )
)
logsig = np.log( signal )
logred = np.log( data[:,0] )
loggreen = np.log( data[:,1] )
logblue = np.log( data[:,2] )
### getting initial parameters
### red
xx0r = determine_start_parameters( signal, data[ :, 0 ] )
xx0g = determine_start_parameters( signal, data[ :, 1 ] )
xx0b = determine_start_parameters( signal, data[ :, 2 ] )
print( xx0r )
print( xx0g )
print( xx0b )
xl = np.linspace( 1, 6, 150 )
tl = np.linspace( 1, 260, 150 )
solred = curve_fit( func, logsig, logred, p0=xx0r )[0]
solgreen = curve_fit( func, logsig, loggreen, p0=xx0g )[0]
solblue = curve_fit( func, logsig, logblue, p0=xx0b )[0]
print( solred )
print( solgreen )
print( solblue )
fig = plt.figure()
ax = fig.add_subplot( 2, 1, 1 )
bx = fig.add_subplot( 2, 1, 2 )
ax.scatter( np.log( signal ), np.log( data[:,0] ), color = 'r' )
ax.scatter( np.log( signal ), np.log( data[:,1] ), color = 'g' )
ax.scatter( np.log( signal ), np.log( data[:,2] ), color = 'b' )
ax.plot( xl, func( xl, *solred ), color = 'r' )
ax.plot( xl, func( xl, *solgreen ), color = 'g' )
ax.plot( xl, func( xl, *solblue ), color = 'b' )
bx.scatter( signal, data[:,0], color = 'r' )
bx.scatter( signal, data[:,1], color = 'g' )
bx.scatter( signal, data[:,2], color = 'b' )
bx.plot( tl, expfunc2( tl, *solred), color = 'r' )
bx.plot( tl, expfunc2( tl, *solgreen), color = 'g' )
bx.plot( tl, expfunc2( tl, *solblue), color = 'b' )
plt.show()
Which results in

How to make Matplotlib Animation for a computer simulation of Earth, Moon and Asteroid (only animation part) in Python?

I'm trying to make a computer simulation for Earth, Moon and Asteroid as they orbit around the Sun.
The data r_earth, r_moon and the r_asteroid are already given (they are an array). The xlim and ylim are so big because I have to use real values from the universe, otherwise I wouldn't see anything on the animation.
import numpy as np
def RK4_step(y, t, f, dt): #for numerical part I decided to use RK4 method
k1 = dt * f(y, t)
k2 = dt * f(y + 0.5 * k1, t + 0.5 * dt)
k3 = dt * f(y + 0.5 * k2, t + 0.5 * dt)
k4 = dt * f(y + k3, t + dt)
return (t + dt, y + (k1 + 2. * (k2 + k3) + k4) / 6.)
def integrate_ode(y0, derivative_fn, step_fn, a, b, n):
dt = (b - a) / (n - 1)
t = a
y = np.array(y0)
res = [y]
for _ in range(1, n):
t, y = step_fn(y, t, derivative_fn, dt)
res.append(y)
return (np.linspace(a, b, n), np.array(res))
# np.array(res) is 2D array of dimensions (<time steps>, <um variables>) that holds the values for each time step.
# Initial parameters for body masses(Sun, Earth, Moon, Asteroid)
G = 6.674e-11
m_s = 1.989e30
m_e = 5.972e24
m_m = 7.346e22
m_a = 1e25
def derivative_fn(y, t): # vectors necessary for 2.Newton's Law
d1 = y[1] - y[0]
vec01 = d1 / np.linalg.norm(d1) ** 3
d2 = y[2] - y[0]
vec02 = d2 / np.linalg.norm(d2) ** 3
d3 = y[2] - y[1]
vec12 = d3 / np.linalg.norm(d3) ** 3
der = np.array([
y[3],
y[4],
y[5],
- G * m_s * y[0] / np.linalg.norm(y[0]) ** 3 + G * m_m * vec01 + G * m_a * vec02,
- G * m_s * y[1] / np.linalg.norm(y[1]) ** 3 - G * m_e * vec01 + G * m_a * vec12,
- G * m_s * y[2] / np.linalg.norm(y[2]) ** 3 - G * m_e * vec02 - G * m_m * vec12])
return der
# numerically computed data in the array form
# y = [r_e, r_m, r_a, v_e, v_m, v_a] -> 2D array of size (6, 2)
# the initial distances between the bodies,
# for Earth and Moon we compute it from 2nd Newton's Law (orbital velocity)
r_e0 = 1.519e11
v_e0 = np.sqrt(G * m_s / r_e0)
r_m0 = 1.521e11
v_m0 = np.sqrt(G * m_e / abs(r_m0 - r_e0))
r_a0 = 1e11
v_a0 = 31293 # m/s
y0 = np.array([[r_e0, 0.], [r_m0, 0.], [r_a0, 0.], [0., v_e0], [0., v_e0 + v_m0], [0., v_e0 + v_a0]])
# all the parameters are now in the array y0,
# the last two are velocities relative to the Earth
n = 10000
ts, ys = integrate_ode(y0, derivative_fn, RK4_step, 0., 5.154e7, n)
r_earth = ys[:, 0, :]
r_moon = ys[:, 1, :]
r_asteroid = ys[:, 2, :]
# Plotting
import matplotlib.pyplot as plt
plt.xlabel("x")
plt.ylabel("y")
plt.plot(r_earth[:, 0], r_earth[:, 1], label="Earth")
plt.plot(r_moon[:, 0], r_moon[:, 1], label="Moon")
plt.plot(r_asteroid[:, 0], r_asteroid[:, 1], label="Asteroid")
plt.show()
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ax.set_xlim([-0.3e12, 0.3e12])
ax.set_ylim([-0.3e12, 0.3e12])
# xlim and ylim are so big because we wouldn't see the animation.
sun, = ax.plot(0., 0., 'oy', ms=20)
earth, = ax.plot(r_earth[0, 0], r_earth[0, 1], 'og', ms=10)
moon, = ax.plot(r_moon[0, 0], r_moon[0, 0], 'ob', ms=3)
asteroid, = ax.plot(r_asteroid[0, 0], r_asteroid[0, 0], 'ok', ms=1)
num_frames = 10000
frame_duration = n / num_frames
def animation_frame(frame):
index = int(frame_duration * frame)
earth.set_data(r_earth[index, 0], r_earth[index, 1])
moon.set_data(r_moon[index, 0] / r_earth[index, 0], r_moon[index, 1] / r_earth[index, 1])
asteroid.set_data(r_asteroid[index, 0] / r_earth[index, 0], r_asteroid[index, 1] / r_earth[index, 1])
return earth, moon, asteroid
animation = FuncAnimation(fig, func=animation_frame, frames=range(num_frames), interval=50)
plt.show()
When I start my code, the Moon is in the same position as Sun, both are static. The Earth is moving around the Sun very slowly and there is no Asteroid on the animation.
How to fix this?
I still want to use real data, but wish that animation would go 100x faster(but still smooth animation). How do I do that?

How to Fit to The Outer Shell of a Function

I am trying to make a gaussian fit on a function that is messy. I want to only fit the exterior outer shell (these are not just the max values at each x, because some of the max values will be too low too, because the sample size is low).
from scipy.optimize import curve_fit
def Gauss(x, a, x0, sigma, offset):
return a * np.exp(-np.power(x - x0,2) / (2 * np.power(sigma,2))) + offset
def fitNormal(x, y):
popt, pcov = curve_fit(Gauss, x, y, p0=[np.max(y), np.median(x), np.std(x), np.min(y)])
return popt
plt.plot(xPlot,yPlot, 'k.')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Y(x)')
x,y = xPlot,yPlot
popt = fitNormal(x, y)
minx, maxx = np.min(x), np.max(x)
xFit = np.arange(start=minx, stop=maxx, step=(maxx-minx)/1000)
yFitTest = Gauss(xPlot, popt[0], popt[1], popt[2], popt[3])
print('max fit test: ',np.max(yFitTest))
print('max y: ',np.max(yPlot))
maxIndex = np.where(yPlot==np.max(yPlot))[0][0]
factor = yPlot[maxIndex]/yFitTest[maxIndex]
yFit = Gauss(xPlot, popt[0], popt[1], popt[2], popt[3]) * factor
plt.plot(xFit,yFit,'r')
This is an iterative approach similar to this post. It is different in the sense that the shape of the graph does not permit the use of convex hull. So the idea is to create a cost function that tries to minimize the area of the graph while paying high cost if a point is above the graph. Depending on the type of the graph in OP the cost function needs to be adapted. One also has to check if in the final result all points are really below the graph. Here one can fiddle with details of the cost function. One my, e.g., include an offset in the tanh like tanh( slope * ( x - offset) ) to push the solution farther away from the data.
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import leastsq
def g( x, a, s ):
return a * np.exp(-x**2 / s**2 )
def cost_function( params, xData, yData, slope, val ):
a,s = params
area = 0.5 * np.sqrt( np.pi ) * a * s
diff = np.fromiter ( ( y - g( x, a, s) for x, y in zip( xData, yData ) ), np.float )
cDiff = np.fromiter( ( val * ( 1 + np.tanh( slope * d ) ) for d in diff ), np.float )
out = np.concatenate( [ [area] , cDiff ] )
return out
xData = np.linspace( -5, 5, 500 )
yData = np.fromiter( ( g( x, .77, 2 ) * np.sin( 257.7 * x )**2 for x in xData ), np.float )
sol=[ [ 1, 2.2 ] ]
for i in range( 1, 6 ):
solN, err = leastsq( cost_function, sol[-1] , args=( xData, yData, 10**i, 1 ) )
sol += [ solN ]
print sol
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1)
ax.scatter( xData, yData, s=1 )
for solN in sol:
solY = np.fromiter( ( g( x, *solN ) for x in xData ), np.float )
ax.plot( xData, solY )
plt.show()
giving
>> [0.8627445 3.55774814]
>> [0.77758636 2.52613376]
>> [0.76712184 2.1181137 ]
>> [0.76874125 2.01910211]
>> [0.7695663 2.00262339]
and
Here is a different approach using scipy's Differental Evolution module combined with a "brick wall", where if any predicted value during the fit is greater than the corresponding Y value, the fitting error is made extremely large. I have shamelessly poached code from the answer of #mikuszefski to generate the data used in this example.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import warnings
from scipy.optimize import differential_evolution
def g( x, a, s ):
return a * np.exp(-x**2 / s**2 )
xData = np.linspace( -5, 5, 500 )
yData = np.fromiter( ( g( x, .77, 2 )* np.sin( 257.7 * x )**2 for x in xData ), np.float )
def Gauss(x, a, x0, sigma, offset):
return a * np.exp(-np.power(x - x0,2) / (2 * np.power(sigma,2))) + offset
# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
val = Gauss(xData, *parameterTuple)
multiplier = 1.0
for i in range(len(val)):
if val[i] < yData[i]: # ****** brick wall ******
multiplier = 1.0E10
return np.sum((multiplier * (yData - val)) ** 2.0)
def generate_Initial_Parameters():
# min and max used for bounds
maxX = max(xData)
minX = min(xData)
maxY = max(yData)
minY = min(yData)
minData = min(minX, minY)
maxData = max(maxX, maxY)
parameterBounds = []
parameterBounds.append([minData, maxData]) # parameter bounds for a
parameterBounds.append([minData, maxData]) # parameter bounds for x0
parameterBounds.append([minData, maxData]) # parameter bounds for sigma
parameterBounds.append([minData, maxData]) # parameter bounds for offset
# "seed" the numpy random number generator for repeatable results
result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3, polish=False)
return result.x
# generate initial parameter values
geneticParameters = generate_Initial_Parameters()
# create values for display of fitted function
y_fit = Gauss(xData, *geneticParameters)
plt.scatter(xData, yData, s=1 ) # plot the raw data
plt.plot(xData, y_fit) # plot the equation using the fitted parameters
plt.show()
print('parameters:', geneticParameters)

Bezier curve surface

I have Bezier curve and I need to make surface of revolution based on this curve. Bezier curve should be rotating around an axis of rotation. How it can be done? Maybe some examples? Dots can be fixed, but only 3
import matplotlib as mpl
import numpy as np
from scipy.misc import comb
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import math
import pylab
def bernstein_poly(i, n, t):
return comb(n, i) * (t**(n - i)) * (1 - t)**i
def bezier_curve(points, nTimes=1000):
nPoints = len(points)
xPoints = np.array([p[0] for p in points])
yPoints = np.array([p[1] for p in points])
zPoints = np.array([p[2] for p in points])
t = np.linspace(0.0, 1.0, nTimes)
polynomial_array = np.array(
[bernstein_poly(i, nPoints - 1, t) for i in range(0, nPoints)])
xvals = np.dot(xPoints, polynomial_array)
yvals = np.dot(yPoints, polynomial_array)
zvals = np.dot(zPoints, polynomial_array)
return xvals, yvals, zvals
from math import pi ,sin, cos
def R(theta, u):
return [[cos(theta) + u[0]**2 * (1-cos(theta)),
u[0] * u[1] * (1-cos(theta)) - u[2] * sin(theta),
u[0] * u[2] * (1 - cos(theta)) + u[1] * sin(theta)],
[u[0] * u[1] * (1-cos(theta)) + u[2] * sin(theta),
cos(theta) + u[1]**2 * (1-cos(theta)),
u[1] * u[2] * (1 - cos(theta)) - u[0] * sin(theta)],
[u[0] * u[2] * (1-cos(theta)) - u[1] * sin(theta),
u[1] * u[2] * (1-cos(theta)) + u[0] * sin(theta),
cos(theta) + u[2]**2 * (1-cos(theta))]]
def Rotate(pointToRotate, point1, point2, theta):
u= []
squaredSum = 0
for i,f in zip(point1, point2):
u.append(f-i)
squaredSum += (f-i) **2
u = [i/squaredSum for i in u]
r = R(theta, u)
rotated = []
for i in range(3):
rotated.append(round(sum([r[j][i] * pointToRotate[j] for j in range(3)])))
return rotated
And main part
if __name__ == "__main__":
nPoints = 3
points = [[0,0,0],[0,1,2],[0,2,1]]#3 points of curve
xvals, yvals, zvals = bezier_curve(points, nTimes=1000)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(xvals, yvals, zvals, label='bezier')
p1=[0,0,0]#axis of rotation
p2=[0,0,1]#axis of rotation
angle = pi/12
while angle <= 2*pi:
pp1 = Rotate(points[0], p1, p2, angle)
pp2 = Rotate(points[1], p1, p2, angle)
pp3 = Rotate(points[2], p1, p2, angle)
npoints=[pp1,pp2,pp3]
xnvals, ynvals, znvals = bezier_curve(npoints, nTimes=1000)
ax.plot(xnvals, ynvals, znvals, label='bezier')
angle= angle + pi/24
plt.show()
upd: I do something on this task. But this is not exactly what I need.Now its many lines, not surface. I tried to make a surface, but failed
Upd2:
This is my attempt to make surface of revolution, but its failed too
x = np.arange (0, 0.1, 0.005)
y = np.arange (0, 0.1, 0.005)
xgrid, ygrid = np.meshgrid(x, y)
points = [[0,0,0],[0,1,2],[0,2,1]] #Bezier curve points
p1=[0,0,0]#axis of rotation
p2=[0,0,1]#axis of rotation
angle = pi/12
xval, yval, zval = bezier_curve(points, nTimes=20)
x=xgrid
y=ygrid
z=zval
fig = pylab.figure()
axes = Axes3D(fig)
axes.plot_surface(x, y, z)
while angle <= 2*pi:
pp1 = Rotate(points[0], p1, p2, angle)
pp2 = Rotate(points[1], p1, p2, angle)
pp3 = Rotate(points[2], p1, p2, angle)
npoints=[pp1,pp2,pp3]
x = np.arange (0, 0.1, 0.005)
y = np.arange (0, 0.1, 0.005)
xgrid, ygrid = np.meshgrid(x, y)
xval, yval, zval = bezier_curve(npoints, nTimes=20)
zgrid= zval
x=xgrid
y=ygrid
z=zgrid
axes.plot_surface(x, y, z)
angle= angle + pi/24

Categories

Resources