joint_slider.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2012 Shadow Robot Company Ltd.
00004 #
00005 # This program is free software: you can redistribute it and/or modify it
00006 # under the terms of the GNU General Public License as published by the Free
00007 # Software Foundation, either version 2 of the License, or (at your option)
00008 # any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful, but WITHOUT
00011 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00013 # more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 #
00018 
00019 import os
00020 import rospkg
00021 import rospy
00022 import math
00023 
00024 from xml.etree import ElementTree as ET
00025 
00026 from qt_gui.plugin import Plugin
00027 from python_qt_binding import loadUi
00028 
00029 from QtCore import Qt
00030 from QtGui import QWidget, QMessageBox
00031 
00032 from controller_manager_msgs.srv import ListControllers
00033 from control_msgs.msg import JointControllerState
00034 from sr_robot_msgs.msg import JointControllerState as SrJointControllerState
00035 from sr_robot_msgs.msg import JointMusclePositionControllerState
00036 from control_msgs.msg import JointTrajectoryControllerState
00037 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
00038 
00039 from sr_gui_joint_slider.sliders import JointController, Joint, EtherCATHandSlider
00040 from sr_gui_joint_slider.sliders import EtherCATHandTrajectorySlider, EtherCATSelectionSlider
00041 
00042 
00043 class SrGuiJointSlider(Plugin):
00044 
00045     """
00046     A rosgui plugin to change the position of the different joints
00047     """
00048 
00049     # For each controller type this defines the category of controller it belongs to
00050     # (position, velocity, effort, position_trajectory)
00051     # and the msg type of the controller state topic
00052     controller_state_types = {
00053         "sr_mechanism_controllers/SrhJointPositionController": ("position", JointControllerState),
00054         "sr_mechanism_controllers/SrhEffortJointController": ("effort", JointControllerState),
00055         "sr_mechanism_controllers/SrhJointVelocityController": ("velocity", JointControllerState),
00056         "sr_mechanism_controllers/SrhMixedPositionVelocityJointController": ("position", SrJointControllerState),
00057         "sr_mechanism_controllers/SrhMuscleJointPositionController": ("position", JointMusclePositionControllerState),
00058         "position_controllers/JointTrajectoryController": ("position_trajectory", JointTrajectoryControllerState),
00059         "effort_controllers/JointTrajectoryController": ("position_trajectory", JointTrajectoryControllerState)}
00060 
00061     def __init__(self, context):
00062         super(SrGuiJointSlider, self).__init__(context)
00063         self.setObjectName('SrGuiJointSlider')
00064 
00065         self._robot_description_xml_root = None
00066 
00067         self._widget = QWidget()
00068 
00069         ui_file = os.path.join(rospkg.RosPack().get_path(
00070             'sr_gui_joint_slider'), 'uis', 'SrJointSlider.ui')
00071         loadUi(ui_file, self._widget)
00072 
00073         self._widget.setObjectName('SrJointSliderUi')
00074         context.add_widget(self._widget)
00075 
00076         self.joints = []
00077 
00078         self.sliders = []
00079         self.selection_slider = None
00080 
00081         # to be used by trajectory controller sliders
00082         self.trajectory_state_sub = []
00083         # self.trajectory_state = []
00084         self.trajectory_state_slider_cb = []
00085         self.trajectory_pub = []
00086         self.trajectory_target = []
00087 
00088         self._widget.reloadButton.pressed.connect(
00089             self.on_reload_button_cicked_)
00090         self._widget.refreshButton.pressed.connect(
00091             self.on_refresh_button_cicked_)
00092         self._widget.sliderReleaseCheckBox.stateChanged.connect(
00093             self.on_slider_release_checkbox_clicked_)
00094 
00095         self._widget.reloadButton.setEnabled(True)
00096         self.on_reload_button_cicked_()
00097 
00098     def _unregister(self):
00099         pass
00100 
00101     def shutdown_plugin(self):
00102         self._unregister()
00103 
00104     def save_settings(self, global_settings, perspective_settings):
00105         pass
00106 
00107     def restore_settings(self, global_settings, perspective_settings):
00108         pass
00109 
00110     def on_robot_type_changed_(self):
00111         pass
00112 
00113     def on_reload_button_cicked_(self):
00114         """
00115         Clear existing slider widgets from layout
00116         Load the correct robot library
00117         Create and load the new slider widgets
00118         """
00119 
00120         self._load_robot_description()
00121         controllers = self.get_current_controllers()
00122 
00123         self.joints = self._create_joints(controllers)
00124 
00125         self.delete_old_sliders_()
00126 
00127         self._widget.sliderReleaseCheckBox.setCheckState(Qt.Unchecked)
00128 
00129         self.load_new_sliders_()
00130 
00131     def on_refresh_button_cicked_(self):
00132         """
00133         Call refresh for every slider
00134         """
00135         for slider in self.sliders:
00136             slider.refresh()
00137 
00138     def on_slider_release_checkbox_clicked_(self, state):
00139         """
00140         Set tracking behaviour of each slider to false if checkbox is checked, true otherwise
00141         """
00142 
00143         if state == Qt.Checked:
00144             for slider in self.sliders:
00145                 slider.set_new_slider_behaviour(False)
00146         else:
00147             for slider in self.sliders:
00148                 slider.set_new_slider_behaviour(True)
00149 
00150     def delete_old_sliders_(self):
00151         """
00152         Clear existing slider widgets from layout
00153         Empty the slider list
00154         """
00155         for old_slider in self.sliders:
00156             self._widget.horizontalLayout.removeWidget(old_slider)
00157             old_slider.close()
00158             old_slider.deleteLater()
00159 
00160         self.sliders = []
00161 
00162         if(self.selection_slider is not None):
00163             self._widget.horizontalLayout.removeWidget(self.selection_slider)
00164             self.selection_slider.close()
00165             self.selection_slider.deleteLater()
00166             self.selection_slider = None
00167 
00168     def load_new_sliders_(self):
00169         """
00170         Create the new slider widgets
00171         Load the new slider
00172         Put the slider in the list
00173         """
00174         self.sliders = list()
00175         for joint in self.joints:
00176             slider = None
00177             slider_ui_file = os.path.join(
00178                 rospkg.RosPack().get_path('sr_gui_joint_slider'), 'uis', 'Slider.ui')
00179 
00180             try:
00181                 if joint.controller.controller_category == "position_trajectory":
00182                     slider = EtherCATHandTrajectorySlider(
00183                         joint, slider_ui_file, self, self._widget.scrollAreaWidgetContents)
00184                 else:
00185                     slider = EtherCATHandSlider(
00186                         joint, slider_ui_file, self, self._widget.scrollAreaWidgetContents)
00187             except Exception, e:
00188                 rospy.loginfo(e)
00189 
00190             if slider is not None:
00191                 slider.setMaximumWidth(100)
00192                 # Load the new slider
00193                 self._widget.horizontalLayout.addWidget(slider)
00194                 # Put the slider in the list
00195                 self.sliders.append(slider)
00196 
00197         # Create the slider to move all the selected joint sliders
00198         selection_slider_ui_file = os.path.join(
00199             rospkg.RosPack().get_path('sr_gui_joint_slider'), 'uis', 'SelectionSlider.ui')
00200         self.selection_slider = EtherCATSelectionSlider(
00201             "Change sel.", 0, 100, selection_slider_ui_file, self, self._widget.scrollAreaWidgetContents)
00202 
00203         self.selection_slider.setMaximumWidth(100)
00204         self._widget.horizontalLayout.addWidget(self.selection_slider)
00205 
00206     def get_current_controllers(self):
00207         """
00208         @return: list of current controllers with associated data
00209         """
00210         success = True
00211         list_controllers = rospy.ServiceProxy(
00212             'controller_manager/list_controllers', ListControllers)
00213         try:
00214             resp1 = list_controllers()
00215         except rospy.ServiceException:
00216             success = False
00217 
00218         if success:
00219             return [c for c in resp1.controller if c.state == "running"]
00220         else:
00221             rospy.loginfo(
00222                 "Couldn't get list of controllers from controller_manager/list_controllers service")
00223             return []
00224 
00225     def _load_robot_description(self):
00226         """
00227         Load the description from the param named in the edit as an ET element.
00228         Sets self._robot_description_xml_root to the element.
00229         """
00230         name = self._widget.robot_description_edit.text()
00231         self._robot_description_xml_root = None
00232         try:
00233             xml = rospy.get_param(name)
00234             self._robot_description_xml_root = ET.fromstring(xml)
00235         except KeyError as e:
00236             rospy.logerr(
00237                 "Failed to get robot description from param %s : %s" % (name, e))
00238             return
00239         except:
00240             raise
00241 
00242     def _get_joint_min_max_vel(self, jname):
00243         """Get the min and max from the robot description for a given joint."""
00244         root = self._robot_description_xml_root
00245         if root is not None:
00246             joint_type = root.findall(".joint[@name='" + jname + "']")[0].attrib['type']
00247             if joint_type == "continuous":
00248                 limit = root.findall(".//joint[@name='" + jname + "']/limit")
00249                 if limit is None or len(limit) == 0:
00250                     return (-math.pi,
00251                             math.pi,
00252                             3.0)  # A default speed
00253                 else:
00254                     return (-math.pi,
00255                             math.pi,
00256                             float(limit[0].attrib['velocity']))
00257             else:
00258                 limit = root.findall(".//joint[@name='" + jname + "']/limit")
00259                 if limit is None or len(limit) == 0:
00260                     # Handles upper case joint names in the model. e.g. the E1
00261                     # shadowhand
00262                     limit = root.findall(
00263                         ".//joint[@name='" + jname.upper() + "']/limit")
00264                 if limit is not None and len(limit) > 0:
00265                     return (float(limit[0].attrib['lower']),
00266                             float(limit[0].attrib['upper']),
00267                             float(limit[0].attrib['velocity']))
00268                 else:
00269                     rospy.logerr("Limit not found for joint %s", jname)
00270         else:
00271             rospy.logerr("robot_description_xml_root == None")
00272         return (None, None, None)
00273 
00274     def _get_joint_min_max_vel_special(self, jname):
00275         if "J0" in jname:
00276             jname1 = jname.replace("J0", "J1")
00277             jname2 = jname.replace("J0", "J2")
00278             min1, max1, vel1 = self._get_joint_min_max_vel(jname1)
00279             min2, max2, vel2 = self._get_joint_min_max_vel(jname2)
00280             return (min1 + min2, max1 + max2, vel1 + vel2)
00281         else:
00282             return self._get_joint_min_max_vel(jname)
00283 
00284     def _create_joints(self, controllers):
00285         joints = []
00286         trajectory_ctrl_joint_names = []
00287         self.trajectory_target = []
00288         self.trajectory_state_sub = []
00289         # self.trajectory_state = []
00290         self.trajectory_state_slider_cb = []
00291         self.trajectory_pub = []
00292 
00293         for controller in controllers:
00294             if controller.type == "position_controllers/JointTrajectoryController":
00295                 for j_name in controller.resources:
00296                     trajectory_ctrl_joint_names.append(j_name)
00297 
00298         for controller in controllers:
00299             if rospy.has_param(controller.name):
00300                 ctrl_params = rospy.get_param(controller.name)
00301                 controller_type = ctrl_params["type"]
00302                 if controller_type in self.controller_state_types:
00303                     controller_state_type = self.controller_state_types[
00304                         controller_type][1]
00305                     controller_category = self.controller_state_types[
00306                         controller_type][0]
00307 
00308                     if controller_category == "position_trajectory":
00309                         # for a trajectory controller we will load a slider for every resource it manages
00310                         self.trajectory_target.append(JointTrajectory())
00311                         self.trajectory_state_sub.append(
00312                             rospy.Subscriber(controller.name + "/state", controller_state_type,
00313                                              self._trajectory_state_cb,
00314                                              callback_args=len(self.trajectory_state_sub)))
00315 
00316                         self.trajectory_state_slider_cb.append([])
00317                         self.trajectory_pub.append(
00318                             rospy.Publisher(controller.name + "/command", JointTrajectory, queue_size=1, latch=True))
00319                         for j_name in controller.resources:
00320                             joint_controller = JointController(
00321                                 controller.name, controller_type, controller_state_type, controller_category,
00322                                 self.trajectory_state_slider_cb[
00323                                     len(self.trajectory_state_slider_cb) - 1],
00324                                 self.trajectory_pub[
00325                                     len(self.trajectory_pub) - 1],
00326                                 self.trajectory_target[len(self.trajectory_target) - 1])
00327                             rospy.loginfo(
00328                                 "controller category: %s", controller_category)
00329 
00330                             if self._widget.joint_name_filter_edit.text() not in j_name:
00331                                 continue
00332 
00333                             min, max, vel = self._get_joint_min_max_vel_special(
00334                                 j_name)
00335                             joint = Joint(
00336                                 j_name, min, max, vel, joint_controller)
00337                             joints.append(joint)
00338                     else:
00339                         joint_name = ctrl_params["joint"]
00340                         if joint_name in trajectory_ctrl_joint_names:
00341                             # These joints are controlled by the trajectory controller
00342                             continue
00343 
00344                         if "J0" in joint_name:  # xxJ0 are controlled by the by the trajectory controller xxJ1 and xxJ2
00345                             jname1 = joint_name.replace("J0", "J1")
00346                             jname2 = joint_name.replace("J0", "J2")
00347                             if jname1 in trajectory_ctrl_joint_names \
00348                                     and jname2 in trajectory_ctrl_joint_names:
00349                                 continue
00350 
00351                         joint_controller = JointController(
00352                             controller.name, controller_type, controller_state_type, controller_category)
00353                         rospy.loginfo(
00354                             "controller category: %s", controller_category)
00355 
00356                         if self._widget.joint_name_filter_edit.text() not in joint_name:
00357                             continue
00358 
00359                         min, max, vel = self._get_joint_min_max_vel_special(
00360                             joint_name)
00361                         joint = Joint(
00362                             joint_name, min, max, vel, joint_controller)
00363                         joints.append(joint)
00364                 else:
00365                     rospy.logwarn(
00366                         "Controller %s of type %s not supported", controller.name, controller_type)
00367                     continue
00368             else:
00369                 rospy.logwarn(
00370                     "Parameters for controller %s not found", controller.name)
00371                 continue
00372 
00373         return joints
00374 
00375     def _trajectory_state_cb(self, msg, index):
00376         if not self.trajectory_target[index].joint_names:  # Initialize the targets with the current position
00377             self.trajectory_target[index].joint_names = msg.joint_names
00378             point = JointTrajectoryPoint()
00379             point.positions = list(msg.actual.positions)  # This is a list for some reason? Should be tuple..
00380             point.velocities = [0] * len(msg.joint_names)
00381             point.time_from_start = rospy.Duration.from_sec(0.005)
00382             self.trajectory_target[index].points = [point]
00383 
00384         for cb in self.trajectory_state_slider_cb[index]:  # call the callbacks of the sliders in the list
00385             cb(msg)


sr_gui_joint_slider
Author(s): Ugo Cupcic
autogenerated on Thu Jun 6 2019 21:13:55