Related
I need to simulate a penalty where the ball shoots in every direction. For this I use solve_ivp and I terminate the integration when the ball crosses the backline. When this happens I want to use the values found when the integration stops to see if the ball at that point is within the dimensions of the goal (and count it as a goal). However, solution.y does not give the required precision that I want. The desired values are within solution.y_events, however, I can't seem to be able to get receive them. I also can't find information about this online. My code:
import numpy as np
from scipy import integrate
from scipy import constants
import matplotlib.pyplot as plt
#### Constants
# Number of simulations
number_of_penalty_shots = 10
# Angle of the shots
theta = np.random.uniform(0, 2.0*np.pi, number_of_penalty_shots)
phi = np.random.uniform(0, np.pi, number_of_penalty_shots)
# Velocity of the ball
v_magnitude = 80
### Starting Position Ball (defined as the penalty stip)
pos_x = 0.0
pos_y = 0.0
pos_z = 0.0
in_position = np.array([pos_x, pos_y, pos_z]) # Inital position in m
def homo_magnetic_field(t, vector):
vx = vector[3] # dx/dt = vx
vy = vector[4] # dy/dt = vy
vz = vector[5] # dz/dt = vz
# ax = -0.05*vector[3] # dvx/dt = ax
# ay = -0.05*vector[4] # dvy/dy = ay
# az = -0.05*vector[5] - constants.g #dvz/dt = az
ax = 0
ay = 0
az = 0
dvectordt = (vx,vy,vz,ax,ay,az)
return(dvectordt)
def goal(t, vector):
return vector[1] - 11
def own_goal(t,vector):
return vector[1] + 100
def ground(t,vector):
return vector[2]
goal.terminal=True
own_goal.terminal=True
def is_it_goal(vector):
if vector.status == 1:
if (vector.y[1][len(vector.y[1])-1] > 0) and (-3.36 < vector.y[0][len(vector.y[1])-1] < 3.36) and (vector.y[2][len(vector.y[1])-1] < 2.44):
print("GOAAAAAAAAAAAAL!")
elif (vector.y[1][len(vector.y[1])-1] < 0) and (-3.36 < vector.y[0][len(vector.y[1])-1] < 3.36) and (vector.y[2][len(vector.y[1])-1] < 2.44):
print("Own goal?! Why?")
else: print("Awwwwh")
else: print("Not even close, lol")
# Integrating
time_range = (0.0, 10**2)
for i in range(number_of_penalty_shots):
v_x = v_magnitude*np.sin(phi[i])*np.cos(theta[i])
v_y = v_magnitude*np.sin(phi[i])*np.sin(theta[i])
v_z = v_magnitude*np.cos(phi[i])
in_velocity = np.array([v_x, v_y, v_z])
initial_point = np.array([in_position, in_velocity])
start_point = initial_point.reshape(6,)
solution = integrate.solve_ivp(homo_magnetic_field , time_range, start_point,events=(goal, own_goal))
is_it_goal(solution)
Here I want to change vector.y[1][len(vector.y[1])-1] into something like vector.y_events...
I used the skeleton of a code modeling the three body problem. However, I am trying to figure out why the vectors will not work. I have commented them out (lines 56-60) so that I can see the rest of the program working. I'll attach the code so you can see the error. It is an error telling me the specs for a vector, but I don't see why the input wouldn't work. Thanks !
import numpy as np
import matplotlib.pyplot as plt
from vpython import *
from IPython.display import display
scene = display(title = "Earth's Orbit", width = 500, height = 500, range = 3.e11)
# #
# scene.autoscale = 0 # Turn off auto scaling of display
#
# Define the Sun and the Earth objects.
#
sun = sphere(color = color.yellow)
earth = sphere(color = color.blue)
venus = sphere(color = color.red)
# Gravitational constant (Nm**2/kg**2)
G = 6.67 * 10 ** -11
sun.pos = vector(0, 0, 0) # Initial Sun position (m)
earth.pos = vector(0, -149.6 * 10 ** 7, 149.6 * 10 ** 9) # Initial Earth position (m)
venus.pos = vector(1.0820948 * 10 **11, -1.0820948 * 10 **11, 0) # Initial Venus position (m)
rhat = -norm(earth.pos) # Getting Magnitude, probably going touse normalized vectors for simplicity
sun.mass = 2 * 10 ** 30 # Mass of the Sun (kg)
earth.mass = 6 * 10 ** 24 # Mass of the Earth (kg)
venus.mass = 4.867 * 10 ** 24 # Mass of Venus (kg)
earth.velocity = vector(30 * 10 ** 3, 0, 0)
venus.velocity = vector(35.02 * 10 ** 3, 0, 0)
# Initial velocity in seconds (THIS IS WHERE WE CAN CHANGE THINGS UP BUT BE SENSIBLE)
dt = 86000
#
total = 0 #Initializes the totl elpsed time
#
# Scale factors to control how big the Earth and Sun are drawn in the display
#
sun.scale = 1e1
earth.scale = 5e2
venus.scale = earth.scale
#
sun.radius = 7.e8 * sun.scale
earth.radius = 6.4e6 * earth.scale
venus.radius = 6.052e6 * venus.scale
#
#Initialize the momentum and path of the Earth
#
earth.momentum = earth.mass * earth.velocity # momentum of Earth
venus.momentum = venus.mass * venus.velocity # momentum of Venus
earth.trail = curve(color = earth.color) # Defines Earth's path
# Set initial position of the Earth
earth.trail.append(pos = earth.pos)
#
# Define an arrow thata points from the origin to the Earth
#
##rearrow = arrow(pos = (0, 0, 0) ,axis = earth.pos,
## color = earth.color, shaftwidth = 1e6)
##momentumArrow = arrow(pos = earth.pos, axis = earth.momentum,
## color = earth.color, shaftwidth = 1e6)
#
tmax = 3600 * 24 * 364.25 # Number of seconds in a year
#
# Start of the loop structure
#
while(True):
#
rate(100) # limit the loop to a maximum of 100 times per second
#
# Fill in the next 3 lines with the correct expressions
earthToSun = -norm(earth.pos)
venusToSun = -norm(venus.pos)
earthToVenus = -norm(earth.pos - venus.pos)
# Compute the force that the Sun exerts on the Earth and added Venus's influence
earth.force = ((G * earth.mass * sun.mass) / (mag(earth.pos)) ** 2 * rhat
+ G * venus.mass * earth.mass / mag(earth.pos - venus.pos) ** 2 * earthToVenus)
earth.momentum = earth.momentum + earth.force * \
dt # Update Earth's momentum
# Let's updaate Earth's position
earth.pos = earth.pos + (earth.momentum / earth.mass) * dt
forceEarth = (G * earth.mass * venus.mass) / (mag(earth.pos - venus.pos)) ** 2 * earthToVenus
forceSun = G * venus.mass * sun.mass / (mag(venus.pos)) ** 2 * venusToSun
venus.force = forceEarth + forceSun
venus.momentum += venus.force * dt
venus.pos += (venus.momentum / venus.mass) * dt
momentumArrow.pos = earth.pos
momentumArrow.axis = earth.momentum * 10 ** -18
earth.trail.append(pos = earth.pos) # Updates Earth' trail
rearrow.axis = earth.pos # Move Earth's position arrow
total = total + dt #Increment the Time
#
# Print
#
print(earth.pos)
(0,0,0) is not a vector, it's a Python tuple. You need to say vector(0,0,0) or vec(0,0,0).
Excuse me for the length of the title please but this is a pretty specific question. I'm currently simulating a launch of a rocket to mars in the 2022 launch window and I noticed that my rocket is a far distance away from Mars, even though it's traveling in the right direction. After simplifying my code to narrow down the problem, I simply plotted the orbits of the Earth and Mars (Using data from NASA's SPICE library) and propagated the position and velocity given to me by the lambert solver I implemented (Universal variables) to plot the final orbit of the rocket.
I'm only letting the Sun's gravity effect the rocket, not the Earth or Mars, to minimize my problem space. Yet even though I've simplified my problem so far, the intersection between Mars' and my rocket's orbits happens well before the time of flight has been simulated all the way, and the minimum distance between the two bodies is more than a million kilometers at all times.
That being said, something must be wrong but I cannot find the problem. I've made sure the lambert solver code I copied is correct by comparing it to Dario Izzo's method and both gave the same results. Furthermore, I've also checked that my orbit propagator works by propagating Mars' and the Earth's orbits and comparing those ellipses to the data from SPICE.
In conclusion, I assume this must be a stupid little mistake I made somewhere, but cannot find because I lack experience in this field. Thank you for any help! :)
This is the JupyterLab notebook I used:
import numpy as np
import matplotlib.pyplot as plt
import json
import math
import spiceypy as spice
# Physics
G = 6.6741e-11
class Entity:
def __init__(self, x, v, m, do_gravity):
self.x = x
self.v = v
self.a = np.array([0,0,0])
self.m = m
self.do_gravity = do_gravity
def apply_force(self, f):
self.a = np.add(self.a, f / self.m);
def step(self, dt):
self.v = np.add(self.v, self.a * dt)
self.x = np.add(self.x, self.v * dt)
self.a = np.array([0,0,0])
class StaticEntity(Entity):
def __init__(self, body, t, do_gravity):
super().__init__(self.get_state(body, t)[:3], self.get_state(body, t)[3:], self.get_mass(body), do_gravity)
self.body = body
self.t = t
def step(self, dt):
self.t += dt
state = self.get_state(self.body, self.t)
self.x = state[:3]
self.v = state[3:]
#classmethod
def get_state(self, body, t):
[state, lt] = spice.spkezr(body, t, "J2000", "NONE", "SSB")
return state * 1000
#classmethod
def get_mass(self, body):
[dim, gm] = spice.bodvrd(body, "GM", 1)
return gm * 1e9 / G
def get_position(self, t):
return self.get_state(self.body, t)[:3]
def get_velocity(self, t):
return self.get_state(self.body, t)[3:]
class Propagator:
def __init__(self, entities):
self.entities = entities
def step(self, dt):
for e1 in self.entities:
for e2 in self.entities:
if (e1 is e2) or (not e1.do_gravity) or isinstance(e2, StaticEntity):
continue
diff = np.subtract(e1.x, e2.x)
fg = G * e1.m * e2.m / np.dot(diff, diff)
force = fg * diff / np.linalg.norm(diff)
e2.apply_force(force)
for entity in self.entities:
entity.step(dt)
# Lambert solver
def C2(psi):
if psi >= 0.0:
sp = math.sqrt(psi)
return (1 - math.cos(sp)) / psi
else:
sp = math.sqrt(-psi)
return (1 - math.cosh(sp)) / psi
def C3(psi):
if psi >= 0.0:
sp = math.sqrt(psi)
return (sp - math.sin(sp)) / (psi * sp)
else:
sp = math.sqrt(-psi)
return (sp - math.sinh(sp)) / (psi * sp)
def lambert_solve(r1, r2, tof, mu, iterations, tolerance):
R1 = np.linalg.norm(r1)
R2 = np.linalg.norm(r2)
cos_a = np.dot(r1, r2) / (R1 * R2)
A = math.sqrt(R1 * R2 * (1.0 + cos_a))
sqrt_mu = math.sqrt(mu)
if A == 0.0:
return None
psi = 0.0
psi_lower = -4.0 * math.pi * math.pi
psi_upper = 4.0 * math.pi * math.pi
c2 = 1.0 / 2.0
c3 = 1.0 / 6.0
for i in range(iterations):
B = R1 + R2 + A * (psi * c3 - 1.0) / math.sqrt(c2)
if A > 0.0 and B < 0.0:
psi_lower += math.pi
B = -B
chi = math.sqrt(B / c2)
chi3 = chi * chi * chi
tof_new = (chi3 * c3 + A * math.sqrt(B)) / sqrt_mu
if math.fabs(tof_new - tof) < tolerance:
f = 1.0 - B / R1
g = A * math.sqrt(B / mu)
g_dot = 1.0 - B / R2
v1 = (r2 - f * r1) / g
v2 = (g_dot * r2 - r1) / g
return (v1, v2)
if tof_new <= tof:
psi_lower = psi
else:
psi_upper = psi
psi = (psi_lower + psi_upper) * 0.5
c2 = C2(psi)
c3 = C3(psi)
return None
# Set up solar system
spice.furnsh('solar_system.tm')
inject_time = spice.str2et('2022 Sep 28 00:00:00')
exit_time = spice.str2et('2023 Jun 1 00:00:00')
sun = StaticEntity("Sun", inject_time, True)
earth = StaticEntity("Earth", inject_time, False)
mars = StaticEntity("Mars Barycenter", inject_time, False)
(v1, v2) = lambert_solve(earth.get_position(inject_time), mars.get_position(exit_time), exit_time - inject_time, G * sun.m, 1000, 1e-4)
rocket = Entity(earth.x, v1, 100000, False)
propagator = Propagator([sun, earth, mars, rocket])
# Generate data
earth_pos = [[], [], []]
mars_pos = [[], [], []]
rocket_pos = [[], [], []]
t = inject_time
dt = 3600 # seconds
while t < exit_time:
propagator.step(dt)
earth_pos[0].append(earth.x[0])
earth_pos[1].append(earth.x[1])
earth_pos[2].append(earth.x[2])
mars_pos[0].append(mars.x[0])
mars_pos[1].append(mars.x[1])
mars_pos[2].append(mars.x[2])
rocket_pos[0].append(rocket.x[0])
rocket_pos[1].append(rocket.x[1])
rocket_pos[2].append(rocket.x[2])
t += dt
# Plot data
plt.figure()
plt.title('Transfer orbit')
plt.xlabel('x-axis')
plt.ylabel('y-axis')
plt.plot(earth_pos[0], earth_pos[1], color='blue')
plt.plot(mars_pos[0], mars_pos[1], color='orange')
plt.plot(rocket_pos[0], rocket_pos[1], color='green')
EDIT:
I recently remodeled my code so that it uses orbit class to represent the entities. This actually gave me acceptable results, even though the code is, in theory, not doing anything differently (as far as I can tell; obviously something must be different)
def norm(a):
return np.dot(a, a)**0.5
def fabs(a):
return -a if a < 0 else a
def newton_raphson(f, f_dot, x0, n):
res = x0
for i in range(n):
res = res - f(res) / f_dot(res)
return res
def get_ephemeris(body, time):
state, _ = sp.spkezr(body, time, "J2000", "NONE", "SSB")
return np.array(state[:3]) * ap.units.km, np.array(state[3:]) * ap.units.km / ap.units.s
def get_mu(body):
_, mu = sp.bodvrd(body, "GM", 1)
return mu * ap.units.km**3 / ap.units.s**2
class orbit:
def __init__(self, position, velocity, mu):
self.position = position
self.velocity = velocity
self.mu = mu
#staticmethod
def from_body(name, center, time):
return static_orbit(name, center, time)
def get_ephemerides(self, t, dt):
time = 0
positions = []
velocities = []
#M = self.M
position = self.position
velocity = self.velocity
delta_t = dt * ap.units.s
t1 = t * ap.units.s
while time < t1:
g = self.mu / np.dot(position, position)
g_vec = g * -position / norm(position)
velocity = np.add(velocity, g_vec * delta_t)
position = np.add(position, velocity * delta_t)
positions.append(position)
velocities.append(velocity)
time = time + delta_t
return positions, velocities
class static_orbit(orbit):
def __init__(self, name, center, time):
p, v = get_ephemeris(name, time)
pc, vc = get_ephemeris(center, time)
super().__init__(p - pc, v - vc, get_mu(center))
self.name = name
self.center = center
self.time = time
def get_ephemerides(self, t, dt):
time = 0
positions = []
velocities = []
while time < t:
p, v = get_ephemeris(self.name, time + self.time)
pc, vc = get_ephemeris(self.center, time + self.time)
positions.append(p - pc)
velocities.append(v - vc)
time += dt
return positions, velocities
sp.furnsh('solar_system.tm')
t1 = sp.str2et('2022 Sep 28 00:00:00')
t2 = sp.str2et('2023 Jun 10 00:00:00')
eo = orbit.from_body("Earth", "Sun", t1)
mo = orbit.from_body("Mars Barycenter", "Sun", t1)
earth_x, earth_v = eo.get_ephemerides(t2 - t1, 3600)
mars_x, mars_v = mo.get_ephemerides(t2 - t1, 3600)
l = lambert(earth_x[0], mars_x[-1], t2 - t1, get_mu("Sun"), 1000, 1e-6)
ro = orbit(earth_x[0], l.v1, get_mu("Sun"))
rocket_x, rocket_v = ro.get_ephemerides(t2 - t1, 60)
earth_x = np.array(earth_x)
mars_x = np.array(mars_x)
rocket_x = np.array(rocket_x)
fig = go.Figure()
fig.add_trace(go.Scatter3d(x=earth_x[:,0], y=earth_x[:,1], z=earth_x[:,2], marker_size=1, marker_color='blue'))
fig.add_trace(go.Scatter3d(x=mars_x[:,0], y=mars_x[:,1], z=mars_x[:,2], marker_size=1, marker_color='orange'))
fig.add_trace(go.Scatter3d(x=rocket_x[:,0], y=rocket_x[:,1], z=rocket_x[:,2], marker_size=1, marker_color='green'))
fig.show()
This method generated following plot:
Also, before this is mentioned again, I have varied my integration step size and lambert solver tolerance to no avail, the result was qualitatively different.
So, I managed to figure out what the problem was after much head-scratching. I was simply not taking into account that the Sun is not located at (0,0,0) in my coordinate system. I thought this was negligible, but that is what made the difference. In the end, I simply passed the difference between the Earth and Mars's and the Sun's position vectors and passed those into the Lambert solver. This finally gave me the desired results.
The reason that the error ended up being so "small" (It didn't seem like an obvious bug at first) was because my coordinates are centered at the solar system barycenter which is a few million kilometers away from the Sun, as one would expect.
Thanks for the comments!
First post here and I'm jumping in to python with both feet.
My project is to attempt to calculate the position of a underwater robot using only IMU sensors and a speed table.
I am very new to programming and I'm sure I'll get a lot of great feedback on the attached code, but the step I'm currently stuck on is creating a feedback loop between:
UTC[2] (status of the GPS A=available V=not available),
LATD/LOND (GPS position in decimal degrees), and
IMU_LAT/IMU_LON (IMU position in decimal degrees)
The idea would be that if UTC[2] was "A" the logic would equally average IMU_LAT/IMU_LON and LATD/LOND but if UTC[2] was "V" it would only calculate the position based on the last position recorded and IMU_north/IMU_east (offsets based on heading and acceleration values.
import time, inspect
import IMU
import serial
import datetime
import os
import math
import logging
log = open(time.strftime("%Y%m%d-%H%M%S")+'_GSPData.csv','w')
#f.write("UTC TIME,NAVSTATUS,LAT,LON,HDG,SPD,X,Y,Z")
RAD_TO_DEG = 57.29578
M_PI = 3.14159265358979323846
G_GAIN = 0.070 # [deg/s/LSB] If you change the dps for gyro, you need to update this value accordingly
AA = 0.40 # Complementary filter constant
magXmin = 0
magYmin = 0
magZmin = 0
magXmax = 0
magYmax = 0
magZmax = 0
gyroXangle = 0.0
gyroYangle = 0.0
gyroZangle = 0.0
CFangleX = 0.0
CFangleY = 0.0
IMU.detectIMU() #Detect if BerryIMUv1 or BerryIMUv2 is connected.
IMU.initIMU() #Initialise the accelerometer, gyroscope and compass
a = datetime.datetime.now()
ser = serial.Serial('/dev/serial0', 9600)
def truncate(n, decimals=0):
multiplier = 10 ** decimals
return int(n * multiplier) / multiplier
log.write("UTC,NAVSTAT,LAT,LON,HDG,SPD,xm/s,ym/s,zm/s")
log.write("\n")
try:
while True:
#Read the GPS, accelerometer, gyroscope and magnetometer values
NMEA = ser.readline()
NMEA_str_data = NMEA.decode('utf-8')
NMEA_data_arr=NMEA_str_data.split()
UTC = NMEA_str_data.split(',')
GYRx = IMU.readGYRx()
GYRy = IMU.readGYRy()
GYRz = IMU.readGYRz()
ACCx = IMU.readACCx()
ACCy = IMU.readACCy()
ACCz = IMU.readACCz()
#output the values of the accelerometer in m/s
yG = ((ACCx * 0.244)/1000)*9.80665
xG = ((ACCy * 0.244)/1000)*9.80665
zG = ((ACCz * 0.244)/1000)*9.80665
MAGx = IMU.readMAGx()
MAGy = IMU.readMAGy()
MAGz = IMU.readMAGz()
#Apply compass calibration
MAGx -= (magXmin + magXmax) /2
MAGy -= (magYmin + magYmax) /2
MAGz -= (magZmin + magZmax) /2
##Calculate loop Period(LP). How long between Gyro Reads
b = datetime.datetime.now() - a
a = datetime.datetime.now()
LP = b.microseconds/(1000000*1.0)
outputString = "Loop Time %5.2f " % ( LP )
#Convert Gyro raw to degrees per second
rate_gyr_x = GYRx * G_GAIN
rate_gyr_y = GYRy * G_GAIN
rate_gyr_z = GYRz * G_GAIN
#Calculate the angles from the gyro.
gyroXangle+=rate_gyr_x*LP
gyroYangle+=rate_gyr_y*LP
gyroZangle+=rate_gyr_z*LP
#Convert Accelerometer values to degrees
AccXangle = (math.atan2(ACCy,ACCz)*RAD_TO_DEG)
AccYangle = (math.atan2(ACCz,ACCx)+M_PI)*RAD_TO_DEG
#convert the values to -180 and +180
if AccYangle > 90:
AccYangle -= 270.0
else:
AccYangle += 90.0
#Complementary filter used to combine the accelerometer and gyro values.
CFangleX=AA*(CFangleX+rate_gyr_x*LP) +(1 - AA) * AccXangle
CFangleY=AA*(CFangleY+rate_gyr_y*LP) +(1 - AA) * AccYangle
#Calculate heading
heading = 180 * math.atan2(MAGy,MAGx)/M_PI
#Only have our heading between 0 and 360
if heading < 0:
heading += 360
####################################################################
###################Tilt compensated heading#########################
####################################################################
#Normalize accelerometer raw values.
accXnorm = ACCx/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
accYnorm = ACCy/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
accZnorm = ACCz/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
Zms_norm = zG-9.80665
Yms_norm = yG
Xms_norm = xG
#Calculate course
Course = (180*math.atan2(Xms_norm,Yms_norm)/M_PI)
#Only have our course between 0 and 360
if Course < 0:
Course +=360
#Calculate pitch and roll
pitch = math.asin(accXnorm)
roll = -math.asin(accYnorm/math.cos(pitch))
#Calculate the new tilt compensated values
magXcomp = MAGx*math.cos(pitch)+MAGz*math.sin(pitch)
#The compass and accelerometer are orientated differently on the LSM9DS0 and LSM9DS1 and the Z axis on the compass
#is also reversed. This needs to be taken into consideration when performing the calculations
if(IMU.LSM9DS0):
magYcomp = MAGx*math.sin(roll)*math.sin(pitch)+MAGy*math.cos(roll)-MAGz*math.sin(roll)*math.cos(pitch) #LSM9DS0
else:
magYcomp = MAGx*math.sin(roll)*math.sin(pitch)+MAGy*math.cos(roll)+MAGz*math.sin(roll)*math.cos(pitch) #LSM9DS1
#Calculate tilt compensated heading
tiltCompensatedHeading = 180 * math.atan2(magYcomp,magXcomp)/M_PI
if tiltCompensatedHeading < 0:
tiltCompensatedHeading += 360
#convert IMU readings to northings and eastings
IMU_north= (math.cos(tiltCompensatedHeading))*(Yms_norm+Xms_norm)
IMU_east= (math.sin(tiltCompensatedHeading))*(Yms_norm+Xms_norm)
#convert IMU_north to D.D
IMU_north_D= IMU_north/110723.41272
#Convert IMU_east to d.d
IMU_east_D= IMU_east/103616.02936
############################ END ##################################
#"%am/s": no rounding "%bm/s": unsupported "%cm/s": unsupported
#"%dm/s": whole numbers "%em/s": scientific notation "%fm/s": six digits
#"%gm/s": five digits
if NMEA_str_data.startswith('$GNRMC'):
if UTC[2] =="V":
#print("GPS unavaliable","heading",round(tiltCompensatedHeading,2),",course",round(Course,2),xG,yG,zG,"IMU_LAT","IMU_LON")
print("UTC",truncate(float(UTC[1]),0),",IMU",",LAT",",LON",",heading",round(tiltCompensatedHeading,2),",course",round(Course,2),truncate(IMU_north,4),truncate(IMU_east,4))
#log the output GPS invalid
log.write(UTC[1]+','+UTC[2]+','+""+','""+','+str(round(tiltCompensatedHeading,2))+','+UTC[7]+','+str(IMU_north)+','+str(IMU_east))
else:
#convert UTC from DDMM.MMM to DD.DDDD
if UTC[4] =="N":
LATD= (truncate(float(UTC[3]),-2)/100)+((float(UTC[3])-(truncate(float(UTC[3]),-2)))/60)
else:
LATD= -(truncate(float(UTC[3]),-2)/100)+((float(UTC[3])-(truncate(float(UTC[3]),-2)))/60)
if UTC[6] =="E":
LOND= (truncate(float(UTC[5]),-2)/100)+((float(UTC[5])-(truncate(float(UTC[5]),-2)))/60)
else:
LOND= -(truncate(float(UTC[5]),-2)/100)+((float(UTC[5])-(truncate(float(UTC[5]),-2)))/60)
#calculate IMU_LAT
IMU_LAT= LATD+IMU_north_D
#Calculate IMU_LON
IMU_LON= LOND+IMU_east_D
#write the output
print("UTC",truncate(float(UTC[1]),0),",GPS",",LAT",truncate(LATD,5),truncate(IMU_LAT,5),",LON",truncate(LOND,5),truncate(IMU_LON,5),",heading",round(tiltCompensatedHeading,2),",course",round(Course,2),UTC[8],",speed",truncate(float(UTC[7]),2))
#log the output GPS valid
log.write(UTC[1]+','+UTC[2]+','+str(LATD)+','+str(LOND)+','+str(round(tiltCompensatedHeading,2))+','+UTC[7]+','+str(IMU_north)+','+str(IMU_east))
log.write("\n")
#slow program down a bit, makes the output more readable
time.sleep(0.5)
#print(" aX = %fG aY =%fG aZ =%fG " % ( ACCx, ACCy, ACCz))
#slow program down a bit, makes the output more readable
#time.sleep(0.5)
except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
print ("Done.\nExiting.")
log.close()
Like I said I'm new and I'm sure you pros are going to tell me its really sloppy but I will gladly accept any constructive criticism.
Thanks, Troy
I would like to make random mouse movements in specified rectangle area (limited with coordinates x1, y1, x2, y2, x3, y3, x4, y4).
Movements should be smooth, random, not just straight lines, go randomly up/down/left/right/etc for specified time duration.
Could you give me a hand or working example I can learn from?
many thanks
This code works on Windows only. You can experiment with the parameters inside the random_movement function to get better results. Good luck!
import ctypes
import random
import time
import math
def move_mouse(pos):
x_pos, y_pos = pos
screen_size = ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1)
x = 65536L * x_pos / screen_size[0] + 1
y = 65536L * y_pos / screen_size[1] + 1
return ctypes.windll.user32.mouse_event(32769, x, y, 0, 0)
def random_movement(top_left_corner, bottom_right_corner, min_speed=100, max_speed=200):
'''speed is in pixels per second'''
x_bound = top_left_corner[0], bottom_right_corner[0]
y_bound = top_left_corner[1], bottom_right_corner[1]
pos = (random.randrange(*x_bound),
random.randrange(*y_bound))
speed = min_speed + random.random()*(max_speed-min_speed)
direction = 2*math.pi*random.random()
def get_new_val(min_val, max_val, val, delta=0.01):
new_val = val + random.randrange(-1,2)*(max_val-min_val)*delta
if new_val<min_val or new_val>max_val:
return get_new_val(min_val, max_val, val, delta)
return new_val
steps_per_second = 35.0
while True:
move_mouse(pos)
time.sleep(1.0/steps_per_second)
speed = get_new_val(min_speed, max_speed, speed)
direction+=random.randrange(-1,2)*math.pi/5.0*random.random()
new_pos = (int(round(pos[0]+speed*math.cos(direction)/steps_per_second)),
int(round(pos[1]+speed*math.sin(direction)/steps_per_second)))
while new_pos[0] not in xrange(*x_bound) or new_pos[1] not in xrange(*y_bound):
direction = 2*math.pi*random.random()
new_pos = (int(round(pos[0]+speed*math.cos(direction)/steps_per_second)),
int(round(pos[1]+speed*math.sin(direction)/steps_per_second)))
pos=new_pos
Example:
random_movement((300,300),(600,600))
For random smooth movements constrained to a rectangle I'd try to use Lissajous curves with randomly changing coefficients.
Here is a demo of random X,Y positions you can play as your requirements:
from time import sleep
import pyautogui
import numpy as np
# Check your screen size
print(pyautogui.size())
count=0
while count<1000:
x=np.random.randint(1,1792)
y=np.random.randint(1,1120)
pyautogui.moveTo(x, y)
print(x)
print(y)
sleep(20)
count+=1
Note: install first
pip3 install pyautogui
I made this based on Piotr Dabkowski's code, with some extra features (taking random breaks, random scrolls, and users can end early by right clicking). This works for Python 3, and again, for Windows only.
import ctypes
import random
import time
import math
import win32gui
xmax = ctypes.windll.user32.GetSystemMetrics(0)
ymax = ctypes.windll.user32.GetSystemMetrics(1)
def get_position():
_, _, (x,y) = win32gui.GetCursorInfo()
return (x,y)
def move_mouse(pos):
x_pos, y_pos = pos
x = int(65536 * x_pos / xmax + 1)
y = int(65536 * y_pos / ymax + 1)
return ctypes.windll.user32.mouse_event(32769, x, y, 0, 0)
def start(t=30, min_speed=10, max_speed=500, x_bound=[0,xmax], y_bound=[0,ymax],
p_break = 0.005, break_range = (10, 60), p_scroll = 0.01, scroll_range = (100, 1000)):
def get_new_speed(min_val, max_val, val, delta=0.01):
new_val = val + random.randrange(-1,2)*(max_val-min_val)*delta
if new_val<min_val or new_val>max_val:
return get_new_speed(min_val, max_val, val, delta)
return new_val
steps_per_second = 35.0
print('Started.')
endtime = time.time() + int(t*60)
# Initialize position, speed and direction
pos = get_position()
speed = min_speed + random.random()*(max_speed-min_speed)
direction = 2*math.pi*random.random()
inside_boundary = False
right_clicked = False
# Keep moving mouse until end time, or until right click
while (not right_clicked) and (time.time() < endtime):
if ctypes.windll.user32.GetKeyState(0x02) not in [0,1]:
right_clicked = True
time.sleep(1.0/steps_per_second)
# Taking a break of random duration
duration = random.randint(*break_range) # in unit of seconds
break_endtime = time.time() + duration
r = random.random()
if (1-p_break) <= r < 1:
# Keep checking for right click to exit loop
while (not right_clicked) and (time.time() < break_endtime):
if ctypes.windll.user32.GetKeyState(0x02) not in [0,1]:
right_clicked = True
time.sleep(1.0/steps_per_second)
time_left = break_endtime - time.time()
print('Paused %d / %ds' % (time_left,duration) + ' '*50, end='\r')
pos = get_position()
print(' '*50, end='\r')
# Random scroll
r = random.random()
lines = random.randint(*scroll_range)
sign = random.random()
sign = -1 if sign < 0.5 else 1
if (1-p_scroll) <= r < 1:
time.sleep(random.random())
ctypes.windll.user32.mouse_event(2048, 0, 0, sign*lines, 0)
time.sleep(random.random())
pos = get_position()
# Random move
move_mouse(pos)
time_left = endtime - time.time()
print('Running (time left: %ds)' % time_left + ' '*50, end='\r')
if (pos[0] in range(*x_bound)) and (pos[1] in range(*y_bound)):
inside_boundary = True
# Update position, speed and direction
speed = get_new_speed(min_speed, max_speed, speed)
direction+=random.randrange(-1,2)*math.pi/5.0*random.random()
new_pos = (int(round(pos[0]+speed*math.cos(direction)/steps_per_second)),
int(round(pos[1]+speed*math.sin(direction)/steps_per_second)))
# Once mouse position is inside boundary, new position must also be inside
if inside_boundary:
while new_pos[0] not in range(*x_bound) or new_pos[1] not in range(*y_bound):
direction = 2*math.pi*random.random()
new_pos = (int(round(pos[0]+speed*math.cos(direction)/steps_per_second)),
int(round(pos[1]+speed*math.sin(direction)/steps_per_second)))
pos=new_pos
print('Stopped.' + ' ' * 50)
For performing the movements there is a third-party package call PyUserInput that will allow you to control mouse or keyboard, and it is cross-platform. Install it using pip:
$ pip install PyUserInput
For doing smooth movements you can try what 9000 proposes in his answer.