Advertisement

Calculating intercept angle

Started by November 04, 2002 03:03 PM
6 comments, last by Argus 22 years ago
(this is a cross-post from the math and physics forum - haven''t received any definitive answers from there yet so thought I''d try here. Delete if necessary.) I was wondering whether anyone has a non-iterative formula for finding the angle one should fire a constant velocity projectile in order to hit another constant velocity projectile (in 2d)? If there is definitely no such formula, what''s the best way to write an iterative one? And yes I''ve tried google numerous times, and search on these forums is still broken. Any help would be much appreciated.
I can't provide any real formulas at this time, but here's how it works.

You know that the point you're going to fire at is somewhere along the straight-line trajectory of the target object.

If you know the velocity of your projectile, and the velocity of the target object, then you just solve for the point on the target's path such that...

t0 = target's original position
w0 = weapon's original position
p = point to fire at

| p - t0 | / | p - w0 | = velocity of target / velocity of projectile

In short, the ratio of the velocities of the two objects must equal the ratio of the distances the projectile and target must travel to meet at the same point.

p in this case is t0 + vt*k, where vt is the velocity of the target and k is the constant you will solve for in the equation.

Hopefully that gets you started. Express the magnitudes above as calculations on the vector components, and it should evaluate to a quadratic that has one, two, or no solutions for k.

[edited by - Waverider on November 4, 2002 4:42:48 PM]
It's not what you're taught, it's what you learn.
Advertisement
Well, here is one possiblity. Bear in mind a couple of limitations. First, this doesn''t take into account any initial velocity from cannon. Second, it doesn''t take into account that your universe is quantized in both time and space. Therefore the two objects might never line up precisely because the point or time at which they would have collided is between the quanta.

Also note that I haven''t tested this therefore buyer beware etc.


  const double PI = 3.1415;// returns a positive angle in radians. If there is not// possible trajectory, it returns a negative number.double calculateAngle(double bulletSpeed, double xProjectilePos, double yProjectilePos, double xTargetPos, double yTargetPos, double xTargetVel, double yTargetVel){// first calculate time    double a = pow(yTargetVel, 2) + pow(xTargetVel, 2) - pow(bulletSpeed, 2);    double b = 2 * (yTargetVel * (yTargetPos - yProjectilePos) + xTargetVel * (xTargetPos - xProjectilePos) );    double c = pow(yTargetPos - yProjectilePos, 2) + pow(xTargetPos - xProjectilePos, 2);    double d = pow(b, 2) - 4*a*c;    if ( d < 0)        return -1.0;    double time1 = ((-1.0) * b - sqrt(d)) / (2 * a);    double time2 = ((-1.0) * b + sqrt(d)) / (2 * a);    if (time1 < 0 && time2 < 0)        return -1.0;    double time;    if (time1 < time2)        time = time1;    else        time = time2;// use time to calculate the vector elements of the velocity    double xProjectileVel = (xTargetPos + time * xTargetVel - xProjectilePos) / time;    double yProjectileVel = (yTargetpos + time * yTargetVel - yProjectilePos) / time;    double angle;    if (xProjectileVel != 0.0)        angle = arctan(yProjectileVel / xProjectileVel);    else    {        if (yProjectileVel >= 0)            return PI / 2;        else            return (3.0 * PI) / 2;    }// use the vector elements to calculate the angle// first we need to find the quadrant that the angle is in    if (xProjectileVel >= 0 && yProjectileVel >= 0)    {        // we''re in quadrant 1. This means that we can just         // return the value.        return angle;    }    else if (xProjectileVel < 0 && yProjectileVel >= 0)    {        // we''re in quadrant 2.        return PI + angle;    }    else if (xProjectileVel < 0 && yProjectileVel < 0)    {        // quadrant 3        return PI + angle;    }    else    {        // quadrant 4        return 2*PI + angle;    }}  


-D
Ack. I noticed a bug.

instead of the lines


  if (time1 < 0 && time2 < 0)    return -1.0;double time;if (time1 < time2)    time = time1;else    time = time2;  


Instead substitute


  double time;if (time1 < 0 && time2 < 0)    return -1.0;else if (time1 < 0)    time = time2;else if (time2 < 0)    time = time1;else if (time1 < time2)    time = time1;else    time = time2;  


Here is a bit about how I got the equations I did so you can check me. There are three equations and three unknowns:

time * xProjectileVel + xProjectilePos = time * xTargetVel + xTargetPos

time * yProjectileVel + yProjectilePos = time * yTargetVel + yTargetPos

xProjectileVel^2 + yProjectileVel^2 = bulletSpeed^2

The rest is just the result of substitutions and some algebra.

-D
Thanks guys - I did run into some problems though :

Wave - From my limited knowledge of mathematics, the subtraction of one point from another eg "p - t0", is a vector. I''m not sure what the | | operator does to a vector though, nor how one divides a vector by a vector if the result of the | | operator is a vector itself.

Also, are you suggesting that I substitute t0 + vtarget*k for p, and then algebraically manipulate to get k on one side?

AP - Thanks for all the code, it looks like it might be what I need, but I had a problem where the angle doesn''t seem to be on an intercept heading, but tending more toward the initial position of the target ie lagging behind a lot.

I took the liberty of altering quadrant 2 to return (PI - angle), and quadrant 4 to return (2*PI - angle), although I did also try with the initial expressions.

Can you see anything in your code that might be the cause of the problem?
| | means take the magnitude of the vector which results in a scalar. I would square both sides of the equation to get rid of the square root portion, though.

And yes, substitute p and get k by itself.

Good luck!
It's not what you're taught, it's what you learn.
Advertisement
I wrote up a quick an dirty test program for the algorithm. I modified it so that instead of returning an angle it returned the vector in its component parts.

I tested a couple of cases and they were both exactly right. This means that the problem is the code that converts the vector components into an angle.

Here is the function and driver that just returns a vector:


  #include <iostream>#include <iomanip>#include <cmath>#include <cstdlib>using namespace std;struct Vec { Vec() : x(0), y(0) {} double x; double y;};Vec calculateAngle(double bulletSpeed, double xProjectilePos, double yProjectilePos, double xTargetPos,                       double yTargetPos, double xTargetVel, double yTargetVel);const double PI = 3.1415;int main(){    cout << setprecision(4) << setiosflags(ios::fixed | ios::showpoint);    double bulletSpeed, xProjectilePos, yProjectilePos, xTargetPos, yTargetPos, xTargetVel, yTargetVel;    cout << "\nWhat is the bullet speed?";    cin >> bulletSpeed;    cout << "What is the x projectile position?";    cin >> xProjectilePos;    cout << "What is the y projectile position?";    cin >> yProjectilePos;    cout << "What is the x target position?";    cin >> xTargetPos;    cout << "What is the y target position?";    cin >> yTargetPos;    cout << "What is the x target velocity?";    cin >> xTargetVel;    cout << "What is the y target velocity?";    cin >> yTargetVel;    Vec projectileVel = calculateAngle(bulletSpeed, xProjectilePos, yProjectilePos, xTargetPos, yTargetPos,         xTargetVel, yTargetVel);    for (int i=0; i<100; ++i)    {        cout << "\nTarget: (" << xTargetPos << " , " << yTargetPos << ")\tProjectile: (" << xProjectilePos            << " , " << yProjectilePos << ")";        xProjectilePos += projectileVel.x;        yProjectilePos += projectileVel.y;        xTargetPos += xTargetVel;        yTargetPos += yTargetVel;    }    cout << endl;    system("pause");    return 0;}// returns a positive angle in radians. If there is not// possible trajectory, it returns a negative number.Vec calculateAngle(double bulletSpeed, double xProjectilePos, double yProjectilePos, double xTargetPos,                       double yTargetPos, double xTargetVel, double yTargetVel){    // first calculate time        double a = pow(yTargetVel, 2) + pow(xTargetVel, 2) - pow(bulletSpeed, 2);        double b = 2 * (yTargetVel * (yTargetPos - yProjectilePos) + xTargetVel * (xTargetPos - xProjectilePos) );        double c = pow(yTargetPos - yProjectilePos, 2) + pow(xTargetPos - xProjectilePos, 2);        double d = pow(b, 2) - 4*a*c;        if ( d < 0)                return Vec();        double time1 = ((-1.0) * b - sqrt(d)) / (2 * a);        double time2 = ((-1.0) * b + sqrt(d)) / (2 * a);        double time;    if (time1 < 0 && time2 < 0)         return Vec();    else if (time1 < 0)          time = time2;    else if (time2 < 0)          time = time1;    else if (time1 < time2)         time = time1;    else           time = time2;    // use time to calculate the vector elements of the velocity        double xProjectileVel = (xTargetPos + time * xTargetVel - xProjectilePos) / time;    double yProjectileVel = (yTargetPos + time * yTargetVel - yProjectilePos) / time;       cout << "\nTime: " << time << endl << endl;/*    double angle;       if (xProjectileVel != 0.0)                angle = arctan(yProjectileVel / xProjectileVel);    else        {                if (yProjectileVel >= 0)                   return PI / 2;               else                     return (3.0 * PI) / 2;      }    // use the vector elements to calculate the angle    // first we need to find the quadrant that the angle is in     if (xProjectileVel >= 0 && yProjectileVel >= 0)     {                // we''re in quadrant 1. This means that we can just            // return the value.               return angle;        }        else if (xProjectileVel < 0 && yProjectileVel >= 0)        {        // we''re in quadrant 2.                return PI + angle;        }        else if (xProjectileVel < 0 && yProjectileVel < 0)       {        // quadrant 3                return PI + angle;        }        else       {        // quadrant 4                return 2*PI + angle;        }*/    Vec retVal;    retVal.x = xProjectileVel;    retVal.y = yProjectileVel;    return retVal;}    


If you still need just the angle, find a trig book and learn more about the arc functions. They have a lot of special cases which is probably where I went astray.

-D
Actually I think the problem may be with my estimation of the target heading rather than with your intercept code. Thanks greatly for all the code and if I can help you out some time let me know (would be easier if you weren''t anonymous).

This topic is closed to new replies.

Advertisement