Consider the following minimal working example:
#include <iostream>
#include <math.h>
#include <eigen3/Eigen/Dense>
int main() {
// Set the rotation matrices that give an example of the problem
Eigen::Matrix3d rotation_matrix_1, rotation_matrix_2;
rotation_matrix_1 << 0.15240781108708346, -0.98618841818279246, -0.064840288106743013,
-0.98826031445019891, -0.1527775600229907, 0.00075368177315370682,
-0.0106494132438156, 0.063964216524108775, -0.99789536976680049;
rotation_matrix_2 << -0.12448670851248633, -0.98805453458380521, -0.090836645094957508,
-0.99167686914182451, 0.12086367053038971, 0.044372968742129482,
-0.03286406263376359, 0.095604444636749664, -0.99487674792051639;
// Convert to Euler angles
Eigen::Vector3d euler_angles_1 = rotation_matrix_1.eulerAngles(2, 1, 0)*180.0f/M_PI;
Eigen::Vector3d euler_angles_2 = rotation_matrix_2.eulerAngles(2, 1, 0)*180.0f/M_PI;
// Convert to quaternion
Eigen::Quaternion<double> quaternion_1(rotation_matrix_1);
Eigen::Quaternion<double> quaternion_2(rotation_matrix_2);
// Print out results
std::cout << "Euler angles 1:\nyaw = " << euler_angles_1[0] << "\npitch = " << euler_angles_1[1] << "\nroll = " << euler_angles_1[2] << std::endl;
std::cout << "Quaternion 1:\nw = " << quaternion_1.w() << "\nx = " << quaternion_1.x() << "\ny = " << quaternion_1.y() << "\nz = " << quaternion_1.z() << std::endl;
std::cout << std::endl;
std::cout << "Euler angles 2:\nyaw = " << euler_angles_2[0] << "\npitch = " << euler_angles_2[1] << "\nroll = " << euler_angles_2[2] << std::endl;
std::cout << "Quaternion 2:\nw = " << quaternion_2.w() << "\nx = " << quaternion_2.x() << "\ny = " << quaternion_2.y() << "\nz = " << quaternion_2.z() << std::endl;
}
Whose output is:
Euler angles 1:
yaw = 98.767
pitch = 179.39
roll = -3.66759
Quaternion 1:
w = 0.020826
x = 0.758795
y = -0.650521
z = -0.0248716
Euler angles 2:
yaw = 82.845
pitch = 178.117
roll = -5.48908
Quaternion 2:
w = -0.0193663
x = -0.661348
y = 0.748369
z = 0.0467608
Both rotations are nearly identical (as given by the Euler angles). The expected behavior is that quaternion_2 will have values with same sign as quaternion_1, i.e. for the output to be:
Quaternion 2:
w = 0.0193663
x = 0.661348
y = -0.748369
z = -0.0467608
However, Eigen appears to "flip" the quaternion. I am aware that q and -q represent the same rotation - however, it is visually not appealing, and frankly annoying, that the quaternion would flip sign in each of its values. How can this be rectified for the general case (i.e. that the quaternion always preserves its "handedness", rather than flipping sign for certain rotations)?
When unit quaternions are used to represent 3d rotations, there are two ways to represent each actual rotation - and you can't avoid the 'negative' ones occuring without creating an artificial discontinuity in the space.
Unlike 2d rotations using complex numbers on a unit circle, the farthest point on the unit hypersphere from '0 rotation' has to be '360 degree rotation', not '180 degree'; since there is a 2d-space of possible 180 rotations which needs to be represented, whereas all 360-degree rotations are equivalent regardless of axis.
You can always 'canonicize' by changing the sign of the whole thing when the w component is negative.
There will still be cases where w = 0, these all represent rotations by 180 - e.g. (0,0,1,0) and (0,0,-1,0) represent the same rotation.
And, (0.01, 0.99995,0,0,0) and (-0.01, 0.99995,0,0) represent rotations very close together, but if you change the second one to the equivalent (0.01,-0.99995,0,0) then they are far apart in the 4d vector space.
So, practically speaking, you can still have a concern when you want to find the difference between two rotations to see how close they are. Canonicizing the two individually may not help;
you would generally want to flip signs as needed to make them as close as possible.
Or, to compare rotations q1,q2 : find the quaternion product q1 * q2.conj(); this gives the difference as a rotation quaternion; if it has w < 0, change its signs. For q1 and q2 close together (regardless of initial sign diffs) the result will always be fairly close to (1,0,0,0).
If you only want to check if they are within a certain angle 'th' of each other, you only need the real part of the result. This is equivalent to finding the dot product of q1,q2 (treating them as unit vectors in 4-space), then you check if the abs. value of the result >= cos(th/2).
Another way to find the relative angle: find the vector difference of the two unit vectors, and find the magnitude 'm' of that difference vector, (square root of the sum of squares) which will be in range [0,2]. Then find
th = 4*arcsin(m/2)
... and this will be 0 ... 2*pi.
In cases where m > sqrt(2), th > pi and you are getting the 'wrong side' result (also, the computation will have terrible numeric accuracy as m gets close to 2.0). So, in such cases, change one of the signs (i.e. make m the vector length of the sum of the inputs, rather than the difference); you will then have m <= sqrt(2), th <= pi.
For small m, the arcsin formula has the taylor series
th ~=~ 2*m + (m^3)/12 + ...
So, for small deltas, the relative rotation angle is approximately twice the magnitude of the vector difference (and this is numerically much more reliable than using an inverse-cosine-of-w when w is nearly 1).
The yaw angle is greater than 90 degrees for matrix 1, and less than 90 degrees for matrix 2. This will cause the cosine of the yaw angle to have different signs for the two, which is flipping your Quaternion.
A possible solution would be to check the w value of the Quaternion. If this is negative, you can flip it.
If you have access to the previous and the current quaternion reading, you can flip the sign of the current quaternion if it makes the distance between the quaternions in the 4D vector space smaller.
Flipping the sign will not affect the rotation, but it will ensure that there are no large jumps in 4D vector space when the rotation difference in rotation space (SO(3)) is small.
Quaternion avoidJumps(Quaternion q_Current, Quaternion q_Prev)
{
if ((q_Prev - q_Current).squaredNorm() < (q_Prev + q_Current).squaredNorm())
return -q_Current;
else
return q_Current;
}
Related
Suppose that I have a 3-dimensional frame with rotation roll = 0, pitch = 0 and yaw = 0 about x, y and z axis respectively.
I want the frame to rotate about x-axis for 3.14159 (Pi) or roll = Pi.
Below is the code for said situation.
The problem is, when I want to convert the rotation matrix back to roll, pitch, and yaw, the code gives different answer.
Instead of roll = Pi, the result is roll = 0, pitch = pi, and yaw = pi.
I think RVC toolbox by Peter Corke on Matlab gives correct answer.
Maybe something is not right with with my program or eulerAngles in Eigen works differently? Please help.
Code:
#include <iostream>
#include <Eigen/Dense>
const double PI = 3.14159265359;
int main()
{
using ::Eigen::AngleAxisd;
using ::Eigen::Matrix3d;
using ::Eigen::Vector3d;
using ::std::cout;
using ::std::endl;
Matrix3d R,Rx;
R = AngleAxisd(PI, Vector3d::UnitX())
* AngleAxisd(0, Vector3d::UnitY())
* AngleAxisd(0, Vector3d::UnitZ());
Rx = AngleAxisd(PI, Vector3d::UnitX());
cout << R << endl << endl;
cout << Rx << endl << endl;
Vector3d ea = R.eulerAngles(0,1,2);
Vector3d eax = Rx.eulerAngles(0,1,2);
cout << ea << endl << endl;
cout << eax << endl << endl;
std::cin.ignore();
return 0;
}
Output (I round off numbers which are too small to zero):
1 0 0
0 -1 0
0 0 -1
1 0 0
0 -1 0
0 0 -1
0
3.14159
3.14159
0
3.14159
3.14159
Euler's angles are not unique. In your XYZ convention, both (0, pi, pi) and (pi,0,0) represents the same rotation, and both are correct. The Eigen::eulerAngles method consistently chooses to minimize first angles.
Please refer to the documentation of Eigen:eulerAngles. Details on various conventions of Euler-angles is well documented in Wikipedia and MathWorld.
Edit:
You will get exact results if you use M_PI, which is internally defined, instead of truncated value of PI.
The Euler-angle representation suffers from singularity. The test case that you are trying to compare is a singular position.
You may want to use quaternions or axis-angle representation if you wish to overcome the singularities.
Different order euler angles(roll1, pitch1, yaw1 or pitch2, yaw2, roll2, ...) can result in the same rotation matrix.
Actually, the Eigen document gave the answer.
Read the function declaration of Eigen document more carefully, and you will get the answer.
Matrix< typename MatrixBase< Derived >::Scalar, 3, 1 > Eigen::MatrixBase< Derived >::eulerAngles ( Index a0,
Index a1,
Index a2
) const
Each of the three parameters a0,a1,a2 represents the respective rotation axis as an integer in {0,1,2}. For instance, in:
Vector3f ea = mat.eulerAngles(2, 0, 2);
"2" represents the z axis and "0" the x axis, etc
Im trying to calculate the angle between two edges in a graph, in order to do that I transfer both edges to origin and then used dot product to calculate the angle. my problem is that for some edges like e1 and e2 the output of angle(e1,e2) is -1.#INDOO.
what is this output? is it an error?
Here is my code:
double angle(Edge e1, Edge e2){
Edge t1 = e1, t2 = e2;
Point tail1 = t1.getTail(), head1 = t1.getHead();
Point u(head1.getX() - tail1.getX(), head1.getY() - tail1.getY());
Point tail2 = t2.getTail(), head2 = t2.getHead();
Point v(head2.getX() - tail2.getX(), head2.getY() - tail2.getY());
double dotProduct = u.getX()*v.getX() + u.getY()*v.getY();
double cosAlpha = dotProduct / (e1.getLength()*e2.getLength());
return acos(cosAlpha);
}
Edge is a class that holds two Points, and Point is a class that holds two double numbers as x and y.
Im using angle(e1,e2) to calculate the orthogonal projection length of a vector like b on to a vector like a :
double orthogonalProjectionLength(Edge b, Edge a){
return (b.getLength()*sin(angle(b, a) * (PI / 180)));
}
and this function also sometimes gives me -1.#INDOO. you can see the implementation of Point and Edge here.
My input is a set S of n Points in 2D space. Iv constructed all edges between p and q (p,q are in S) and then tried to calculate the angle like this:
for (int i = 0; i < E.size(); i++)
for (int j = 0; j < E.size(); j++){
if (i == j)
cerr << fixed << angle(E[i], E[j]) << endl; //E : set of all edges
}
If the problem comes from cos() and sin() functions, how can I fix it? is here other libraries that calculate sin and cos in more efficient way?
look at this example.
the inputs in this example are two distinct points(like p and q), and there are two Edges between them (pq and qp). shouldnt the angle(pq , qp) always be 180 ? and angle(pq,pq) and angle(qp,qp) should be 0. my programm shows two different kinds of behavior, sometimes angle(qp,qp) == angle(pq,pq) ==0 and angle(pq , qp) == angle(pq , qp) == 180.0, and sometimes the answer is -1.#INDOO for all four edges.
Here is a code example.
run it for several times and you will see the error.
You want the projection and you go via all this trig? You just need to dot b with the unit vector in the direction of a. So the final answer is
(Xa.Xb + Ya.Yb) / square_root(Xa^2 + Ya^2)
Did you check that cosAlpha doesn't reach 1.000000000000000000000001? That would explain the results, and provide another reason not to go all around the houses like this.
It seems like dividing by zero. Make sure that your vectors always have 0< length.
Answer moved from mine comment
check if your dot product is in <-1,+1> range ...
due to float rounding it can be for example 1.000002045 which will cause acos to fail.
so add two ifs and clamp to this range.
or use faster way: acos(0.99999*dot)
but that lowers the precision for all angles
and also if 0.9999 constant is too big then the error is still present
A recommended way to compute angles is by means of the atan2 function, taking two arguments. It returns the angle on four quadrants.
You can use it in two ways:
compute the angles of u and v separately and subtract: atan2(Vy, Vx) - atan2(Uy, Ux).
compute the cross- and dot-products: atan2(Ux.Vy - Uy.Vx, Ux.Uy + Vx.Vy).
The only case of failure is (0, 0).
I'm trying to speed up my code for calculating the volume of a sphere (see code below).
This volume of the sphere is produced by calculating small volume segments, dv, and summing them into a volume, vol.
In reality this code is just a sanity check before I apply the calculations to other sphere like objects which will have symmetrical properties, hence I should be able to increase the speed of the code by calculating over a small volume and multiplying the end result.
Replace 360 and 180 in while (phid<=(360.0/adstep)) and while (thetad<=(180.0/adstep)) with 180 and 90 respectively and you quarter the calculations required meaning that you can simply multiply the final vol by 4.0.
This works if I set phi to and leave theta at 180, halving the calculations.
But it doesn't like it when I set theta to 90.
Ouput:
Phi 360, Theta 180
Actual Volume Calculated Volume % Difference
4.18879020478639053 4.18878971565348923 0.00001167718922403
Phi 180, Theta 180
4.18879020478639053 4.18878971565618219 0.00001167712493440
Phi 180, Theta 90
4.18879020478639053 4.18586538829648180 0.06987363946500515
You can see in the above that the first two calculations are near identical (I assume the difference is due to precision errors) while the last one gives significantly different results. Could the nested loops cause issues?
Any help would be appreciated as I haven't found anything in my research (google & stack overflow) to describe the problem I'm having.
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{
double thetar, phir, maxr, vol, dv, vol2, arstep, adstep, rad, rad3, thetad, phid, ntheta, nphi;
cout << fixed << setprecision(17); // Set output precision to defined number of decimal places. Note Double has up to 15 decimal place accuracy
vol=0.0; // Initialise volume and set at zero
adstep=0.1; // Steps to rotate angles in degrees
arstep=(adstep/180.0)*M_PI; // Angle steps in radians
phid=1.0; // Phi in degrees starting at adstep
maxr = 1.0; // Radius of the sphere
// Loop to calculate volume
while (phid<=(360.0/adstep)) // Loop over Phi divided by adstep. This scales the loop to the desired number of calculations.
{
phir=((phid*adstep)/180.0)*M_PI; // Phi in radians
thetad=1.0; // Theta in degrees, reset to initial adstep value
while (thetad<=(180.0/adstep)) // Loop over Theta divided by adstep. Like Phi loop, this scales the loop to the desired number of calculations
{
thetar=((thetad*adstep)/180.0)*M_PI; // Convert theta degrees to radians
dv = ((maxr*maxr*maxr) * sin(thetar) * arstep * arstep) / 3.0; // Volume of current segment
vol += dv; // Summing all the dv value together to generate a global volume
thetad+=1.0; // Increase theta (degrees) by a single step
}
phid+=1.0; // Increase phi (degrees) by a single step
}
vol = vol*1.0; // Volume compensated for any reduction in phi and theta
rad3 = (3.0*vol)/(4.0*M_PI); // volume equivalent radius^3
rad = pow(rad3,(1.0/3.0)); // volume equivalent radius
vol2 = (4.0/3.0)*M_PI*(maxr*maxr*maxr); // Calculated volume of a sphere given initial maxr
// Diagnostic output
cout << vol2 << " " << vol << " " << ((vol2-vol)/vol)*100.0 << endl;
}
Edit: Corrected starting values of phid and thetad to 1.0
Edit 2:
I just wanted to update, for future viewers, that using the Kahan summation algorithm (https://en.wikipedia.org/wiki/Kahan_summation_algorithm) has virtually negated all my precision errors due to the summation of a small number onto a large number. There are other methods out there but this is one of the simplest and does the job I need it to.
For posterity this is the example psuedocode taken from the wikipedia page on the topic:
function KahanSum(input)
var sum = 0.0
var c = 0.0 // A running compensation for lost low-order bits.
for i = 1 to input.length do
var y = input[i] - c // So far, so good: c is zero.
var t = sum + y // Alas, sum is big, y small, so low-order digits of y are lost.
c = (t - sum) - y // (t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
sum = t // Algebraically, c should always be zero. Beware overly-aggressive optimizing compilers!
// Next time around, the lost low part will be added to y in a fresh attempt.
return sum
As far as speed goes, I suspect (without having profiled it) that a lot of time is wasted converting between radians and degrees, and also computing all those sins. AFAICT, thetar loops through the same values during each iteration of the outer loop, so it would likely be more efficient to pre-compute sin(thetar) once before the main loop, and do a simple lookup in your inner loop.
As for numerical stability, as vol gets larger and larger than dv, you will start losing more and more precision as you go along. You would in principle get better results if you could store all the dvs in an array, then sum it using a divide and conquer approach rather than a linear pass. Then again I count (only) 6 480 000 total iteration, so I think a double accumulator (holding 15-17 significant base-10 digits) can actually handle losing 6-7 digits without much trouble.
Most likely, your problem is: exiting the loop 1 iteration before you want it. You should not compare floating-point numbers for equality. A quick way to fix it is adding a small constant, e.g.
while (thetad<(180.0/adstep)+1e-8)
This isn't a very thorough analysis but might give you some insight into the source of your error. In your code you are accumulating the value of 3240000 floating point numbers. As the value of vol increases, the ratio between dv and vol is increasing you are losing more and more precision in the addition.
A standard way to mitigate the loss of precision in accumulating many values to a single value (known as a reduction sum) is to perform the addition in blocks: for example, you could add together every 8 values and store them to an array, then add together every 8 values of that array, etc. until you are left with a single value. This is likely to get you a better result.
Also, it is worth considering that you are taking linear steps over a spherical surface, so you are not uniformly sampling your sphere. This is likely to affect your final result. One way of sampling the sphere uniformly is to take linear steps in the azimuthal angle phi from 0 to 360 degrees and take the acos of the range from -1 to 1 for your polar angle theta. See this link on sphere point-picking for a more detailed explanation.
First of all, I think, there are couple of errors in your function. I think both phid and thetad should be initialized to either 0 or 1.0.
Second, you can gain quite a bit by reducing the number of floating point multiplications.
In the code below, I moved the contents of your main function to volume1 and created a function volume2 that contains slightly optimized code.
#include <iostream>
#include <iomanip>
#include <cmath>
#include <ctime>
using namespace std;
void volume1(int numSteps)
{
double thetar, phir, maxr, vol, dv, vol2, arstep, adstep, rad, rad3, thetad, phid, ntheta, nphi;
cout << fixed << setprecision(17); // Set output precision to defined number of decimal places. Note Double has up to 15 decimal place accuracy
vol=0.0; // Initialise volume and set at zero
adstep=360.0/numSteps; // Steps to rotate angles in degrees
arstep=(adstep/180.0)*M_PI; // Angle steps in radians
phid=1.0; // Phi in degrees starting at adstep
maxr = 1.0; // Radius of the sphere
// Loop to calculate volume
while (phid<=(360.0/adstep)) // Loop over Phi divided by adstep. This scales the loop to the desired number of calculations.
{
phir=((phid*adstep)/180.0)*M_PI; // Phi in radians
thetad=1.0; // Theta in degrees, reset to initial adstep value
while (thetad<=(180.0/adstep)) // Loop over Theta divided by adstep. Like Phi loop, this scales the loop to the desired number of calculations
{
thetar=((thetad*adstep)/180.0)*M_PI; // Convert theta degrees to radians
dv = ((maxr*maxr*maxr) * sin(thetar) * arstep * arstep) / 3.0; // Volume of current segment
vol += dv; // Summing all the dv value together to generate a global volume
thetad+=1.0; // Increase theta (degrees) by a single step
}
phid+=1.0; // Increase phi (degrees) by a single step
}
vol = vol*1.0; // Volume compensated for any reduction in phi and theta
rad3 = (3.0*vol)/(4.0*M_PI); // volume equivalent radius^3
rad = pow(rad3,(1.0/3.0)); // volume equivalent radius
vol2 = (4.0/3.0)*M_PI*(maxr*maxr*maxr); // Calculated volume of a sphere given initial maxr
// Diagnostic output
cout << vol2 << " " << vol << " " << ((vol2-vol)/vol)*100.0 << endl << endl;
}
void volume2(int numSteps)
{
double thetar, maxr, vol, vol2, arstep, adstep, rad, rad3, thetad, phid, ntheta, nphi;
cout << fixed << setprecision(17); // Set output precision to defined number of decimal places. Note Double has up to 15 decimal place accuracy
vol=0.0; // Initialise volume and set at zero
adstep = 360.0/numSteps;
arstep=(adstep/180.0)*M_PI; // Angle steps in radians
maxr = 1.0; // Radius of the sphere
double maxRCube = maxr*maxr*maxr;
double arStepSquare = arstep*arstep;
double multiplier = maxRCube*arStepSquare/3.0;
// Loop to calculate volume
int step = 1;
for ( ; step <= numSteps; ++step )
{
int numInnerSteps = numSteps/2;
thetad = adstep; // Theta in degrees, reset to initial adstep value
for ( int innerStep = 1; innerStep <= numInnerSteps; ++innerStep )
{
thetar = innerStep*arstep;
vol += multiplier * sin(thetar); // Volume of current segment
}
}
vol = vol*1.0; // Volume compensated for any reduction in phi and theta
rad3 = (3.0*vol)/(4.0*M_PI); // volume equivalent radius^3
rad = pow(rad3,(1.0/3.0)); // volume equivalent radius
vol2 = (4.0/3.0)*M_PI*(maxr*maxr*maxr); // Calculated volume of a sphere given initial maxr
// Diagnostic output
cout << vol2 << " " << vol << " " << ((vol2-vol)/vol)*100.0 << endl << endl;
}
int main()
{
int numSteps = 3600;
clock_t start = clock();
volume1(numSteps);
clock_t end1 = clock();
volume2(numSteps);
clock_t end2 = clock();
std::cout << "CPU time used: " << 1000.0 * (end1-start) / CLOCKS_PER_SEC << " ms\n";
std::cout << "CPU time used: " << 1000.0 * (end2-end1) / CLOCKS_PER_SEC << " ms\n";
}
The output I get, using g++ 4.7.3:
4.18879020478639053 4.18762558892993564 0.02781088785811153
4.18879020478639053 4.18878914146923176 0.00002538483372773
CPU time used: 639.00000000000000000 ms
CPU time used: 359.00000000000000000 ms
That gets you an improvement of about 44%.
I'm working on a program that require me to find the angle between two lines. I have found all of the points coordinates of the lines, referenced into the 0,0 points, but i dont understand how to implement the dot product in Opencv to find the angle.
I use visual C++ using opencv library. I also found out that i can use cv::Mat::dot function to get dot product, but i can't find any good example. I can not understand the explanation in this link.
Can anyone give me a good example? so i can understand how to use that function to find dot product of two lines. Thank you.
If you have two vectors representing your lines, and call them a and b (of type cv::Mat) then the dot product is calculated as
double p = a.dot(b);
Edit
Example code, by comments from author.
float a[2] = {1.0, 2.0};
float b[2] = {3.0, 4.0};
cv::Mat AA(1,2,CV_32FC1,a);
cv::Mat BB(1,2,CV_32FC1,b);
cout << AA << endl;
cout << BB << endl;
cout << AA.dot(BB) << " should be equal to 11" << endl;
Well, the dot product of two vectors A and B is defined as
(length of A) * (length of B) * cos(angle)
where angle represents the angle between the two vectors. So in order to find the angle between the two, first you have to find the dot product, then divide it by both the length of A and the length of B, then take the inverse cosine.
What this looks like in your case is something along these lines, assuming a and b are declared appropriately as cv::Mats:
double dotprod = a.dot(b);
double angle = arccos(dotprod / (a.size().height * b.size().height))
Write a program that determines how far and for how long a time a rock will travel when you throw it off a cliff. Click here to copy the file toss.txt to your desktop (right click the file name and choose Save as). The file contains the height of the cliff in meters.
The program will then:
Open the file toss.txt and read the cliff height into a double-precision variable, then echo print the value of the cliff height to the screen with an appropriate label.
Ask the user for the angle at which the rock is thrown (90 degrees is straight up, and 0 degrees is straight forward), and the velocity at which the rock is thrown (in miles per hour).
Check to make sure the angle is greater than or equal to 0 and less than or equal to 90. If it is not, the program terminates and prints an appropriate error message to the screen.
Check to make sure the velocity is less than or equal to 100 mph and greater than or equal to 0 mph. If it is not, the program terminates and prints an appropriate error message to the screen.
If the angle and velocity are valid, the program completes the calculations as follows:
Converts miles per hour to meters per second.
Converts the angle to radians.
Calculates the time traveled using the following equations:
where
Calculates the distance traveled in the horizontal direction using:
Outputs the time and distance traveled in the horizontal direction to the screen with appropriate labels.
Prints an appropriate message telling the user if the distance traveled in the horizontal direction was greater than, less than, or equal to the height of the cliff.
/* This program */
using namespace std;
#include<iostream>
#include<cmath>
#include<iomanip>
#include<fstream>
int readit ();
int calcit (double, double, double);
int main()
{
readit ();
system ("pause");
return 0;
}
int readit ()
{
double hite, angl, v;
ifstream datain ( "toss.txt" );
datain >> hite;
cout << "The cliff height is " << hite << " meters"<< endl;
cout << "Enter the angle in degrees (from horizontal) the rock is thrown: "
<< endl;
cin >> angl;
if (angl>=0 && angl<=90)
{
cout << endl << "The angle you have entered is "<<angl<< endl <<endl;
}
else
{
cout << "The angle you have entered is not acceptable" << endl;
return 0;
}
cout << "Enter the velocity in mph the rock is thrown: " << endl;
cin >> v;
if (v>=0 && v<=100)
{
cout << endl << "The velocity at which the rock is thrown is "<<v<<
" mph" << endl << endl;
}
else
{
cout << "The velocity you have entered is not acceptable" << endl;
return 0;
}
calcit (hite, angl, v);
}
int calcit (double hite, double angl, double v)
{
double tyme, dist;
v = v * (1609.344/3600);
angl = angl*(M_PI/180);
tyme = -v*sin(angl) + (sqrt((v*sin(angl)*v*sin(angl)) + 2*9.8*hite)/9.8) + (2*(v*sin(angl))/9.8);
dist = (tyme * v) * cos(angl);
cout << tyme << " " << dist <<endl;
}
I am trying to get the correct time the rock is traveling before it hits the ground but i keep getting incorrect answers. I am not sure if i am turning the equation to figure out the time the rock will be in the air until impact into c++ language right. any have any ideas??? i really need to finish this damn project.
Starting from the equation for the y (height above 0) for the rock we have
y = h + v*sin(a)*t - g/2*t^2
which transforms into
g/2 T^2 - v*sin(a)*T - h == 0
when we solve for the final condition y(T)=0.
This yields
T = v*sin(a)/g + sqrt(v*sin(a)*v*sin(a) + 2*g*h)/g
I just can't figure out where the first part -v*sin(angl) in your equation comes from. Everything else looks just fine. So it seems not to be with your code but with the equation you started.
The equation you want is:
s =ut + 1/2 at^2
s = Total distance traveled. (Height of the cliff)
u = Starting velocity (In your case negative as you are throwing
away from the target. And take into account
that not all the starting velocity is away
from the target (eg angle 0 mean u = 0))
a = acceleration (9.81 m/s2)
t = time (The value you want to calculate).
Rearrange the formula to solve for t
To find the solution for t where s = 0...
This formula is you basic quadratic:
y = a.x^2 + b.x + c
Where:
x/y are variables.
a/b/c are constants.
The solution for a quadratic equation where y is 0 is:
x = [ -b ± sqrt(b^2 - 4ac) ] / 2a
Notice the ± symbol. There are actually two solutions to the problem.
You should be able to deduce which one is correct for you as the other
is probably negative.
In your particular case the map is:
x ==> t
y ==> 0
a ==> 1/2.g
b ==> u
c ==> -s
I would suggest a few things to "clean up" the code a bit:
If functions return int ensure that they do really return something. (main doesn't have to but other functions do).
Calculate v * sin(ang1) once then use it in your formula thereafter. Not only more efficient but will make your code clearer.
Like you have given Pi a "constant", do that with other numbers you are using like 9.8 (gravitational force?)
If you have a confusing formula in the code, just introduce more variable names until the meaning becomes obvious. So long as you don't reassign different values to the same variables, this will not make the program confusing.
int calcit (double hite_meters, double angl_deg, double v_mph)
{
double const gravity = 9.8;
double v_ms = v_mph * (1609.344/3600);
double angl_rad = angl_deg * (M_PI/180);
double v_vertical = v_ms * sin( angl_rad );
double time_up = v_vertical / gravity; // [m/s] / [m/s^2] = [s]
double time_down_over_cliff = time_up;
// use quadratic formula t = ( -v - ( v^2 - 4gd )^1/2 ) / 2g:
double time_under_cliff = ( - v_vertical
- sqrt( ( v_vertical * v_vertical )
- ( 4 * - gravity * hite_meters ) ) // negative gravity = down
) / ( 2 * - gravity ); // ( [m/s] + ([m/s]^2 - [m/s^2]*[m])^1/2 ) / [m/s^2]
// = [m/s] / [m/s^2] = [s]
double time_total = time_up + time_down_over_cliff + time_under_cliff;
double v_horizontal = v_ms * cos( angl_rad );
double dist_horizontal = v_ms * time_total;
cout << time_total << " " << dist_horizontal <<endl;
}
Every line of code produces a new, relevant piece of information. When converting to a new unit, I introduce a new variable with a new name. Formulas involving more than one unit get the unit types explained in a comment. This should help turn up unit conversion errors which otherwise I can't help you catch.
Writing this kind of code involves more typing, but the time saved on head-scratching and asking for help more than makes up for it.
The program itself is not any less efficient. More importantly, it may be easily modified, so it won't turn into an inefficient mess after a few revisions.