Find yaw/pitch of one vector relative to another

Started by
14 comments, last by Zakwayda 18 years, 11 months ago
This is a 3D problem that I am trying to solve. I have a scenario where I need to be able to travel in a direction (v) while maintaining a line of sight with a point in 3D space (u). However, one restriction is that the line of sight must be confined to a fixed viewing area. For instance, the camera traveling in the v direction is restricted so that it can only tilt up/down 45 degrees and turn left/right 50 degrees. If the point in 3D space is too far behind the camera, then the camera will no longer be able to point in the precise direction of the point, but it would point in the general direction of the point. I've been tackling this problem for some time now, and conceptually, it is very easy to understand. The most confusing aspect to me is how to accurately calculate the angles for the yaw and pitch. What I've done so far.... To determine the values for the camera's direction vector, I am taking the known yaw and pitch angles (my current heading) and calculating the x, y, z coordinates onto a unit sphere. v.x = cos(alpha) * cos(beta); v.y = sin(beta); v.z = sin(alpha) * cos(beta); This produces my direction vector in an already normalized form. I then take the point in space that I'm looking at and subtract my current position to create the vector, u, to the point in 3D space. I am not normalizing this vector. Once I've created these two vectors, I attempt to get the yaw and pitch angles between them. To do this, I create a new vector, w, by subtracting u from v (so w = v - u). I then normalize that vector. I know that the yaw angle will be the asin of the y component of the w vector. yaw = asin(w.y) That yaw value can then be plugged in to my sphere equation to give pitch. Of course, I'll have to account for situations where cos(yaw) equals 0. pitch = acos(w.x / cos(yaw)) However, the results that I'm seeing don't match what I am expecting to see. As an example... in some cases, a yaw angle of 82 might be calculated when I'm actually expecting 98. I know that this is due to the fact that my C compiler returns asin values in the range -PI/2 to PI/2, however I don't know how to deal with it. Incidentally, I'm not as concerned with verifying whether or not the camera can actually position itself to view the point. That will be relatively easy. I included that in the description to help indicate *why* I need to know the exact values of the pitch and yaw. Also, the roll angle at this point is 0, but I can't guarantee that it will always be so. Having done some research on this problem, I am unsure as to whether I should be doing this using quaternions or Euclidean math. Quaternions are totally foreign to me, whereas Euclidean math has the drawback of gimbal lock. Does anyone have any experience with this type of problem? If so, any help would be greatly appreciated. Thanks, Brian
Advertisement
I didn't quite follow what you were doing there. Whether that's because it's wrong or just because I didn't understand it, I'm not sure. So I'll just tell you how I would try to solve this problem.

If I understand correctly, we have a camera which for now rotates with yaw and pitch only. We also have a direction vector, v, derived from the yaw and pitch values.

There is a point, which I'll call p, that we would like the camera to look at. We are interested in finding the yaw and pitch deltas that, if applied to our current yaw and pitch, would result in v pointing directly at p. Of course the deltas can be applied incrementally over time rather than all at once.

Let d = p - camera.position. We can project d and v into the xz plane by dropping the y components. We can now use the following code to find the signed angle between the two vectors in 2d:

Vector2 v1(v.x, v.z);
Vector2 v2(d.x, d.z);
deltaYaw = atan2(v1.PerpDot(v2), v1.Dot(v2));

Note that the input need not be normalized. Also, you may have to negate the result, depending on your conventions.

If p is directly above the camera, this will fail, as it should. In that case no deltaYaw is required. If the camera is pointing straight up it will also fail. This wouldn't be the result that I would want, since if the camera is pointing straight up it should still be able to yaw 'towards' p. So instead of using v in the above code, I would just use the vector [cos(yaw), sin(yaw)]. That way the camera will yaw in the appropriate direction even when pointing straight up.

For pitch, we can find the signed angle from d to the xz plane as targetPitch = atan2(d.y, v2.Length()). Then, deltaPitch = targetPitch - pitch.

Note that if p is at or very near the camera position, both calculations will fail, as they should. So you'll only want to proceed if |d| > epsilon.

Well, it's quite late and I could have easily made some mistakes, or just be wrong altogether. In any case, let me know if any of the above is not clear.
Quote:Original post by BrianDavis
Having done some research on this problem, I am unsure as to whether I should be doing this using quaternions or Euclidean math. Quaternions are totally foreign to me, whereas Euclidean math has the drawback of gimbal lock.
Euler angles have the drawback of gymbal lock. The Axis-angle method does not.

Do you want the camera to point as close as possible towards some point, subject to constraints on the amounts of pitch and yaw?
Or do you want some object to move around on the screen but not go outside of what the camera can see, whilst holding the camera still?
Can you sum up what you are wanting to do in one sentence please, if it doesn't match either of these.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
Do you want the camera to point as close as possible towards some point, subject to constraints on the amounts of pitch and yaw?

Yes. That would be a more concise way of putting it.

Basically, I have a vehicle with a camera positioned on its nose which has a 100 degree (left/right) by 90 degree (up/down) range of motion; so if the object I'm wanting to look at falls outside that range, then the camera would point in the most likely direction of the object.

Quote:Original post by jyk
Vector2 v1(v.x, v.z);
Vector2 v2(d.x, d.z);
deltaYaw = atan2(v1.PerpDot(v2), v1.Dot(v2));


How are you calculating v1.PerpDot(v2)? Is it the dot product of v1 with the cross product of v1 and v2?

Quote:Original post by jyk
Also, you may have to negate the result, depending on your conventions.


How do I determine this?
Quote:How are you calculating v1.PerpDot(v2)? Is it the dot product of v1 with the cross product of v1 and v2?
Remember that v1 and v2 are 2d vectors, derived from projecting v and d into the xz plane, so the cross product will not be involved. For two vectors [x1 y1] and [x2 y2], the perp-dot (or at least one formulation of it) is x1y2-x2y1.
Quote:How do I determine this?
If the result is wrong, negate it :) I'm pretty sure the result will be correct, though, unless you're mixing right- and left-handed coordinate systems and rotations.

I'll try to put everything together. The following code should take your current yaw and pitch and a point 'p', and produce appropriate deltas that will align the camera with p.

Vector3 d = p - camera.position;
float s = sin(yaw);
float c = cos(yaw);
deltaYaw = atan2(c*d.z-s*d.x, c*d.x+s*d.z);
deltaPitch = atan2(d.y, sqrt(d.x*d.x+d.z*d.z)) - pitch;

Again, no guarantees here - there are a lot of places I could have messed up. Also, in the above example I didn't check for p being directly above or at the camera position.

This general approach should be perfectly adequate as long as only yaw and pitch are involved. But as has been suggested elsewhere in the thread, once you add roll, you may need to switch to matrices or quaternions.
Quote:Original post by jyk
Vector3 d = p - camera.position;
float s = sin(yaw);
float c = cos(yaw);
deltaYaw = atan2(c*d.z-s*d.x, c*d.x+s*d.z);
deltaPitch = atan2(d.y, sqrt(d.x*d.x+d.z*d.z)) - pitch;

Again, no guarantees here - there are a lot of places I could have messed up. Also, in the above example I didn't check for p being directly above or at the camera position.


Thanks for the code snippet. It's kind of working, but I'm relatively certain that my problem lies in the way my coordinate system has been set up for me (not by me.) How are your axes set up? I have Z up, X in and Y right.

Thanks for your help.
Brian

Quote:Thanks for the code snippet. It's kind of working, but I'm relatively certain that my problem lies in the way my coordinate system has been set up for me (not by me.) How are your axes set up? I have Z up, X in and Y right.
Oops, I inferred y-up from your original post. The code I posted will definitely not work if z is up. I'm wondering, though...here is the code you posted for finding v:

v.x = cos(alpha) * cos(beta);
v.y = sin(beta);
v.z = sin(alpha) * cos(beta);

This looks like y-up to me. Are you sure this code works correctly? Or perhaps I'm missing something...

In any case, I can certainly post a revision with z up. But to make sure I get it right this time: when yaw is zero, where is your camera pointing? (i.e. +/-world x or +/- world y.)

I think the solution is correct; it's just a matter of getting the coordinate system issues worked out.
Perhaps another (general) solution:

1. Create a classic lookAt() matrix using vector arithmetic.
2. Extract Euler angles in the appropriate order.
3. Clamp Euler angles as desired.
4. Create new matrix from clamped Euler angles.

// Extracts yaw, pitch, roll ordering (from <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Shoemake's code</a>).inline Vec3 Mat3::getEulerYXZ(void) const {  Vec3 euler;  euler.x = rAsin(-z.y);  if (euler.x < R_PI/2.f) {    if (euler.x > -R_PI/2.f) {      euler.y = rAtan2(z.x,z.z);      euler.z = rAtan2(x.y,y.y);    } else { // Not a unique solution      euler.y = -rAtan2(-y.x,x.x);      euler.z = 0.f;    } // if  } else { // Not a unique solution    euler.y = rAtan2(-y.x,x.x);    euler.z = 0.f;  } // if  return euler;} // Mat3::getEulerYXZinline Mat3 & Mat3::setEulerYXZ(Vec3 & euler) {  RSinCos ex(euler.x),ey(euler.y),ez(euler.z);  x.x = ey.cosine*ez.cosine + ex.sine*ey.sine*ez.sine;  x.y = ex.cosine*ez.sine;  x.z = -ez.cosine*ey.sine + ey.cosine*ex.sine*ez.sine;  y.x = ez.cosine*ex.sine*ey.sine - ey.cosine*ez.sine;  y.y = ex.cosine*ez.cosine;  y.z = ey.cosine*ez.cosine*ex.sine + ey.sine*ez.sine;  z.x = ex.cosine*ey.sine;  z.y = -ex.sine;  z.z = ex.cosine*ey.cosine;  return *this;} // Mat3::setEulerYXZ
The AP was me (forum logout bug). Here's the link again: Shoemake's code.

This topic is closed to new replies.

Advertisement