sr_hand_calibration_model.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2011 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 roslib
00020 roslib.load_manifest('sr_gui_hand_calibration')
00021 import rospy
00022 
00023 from etherCAT_hand_lib import EtherCAT_Hand_Lib
00024 from QtGui import QTreeWidgetItem, QTreeWidgetItemIterator, QColor, QIcon, QMessageBox
00025 from PyQt4.Qt import QTimer
00026 from PyQt4.QtCore import SIGNAL
00027 
00028 import yaml
00029 try:
00030     from yaml import CLoader as Loader, CDumper as Dumper
00031 except ImportError:
00032     from yaml import Loader, Dumper
00033 import os
00034 
00035 from collections import deque
00036 
00037 green = QColor(153, 231, 96)
00038 orange = QColor(247, 206, 134)
00039 red = QColor(236, 178, 178)
00040 
00041 class IndividualCalibration( QTreeWidgetItem ):
00042     """
00043     """
00044 
00045     def __init__(self, joint_name,
00046                  raw_value, calibrated_value,
00047                  parent_widget, tree_widget,
00048                  robot_lib):
00049         """
00050         """
00051         self.joint_name = joint_name
00052         self.raw_value = int(raw_value)
00053         self.calibrated_value = calibrated_value
00054         self.tree_widget = tree_widget
00055         self.robot_lib = robot_lib
00056 
00057         QTreeWidgetItem.__init__(self, parent_widget, ["", "", str(self.raw_value), str(self.calibrated_value)])
00058 
00059         for col in xrange(self.tree_widget.columnCount()):
00060             self.setBackgroundColor(col, QColor(red))
00061 
00062         self.tree_widget.addTopLevelItem( self )
00063 
00064         self.is_calibrated = False
00065 
00066     def remove(self):
00067         self.tree_widget.remove
00068 
00069     def calibrate(self):
00070         self.raw_value = self.robot_lib.get_average_raw_value(self.joint_name, 100)
00071         self.setText( 2, str(self.raw_value) )
00072 
00073         for col in xrange(self.tree_widget.columnCount()):
00074             #calibrate only the calibration lines, not the items for
00075             # the fingers / joints / hand
00076             if self.text(2) != "":
00077                 self.setBackgroundColor(col, QColor(green))
00078 
00079         self.is_calibrated = True
00080 
00081     def set_is_loaded_calibration(self):
00082         for col in xrange(self.tree_widget.columnCount()):
00083             #set the background to orange: those values are loaded
00084             # from the file, not recalibrated
00085             if self.text(2) != "":
00086                 self.setBackgroundColor(col, QColor(orange))
00087 
00088         self.is_calibrated = True
00089 
00090 
00091     def get_calibration(self):
00092         return [self.raw_value, self.calibrated_value]
00093 
00094 class JointCalibration( QTreeWidgetItem ):
00095     """
00096     """
00097 
00098     nb_values_to_check = 5
00099     def __init__(self, joint_name,
00100                  calibrations,
00101                  parent_widget, tree_widget,
00102                  robot_lib):
00103         """
00104         """
00105         self.joint_name = joint_name
00106         self.tree_widget = tree_widget
00107         self.robot_lib = robot_lib
00108 
00109         self.calibrations = []
00110 
00111         self.last_raw_values = deque()
00112 
00113         QTreeWidgetItem.__init__(self, parent_widget, ["", joint_name, "", ""] )
00114 
00115         for calibration in calibrations:
00116             self.calibrations.append( IndividualCalibration(joint_name,
00117                                                             calibration[0], calibration[1],
00118                                                             self, tree_widget, robot_lib) )
00119 
00120         #display the current joint position in the GUI
00121         self.timer = QTimer()
00122         self.timer.start(200)
00123         tree_widget.addTopLevelItem(self)
00124         tree_widget.connect(self.timer, SIGNAL('timeout()'), self.update_joint_pos)
00125 
00126     def load_joint_calibration(self, new_calibrations):
00127         for calibration in self.calibrations:
00128             self.removeChild( calibration )
00129         self.calibrations = []
00130 
00131         for calibration in new_calibrations:
00132             new_calib =  IndividualCalibration(self.joint_name,
00133                                                calibration[0], calibration[1],
00134                                                self, self.tree_widget, self.robot_lib)
00135             new_calib.set_is_loaded_calibration()
00136             self.calibrations.append( new_calib )
00137 
00138 
00139     def get_joint_calibration(self):
00140         config = []
00141         for calibration in self.calibrations:
00142             if calibration.is_calibrated:
00143                 config.append( calibration.get_calibration() )
00144 
00145         if len(config) <= 1:
00146             #no config, or only one point
00147             # generating flat config
00148             config = [[0, 0.0], [1, 0.0]]
00149         return [self.joint_name, config]
00150 
00151     def update_joint_pos(self):
00152         raw_value = self.robot_lib.get_raw_value( self.joint_name )
00153         self.setText( 2, str(raw_value) )
00154 
00155         #if the 5 last values are equal, then display a warning
00156         # as there's always some noise on the values
00157         self.last_raw_values.append(raw_value)
00158         if len(self.last_raw_values) > self.nb_values_to_check:
00159             self.last_raw_values.popleft()
00160 
00161             #only check if we have enough values
00162             all_equal = True
00163             last_data = self.last_raw_values[0]
00164             for data in self.last_raw_values:
00165                 if data != last_data:
00166                     all_equal = False
00167                     break
00168             if all_equal == True:
00169                 self.setIcon(0, QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), '../icons/warn.gif')))
00170                 self.setToolTip(0,"No noise on the data for the last " + str(self.nb_values_to_check) + " values, there could be a problem with the sensor.")
00171             else:
00172                 self.setIcon(0, QIcon())
00173                 self.setToolTip(0, "")
00174 
00175     def on_close(self):
00176         self.timer.stop()
00177 
00178 
00179 class FingerCalibration( QTreeWidgetItem ):
00180     """
00181     """
00182 
00183     def __init__(self, finger_name,
00184                  finger_joints,
00185                  parent_widget, tree_widget,
00186                  robot_lib):
00187         """
00188         """
00189 
00190         QTreeWidgetItem.__init__(self, parent_widget, [finger_name, "", "", ""] )
00191 
00192         self.joints = []
00193         for joint in finger_joints:
00194             self.joints.append( JointCalibration( joint_name = joint[0],
00195                                                   calibrations = joint[1],
00196                                                   parent_widget = self,
00197                                                   tree_widget = tree_widget,
00198                                                   robot_lib = robot_lib) )
00199 
00200         tree_widget.addTopLevelItem(self)
00201 
00202 class HandCalibration( QTreeWidgetItem ):
00203     """
00204     """
00205     #TODO: Import this from an xml file?
00206     joint_map = {"First Finger":[ [ "FFJ1", [ [0.0, 0.0],
00207                                               [0.0, 22.5],
00208                                               [0.0, 45.0],
00209                                               [0.0, 67.5],
00210                                               [0.0, 90.0] ] ],
00211                                   [ "FFJ2", [ [0.0, 0.0],
00212                                               [0.0, 22.5],
00213                                               [0.0, 45.0],
00214                                               [0.0, 67.5],
00215                                               [0.0, 90.0] ] ],
00216                                   [ "FFJ3", [ [0.0, 0.0],
00217                                               [0.0, 22.5],
00218                                               [0.0, 45.0],
00219                                               [0.0, 67.5],
00220                                               [0.0, 90.0] ] ],
00221                                   [ "FFJ4", [ [0.0, -25.0],
00222                                               [0.0, 0.0],
00223                                               [0.0, 25.0] ] ] ],
00224 
00225                  "Middle Finger":[ [ "MFJ1", [ [0.0, 0.0],
00226                                                [0.0, 22.5],
00227                                                [0.0, 45.0],
00228                                                [0.0, 67.5],
00229                                                [0.0, 90.0] ] ],
00230                                    [ "MFJ2", [ [0.0, 0.0],
00231                                                [0.0, 22.5],
00232                                                [0.0, 45.0],
00233                                                [0.0, 67.5],
00234                                                [0.0, 90.0] ] ],
00235                                    [ "MFJ3", [ [0.0, 0.0],
00236                                                [0.0, 22.5],
00237                                                [0.0, 45.0],
00238                                                [0.0, 67.5],
00239                                                [0.0, 90.0] ] ],
00240                                    [ "MFJ4", [ [0.0, -25.0],
00241                                                [0.0, 0.0],
00242                                                [0.0, 25.0] ] ] ],
00243 
00244                  "Ring Finger":[ [ "RFJ1", [ [0.0, 0.0],
00245                                              [0.0, 22.5],
00246                                              [0.0, 45.0],
00247                                              [0.0, 67.5],
00248                                              [0.0, 90.0] ] ],
00249                                  [ "RFJ2", [ [0.0, 0.0],
00250                                              [0.0, 22.5],
00251                                              [0.0, 45.0],
00252                                              [0.0, 67.5],
00253                                              [0.0, 90.0] ] ],
00254                                  [ "RFJ3", [ [0.0, 0.0],
00255                                              [0.0, 22.5],
00256                                              [0.0, 45.0],
00257                                              [0.0, 67.5],
00258                                              [0.0, 90.0] ] ],
00259                                  [ "RFJ4", [ [0.0, -25.0],
00260                                              [0.0, 0.0],
00261                                              [0.0, 25.0] ] ] ],
00262 
00263                  "Little Finger":[ [ "LFJ1", [ [0.0, 0.0],
00264                                                [0.0, 22.5],
00265                                                [0.0, 45.0],
00266                                                [0.0, 67.5],
00267                                                [0.0, 90.0] ] ],
00268                                    [ "LFJ2", [ [0.0, 0.0],
00269                                                [0.0, 22.5],
00270                                                [0.0, 45.0],
00271                                                [0.0, 67.5],
00272                                                [0.0, 90.0] ] ],
00273                                    [ "LFJ3", [ [0.0, 0.0],
00274                                                [0.0, 22.5],
00275                                                [0.0, 45.0],
00276                                                [0.0, 67.5],
00277                                                [0.0, 90.0] ] ],
00278                                    [ "LFJ4", [ [0.0, -25.0],
00279                                                [0.0, 0.0],
00280                                                [0.0, 25.0] ] ] ,
00281                                    [ "LFJ5", [ [0.0, 0.0],
00282                                                [0.0, 22.5],
00283                                                [0.0, 45.0],
00284                                                [0.0, 67.5],
00285                                                [0.0, 90.0] ] ] ],
00286 
00287                  "Thumb":[ [ "THJ1", [ [0.0, 0.0],
00288                                        [0.0, 22.5],
00289                                        [0.0, 45.0],
00290                                        [0.0, 67.5],
00291                                        [0.0, 90.0] ] ],
00292                            [ "THJ2", [ [0.0, -30.0],
00293                                        [0.0, -15.0],
00294                                        [0.0, 0.0],
00295                                        [0.0, 15.0],
00296                                        [0.0, 30.0] ] ],
00297                            [ "THJ3", [ [0.0, -15.0],
00298                                        [0.0, 0.0],
00299                                        [0.0, 15.0] ] ],
00300                            [ "THJ4", [ [0.0, 0.0],
00301                                        [0.0, 22.5],
00302                                        [0.0, 45.0],
00303                                        [0.0, 67.5] ] ],
00304                            [ "THJ5", [ [0.0, -60.0],
00305                                        [0.0, -30.0],
00306                                        [0.0, 0.0],
00307                                        [0.0, 30.0],
00308                                        [0.0, 60.0] ] ] ],
00309 
00310                  "Wrist":[ [ "WRJ1", [ [0.0, -45.0],
00311                                        [0.0, -22.5],
00312                                        [0.0, 0.0],
00313                                        [0.0, 22.5],
00314                                        [0.0, 35.0] ] ],
00315                            [ "WRJ2", [ [0.0, -30.0],
00316                                        [0.0, 0.0],
00317                                        [0.0, 10.0] ] ] ]
00318                  }
00319 
00320     def __init__(self,
00321                  tree_widget,
00322                  progress_bar,
00323                  fingers = ["First Finger", "Middle Finger",
00324                             "Ring Finger", "Little Finger",
00325                             "Thumb", "Wrist"]):
00326         """
00327         """
00328         self.fingers = []
00329         #this is set to False if the user doesn't want to continue
00330         # when there are no EtherCAT hand node currently running.
00331         self.is_active = True
00332 
00333         QTreeWidgetItem.__init__(self, ["Hand", "", "", ""] )
00334 
00335         self.robot_lib = EtherCAT_Hand_Lib()
00336         if not self.robot_lib.activate():
00337             btn_pressed = QMessageBox.warning(tree_widget, "Warning", "The EtherCAT Hand node doesn't seem to be running, or the debug topic is not being published. Do you still want to continue? The calibration will be useless.",
00338                                               buttons = QMessageBox.Ok |  QMessageBox.Cancel)
00339 
00340             if btn_pressed == QMessageBox.Cancel:
00341                 self.is_active = False
00342 
00343 
00344         for finger in fingers:
00345             if finger in self.joint_map.keys():
00346                 self.fingers.append( FingerCalibration( finger,
00347                                                         self.joint_map[finger],
00348                                                         self, tree_widget,
00349                                                         self.robot_lib) )
00350 
00351             else:
00352                 print finger, " not found in the calibration map"
00353 
00354         self.joint_0_calibration_index = 0
00355 
00356         self.progress_bar = progress_bar
00357 
00358         self.tree_widget = tree_widget
00359         self.tree_widget.addTopLevelItem(self)
00360         self.tree_widget.itemActivated.connect(self.calibrate_item)
00361 
00362     def unregister(self):
00363         it = QTreeWidgetItemIterator( self )
00364         while it.value():
00365             try:
00366                 it.value().on_close()
00367             except:
00368                 pass
00369             it += 1
00370 
00371         self.robot_lib.on_close()
00372 
00373     def calibrate_item(self, item):
00374         try:
00375             #only the IndividualCalibration have the calibrate method
00376             item.calibrate()
00377         except:
00378             pass
00379 
00380         self.progress()
00381 
00382         #select the next row by default
00383         self.tree_widget.setItemSelected( item, False )
00384         next_item = self.tree_widget.itemBelow(item)
00385         self.tree_widget.setItemSelected( next_item, True )
00386         self.tree_widget.setCurrentItem( next_item )
00387 
00388     def calibrate_joint0s(self, btn_joint_0s):
00389         joint0s = [ "FFJ1", "FFJ2",
00390                     "MFJ1", "MFJ2",
00391                     "RFJ1", "RFJ2",
00392                     "LFJ1", "LFJ2" ]
00393 
00394         it = QTreeWidgetItemIterator( self )
00395         while it.value():
00396             if it.value().text(1) in joint0s:
00397                 it += self.joint_0_calibration_index + 1
00398                 it.value().calibrate()
00399             it += 1
00400 
00401         self.joint_0_calibration_index += 1
00402         if self.joint_0_calibration_index == len( self.joint_map["First Finger"][0][1] ):
00403             self.joint_0_calibration_index = 0
00404 
00405         #updating the btn text
00406         btn_joint_0s.setText( "Save all Joint 0s (angle = "+ str(self.joint_map["First Finger"][0][1][self.joint_0_calibration_index][1]) +")" )
00407 
00408         self.progress()
00409 
00410     def progress(self):
00411         it = QTreeWidgetItemIterator( self )
00412         nb_of_items = 0
00413         nb_of_calibrated_items = 0
00414         while it.value():
00415             it += 1
00416             try:
00417                 if it.value().is_calibrated:
00418                     nb_of_calibrated_items += 1
00419                 nb_of_items += 1
00420             except:
00421                 pass
00422 
00423         self.progress_bar.setValue( int( float(nb_of_calibrated_items) / float(nb_of_items) * 100.0 ) )
00424 
00425     def load(self, filepath):
00426         f = open(filepath,'r')
00427         document = ""
00428         for line in f.readlines():
00429             document += line
00430         f.close()
00431         yaml_config = yaml.load(document)
00432 
00433         for joint in yaml_config["sr_calibrations"]:
00434             it = QTreeWidgetItemIterator( self )
00435             while it.value():
00436                 if it.value().text(1) == joint[0]:
00437                     it.value().load_joint_calibration( joint[1] )
00438                 it += 1
00439 
00440     def save(self, filepath):
00441         yaml_config = {}
00442 
00443         joint_configs = []
00444         it = QTreeWidgetItemIterator( self )
00445         while it.value():
00446             try:
00447                 joint_configs.append( it.value().get_joint_calibration() )
00448             except:
00449                 pass
00450             it += 1
00451 
00452         #this doesn't work as we'd like
00453         #yaml_config["sr_calibrations"] = joint_configs
00454         #full_config_to_write = yaml.dump(yaml_config, default_flow_style=False)
00455         full_config_to_write = "{\'sr_calibrations\': [\n"
00456         for joint_config in joint_configs:
00457             full_config_to_write += "[\""
00458             full_config_to_write += joint_config[0] + "\", "
00459 
00460             #the etherCAT driver wants floats
00461             for index,calib in enumerate(joint_config[1]):
00462                 joint_config[1][index][0] = float(calib[0])
00463                 joint_config[1][index][1] = float(calib[1])
00464 
00465             full_config_to_write += str(joint_config[1])
00466             full_config_to_write += "], \n"
00467         full_config_to_write += "]}"
00468 
00469         f = open(filepath, 'w')
00470         f.write(full_config_to_write)
00471         f.close()
00472 
00473     def is_calibration_complete(self):
00474         it = QTreeWidgetItemIterator( self )
00475         while it.value():
00476             try:
00477                 if not it.value().is_calibrated:
00478                     return False
00479             except:
00480                 pass
00481             it += 1
00482 
00483         return True
00484 


sr_gui_hand_calibration
Author(s): Ugo Cupcic
autogenerated on Fri Jan 3 2014 12:08:02