Search Unity

How to plot intercept position with moving target (for guided missile)

Discussion in 'Physics' started by Palimon, Oct 9, 2015.

  1. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    Hey guys, I've been working on a missile script to make them home in on a target by estimating the intercept position every update and pointing them towards that location. Known defects/improvements are:
    1) Only the target's current velocity is taken into account. There is no prediction of turns or acceleration.
    2) I also don't take into account the missile's own acceleration, which is known.

    My missile, with this script, will hit the ship if the ship is completely stationary. It will always arrive behind the ship if the ship is moving at all, even at a slow, constant vector. It then spins around, decelerates, accelerates back at the ship, and again arriving behind the ship. Given the way my script is written and not taking into account the missile's acceleration, I'd expect to at least strike in front of the slowly moving ship due to assumed ETA always being faster than anticipated. So, here's my intercept method - can anyone take a look and see where I went wrong? I took calculus ~10yrs ago, so this is some mathematical stretching for me :p.

    Code (CSharp):
    1.     public void intercept(GameObject target, GameObject interceptor)
    2.     {
    3.         // 1) Find time for interceptor to reach target's current position: distance/speed
    4.         float interceptTime = Vector3.Distance(target.transform.position, interceptor.transform.position)/interceptor.GetComponent<Rigidbody>().velocity.magnitude;
    5.  
    6.         // 2) Loop steps 2 & 3 until oldInterceptPosition and newInterceptPosition differ by < .5m
    7.         Vector3 oldInterceptPosition = target.transform.position + (interceptor.GetComponent<Rigidbody>().velocity * interceptTime);
    8.         Vector3 newInterceptPosition;
    9.         float diff = 100;
    10.         int count = 0; //<- added tripwire so Unity didn't infi-loop in here. It never reaches diff < .5f apparently
    11.         do
    12.         {
    13.             // 2.1) Find time for interceptor to reach target's newly predicted position
    14.             interceptTime = Vector3.Distance(oldInterceptPosition, interceptor.transform.position) / interceptor.GetComponent<Rigidbody>().velocity.magnitude;
    15.        
    16.             // 2.2) Find the position target will move to during time of interception.
    17.             newInterceptPosition = target.transform.position + (interceptor.GetComponent<Rigidbody>().velocity * interceptTime);
    18.  
    19.             // 2.3) Check the difference in distances between old and new positions
    20.             diff = Vector3.Distance(oldInterceptPosition, newInterceptPosition);
    21.             count++;
    22.         } while (diff > .5f && count < 20);
    23.  
    24.         // 3) Slerp towards the estimate of intercept position
    25.         if (newInterceptPosition - transform.position != Vector3.zero)
    26.         {
    27.             Quaternion rotation = Quaternion.LookRotation(newInterceptPosition - transform.position);
    28.             float damping = 100;
    29.             interceptor.transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
    30.         }
    31.     }
     
  2. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    No need for all that with a homing missile.

    I have homing missiles that do no prediction. They hit accurately. Not sure why you are doing all that extra calculation. makes no sense. If the missile wasnt tracking (like a bullet). sure it would make sense.

    If you really want to get into it, this is how the bullet projection is done.

    http://wiki.unity3d.com/index.php/Calculating_Lead_For_Projectiles
     
  3. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Maybe the reason for using all the calculations is to be able to also have the homing missiles miss their target? Be it a decoy or a failure.

    It all depends on the case really. At least the code is interesting to read hah! :)
     
  4. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    Well, for whatever reason, just going with the "1) aim at the target 2) add thrust" loop wasn't working. Had almost the exact same result as always ending up behind the moving target. That said, yes, I do want the missiles to be able to miss the target based on fancy flying :).
     
  5. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    Perhaps that's a better question for me to ask right now - why don't my simple homing missiles hit a moving, non-accelerating, target? During Update() I was running:
    Code (CSharp):
    1. this.transform.rotation = Quaternion.LookRotation(target.transform.position - this.transform.position);
    And during FixedUpdate() I was running:
    Code (CSharp):
    1. if (this.GetComponent<Rigidbody>().velocity.magnitude < 500)
    2.         {
    3.             this.GetComponent<Rigidbody>().AddRelativeForce(Vector3.forward * Time.fixedDeltaTime * 1000, ForceMode.Force);
    4.         }
     
  6. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    Haha, thanks! That's what I was going for, interesting code.... :p.
     
  7. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    I'm not able to tell you based on just the code, when I get home I'll try this code and see if I can get it to work for you :) your subject got me very much intrigued to try myself haha.
     
  8. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Well so far all I came up with was doing it in Update() with Vector3.MoveTowards();

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class HomingMissile : MonoBehaviour {
    4.     public GameObject target;
    5.     public float missileSpeed = 50f;
    6.  
    7.     [SerializeField]
    8.     private float _distanceFromTarget;
    9.  
    10.     void Update()
    11.     {
    12.         _distanceFromTarget = Vector3.Distance(this.transform.position, target.transform.position);
    13.         float step = missileSpeed * Time.deltaTime;
    14.  
    15.         if(target != null)
    16.         {
    17.             this.transform.rotation = Quaternion.LookRotation(target.transform.position - this.transform.position);
    18.             this.transform.position = Vector3.MoveTowards(this.transform.position, target.transform.position, step);
    19.         }
    20.     }
    21. }
    I had the same issue with the missile just ending up missing the target at its rear and then taking too much time to turn back around and gain enough momentum to go after the target again. It happens everytime it gets close to the target.

    I'm sure real homing missiles however have a system that checks how close it is to its target and adjusts maybe its speed or trajectory to make sure it will intercept the target properly.

    I did a quick search on Google and came up with a nice quote from a Wiki page:
    Maybe either of the pages can help us achieve our goal? haha ;)
     
  9. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    This is my missile code.

    Code (csharp):
    1.  
    2.  
    3. Transform _transform;
    4. Rigidbody _rigidbody;
    5.  
    6. Transform CurrentTarget;
    7. float CurrentFuel = 5;
    8. float TrackingRotation = 5;
    9. bool IsScrambled = false;
    10.  
    11. void Start()
    12. {
    13.   _transform = transform;
    14.   _rigidbody = GetComponent<Rigidbody>();
    15. }
    16.  
    17. void FixedUpdate()
    18.     {
    19.      
    20.          
    21.             if(CurrentFuel> 0f)
    22.             {
    23.                 //locks onto the target
    24.                 if(CurrentTarget != null && !IsScrambled)
    25.                 {
    26.                     Vector3 TargetLocation =  CurrentTarget.position - _transform.position;
    27.                     Quaternion targetRotation = Quaternion.LookRotation(TargetLocation);
    28.  
    29.                     _rigidBody.MoveRotation(Quaternion.RotateTowards(_rigidBody.rotation, targetRotation, TrackingRotation));
    30.                 }
    31.                 else {
    32.                      //handle scrambled behaviour...
    33.                 }
    34.                              
    35.                 _rigidBody.velocity = _transform.forward * velocity;
    36.             }
    37.             else {
    38.                 _rigidBody.drag = 0.01f;
    39.                 _rigidBody.useGravity = true;
    40.             }
    41.  
    42.            CurrentFuel -= Time.deltaTime;
    43.     }
    44.  
    45.  
     
  10. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Nice and simple code :) so you don't have the issue of missing targets etc? Also when for instance the target makes a sharp turn and accelerates?
     
  11. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    I see the same behavior using your code as my simple "fly towards" approach. It hits a stationary target, but will never hit a moving target.
     
  12. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    You can see the projectiles in a video of inferno in my sig. Hits moving targets fine.

    You can make the missile miss by having a slow turn rate. Obviously the higher the turn rate, the less chance you have of getting away. Also speed of missile comes into play there. If the missile is too fast and turns too slow, likely to miss a fast target.
     
  13. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    I found the problem! It was how I was moving the missile. I was using
    Code (CSharp):
    1. this.GetComponent<Rigidbody>().AddRelativeForce(Vector3.forward * Time.fixedDeltaTime * 1000, ForceMode.Acceleration);
    instead of
    Code (CSharp):
    1. _rigidBody.velocity = transform.forward * 100;
    Apparently dealing with force on a RB missile will ensure they always miss, due to not correcting for inertia. I didn't think it'd cause enough discrepancy between desired and actual path, but it does. If I want to go back to a thrust-based system I'll have to correct for my current momentum.
     
  14. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Im surprised that was the problem.

    I found velocity was the best approach to controlling missiles though.
     
  15. Palimon

    Palimon

    Joined:
    Apr 18, 2013
    Posts:
    225
    Yeah, same, which is why I never bothered to try it until it was obviously the last thing I could try...sigh, lol. Thanks for the help, both of you!
     
  16. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Yep like it says on that wiki page I linked you'll have to calculate the proper intercept path for the missile. Trying to do it at the moment just for fun, too bad I suck at math :) what better than to take such a difficult thing to pain myself in the evening haha.
     
  17. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Prediction for a missile should be relatively easy. If you want to give it a bit of lead...

    psudo code...

    distanceOffset = Clamp(100 / distance, 0, 1);
    target.forward * target.speed * distanceOffset;
     
  18. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Wow that was quick. To be honest I would've never thought of it that easy haha. Like I said I'm no math king haha. Was literally trying to recreate https://en.wikipedia.org/wiki/Proportional_navigation
     
  19. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Heh, tha'ts relatively simple. The fun is when you want to make a bullet hit a moving target. Combination of that first link + angle of reach calculation.
     
  20. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Now you're just making me jealous with your wisdom haha. :p Do you maybe know a link or tutorial somewhere on how to read those mathmatical formulas like on that Wiki link I gave? I'll dive into my Math 101 book from pre-school again.. :confused:
     
  21. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    lol.

    Nah most of it goes back to my old school days of BEDMAS (think its called something different now). Operator Precedence.

    All the trajectory stuff I've used came from here: https://en.wikipedia.org/wiki/Trajectory_of_a_projectile

    Here's some free codes... Might help with understanding how I turned algorithms into code.

    To calculate Angle of reach (how much angle to aim above target to hit with bullet)

    Code (csharp):
    1.  
    2.     public static bool CalculateTrajectory(float TargetDistance, float ProjectileVelocity, out float CalculatedAngle)
    3.     {
    4.         CalculatedAngle = 0.5f * (Mathf.Asin ((-Physics.gravity.y * TargetDistance) / (ProjectileVelocity * ProjectileVelocity)) * Mathf.Rad2Deg);
    5.         if(float.IsNaN(CalculatedAngle))
    6.         {
    7.             CalculatedAngle = 0;
    8.             return false;
    9.         }  
    10.         return true;
    11.     }
    12.  
    13.  

    The one that took me a while to get right was..
    Angle required to hit coordinate (x,y)

    Code (csharp):
    1.  
    2.         float v = WeaponSystem.Projectile.Velocity;
    3.         float g = Physics.gravity.y;
    4.         float x = actualDistance;
    5.         float y = 0;
    6.        
    7.         float v2 = v * v;
    8.         float v4 = v * v * v * v;
    9.        
    10.         float gx2 = g * x * x;
    11.         float yv2 = 2 * y * v * v;
    12.         float gx = g*x;
    13.        
    14.         float res =  Mathf.Sqrt(v4 - g * (gx2 + yv2));
    15.         float res1 = v2 + res;
    16.         float res2 = res1 / gx;
    17.        
    18.         float trajectoryAngle = Mathf.Atan(res2) * 180 / Mathf.PI;
    19. [/SIZE]
    20.  


     
  22. Octopixell

    Octopixell

    Joined:
    May 18, 2013
    Posts:
    29
    Thanks James! This will help me a lot :) (I'm sure it'll also help many others)