Rotation with limited turn rate in degrees

Recommended Posts

Hey guys,

I have a homing missile which I want to direct towards the target with a limited rotation speed.

The rotation should be limited by an angle in Degrees per second.

How do I accomplish this?

2D or 3D?

It's 3D

Share on other sites
Posted (edited)
	vec curDir = turret.orientation.FrontDirection();
vec targetDir = (target - turret.Position).Unit();
// calculate axis and angle rotation between directions
vec axis = (curDir.Cross(targetDir)).Unit(); // eventually reverse operands - not sure about it
float angle = acos( curDir.Dot(targetDir) ); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
// limit angle if necessary
const float maxAngularVelocity = 1.0f / 180.0f * 3.14f; // e.g. 1 degree per second
const float timestep = 1.0f / 60.0f;
float maxAnglePerUpdate = maxAngularVelocity / timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;
// finally rotate turret
turret.orientation.RotateByAxisAndAngle (axis, angle);


I hope this is correct - written just out of my head and not tested.

Be sure to try the correct order for the cross product.

EDIT: bugfix: maxAnglePerUpdate = maxAngularVelovity * timestep

Edited by JoeJ

Share on other sites

sadly it isn't working for me. The projectile either keeps spinning wildly or is going in a different direction

Share on other sites
31 minutes ago, Twaxx said:

The projectile either keeps spinning wildly or is going in a different direction

Oops, used division instead multiplication, see Edit of above post.

I'v tested it now with this code:

static bool visTurret = 1; ImGui::Checkbox("visTurret", &visTurret);
if (visTurret)
{
struct Turret
{
quat orientation;
vec position;

Turret ()
{
orientation.Identity();
position = vec(0,0,0);
}
};
static Turret turret;

static vec target (10, 3, 0);
ImGui::DragFloat3 ("target pos", (float*)&target, 0.01f);

vec curDir = turret.orientation.Rotate(vec(1,0,0));
vec targetDir = vec(target - turret.position).Unit();

// calculate axis and angle rotation between directions
float angle = acos( curDir.Dot(targetDir) ); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
if (angle > 1.0e-5f) // otherwise axis would be result of division by zero
{
vec axis = (curDir.Cross(targetDir)).Unit(); // eventually reverse operands - not sure about it

// limit angle if necessary
const float maxAngularVelocity = 10.0f / 180.0f * 3.14f; // e.g. 10 degree per second
const float timestep = 1.0f / 500.0f; // i have 500 fps
float maxAnglePerUpdate = maxAngularVelocity * timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;

// finally rotate turret
quat rot; rot.FromAxisAndAngle (axis, angle);
turret.orientation *= rot;
turret.orientation.Normalize();
}

// visualize
RenderLine (target, turret.position, 0.5,0.5,0.5);
RenderQuat (turret.position, turret.orientation);
RenderPoint (target, 1,1,1);
}

There is still one problem: If curDir and TargetDir point to opposite halfspace, the turret aligns to the back side

But i doubt it is worth to fix this, because a real turret has some joint limits, and can not roll. It would be better to start a new approach that builds two rotations from two Euler angles (like a FPS camera - no roll there as well).

I suggest you try it out anyways and tell me what you think...

Share on other sites

Thank you very much, I got it working

Share on other sites

I just wanted to mention that Slerp https://en.wikipedia.org/wiki/Slerp also does this and solves some simple problems you get from this kind of rotation.

You would calculate the offset to find the rotation to the target. Then use speed as the time value, updating each tic. This is a real easy way if you have a math library already hooked up.For example the Unity version would look like this:

Spoiler

Unity:


void HomeOnTarget()
{
//Get offset and make it a rotation
Vector3 offset = Target.transform.position - this.transform.position;
Vector3 TargetDirection = offset.normalized;

//Slerp between current rotation and target rotation based on speed
this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
Quaternion.LookRotation(TargetDirection, this.transform.up), Time.deltaTime/ Speed);

//Move in rotation direction
this.transform.position += this.transform.forward * Speed * Time.fixedDeltaTime;
}

Slerp makes a lot of rotation math simple.

Share on other sites
3 hours ago, Scouting Ninja said:

Quaternion.LookRotation﻿(TargetDirection, this.transform.up)﻿

That's nice. But it would be better to use global up direction instead the turrets local, then the rolling is already prevented.

My backwards bug still bothered me and while fixing it i tried this and it works nicely, also added a damping so motion is smooth (constant speed always looks so cheap).

Other than that our approaches should be equivalent, assuming Unity already chooses the shorter arc for the slerp internally.

	struct Turret
{
quat orientation;
vec position;

Turret ()
{
orientation.Identity();
position = vec(0,0,0);
}
};
static Turret turret;

static vec target (10, 3, 0);
ImGui::DragFloat3 ("target pos", (float*)&target, 0.01f);

vec curDir = turret.orientation.Rotate(vec(1,0,0));
vec targetDir = vec(target - turret.position).Unit();

float dot = curDir.Dot(targetDir);
if (fabs(dot) < 1.0f - 1.0e-5f) // otherwise axis would be result of division by zero
{
// calculate axis and angle rotation between directions
float angle = acos(dot); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
vec axis = (curDir.Cross(targetDir)).Unit();

// optional: some damping to make it smooth. This simple approach assumes constant timestep and needs to be adjusted to it.
const float damp = 0.03f;
angle *= damp;

// limit angle if necessary
const float maxAngularVelocity = 30.0f / 180.0f * 3.14f; // e.g. 30 degrees per second
const float timestep = 1.0f / 500.0f; // i have 500 fps
float maxAnglePerUpdate = maxAngularVelocity * timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;

// finally rotate turret
quat rot; rot.FromAxisAndAngle (axis, angle);

if (0) // no upvector - turret will roll and go out of place after some time
{
//turret.orientation *= rot; // depending on conventions, multiplication order decides to do the rotation in either local or global space, which i never remember. So this line caused the backwards bug for me i've mentioned before.
turret.orientation = rot * turret.orientation;
turret.orientation.Normalize();
}
else // using upvector, turret Y axis remains on the ground plane
{
const vec globalUp (0,1,0);
vec newTurretDir = rot.Rotate(curDir);
if (fabs( globalUp.Dot(newTurretDir) ) < 1.0f - 1.0e-5f) // turret would be clueless uf pointing exactly up / downwards
{
vec side = vec(globalUp.Cross(newTurretDir)).Unit();
vec localUp = newTurretDir.Cross(side);

matrix temp;
temp[0] = newTurretDir;
temp[1] = side;
temp[2] = localUp;
temp.Rotation()->ToQuat(turret.orientation);
}

}

}

// visualize
RenderLine (target, turret.position, 0.5,0.5,0.5);
RenderQuat (turret.position, turret.orientation);
RenderPoint (target, 1,1,1);
RenderCircle (1, turret.position, vec(0,1,0), 0,1,0);

Share on other sites
Posted (edited)

The way I would do it is:

• Compute the quaternion that would produce the "most natural rotation" that would align your missile with the target.
• Look at its real coordinate to see if the rotation is larger than the limit. The real part should be at least cos(limit_angle/2). If needed, modify the quaternion so its real part is exactly cos(limit_angle/2), scaling the imaginary parts to keep the quaternion having unit length.
• Apply the quaternion to rotate the missile.

You can find code to compute the "most natural rotation" in this thread

Depending on the genre, a more realistic Physics simulation of the missile's dynamics and a control mechanism (like a PID loop) to guide the missile could give you better results.

Edited by alvaro

Create an account

Register a new account

1. 1
2. 2
3. 3
Rutin
15
4. 4
khawk
14
5. 5
frob
12

• 9
• 11
• 11
• 23
• 12
• Forum Statistics

• Total Topics
633661
• Total Posts
3013226
×