Advertisement

LookAt function

Started by September 05, 2006 10:14 AM
20 comments, last by gorogoro 18 years, 5 months ago
Hi! I'm having a lot of problems doing a LookAt function. The lookat function should rotate the agent so it faces some position, or direction. Here is the function:

bool Agent::LookAtDirection(Vector3f& dir, float angle, float speed /*=0*/)
{
	if(!speed)
		speed = GetMaxSpeed();

	dir.Normalize();

	Vector3f direction = GetWorldDirection();

	Vector3f upVector;
	GetWorldRotation().TransformVector(&upVector, Vector3f::k);

	float actualAngle = direction.Dot(dir);


	Vector3f c;
	float amount = 1;
	c.Cross(direction, dir);

	if (upVector.Dot(c) < 0)
		amount = -amount;
			
	if ( !((actualAngle >= angle)))
	{
            SetDesirableRotateZSpeed(-amount * speed);
		return false;
	}
}


As you notest I have a variable named amount this is used to determine to wich direction should the agent turn, to the left or to the rigth. But sometimes the agent passes the angle, and enters in the if, then the amount change the signal and agent turns to the other side. So the agent does'nt move and keeps shaking. The angle is the aperture angle so it is safe to say, ok you are aligned. The angle should vary accordingly with the distance. So as longer the distance the angle should be closer to 1, else it should be closer to 0.85 or something like that. For the angle I have this function:


float Agent::CalculateAngle(const Vector3f& pos)
{
	float sqrdist = GetWorldPosition().SquareDistance(pos);
	float alpha = sqrdist / (1600 + sqrdist);

	//alpha = cos(pi / 2 - atan(sqrdist));
	// alpha = sqrdist / sqrt( 500 + sqrdist * sqrdist );
	return ClampMinMax(alpha, 0.85f, 0.97f);
}


The comment code is some experience based on a post in this forum to. Basically I've tried to change the aperture of the angle, but or it is to much and the agent thinks that is in front of something and it isn't or else it starts shaking :p. I've tried to make some signal test, I mean when the variable amount changes its signal i return true. But it makes the same shake problem sometimes. I've also tried to slow down the rotation speed so it should not "jump" through the angle, without any positive result also. Now I'm running out of ideias. :p Have anyone passed through the same problem? Any more ideas?
Quote:
Original post by Anonymous Poster
Yes


use a PD-Controller and some dampening
works wonders

if you dont know that means, say so...


Help? :)
Advertisement
Quote:
Original post by Anonymous Poster
Special note:
the angle input you pass to controldecision
Must be the Relative angle between your current direction, and your goal direction; from the character's reference frame. Do not use absolute coord system angles, since that will mess up....
and it should be signed so that for example left is - and right is + (or vice versa)


Thanks a lot for the help!
Never heard about that PID controller! Do you have any reference of interest? I've google it, but didn't find any really interesting!

By the way! If you my lookAt function returns a boolean telling if the agent is already at position or not. How can I now that with a PID controller? When the returning value its close to 0??

[Edited by - gorogoro on September 6, 2006 9:57:05 AM]
Get any introductory text book on Control Theory and it will cover, in depth, PID-based control. My personal recommendation is 'Automatic Control Systems' by Benjamin Kuo. Books such as this, aimed at 2nd and 3rd year engineering students, typically cover the mathematical preliminaries you need for understanding and analysis in their early chapters, so if you're coming from a non-engineering background, you shouldn't have too much trouble learning the material (particularly if you keep a basic calculus book beside you as your read it).

As to where PIDs are used, well, it's not just robotics. Indeed, around 95% of controllers used in commercial control applications (and here I mean everything from spacecraft and aircraft flight systems through to thermostats in your house) are PI or PD controllers (there are very few full PID controllers, since they are generally not needed).

If you want more specific information or need help understanding PIDs, just holler.

Cheers,

Timkin
Freaking out once again! :p

I've read the wiki tutorial and the paper you post, but didn't understand to much about the process. Have to read more times I guess :P

I've implemented the pidController just like yours.

But the behaviour is exactly the same :(. It never stops from oscilate, even if I increase the D value or the P value. I'm seek of trying to tunne this values without good results. Humpf.

I've levead the CalculateAngle function.

Here are the alterations I've done to the LookAt function:

bool Agent::LookAtDirection(Vector3f& dir, float angle, float speed /*=0*/){	if(!speed)		speed = GetMaxSpeed();	dir.Normalize();	Vector3f direction = GetLocalDirection();	Vector3f upVector;	GetWorldRotation().TransformVector(&upVector, Vector3f::k);	float actualAngle = direction.Dot(dir);	//float error = angle - actualAngle;	if(actualAngle >= angle)		actualAngle = 0;	else	      actualAngle = 1 - actualAngle;			//TODO: Think of a way to do this in 3D (plain behavior!!)	Vector3f c;	float amount = 1;	c.Cross(direction, dir);	if (upVector.Dot(c) < 0)		amount = -amount;	float amountAux = m_pidController.ControlDecision( -amount * (actualAngle));		std::string text("PIDController returned: ");	SharedTools::RealToString(amountAux, text);	FE_LOG(text);	if ( amountAux < 0.05 && amountAux > -0.05)		return true;			SetDesirableRotateZSpeed(amountAux);	return false;}



any more suggestions? I'm doing anything wrong?
[depressed]
Tunning tunning! Can't stable this thing.

Ok! Some changes done. Even with low values I make the rotation now. And I've changed the way I calculate the error (PID input).

Now it is like this:

bool Agent::LookAtDirection(Vector3f& dir, float angle, float speed /*=0*/){	if(!speed)		speed = GetMaxSpeed();	dir.Normalize();	Vector3f direction = GetLocalDirection();	Vector3f upVector;	GetWorldRotation().TransformVector(&upVector, Vector3f::k);	float actualAngle = direction.Dot(dir);	float error;	if(actualAngle >= angle)		error = 0;	else if(actualAngle > 0)		error = angle - actualAngle;	else		error = -actualAngle + angle;	Vector3f c;	float amount = 1;	c.Cross(direction, dir);	if (upVector.Dot(c) < 0)		amount = -amount;	float amountAux = m_pidController.ControlDecision( -amount * (error));	std::string text("PIDController returned: ");	SharedTools::RealToString(amountAux, text);	FE_LOG(text);	SetDesirableRotateZSpeed(amountAux);	if ( amountAux < 0.05 && amountAux > -0.05)		return true;		return false;}


Since the angle is the cos(alpha).
The angle is calculated the same way, using the CalculateAngle function posted in the begging of the thread.
Advertisement
I would have set a target angle and simply rotated the agent according to an interpolated value between the start and target angles.
Quote:
Original post by Eldritch
I would have set a target angle and simply rotated the agent according to an interpolated value between the start and target angles.


I've tried that with no good results

Quote:
Original post by Anonymous Poster
Depends if your agent motion is based on physics or if you just want graphical smoothness...

I use the method you describe for my camera, but PD Controllers with Newtonian physics for all in-game entities


The motion is based on physics. But I'm not getting there with the PD Controller either....

Any other suggestion?? This is suposed to be a simple problem, but I'm not getting there :'(

Damn!

[Edited by - gorogoro on September 11, 2006 9:15:03 AM]
Ok!

Let's do it.

//The angle is the acceptable angle so we can say: "Ok your are facing the //rigth direction!//It varys, so if the agent is far from the thing it wants to face the angle is //near 0.97 (near 1). Else if the agent is close then the acceptable angle is //near 0.70bool Agent::LookAtDirection(Vector3f& dir, float angle, float speed /*=0*/){	if(!speed)		speed = GetMaxSpeed();	dir.Normalize();	//is this returning the current direction your character faces?	//YES	Vector3f direction = GetLocalDirection();	Vector3f upVector;	GetWorldRotation().TransformVector(&upVector, Vector3f::k);	//here you seem to be finding the dot produce between direction and dir	//but I dont know exactly what each one means...	//Here I'm just finding the up vector of the agent. 	//this is used just to determine if the agent should turn to the right or to the left	//if the angle between them is small... this should return near1	//if they are near perpendicualr, it shoudl return near 0	//obtuse angles are negative...		//Exacly, thats the point	//it tells nothing about rotating left or right, just magnitude	//are you sure that dot product is what you wanted?	//it's not Exactly the same as the actual angle between them...	//BUG?		//the point here is to determine de cos(angle) of the agent and the 	//goal diretion it wants to face.	float actualAngle = direction.Dot(dir);	//Dot product from the agent orientation and the goaldirection	float error;	//if the Dot product from the agent orientation and the dir is longer than the angle then it is facing the goaldirection	if(actualAngle >= angle)		error = 0;	else if(actualAngle > 0)//both of these branches are the same!!??		error = angle - actualAngle;	else		//-actualangle+angle is the same as angle-actualangle above		//this IF doesnt actually do anything		//BUG?		//you are rigth. this is not a bug, but an useless code.		error = -actualAngle + angle;	//not sure where you're going with this part...	//complicated, can you explain?	//this is used to determine to wich side the agent turns (left or rigth)	Vector3f c;	float amount = 1;	c.Cross(direction, dir);	if (upVector.Dot(c) < 0)		amount = -amount;      //and is working fine :)	float amountAux = m_pidController.ControlDecision( -amount * (error));	//debug stuff	std::string text("PIDController returned: ");	SharedTools::RealToString(amountAux, text);	FE_LOG(text);	SetDesirableRotateZSpeed(amountAux);	if ( amountAux < 0.05 && amountAux > -0.05)		return true;		return false;}


I'm going to put a cleaner code now.

bool Agent::LookAtDirection(Vector3f& goalDirection, float acceptableAngle, float speed /*=0*/){	//Speed is deprecated 	if(!speed)		speed = GetMaxSpeed();	dir.Normalize();	//returning the current direction the character face		Vector3f agentDirection = GetLocalDirection();	//the point here is to determine de cos(angle) of the agent and the 	//goal diretion it wants to face.	float actualAngle = agentDirection.Dot(goalDirection);	//Dot product from the agent orientation and the goaldirection	//if the angle between them is small... this should return near 1	//if they are near perpendicualr, it shoudl return near 0	//obtuse angles are negative...	float error;	//if the Dot product from the agent orientation and the dir is longer than the angle then it is facing the goaldirection	if(actualAngle >= acceptableAngle)		error = 0;	else 		error = acceptableAngle - actualAngle;			//this is used to determine to wich side the agent turns (left or rigth)	//GetWorldRoation gives the world rotation of the agent	Vector3f upVector;	GetWorldRotation().TransformVector(&upVector, Vector3f::k);		Vector3f c;	float amount = 1;	c.Cross(agentDirection, goalDirection);	if (upVector.Dot(c) < 0)		amount = -amount;    	//and is working fine :)	//the pid controller	float amountAux = m_pidController.ControlDecision( -amount * (error));	//debug stuff	std::string text("PIDController returned: ");	SharedTools::RealToString(amountAux, text);	FE_LOG(text);	SetDesirableRotateZSpeed(amountAux);	if ( amountAux < 0.05 && amountAux > -0.05)		return true;		return false;}


Now I'm going to try something simpler as you suggested :)

[Edited by - gorogoro on September 12, 2006 6:54:07 AM]
Thanks a Lot for your help Anonymous Poster.

It is working fine now with no occilations (at least it seams like that). More testing to come, but I have to move on..

Here is my final code:

bool Agent::LookAtDirection(Vector3f& goalDirection, float angle, float speed /*=0*/){      //speed deprecated	if(!speed)		speed = GetMaxSpeed();	goalDirection.Normalize();	Vector3f up;	GetWorldRotation().TransformVector(&up, Vector3f::k);	Vector3f rightside; 	rightside.Cross(GetLocalDirection(), up); //this might be backwards	float turnapprox = goalDirection.Dot(rightside);	float wtf = GetLocalDirection().Dot(goalDirection);	float amountturn = m_pidController.ControlDecision( turnapprox );	std::string text("PIDController returned: ");	SharedTools::RealToString(amountturn, text);	FE_LOG(text);	SetDesirableRotateZSpeed(amountturn);	if ( amountturn < 0.05 && amountturn > -0.05)		return true;	return false;}


[Edited by - gorogoro on September 12, 2006 9:02:51 AM]

This topic is closed to new replies.

Advertisement