00001 import roslib; roslib.load_manifest('rcommander')
00002 import rospy
00003 import actionlib
00004 import trajectory_msgs.msg as tm
00005 import numpy as np
00006 import functools as ft
00007 import sensor_msgs.msg as sm
00008 import std_msgs.msg as stdm
00009 import pr2_controllers_msgs.msg as pm
00010 import geometry_msgs.msg as gm
00011 import geometry_msgs.msg as geo
00012 import time
00013 from kinematics_msgs.srv import GetKinematicSolverInfo
00014 from pycontroller_manager.pycontroller_manager import ControllerManager
00015 from PyQt4.QtGui import *
00016 from PyQt4.QtCore import *
00017 import tf_utils as tfu
00018 from object_manipulator.convert_functions import stamp_pose
00019 import tf.transformations as tr
00020 from tf_broadcast_server.srv import GetTransforms
00021 import rcommander.tool_utils as tu
00022 import roslib
00023 import os.path as pt
00024
00025
00026 class SE3Tool:
00027
00028 def __init__(self):
00029 self.frames_service = rospy.ServiceProxy('get_transforms', GetTransforms)
00030
00031 def make_se3_boxes(self, pbox):
00032 self.xline = tu.double_spin_box(pbox, -3.,3.,.01)
00033 self.yline = tu.double_spin_box(pbox, -3.,3.,.01)
00034 self.zline = tu.double_spin_box(pbox, -3.,3.,.01)
00035 self.phi_line = tu.double_spin_box(pbox, -360., 360., 1)
00036 self.theta_line = tu.double_spin_box(pbox, -360., 360., 1)
00037 self.psi_line = tu.double_spin_box(pbox, -360., 360., 1)
00038
00039 position_box = QGroupBox('Position', pbox)
00040 position_layout = QFormLayout(position_box)
00041 position_box.setLayout(position_layout)
00042 position_layout.addRow("&X", self.xline)
00043 position_layout.addRow("&Y", self.yline)
00044 position_layout.addRow("&Z", self.zline)
00045
00046 orientation_box = QGroupBox('Orientation', pbox)
00047 orientation_layout = QFormLayout(orientation_box)
00048 orientation_box.setLayout(orientation_layout)
00049 orientation_layout.addRow("&Phi", self.phi_line)
00050 orientation_layout.addRow("&Theta", self.theta_line)
00051 orientation_layout.addRow("&Psi", self.psi_line)
00052
00053 return [position_box, orientation_box]
00054
00055 def make_frame_box(self, pbox):
00056 frame_box = QComboBox(pbox)
00057
00058 for f in self.frames_service().frames:
00059 frame_box.addItem(f)
00060 return frame_box
00061
00062 def make_task_frame_box(self, pbox):
00063 self.frame_box = self.make_frame_box(pbox)
00064 return self.frame_box
00065
00066 def get_posestamped(self):
00067 pose = geo.Pose()
00068 pose.position = geo.Point(*[float(vr.value()) for vr in [self.xline, self.yline, self.zline]])
00069 pose.orientation = geo.Quaternion(*tr.quaternion_from_euler(*[float(np.radians(vr.value())) for vr in [self.phi_line, self.theta_line, self.psi_line]]))
00070 ps = stamp_pose(pose, str(self.frame_box.currentText()))
00071 return ps
00072
00073 def set_posestamped(self, pose_stamped):
00074 for value, vr in zip(position(pose_stamped.pose.position), [self.xline, self.yline, self.zline]):
00075 vr.setValue(value)
00076 for value, vr in zip(tr.euler_from_quaternion(quaternion(pose_stamped.pose.orientation)), [self.phi_line, self.theta_line, self.psi_line]):
00077 vr.setValue(value)
00078 idx = tu.combobox_idx(self.frame_box, pose_stamped.header.frame_id)
00079 self.frame_box.setCurrentIndex(idx)
00080
00081
00082 class ListManager:
00083
00084 def __init__(self, get_current_data_cb, set_current_data_cb, add_element_cb=None, name_preffix='point'):
00085 self.get_current_data_cb = get_current_data_cb
00086 self.set_current_data_cb = set_current_data_cb
00087 self.add_element_cb = add_element_cb
00088 self.name_preffix = name_preffix
00089 self.data_list = []
00090
00091 def item_selection_changed_cb(self):
00092 selected = self.list_widget.selectedItems()
00093 if len(selected) == 0:
00094 return
00095 idx = self._find_index_of(str(selected[0].text()))
00096 self.curr_selected = idx
00097 self.set_current_data_cb(self.data_list[idx]['data'])
00098
00099 def set_default_selection(self):
00100 if len(self.data_list) > 0:
00101 self.list_widget.setCurrentItem(0)
00102
00103 def _find_index_of(self, name):
00104 for idx, tup in enumerate(self.data_list):
00105 if tup['name'] == name:
00106 return idx
00107 return None
00108
00109 def make_widgets(self, parent, connector):
00110 self.list_box = QWidget(parent)
00111 self.list_box_layout = QHBoxLayout(self.list_box)
00112 self.list_box_layout.setMargin(0)
00113
00114 self.list_widget = QListWidget(self.list_box)
00115 connector.connect(self.list_widget, SIGNAL('itemSelectionChanged()'), self.item_selection_changed_cb)
00116 self.list_box_layout.addWidget(self.list_widget)
00117
00118 self.list_widget_buttons = QWidget(parent)
00119 self.lbb_hlayout = QHBoxLayout(self.list_widget_buttons)
00120
00121
00122 base_path = roslib.packages.get_pkg_dir('rcommander_pr2_gui')
00123
00124 icon = QIcon()
00125 icon.addPixmap(QPixmap(pt.join(base_path, "icons/AddButton.png")), QIcon.Normal, QIcon.Off)
00126 self.add_button = QPushButton(self.list_widget_buttons)
00127 self.add_button.setToolTip('Add')
00128 self.add_button.setIcon(icon)
00129 connector.connect(self.add_button, SIGNAL('clicked()'), self.add_cb)
00130
00131 icon = QIcon()
00132 icon.addPixmap(QPixmap(pt.join(base_path, "icons/RemoveButton.png")), QIcon.Normal, QIcon.Off)
00133 self.remove_button = QPushButton(self.list_widget_buttons)
00134 self.remove_button.setToolTip('Remove')
00135 self.remove_button.setIcon(icon)
00136 connector.connect(self.remove_button, SIGNAL('clicked()'), self.remove_pose_cb)
00137
00138 icon = QIcon()
00139 icon.addPixmap(QPixmap(pt.join(base_path, "icons/SaveButton.png")), QIcon.Normal, QIcon.Off)
00140 self.save_button = QPushButton(self.list_widget_buttons)
00141 self.save_button.setToolTip('Save')
00142 self.save_button.setIcon(icon)
00143 connector.connect(self.save_button, SIGNAL('clicked()'), self.save_button_cb)
00144
00145 spacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
00146
00147 icon = QIcon()
00148
00149 icon.addPixmap(QPixmap(pt.join(base_path, "icons/UpButton.png")), QIcon.Normal, QIcon.Off)
00150 self.move_up_button = QPushButton(self.list_widget_buttons)
00151 self.move_up_button.setToolTip('Up')
00152 self.move_up_button.setIcon(icon)
00153 connector.connect(self.move_up_button, SIGNAL('clicked()'), self.move_up_cb)
00154
00155 icon = QIcon()
00156 icon.addPixmap(QPixmap(pt.join(base_path, "icons/DownButton.png")), QIcon.Normal, QIcon.Off)
00157 self.move_down_button = QPushButton(self.list_widget_buttons)
00158 self.move_down_button.setToolTip('Down')
00159 self.move_down_button.setIcon(icon)
00160 connector.connect(self.move_down_button, SIGNAL('clicked()'), self.move_down_cb)
00161
00162 self.lbb_hlayout.addWidget(self.add_button)
00163 self.lbb_hlayout.addWidget(self.remove_button)
00164 self.lbb_hlayout.addWidget(self.save_button)
00165 self.lbb_hlayout.addItem(spacer)
00166 self.lbb_hlayout.addWidget(self.move_up_button)
00167 self.lbb_hlayout.addWidget(self.move_down_button)
00168 self.lbb_hlayout.setContentsMargins(2, 2, 2, 2)
00169 return [self.list_box, self.list_widget_buttons]
00170
00171
00172 def _refill_list_widget(self, data_list):
00173 self.list_widget.clear()
00174 for d in data_list:
00175 self.list_widget.addItem(d['name'])
00176
00177 def _has_name(self, test_name):
00178 for rec in self.data_list:
00179 if rec['name'] == test_name:
00180 return True
00181 else:
00182 return False
00183
00184 def _create_name(self):
00185 idx = len(self.data_list)
00186 tentative_name = self.name_preffix + ('%d' % idx)
00187 while self._has_name(tentative_name):
00188 idx = idx + 1
00189 tentative_name = self.name_preffix + ('%d' % idx)
00190 return tentative_name
00191
00192 def add_cb(self):
00193 name = self._create_name()
00194 self.data_list.append({'name': name,
00195 'data': self.get_current_data_cb()})
00196 self._refill_list_widget(self.data_list)
00197 if self.add_element_cb != None:
00198 self.add_element_cb()
00199
00200 def _selected_idx(self):
00201
00202 selected = self.list_widget.selectedItems()
00203 if len(selected) == 0:
00204 return None
00205 sname = str(selected[0].text())
00206
00207
00208 idx = self._find_index_of(sname)
00209 return idx
00210
00211 def move_up_cb(self):
00212
00213 idx = self._selected_idx()
00214 if idx == None:
00215 return
00216
00217
00218 item = self.data_list.pop(idx)
00219 self.data_list.insert(idx-1, item)
00220
00221
00222 self._refill_list_widget(self.data_list)
00223 self.list_widget.setCurrentItem(self.list_widget.item(idx-1))
00224
00225 def move_down_cb(self):
00226
00227 idx = self._selected_idx()
00228 if idx == None:
00229 return
00230
00231
00232 item = self.data_list.pop(idx)
00233 self.data_list.insert(idx+1, item)
00234
00235
00236 self._refill_list_widget(self.data_list)
00237 self.list_widget.setCurrentItem(self.list_widget.item(idx+1))
00238
00239 def select_default_item(self):
00240 self.list_widget.setCurrentItem(self.list_widget.item(0))
00241
00242 def save_button_cb(self):
00243 idx = self._selected_idx()
00244 if idx == None:
00245 return
00246 el = self.data_list[idx]
00247 self.data_list[idx] = {'name': el['name'],
00248 'data': self.get_current_data_cb()}
00249
00250 def remove_pose_cb(self):
00251 idx = self._selected_idx()
00252 if idx == None:
00253 return
00254 if idx == None:
00255 raise RuntimeError('Inconsistency detected in list')
00256 else:
00257 self.data_list.pop(idx)
00258 self._refill_list_widget(self.data_list)
00259
00260 def get_data(self):
00261
00262 return self.data_list
00263
00264 def set_data(self, data_list):
00265 self.data_list = data_list
00266 self._refill_list_widget(self.data_list)
00267
00268
00269
00270 def position(point):
00271 return [point.x, point.y, point.z]
00272
00273 def quaternion(quaternion):
00274 return [quaternion.x, quaternion.y, quaternion.z, quaternion.w]
00275
00276
00277 def unwrap2(cpos, npos):
00278 two_pi = 2*np.pi
00279 nin = npos % two_pi
00280 n_multiples_2pi = np.floor(cpos/two_pi)
00281 return nin + n_multiples_2pi*two_pi
00282
00283 def standard_rad(t):
00284 if t > 0:
00285 return ((t + np.pi) % (np.pi * 2)) - np.pi
00286 else:
00287 return ((t - np.pi) % (np.pi * -2)) + np.pi
00288
00289
00290
00291
00292 class GenericListener:
00293
00294
00295
00296
00297
00298
00299
00300
00301 def __init__(self, node_name, message_type, listen_channel,
00302 frequency, message_extractor=None, queue_size=None):
00303
00304
00305
00306
00307
00308 self.last_msg_returned = None
00309 self.last_call_back = None
00310 self.delay_tolerance = 1/frequency
00311 self.reading = {'message':None, 'msg_id':-1}
00312 self.curid = 0
00313 self.message_extractor = message_extractor
00314
00315 def callback(*msg):
00316
00317 if 'header' in dir(msg):
00318 if msg.__class__ == ().__class__:
00319 msg_number = msg[0].header.seq
00320 else:
00321 msg_number = msg.header.seq
00322 else:
00323 msg_number = self.curid
00324 self.curid += 1
00325
00326
00327 if len(msg) == 1:
00328 msg = msg[0]
00329
00330 self.reading = {'message':msg, 'msg_id':msg_number}
00331
00332
00333 self.last_call_back = time.time()
00334
00335 if message_type.__class__ == [].__class__:
00336 import message_filters
00337 subscribers = [message_filters.Subscriber(channel, mtype) for channel, mtype in zip(listen_channel, message_type)]
00338 queue_size = 10
00339 ts = message_filters.TimeSynchronizer(subscribers, queue_size)
00340 ts.registerCallback(callback)
00341 else:
00342 rospy.Subscriber(listen_channel, message_type, callback,
00343 queue_size = queue_size)
00344
00345 self.node_name = node_name
00346
00347 rospy.loginfo('%s: subscribed to %s' % (node_name, listen_channel))
00348
00349 def _check_for_delivery_hiccups(self):
00350
00351 if self.last_call_back != None:
00352
00353 time_diff = time.time() - self.last_call_back
00354
00355 if time_diff > self.delay_tolerance:
00356 print self.node_name, ': have not heard back from publisher in', time_diff, 's'
00357
00358 def _wait_for_first_read(self, quiet=False):
00359 if not quiet:
00360 rospy.loginfo('%s: waiting for reading ...' % self.node_name)
00361 while self.reading['message'] == None and not rospy.is_shutdown():
00362 rospy.sleep(0.1)
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372 def read(self, allow_duplication=False, willing_to_wait=True, warn=False, quiet=True):
00373 if allow_duplication:
00374 if willing_to_wait:
00375 raise RuntimeError('Invalid settings for read.')
00376 else:
00377
00378
00379 reading = self.reading
00380 self.last_msg_returned = reading['msg_id']
00381 if self.message_extractor is not None:
00382 return self.message_extractor(reading['message'])
00383 else:
00384 return reading['message']
00385 else:
00386 if willing_to_wait:
00387
00388 self._wait_for_first_read(quiet)
00389 while self.reading['msg_id'] == self.last_msg_returned and not rospy.is_shutdown():
00390 if warn:
00391 self._check_for_delivery_hiccups()
00392 rospy.sleep(1/1000.0)
00393 reading = self.reading
00394 self.last_msg_returned = reading['msg_id']
00395 if self.message_extractor is not None:
00396 return self.message_extractor(reading['message'])
00397 else:
00398 return reading['message']
00399 else:
00400
00401 if self.last_msg_returned == self.reading['msg_id']:
00402 return None
00403 else:
00404 reading = self.reading
00405 self.last_msg_returned = reading['msg_id']
00406 if self.message_extractor is not None:
00407 return self.message_extractor(reading['message'])
00408 else:
00409 return reading['message']
00410
00411
00412 class Joint:
00413
00414 def __init__(self, name, joint_provider):
00415 self.joint_provider = joint_provider
00416 self.joint_names = rospy.get_param('/%s/joints' % name)
00417 self.pub = rospy.Publisher('%s/command' % name, tm.JointTrajectory)
00418 self.names_index = None
00419 self.zeros = [0 for j in range(len(self.joint_names))]
00420
00421 def pose(self, joint_states=None):
00422
00423 if joint_states == None:
00424 joint_states = self.joint_provider()
00425
00426
00427 if self.names_index == None:
00428 self.names_index = {}
00429 for i, n in enumerate(joint_states.name):
00430 self.names_index[n] = i
00431 self.joint_idx = [self.names_index[n] for n in self.joint_names]
00432
00433
00434 return (np.matrix(joint_states.position).T)[self.joint_idx, 0]
00435
00436 def _create_trajectory(self, pos_mat, times, vel_mat=None):
00437
00438 points = [tm.JointTrajectoryPoint() for i in range(pos_mat.shape[1])]
00439 for i in range(pos_mat.shape[1]):
00440 points[i].positions = pos_mat[:,i].A1.tolist()
00441 points[i].accelerations = self.zeros
00442 if vel_mat == None:
00443 points[i].velocities = self.zeros
00444 else:
00445 points[i].velocities = vel_mat[:,i].A1.tolist()
00446
00447 for i in range(pos_mat.shape[1]):
00448 points[i].time_from_start = rospy.Duration(times[i])
00449
00450
00451 jt = tm.JointTrajectory()
00452 jt.joint_names = self.joint_names
00453 jt.points = points
00454 jt.header.stamp = rospy.get_rostime() + rospy.Duration(1.)
00455 return jt
00456
00457 def set_poses(self, pos_mat, times):
00458
00459
00460
00461 joint_trajectory = self._create_trajectory(pos_mat, times)
00462 print 'Sending message', joint_trajectory
00463 self.pub.publish(joint_trajectory)
00464
00465 def get_joint_names(self):
00466 return self.joint_names
00467
00468
00469 class PR2Arm(Joint):
00470
00471 def __init__(self, joint_provider, tf_listener, arm):
00472 joint_controller_name = arm + '_arm_controller'
00473 cart_controller_name = arm + '_cart'
00474 Joint.__init__(self, joint_controller_name, joint_provider)
00475 self.arm = arm
00476 self.tf_listener = tf_listener
00477
00478 self.client = actionlib.SimpleActionClient('/%s/joint_trajectory_action' % joint_controller_name, pm.JointTrajectoryAction)
00479 self.joint_controller_name = joint_controller_name
00480
00481 self.cart_posure_pub = rospy.Publisher("/%s/command_posture" % cart_controller_name, stdm.Float64MultiArray).publish
00482 self.cart_pose_pub = rospy.Publisher("/%s/command_pose" % cart_controller_name, gm.PoseStamped).publish
00483 if arm == 'l':
00484 self.full_arm_name = 'left'
00485 else:
00486 self.full_arm_name = 'right'
00487
00488
00489
00490
00491
00492 self.limits_dict, self.vel_limit_dict = self._limits()
00493
00494 self.POSTURES = {
00495 'off': np.matrix([]),
00496 'mantis': np.matrix([0, 1, 0, -1, 3.14, -1, 3.14]).T,
00497 'elbowupr': np.matrix([-0.79,0,-1.6, 9999, 9999, 9999, 9999]).T,
00498 'elbowupl': np.matrix([0.79,0,1.6 , 9999, 9999, 9999, 9999]).T,
00499 'old_elbowupr': np.matrix([-0.79,0,-1.6, -0.79,3.14, -0.79,5.49]).T,
00500 'old_elbowupl': np.matrix([0.79,0,1.6, -0.79,3.14, -0.79,5.49]).T,
00501 'elbowdownr': np.matrix([-0.028262077316910873, 1.2946342642324222, -0.25785640577652386, -1.5498884526859626]).T,
00502 'elbowdownl': np.matrix([-0.0088195719039858515, 1.2834828245284853, 0.20338442004843196, -1.5565279256852611]).T
00503 }
00504
00505 def get_limits(self):
00506 return self.limits_dict
00507
00508 def get_vel_limits(self):
00509 return self.vel_limit_dict
00510
00511 def _limits(self):
00512 service_name = '/pr2_%s_arm_kinematics/get_ik_solver_info' % self.full_arm_name
00513 print service_name
00514 proxy = rospy.ServiceProxy(service_name, GetKinematicSolverInfo)
00515
00516
00517 info = proxy().kinematic_solver_info
00518 limit_dict = {}
00519 vel_limit_dict = {}
00520 for idx, name in enumerate(info.joint_names):
00521 limit = info.limits[idx]
00522 if limit.has_position_limits:
00523 limit_dict[name] = [limit.min_position, limit.max_position]
00524 if limit.has_velocity_limits:
00525 vel_limit_dict[name] = limit.max_velocity
00526 return limit_dict, vel_limit_dict
00527
00528 def set_posture(self, posture_mat):
00529 self.cart_posure_pub(stdm.Float64MultiArray(data=posture_mat.A1.tolist()))
00530
00531
00532
00533
00534
00535
00536
00537 def set_cart_pose(self, trans, rot, frame, msg_time):
00538 ps = gm.PoseStamped()
00539 for i, field in enumerate(['x', 'y', 'z']):
00540 exec('ps.pose.position.%s = trans[%d]' % (field, i))
00541 for i, field in enumerate(['x', 'y', 'z', 'w']):
00542 exec('ps.pose.orientation.%s = rot[%d]' % (field, i))
00543 ps.header.frame_id = frame
00544 ps.header.stamp = rospy.Time(msg_time)
00545 self.cart_pose_pub(ps)
00546
00547
00548
00549
00550 def set_poses(self, pos_mat, times, vel_mat=None, block=True):
00551
00552
00553
00554
00555
00556
00557 pos_mat = np.column_stack([self.pose(), pos_mat])
00558
00559 times = np.concatenate(([0], times))
00560 times = times + .1
00561
00562 joint_traj = Joint._create_trajectory(self, pos_mat, times, vel_mat)
00563
00564
00565
00566 g = pm.JointTrajectoryGoal()
00567 g.trajectory = joint_traj
00568
00569 self.client.send_goal(g)
00570 if block:
00571 return self.client.wait_for_result()
00572 return self.client.get_state()
00573
00574 def stop_trajectory_execution(self):
00575 self.client.cancel_all_goals()
00576
00577 def has_active_goal(self):
00578 s = self.client.get_state()
00579 if s == amsg.GoalStatus.ACTIVE or s == amsg.GoalStatus.PENDING:
00580 return True
00581 else:
00582 return False
00583
00584 def set_poses_monitored(self, pos_mat, times, vel_mat=None, block=True, time_look_ahead=.050):
00585 joint_traj = Joint._create_trajectory(self, pos_mat, times, vel_mat)
00586
00587
00588
00589 g = pm.JointTrajectoryGoal()
00590 g.trajectory = joint_traj
00591 self.client.send_goal(g)
00592 if block:
00593 return self.client.wait_for_result()
00594 return self.client.get_state()
00595
00596 def set_pose(self, pos, nsecs=5., block=True):
00597 for i in range(2):
00598 cpos = self.pose()
00599 pos[4,0] = unwrap(cpos[4,0], pos[4,0])
00600 pos[6,0] = unwrap(cpos[6,0], pos[6,0])
00601 self.set_poses(np.column_stack([pos]), np.array([nsecs]), block=block)
00602
00603
00604 def pose_cartesian(self, frame='base_link'):
00605 gripper_tool_frame = self.arm + '_gripper_tool_frame'
00606 return tfu.transform(frame, gripper_tool_frame, self.tf_listener)
00607
00608 def pose_cartesian_tf(self, frame='base_link'):
00609 p, r = tfu.matrix_as_tf(self.pose_cartesian(frame))
00610 return np.matrix(p).T, np.matrix(r).T
00611
00612
00613 class PR2Torso(Joint):
00614
00615 def __init__(self, joint_provider):
00616 Joint.__init__(self, 'torso_controller', joint_provider)
00617 self.torso = actionlib.SimpleActionClient('torso_controller/position_joint_action', pm.SingleJointPositionAction)
00618 rospy.loginfo('waiting for torso_controller')
00619 self.torso.wait_for_server()
00620
00621 def set_pose(self, p, block=True):
00622 self.torso.send_goal(pm.SingleJointPositionGoal(position = p))
00623 if block:
00624 self.torso.wait_for_result()
00625 return self.torso.get_state()
00626
00627
00628 class PR2Head(Joint):
00629
00630 def __init__(self, joint_provider):
00631 Joint.__init__(self, 'head_traj_controller', joint_provider)
00632
00633 def set_pose(self, pos, length=5., block=False):
00634 for i in range(2):
00635 cpos = self.pose()
00636 MIN_TIME = .1
00637 self.set_poses(np.column_stack([cpos, pos]), np.array([MIN_TIME, MIN_TIME+length]))
00638
00639 class PR2:
00640
00641 def __init__(self, tf_listener):
00642
00643
00644
00645 self.tf_listener = tf_listener
00646 jl = GenericListener('joint_state_listener', sm.JointState, 'joint_states', 100)
00647 joint_provider = ft.partial(jl.read, allow_duplication=False, willing_to_wait=True, warn=False, quiet=True)
00648 self.left = PR2Arm(joint_provider, tf_listener, 'l')
00649 self.right = PR2Arm(joint_provider, tf_listener, 'r')
00650 self.torso = PR2Torso(joint_provider)
00651 self.head = PR2Head(joint_provider)
00652 self.controller_manager = ControllerManager()
00653