绘制航天器随时间的距离

时间:2016-10-20 05:21:08

标签: javascript math physics game-physics

我正在研究使用力,加速度和质量在笛卡尔地图上进行点对点太空飞船旅行的逻辑。该船将在1G处加速并燃烧至目的地,在中途标记处翻转180度,并在1G处减速以在目的地处达到相对停止。

我遇到的问题是在船舶处于加速或减速状态时使用行驶时间来确定(x,y)坐标。

以下是ship的规范:

ship = {
  mass: 135000, // kg
  force: 1324350, // Newtons
  p: { x: -1, y: -5 } // (x,y) coordinates
}

dest: {
  p: { x: 15, y: 30 }  // (x,y) coordinates
}

对于问题的第一部分,我计算了到达目的地的时间:

var timeToDestination = function(ship, dest) {

  // calculate acceleration (F = ma)
  var acceleration = ship.force / ship.mass; // ~9.81 (1G)

  // calculate distance between 2 points (Math.sqrt((a - x)^2+(b - y)^2))
  var totalDistance = Math.sqrt(
    Math.pow(dest.p.x - ship.p.x, 2) + 
    Math.pow(dest.p.y - ship.p.y, 2)
  ); // 38.48376280978771

  // multiply grid system to galactic scale
  var actualDistance = totalDistance * 1e9; // 1 = 1Mkm (38,483,763km) Earth -> Venus

  // determine the mid-point where ship should flip and burn to decelerate
  var midPoint = actualDistance / 2;

  // calculate acceleration + deceleration time by solving t for each: (Math.sqrt(2d/a))
  return Math.sqrt( 2 * midPoint / acceleration ) * 2; // 125,266s or 34h or 1d 10h
}

第二部分有点棘手,在delta时间后得到(x,y)坐标。这是我陷入困境的地方,但到目前为止我还有:

var plotCurrentTimeDistance = function(ship, dest, time) {

  // recalculate acceleration (F = ma)
  var acc = ship.force / ship.mass; //~9.81m/s^2

  // recalculate total distance
  var distance = Math.sqrt(
    Math.pow(dest.p.x - ship.p.x, 2) + 
    Math.pow(dest.p.y - ship.p.y, 2)
  ) * 1e9; // 38,483,762,810m

  // get distance traveled (d = (1/2) at^2)
  var traveled = (acc * Math.pow(time, 2)) / 2;

  // get ratio of distance traveled to actualDistance
  var ratio = traveled / distance;

  // midpoint formula to test @ 50% time ((x+a)/2,(y+b)/2)
  console.log({ x: (ship.p.x+dest.p.x)/2, y: (ship.p.y+dest.p.y)/2})

  // get the point using this formula (((1−t)a+tx),((1−t)b+ty))
  return { 
    x: (( 1 - ratio ) * ship.p.x) + (ratio * dest.p.x), 
    y: (( 1 - ratio ) * ship.p.y) + (ratio * dest.p.y) 
  };
}

@ 50%时间,62,633s点返回为(~7,~12.5),它与中点公式匹配,返回为(~7,~12.5)。但是,您输入的任何其他距离/时间都将非常错误。我的猜测是加速会弄乱计算,但我无法弄清楚如何改变公式以使其有效。谢谢你的时间。

2 个答案:

答案 0 :(得分:2)

First, you say that distance is the total distance, but it really is the distance left from the ship to the destination. I don't fully understand your plan on how to do the calculations, so I will suggest something different below.

Lets call the start position start, and it has a x, and y coordinate: start.x and start.y. Similarly with end.

Now, for a simulation like this to work we need velocity as a property on the ship as well, so

ship = {
    ...
    v : {x : 0, y : 0}
}

It will start from rest, and it should reach rest. Ideally it should have acceleration a for a general movement, but we can skip that right now. We will have two different behaviours

  1. The ship starts from rest, accelerates with 9.8 m/s^2 towards the goal until the half point is reached.
  2. The ship starts at speed v at the midpoint, decelerates with -9.8 m/s^2 towards the goal until the speed is 0. Now we should have reached the goal.

To get velocity from accelerations we use v = v_start + a*time, and to get position from velocity we use x = x_start + v*time. The current position of the ship is then for the two cases

// Case 1
current = start + a*time*time
// the above is due to the fact that
//     current = start + v*time
// and the fact that v = v_start + a*time as I wrote previously,
// with v_start.x = v_start.y = 0

//Case 2
current = midpoint + (v_at_midpoint - a*time_since_midpoint)*time_since_midpoint

Note here that start, current and a here are vectors, so they will have a x and y (and potentially z) coordinate each.

The acceleration you get by the following algorithm

 a = (start - end)/totalDistance * 9.81 // 9.81 is the desired acceleration -- change it to whatever you want

If you want to understand what the above actually means, it calculates what is called a unit vector, which tells us what direction the force points at.

What you will need to do now is as follows:

  1. Calculate the total distance and the acceleration

  2. Determine if you're in case 1 or 2 given the input time in the function.

  3. Once you've reached the midpoint, store the velocity and how long it took to get there and use it to determine the motion in case 2.

  4. Stop once you've reached the destination, or you will go back to the start eventually!

Good luck!

P.S I should also note here that this does not take into account special relativity, so if your distances are too far apart you will get non-physical speeds. It gets a lot messier if you want to take this into account, however.

答案 1 :(得分:0)

非常感谢@pingul,我能够根据他的建议得到答案。

var ship = {
  mass: 135000,
  force: 1324350,
  accel: 0,
  nav: {
  	startPos: { x: 0, y: 0 },
    endPos: { x: 0, y: 0 },
  	distanceToDest: 0,
    distanceTraveled: 0,
    departTime: 0,
    timeToDest: 0,
    arriveTime: 0,
    startDeceleration: 0
  }
};

var log = [];
var start = { x: -1, y: -5 };
var end = { x: 15, y: 30 };

var updateLog = function() {
 document.getElementById('ship').textContent = JSON.stringify(ship, null, 2);
 document.getElementById('pos').textContent = JSON.stringify(log, null, 2);
}

var plotCourse = function(ship, start, end) {

  // calculate acceleration (F = ma)
  var acceleration = ship.force / ship.mass; // ~9.81 (1G)

  // calculate distance between 2 points (Math.sqrt((a - x)^2+(b - y)^2))
  var totalDistance = Math.sqrt(
    Math.pow(end.x - start.x, 2) + 
    Math.pow(end.y - start.y, 2)
  ); // 38.48376280978771

  // multiply grid system to galactic scale
  var actualDistance = totalDistance * 1e9; // 1 = 1Mkm (38,483,763km) Earth -> Venus

  // determine the mid-point where ship should flip and burn to decelerate
  var midpoint = actualDistance / 2;
  
  // calculate acceleration + deceleration time by solving t for each: (Math.sqrt(2d/a))
  var time = Math.sqrt( 2 * midpoint / acceleration ) * 2; // 125,266s or 34h or 1d 10h
  
  // load data into ship nav
  ship.nav = {
  	startPos: start,
    endPos: end,
  	distanceToDest: actualDistance,
    timeToDest: time,
    startDeceleration: time / 2
  }
  ship.accel = acceleration

  //log it
	updateLog();  
};

var goUnderway = function(ship) {
	var arrivalEl = document.getElementById('arrivalTime');
  
	// set depart and arrive times
  var timeToDest = ship.nav.timeToDest * 1000; // convert to ms
  ship.nav['departTime'] = moment().valueOf(); // returns now as unix ms
  ship.nav['arriveTime'] = moment(ship.nav.departTime).add(timeToDest).valueOf();
  
  //log it
  arrivalEl.textContent = 'Your ship will arrive ' + moment(ship.nav.arriveTime).calendar();
	updateLog();  
};

var getPosition = function(ship, date) {
	var currentTime = date ? moment(date).valueOf() : moment().valueOf() // unix ms
  var elapsedTime = (currentTime - ship.nav.departTime) / 1000; // convert to s
  var remainingTime = (ship.nav.arriveTime - currentTime) / 1000;  // convert to s
  var distanceAtMidpoint = 0;
  var timeSinceMidpoint = 0;
  var pos = { x: 0, y: 0 };
  
  // calculate velocity from elapsed time
	if (elapsedTime < ship.nav.startDeceleration) {
  
  	// if ship is accelerating use this function
    ship.nav.distanceTraveled = 0 + ship.accel * Math.pow(elapsedTime, 2) / 2;
  } else if (elapsedTime > ship.nav.startDeceleration) {
  
  	// else if ship is decelerating use this function
  	distanceAtMidpoint = 0 + ship.accel * Math.pow(ship.nav.startDeceleration, 2) / 2; // distance at midpoint
    timeSinceMidpoint = elapsedTime - ship.nav.startDeceleration;
  	ship.nav.distanceTraveled = distanceAtMidpoint + ship.accel * Math.pow(timeSinceMidpoint, 2) / 2;
  }
  
  if (remainingTime <= 0) {
  	ship.force = ship.vel = ship.accel = 0;
    pos = ship.nav.endPos;
  } else {
    // get ratio of distance traveled to actualDistance
    var ratio = ship.nav.distanceTraveled / ship.nav.distanceToDest;
  	// get the point using this formula (((1−t)a+tx),((1−t)b+ty))
    pos = { 
      x: (( 1 - ratio ) * start.x) + (ratio * end.x), 
      y: (( 1 - ratio ) * start.y) + (ratio * end.y) 
    };
  }
  
  log.push({
  	timestamp: moment(currentTime),
    pos: pos
  })
  
  //log it
  updateLog();
};

plotCourse(ship, start, end);
goUnderway(ship);
for (var i = 0; i < 35; i++) {
	getPosition(ship, moment().add(i, 'hour'));
}
pre {
  outline: 1px solid #ccc;
  padding: 5px;
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment.min.js"></script>
Ship: <span id="arrivalTime"></span>
<pre id="ship"></pre> Last Known Positions:
<pre id="pos"></pre>