Advertisement
Guest User

Untitled

a guest
May 27th, 2015
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.05 KB | None | 0 0
  1. from __future__ import print_function, unicode_literals, division, absolute_import
  2. import math
  3. import numpy
  4.  
  5. def unit_vector(vector):
  6. """ Returns the unit vector of the vector. """
  7. return vector / numpy.linalg.norm(vector)
  8.  
  9. def angle_between(v1, v2):
  10. """ Returns the angle in radians between vectors 'v1' and 'v2'::
  11.  
  12. >>> angle_between((1, 0, 0), (0, 1, 0))
  13. 1.5707963267948966
  14. >>> angle_between((1, 0, 0), (1, 0, 0))
  15. 0.0
  16. >>> angle_between((1, 0, 0), (-1, 0, 0))
  17. 3.141592653589793
  18. """
  19. v1_u = unit_vector(v1)
  20. v2_u = unit_vector(v2)
  21. angle = numpy.arccos(numpy.dot(v1_u, v2_u))
  22. if numpy.isnan(angle):
  23. if (v1_u == v2_u).all():
  24. return 0.0
  25. else:
  26. return numpy.pi
  27. return angle
  28.  
  29.  
  30.  
  31.  
  32. class TrajectoryLeg:
  33. def __init__(self, thrustvector=None, t=0):
  34. if thrustvector is None:
  35. thrustvector = numpy.zeros(3)
  36. self.thrustvector = thrustvector #thrust vector
  37. self.t = t #length of time to apply the thrust
  38.  
  39. def __repr__(self):
  40. return repr( (self.thrustvector, self.t) )
  41.  
  42. #
  43. #* Computes the optimal trajectory from A to B in a solar system using classical
  44. #* mechanics. Finds the trajectory legs (each expressed as a thrust vector and a
  45. #* length of time) that as soon as possible will move the ship to the same
  46. #* position and velocity as the destination.
  47. #* <P>
  48. #* The objective is to plan the space trajectory needed to rendezvous with a
  49. #* given destination within the solar system. It is needed to implement the
  50. #* game's autopilot which is used by both computer-controlled ships and players.
  51. #* The trajectory must be computed in advance since simply going in the
  52. #* direction of the destination would result in arriving with a speed very
  53. #* different from that of the destination object. We need to know beforehand
  54. #* when to 'gas' and when to 'break', and in what precise directions to do this.
  55. #* <P>
  56. #* The planned trajectory should be fairly efficient (primarily regarding travel
  57. #* time, but also regarding fuel consumption) and preferably not cheat by using
  58. #* different physics than the player experiences with the manual controls. It
  59. #* must also be fairly fast to compute - it's an MMO game and there can be many
  60. #* thousands of traveling ships.
  61. #* <P>
  62. #* The formal problem is: The traveling spaceship, at current position P in
  63. #* space and with current velocity V, is to travel and rendezvous with a
  64. #* destination object (a planet, another ship etc) which is at current position
  65. #* Q and with current velocity W. Since it's a rendezvous (e.g. docking or
  66. #* landing) the traveling spaceship must have the same velocity W as the
  67. #* destination upon arrival.
  68. #* <P>
  69. #* <I>Input:</I> An instance of TrajectoryParameters that contains:<BR>
  70. #* deltaPosition<BR>
  71. #* deltaVelocity<BR>
  72. #* maxForce (ship's max thrust)<BR>
  73. #* mass (ship's mass)
  74. #* <P>
  75. #* <I>Output:</I> An array of TrajectoryLeg instances, each containing:<BR>
  76. #* thrust vector (length of which is the thrust force; less than or equal to maxForce)<BR>
  77. #* time (length of time to apply the thrust)
  78. #* <P>
  79. #* The problem solved here makes no mention of changes to the destination's
  80. #* velocity over time. It's been simplified to disregard the destination's own
  81. #* acceleration. In theory the change in velocity direction over time is
  82. #* possible to compute for orbiting celestial bodies, although difficult within
  83. #* the context of this problem. But other spaceships are impossible to predict
  84. #* because someone else is controlling them! <I>Therefore the trajectory must be
  85. #* recomputed regularly during the journey - and we might as well disregard the
  86. #* complication of the destination's continuously changing acceleration in this
  87. #* solution.</I>
  88. #*
  89. #* @author Christer Swahn
  90. # (ported to Python anda adapted by proycon)
  91. #*/
  92.  
  93.  
  94. class TrajectoryPlanner:
  95. #the time tolerance
  96. TIME_TOLERANCE = 1e-6;
  97. #defines what is regarded as equivalent position and speed */
  98. GEOMETRIC_TOLERANCE = 1e-6;
  99. #the force tolerance during calculation
  100. FORCE_TOLERANCE = 1e-6;
  101. #the force difference tolerance of the result
  102. RESULT_FORCE_TOLERANCE = 1e-2;
  103.  
  104. ITER_LIMIT = 30
  105.  
  106.  
  107.  
  108.  
  109. def __init__(self, originpos, destpos, originv, destv, maxforce, mass):
  110. #originpos and destpos are vectors (coordinates) at time 0, same for originv and destv
  111.  
  112.  
  113. self.originpos = originpos
  114. self.originv = originv
  115.  
  116. #we translate to a relative frame of reference
  117. self.deltapos = destpos - originpos
  118. self.deltav = destv - originv
  119.  
  120. self.maxforce = maxforce
  121. self.mass = mass
  122.  
  123.  
  124. self.trajectory = [TrajectoryLeg(), TrajectoryLeg()] #will consist of two trajectory legs, each trajectory leg is a (thrustvector,time) tuple
  125.  
  126.  
  127. dV_magnitude = numpy.linalg.norm(self.deltav)
  128. distance = numpy.linalg.norm(self.deltapos)
  129.  
  130. #check special case 1, dV = 0:
  131. if (dV_magnitude < self.GEOMETRIC_TOLERANCE):
  132. if (distance < self.GEOMETRIC_TOLERANCE):
  133. raise Exception("Already at destination")
  134.  
  135. t_tot = (2*self.mass*distance / self.maxforce)**0.5
  136. self.trajectory[0].thrustvector = self.deltapos * (self.maxforce/distance) #accelerating
  137. self.trajectory[1].thrustvector = self.trajectory[0].thrustvector * -1 #breaking
  138. self.trajectory[0].t = self.trajectory[1].t = t_tot / 2;
  139. return
  140.  
  141. F_v = numpy.zeros(3)
  142. D_ttot = numpy.zeros(3)
  143. V_d_ttot = numpy.zeros(3)
  144. R_d = numpy.zeros(3)
  145. F_d = numpy.zeros(3)
  146. F_d2 = numpy.zeros(3)
  147.  
  148. #pick f_v in (0, tp.maxForce) via f_v_ratio in (0, 1):
  149. best_f_v_ratio = -1.0;
  150. min_f_v_ratio = 0.0;
  151. max_f_v_ratio = 1.0;
  152. simple_f_v_ratio = self.calcsimpleratio()
  153. f_v_ratio = simple_f_v_ratio #start value
  154. min_f_v_ratio = simple_f_v_ratio * .99 #(account for rounding error)
  155. nofIters = 0
  156. while nofIters < self.ITER_LIMIT:
  157. nofIters += 1
  158. f_v = f_v_ratio * self.maxforce;
  159.  
  160. t_tot = self.mass / f_v * dV_magnitude
  161.  
  162. F_v = self.deltav * (self.mass / t_tot)
  163. D_ttot = self.deltav * (t_tot/2) + self.deltapos
  164.  
  165. dist_ttot = numpy.linalg.norm(D_ttot)
  166.  
  167. #check special case 2, dP_ttot = 0:
  168. if dist_ttot < self.GEOMETRIC_TOLERANCE:
  169. # done! F1 = F2 = Fv (only one leg) (such exact alignment of dV and dP is rare)
  170. # FUTURE: should we attempt to find more optimal trajectory in case f_v < maxForce?
  171. if f_v_ratio < 0.5:
  172. print("Non-optimal trajectory for special case: dV and dP aligned: f_v_ratio=" + f_v_ratio)
  173. self.trajectory = [ TrajectoryLeg(F_v, t_tot) ]
  174. return
  175.  
  176. V_d_ttot = D_ttot * (2/t_tot)
  177. R_d = D_ttot * (1/dist_ttot) #normalized D_ttot
  178.  
  179.  
  180.  
  181.  
  182. alpha = math.pi - angle_between(F_v,R_d) #angle between F_v and F_p1
  183. assert (alpha >= 0 and alpha <= math.pi) #alpha + " not in (0, PI), F_v.dot(R_p1)=" + F_v.dot(R_d);
  184.  
  185. if math.pi - alpha < 0.00001:
  186. #special case 3a, F_v and F_p1 are parallel in same direction
  187. f_d = self.maxforce - f_v
  188. elif alpha < 0.00001:
  189. #special case 3b, F_v and F_p1 are parallel in opposite directions
  190. f_d = self.maxforce + f_v
  191. else:
  192. sin_alpha = math.sin(alpha)
  193. f_d = self.maxforce/sin_alpha * math.sin(math.pi - alpha - math.asin(f_v/self.maxforce*sin_alpha))
  194. assert (f_d > 0 and f_d < 2*self.maxforce) #: f_d + " not in (0, " + (2*tp.maxForce) + ")";
  195.  
  196. t_1 = 2*self.mass*dist_ttot / (t_tot*f_d)
  197. t_2 = t_tot - t_1
  198. if t_2 < self.TIME_TOLERANCE:
  199. #pick smaller f_v
  200. #LOG.debug(String.format("Iteration %2d: f_v_ratio %f; t_2 %,7.3f", nofIters, f_v_ratio, t_2));
  201. max_f_v_ratio = f_v_ratio
  202. f_v_ratio += (min_f_v_ratio - f_v_ratio) / 2 #(divisor experimentally calibrated)
  203. continue
  204.  
  205. F_d = R_d * f_d
  206. F_d2 = F_d * (-t_1/t_2) #since I_d = -I_d2
  207. #assert (F_d.copy().scale( t_1/tp.mass).differenceMagnitude(V_d_ttot) < GEOMETRIC_TOLERANCE) : F_d;
  208. #assert (F_d2.copy().scale(-t_2/tp.mass).differenceMagnitude(V_d_ttot) < GEOMETRIC_TOLERANCE) : F_d2;
  209.  
  210. F_1 = F_d + F_v
  211. F_2 = F_d2 + F_v
  212. #assert (Math.abs(F_1.length()-tp.maxForce)/tp.maxForce < FORCE_TOLERANCE) : "f1=" + F_1.length() + " != f=" + tp.maxForce;
  213. #assert verifyF1F2(tp, t_1, t_2, F_1, F_2) : "F1=" + F_1 + "; F2=" + F_2;
  214.  
  215. f_2 = numpy.linalg.norm(F_2)
  216. if f_2 > self.maxforce:
  217. #// pick smaller f_v
  218. #//LOG.debug(String.format("Iteration %2d: f_v_ratio %f; f_2 diff %e", nofIters, f_v_ratio, (tp.maxForce-f_2)));
  219. max_f_v_ratio = f_v_ratio
  220. f_v_ratio += (min_f_v_ratio - f_v_ratio) / 1.25 #divisor experimentally calibrated)
  221. else:
  222. #best so far
  223. #LOG.debug(String.format("Iteration %2d: best so far f_v_ratio %f; f_2 diff %e; max_f_v_ratio %f", nofIters, f_v_ratio, (tp.maxForce-f_2), max_f_v_ratio));
  224. best_f_v_ratio = f_v_ratio
  225. self.trajectory[0].thrustvector = F_1
  226. self.trajectory[0].t = t_1
  227. self.trajectory[1].thrustvector = F_2
  228. self.trajectory[1].t = t_2
  229. if f_2 < (self.maxforce*(1-self.RESULT_FORCE_TOLERANCE)):
  230. #// pick greater f_v
  231. min_f_v_ratio = f_v_ratio;
  232. f_v_ratio += (max_f_v_ratio - f_v_ratio) / 4 #// (divisor experimentally calibrated)
  233. else:
  234. break #done!
  235.  
  236.  
  237. if best_f_v_ratio >= 0:
  238. return
  239. else:
  240. print("Couldn't determine full trajectory for %s (nofIters %d) best_f_v_ratio=%.12f" % (tp, nofIters, best_f_v_ratio))
  241. self.trajectory = [ TrajectoryLeg(self.deltav, dV_magnitude*self.mass/self.maxforce) ]
  242. #trajectory[0].thrust.add(tp.deltaPosition).normalize().scale(tp.maxForce); // set thrust direction to average of dP and dV
  243.  
  244. # set thrust direction to average of dP and dV
  245. self.trajectory[0].thrustvector += self.deltapos
  246. self.trajectory[0].thrustvector = (self.trajectory[0].thrustvector / numpy.linalg.norm(self.trajectory[0].thrustvector)) * self.maxforce #normalize and extend
  247.  
  248. return
  249.  
  250. #private static boolean verifyF1F2(TrajectoryParameters tp, double t_1, double t_2, SpatialVector F_1, SpatialVector F_2) {
  251. # final double REL_TOLERANCE = 1e-5;
  252.  
  253. # SpatialVector V_1 = F_1.copy().scale(t_1/tp.mass);
  254. # SpatialVector V_2 = F_2.copy().scale(t_2/tp.mass);
  255. # SpatialVector achievedDeltaVelocity = V_1.copy().add(V_2);
  256. # double velDiff = achievedDeltaVelocity.differenceMagnitude(tp.deltaVelocity);
  257. # assert (velDiff/tp.deltaVelocity.length() < REL_TOLERANCE) : velDiff/tp.deltaVelocity.length() +
  258. # "; difference=" + velDiff + "; achievedDeltaVelocity=" + achievedDeltaVelocity + "; tp.deltaVelocity=" + tp.deltaVelocity;
  259.  
  260. # SpatialVector targetPosition = tp.deltaVelocity.copy().scale(t_1+t_2).add(tp.deltaPosition);
  261. # SpatialVector achievedPosition = V_1.scale(t_1/2+t_2).add(V_2.scale(t_2/2));
  262. # double posDiff = achievedPosition.differenceMagnitude(targetPosition);
  263. # assert (posDiff/tp.deltaPosition.length() < REL_TOLERANCE) : posDiff/tp.deltaPosition.length() +
  264. # "; difference=" + posDiff + "; achievedPosition=" + achievedPosition + "; targetPosition=" + targetPosition;
  265.  
  266. # return true;
  267. #}
  268.  
  269.  
  270. def calcsimpleratio(self):
  271. # compute the f_v ratio of the simplest trajectory where we accelerate in two steps:
  272. # 1) reduce the velocity difference with the destination to 0 (time needed: t_v)
  273. # 2) traverse the distance to arrive at the destination (time needed: t_p)
  274. # (This usually takes longer than the optimal trajectory, since the distance traversal
  275. # does not utilize the full travel time period. (The greater travel period that
  276. # the distance traversal can use, the less impulse (acceleration) it needs.))
  277.  
  278. inv_acc = self.mass / self.maxforce
  279. t_v = inv_acc * numpy.linalg.norm(self.deltav)
  280.  
  281. newdeltapos = (self.deltav * (t_v/2)) + self.deltapos
  282. distance = numpy.linalg.norm(newdeltapos)
  283. t_p = 2 * (inv_acc*distance)**0.5
  284.  
  285. t_tot = t_v + t_p
  286. f_v_ratio = t_v / t_tot
  287. return f_v_ratio
  288.  
  289.  
  290. def velocity(self,t,m):
  291. """Compute velocity vector at time t"""
  292.  
  293. pos = self.originpos.copy()
  294. v = self.originv.copy()
  295.  
  296. for trajectoryleg in self.trajectory:
  297. a = (trajectoryleg.thrustvector) / m # a = F/m
  298. if t <= trajectoryleg.t:
  299. v += a * t
  300. break
  301. else:
  302. v += a * trajectoryleg.t
  303. t = t - trajectoryleg.t
  304. return v
  305.  
  306.  
  307.  
  308. #Visualisation stuff
  309.  
  310.  
  311.  
  312. AU = 149597870700 #m
  313.  
  314. originpos = numpy.array([1*AU,1*AU,1*AU])
  315. originv = numpy.array((-800000,0,0))
  316. destpos = numpy.array([3*AU,0,0])
  317. destv = numpy.array((-50000,-1000000,50000))
  318. mass = 10000
  319. maxforce = mass*11
  320.  
  321.  
  322. tp = TrajectoryPlanner(originpos, destpos, originv, destv, maxforce, mass)
  323. print(tp.trajectory)
  324.  
  325.  
  326. from visual import * #requires vpython
  327. def np2v(a):
  328. """Numpy to vpython"""
  329. return vector(a[0],a[1],a[2])
  330.  
  331. scene.center = (0,0,0)
  332. scene.width = 1600
  333. scene.height = 1200
  334. scene.range = (5*AU,5*AU,5*AU)
  335.  
  336. A = sphere(pos=originpos,radius=0.05*AU,color=color.red)
  337. B = sphere(pos=destpos,radius=0.05*AU,color=color.blue)
  338.  
  339. x = sphere(pos=originpos,radius=0.03*AU,color=color.white,make_trail=True)
  340.  
  341. t = 0
  342. while True:
  343. t += 1
  344. rate(10000) # a high number pretty much means go as fast as our computer can
  345.  
  346. A.pos = A.pos + vector(originv)
  347. B.pos = B.pos + vector(destv)
  348. v = tp.velocity(t,mass)
  349. x.pos = x.pos + np2v(v)
  350. #print(t,v)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement