我正在尝试找出一种在两艘宇宙飞船之间建立会合点的算法。
没有重力或阻力。两艘宇宙飞船在开始时都有一个位置和一个速度。B 飞船在没有加速的情况下继续前进,所以 A 飞船需要加速以拉近它们之间的距离,然后在到达 B 飞船的位置时匹配速度。
飞船可以瞬间改变推力方向,但只能使用最大加速度或根本不使用加速度。我还想限制机动期间飞船之间的速度差异。
我希望输出采用多个轨迹腿的形式,即:leg1:加速方向 x t1 秒,leg2:滑行 t2 秒,leg3:加速方向 y t3 秒。
我不需要最佳解决方案,但我希望它“看起来正确”。
我试图做一个脉冲来平衡速度并将它添加到一个向宇宙飞船 B 移动的脉冲,但即使宇宙飞船 A 最终以正确的速度结束,它也无法到达目标位置。我自己尝试了这些冲动,它们似乎按预期执行,所以我猜这是我将它们加在一起的方式,这就是问题所在。我不知道我是否执行不正确,或者这种方法根本行不通。我希望有更强的数学和物理技能的人能启发我。
这是我正在使用的代码:
// var velocityAdjustmentTime = (int)Math.Sqrt(2 * velocityDelta.Length / tp.Acceleration);
var velocityAdjustmentTime = (int)(velocityDelta.Length / tp.Acceleration);
var velocityAdjustVector = velocityDelta;
velocityAdjustVector.Normalize();
velocityAdjustVector *= tp.Acceleration;
var targetAccelerationDisplacement = new Vector3D(0, 0, 0); // TODO: Replace this with proper values.
Vector3D newPosition;
Vector3D newVelocity;
Vector3D targetNewPosition;
// Check if the formation and the target already have a paralell course with the same velocity.
if (velocityAdjustmentTime > 0)
{
// If not, calculate the position and velocity after the velocity has been aligned.
newPosition = tp.StartPosition + (tp.StartVelocity * velocityAdjustmentTime) + ((velocityAdjustVector * velocityAdjustmentTime * velocityAdjustmentTime) / 2);
newVelocity = tp.StartVelocity + velocityAdjustVector * velocityAdjustmentTime;
targetNewPosition = tp.TargetStartPosition + (tp.TargetStartVelocity * velocityAdjustmentTime) + targetAccelerationDisplacement;
}
else
{
// Else, new and old is the same.
newPosition = tp.StartPosition;
newVelocity = tp.StartVelocity;
targetNewPosition = tp.TargetStartPosition;
}
// Get the new direction from the position after velocity change.
var newDirection = targetNewPosition - newPosition;
// Changing this value moves the end position closer to the target. Thought it would be newdirection length, but then it doesn't reach the target.
var length = newDirection.Length;
// I don't think this value matters.
var speed = (int)(cruiseSpeed);
var legTimes = CalculateAccIdleDecLegs(tp.Acceleration, length, speed);
// Sets how much of the velocity change happens on the acceleration or deceleration legs.
var velFactorAcc = 1;
var velFactorDec = 1 - velFactorAcc;
// Make the acceleration vector.
accelerationVector = newDirection;
accelerationVector.Normalize();
accelerationVector *= legTimes[0] * tp.Acceleration;
accelerationVector += velocityDelta * velFactorAcc;
accelerationTime = (int)(accelerationVector.Length / tp.Acceleration);
accelerationVector.Normalize();
accelerationVector *= tp.Acceleration;
// Make the acceleration order.
accelerationLeg.Acceleration = accelerationVector;
accelerationLeg.Duration = accelerationTime;
// Make the deceleration vector.
decelerationVector = newDirection;
decelerationVector.Negate();
decelerationVector.Normalize();
decelerationVector *= legTimes[2] * tp.Acceleration;
decelerationVector += velocityDelta * velFactorDec;
decelerationTime = (int)(decelerationVector.Length / tp.Acceleration);
decelerationVector.Normalize();
decelerationVector *= tp.Acceleration;
// And deceleration order.
decelerationLeg.Acceleration = decelerationVector;
decelerationLeg.Duration = decelerationTime;
// Add the orders to the list.
trajectory.Add(accelerationLeg);
// Check if there is an idle leg in the middle...
if (legTimes[1] > 0)
{
// ... if so, make the order and add it to the list.
idleLeg.Duration = legTimes[1];
trajectory.Add(idleLeg);
}
// Add the deceleration order.
trajectory.Add(decelerationLeg);
以及计算接近腿的功能:
private static int[] CalculateAccIdleDecLegs(double acceleration, double distance, int cruiseSpeed)
{
int[] legDurations = new int[3];
int accelerationTime;
int idleTime;
int decelerationTime;
// Calculate the max speed it's possible to accelerate before deceleration needs to begin.
var topSpeed = Math.Sqrt(acceleration * distance);
// If the cruise speed is higher than or equal to the possible top speed, the formation should accelerate to top speed and then decelerate.
if (cruiseSpeed >= topSpeed)
{
// Get the time to accelerate to the max velocity.
accelerationTime = (int)((topSpeed) / acceleration);
// Idle time is zero.
idleTime = 0;
// Get the deceleration time.
decelerationTime = (int)(topSpeed / acceleration);
}
// Else, the formation should accelerate to max velocity and then coast until it starts decelerating.
else
{
// Find the acceleration time.
accelerationTime = (int)((cruiseSpeed) / acceleration);
// Get the deceleration time.
decelerationTime = (int)(cruiseSpeed / acceleration);
// Calculate the distance traveled while accelerating.
var accelerationDistance = 0.5 * acceleration * accelerationTime * accelerationTime;
// Calculate the distance traveled while decelerating.
var decelerationDistance = 0.5 * acceleration * decelerationTime * decelerationTime;
// Add them together.
var thrustDistance = accelerationDistance + decelerationDistance;
// Find the idle distance.
var idleDistance = distance - thrustDistance;
// And the time to idle.
idleTime = (int)(idleDistance / cruiseSpeed);
}
legDurations[0] = accelerationTime;
legDurations[1] = idleTime;
legDurations[2] = decelerationTime;
return legDurations;
}