moveitjoy_module.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # ********************************************************************
3 # Software License Agreement (BSD License)
4 #
5 # Copyright (c) 2014, JSK, The University of Tokyo.
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 the JSK, The University of Tokyo nor the names of its
19 # nor the names of its contributors may be
20 # used to endorse or promote products derived
21 # from this software without specific prior written permission.
22 #
23 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 # POSSIBILITY OF SUCH DAMAGE.
35 # ********************************************************************/
36 
37 # Author: Ryohei Ueda, Dave Coleman
38 # Desc: Interface between PS3/XBox controller and MoveIt! Motion Planning Rviz Plugin
39 
40 from __future__ import print_function
41 
42 import xml.dom.minidom
43 from operator import add
44 import sys
45 import threading
46 from moveit_ros_planning_interface._moveit_robot_interface import RobotInterface
47 
48 import rospy
49 import roslib
50 import numpy
51 import time
52 import tf
53 from std_msgs.msg import Empty, String
54 from sensor_msgs.msg import Joy
55 from geometry_msgs.msg import PoseStamped
56 from visualization_msgs.msg import InteractiveMarkerInit
57 
58 def signedSquare(val):
59  if val > 0:
60  sign = 1
61  else:
62  sign = -1
63  return val * val * sign
64 
65 
66 # classes to use joystick of xbox, ps3(wired) and ps3(wireless).
67 
68 class JoyStatus():
69  def __init__(self):
70  self.center = False
71  self.select = False
72  self.start = False
73  self.L3 = False
74  self.R3 = False
75  self.square = False
76  self.up = False
77  self.down = False
78  self.left = False
79  self.right = False
80  self.triangle = False
81  self.cross = False
82  self.circle = False
83  self.L1 = False
84  self.R1 = False
85  self.L2 = False
86  self.R2 = False
87  self.left_analog_x = 0.0
88  self.left_analog_y = 0.0
89  self.right_analog_x = 0.0
90  self.right_analog_y = 0.0
91 
93  def __init__(self, msg):
94  JoyStatus.__init__(self)
95  if msg.buttons[8] == 1:
96  self.center = True
97  else:
98  self.center = False
99  if msg.buttons[6] == 1:
100  self.select = True
101  else:
102  self.select = False
103  if msg.buttons[7] == 1:
104  self.start = True
105  else:
106  self.start = False
107  if msg.buttons[9] == 1:
108  self.L3 = True
109  else:
110  self.L3 = False
111  if msg.buttons[10] == 1:
112  self.R3 = True
113  else:
114  self.R3 = False
115  if msg.buttons[2] == 1:
116  self.square = True
117  else:
118  self.square = False
119  if msg.buttons[1] == 1:
120  self.circle = True
121  else:
122  self.circle = False
123  if msg.axes[7] > 0.1:
124  self.up = True
125  else:
126  self.up = False
127  if msg.axes[7] < -0.1:
128  self.down = True
129  else:
130  self.down = False
131  if msg.axes[6] > 0.1:
132  self.left = True
133  else:
134  self.left = False
135  if msg.axes[6] < -0.1:
136  self.right = True
137  else:
138  self.right = False
139  if msg.buttons[3] == 1:
140  self.triangle = True
141  else:
142  self.triangle = False
143  if msg.buttons[0] == 1:
144  self.cross = True
145  else:
146  self.cross = False
147  if msg.buttons[4] == 1:
148  self.L1 = True
149  else:
150  self.L1 = False
151  if msg.buttons[5] == 1:
152  self.R1 = True
153  else:
154  self.R1 = False
155  if msg.axes[2] < -0.5:
156  self.L2 = True
157  else:
158  self.L2 = False
159  if msg.axes[5] < -0.5:
160  self.R2 = True
161  else:
162  self.R2 = False
163  self.left_analog_x = msg.axes[0]
164  self.left_analog_y = msg.axes[1]
165  self.right_analog_x = msg.axes[3]
166  self.right_analog_y = msg.axes[4]
167  self.orig_msg = msg
168 
170  def __init__(self, msg):
171  JoyStatus.__init__(self)
172  # creating from sensor_msgs/Joy
173  if msg.buttons[16] == 1:
174  self.center = True
175  else:
176  self.center = False
177  if msg.buttons[0] == 1:
178  self.select = True
179  else:
180  self.select = False
181  if msg.buttons[3] == 1:
182  self.start = True
183  else:
184  self.start = False
185  if msg.buttons[1] == 1:
186  self.L3 = True
187  else:
188  self.L3 = False
189  if msg.buttons[2] == 1:
190  self.R3 = True
191  else:
192  self.R3 = False
193  if msg.axes[15] < 0:
194  self.square = True
195  else:
196  self.square = False
197  if msg.axes[4] < 0:
198  self.up = True
199  else:
200  self.up = False
201  if msg.axes[6] < 0:
202  self.down = True
203  else:
204  self.down = False
205  if msg.axes[7] < 0:
206  self.left = True
207  else:
208  self.left = False
209  if msg.axes[5] < 0:
210  self.right = True
211  else:
212  self.right = False
213  if msg.axes[12] < 0:
214  self.triangle = True
215  else:
216  self.triangle = False
217  if msg.axes[14] < 0:
218  self.cross = True
219  else:
220  self.cross = False
221  if msg.axes[13] < 0:
222  self.circle = True
223  else:
224  self.circle = False
225  if msg.axes[10] < 0:
226  self.L1 = True
227  else:
228  self.L1 = False
229  if msg.axes[11] < 0:
230  self.R1 = True
231  else:
232  self.R1 = False
233  if msg.axes[8] < 0:
234  self.L2 = True
235  else:
236  self.L2 = False
237  if msg.axes[9] < 0:
238  self.R2 = True
239  else:
240  self.R2 = False
241  self.left_analog_x = msg.axes[0]
242  self.left_analog_y = msg.axes[1]
243  self.right_analog_x = msg.axes[2]
244  self.right_analog_y = msg.axes[3]
245  self.orig_msg = msg
246 
248  def __init__(self, msg):
249  JoyStatus.__init__(self)
250  # creating from sensor_msgs/Joy
251  if msg.buttons[16] == 1:
252  self.center = True
253  else:
254  self.center = False
255  if msg.buttons[0] == 1:
256  self.select = True
257  else:
258  self.select = False
259  if msg.buttons[3] == 1:
260  self.start = True
261  else:
262  self.start = False
263  if msg.buttons[1] == 1:
264  self.L3 = True
265  else:
266  self.L3 = False
267  if msg.buttons[2] == 1:
268  self.R3 = True
269  else:
270  self.R3 = False
271  if msg.buttons[15] == 1:
272  self.square = True
273  else:
274  self.square = False
275  if msg.buttons[4] == 1:
276  self.up = True
277  else:
278  self.up = False
279  if msg.buttons[6] == 1:
280  self.down = True
281  else:
282  self.down = False
283  if msg.buttons[7] == 1:
284  self.left = True
285  else:
286  self.left = False
287  if msg.buttons[5] == 1:
288  self.right = True
289  else:
290  self.right = False
291  if msg.buttons[12] == 1:
292  self.triangle = True
293  else:
294  self.triangle = False
295  if msg.buttons[14] == 1:
296  self.cross = True
297  else:
298  self.cross = False
299  if msg.buttons[13] == 1:
300  self.circle = True
301  else:
302  self.circle = False
303  if msg.buttons[10] == 1:
304  self.L1 = True
305  else:
306  self.L1 = False
307  if msg.buttons[11] == 1:
308  self.R1 = True
309  else:
310  self.R1 = False
311  if msg.buttons[8] == 1:
312  self.L2 = True
313  else:
314  self.L2 = False
315  if msg.buttons[9] == 1:
316  self.R2 = True
317  else:
318  self.R2 = False
319  self.left_analog_x = msg.axes[0]
320  self.left_analog_y = msg.axes[1]
321  self.right_analog_x = msg.axes[2]
322  self.right_analog_y = msg.axes[3]
323  self.orig_msg = msg
324 
326  def __init__(self, max_length=10):
327  self.max_length = max_length
328  self.buffer = []
329  def add(self, status):
330  self.buffer.append(status)
331  if len(self.buffer) > self.max_length:
332  self.buffer = self.buffer[1:self.max_length+1]
333  def all(self, proc):
334  for status in self.buffer:
335  if not proc(status):
336  return False
337  return True
338  def latest(self):
339  if len(self.buffer) > 0:
340  return self.buffer[-1]
341  else:
342  return None
343  def length(self):
344  return len(self.buffer)
345  def new(self, status, attr):
346  if len(self.buffer) == 0:
347  return getattr(status, attr)
348  else:
349  return getattr(status, attr) and not getattr(self.latest(), attr)
350 
351 class MoveitJoy:
352  def parseSRDF(self):
353  ri = RobotInterface("/robot_description")
354  planning_groups = {}
355  for g in ri.get_group_names():
356  self.planning_groups_tips[g] = ri.get_group_joint_tips(g)
357  planning_groups[g] = ["/rviz/moveit/move_marker/goal_" + l
358  for l in self.planning_groups_tips[g]]
359  for name in planning_groups.keys():
360  if len(planning_groups[name]) == 0:
361  del planning_groups[name]
362  else:
363  print(name, planning_groups[name])
364  self.planning_groups = planning_groups
365  self.planning_groups_keys = planning_groups.keys() #we'd like to store the 'order'
366  self.frame_id = ri.get_planning_frame()
367  def __init__(self):
368  self.initial_poses = {}
371  self.marker_lock = threading.Lock()
372  self.prev_time = rospy.Time.now()
373  self.counter = 0
374  self.history = StatusHistory(max_length=10)
375  self.pre_pose = PoseStamped()
376  self.pre_pose.pose.orientation.w = 1
379  self.initialize_poses = False
380  self.initialized = False
381  self.parseSRDF()
382  self.plan_group_pub = rospy.Publisher('/rviz/moveit/select_planning_group', String, queue_size=5)
383  self.updatePlanningGroup(0)
384  self.updatePoseTopic(0, False)
385  self.joy_pose_pub = rospy.Publisher("/joy_pose", PoseStamped, queue_size=1)
386  self.plan_pub = rospy.Publisher("/rviz/moveit/plan", Empty, queue_size=5)
387  self.execute_pub = rospy.Publisher("/rviz/moveit/execute", Empty, queue_size=5)
388  self.update_start_state_pub = rospy.Publisher("/rviz/moveit/update_start_state", Empty, queue_size=5)
389  self.update_goal_state_pub = rospy.Publisher("/rviz/moveit/update_goal_state", Empty, queue_size=5)
390  self.interactive_marker_sub = rospy.Subscriber("/rviz_moveit_motion_planning_display/robot_interaction_interactive_marker_topic/update_full",
391  InteractiveMarkerInit,
392  self.markerCB, queue_size=1)
393  self.sub = rospy.Subscriber("/joy", Joy, self.joyCB, queue_size=1)
394  def updatePlanningGroup(self, next_index):
395  if next_index >= len(self.planning_groups_keys):
397  elif next_index < 0:
399  else:
400  self.current_planning_group_index = next_index
401  next_planning_group = None
402  try:
403  next_planning_group = self.planning_groups_keys[self.current_planning_group_index]
404  except IndexError:
405  msg = 'Check if you started movegroups. Exiting.'
406  rospy.logfatal(msg)
407  raise rospy.ROSInitException(msg)
408  rospy.loginfo("Changed planning group to " + next_planning_group)
409  self.plan_group_pub.publish(next_planning_group)
410  def updatePoseTopic(self, next_index, wait=True):
411  planning_group = self.planning_groups_keys[self.current_planning_group_index]
412  topics = self.planning_groups[planning_group]
413  if next_index >= len(topics):
414  self.current_eef_index = 0
415  elif next_index < 0:
416  self.current_eef_index = len(topics) - 1
417  else:
418  self.current_eef_index = next_index
419  next_topic = topics[self.current_eef_index]
420 
421  rospy.loginfo("Changed controlled end effector to " + self.planning_groups_tips[planning_group][self.current_eef_index])
422  self.pose_pub = rospy.Publisher(next_topic, PoseStamped, queue_size=5)
423  if wait:
424  self.waitForInitialPose(next_topic)
425  self.current_pose_topic = next_topic
426  def markerCB(self, msg):
427  try:
428  self.marker_lock.acquire()
429  if not self.initialize_poses:
430  return
431  self.initial_poses = {}
432  for marker in msg.markers:
433  if marker.name.startswith("EE:goal_"):
434  # resolve tf
435  if marker.header.frame_id != self.frame_id:
436  ps = PoseStamped(header=marker.header, pose=marker.pose)
437  try:
438  transformed_pose = self.tf_listener.transformPose(self.frame_id, ps)
439  self.initial_poses[marker.name[3:]] = transformed_pose.pose
441  rospy.logerr("tf error when resolving tf: %s" % e)
442  else:
443  self.initial_poses[marker.name[3:]] = marker.pose #tf should be resolved
444  finally:
445  self.marker_lock.release()
446  def waitForInitialPose(self, next_topic, timeout=None):
447  counter = 0
448  while not rospy.is_shutdown():
449  counter = counter + 1
450  if timeout and counter >= timeout:
451  return False
452  try:
453  self.marker_lock.acquire()
454  self.initialize_poses = True
455  topic_suffix = next_topic.split("/")[-1]
456  if self.initial_poses.has_key(topic_suffix):
457  self.pre_pose = PoseStamped(pose=self.initial_poses[topic_suffix])
458  self.initialize_poses = False
459  return True
460  else:
461  rospy.logdebug(self.initial_poses.keys())
462  rospy.loginfo("Waiting for pose topic of '%s' to be initialized",
463  topic_suffix)
464  rospy.sleep(1)
465  finally:
466  self.marker_lock.release()
467  def joyCB(self, msg):
468  if len(msg.axes) == 27 and len(msg.buttons) == 19:
469  status = PS3WiredStatus(msg)
470  elif len(msg.axes) == 8 and len(msg.buttons) == 11:
471  status = XBoxStatus(msg)
472  elif len(msg.axes) == 20 and len(msg.buttons) == 17:
473  status = PS3Status(msg)
474  else:
475  raise Exception("Unknown joystick")
476  self.run(status)
477  self.history.add(status)
478  def computePoseFromJoy(self, pre_pose, status):
479  new_pose = PoseStamped()
480  new_pose.header.frame_id = self.frame_id
481  new_pose.header.stamp = rospy.Time(0.0)
482  # move in local
483  dist = status.left_analog_y * status.left_analog_y + status.left_analog_x * status.left_analog_x
484  scale = 200.0
485  x_diff = signedSquare(status.left_analog_y) / scale
486  y_diff = signedSquare(status.left_analog_x) / scale
487  # z
488  if status.L2:
489  z_diff = 0.005
490  elif status.R2:
491  z_diff = -0.005
492  else:
493  z_diff = 0.0
494  if self.history.all(lambda s: s.L2) or self.history.all(lambda s: s.R2):
495  z_scale = 4.0
496  else:
497  z_scale = 2.0
498  local_move = numpy.array((x_diff, y_diff,
499  z_diff * z_scale,
500  1.0))
501  q = numpy.array((pre_pose.pose.orientation.x,
502  pre_pose.pose.orientation.y,
503  pre_pose.pose.orientation.z,
504  pre_pose.pose.orientation.w))
505  xyz_move = numpy.dot(tf.transformations.quaternion_matrix(q),
506  local_move)
507  new_pose.pose.position.x = pre_pose.pose.position.x + xyz_move[0]
508  new_pose.pose.position.y = pre_pose.pose.position.y + xyz_move[1]
509  new_pose.pose.position.z = pre_pose.pose.position.z + xyz_move[2]
510  roll = 0.0
511  pitch = 0.0
512  yaw = 0.0
513  DTHETA = 0.005
514  if status.L1:
515  if self.history.all(lambda s: s.L1):
516  yaw = yaw + DTHETA * 2
517  else:
518  yaw = yaw + DTHETA
519  elif status.R1:
520  if self.history.all(lambda s: s.R1):
521  yaw = yaw - DTHETA * 2
522  else:
523  yaw = yaw - DTHETA
524  if status.up:
525  if self.history.all(lambda s: s.up):
526  pitch = pitch + DTHETA * 2
527  else:
528  pitch = pitch + DTHETA
529  elif status.down:
530  if self.history.all(lambda s: s.down):
531  pitch = pitch - DTHETA * 2
532  else:
533  pitch = pitch - DTHETA
534  if status.right:
535  if self.history.all(lambda s: s.right):
536  roll = roll + DTHETA * 2
537  else:
538  roll = roll + DTHETA
539  elif status.left:
540  if self.history.all(lambda s: s.left):
541  roll = roll - DTHETA * 2
542  else:
543  roll = roll - DTHETA
544  diff_q = tf.transformations.quaternion_from_euler(roll, pitch, yaw)
545  new_q = tf.transformations.quaternion_multiply(q, diff_q)
546  new_pose.pose.orientation.x = new_q[0]
547  new_pose.pose.orientation.y = new_q[1]
548  new_pose.pose.orientation.z = new_q[2]
549  new_pose.pose.orientation.w = new_q[3]
550  return new_pose
551  def run(self, status):
552  if not self.initialized:
553  # when not initialized, we will force to change planning_group
554  while True:
556  planning_group = self.planning_groups_keys[self.current_planning_group_index]
557  topics = self.planning_groups[planning_group]
558  next_topic = topics[self.current_eef_index]
559  if not self.waitForInitialPose(next_topic, timeout=3):
560  rospy.logwarn("Unable to initialize planning group " + planning_group + ". Trying different group.")
561  rospy.logwarn("Is 'Allow External Comm.' enabled in Rviz? Is the 'Query Goal State' robot enabled?")
562  else:
563  rospy.loginfo("Initialized planning group")
564  self.initialized = True
566  return
567  # Try to initialize with different planning group
570  self.current_planning_group_index = 0 # reset loop
571  if self.history.new(status, "select"): #increment planning group
573  self.current_eef_index = 0 # force to reset
575  return
576  elif self.history.new(status, "start"): #decrement planning group
578  self.current_eef_index = 0 # force to reset
580  return
581  elif self.history.new(status, "triangle"):
582  self.updatePoseTopic(self.current_eef_index + 1)
583  return
584  elif self.history.new(status, "cross"):
585  self.updatePoseTopic(self.current_eef_index - 1)
586  return
587  elif self.history.new(status, "square"): #plan
588  rospy.loginfo("Plan")
589  self.plan_pub.publish(Empty())
590  return
591  elif self.history.new(status, "circle"): #execute
592  rospy.loginfo("Execute")
593  self.execute_pub.publish(Empty())
594  return
595  self.marker_lock.acquire()
596  pre_pose = self.pre_pose
597  new_pose = self.computePoseFromJoy(pre_pose, status)
598  now = rospy.Time.from_sec(time.time())
599  # placement.time_from_start = now - self.prev_time
600  if (now - self.prev_time).to_sec() > 1 / 30.0:
601  # rospy.loginfo(new_pose)
602  self.pose_pub.publish(new_pose)
603  self.joy_pose_pub.publish(new_pose)
604  self.prev_time = now
605  # sync start state to the real robot state
606  self.counter = self.counter + 1
607  if self.counter % 10:
608  self.update_start_state_pub.publish(Empty())
609  self.pre_pose = new_pose
610  self.marker_lock.release()
611  # update self.initial_poses
612  self.marker_lock.acquire()
613  self.initial_poses[self.current_pose_topic.split("/")[-1]] = new_pose.pose
614  self.marker_lock.release()
def updatePoseTopic(self, next_index, wait=True)
def waitForInitialPose(self, next_topic, timeout=None)


visualization
Author(s): Ioan Sucan , Dave Coleman , Sachin Chitta
autogenerated on Sun Oct 18 2020 13:19:09