00001 import rospy
00002 from PyQt4.QtGui import *
00003 from PyQt4.QtCore import *
00004 import rcommander.tool_utils as tu
00005 import actionlib_msgs.msg as am
00006 import numpy as np
00007 import smach
00008 import roslib
00009 import os.path as pt
00010
00011 class MoveHeadTool(tu.ToolBase):
00012
00013 def __init__(self, rcommander):
00014 tu.ToolBase.__init__(self, rcommander, 'move_head_ang', 'Move Head (ang)', MoveHeadState)
00015 self.head_angs_list = None
00016 self.joint_names = ["head_pan_joint", "head_tilt_joint"]
00017 self.status_bar_timer = QTimer()
00018 self.rcommander.connect(self.status_bar_timer, SIGNAL('timeout()'), self.get_current_joint_angles_cb)
00019
00020 def fill_property_box(self, pbox):
00021 formlayout = pbox.layout()
00022
00023 joint_names = self.rcommander.robot.head.get_joint_names()
00024 self.joint_boxes = []
00025 for jname in joint_names:
00026
00027 exec("self.%s = QDoubleSpinBox(pbox)" % jname)
00028 exec('box = self.%s' % jname)
00029 box.setMinimum(-9999.)
00030 box.setMaximum(9999.)
00031 box.setSingleStep(1.)
00032 self.joint_boxes.append(box)
00033 formlayout.addRow('&%s' % jname, box)
00034
00035 self.head_angs_list = []
00036 self.time_box = QDoubleSpinBox(pbox)
00037 self.time_box.setMinimum(0)
00038 self.time_box.setMaximum(1000.)
00039 self.time_box.setSingleStep(.5)
00040 formlayout.addRow('&Time', self.time_box)
00041
00042 self.update_checkbox = QCheckBox(pbox)
00043 self.update_checkbox.setTristate(False)
00044 formlayout.addRow('&Live Update', self.update_checkbox)
00045 self.rcommander.connect(self.update_checkbox, SIGNAL('stateChanged(int)'), self.update_selected_cb)
00046
00047 self.current_pose_button = QPushButton(pbox)
00048 self.current_pose_button.setText('Current Pose')
00049 self.rcommander.connect(self.current_pose_button,
00050 SIGNAL('clicked()'), self.get_current_joint_angles_cb)
00051 formlayout.addRow(self.current_pose_button)
00052
00053
00054 self.list_box = QWidget(pbox)
00055 self.list_box_layout = QHBoxLayout(self.list_box)
00056 self.list_box_layout.setMargin(0)
00057
00058 self.list_widget = QListWidget(self.list_box)
00059 self.rcommander.connect(self.list_widget, SIGNAL('itemSelectionChanged()'), self.item_selection_changed_cb)
00060 self.list_box_layout.addWidget(self.list_widget)
00061
00062
00063
00064
00065
00066 self.list_widget_buttons = QWidget(pbox)
00067 self.list_widget_buttons2 = QWidget(pbox)
00068 self.lbb_hlayout = QHBoxLayout(self.list_widget_buttons)
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 self.move_up_button = QPushButton(self.list_widget_buttons)
00090 self.move_up_button.setText("")
00091 icon = QIcon()
00092 base_path = roslib.packages.get_pkg_dir('rcommander_pr2_gui')
00093 icon.addPixmap(QPixmap(pt.join(base_path, "icons/UpButton.png")), QIcon.Normal, QIcon.Off)
00094 self.move_up_button.setIcon(icon)
00095 self.move_up_button.setObjectName("up_button")
00096
00097
00098 self.rcommander.connect(self.move_up_button, SIGNAL('clicked()'), self.move_up_cb)
00099
00100 self.move_up_button.setToolTip('Move Up')
00101
00102 self.move_down_button = QPushButton(self.list_widget_buttons)
00103 self.move_down_button.setText("")
00104 icon = QIcon()
00105 icon.addPixmap(QPixmap(pt.join(base_path,"icons/DownButton.png")), QIcon.Normal, QIcon.Off)
00106 self.move_down_button.setIcon(icon)
00107 self.move_down_button.setObjectName("down_button")
00108
00109
00110 self.rcommander.connect(self.move_down_button, SIGNAL('clicked()'), self.move_down_cb)
00111
00112
00113 self.move_down_button.setToolTip('Move Down')
00114
00115 self.add_head_set_button = QPushButton(self.list_widget_buttons)
00116 self.add_head_set_button.setText("")
00117 icon = QIcon()
00118 icon.addPixmap(QPixmap(pt.join(base_path, "icons/AddButton.png")), QIcon.Normal, QIcon.Off)
00119 self.add_head_set_button.setIcon(icon)
00120 self.add_head_set_button.setObjectName("add_button")
00121
00122
00123 self.rcommander.connect(self.add_head_set_button, SIGNAL('clicked()'), self.add_head_set_cb)
00124 self.add_head_set_button.setToolTip('Add')
00125
00126 self.remove_head_set_button = QPushButton(self.list_widget_buttons)
00127 self.remove_head_set_button.setText("")
00128 icon = QIcon()
00129 icon.addPixmap(QPixmap(pt.join(base_path, "icons/RemoveButton.png")), QIcon.Normal, QIcon.Off)
00130 self.remove_head_set_button.setIcon(icon)
00131 self.remove_head_set_button.setObjectName("remove_button")
00132
00133
00134 self.rcommander.connect(self.remove_head_set_button, SIGNAL('clicked()'), self.remove_pose_cb)
00135 self.remove_head_set_button.setToolTip('Remove')
00136
00137 self.save_button = QPushButton(self.list_widget_buttons)
00138 self.save_button.setText("")
00139 icon = QIcon()
00140 icon.addPixmap(QPixmap(pt.join(base_path, "icons/SaveButton.png")), QIcon.Normal, QIcon.Off)
00141 self.save_button.setIcon(icon)
00142 self.save_button.setObjectName("save_button")
00143
00144
00145 self.rcommander.connect(self.save_button, SIGNAL('clicked()'), self.save_button_cb)
00146 self.save_button.setToolTip('Save')
00147
00148 spacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
00149 self.lbb_hlayout.addWidget(self.add_head_set_button)
00150 self.lbb_hlayout.addWidget(self.remove_head_set_button)
00151 self.lbb_hlayout.addWidget(self.save_button)
00152 self.lbb_hlayout.addItem(spacer)
00153 self.lbb_hlayout.addWidget(self.move_up_button)
00154 self.lbb_hlayout.addWidget(self.move_down_button)
00155 self.lbb_hlayout.setContentsMargins(2, 2, 2, 2)
00156
00157 formlayout.addRow('\n', self.list_box)
00158 formlayout.addRow('&Sequence:', self.list_box)
00159 formlayout.addRow(self.list_box)
00160 formlayout.addRow(self.list_widget_buttons)
00161
00162 self.reset()
00163
00164 def _refill_list_widget(self, joints_list):
00165 self.list_widget.clear()
00166 for d in joints_list:
00167 self.list_widget.addItem(d['name'])
00168
00169 def get_current_joint_angles_cb(self):
00170 poses = self.rcommander.robot.head.pose()
00171 self.joint_boxes[0].setValue(np.degrees(poses[0,0]))
00172 self.joint_boxes[1].setValue(np.degrees(poses[1,0]))
00173
00174 def set_node_properties(self, my_node):
00175 self.joint_boxes[0].setValue(np.degrees(my_node.poses[0,0]))
00176 self.joint_boxes[1].setValue(np.degrees(my_node.poses[1,0]))
00177
00178 self.head_angs_list = my_node.joint_waypoints
00179 self._refill_list_widget(my_node.joint_waypoints)
00180 self.list_widget.setCurrentItem(self.list_widget.item(0))
00181
00182
00183 def item_selection_changed_cb(self):
00184 selected = self.list_widget.selectedItems()
00185 if len(selected) == 0:
00186 return
00187 idx = self._find_index_of(str(selected[0].text()))
00188 head_angs = self.head_angs_list[idx]['angs']
00189 self._set_joints_to_fields(head_angs)
00190 self.time_box.setValue(self.head_angs_list[idx]['time'])
00191
00192 def _set_joints_to_fields(self, joints):
00193 for idx, name in enumerate(self.joint_names):
00194 deg = np.degrees(joints[idx])
00195 exec('line_edit = self.%s' % name)
00196 line_edit.setValue(deg)
00197
00198
00199 def _has_name(self, test_name):
00200 for rec in self.head_angs_list:
00201 if rec['name'] == test_name:
00202 return True
00203 else:
00204 return False
00205
00206
00207 def move_up_cb(self):
00208
00209 idx = self._selected_idx()
00210 if idx == None:
00211 return
00212
00213
00214 item = self.head_angs_list.pop(idx)
00215 self.head_angs_list.insert(idx-1, item)
00216
00217
00218 self._refill_list_widget(self.head_angs_list)
00219 self.list_widget.setCurrentItem(self.list_widget.item(idx-1))
00220
00221
00222 def move_down_cb(self):
00223
00224 idx = self._selected_idx()
00225 if idx == None:
00226 return
00227
00228
00229 item = self.head_angs_list.pop(idx)
00230 self.head_angs_list.insert(idx+1, item)
00231
00232
00233 self._refill_list_widget(self.head_angs_list)
00234 self.list_widget.setCurrentItem(self.list_widget.item(idx+1))
00235
00236
00237 def add_head_set_cb(self):
00238
00239 name = self._create_name()
00240
00241 self.head_angs_list.append({'name':name, 'time': self.time_box.value(), 'angs': self._read_head_position_from_fields()})
00242 self._refill_list_widget(self.head_angs_list)
00243
00244
00245 def save_button_cb(self):
00246 idx = self._selected_idx()
00247 if idx == None:
00248 return
00249 el = self.head_angs_list[idx]
00250 self.head_angs_list[idx] = {'name': el['name'],
00251 'time': self.time_box.value(),
00252 'angs': self._read_head_position_from_fields()}
00253
00254
00255 def _read_head_position_from_fields(self):
00256 headPos = []
00257 for name in self.joint_names:
00258
00259 exec('rad = np.radians(self.%s.value())' % name)
00260 headPos.append(rad)
00261 return headPos
00262
00263
00264 def remove_pose_cb(self):
00265 idx = self._selected_idx()
00266 if idx == None:
00267 return
00268
00269
00270 if idx == None:
00271 raise RuntimeError('Inconsistency detected in list')
00272 else:
00273 self.head_angs_list.pop(idx)
00274 self._refill_list_widget(self.head_angs_list)
00275
00276
00277 def _create_name(self):
00278 idx = len(self.head_angs_list)
00279 tentative_name = 'point%d' % idx
00280
00281 while self._has_name(tentative_name):
00282 idx = idx + 1
00283 tentative_name = 'point%d' % idx
00284
00285 return tentative_name
00286
00287
00288 def _selected_idx(self):
00289
00290 selected = self.list_widget.selectedItems()
00291 if len(selected) == 0:
00292 return None
00293 sname = str(selected[0].text())
00294
00295
00296 idx = self._find_index_of(sname)
00297 return idx
00298
00299
00300 def _find_index_of(self, name):
00301 for idx, tup in enumerate(self.head_angs_list):
00302 if tup['name'] == name:
00303 return idx
00304 return None
00305
00306 def reset(self):
00307 for box in self.joint_boxes:
00308 box.setValue(0)
00309 self.time_box.setValue(1.)
00310 self.update_checkbox.setCheckState(False)
00311 self.status_bar_timer.stop()
00312 self.current_pose_button.setEnabled(True)
00313
00314 def new_node(self, name=None):
00315 if name == None:
00316 nname = self.name + str(self.counter)
00317 else:
00318 nname = name
00319
00320 poses = np.matrix([np.radians(self.joint_boxes[0].value()),
00321 np.radians(self.joint_boxes[1].value())]).T
00322 return MoveHeadState(nname, poses, self.head_angs_list)
00323
00324
00325 def save_button_cb(self):
00326 idx = self._selected_idx()
00327 if idx == None:
00328 return
00329 el = self.head_angs_list[idx]
00330 self.head_angs_list[idx] = {'name': el['name'],
00331 'time': self.time_box.value(),
00332 'angs': self._read_head_position_from_fields()}
00333
00334 def update_selected_cb(self, state):
00335
00336 if state == 2:
00337 self.status_bar_timer.start(30)
00338 self.current_pose_button.setEnabled(False)
00339 self.get_current_joint_angles_cb()
00340
00341 if state == 0:
00342 self.status_bar_timer.stop()
00343 self.current_pose_button.setEnabled(True)
00344
00345
00346 def move_up_cb(self):
00347
00348 idx = self._selected_idx()
00349 if idx == None:
00350 return
00351
00352
00353 item = self.head_angs_list.pop(idx)
00354 self.head_angs_list.insert(idx-1, item)
00355
00356
00357 self._refill_list_widget(self.head_angs_list)
00358 self.list_widget.setCurrentItem(self.list_widget.item(idx-1))
00359
00360 def get_current_joint_angles(self):
00361 pose_mat = rcommander.robot.head.pose()
00362
00363 for idx, name in enumerate(self.joint_name_fields):
00364 deg = np.degrees(pose_mat[idx, 0])
00365 exec('line_edit = self.%s' % name)
00366
00367 line_edit.setValue(deg)
00368
00369 def move_down_cb(self):
00370
00371 idx = self._selected_idx()
00372 if idx == None:
00373 return
00374
00375
00376 item = self.head_angs_list.pop(idx)
00377 self.head_angs_list.insert(idx+1, item)
00378
00379
00380 self._refill_list_widget(self.head_angs_list)
00381 self.list_widget.setCurrentItem(self.list_widget.item(idx+1))
00382
00383
00384 class MoveHeadState(tu.StateBase):
00385
00386 def __init__(self, name, poses, joint_waypoints):
00387 tu.StateBase.__init__(self, name)
00388 self.poses = poses
00389 self.joint_waypoints = joint_waypoints
00390
00391 def get_smach_state(self):
00392 return MoveHeadStateSmach(self.poses, self.joint_waypoints)
00393
00394
00395 class MoveHeadStateSmach(smach.State):
00396
00397 TIME_OUT_FACTOR = 3.
00398
00399 def __init__(self, poses, joint_waypoints):
00400 smach.State.__init__(self, outcomes=['preempted', 'done', 'None'],
00401 input_keys=[], output_keys=[])
00402 self.joint_waypoints = joint_waypoints
00403 self.poses = poses
00404
00405 def set_robot(self, robot):
00406 if robot != None:
00407 self.head_obj = robot.head
00408 self.controller_manager = robot.controller_manager
00409
00410 def execute(self, userdata):
00411
00412
00413
00414 times = []
00415 wps = []
00416 for x in self.joint_waypoints:
00417 wps.append(np.matrix(x['angs']).T)
00418 times.append(x['time'])
00419
00420 self.head_obj.set_poses(np.column_stack(wps), np.cumsum(np.array(times)))
00421
00422
00423
00424
00425 trajectory_time_out = MoveHeadStateSmach.TIME_OUT_FACTOR * np.sum(times)
00426 succeeded = False
00427 preempted = False
00428 r = rospy.Rate(30)
00429 start_time = rospy.get_time()
00430 while (rospy.get_time() - start_time) < trajectory_time_out:
00431 r.sleep()
00432 if self.preempt_requested():
00433 self.services_preempt()
00434 return 'preempted'
00435 return 'done'
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446