I'm sorry for my ignorance in using NumPy and python. I'm much more comfortable in Matlab however I've been required to convert my code from Matlab to Python. To run my script in Python takes 113 seconds meanwhile it takes 7 seconds in MATLAB. Both produce correct results albeit different due to stochasticity. The big culprit I believe is my derivative function. I profiled my python code and it says that this function accounts for 76% of the run time (It doesn't take up nearly as much percent-wise in MATLAB). I understand there is a lot of code so if anyone could help me with making my python derivative code perform more similarly to MATLAB that would be greatly appreciated! If you have any questions feel free to ask. Thank you!
Derivative6DOF.py
## Derivative6DOF Variables
# Input Variables
# x - vehicle state = [x, y, z, u, v, w, phi, theta, psi, p, q, r, omega1, omega2, omega3, omega4]'
# dt - time step
# bigOmegasTarget - commanded rotor speeds = [omega1, omega2, omega3, omega4]'
# batterySOC - percent battery charge remaining
# m - mass of vehicle
# g - gravitational acceleration constant
# I - moment of inertia = eye([I11, I22, I33]')
# Cd0 - Profile drag coefficient of each rotor
# pitch - pitch of blade
# rho - density of air
# a - lift curve slope of the airfoil
# b - number of blades
# c - chord length of blade
# R - total length of the rotor blade
# viGuessInitial - guess induced velocity
# posProps - position of propellers from center of gravity = [x, y, z]'
# A - area of propeller
# maxCapacity - max battery capacity
# powerDrawComponents - power drawn by components from battery
# Kv - voltage coefficient of motor [rad/sec/V]
# Kt - motor torque coefficent [N*m/A]
# voltMax - max voltage supplied
# resistanceMotor - resistance of motor
# PeMax - max power possible
# Jp - inertia about each motor (includes propellor and motor) [kg*m^2]
# Output Variables
# xdot - vehicle state derivative = [u, v, w, udot, vdot, zdot, p, q, r, pdot, qdot, rdot, omegadot1 omegadot2 omegadot3 omegadot4]'
# battery - battery state = [percentRemaining, dischargeRate]'
# k - thrust-to-omega coefficient (https://andrew.gibiansky.com/blog/physics/quadcopter-dynamics/#:~:text=Each#20rotor#20contributes#20some#20torque,CDAv2.)
# b_coeff - yaw-to-omega coefficient (https://andrew.gibiansky.com/blog/physics/quadcopter-dynamics/#:~:text=Each#20rotor#20contributes#20some#20torque,CDAv2.)
# Intermediate Variables
# DCM - rotation matrix from body frame to inertial frame
# UVWp - translational velocity at each propeller = [u, v, w]'
# theta0 - root blade angle
# theta1 - linear twist of blade
# T - thrust
# vi - induced velocity iterated guess
# vPrime - effective speed of flow at the rotor
# viCalc - induced velocity calculated
# Pinduced - induced power
# Pprofile - profile power
# Qp - rotor aerodynamic torque (current motor torque)
# totalThrust - sum of each rotor thrust
# QeMax - max electric motor torque possible
# PeMax - max electric power available
# Qe - electric motor torque (desired motor torque)
# ind - index
# Maero - aerodynamic pitching, rolling and yawing moments = [x, y, z]'
# H - hub forces = [x, y, z]'
# Mgyro - gyroscopic moments = [x, y, z]'
# Cl - coefficient of lift
# AR - aspect ratio
# Cd - coefficient of drag
# lx - distance of props in x-direction
# ly - distance of props in y-direction
# LScale - pitch scalar coefficient
# MScale - roll scalar coefficient
# NScale - yaw scalar coefficient
# MThrust - differential thrust = [x, y, z]'
# F - sum of all forces acting on vehicle
# M - sum of all moments acting on vehicle
# powerDrawMotors - sum of all power draw from all 4 motors
# totalPowerDraw - total power load on battery from all components (motors, camera, electronics, etc.)
# maxBatteryPower = maxCapacity*voltMax/1000; # (Whr) Max possible power from battery at start
# State Element Vector
# x - x-position
# y - y-position
# z - z-position
# u - x-velocity
# v - y-velocity
# w - z-velocity
# udot - x-acceleration
# vdot - y-acceleration
# wdot - z-acceleration
# phi - angle about y-axis
# theta - angle about x-axis
# psi - angle about z-axis
# p - angular velocity about y-axis
# q - angular velocity about x-axis
# r - angular velocity about z-axis
# pdot - angular acceleration about y-axis
# qdot - angular acceleration about x-axis
# rdot - angular acceleration about z-axis
# omega1 - angular speed of front left rotor
# omega2 - angular speed of back right rotor
# omega3 - angular speed of front right rotor
# omega4 - angular speed of back left rotor
# omegadot1 - angular acceleration of front left rotor
# omegadot2 - angular acceleration of back right rotor
# omegadot3 - angular acceleration of front right rotor
# omegadot4 - angular acceleration of back left rotor
# percentRemaining - percent battery charge remaining
# dischargeRate - discharge rate of battery
# Additional Notes
# Mgyro Derivation Code (Page 639)
# syms p q r Omega Jp
# omega = [0; 0; Jp*Omega]; rotation about positive z-axis
# skewMatrix = [0 -r q; r 0 -p; -q p 0];
# Mgyro = -skewMatrix*omega
import numpy as np
def Derivative6DOF(x,dt,bigOmegasTarget,batterySOC,m,g,I,Cd0,pitch,rho,a,b,c,R,viGuessInitial,posProps,A,maxCapacity,powerDrawComponents,Kv,Kt,voltMax,resistanceMotor,PeMax,Jp,processNoise,theta0,theta1):
# direction cosine matrix (from bodyframe to NED)
DCM = np.array([[np.cos(x[8]) * np.cos(x[7]),- np.sin(x[8]) * np.cos(x[6]) + np.cos(x[8]) * np.sin(x[7]) * np.sin(x[6]),np.sin(x[8]) * np.sin(x[6]) + np.cos(x[8]) * np.cos(x[6]) * np.sin(x[7])],[np.cos(x[7]) * np.sin(x[8]),np.cos(x[8]) * np.cos(x[6]) + np.sin(x[8]) * np.sin(x[7]) * np.sin(x[6]),- np.cos(x[8]) * np.sin(x[6]) + np.sin(x[8]) * np.cos(x[6]) * np.sin(x[7])],[- np.sin(x[7]),np.sin(x[6]) * np.cos(x[7]),np.cos(x[7]) * np.cos(x[6])]])
# propeller velocity
UVWp = np.zeros((4,3))
for i in range(0,4):
UVWp[i,:] = x[3:6] + np.cross(x[9:12],posProps[i,:])
T = np.zeros((4,))
vi = np.ones((4,)) * viGuessInitial
# induced velocity for each rotor
for i in range(0,4):
# induced velocity
vPrime = np.sqrt(UVWp[i,0] ** 2 + UVWp[i,1] ** 2 + (UVWp[i,2] - vi[i]) ** 2)
T[i] = rho * a * b * c * R / 4 * ((UVWp[i,2] - vi[i]) * x[12 + i] * R + 2 / 3 * (x[12 + i] * R) ** 2 * (theta0 + 0.75 * theta1) + (UVWp[i,0] ** 2 + (UVWp[i,1] ** 2)) * (theta0 + 0.5 * theta1))
viCalc = T[i] / (2 * rho * A * vPrime)
# converge iterated induced velocity and calculated induced velocity
while np.abs(vi[i] - viCalc) > 1e-05:
# induced velocity
vPrime = np.sqrt(UVWp[i,0] ** 2 + UVWp[i,1] ** 2 + (UVWp[i,2] - vi[i]) ** 2)
T[i] = rho * a * b * c * R / 4 * ((UVWp[i,2] - vi[i]) * x[12 + i] * R + 2 / 3 * (x[12 + i] * R) ** 2 * (theta0 + 0.75 * theta1) + (UVWp[i,0] ** 2 + (UVWp[i,1] ** 2)) * (theta0 + 0.5 * theta1))
viCalc = T[i] / (2 * rho * A * vPrime)
# increment the induced velocity guess
vi[i] = vi[i] + 0.1 * (viCalc - vi[i])
# calculate induced power, profile power, and rotor aerodynamic torque for each rotor
Pinduced = T*(vi-UVWp[:,2])
Pprofile = (rho*Cd0*b*c*x[12:16]*R**2/8 * ((x[12:16]*R)**2 + UVWp[:,0]**2 + UVWp[:,1]**2))
Qp = 1/x[12:16]*(Pinduced + Pprofile)
# calculate total thrust
totalThrust = np.array([[0],[0],[-np.sum(T)]]).reshape((3,))
# calculate electric motor torque
QeMax = PeMax / x[12:16]
Qe = (Kt / resistanceMotor) * (bigOmegasTarget - x[12:16]) / Kv
# confirm electric motor torque isn't exceeding limit
np.where(Qe > QeMax, Qe, QeMax)
# calculate aerodynamic pitching, rolling, and yawing moments
Maero = np.zeros((3,))
Maero[0] = np.sum(np.array([[- 1],[- 1],[1],[1]]) * rho * a * b * c * R**2 * (x[12:16] * R**2 / 16 * x[9] + ((UVWp[:,2] - vi) / 8 + x[12:16] * R * theta0 / 6 + x[12:16] * R * theta1 / 8) * UVWp[:,0]))
Maero[1] = np.sum(np.array([[- 1],[- 1],[1],[1]]) * rho * a * b * c * R**2 * (x[12:16] * R**2 / 16 * x[10] + ((UVWp[:,2] - vi) / 8 + x[12:16] * R * theta0 / 6 + x[12:16] * R * theta1 / 8) * UVWp[:,1]))
Maero[2] = np.sum(np.array([[- 1],[- 1],[1],[1]]) * Qe)
# calculate hub force
H = -np.sign(x[3:6]) * np.sum(rho * Cd0 * b * c * x[12:16] * R**2 / 4 * np.array([UVWp[:,0],UVWp[:,1],np.array([[0],[0],[0],[0]]).reshape(4,)]))
# calculate gyroscopic moments
Mgyro = np.sum(x[12:16] * np.array([[-1],[-1],[1],[1]]).reshape(4,)) * Jp * np.array([-x[10], x[9], 0]).reshape((3,))
# calculate thrust-to-omega coefficient
k = T / x[12:16]**2
# calculate yaw-to-omega coefficient
Cl = 2 * T / (rho * R**2 * x[12:16]**2)
AR = R**2 / A
Cd = Cd0 + Cl**2 / (np.pi * AR * 1)
b_coeff = 0.5 * R ** 3 * rho * Cd * A
# calculate motor-mixer scalar coefficients
lx = np.abs(posProps[:,0])
ly = np.abs(posProps[:,1])
LScale = k * ly
MScale = k * lx
NScale = b_coeff
# calculate differential thrust
MThrust = np.zeros((3,))
MThrust[0] = np.sum(x[12:16]**2 * np.array([1,-1,-1,1]).reshape((4,)) * LScale)
MThrust[1] = np.sum(x[12:16]**2 * np.array([1,-1,1,-1]).reshape((4,)) * MScale)
MThrust[2] = np.sum(x[12:16]**2 * np.array([-1,-1,1,1]).reshape((4,)) * NScale)
# total force and moment acting on quadcopter in inertial frame
F = DCM # (totalThrust + H)
M = DCM # (Maero + Mgyro + MThrust)
# calculate state derivative
xdot = np.zeros((16,))
xdot[0:3] = np.array([x[3],x[4],x[5]])
xdot[3] = F[0] / m
xdot[4] = F[1] / m
xdot[5] = g + F[2] / m
xdot[6] = x[9] + np.tan(x[7]) * np.sin(x[6]) * x[10] + np.tan(x[7]) * np.cos(x[6]) * x[11]
xdot[7] = np.cos(x[6]) * x[10] - np.sin(x[6]) * x[11]
xdot[8] = 1/np.cos(x[7]) * np.sin(x[6]) * x[10] + 1/np.cos(x[7]) * np.cos(x[6]) * x[11]
xdot[9] = (M[0] + x[10] * x[11] * (I[1,1] - I[2,2])) / I[0,0]
xdot[10] = (M[1] + x[9] * x[11] * (I[2,2] - I[0,0])) / I[1,1]
xdot[11] = (M[2] + x[9] * x[10] * (I[0,0] - I[1,1])) / I[2,2]
xdot[12:16] = 1/Jp * (Qe - Qp)
xdot = xdot + np.random.randn(16,) * processNoise
# calculate battery state
powerDrawMotors = np.sum(Qp * x[12:16])
totalPowerDraw = powerDrawMotors + powerDrawComponents
maxBatteryPower = maxCapacity * voltMax / 1000
discharge = totalPowerDraw * dt / 3600 / maxBatteryPower * 100
batterySOC = batterySOC - discharge
battery = np.array([[batterySOC],[discharge]]).reshape((2,))
return xdot,battery,k,b_coeff,vi
Related
I have a modified version of the spring optimization Gekko model.
Is there a way to slightly relax constraints so that the solver can still give me solutions even if they start falling out of the range of my constraints?
I'm aware of RTOL, but is there a way of specifying tolerances for individual equations?
One way to do this is create a new variable eps that has a lower bound of zero and an upper bound that is the maximum allowable violation. This becomes the deviation of the inequality constraints that can be minimized with an importance factor (e.g. 10).
eps = m.Var(lb=0,ub=0.5)
m.Minimize(10*eps)
Here are modified equations with eps:
m.Equations([
d_coil / d_wire >= 4-eps,
d_coil / d_wire <= 16+eps
])
and the full script:
from gekko import GEKKO
# Initialize Gekko model
m = GEKKO()
#Maximize force of a spring at its preload height h_0 of 1 inches
#The stress at Hs (solid height) must be less than Sy to protect from damage
# Constants
from numpy import pi
# Model Parameters
delta_0 = 0.4 # inches (spring deflection)
h_0 = 1.0 # inches (preload height)
Q = 15e4 # psi
G = 12e6 # psi
S_e = 45e3 # psi
S_f = 1.5
w = 0.18
# Variables
# inches (wire diameter)
d_wire = m.Var(value=0.07247, lb = 0.01, ub = 0.2)
# inches (coil diameter)
d_coil = m.Var(value=0.6775, lb = 0.0)
# number of coils in the spring
n_coils = m.Var(value=7.58898, lb = 0.0)
# inches (free height spring exerting no force)
h_f = m.Var(value=1.368117, lb = 1.0)
F = m.Var() # Spring force
# Intermediates
S_y = m.Intermediate((0.44 * Q) / (d_wire**w))
h_s = m.Intermediate(n_coils * d_wire)
k = m.Intermediate((G * d_wire**4)/(8 * d_coil**3 * n_coils))
kW = m.Intermediate((4 * d_coil - d_wire)/(4 * (d_coil-d_wire)) \
+ 0.62 * d_wire/d_coil)
n = m.Intermediate((8 * kW * d_coil)/(pi * d_wire**3))
tau_max = m.Intermediate((h_f - h_0 + delta_0) * k * n)
tau_min = m.Intermediate((h_f - h_0) * k * n)
tau_mean = m.Intermediate((tau_max + tau_min) / 2)
tau_alt = m.Intermediate((tau_max - tau_min) / 2)
h_def = m.Intermediate(h_0 - delta_0)
tau_hs = m.Intermediate((h_f - h_s) * k * n)
# Equations
eps = m.Var(lb=0,ub=0.5)
m.Minimize(10*eps)
m.Equations([
F == k * (h_f - h_0),
d_coil / d_wire >= 4-eps,
d_coil / d_wire <= 16+eps,
d_coil + d_wire < 0.75,
h_def - h_s > 0.05,
tau_alt < S_e / S_f,
tau_alt + tau_mean < S_y / S_f,
tau_hs < S_y / S_f
])
# Objective function
m.Maximize(F)
# Send to solver
m.solve()
# Print solution
print('Maximum force: ' + str(F[0]))
print('Optimal values: ')
print('d_wire : ' + str(d_wire[0]))
print('d_coil : ' + str(d_coil[0]))
print('n_coils : ' + str(n_coils[0]))
print('h_f : ' + str(h_f[0]))
print('eps : ' + str(eps[0]))
I'm trying to solve a set of transient flow equations of a body firing from a pressurised tube
from scipy.integrate import odeint
import numpy as np
# parameters
Db = 0.5 # body diameter
Lb = 2.0 # body length
mb = 1000 # body mass
Ai = 0.5 # inlet area
Ki = 1.0 # inlet loss coefficient
Li = 1.5 # inlet position
Dt = 0.6 # tube diameter
Lt = 3.0 # tube length
epsilon = 0.0001 # roughness
rho = 1000 # density
mu = 0.0011 # dynamic viscosity
par = [Db, Lb, mb, Ai, Ki, Li, Dt, Lt, epsilon, rho, mu]
# boundary conditions
pp = 5e5 # pump pressure
patm = 0.0 # atmosphere pressure
bound = [pp, patm]
# time
ts = 0.001 # time step
tmax = 2.0 # total time
t = np.linspace(0, tmax, int(tmax/ts) + 1)
# initial conditions
v0 = 0.0 # initial velocity
s0 = 0.5 # initial displacement
Qlr0 = 0.0 # initial leakage to rear
Qlf0 = 0.0 # initial leakage to front
Qf0 = 0.0 # initial front flow
initVar = [v0, s0, Qlr0, Qlf0, Qf0]
# friction factor calculation
def ff(Q, A, L):
D = 2.0 * (A / np.pi)**0.5 # hydraulic diameter
Re = (rho * (Q / A) * L) / mu # reynolds number
ff = 0.25 / (np.log10((epsilon / D)+(5.74 / Re**0.9)))**2.0 # friction factor
return ff
# main calculation
def calc_main(var, t, val):
par, bound = val
Db, Lb, mb, Ai, Ki, Li, Dt, Lt, epsilon, rho, mu = par
pp, patm = bound
vb, sb, Qlr, Qlf, Qf = var
Ab = np.pi * (Db / 2.0)**2.0 # body area
At = np.pi * (Dt / 2.0)**2.0 # tube area
Alr = At - Ab # rear leak area
Alf = At - Ab # front leak area
Af = At # front flow area
Dlr = 2.0 * (Alr / np.pi)**0.5 # hydraulic diameter of rear leak
Dlf = 2.0 * (Alf / np.pi)**0.5 # hydraulic diameter of front leak
Df = Dt # hydraulic diameter of front flow
Lr = sb # rear position
Lf = Lr + Lb # front position
Llr = max(0.0, Li - Lr) # length of rear leak
Llf = min(Lt - Li, Lf - Li) # length of front leak
Lf = max(0.0, Lt - Lf) # length of front flow
Qi = (1 / Ki)**(0.5) * Ab * ((2 * (pp - pi)) / rho)**(0.5) # inlet flow rate
dQlrdt = Alr / (rho * Llr) * (pi - pr) * ((ff(Qlr, Alr, Llr) * Qlr**2.0) / (2 * Dlr * Alr)) # rear leak derivative
dQlfdt = Alf / (rho * Llf) * (pi - pf) * ((ff(Qlf, Alf, Llf) * Qlf**2.0) / (2 * Dlf * Alf)) # front leak derivative
dQfdt = Af / (rho * Lf) * (pr - patm) * ((ff(Qf, Af, Lf) * Qf**2.0) / (2 * Df * Af)) # front flow derivative
Qf = Qi # flow continuity
Qf = Qlr + Qlf # flow continuity
Qlr = Ab * vb # flow continuity
Fb = (pr - pf) * (pi * (Db / 2.0) ** 2.0) # force on body
ab = Fb / mb # body acceleration
dvbdt = ab # body velocity derivative
dsbdt = vb # body displacement derivative
return dvbdt, dsbdt, dQlrdt, dQlfdt, dQfdt
# solve
res = odeint(calc_main, initVar, t, args=([par, bound],))
If I take out the flow equations and have pr and pf equal to pp and patm respectively, then the motion equations work fine (results below), but with the flow equations as above it complains that the pressures are undefined (understandably, I have nothing saying pf=[] or pr=[])
What I've attempted is to use a combination of fsolve and odeint to solve the simultaneous flow equations, but also solve the ODEs, to little success
from scipy.optimize import fsolve
# main calculation
def calc_main(var, t, val):
par, bound = val
Db, Lb, mb, Ai, Ki, Li, Dt, Lt, epsilon, rho, mu = par
pp, patm = bound
vb, sb, Qlr, Qlf, Qf = var
Ab = np.pi * (Db / 2.0)**2.0 # body area
At = np.pi * (Dt / 2.0)**2.0 # tube area
Alr = At - Ab # rear leak area
Alf = At - Ab # front leak area
Af = At # front flow area
Dlr = 2.0 * (Alr / np.pi)**0.5 # hydraulic diameter of rear leak
Dlf = 2.0 * (Alf / np.pi)**0.5 # hydraulic diameter of front leak
Df = Dt # hydraulic diameter of front flow
Lr = sb # rear position
Lf = Lr + Lb # front position
Llr = max(0.0, Li - Lr) # length of rear leak
Llf = min(Lt - Li, Lf - Li) # length of front leak
Lf = max(0.0, Lt - Lf) # length of front flow
def calc_fluid (var):
Qi, dQlrdt, dQlfdt, dQfdt, pi, pr, pf = var
eq1 = (1 / Ki)**(0.5) * Ab * ((2 * (pp - pi)) / rho)**(0.5) - Qi # inlet flow rate
eq2 = Alr / (rho * Llr) * (pi - pr) * ((ff(Qlr, Alr, Llr) * Qlr**2.0) / (2 * Dlr * Alr)) - dQlrdt # rear leak derivative
eq3 = Alf / (rho * Llf) * (pi - pf) * ((ff(Qlf, Alf, Llf) * Qlf**2.0) / (2 * Dlf * Alf)) - dQlfdt # front leak derivative
eq4 = Af / (rho * Lf) * (pr - patm) * ((ff(Qf, Af, Lf) * Qf**2.0) / (2 * Df * Af)) - dQfdt # front flow derivative
eq5 = Qi - Qf # flow continuity
eq6 = Qlr + Qlf - Qf # flow continuity
eq7 = Ab * vb - Qlr # flow continuity
return eq1, eq2, eq3, eq4, eq5, eq6, eq7
Qi, dQlrdt, dQlfdt, dQfdt, pi, pr, pf = fsolve(calc_fluid, [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
Fb = (pr - pf) * (pi * (Db / 2.0) ** 2.0) # force on body
ab = Fb / mb # body acceleration
dvbdt = ab # body velocity derivative
dsbdt = vb # body displacement derivative
return dvbdt, dsbdt, dQlrdt, dQlfdt, dQfdt
I get the error "RuntimeWarning: invalid value encountered in double_scalars" for a few different lines while it runs, but I don't get told specifically which time step that happens at
I'm not convinced that the results I get are right, as I seem to now not move the body at all
Can anyone see what I've done wrong, or know of a better way to solve the simultaneous equations alongside the derivatives?
I've been shooting in the dark a little having not done anything quite like this before so it's possible I've gone completely off the rails with how these functions are meant to be used, any explanation is much appreciated!
Your example does has some undefined variables, so I didn't try it here.
What you have to do is to write the constraints dependent variables in a separate function and then use solve_bvp to integrate the differential equation with the boundary conditions.
The bc function is evaluated with the ya and yb being the value of your dependent variables at the beginning and the end of your interval. It will return an array of values that are 0 for the expected solution, and non-zero for other solutions.
Note
Also, you may like to know that
has a closed form solution
I am what varies in your equations, but the coefficients of your differential equations for Q then you can use the closed form, and solving the boundary conditions for the free parameters.
I am trying to make a plot of a projectile motion of a mass which is under the effect of gravitational, buoyancy and drag force. Basically, I want to show effects of the buoyancy and drag force on flight distance, flight time and velocity change on plotting.
import matplotlib.pyplot as plt
import numpy as np
V_initial = 30 # m/s
theta = np.pi/6 # 30
g = 3.711
m =1
C = 0.47
r = 0.5
S = np.pi*pow(r, 2)
ro_mars = 0.0175
t_flight = 2*(V_initial*np.sin(theta)/g)
t = np.linspace(0, t_flight, 200)
# Drag force
Ft = 0.5*C*S*ro_mars*pow(V_initial, 2)
# Buoyant Force
Fb = ro_mars*g*(4/3*np.pi*pow(r, 3))
x_loc = []
y_loc = []
for time in t:
x = V_initial*time*np.cos(theta)
y = V_initial*time*np.sin(theta) - (1/2)*g*pow(time, 2)
x_loc.append(x)
y_loc.append(y)
x_vel = []
y_vel = []
for time in t:
vx = V_initial*np.cos(theta)
vy = V_initial*np.sin(theta) - g*time
x_vel.append(vx)
y_vel.append(vy)
v_ch = [pow(i**2+ii**2, 0.5) for i in x_vel for ii in y_vel]
tau = []
for velocity in v_ch:
Ft = 0.5*C*S*ro_mars*pow(velocity, 2)
tau.append(Ft)
buoy = []
for velocity in v_ch:
Fb = ro_mars*g*(4/3*np.pi*pow(r, 3))
buoy.append(Fb)
after this point, I couldn't figure out how to plot to a projectile motion under this forces. In other words, I'm trying to compare the projectile motion of the mass under three circumstances
Mass only under the effect of gravity
Mass under the effect of gravity and air resistance
Mass under the effect of gravity, air resistance, and buoyancy
You must calculate each location based on the sum of forces at the given time. For this it is better to start from calculating the net force at any time and using this to calculate the acceleration, velocity and then position. For the following calculations, it is assumed that buoyancy and gravity are constant (which is not true in reality but the effect of their variability is negligible in this case), it is also assumed that the initial position is (0,0) though this can be trivially changed to any initial position.
F_x = tau_x
F_y = tau_y + bouyancy + gravity
Where tau_x and tau_y are the drag forces in the x and y directions, respectively. The velocities, v_x and v_y, are then given by
v_x = v_x + (F_x / (2 * m)) * dt
v_y = v_y + (F_y / (2 * m)) * dt
So the x and y positions, r_x and r_y, at any time t are given by the summation of
r_x = r_x + v_x * dt
r_y = r_y + v_y * dt
In both cases this must be evaluated from 0 to t for some dt where dt * n = t if n is the number of steps in summation.
r_x = r_x + V_i * np.cos(theta) * dt + (F_x / (2 * m)) * dt**2
r_y = r_y + V_i * np.sin(theta) * dt + (F_y / (2 * m)) * dt**2
The entire calculation can actually be done in two lines,
r_x = r_x + V_i * np.cos(theta) * dt + (tau_x / (2 * m)) * dt**2
r_y = r_y + V_i * np.sin(theta) * dt + ((tau_y + bouyancy + gravity) / (2 * m)) * dt**2
Except that v_x and v_y require updating at every time step. To loop over this and calculate the x and y positions across a range of times you can simply follow the below (edited) example.
The following code includes corrections to prevent negative y positions, since the given value of g is for the surface or Mars I assume this is appropriate - when you hit zero y and try to keep going you may end up with a rapid unscheduled disassembly, as we physicists call it.
Edit
In response to the edited question, the following example has been modified to plot all three cases requested - gravity, gravity plus drag, and gravity plus drag and buoyancy. Plot setup code has also been added
Complete example (edited)
import numpy as np
import matplotlib.pyplot as plt
def projectile(V_initial, theta, bouyancy=True, drag=True):
g = 9.81
m = 1
C = 0.47
r = 0.5
S = np.pi*pow(r, 2)
ro_mars = 0.0175
time = np.linspace(0, 100, 10000)
tof = 0.0
dt = time[1] - time[0]
bouy = ro_mars*g*(4/3*np.pi*pow(r, 3))
gravity = -g * m
V_ix = V_initial * np.cos(theta)
V_iy = V_initial * np.sin(theta)
v_x = V_ix
v_y = V_iy
r_x = 0.0
r_y = 0.0
r_xs = list()
r_ys = list()
r_xs.append(r_x)
r_ys.append(r_y)
# This gets a bit 'hand-wavy' but as dt -> 0 it approaches the analytical solution.
# Just make sure you use sufficiently small dt (dt is change in time between steps)
for t in time:
F_x = 0.0
F_y = 0.0
if (bouyancy == True):
F_y = F_y + bouy
if (drag == True):
F_y = F_y - 0.5*C*S*ro_mars*pow(v_y, 2)
F_x = F_x - 0.5*C*S*ro_mars*pow(v_x, 2) * np.sign(v_y)
F_y = F_y + gravity
r_x = r_x + v_x * dt + (F_x / (2 * m)) * dt**2
r_y = r_y + v_y * dt + (F_y / (2 * m)) * dt**2
v_x = v_x + (F_x / m) * dt
v_y = v_y + (F_y / m) * dt
if (r_y >= 0.0):
r_xs.append(r_x)
r_ys.append(r_y)
else:
tof = t
r_xs.append(r_x)
r_ys.append(r_y)
break
return r_xs, r_ys, tof
v = 30
theta = np.pi/4
fig = plt.figure(figsize=(8,4), dpi=300)
r_xs, r_ys, tof = projectile(v, theta, True, True)
plt.plot(r_xs, r_ys, 'g:', label="Gravity, Buoyancy, and Drag")
r_xs, r_ys, tof = projectile(v, theta, False, True)
plt.plot(r_xs, r_ys, 'b:', label="Gravity and Drag")
r_xs, r_ys, tof = projectile(v, theta, False, False)
plt.plot(r_xs, r_ys, 'k:', label="Gravity")
plt.title("Trajectory", fontsize=14)
plt.xlabel("Displacement in x-direction (m)")
plt.ylabel("Displacement in y-direction (m)")
plt.ylim(bottom=0.0)
plt.legend()
plt.show()
Note that this preserves and returns the time-of-flight in the variable tof.
Using vector notation, and odeint.
import numpy as np
from scipy.integrate import odeint
import scipy.constants as SPC
import matplotlib.pyplot as plt
V_initial = 30 # m/s
theta = np.pi/6 # 30
g = 3.711
m = 1 # I assume this is your mass
C = 0.47
r = 0.5
ro_mars = 0.0175
t_flight = 2*(V_initial*np.sin(theta)/g)
t = np.linspace(0, t_flight, 200)
pos0 = [0, 0]
v0 = [np.cos(theta) * V_initial, np.sin(theta) * V_initial]
def f(vector, t, C, r, ro_mars, apply_bouyancy=True, apply_resistance=True):
x, y, x_prime, y_prime = vector
# volume and surface
V = np.pi * 4/3 * r**3
S = np.pi*pow(r, 2)
# net weight bouyancy
if apply_bouyancy:
Fb = (ro_mars * V - m) * g *np.array([0,1])
else:
Fb = -m * g * np.array([0,1])
# velocity vector
v = np.array([x_prime, y_prime])
# drag force - corrected to be updated based on current velocity
# Ft = -0.5*C*S*ro_mars*pow(V_initial, 2)
if apply_resistance:
Ft = -0.5*C*S*ro_mars* v *np.linalg.norm(v)
else:
Ft = np.array([0, 0])
# resulting acceleration
x_prime2, y_prime2 = (Fb + Ft) / m
return x_prime, y_prime, x_prime2, y_prime2
sol = odeint(f, pos0 + v0 , t, args=(C, r, ro_mars))
plt.plot(sol[:,0], sol[:, 1], 'g', label='tray')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.show()
Note that I corrected your drag force to use the actual (not initial) velocity, I do not know if that was your mistake or it was on purpose.
Also please check the documentation for odeint to understand better how to turn a second order ODE (like the one in your problem) to a first order vector ODE.
To remove air resistance or bouyancy, set apply_bouyancy and apply_resistance to True or False by adding them to the args=(...)
How can I find the closet distance between my trajectory and (384400,0,0)?
Also, how can I the distance from (384400,0,0) to the path at time t = 197465?
I understand that the arrays have the data but is there a way to have it check the distance of all the points (x,y,z) and return what I am looking for?
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -129.21 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
# spacecraft x location relative to the earth
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[5] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[4] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.arange(0.0, 320000.0, 1) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
plt.show()
To find the distance along each point in the trajectory:
my_x, my_y, my_z = (384400,0,0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.sqrt(np.power(delta_x, 2) +
np.power(delta_y, 2) +
np.power(delta_z, 2))
And then to find the min and that specific distance:
i = np.argmin(distance)
t_min = i / UNITS_SCALE # I can't tell how many units to a second. Is it 1.6?
d_197465 = distance[int(197465 * UNITS_SCALE)]
PS, I'm no rocket scientist, and thus haven't actually checked the stuff above x, y, z, x2, y2, z2 = u.T. I'm assuming that those are your trajectory's position and velocity?
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I followed this method from my other post [distance between a point and a curve[(find the distance between a point and a curve python) but something is wrong. The values aren't accurate.
I plotted this same trajectory in Mathematica and checked a few distances and I have found distances as low as 18000 where python is returning a minimum of 209000.
What is going wrong in the code at the bottom?
EDIT There was an error in this code everything checks out now. Thanks.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -129.21 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
# spacecraft x location relative to the earth
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[5] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[4] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.arange(0.0, 320000.0, 1) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
plt.show()
my_x, my_y, my_z = (384400,0,0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.array([np.sqrt(delta_x ** 2 + delta_y ** 2 +
delta_z ** 2)])
print(distance.min())
Corrected code
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -129.21 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
# spacecraft x location relative to the earth
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[4] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[3] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.arange(0.0, 320000.0, 1) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
plt.show()
my_x, my_y, my_z = (384400,0,0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.array([np.sqrt(delta_x ** 2 + delta_y ** 2 +
delta_z ** 2)])
print(distance.min())