Rotation with limited turn rate in degrees

Started by
45 comments, last by the incredible smoker 5 years, 9 months ago
1 hour ago, alvaro said:

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

... which is exactly what i've done in my first reply. Your 'most natural rotation' is just the shortest rotation between two directions. You calculate it by converting axis and angle to quaternion, while i kept working with axis and angle because i think it's more intuitive for somebody asking a question like this.

Only mentioning this to avoid confusion or wrong expections, correct me if i'm wrong.

The problem is, a turret mounted on ground or a vehicle can not take any most natural rotation because it is usually constructed by two hinges. The up vector fixes this in a simple way, but this does not help with joint limits. So if this is necessary we either need to model the two hinges, or (more easily) convert the target direction to spherical coords in the turret reference orientation and clip them.

Advertisement

I

8 minutes ago, JoeJ said:

... which is exactly what i've done in my first reply. Your 'most natural rotation' is just the shortest rotation between two directions. You calculate it by converting axis and angle to quaternion, while i kept working with axis and angle because i think it's more intuitive for somebody asking a question like this.

Only mentioning this to avoid confusion or wrong expections, correct me if i'm wrong.

No, you are probably right. But you mentioned you were getting the problem that it could align to the opposite of the desired direction, so I tried to think about it from scratch.

 

Quote

The problem is, a turret mounted on ground or a vehicle can not take any most natural rotation because it is usually constructed by two hinges. The up vector fixes this in a simple way, but this does not help with joint limits. So if this is necessary we either need to model the two hinges, or (more easily) convert the target direction to spherical coords in the turret reference orientation and clip them.

I though we were talking about a homing missile, not a turret.

 

1 hour ago, alvaro said:

you were getting the problem that it could align to the opposite of the desired direction

That was just a bug caused by wrong quat multiplication order.

I could not resist to implement angle limits based on spherical coords - expected 10 mins of work but underestimated trig trial and error fun, haha. Works pretty well however :)


static bool visTurret = 1; ImGui::Checkbox("visTurret", &visTurret);
if (visTurret)
{
	struct Turret
	{
		// orientaton convention: X axis: front (turret shooting direction), Y axis: up, Z axis: side
		quat orientation;
		vec position;
		
		quat mountOrientation; // e.g. the ground or a vehicle frame, we use it to define up vector and angle limits
		float angleLimitLeft;
		float angleLimitRight;
		float angleLimitUp;
		float angleLimitDown;

		Turret ()
		{
			orientation.Identity();
			mountOrientation.Identity();
			position = vec(0,0,0);
			angleLimitLeft		= - 45.0f / 180.0f * 3.14f;
			angleLimitRight		= + 45.0f / 180.0f * 3.14f;
			angleLimitUp		= - 30.0f / 180.0f * 3.14f;
			angleLimitDown		= 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();

	if (1) // angle limits
	{
		vec dir = turret.mountOrientation.Unrotate(targetDir); // move to local mount space

		const int a=0, b=2, c=1;
		// cartesian unit vector to spherical coordinates
		float theta, phi; 
		{
			float denom = sqrt(dir[a]*dir[a] + dir[b]*dir[b]);
			float t = -dir[a] / (denom + 1.0e-16f);
			theta = acos( max(-1,min(1,dir[c])) );
			phi =	acos( max(-1,min(1,t)) );
			if (dir[b] > 0) phi = 2.0f*PI - phi;
		}

		// clip spherical coordinates
		phi -= PI;
		if (phi < turret.angleLimitLeft) phi = turret.angleLimitLeft;
		if (phi > turret.angleLimitRight) phi = turret.angleLimitRight;
		phi += PI;
		theta -= PI/2;
		if (theta < turret.angleLimitUp) theta = turret.angleLimitUp;
		if (theta > turret.angleLimitDown) theta = turret.angleLimitDown;
		theta += PI/2;

		ImGui::Text("t %f  p %f", theta/PI*180, phi/PI*180);
		// spherical coordinates to cartesian unit vector
		{
			dir[a] = -sin(theta) * cos(phi);
			dir[b] = -sin(theta) * sin(phi);
			dir[c] = cos(theta);
		}

		targetDir = turret.mountOrientation.Rotate(dir); // back to global space
	}

	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 this value. (for 60 fps try 0.3)
		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, ok for a spaceship
		{
			turret.orientation = rot * turret.orientation;
			turret.orientation.Normalize();
		}
		else // using upvector, turret side axis remains on the up plane defined by mountOrientation
		{
			const vec globalUp = turret.mountOrientation.Rotate(vec(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.5f,0.5f,0.5f);
	RenderQuat (turret.position, turret.orientation);
	RenderQuat (turret.position, turret.mountOrientation, 0.2f);
	RenderPoint (target, 1,1,1);
	RenderCircle (1, turret.position, vec(0,1,0), 0,1,0);
}

Edit: Doh - it would be faster and easier to use 4 plane constraints to limit target dir instead using spherical coords.

On 7/7/2018 at 3:01 PM, Twaxx said:

How do I accomplish this?

https://en.wikipedia.org/wiki/Proportional_navigation

#define if(a) if((a) && rand()%100)

I bet the topic starter is uninstalling his compiler by now,

You surely are some professors here, i try to understand your code : i gave up, tomorrow i read again.

 

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

12 minutes ago, Fulcrum.013 said:
On 7/7/2018 at 2:01 PM, Twaxx said:

How do I accomplish this?

https://en.wikipedia.org/wiki/Proportional_navigation

I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games). 

 

7 minutes ago, the incredible smoker said:

You surely are some professors here

Not really - just made the same mistake i've mentioned you did recently: Using trig when it's not necessary :)

(Although the plane based approach for limits might have disadvantages on ranges > 180 dgrees)

41 minutes ago, JoeJ said:

I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games). 

What's the scenario you're describing here?  A turret on a moving object trying to hit another moving object?

1 hour ago, JoeJ said:

Would look really cool if such a turret can still hit a target moving at constant velocity.

It just keept velocity vector directed to interception point while missile have enought energy to change its velocity vector. And it works with changes of sight line, that is result of movements,  so automatically take care about any forces applied to both  missile and target regardless of its nature. It able to hit ever maneuring targets while missile have enought motor enerjy to make maneurs with G that exids target's maneurs G. 

 

1 hour ago, JoeJ said:

And i've never seen this in a game yet (but i never play RTS games). 

I guess it just becouse automation control theory exids college course of CS. Locally even not all universities have it course. Also i dont know how it been at overseas, but locally prior middle of 80th it has been teached only into "closed" universities where KGB keept watch that studients and instructors can not get their texbooks and records out of campus. It technique firstly seen into AAA+++ RTS with elements of FPS released at 1947 AKA Coold War. So it enougt roubst to implement even on CPU that slover then controllers that drive LED lamps. Also it widely used  into air combat simulations games.

44 minutes ago, ferrous said:

A turret on a moving object trying to hit another moving object?

Topic starter question related to homing missiles aiming, that is really something like flying  singly charged shotgun.

#define if(a) if((a) && rand()%100)

2 hours ago, JoeJ said:

I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games). 

I seem to recall that Age of Empires had something like this. The archers would fire at a target, but the arrows would follow a ballistic trajectory and could miss. I don't think they really took tracking into account, though.

Other RTS games (StarCraft comes to mind) intentionally don't have this for anything other than visual effects. Actually simulating a homing missile would mean you need to simulate more stuff and make combat less deterministic, which would result in networking and tweaking game balance becoming harder.

1 hour ago, Fulcrum.013 said:

I guess it just becouse automation control theory exids college course of CS.

I would say that automation control theory is more of an engineering topic than computer science, since it's really just applied calculus. At least where I went to school, you might come across it in a robotics course or a course about numerical integration, but I wouldn't expect any particular computer science graduate to know anything about it. Many computer science graduates can barely do calculus as it is, since most CS (at that level) is discrete and abstract mathematics. I can think of exactly one computer science class where we used calculus and that was during maybe one lecture. :D

1 hour ago, ferrous said:

What's the scenario you're describing here?  A turret on a moving object trying to hit another moving object?

Think of throwing a stone at a hole in the ground. By using the equation describing its motion, you can calculate the parabolic trajectory, necessary initial velocity and direction of the stone, and the time duration it requires to hit the target. (Can be done all analytically.)

Now if the target would be a toy car driving at constant speed and direction, we could do as above to calculate the duration, then we integrate the duration to car position and repeat the process for a few iterations to get a good enough approximization. My question is, can we do this analytically as well without a need for iterations? I guess so but i've never tried, and often problems that appear easy to me turn out to have no analytic solution and vice versa. (So if you know the answer is no please tell me to save time - 2 or 3 iterations should be good enough anyways.)

If the guy who throws the stone is moving as well is of no concern, because it is only the relative velocity between turret and target that matters, which we can calculate easily in any case. 

But what makes it interesting is, there are always 2 solutions to this (it's a quadratic equation), so we can choose a higher or a lower angle. In realty one always chooses the lower angle: less energy, shorter hit duraton, better chance to hit. But if we pick the higher angle, the projectile is much longer in the air, and then it looks really cool if it still hits the target, like clever AI or something. I think this is very useful for games.

 

1 hour ago, Fulcrum.013 said:

Topic starter question related to homing missiles aiming

Oh, missed the 'homing' missle part, but the code to control a turret towards a direction is useful in any case. For advanced stuff now being discussed we would just calculate a better target vector.

 

34 minutes ago, Oberon_Command said:

Many computer science graduates can barely do calculus as it is

Makes me feel better :) I've never learned Calculus at all, not even proper linear algebra. (CS neither)

I wonder how you can do anything without calculus in CS. I mean, learning trees, graphs or sorting algorithms isn't a hurdle, but knowing nothing about calculus is. I think i've wasted more years in reinventing wheels than proper education would have taken. I can not even ask for help, because i really do not know what questions to ask in what order...  sigh :(

This topic is closed to new replies.

Advertisement