test_kinematics_as_service.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 /* based on upmc_fkik_test.cpp
36 *
37 * test the forward/inverse kinematics for the Shadow hand with
38 * J1/J2 finger-couplings, as developed by UPMC.
39 
40 * 08.08.2012 - new file
41 *
42 * (C) 2012 fnh
43 * moved to gtest by Guillaume WALCK in 2014
44 * Copyright Guillaume Walck <Guillaume Walck>
45 */
46 
47 
48 #include <map>
49 #include <string>
50 #include <vector>
51 #include <math.h>
52 
53 #include <pluginlib/class_loader.h>
55 
56 
57 // Gtest
58 #include <gtest/gtest.h>
59 #include <sr_utilities/sr_math_utils.hpp>
60 
61 
62 #define DEG2RAD (M_PI / 180.0)
63 #define RAD2DEG (180.0 / M_PI)
64 
65 // the maximum error (in radians) that we tolerate to count IK as a success
66 #define MAX_DELTA 0.01
67 #define IK_NEAR 0.001
68 
70 bool verbose;
71 
72 double rand_range(double min_n, double max_n)
73 {
74  return sr_math_utils::Random::instance().generate<double>(min_n, max_n);
75 }
76 
77 void random_test_finger_fkik(std::string PREFIX, std::string prefix, int n_tests, bool verbose = false)
78 {
79  // create joint-names
80  std::string tipName = prefix + "tip";
81  bool isLittleFinger = (prefix == "lf");
82  bool isThumb = (prefix == "th");
83  std::string hand_prefix = "rh_";
84 
85  std::vector<std::string> jointNames;
86  jointNames.push_back(hand_prefix + PREFIX + "J1");
87  jointNames.push_back(hand_prefix + PREFIX + "J2");
88  jointNames.push_back(hand_prefix + PREFIX + "J3");
89  jointNames.push_back(hand_prefix + PREFIX + "J4");
90  if (isLittleFinger || isThumb)
91  {
92  jointNames.push_back(hand_prefix + PREFIX + "J5");
93  }
94 
95  // we also want a map from joint-name to index, because IK returns
96  // the joints in a different order
97 
98  std::map<std::string, int> jointIndexMap;
99  for (unsigned int j = 0; j < jointNames.size(); j++)
100  {
101  jointIndexMap[jointNames[j]] = j;
102  }
103 
104  // check that the FK/IK services are available for the finger
105  ROS_INFO("waiting for FK/IK service for finger %s", prefix.c_str());
106  ros::service::waitForService(prefix + "_kinematics/get_fk");
107  ros::service::waitForService(prefix + "_kinematics/get_ik");
108 
109  // create the service clients; note that we reuse them throughout
110  // the whole test for a given finger
111  ros::ServiceClient fk = nh->serviceClient<moveit_msgs::GetPositionFK>(prefix + "_kinematics/get_fk", true);
112  ros::ServiceClient ik = nh->serviceClient<moveit_msgs::GetPositionIK>(prefix + "_kinematics/get_ik", true);
113 
114  moveit_msgs::GetPositionFK fkdata;
115 
116  // generate n_tests random positions, check that pos(fk) == ik(joints)
117  // hardcoded joint limits: J1=J2=J3: 0..90 degrees, J4 -25..25 degrees
118  double j1j2 = 0;
119  int n_matched = 0;
120  int n_iksolved = 0;
121  std::vector<double> jj;
122  for (int i = 0; i < n_tests; i++)
123  {
124  jj.resize(0);
125  if (isThumb)
126  {
127  // min-degrees 0 -30 -15 0 -60 max-degrees 90 30 15 75 60
128  jj.push_back(rand_range(0 * DEG2RAD, 90 * DEG2RAD)); // J1
129  jj.push_back(rand_range(-40 * DEG2RAD, 40 * DEG2RAD)); // J2
130  jj.push_back(rand_range(-12 * DEG2RAD, 12 * DEG2RAD)); // J3
131  jj.push_back(rand_range(0 * DEG2RAD, 70 * DEG2RAD)); // J4
132  jj.push_back(rand_range(-60 * DEG2RAD, 60 * DEG2RAD)); // J5
133  }
134  else
135  {
136  jj.push_back(j1j2 = rand_range(0 * DEG2RAD, 90 * DEG2RAD)); // J1
137  jj.push_back(j1j2); // we need J2==J1 to enforce the joint-coupling
138  jj.push_back(rand_range(0 * DEG2RAD, 90 * DEG2RAD)); // J3
139  jj.push_back(rand_range(-20 * DEG2RAD, 20 * DEG2RAD)); // J4 abduction
140  if (isLittleFinger)
141  {
142  jj.push_back(rand_range(0 * DEG2RAD, 45 * DEG2RAD)); // LFJ5
143  }
144  }
145 
146  // call fk to get the fingertip <prefix+tip> position
147  //
148  fkdata.request.header.frame_id = hand_prefix + "palm";
149  fkdata.request.header.stamp = ros::Time::now();
150  fkdata.request.fk_link_names.resize(1);
151  fkdata.request.fk_link_names.push_back(tipName);
152  fkdata.request.robot_state.joint_state.header.stamp = ros::Time::now();
153  fkdata.request.robot_state.joint_state.header.frame_id = "";
154  fkdata.request.robot_state.joint_state.name.resize(jointNames.size());
155  fkdata.request.robot_state.joint_state.position.resize(jointNames.size());
156 
157  if (verbose)
158  ROS_INFO("FK req fk_link_names[0] is %s", tipName.c_str());
159  for (unsigned int j = 0; j < jointNames.size(); j++)
160  {
161  fkdata.request.robot_state.joint_state.name[j] = jointNames[j];
162  fkdata.request.robot_state.joint_state.position[j] = jj[j];
163  if (verbose)
164  ROS_INFO("FK req %d joint %d %s radians %6.2f", i, j, jointNames[j].c_str(), jj[j]);
165  }
166  fk.call(fkdata); // (fkeq, fkres );
167 
168  // arm_navigation_msgs::ArmNavigationErrorCodes status = fkdata.response.error_code.val;
169  int status = fkdata.response.error_code.val;
170  if (verbose)
171  ROS_INFO("FK returned status %d", static_cast<int>(status));
172 
173  std::vector<geometry_msgs::PoseStamped> pp = fkdata.response.pose_stamped;
174  geometry_msgs::Pose pose = pp[0].pose; // position.xyz orientation.xyzw
175  // string[] fk_link_names
176  if (status == fkdata.response.error_code.SUCCESS)
177  {
178  if (verbose)
179  ROS_INFO("FK success, pose is %8.4f %8.4f %8.4f",
180  pose.position.x,
181  pose.position.y,
182  pose.position.z);
183  }
184  else
185  {
186  continue; // no use trying IK without FK success
187  }
188 
189  // now try IK to reconstruct FK angles
190  moveit_msgs::GetPositionIK::Request ikreq;
191  moveit_msgs::GetPositionIK::Response ikres;
192 
193  ikreq.ik_request.ik_link_name = tipName;
194  ikreq.ik_request.pose_stamped.header.frame_id = hand_prefix + "palm";
195  ikreq.ik_request.pose_stamped.pose.position.x = pose.position.x;
196  ikreq.ik_request.pose_stamped.pose.position.y = pose.position.y;
197  ikreq.ik_request.pose_stamped.pose.position.z = pose.position.z;
198 
199  // ori is not relevant with the used ik_solver but set it anyway to identity
200  ikreq.ik_request.pose_stamped.pose.orientation.x = 0.0;
201  ikreq.ik_request.pose_stamped.pose.orientation.y = 0.0;
202  ikreq.ik_request.pose_stamped.pose.orientation.z = 0.0;
203  ikreq.ik_request.pose_stamped.pose.orientation.w = 1.0;
204 
205  ikreq.ik_request.robot_state.joint_state.position.resize(jointNames.size());
206  ikreq.ik_request.robot_state.joint_state.name.resize(jointNames.size());
207  for (unsigned int j = 0; j < jointNames.size(); j++)
208  {
209  ikreq.ik_request.robot_state.joint_state.name[j] = jointNames[j];
210  }
211 
212  ik.call(ikreq, ikres);
213  if (ikres.error_code.val == ikres.error_code.SUCCESS)
214  {
215  n_iksolved++;
216  if (verbose)
217  ROS_INFO("IK found a solution: ");
218  for (unsigned int j = 0; j < ikres.solution.joint_state.name.size(); j++)
219  {
220  if (verbose)
221  ROS_INFO("Joint: %s %f", ikres.solution.joint_state.name[j].c_str(),
222  ikres.solution.joint_state.position[j]);
223  }
224  bool ok = true;
225  for (unsigned int j = 0; j < ikres.solution.joint_state.name.size(); j++)
226  {
227  std::string name = ikres.solution.joint_state.name[j];
228  int ix = jointIndexMap[name];
229  double x = ikres.solution.joint_state.position[j];
230  if (fabs(x - jj[ix]) > MAX_DELTA)
231  {
232  if (verbose)
233  ROS_INFO("FK/IK mismatch for joint %d %s, %6.4f %6.4f", j, name.c_str(), x, jj[ix]);
234  ok = false;
235  }
236  }
237  if (ok)
238  {
239  n_matched++;
240  }
241  if (verbose)
242  ROS_INFO("FK/IK comparison %s", (ok ? "matched" : "failed"));
243  }
244  else
245  {
246  // not solved
247  ROS_WARN("IK Not solved for FK req %d", i);
248  for (unsigned int j = 0; j < jointNames.size(); j++)
249  {
250  ROS_WARN(" joint %d %s radians %6.2f", j, jointNames[j].c_str(), jj[j]);
251  }
252  ROS_WARN("Pose was %f, %f, %f", pose.position.x, pose.position.y, pose.position.z);
253  }
254 
255  // call fk again but this time with the joint-angles from IK
256  fkdata.request.header.frame_id = hand_prefix + "palm";
257  fkdata.request.header.stamp = ros::Time::now();
258  fkdata.request.fk_link_names.resize(1);
259  fkdata.request.fk_link_names.push_back(tipName);
260  fkdata.request.robot_state.joint_state.header.stamp = ros::Time::now();
261  fkdata.request.robot_state.joint_state.header.frame_id = "";
262  fkdata.request.robot_state.joint_state.name.resize(jointNames.size());
263  fkdata.request.robot_state.joint_state.position.resize(jointNames.size());
264  if (verbose)
265  ROS_INFO("FK req with IK result fk_link_names[0] is %s", tipName.c_str());
266  for (unsigned int j = 0; j < ikres.solution.joint_state.name.size(); j++)
267  {
268  std::string name = ikres.solution.joint_state.name[j];
269  int ix = jointIndexMap[name];
270  double x = ikres.solution.joint_state.position[j];
271 
272  fkdata.request.robot_state.joint_state.name[ix] = jointNames[ix];
273  fkdata.request.robot_state.joint_state.position[ix] = x;
274  if (verbose)
275  ROS_INFO("FK req %d with IK result, joint %d %s radians %6.2f", i, j, jointNames[ix].c_str(), x);
276  }
277  fk.call(fkdata); // (fkeq, fkres );
278 
279  status = fkdata.response.error_code.val;
280  if (verbose)
281  ROS_INFO("FK returned status %d", static_cast<int>(status));
282 
283  std::vector<geometry_msgs::PoseStamped> ppp = fkdata.response.pose_stamped;
284  geometry_msgs::Pose pppose = ppp[0].pose; // position.xyz orientation.xyzw
285  // string[] fk_link_names
286  if (status == fkdata.response.error_code.SUCCESS)
287  {
288  if (verbose)
289  ROS_INFO("FK pose with IK result is %8.4f %8.4f %8.4f",
290  pppose.position.x,
291  pppose.position.y,
292  pppose.position.z);
293  }
294  if (verbose)
295  ROS_INFO("\n");
296  EXPECT_NEAR(pose.position.x, pppose.position.x, IK_NEAR);
297  EXPECT_NEAR(pose.position.y, pppose.position.y, IK_NEAR);
298  EXPECT_NEAR(pose.position.z, pppose.position.z, IK_NEAR);
299  }
300 
301  ROS_INFO("-#- tested %d random positions, %d IK solved, %d matched", n_tests, n_iksolved, n_matched);
302  EXPECT_NEAR(n_iksolved, n_tests, 2); // very few times, when values are close to joint limits, no solution is found
303  // matching is when angles are the same at 0.5 deg precision, but this seems
304  // not often true although IK can reach 1e-4 precision
305  // EXPECT_TRUE(double(n_matched)/n_tests >= 0.80);
306 }
307 
308 // Declare a test
309 TEST(FKIK, first_finger)
310 {
311  random_test_finger_fkik("FF", "ff", 1000, verbose);
312 }
313 
314 TEST(FKIK, middle_finger)
315 {
316  random_test_finger_fkik("MF", "mf", 1000, verbose);
317 }
318 
319 TEST(FKIK, ring_finger)
320 {
321  random_test_finger_fkik("RF", "rf", 1000, verbose);
322 }
323 
324 TEST(FKIK, little_finger)
325 {
326  random_test_finger_fkik("LF", "lf", 1000, verbose);
327 }
328 
329 TEST(FKIK, thumb)
330 {
331  random_test_finger_fkik("TH", "th", 1000, verbose);
332 }
333 
345 // Run all the tests that were declared with TEST()
346 int main(int argc, char **argv)
347 {
348  testing::InitGoogleTest(&argc, argv);
349 
350  ros::init(argc, argv, "upmc_fkik_test");
351  nh = new ros::NodeHandle();
352 
353  if (argc > 1)
354  {
355  char argument = static_cast<char>((argv[1][0]));
356  if (argument == 'v')
357  {
358  verbose = true;
359  }
360  else
361  {
362  verbose = false;
363  }
364  }
365 
366  int seed = 0;
367  while (seed == 0)
368  {
369  seed = ros::Time::now().sec; // wait until /clock received
370  }
371  ROS_INFO("-#- FK/IK test started, random seed is %d", seed);
372  srandom(seed);
373 
374  return RUN_ALL_TESTS();
375 }
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
#define DEG2RAD
TEST(FKIK, first_finger)
#define IK_NEAR
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
#define EXPECT_NEAR(a, b, prec)
#define ROS_WARN(...)
#define ROS_INFO(...)
double rand_range(double min_n, double max_n)
ros::NodeHandle * nh
int main(int argc, char **argv)
static Time now()
#define MAX_DELTA
double x
void random_test_finger_fkik(std::string PREFIX, std::string prefix, int n_tests, bool verbose=false)
ROSCPP_DECL bool waitForService(const std::string &service_name, int32_t timeout)


hand_kinematics
Author(s): Juan Antonio Corrales Ramon (UPMC),, Guillaume Walck (CITEC)
autogenerated on Wed Oct 14 2020 04:05:07