How to create a homing missile?

Post Reply
Hotshot5000
Posts: 2
Joined: Sun Aug 12, 2018 11:30 am

How to create a homing missile?

Post by Hotshot5000 »

I have a rocket that moves with constant velocity. The linear velocity is always applied to the -Z axis(moving it forward). I want to be able to generate torque impulses that nudge the orientation of the rocket towards the enemy ship. I have searched these (and other forums) for the answer and there are things that are still unclear to me. I've read about calculateVelocity in src/LinearMath/btTransformUtil.cpp (https://pybullet.org/Bullet/phpBB3/view ... ate#p22997) but it doesn't seem to to what I need it to do. The linear velocity is constant and the returned angular velocity does not help me with the needed impulse.

What I would like is a function that gets called every frame and adjusts the course. Right now I have the following (java code from libgdx using a bullet wrapper):

Code: Select all

ENG_Vector4D entityPos = entityProperties.getNode().getPosition();
        ENG_Quaternion entityOrientation = entityProperties.getNode().getOrientation();
        ENG_Vector4D othEntityPosition = otherEntityProperties.getNode().getPosition();
        ENG_Quaternion othEntityOrientation = otherEntityProperties.getNode().getOrientation();
        ENG_Vector4D diff = otherEntityProperties.getNode().getPosition().subAsVec(entityProperties.getNode().getPosition());
        diff.normalize();

        ENG_Quaternion rotationTo = entityProperties.getNode().getLocalInverseZAxis().getRotationTo(diff);
        btTransformUtil.calculateVelocityQuaternion(new Vector3(entityPos.x, entityPos.y, entityPos.z),
                new Vector3(othEntityPosition.x, othEntityPosition.y, othEntityPosition.z),
                new Quaternion(entityOrientation.x, entityOrientation.y, entityOrientation.z, entityOrientation.w),
                new Quaternion(rotationTo.x, rotationTo.y, rotationTo.z, rotationTo.w),
                MainApp.getMainThread().getCurrentElapsedTime(), linearVelocity, angularVelocity);
        
        entityProperties.getRigidBody().applyTorqueImpulse(angularVelocity * some scaling); //This makes no sense, I know
What I do is I take the missile position with its front vector and find the quaternion that represents the rotation needed to get from the front to pointing in the enemy direction. Then I find the angular velocity using the calculateVelocityQuaternion() function. I then use (in code not shown here) the angular velocity as a sort of direction in which to apply the torque and scale it to some values until it mostly follows the enemy ship. The problem with this (wrong) approach is that sometimes the missile is almost properly aligned but still gets an impulse to fully align, but instead of aligning it overshoots. After that, the impulse gets bigger so it overshoots even more in the other direction, completely missing the target spinning around like an idiot.

Any idea on how I can gently slow down correctly when the missile is almost on target?

Another constraint that I forgot about is that the missile has a maximum angular velocity, so the impulse must not make the missile rotate more distance per second than allowed. If that limit is reached then the missile should simply fly around the target, missing it.

[EDIT]: The game takes place in space and there is no gravity. But there is angular and linear damping so things don't spin and move endlessly. I also checked the first answer from https://answers.unity.com/questions/488 ... sort=votes but I am not able to find the equivalent values from PhysX in Bullet. Namely the inertiaTensorRotation and the inertiaTensor. It seems bullet only uses inverse values for these things.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to create a homing missile?

Post by drleviathan »

Here's an idea: slam the velocity rather than applying impulse. I would do it something like below. Note: I don't know java so consider the example to be pseudocode. Dunno if it is bug-free: I didn't test.

Code: Select all

ENG_Vector4D newMissileDirection = targetProperties.getNode().getPosition().subAsVec(missileProperties.getNode().getPosition());
newMissileDirection.normalize();

ENG_Vector4D missileDirection = missileProperties.getNode().getLocalInverseZAxis();
float cosAngle = missileDirection.dot(newMissileDirection);
if (cosAngle > 0.99) {
    // properly aligned so we zero angularVelocity
    missileProperties.getRigidBody().setAngularVelocity(Vector3(0.0, 0.0, 0.0));
} else {
    // need to change orientation
    if (cosAngle <= -1.0) {
        // need to flip all the way around
        // ignore this case
    } else {
        float angle = acosf(cosAngle);
        ENG_Vector axis = missileDirection.cross(newMissileDirection);
        axis.normalize();
        Vector3 angularVelocity(axis.x, axis.y, axis.z);

        // exponentially decay the angular velocity toward the new direction
        // this is easy because when we assume the motion has exponential form the velocity is a scaled version of the displacement
        //     x(t) = Xo * exp(-t / tau)
        //     v(t) = (-1/tau) x(t)
        // for our case we've already computed the correct direction so we don't need the extra negative sign

        // tune the angular response by adjusting EXPONENTIAL_TIMESCALE which represents the time it takes for the rotation
        // to reduce its offset by 1/e.  After only three timescales the orientation should be very close.
        // Larger timescales are slower, smaller timescales are faster.
        float EXPONENTIAL_TIMESCALE = 0.5;

        // but if you go too small because you risk instability (never use timescales smaller than the substep).
        float deltaTime = MainApp.getMainThread().getCurrentElapsedTime();
        if (deltaTime > EXPONENTIAL_TIMESCALE) {
            EXPONENTIAL_TIMESCALE = 1.5 * deltaTime;
        }

        // compute angularVelocity
        float angularSpeed = angle / EXPONENTIAL_TIMESCALE;
        if (angularSpeed > MAX_ANGULAR_SPEED) {
            angularSpeed = MAX_ANGULAR_SPEED;
        }
        angularVelocity *= angularSpeed;

        missileProperties.getRigidBody().setAngularVelocity(angularVelocity);
    }
}
Hotshot5000
Posts: 2
Joined: Sun Aug 12, 2018 11:30 am

Re: How to create a homing missile?

Post by Hotshot5000 »

Thank you very much. Initially I did it using angular velocity but didn't take into account the EXPONENTIAL_TIMESCALE so I was seeing instability. I though about solving it by going the torque route. Turns out it wasn't needed and I only made things harder for myself. Again, thank you! Now it's working correctly.
PcChip
Posts: 33
Joined: Sun May 20, 2018 3:09 pm

Re: How to create a homing missile?

Post by PcChip »

is that sometimes the missile is almost properly aligned but still gets an impulse to fully align, but instead of aligning it overshoots. After that, the impulse gets bigger so it overshoots even more in the other direction
sounds like you could solve this by using a simple PID controller, there are two good ones easily found on github

I used one to stabilize an (extremely simplified) quadrotor - https://www.youtube.com/watch?v=_W4QTdgoy8g
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: How to create a homing missile?

Post by paokakis »

I know that this is an old topic but here I go. I am using the code that DrLeviathan has provided with pretty good results. My problem is that once in a while the missile won't hit the target but will go around the target in a spherical loop. Any pointers what could be optimized to fix this?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to create a homing missile?

Post by drleviathan »

I have a few ideas for making a homing missile smarter:

(1) Reduce the value of EXPONENTIAL_TIMESCALE. This will make the missile orient toward its intended target faster. However, this just effectively reduces the orbit radius of the problem you're seeing and it makes the homing behavior more aggressive. If the missile seems too smart/aggressive at the start you could try making the EXPONENTIAL_TIMESCALE dynamic instead of constant: reduce its value as the missile gets closer. If you reduce the effective orbit radius to be smaller than that of your target, then the missile should always be able to hit a stationary target.

The logic above only orients the missile. It doesn't know or use linear velocity. You could make it smarter by measuring the relative velocity between missile and target, and using that to adjust the missile's orientation. I see two levels of doing this:

(2) Rather than have the missile orient toward the target's current world-frame position... you could compute the target's position + velocity in the missile's local-frame, approximate the missile's time to arrival (using simple time=distance/rate formula which assumes the missile is traveling straight at the target with constant velocity), then orient the missile toward the target's future position.

(3) Make the missile over correct its angular orientation. That is, rather than aim its orientation toward the target's future local-frame position, it should orient itself so that it adjusts its linear velocity to point at the target's future position. The best strategy for velocity reorientation depends on how the missile manages to change it: Is it only via its thruster? (e.g. is it in a vacuum with no air-foils?) or does it have guiding fins in an atmospheric substrate that help deflect sideways velocity component toward missile-forward direction?

(4) Finally, if you want to make a really smart missile that almost never misses... you could implement (3) but replace the naive first-order ETA approximation mentioned in (2) with a higher-order system that takes into consideration variable linear velocity and variable orientation. The math here gets complicated and would require derivation of second-order analytic equations and maybe even some numeric integration loops.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to create a homing missile?

Post by drleviathan »

I forgot to mention some other ideas:

(5) As hinted at in (3) where I mentioned fins + atmospheric substrate... if you provide a stronger coupling between the missile's forward direction and its linear velocity, such that when it changes direction it also tends to re-orient the existing velocity to point along the missile's new forward direction (e.g. give it "fins"), then it will be more likely to spiral in on its target rather than just orbit.

(6) Rather than using pure exponential decay, which slows down as x gets close to zero, you could add a constant-ish term to the velocity relation so that the slewing of the missile remains fast enough to close the final angle within a single substep. This would give you two knobs to tune instead of just one, and could cause the missile to overshoot if the knobs are adjusted too agressively: that is, it might wobble along the straight path once it is pointed in the right direction.

Code: Select all

        //     x(t) = Xo * exp(-t / tau)
        //     v(t) = (-1/tau) x(t) - C * x_direction(t)
        // where x_direction = x / |x| (with care not to divide by zero)
        // and C = some constant D when |x| > D * substep, else C = |x| / substep (or zero here might work)
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: How to create a homing missile?

Post by paokakis »

Thank you for the replies DrLeviathan. I reduced the exponential time scale and the results are much better now
Post Reply