-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update/sensor noise qual #52
Update/sensor noise qual #52
Conversation
636ddf5
to
f433458
Compare
One more comment: could you move the noise qualification code into a directory inside |
rct_optimizations/include/rct_optimizations/validation/noise_qualifier.h
Outdated
Show resolved
Hide resolved
8804cf3
to
3162925
Compare
Eigen::Quaterniond q; | ||
Eigen::Matrix3d m = result.camera_to_target.rotation(); | ||
q = m; | ||
|
||
Eigen::Vector4d v(q.x(), q.y(), q.z(),q.w()); | ||
|
||
orientations.push_back(v); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could reduce with orientations.push_back(Eigen::Quaterniond(result.camera_to_target.rotation()).coeffs())
//Mean quaternion is found using method described by Markley et al: Quaternion Averaging | ||
//All quaternions are equally weighted | ||
|
||
//Accumulator Matrix | ||
Eigen::Matrix4d A = Eigen::Matrix4d::Zero(); | ||
|
||
for (std::size_t i = 0; i <= count; ++i) | ||
{ | ||
//rank 1 update of 'A', the accumulator matrix, with the u,v = q,qT | ||
Eigen::Matrix4d temp = (orientations[i] * orientations[i].transpose()); | ||
A += temp; | ||
} | ||
|
||
//scale A | ||
A = (1.0/double(count)) * A; | ||
|
||
//Eigenvectors,values should be strictly real | ||
Eigen::EigenSolver<Eigen::Matrix4d> E(A, true); | ||
|
||
//Each column of 4x4 vectors is an eigenvector; desired mean has max eigenvalue | ||
Eigen::Index max_evi, one; | ||
|
||
//find maximium eigenvalue, and store its index in max_evi | ||
E.eigenvalues().real().maxCoeff(&max_evi, &one); | ||
Eigen::Vector4d mean = E.eigenvectors().real().col(max_evi); | ||
|
||
output.i.mean = mean[0]; | ||
output.j.mean = mean[1]; | ||
output.k.mean = mean[2]; | ||
output.w.mean = mean[3]; | ||
|
||
//Manually calculate standard deviations from mean | ||
Eigen::Array4d std_dev = Eigen::Array4d::Zero(); | ||
Eigen::Array4d sum = Eigen::Array4d::Zero(); | ||
for (std::size_t i =0; i< count; ++i) | ||
{ | ||
//absolute value taken because a quaternion is equal to its opposite | ||
Eigen::Array4d term = orientations[i].cwiseAbs() - mean.cwiseAbs(); | ||
term = term.square(); | ||
sum+= term; | ||
} | ||
|
||
std_dev = sum/double(count); | ||
std_dev = std_dev.sqrt(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably makes sense to move this to a standalone function, since other features might use this (and so you don't have to copy it for the 3D case below). It would be nice to verify that this method also works as expected in a unit test since it just came from a paper we found online
output.y.std_dev = sqrt(boost::accumulators::variance(y_acc)); | ||
output.z.std_dev = sqrt(boost::accumulators::variance(z_acc)); | ||
|
||
//Mean quaternion is found using method described by Markley et al: Quaternion Averaging |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the website for future reference (or the PDF to the docs folder)
NoiseStatistics i; | ||
NoiseStatistics j; | ||
NoiseStatistics k; | ||
NoiseStatistics w; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please name these qx, qy, qz, and qw to conform to normal conventions
/** | ||
* @brief qualifyNoise2D This function qualifies 2d sensor noise by | ||
* comparing PnP results from images taken with the same poses. | ||
* Sensor noise can be understood by inspecting the returned standard | ||
* deviations. Position and orientation is returned, but orientation | ||
* is expressed in Euler angles and may not be consistent for | ||
* similar positions. | ||
* @param Sets of PnP 2D problem parameters | ||
* @return Noise Statistics: a vector of means & std devs | ||
*/ | ||
PnPNoiseStat qualifyNoise2D(const std::vector<PnPProblem>& params); | ||
|
||
/** | ||
* @brief qualifyNoise3D This function qualifies 3d sensor noise by | ||
* comparing PnP results from scans taken with the same poses. | ||
* Sensor noise can be understood by inspecting the returned standard | ||
* deviations. Position and orientation is returned, but orientation | ||
* is expressed in Euler angles and may not be consistent for | ||
* similar positions. | ||
* @param params 3D image parameters | ||
* @return Noise Statiscics: a vector of standard deviations and the mean pos | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update documentation references to Euler angles
Eigen::Quaterniond q_ver; | ||
Eigen::Matrix3d m = camera_loc.rotation(); | ||
q_ver = m; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can construct the quaternion inline
EXPECT_TRUE(output.x.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.y.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.z.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.i.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.j.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.k.std_dev < 1.0e-14); | ||
EXPECT_TRUE(output.w.std_dev < 1.0e-14); | ||
|
||
//Absolute value of quaternion is taken, since quaternions equal their oppoisite | ||
EXPECT_TRUE(abs(output.x.mean - camera_loc.translation()(0)) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.y.mean - camera_loc.translation()(1)) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.z.mean - camera_loc.translation()(2)) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.i.mean) - abs(q_ver.x()) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.j.mean) - abs(q_ver.y()) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.k.mean) - abs(q_ver.z()) < 1.0e-14); | ||
EXPECT_TRUE(abs(output.w.mean) - abs(q_ver.w()) < 1.0e-14); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might consider using EXPECT_LT
instead. If/when this fails, EXPECT_TRUE
will only tell you the condition was not right, but EXPECT_LT
will print the actual and expected values to the terminal which is helpful for debug
|
||
//add noise boilerplate | ||
const double mean = 0.0; | ||
const double stddev = 0.001; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This standard deviation is in pixels (as you are applying it to the image u, v coordinates), and seems really small. You might make it something more reasonable like 1.0 pixel
EXPECT_TRUE(output.x.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.y.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.z.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.i.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.j.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.k.std_dev < 1.5 * stddev); | ||
EXPECT_TRUE(output.w.std_dev < 1.5 * stddev); | ||
|
||
//Absolute value of quaternion is taken, since quaternions equal their oppoisite | ||
EXPECT_TRUE(abs(output.x.mean - camera_loc.translation()(0)) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.y.mean - camera_loc.translation()(1)) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.z.mean - camera_loc.translation()(2)) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.i.mean) - abs(q_ver.x()) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.j.mean) - abs(q_ver.y()) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.k.mean) - abs(q_ver.z()) < 1.5 * stddev); | ||
EXPECT_TRUE(abs(output.w.mean) - abs(q_ver.w()) < 1.5 * stddev); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can compare position a little bit more accurately. Using the camera projection matrix we can come up with the differentials of (x, y, z) with respect to our perturbed input (u, v)
- delta_x < (z / f_x) * delta_u
dx < (camera_loc.translation.z() / camera.fx()) * stddev
- delta_y < (z / f_y) * delta_u
dy < (camera_loc.translation.z() / camera.fy()) * stddev
- delta_z < (f_x * x * (u - c_x)^-2) * delta_u
dz < (camera.fx() * camera_loc.translation.x() * std::pow((u - camera.cx()), -2.0)) * stddev
u
in this case is obtained by projecting the actual point into the camera
As far as orientation, I don't think we can directly compare it to the pixel noise level (i.e. stddev
), so I would suggest setting a somewhat arbitrary value as the threshold and comparing distance between the mean and expected quaternion with:
EXPECT_LT(Eigen::Quaterniond(output.w.mean, output.i.mean, output.j.mean, output.k.mean).angularDistance(q_ver), angular_threshold);
rct_optimizations/test/pnp_utest.cpp
Outdated
PnPResult res = rct_optimizations::optimize(setup); | ||
|
||
EXPECT_TRUE(res.converged); | ||
//costs are oddly high for 3d, perfect guess |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be an indication that your cost function is not correct. If you put in a perfect initial guess and there is no simulated noise in your correspondences, your initial cost per observation should be close to the machine epsilon. You should be seeing error on the order of 1.0e-15.
Another way to validate is to make a test where the starting condition is not perfect and see if it converges with low residual error
…ented, and some improved methods
…ther documentation
…NaN returns or 60 deg oritentation shits
3162925
to
39f8d42
Compare
Sensor Noise Qualification Updates
Adds a tool for qualifying sensor noise, as well as an implementation of 3d pnp.