moveit2
The MoveIt Motion Planning Framework for ROS 2.
locked_robot_state_test.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Willow Garage, Inc.
5  * Copyright (c) 2014, SRI International
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Acorn Pooley, Ioan Sucan */
37 
38 #include <gtest/gtest.h>
42 #include <urdf_parser/urdf_parser.h>
43 
44 static const char* URDF_STR = "<?xml version=\"1.0\" ?>"
45  "<robot name=\"one_robot\">"
46  "<link name=\"base_link\">"
47  " <inertial>"
48  " <mass value=\"2.81\"/>"
49  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
50  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
51  " </inertial>"
52  " <collision name=\"my_collision\">"
53  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
54  " <geometry>"
55  " <box size=\"1 2 1\" />"
56  " </geometry>"
57  " </collision>"
58  " <visual>"
59  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
60  " <geometry>"
61  " <box size=\"1 2 1\" />"
62  " </geometry>"
63  " </visual>"
64  "</link>"
65  "<joint name=\"joint_a\" type=\"continuous\">"
66  " <axis xyz=\"0 0 1\"/>"
67  " <parent link=\"base_link\"/>"
68  " <child link=\"link_a\"/>"
69  " <origin rpy=\" 0.0 0 0 \" xyz=\"0.0 0 0 \"/>"
70  "</joint>"
71  "<link name=\"link_a\">"
72  " <inertial>"
73  " <mass value=\"1.0\"/>"
74  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
75  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
76  " </inertial>"
77  " <collision>"
78  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
79  " <geometry>"
80  " <box size=\"1 2 1\" />"
81  " </geometry>"
82  " </collision>"
83  " <visual>"
84  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
85  " <geometry>"
86  " <box size=\"1 2 1\" />"
87  " </geometry>"
88  " </visual>"
89  "</link>"
90  "<joint name=\"joint_b\" type=\"fixed\">"
91  " <parent link=\"link_a\"/>"
92  " <child link=\"link_b\"/>"
93  " <origin rpy=\" 0.0 -0.42 0 \" xyz=\"0.0 0.5 0 \"/>"
94  "</joint>"
95  "<link name=\"link_b\">"
96  " <inertial>"
97  " <mass value=\"1.0\"/>"
98  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
99  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
100  " </inertial>"
101  " <collision>"
102  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
103  " <geometry>"
104  " <box size=\"1 2 1\" />"
105  " </geometry>"
106  " </collision>"
107  " <visual>"
108  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
109  " <geometry>"
110  " <box size=\"1 2 1\" />"
111  " </geometry>"
112  " </visual>"
113  "</link>"
114  " <joint name=\"joint_c\" type=\"prismatic\">"
115  " <axis xyz=\"1 0 0\"/>"
116  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.09\" velocity=\"0.2\"/>"
117  " <safety_controller k_position=\"20.0\" k_velocity=\"500.0\" "
118  "soft_lower_limit=\"0.0\" soft_upper_limit=\"0.089\"/>"
119  " <parent link=\"link_b\"/>"
120  " <child link=\"link_c\"/>"
121  " <origin rpy=\" 0.0 0.42 0.0 \" xyz=\"0.0 -0.1 0 \"/>"
122  " </joint>"
123  "<link name=\"link_c\">"
124  " <inertial>"
125  " <mass value=\"1.0\"/>"
126  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 .0\"/>"
127  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
128  " </inertial>"
129  " <collision>"
130  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
131  " <geometry>"
132  " <box size=\"1 2 1\" />"
133  " </geometry>"
134  " </collision>"
135  " <visual>"
136  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
137  " <geometry>"
138  " <box size=\"1 2 1\" />"
139  " </geometry>"
140  " </visual>"
141  "</link>"
142  " <joint name=\"mim_f\" type=\"prismatic\">"
143  " <axis xyz=\"1 0 0\"/>"
144  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.19\" velocity=\"0.2\"/>"
145  " <parent link=\"link_c\"/>"
146  " <child link=\"link_d\"/>"
147  " <origin rpy=\" 0.0 0.1 0.0 \" xyz=\"0.1 0.1 0 \"/>"
148  " <mimic joint=\"joint_f\" multiplier=\"1.5\" offset=\"0.1\"/>"
149  " </joint>"
150  " <joint name=\"joint_f\" type=\"prismatic\">"
151  " <axis xyz=\"1 0 0\"/>"
152  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.19\" velocity=\"0.2\"/>"
153  " <parent link=\"link_d\"/>"
154  " <child link=\"link_e\"/>"
155  " <origin rpy=\" 0.0 0.1 0.0 \" xyz=\"0.1 0.1 0 \"/>"
156  " </joint>"
157  "<link name=\"link_d\">"
158  " <collision>"
159  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
160  " <geometry>"
161  " <box size=\"1 2 1\" />"
162  " </geometry>"
163  " </collision>"
164  " <visual>"
165  " <origin rpy=\"0 1 0\" xyz=\"0 0.1 0\"/>"
166  " <geometry>"
167  " <box size=\"1 2 1\" />"
168  " </geometry>"
169  " </visual>"
170  "</link>"
171  "<link name=\"link_e\">"
172  " <collision>"
173  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
174  " <geometry>"
175  " <box size=\"1 2 1\" />"
176  " </geometry>"
177  " </collision>"
178  " <visual>"
179  " <origin rpy=\"0 1 0\" xyz=\"0 0.1 0\"/>"
180  " <geometry>"
181  " <box size=\"1 2 1\" />"
182  " </geometry>"
183  " </visual>"
184  "</link>"
185  "</robot>";
186 
187 static const char* SRDF_STR =
188  "<?xml version=\"1.0\" ?>"
189  "<robot name=\"one_robot\">"
190  "<virtual_joint name=\"base_joint\" child_link=\"base_link\" parent_frame=\"odom_combined\" type=\"planar\"/>"
191  "<group name=\"base_from_joints\">"
192  "<joint name=\"base_joint\"/>"
193  "<joint name=\"joint_a\"/>"
194  "<joint name=\"joint_c\"/>"
195  "</group>"
196  "<group name=\"mim_joints\">"
197  "<joint name=\"joint_f\"/>"
198  "<joint name=\"mim_f\"/>"
199  "</group>"
200  "<group name=\"base_with_subgroups\">"
201  "<group name=\"base_from_base_to_tip\"/>"
202  "<joint name=\"joint_c\"/>"
203  "</group>"
204  "<group name=\"base_from_base_to_tip\">"
205  "<chain base_link=\"base_link\" tip_link=\"link_b\"/>"
206  "<joint name=\"base_joint\"/>"
207  "</group>"
208  "</robot>";
209 
210 // index of joints from URDF
211 enum
212 {
213  JOINT_A = 3,
214  JOINT_C = 4,
215  MIM_F = 5,
216  JOINT_F = 6
217 };
218 
219 static moveit::core::RobotModelPtr getModel()
220 {
221  static moveit::core::RobotModelPtr model;
222  if (!model)
223  {
224  urdf::ModelInterfaceSharedPtr urdf(urdf::parseURDF(URDF_STR));
225  auto srdf = std::make_shared<srdf::Model>();
226  srdf->initString(*urdf, SRDF_STR);
227  model = std::make_shared<moveit::core::RobotModel>(urdf, srdf);
228  }
229  return model;
230 }
231 
232 // Test constructors and robot model loading
233 TEST(LockedRobotState, load)
234 {
235  moveit::core::RobotModelPtr model = getModel();
236 
238 
239  moveit::core::RobotState state2(model);
240  state2.setToDefaultValues();
242 
243  auto ls4 = std::make_shared<robot_interaction::LockedRobotState>(model);
244 }
245 
246 // sanity test the URDF and enum
247 TEST(LockedRobotState, URDF_sanity)
248 {
249  moveit::core::RobotModelPtr model = getModel();
251 
252  EXPECT_EQ(ls1.getState()->getVariableNames()[JOINT_A], "joint_a");
253 }
254 
255 // A superclass to test the robotStateChanged() virtual method
257 {
258 public:
259  Super1(const moveit::core::RobotModelPtr& model) : LockedRobotState(model), cnt_(0)
260  {
261  }
262 
263  void robotStateChanged() override
264  {
265  cnt_++;
266  }
267 
268  int cnt_;
269 };
270 
271 static void modify1(moveit::core::RobotState* state)
272 {
273  state->setVariablePosition(JOINT_A, 0.00006);
274 }
275 
276 TEST(LockedRobotState, robotStateChanged)
277 {
278  moveit::core::RobotModelPtr model = getModel();
279 
280  Super1 ls1(model);
281 
282  EXPECT_EQ(ls1.cnt_, 0);
283 
285  cp1.setVariablePosition(JOINT_A, 0.00001);
286  cp1.setVariablePosition(JOINT_C, 0.00002);
287  cp1.setVariablePosition(JOINT_F, 0.00003);
288  ls1.setState(cp1);
289 
290  EXPECT_EQ(ls1.cnt_, 1);
291 
292  ls1.modifyState(modify1);
293  EXPECT_EQ(ls1.cnt_, 2);
294 
295  ls1.modifyState(modify1);
296  EXPECT_EQ(ls1.cnt_, 3);
297 }
298 
299 // Class for testing LockedRobotState in multithreaded environment.
300 // Contains thread functions for modifying/checking a LockedRobotState.
301 class MyInfo
302 {
303 public:
304  MyInfo() : quit_(false)
305  {
306  }
307 
308  // Thread that repeatedly sets state to different values
309  void setThreadFunc(robot_interaction::LockedRobotState* locked_state, int* counter, double offset);
310 
311  // Thread that repeatedly modifies state with different values
312  void modifyThreadFunc(robot_interaction::LockedRobotState* locked_state, int* counter, double offset);
313 
314  // Thread that repeatedly checks that state is valid (not half-updated)
315  void checkThreadFunc(robot_interaction::LockedRobotState* locked_state, int* counter);
316 
317  // Thread that waits for other threads to complete
318  void waitThreadFunc(robot_interaction::LockedRobotState* locked_state, int** counters, int max);
319 
320 private:
321  // helper function for modifyThreadFunc
322  void modifyFunc(moveit::core::RobotState& state, double val);
323 
324  // Checks state for validity and self-consistancy.
325  void checkState(robot_interaction::LockedRobotState& locked_state);
326 
327  // mutex protects quit_ and counter variables
328  std::mutex cnt_lock_;
329 
330  // set to true by waitThreadFunc() when all counters have reached at least
331  // max.
332  bool quit_;
333 };
334 
335 // Check the state. It should always be valid.
336 void MyInfo::checkState(robot_interaction::LockedRobotState& locked_state)
337 {
338  moveit::core::RobotStateConstPtr s = locked_state.getState();
339 
340  moveit::core::RobotState cp1(*s);
341 
342  // take some time
343  cnt_lock_.lock();
344  cnt_lock_.unlock();
345  cnt_lock_.lock();
346  cnt_lock_.unlock();
347  cnt_lock_.lock();
348  cnt_lock_.unlock();
349 
350  // check mim_f == joint_f
351  EXPECT_EQ(s->getVariablePositions()[MIM_F], s->getVariablePositions()[JOINT_F] * 1.5 + 0.1);
352 
353  moveit::core::RobotState cp2(*s);
354 
355  EXPECT_NE(cp1.getVariablePositions(), cp2.getVariablePositions());
356  EXPECT_NE(cp1.getVariablePositions(), s->getVariablePositions());
357 
358  int cnt = cp1.getVariableCount();
359  for (int i = 0; i < cnt; ++i)
360  {
361  EXPECT_EQ(cp1.getVariablePositions()[i], cp2.getVariablePositions()[i]);
362  EXPECT_EQ(cp1.getVariablePositions()[i], s->getVariablePositions()[i]);
363  }
364 
365  // check mim_f == joint_f
366  EXPECT_EQ(s->getVariablePositions()[MIM_F], s->getVariablePositions()[JOINT_F] * 1.5 + 0.1);
367 }
368 
369 // spin, checking the state
371 {
372  bool go = true;
373  while (go)
374  {
375  for (int loops = 0; loops < 100; ++loops)
376  {
377  checkState(*locked_state);
378  }
379 
380  cnt_lock_.lock();
381  go = !quit_;
382  ++*counter;
383  cnt_lock_.unlock();
384  }
385 }
386 
387 // spin, setting the state to different values
388 void MyInfo::setThreadFunc(robot_interaction::LockedRobotState* locked_state, int* counter, double offset)
389 {
390  bool go = true;
391  while (go)
392  {
393  double val = offset;
394  for (int loops = 0; loops < 100; ++loops)
395  {
396  val += 0.0001;
397  moveit::core::RobotState cp1(*locked_state->getState());
398 
399  cp1.setVariablePosition(JOINT_A, val + 0.00001);
400  cp1.setVariablePosition(JOINT_C, val + 0.00002);
401  cp1.setVariablePosition(JOINT_F, val + 0.00003);
402 
403  locked_state->setState(cp1);
404  }
405 
406  cnt_lock_.lock();
407  go = !quit_;
408  ++*counter;
409  cnt_lock_.unlock();
410 
411  checkState(*locked_state);
412 
413  val += 0.000001;
414  }
415 }
416 
417 // modify the state in place. Used by MyInfo::modifyThreadFunc()
418 void MyInfo::modifyFunc(moveit::core::RobotState& state, double val)
419 {
420  state.setVariablePosition(JOINT_A, val + 0.00001);
421  state.setVariablePosition(JOINT_C, val + 0.00002);
422  state.setVariablePosition(JOINT_F, val + 0.00003);
423 }
424 
425 // spin, modifying the state to different values
426 void MyInfo::modifyThreadFunc(robot_interaction::LockedRobotState* locked_state, int* counter, double offset)
427 {
428  bool go = true;
429  while (go)
430  {
431  double val = offset;
432  for (int loops = 0; loops < 100; ++loops)
433  {
434  val += 0.0001;
435 
436  locked_state->modifyState([this, val](moveit::core::RobotState* state) { modifyFunc(*state, val); });
437  }
438 
439  cnt_lock_.lock();
440  go = !quit_;
441  ++*counter;
442  cnt_lock_.unlock();
443 
444  checkState(*locked_state);
445 
446  val += 0.000001;
447  }
448 }
449 
450 // spin until all counters reach at least max
451 void MyInfo::waitThreadFunc(robot_interaction::LockedRobotState* locked_state, int** counters, int max)
452 {
453  bool go = true;
454  while (go)
455  {
456  go = false;
457  cnt_lock_.lock();
458  for (int i = 0; counters[i]; ++i)
459  {
460  if (counters[i][0] < max)
461  go = true;
462  }
463  cnt_lock_.unlock();
464 
465  checkState(*locked_state);
466  checkState(*locked_state);
467  }
468  cnt_lock_.lock();
469  quit_ = true;
470  cnt_lock_.unlock();
471 }
472 
473 // Run several threads and ensure they modify the state consistently
474 // ncheck - # of checkThreadFunc threads to run
475 // nset - # of setThreadFunc threads to run
476 // nmod - # of modifyThreadFunc threads to run
477 static void runThreads(int ncheck, int nset, int nmod)
478 {
479  MyInfo info;
480 
481  moveit::core::RobotModelPtr model = getModel();
483 
484  int num = ncheck + nset + nmod;
485 
486  typedef int* int_ptr;
487  typedef std::thread* thread_ptr;
488  int* cnt = new int[num];
489  int_ptr* counters = new int_ptr[num + 1];
490  thread_ptr* threads = new thread_ptr[num];
491 
492  int p = 0;
493  double val = 0.1;
494 
495  // These threads check the validity of the RobotState
496  for (int i = 0; i < ncheck; ++i)
497  {
498  cnt[p] = 0;
499  counters[p] = &cnt[p];
500  threads[p] = new std::thread(&MyInfo::checkThreadFunc, &info, &ls1, &cnt[p]);
501  val += 0.1;
502  p++;
503  }
504 
505  // These threads set the RobotState to new values
506  for (int i = 0; i < nset; ++i)
507  {
508  cnt[p] = 0;
509  counters[p] = &cnt[p];
510  threads[p] = new std::thread(&MyInfo::setThreadFunc, &info, &ls1, &cnt[p], val);
511  val += 0.1;
512  p++;
513  }
514 
515  // These threads modify the RobotState in place
516  for (int i = 0; i < nmod; ++i)
517  {
518  cnt[p] = 0;
519  counters[p] = &cnt[p];
520  threads[p] = new std::thread(&MyInfo::modifyThreadFunc, &info, &ls1, &cnt[p], val);
521  val += 0.1;
522  p++;
523  }
524 
525  ASSERT_EQ(p, num);
526  counters[p] = nullptr;
527 
528  // this thread waits for all the other threads to make progress, then stops
529  // everything.
530  std::thread wthread(&MyInfo::waitThreadFunc, &info, &ls1, counters, 1000);
531 
532  // wait for all threads to finish
533  for (int i = 0; i < p; ++i)
534  {
535  if (threads[i]->joinable())
536  {
537  threads[i]->join();
538  }
539  }
540  if (wthread.joinable())
541  {
542  wthread.join();
543  }
544 
545  // clean up
546  for (int i = 0; i < p; ++i)
547  {
548  delete threads[i];
549  }
550  delete[] cnt;
551  delete[] counters;
552  delete[] threads;
553 }
554 
555 TEST(LockedRobotState, set1)
556 {
557  runThreads(1, 1, 0);
558 }
559 
560 // skip all more complex locking checks when optimizations are disabled
561 // they can easily outrun 20 minutes with Debug builds
562 #ifdef NDEBUG
563 #define OPT_TEST(F, N) TEST(F, N)
564 #else
565 #define OPT_TEST(F, N) TEST(F, DISABLED_##N)
566 #endif
567 
568 OPT_TEST(LockedRobotState, set2)
569 {
570  runThreads(1, 2, 0);
571 }
572 
573 OPT_TEST(LockedRobotState, set3)
574 {
575  runThreads(1, 3, 0);
576 }
577 
578 OPT_TEST(LockedRobotState, mod1)
579 {
580  runThreads(1, 0, 1);
581 }
582 
583 OPT_TEST(LockedRobotState, mod2)
584 {
585  runThreads(1, 0, 1);
586 }
587 
588 OPT_TEST(LockedRobotState, mod3)
589 {
590  runThreads(1, 0, 1);
591 }
592 
593 OPT_TEST(LockedRobotState, set1mod1)
594 {
595  runThreads(1, 1, 1);
596 }
597 
598 OPT_TEST(LockedRobotState, set2mod1)
599 {
600  runThreads(1, 2, 1);
601 }
602 
603 OPT_TEST(LockedRobotState, set1mod2)
604 {
605  runThreads(1, 1, 2);
606 }
607 
608 OPT_TEST(LockedRobotState, set3mod1)
609 {
610  runThreads(1, 3, 1);
611 }
612 
613 OPT_TEST(LockedRobotState, set1mod3)
614 {
615  runThreads(1, 1, 3);
616 }
617 
618 OPT_TEST(LockedRobotState, set3mod3)
619 {
620  runThreads(1, 3, 3);
621 }
622 
623 OPT_TEST(LockedRobotState, set3mod3c3)
624 {
625  runThreads(3, 3, 3);
626 }
627 
628 int main(int argc, char** argv)
629 {
630  testing::InitGoogleTest(&argc, argv);
631  return RUN_ALL_TESTS();
632 }
void modifyThreadFunc(robot_interaction::LockedRobotState *locked_state, int *counter, double offset)
void waitThreadFunc(robot_interaction::LockedRobotState *locked_state, int **counters, int max)
void checkThreadFunc(robot_interaction::LockedRobotState *locked_state, int *counter)
void setThreadFunc(robot_interaction::LockedRobotState *locked_state, int *counter, double offset)
Super1(const moveit::core::RobotModelPtr &model)
void robotStateChanged() override
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
void setVariablePosition(const std::string &variable, double value)
Set the position of a single variable. An exception is thrown if the variable name is not known.
Definition: robot_state.h:188
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
Maintain a RobotState in a multithreaded environment.
moveit::core::RobotStateConstPtr getState() const
get read-only access to the state.
LockedRobotState(const moveit::core::RobotState &state)
void modifyState(const ModifyStateFunction &modify)
void setState(const moveit::core::RobotState &state)
Set the state to the new value.
int main(int argc, char **argv)
#define OPT_TEST(F, N)
TEST(LockedRobotState, load)
p
Definition: pick.py:62