Quaternion and axis of rotation - c++

This question has been answered. Below is most of the code that you need to make it work! Hope it helps others.
Thanks to #Aki Suihkonen, #David Hammen, and #MBo.
Both Angle functions give the right answer.
I have three points:
A: 12 4 5
B: 6 8 -10
C: 5 6 7
I have implemented quaternions. I want to rotate point C so that Angle( A, B, C ) would be 40 degrees higher than before.
My question is: According to which axis do I have to rotate? I imagined that because A,B, and C create a plane I have to rotate point C according to the perpendicular axis to the vectors BA and BC. I obtained it with the CrossProduct of their unit vectors but when I try to get the Angle( A, B, C ) it does not give me the right result.
This is how I get the angle: (The old way)
results:
Angle of ABC before rotation = 67.3895.
Angle of ABC after rotation = 107.389.
float Angle( float x1, float y1, float z1,
float x2, float y2, float z2 )
{
float x, y, z;
CrossProduct( x1, y1, z1, x2, y2, z2, &x, &y, &z );
float result = atan2 ( L2Norm( x, y, z ),
DotProduct( x1, y1, z1, x2, y2, z2 ) );
return result;
}
Where the x1, y1, z1, x2, y2, z2 are the results from the unit vectors B-A and B-C.
Updated angle function:
results:
Angle of ABC before rotation = 67.3895.
Angle of ABC after rotation = 107.389.
Angle( Atom &atom1, Atom &atom2, Atom &atom3 )
{
float px1, py1, pz1;
float px2, py2, pz2;
UnitVector( atom1.X(), atom1.Y(), atom1.Z(),
atom2.X(), atom2.Y(), atom2.Z(),
&px1, &py1, &pz1 );
UnitVector( atom3.X(), atom3.Y(), atom3.Z(),
atom2.X(), atom2.Y(), atom2.Z(),
&px2, &py2, &pz2 );
float dot_product = DotProduct( px1, py1, pz1, px2, py2, pz2 );
float length_BA = sqrt( px1*px1 + py1*py1 + pz1*pz1 );
float length_BC = sqrt( px2*px2 + py2*py2 + pz2*pz2 );
return acos( dot_product / ( length_BA * length_BC ) );
}
float DotProduct( float x1, float y1, float z1,
float x2, float y2, float z2 )
{
return x1*x2 + y1*y2 + z1*z2;
}
void CrossProduct( float x1, float y1, float z1,
float x2, float y2, float z2,
float *ox, float *oy, float *oz )
{
*ox = (y1*z2) -(z1*y2);
*oy = -((x1*z2) -(z1*x2));
*oz = (x1*y2) -(y1*x2);
}
So my question is: According to which axis do I have to rotate point C so that Angle(A,B,C) would be 40 degrees larger than before?
// The 3 points.
Atom A( "A", -4, 2 , 8 );
Atom B( "B", -1, 3 , 4 );
Atom C( "C", -2, -4 , 5 );
float x1, y1, z1;
float x2, y2, z2;
float x, y, z;
// Get the cross product. Create the perpendicular vector to the BA and BC vectors.
PointVector( A.X(), A.Y(), A.Z(), B.X(), B.Y(), B.Z(), &x1, &y1, &z1 );
PointVector( C.X(), C.Y(), C.Z(), B.X(), B.Y(), B.Z(), &x2, &y2, &z2 );
CrossProduct( x1, y1, z1, x2, y2, z2, &x, &y, &z );
// Normalize the coordinates.
float length = sqrt( x*x + y*y + z*z );
length = 1.0 / length;
x *= length;
y *= length;
z *= length;
// Create the 40 degrees angle. It is supposed to increment the current ABC angle by 40 degrees.
float angle = 40*M_PI/180;
float sinAngleOver2 = sin(angle/2);
float w = cos(angle/2);
// Create the axis quat.
Quatd q(w, x * sinAngleOver2, y * sinAngleOver2, z * sinAngleOver2);
// Create the point quaternal. The angle of it equals to the current ABC angle.
angle = Angle( A, B, C ) *180/M_PI;
angle *= M_PI/180;
sinAngleOver2 = sin(angle/2);
w = cos(angle/2);
// Normalize the coordinates. The coordinates are the C point coordinates.
x = C.X() - B.X();
y = C.Y() - B.Y();
z = C.Z() - B.Z();
length = sqrt( x*x + y*y + z*z );
length = 1.0 / length;
x *= length;
y *= length;
z *= length;
Quatd qpt(w, x * sinAngleOver2, y * sinAngleOver2, z * sinAngleOver2);
// Rotate.
qpt = q*qpt*q.unitInverse();
Atom new_C;
new_C.X( qpt.x + B.X() );
new_C.Y( qpt.y + B.Y() );
new_C.Z( qpt.z + B.Z() );
cout << "Angle: " << Angle( A, B, new_C )*180/M_PI << '\n';

This code is incorrect:
// Normalize the coordinates. The coordinates are the C point coordinates.
length = sqrt( C.X()*C.X() + C.Y()*C.Y() + C.Z()*C.Z() );
length = 1.0 / length;
x = C.X() / length;
y = C.Y() / length;
z = C.Z() / length;
You've confused yourself by reusing your length variable to store 1 / length. You need to multiply the components of C by length to normalize your vector here.
I'm not familiar enough with quaternion maths to understand what you are trying to do at the end, but it does seem like you are rotating C around the origin. You want to rotate around B, which would mean subtracting B from C, performing your rotation around the origin, and then adding B onto the result to get back into your original space.
Personally, I would implement some kind of quaternion-vector multiplication, or convert the quaternion to a matrix in order to perform the transformation.

Related

Given 2 point and a range from first point to the point that I have to find. How to find the point I have to find (on 3D Space)

I don't know how to find the third point that I have said.
However, I try to write code write this:
float extraX; // x of the third point
float extraY; // y of the third point
float extraZ; // z of the third point
void CalculateExtraPoint(float x1, float y1, float z1, float x2, float y2, float z2){
float range = extraRange; //range from the first point to third point
float d_x = x1-x2;
float d_y = y1-y2;
float d_z = z1-z2;
distance1_2 = sqrt(pow(d_x,2)+pow(d_y,2)+pow(d_z,2));
float temp = (range/distance1_2)+1;
extraX = x2 + (temp*d_x);
extraY = y2 + (temp*d_y);
extraZ = z2 + (temp*d_z);
}
It come from vector equation, r=(x2,y2,z2) + (lambda)[x1-x2,y1-y2,z1-z2]. r is any point.
Is it correct? Thanks in advance.
Yes, it is correct.
Slightly simpler, more convenient variant. Here it is more clear that we consider range from the first point:
float temp = range/distance1_2;
extraX = x1 + (temp*d_x);
extraY = y1 + (temp*d_y);
extraZ = z1 + (temp*d_z);

Values for chaotic scattering simulation do not match with the base case

My first post on Stack Overflow, be gentle. I wrote a code to follow the position on the x,y plane of a particle of mass M on a potential V(r) described by a four-dimensional system of equations of motion
M(dv/dt)=-grad V(r), dr/dt=v,
Which are solved by using the Runge Kutta 4th Order method, where r=(x,y) and v=(vx,vy); now the state of the particle is defined by x, y and the angle theta between the vector v and the positive x-axis where the magnitude of the velocity is given by
|v|=sqrt(2(E-V(r))/M)
where E is the energy in the plane and the potential V(r) is given by
V(r)=x^2y^2exp[-(x^2+y^2)],
now here is the code I made for the initial values
x(0)=3,
y(0)=0.3905,
vx(0)=0,
vy(0)=-sqrt(2*(E-V(x(0), y(0)))),
where E=0.260*(1/exp(2))
// RK4
#include <iostream>
#include <cmath>
// constant global variables
const double M = 1.0;
const double DeltaT = 1.0;
// function declaration
double f0(double t, double y0, double y1, double y2, double y3); // derivative of y0
double f1(double t, double y0, double y1, double y2, double y3); // derivative of y1
double f2(double t, double y0, double y1, double y2, double y3); // derivative of y2
double f3(double t, double y0, double y1, double y2, double y3); // derivative of y3
void rk4(double t, double h, double &y0, double &y1, double &y2, double &y3); // method of runge kutta 4th order
double f(double y0, double y1); //function to use
int main(void)
{
double y0, y1, y2, y3, time, E, Em;
Em = (1.0/(exp(2.0)));
E = 0.260*Em;
y0 = 3.0; //x
y1 = 0.3905; //y
y2 = 0.0; //vx
y3 = -(std::sqrt((2.0*(E-f(3.0, 0.0)))/M)); //vy
for(time = 0.0; time <= 400.0; time += DeltaT)
{
std::cout << time << "\t\t" << y0 << "\t\t" << y1 << "\t\t" << y2 << "\t\t" << y3 << std::endl;
rk4(time, DeltaT, y0, y1, y2, y3);
}
return 0;
}
double f(double y0, double y1)
{
return y0*y0*y1*y1*(exp(-(y0*y0)-(y1*y1)));
}
double f0(double t, double y0, double y1, double y2, double y3)
{
return y2;
}
double f1(double t, double y0, double y1, double y2, double y3)
{
return y3;
}
double f2(double t, double y0, double y1, double y2, double y3)
{
return 2*y0*((y0*y0)-1)*(y1*y1)*(exp(-(y0*y0)-(y1*y1)))/M;
}
double f3(double t, double y0, double y1, double y2, double y3)
{
return 2*(y0*y0)*y1*((y1*y1)-1)*(exp(-(y0*y0)-(y1*y1)))/M;
}
void rk4(double t, double h, double &y0, double &y1, double &y2, double &y3) // method of runge kutta 4th order
{
double k10, k11, k12, k13, k20, k21, k22, k23, k30, k31, k32, k33, k40, k41, k42, k43;
k10 = h*f0(t, y0, y1, y2, y3);
k11 = h*f1(t, y0, y1, y2, y3);
k12 = h*f2(t, y0, y1, y2, y3);
k13 = h*f3(t, y0, y1, y2, y3);
k20 = h*f0(t+h/2, y0 + k10/2, y1 + k11/2, y2 + k12/2, y3 + k13/2);
k21 = h*f1(t+h/2, y0 + k10/2, y1 + k11/2, y2 + k12/2, y3 + k13/2);
k22 = h*f2(t+h/2, y0 + k10/2, y1 + k11/2, y2 + k12/2, y3 + k13/2);
k23 = h*f3(t+h/2, y0 + k10/2, y1 + k11/2, y2 + k12/2, y3 + k13/2);
k30 = h*f0(t+h/2, y0 + k20/2, y1 + k21/2, y2 + k22/2, y3 + k23/2);
k31 = h*f1(t+h/2, y0 + k20/2, y1 + k21/2, y2 + k22/2, y3 + k23/2);
k32 = h*f2(t+h/2, y0 + k20/2, y1 + k21/2, y2 + k22/2, y3 + k23/2);
k33 = h*f3(t+h/2, y0 + k20/2, y1 + k21/2, y2 + k22/2, y3 + k23/2);
k40 = h*f0(t + h, y0 + k30, y1 + k31, y2 + k32, y3 + k33);
k41 = h*f1(t + h, y0 + k30, y1 + k31, y2 + k32, y3 + k33);
k42 = h*f2(t + h, y0 + k30, y1 + k31, y2 + k32, y3 + k33);
k43 = h*f3(t + h, y0 + k30, y1 + k31, y2 + k32, y3 + k33);
y0 = y0 + (1.0/6.0)*(k10 + 2*k20 + 2*k30 + k40);
y1 = y1 + (1.0/6.0)*(k11 + 2*k21 + 2*k31 + k41);
y2 = y2 + (1.0/6.0)*(k12 + 2*k22 + 2*k32 + k42);
y3 = y3 + (1.0/6.0)*(k13 + 2*k23 + 2*k33 + k43);
}
The problem here is that when I run the code with the initial conditions given, the values do not match with what it is supposed to according to the case given by the problem
what the graphic should look like with the initial conditions given
now, I think i got right the implementation of the method but i do not know why the graphs do not match because when i run the code the particle goes away from the potential.
Any help will be appreciated.
The paths look chaotic with sharp turns. This requires an adaptive step size, you will need to implement some step size control. Either by comparing each step with two steps of half the step length or by using a method with embedded methods of higher order like Fehlberg or Dormand-Price.
More immediate errors:
define Em as V(1,1) to avoid unnecessary magic numbers
your initial position is, if you read the chart right,
y0 = 3.0;
y1 = -0.3905+k*0.0010;
with k=-1,0,1, note the minus sign.
your initial velocity is horizontal, and the kinetic energy is computed to complement the potential energy at that position. Thus
y2 = v0 = -(std::sqrt((2.0*(E-V(y0, y1)))/M));
y3 = v1 = 0.0;
With these changes and an adaptive solver I get (in python) the plot
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# capture the structure of the potential
f = lambda r : r*np.exp(-r);
df = lambda r : (1-r)*np.exp(-r);
V = lambda y1,y2 : f(y1*y1)*f(y2*y2);
M= 1.0
Em = V(1.0,1.0);
E = 0.260*Em;
def prime(t,y):
x1,x2,v1,v2 = y
dV_dx1 = 2*x1*df(x1*x1)*f(x2*x2);
dV_dx2 = 2*x2*df(x2*x2)*f(x1*x1);
return [ v1, v2, -dV_dx1/M, -dV_dx2/M ];
# prepare and draw the contour plot
X1,X0=np.ogrid[-4:3:100j,-4:3:100j]
plt.contour(X0.ravel(), X1.ravel(), V(X0,X1), Em*np.arange(0,1,0.1), colors='k', linewidths=0.3)
# display grid and fix the coordinate ranges
plt.grid();plt.autoscale(False)
for k in range(-1,1+1):
x01 = 3.0;
x02 = b = -0.3905 + 0.0010*k;
v01 = -( ( E-V(x01,x02) )*2.0/M )**0.5;
v02 = 0.0;
print "initial position (%.4f, %.4f), initial velocity (%.4f, %.4f)" % ( x01, x02, v01, v02 )
t0 = 0.0
tf = 50.0
tol = 1e-10
y0 = [ x01, x02, v01, v02 ]
t = np.linspace(t0,tf,501); y = odeint(lambda y,t: prime(t,y) , y0, t)
plt.plot(y[:,0], y[:,1], label="b=%.4f" % b, linewidth=2)
plt.legend(loc='best')
plt.show()

Converting a 3x3 matrix to Euler/Tait Bryan angles (pitch yaw roll)

I have the Razer Hydra SDK here, and I want to transform the rotation matrix I get from the hardware, into pitch, yaw and roll.
The documentation states:
rot_mat - A 3x3 matrix describing the rotation of the controller.
My code is currently:
roll = atan2(rot_mat[2][0], rot_mat[2][1]);
pitch = acos(rot_mat[2][2]);
yaw = -atan2(rot_mat[0][2], rot_mat[1][2]);
Yet this seems to give me wrong results.
Would somebody know how I can easily translate this, and what I am doing wrong?
You can calculate pitch, roll and yaw like this.
Based on that:
#include <array>
#include <limits>
typedef std::array<float, 3> float3;
typedef std::array<float3, 3> float3x3;
const float PI = 3.14159265358979323846264f;
bool closeEnough(const float& a, const float& b, const float& epsilon = std::numeric_limits<float>::epsilon()) {
return (epsilon > std::abs(a - b));
}
float3 eulerAngles(const float3x3& R) {
//check for gimbal lock
if (closeEnough(R[0][2], -1.0f)) {
float x = 0; //gimbal lock, value of x doesn't matter
float y = PI / 2;
float z = x + atan2(R[1][0], R[2][0]);
return { x, y, z };
} else if (closeEnough(R[0][2], 1.0f)) {
float x = 0;
float y = -PI / 2;
float z = -x + atan2(-R[1][0], -R[2][0]);
return { x, y, z };
} else { //two solutions exist
float x1 = -asin(R[0][2]);
float x2 = PI - x1;
float y1 = atan2(R[1][2] / cos(x1), R[2][2] / cos(x1));
float y2 = atan2(R[1][2] / cos(x2), R[2][2] / cos(x2));
float z1 = atan2(R[0][1] / cos(x1), R[0][0] / cos(x1));
float z2 = atan2(R[0][1] / cos(x2), R[0][0] / cos(x2));
//choose one solution to return
//for example the "shortest" rotation
if ((std::abs(x1) + std::abs(y1) + std::abs(z1)) <= (std::abs(x2) + std::abs(y2) + std::abs(z2))) {
return { x1, y1, z1 };
} else {
return { x2, y2, z2 };
}
}
}
If you still get wrong angles with this, you may be using a row-major matrix as opposed to column-major, or vice versa - in that case you'll need to flip all R[i][j] instances to R[j][i].
Depending on the coordinate system used (left handed, right handed) x,y,z may not correspond to the same axes, but once you start getting the right numbers, figuring out which axis is which should be easy :)
Alternatively, to convert from a Quaternion to euler angles like shown here:
float3 eulerAngles(float q0, float q1, float q2, float q3)
{
return
{
atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2)),
asin( 2 * (q0*q2 - q3*q1)),
atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3))
};
}
This is the an formula that will do, keep in mind that the higher the precision the more variables in the rotation matrix are important:
roll = atan2(rot_mat[2][1], rot_mat[2][2]);
pitch = asin(rot_mat[2][0]);
yaw = -atan2(rot_mat[1][0], rot_mat[0][0]);
http://nghiaho.com/?page_id=846
This is also used in the point cloud library, function : pcl::getEulerAngles

How to place a point in 3D so that it creates a certain angle?

I have a small question about 3D.
What follows is an example of my problem.
I have two points:
A: 12 4 5
B: 6 8 -10
I have another point:
C: 5 6 7
I need to transform(?) point C so that the angle ABC is 48 degrees.
How do I do this? I would appreciate if someone can help me with the formulas or maybe even make the above example into a working one.
Another way to put it: How do I transform C.x, C.y, and C.z so that the angle ABC is 48 degrees?
I would really appreciate some help on this as I am stuck at the moment.
Side note:
I already implemented a method for finding the angle:
float Angle( float x1, float y1, float z1,
float x2, float y2, float z2 )
{
float x, y, z;
CrossProduct( x1, y1, z1, x2, y2, z2, &x, &y, &z );
float result = atan2 ( L2Norm( x, y, z ),
DotProduct( x1, y1, z1, x2, y2, z2 ) );
return result;
}
You use it:
Angle( B.x - A.x, B.y - A.y, B.z - A.z,
C.x - B.x, C.y - B.y, C.z - B.z );
A------C
|
c'' | c'
B
As three point in 3D define a plane, there are only 2 possible candidates for a transform C-->c' or C-->c'' at that plane.
c' would be then c' = A+t*(B-A) + u*(C-A) with constraint Normalize(c'-A) dot Normalize(B-A) == cos (48 / 180 * pi).
I'd first suggest normalizing D=(B-A), after that:
D dot D+u*(C-A) = 1 * |D+u(C-A)| * cos (48 degrees)
Dx*(Dx+u*(Cx-Ax))+ Dy*(Dy+u*(Cy-Ay))+Dz*(Dz+u*(Cz-Az)) ==
0.669 * sqrt ((Dx+u*(Cx-Ax))^2+(Dy+u*(Cy-Ay))^2+(Dz+u*(Cz-Az))^2)
This is of form a+u*b == 0.669*sqrt(c+du+e*u^2), which will be simplified to a second degree polynomial in u by squaring both sides.
The track of point C is actually a cone, you can imagine, B is the vertex and line AB is the central line of the cone, means the 3D cone is symmetric on AB.

Given a start and end point, and a distance, calculate a point along a line

Looking for the quickest way to calculate a point that lies on a line
a given distance away from the end point of the line:
void calculate_line_point(int x1, int y1, int x2, int y2, int distance, int *px, int *py)
{
//calculate a point on the line x1-y1 to x2-y2 that is distance from x2-y2
*px = ???
*py = ???
}
Thanks for the responses, no this is not homework, just some hacking out of
my normal area of expertise.
This is the function suggested below. It's not close to working. If I
calculate points every 5 degrees on the upper right 90 degree portion of
a circle as starting points and call the function below with the center of the circle as x2,y2 with a distance of 4 the end points are totally wrong. They lie below and to the right of the center and the length is as long as the center point. Anyone have any suggestions?
void calculate_line_point(int x1, int y1, int x2, int y2, int distance)
{
//calculate a point on the line x1-y1 to x2-y2 that is distance from x2-y2
double vx = x2 - x1; // x vector
double vy = y2 - y1; // y vector
double mag = sqrt(vx*vx + vy*vy); // length
vx /= mag;
vy /= mag;
// calculate the new vector, which is x2y2 + vxvy * (mag + distance).
px = (int) ( (double) x2 + vx * (mag + (double)distance) );
py = (int) ( (double) y2 + vy * (mag + (double)distance) );
}
I've found this solution on stackoverflow but don't understand it completely, can anyone clarify?
I think this belongs on MathOverflow, but I'll answer since this is your first post.
First you calculate the vector from x1y1 to x2y2:
float vx = x2 - x1;
float vy = y2 - y1;
Then calculate the length:
float mag = sqrt(vx*vx + vy*vy);
Normalize the vector to unit length:
vx /= mag;
vy /= mag;
Finally calculate the new vector, which is x2y2 + vxvy * (mag + distance).
*px = (int)((float)x1 + vx * (mag + distance));
*py = (int)((float)y1 + vy * (mag + distance));
You can omit some of the calculations multiplying with distance / mag instead.
These equations are wrong:
px = (int) ( (double) x2 + vx * (mag + (double)distance) );
py = (int) ( (double) y2 + vy * (mag + (double)distance) );
The correct equations are:
px = (int) ( (double) x2 + vx * (double)distance );
py = (int) ( (double) y2 + vy * (double)distance );
Tom