-
Notifications
You must be signed in to change notification settings - Fork 42
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
Basic unit tests for SNS position and velocity IK #75
Basic unit tests for SNS position and velocity IK #75
Conversation
This commit addes unit tests for: - SNS position IK (all versions) - SNS velocity IK (all versions) - KDL position IK (pinv NR only) It also addes a pair of functions that directly create a KDL chain model for Sawyer, which allows the unit tests to run without depending on external repositories or URDF files. sns_ik_data_utilities --> rng_utilities This commit also turns on compiler warnings and treats them as errors. Issues: RethinkRobotics-opensource#74
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.
Two minor things to fix that I missed on my first pass.
Also need to create issues:
- bug: in Ubuntu 16.04 with Eigen 3.3.4 SNS-IK crashes the unit test with a segfault.
- bug: Optimal SNS velocity IK solver fails unit test
- feature: add more details to SNS-IK unit tests, as well as additional comparisons to KDL.
sns_ik_lib/src/robot_model.cpp
Outdated
*qUpp = KDL::JntArray(nJnt); | ||
*vMax = KDL::JntArray(nJnt); | ||
*aMax = KDL::JntArray(nJnt); | ||
(*qLow)(0) = -3.050300; |
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.
remove extra trailing zeros here
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.
and on following lines.
sns_ik_lib/test/sns_ik_vel_test.cpp
Outdated
#include <kdl/chain.hpp> | ||
#include <kdl/chainiksolvervel_pinv.hpp> | ||
#include <kdl/chainfksolvervel_recursive.hpp> | ||
#include <kdl/chainiksolvervel_pinv_nso.hpp> |
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 header is not needed
seed++; | ||
int nRows = sns_ik::rng_util::getRngInt(seed + 20817, 1, 9); | ||
int nCols = sns_ik::rng_util::getRngInt(seed + 66461, 1, 9); | ||
int nRank = sns_ik::rng_util::getRngInt(seed + 77126, 1, 9); |
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.
nRank is unused.
// Check the rank: | ||
nRank = std::min({nRows, nCols, nRank}); | ||
Eigen::ColPivHouseholderQR<Eigen::MatrixXd> decomp(X); | ||
ASSERT_LE(decomp.rank(), nRank); |
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.
ASSERT_EQ rather than ASSERT_LE
Eigen::MatrixXd A; // test matrix | ||
int rank; // rank of invA, computed by pseudoInverse(A) | ||
bool damped; // true iff invA was computed with a damped pseudo-inverse | ||
for (int iTest = 0; iTest < 20; iTest++) { |
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.
replace 20 with nTest
double tolMat = 1e-6; // tolerance for matrix equality check | ||
int nTest = 15; | ||
int seed = 76407; | ||
double low = -2.0; double upp = 2.0; // bounds on values in the A and P matrix |
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 line is unused and can be deleted.
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 reviewed about 70% of this. After some feedback is incorporated, I'll take another pass.
sns_ik_lib/CMakeLists.txt
Outdated
@@ -6,9 +6,9 @@ include(CheckCXXCompilerFlag) | |||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) | |||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) | |||
if(COMPILER_SUPPORTS_CXX11) | |||
set(CMAKE_CXX_FLAGS "-std=c++11 -O2") | |||
set(CMAKE_CXX_FLAGS "-std=c++11 -O2 -Wall -Werror") |
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.
Hmm. I'm not sure this is standard practice, to kill the build on every warning. While I don't mind it for our own builds, we need to double check that this is acceptable for the ROS Buildfarm.
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.
Hmmm... Good point. How can I look up the best practices for this? I did a quick google search and made little progress.
Perhaps I should change it to -Wall -Wextra
but let builds pass with warnings?
My goal here was to prevent pull requests that added questionable code to the project, assuming that everyone would build their project before making a pull-request.
I'll leave it for now, but I'll defer to you on your next pass through the review.
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 removed the error on warning flag, but kept -Wall
.
* This function returns a KDL chain object that represents the kinematics of a Sawyer Robot arm. | ||
* Full model description at: https://github.com/RethinkRobotics/sawyer_robot | ||
* @param[out] jointNames: names of all non-trivial joints | ||
* @return: kinematic chain betweem right_arm_mount and right_hand for Sawyer. |
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.
*between
@@ -0,0 +1,56 @@ | |||
/** @file robot_model.hpp |
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 would prefer a different name than robot_model
for this file. There are other robot model header files in the ROS world, and I'd like to avoid any confusion. Additionally, this tester file should not be part of our public API. Either place it inside the src/ directory, or create a separate test directory for all test headers & sources.
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.
What is the best way to enforce a public and private API for a ROS package?
The code for both robot_model
and rng_utilities
are both used only by the unit test and should be private. I could not figure out how to get the unit test to correctly link to them without either A) putting them alongside the other files or B) putting the implementation in a header file and #including it, which seemed like a bad idea.
This is part of a bigger discussion #69: what should be public and private, and how to enforce? Perhaps we could talk off-line to decide the best plan going forward.
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.
After talking to you I have a much better understanding of how the public vs private API in the sense of the installer (rather than C++ class keywords).
Updates:
robot_model
is nowsawyer_model
- testing code is moved to a private library
- utility code is moved to a private library
sns_ik_lib/CMakeLists.txt
Outdated
@@ -39,7 +39,8 @@ add_library(sns_ik | |||
src/fsns_velocity_ik.cpp | |||
src/osns_sm_velocity_ik.cpp | |||
src/osns_velocity_ik.cpp | |||
src/sns_ik_data_utilities.cpp | |||
src/rng_utilities.cpp | |||
src/robot_model.cpp |
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.
robot_model.cpp
shouldn't be built into the SNS library, just used for testing.
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.
Same issue as above - I think that I'm missing something fundamental about how ROS package API works. Let's talk off-line so that I first understand the situation better.
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.
fixed in the new commit.
sns_ik_lib/CMakeLists.txt
Outdated
target_link_libraries(sns_ik_data_utilities_test ${catkin_LIBRARIES} sns_ik) | ||
catkin_add_gtest(sns_ik_pos_test test/sns_ik_pos_test.cpp) | ||
catkin_add_gtest(sns_ik_vel_test test/sns_ik_vel_test.cpp) | ||
target_link_libraries(rng_utilities_test ${catkin_LIBRARIES} sns_ik) |
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's good practice to link against sns_ik
first, catkin_LIBRARIES
second. That will prevent the unfortunate circumstance of hiding your source library behind the debian installed version of your library.
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.
Good to know! I'll fix it.
@@ -83,7 +85,7 @@ bool pinv_damped_P(const Eigen::MatrixXd &A, Eigen::MatrixXd *invA, Eigen::Matri | |||
sigma(i) = 1.0 / sigma(i); | |||
} | |||
(*invA) = svd_A.matrixU() * sigma.asDiagonal() * svd_A.matrixV().transpose(); | |||
(*P) = ((*P) - svd_A.matrixU() * svd_A.matrixU().transpose()).eval(); | |||
if (P){ *P = ((*P) - svd_A.matrixU() * svd_A.matrixU().transpose()).eval(); } | |||
return true; |
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.
If P is null, we still return true? Is that intentional?
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 is intentional - I did this so that the unit test can call the old version of the damped pseudo inverse to compare the results to the new implementation.
The return value is not an error flag, it is whether the matrix was full-rank. This is a confusing API and I changed it for the pseudoInverse() function that I added.
Passing *P = nullptr
is used when the caller wants to compute the pseudo inverse without also computing the P matrix. An alternative would be to default to setting P to the identity matrix, but this incurs an extra computational cost.
Ultimately we should remove all of these utility functions and replace them with new ones that are better thought-out and implemented.
int rankA = 0; | ||
int nSigma = sigma.size(); | ||
for (int i = 0; i < nSigma; i++) { | ||
if (sigma(i) > eps) rankA++; // increment the rank counter |
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.
Do we care if eps
is negative and therefore sigma(i) == 0
?
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.
Good point - we should do input validation on eps
to ensure that it is positive. It will also break the code if it is zero.
@@ -0,0 +1,198 @@ | |||
/*! \file rng_utilities_test.cpp | |||
* \brief Unit Test: sns_ik_math_utils |
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.
should the name match the file?
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.
yep
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.
Fixed in a few other places as well.
sns_ik_lib/test/sns_ik_vel_test.cpp
Outdated
@@ -0,0 +1,290 @@ | |||
/*! \file sns_ik_test.cpp |
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.
Looks like these lines need to be updated
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.
fixed.
* P.rows() == P.cols() = A.cols() is required | ||
* P should be initialized with the identity matrix | ||
* @param[opt] lambda_max: damping parameter for the damped pseudoinverse | ||
* @param[opt] eps: singular values smaller than this will be set to zero | ||
* @return: true if A is full rank, false if A is rank deficient | ||
*/ | ||
bool pinv_damped_P(const Eigen::MatrixXd &A, Eigen::MatrixXd *invA, Eigen::MatrixXd *P, | ||
bool pinv_damped_P(const Eigen::MatrixXd &A, Eigen::MatrixXd *invA, Eigen::MatrixXd *P = nullptr, |
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.
Note for the future: we should be consistent about how we use references and pointers in function signatures. Also, when this is going to 18.04, let's move towards using std::unique_ptr
's instead of raw pointers.
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.
We definitely need to clean up the way that the library handles pointers and references (#43). There are really two different issues:
- how to handle ownership of objects, especially those with non-trivial constructors
- how to handle output arguments
360d0e1
to
6c0e4dd
Compare
#include <sns_ik/rng_utilities.hpp> | ||
#include <sns_ik/sns_ik_math_utils.hpp> | ||
#include "rng_utilities.hpp" | ||
#include "../src/sns_ik_math_utils.hpp" |
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.
bah to relative paths, but I can't think of an elegant way around it off the top of my head.
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 its possibly an indicator that we should consider adding a subset of math utilities to the public API?
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 that this is fixed now. I moved the utilities to their own private library, which both the test and main libraries link against. Let me know if I managed the CMakeLists correctly for it.
Changes: - robot_model renamed sawyer_model - testing code moved to private library - utility code moved to private library
sns_ik_lib/CMakeLists.txt
Outdated
# library for private utilities | ||
add_library(sns_ik_util | ||
utilities/sns_ik_math_utils.cpp) | ||
target_link_libraries(sns_ik_util) |
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.
Hmmmm. This linking is missing things to be linked to. You can likely remove line 39.
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.
That is unless you actually should be linking against things like ${catkin_LIBRARIES}
...
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.
Looks like everything works fine after deleting line 39.
|
||
# install the public API |
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.
+1 for the clarification
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.
Looks good! I can merge once the CMakeLists line is updated.
This commit addes unit tests for:
It also addes a pair of functions that directly create a KDL chain model
for Sawyer, which allows the unit tests to run without depending on external
repositories or URDF files.
This commit includes code for a cleaned-up version of the pseudo-inverse function, along with a unit test for both the full-rank and rank-deficient cases.
sns_ik_data_utilities --> rng_utilities
This commit also turns on compiler warnings and treats them as errors.
Issues: #74