hironx_client.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2013, JSK Lab, University of Tokyo
00006 # All rights reserved.
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions
00010 # are met:
00011 #
00012 #  * Redistributions of source code must retain the above copyright
00013 #    notice, this list of conditions and the following disclaimer.
00014 #  * Redistributions in binary form must reproduce the above
00015 #    copyright notice, this list of conditions and the following
00016 #    disclaimer in the documentation and/or other materials provided
00017 #    with the distribution.
00018 #  * Neither the name of JSK Lab, University of Tokyo. nor the
00019 #    names of its contributors may be used to endorse or promote products
00020 #    derived from this software without specific prior written permission.
00021 #
00022 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 
00035 import math
00036 import numpy
00037 import os
00038 import time
00039 
00040 import roslib; roslib.load_manifest("hrpsys")
00041 from hrpsys.hrpsys_config import *
00042 import OpenHRP
00043 import OpenRTM_aist
00044 import OpenRTM_aist.RTM_IDL
00045 import rtm
00046 from waitInput import waitInputConfirm, waitInputSelect
00047 
00048 
00049 SWITCH_ON = OpenHRP.RobotHardwareService.SWITCH_ON
00050 SWITCH_OFF = OpenHRP.RobotHardwareService.SWITCH_OFF
00051 _MSG_ASK_ISSUEREPORT = 'Your report to ' + \
00052                        'https://github.com/start-jsk/rtmros_hironx/issues ' + \
00053                        'about the issue you are seeing is appreciated.'
00054 
00055 
00056 class HIRONX(HrpsysConfigurator):
00057     '''
00058     @see: <a href = "https://github.com/fkanehiro/hrpsys-base/blob/master/python/hrpsys_config.py">HrpsysConfigurator</a>
00059 
00060     This class holds methods that are specific to Kawada Industries' dual-arm
00061     robot called Hiro.
00062 
00063     For the API doc for the derived methods, please see the parent 
00064     class via the link above; nicely formatted api doc web page
00065     isn't available yet (discussed in 
00066     https://github.com/fkanehiro/hrpsys-base/issues/268).
00067     '''
00068 
00069     Groups = [['torso', ['CHEST_JOINT0']],
00070               ['head', ['HEAD_JOINT0', 'HEAD_JOINT1']],
00071               ['rarm', ['RARM_JOINT0', 'RARM_JOINT1', 'RARM_JOINT2',
00072                         'RARM_JOINT3', 'RARM_JOINT4', 'RARM_JOINT5']],
00073               ['larm', ['LARM_JOINT0', 'LARM_JOINT1', 'LARM_JOINT2',
00074                         'LARM_JOINT3', 'LARM_JOINT4', 'LARM_JOINT5']]]
00075 
00076     '''
00077     For OffPose and _InitialPose, the angles of each joint are listed in the
00078     ordered as defined in Groups variable.'''
00079     OffPose = [[0], [0, 0],
00080                    [25, -139, -157, 45, 0, 0],
00081                    [-25, -139, -157, -45, 0, 0],
00082                    [0, 0, 0, 0],
00083                    [0, 0, 0, 0]]
00084     # With this pose the EEFs level up the tabletop surface.
00085     _InitialPose = [[0], [0, 0],
00086                    [-0.6, 0, -100, 15.2, 9.4, 3.2],
00087                    [0.6, 0, -100, -15.2, 9.4, -3.2],
00088                    [0, 0, 0, 0],
00089                    [0, 0, 0, 0]]
00090     # This pose sets joint angles at the factory initial pose. No danger, but
00091     # no real advange either for in normal usage.
00092     # See https://github.com/start-jsk/rtmros_hironx/issues/107
00093     _InitialPose_Factory = [[0], [0, 0],
00094                    [-0, 0, -130, 0, 0, 0],
00095                    [0, 0, -130, 0, 0, 0],
00096                    [0, 0, 0, 0],
00097                    [0, 0, 0, 0]]
00098     INITPOS_TYPE_EVEN = 0
00099     INITPOS_TYPE_FACTORY = 1
00100 
00101     HandGroups = {'rhand': [2, 3, 4, 5], 'lhand': [6, 7, 8, 9]}
00102 
00103     RtcList = []
00104 
00105     # servo controller (grasper)
00106     sc = None
00107     sc_svc = None
00108 
00109     def init(self, robotname="HiroNX(Robot)0", url=""):
00110         '''
00111         Calls init from its superclass, which tries to connect RTCManager,
00112         looks for ModelLoader, and starts necessary RTC components. Also runs
00113         config, logger.
00114         Also internally calls setSelfGroups().
00115 
00116         @type robotname: str
00117         @type url: str
00118         '''
00119         HrpsysConfigurator.init(self, robotname=robotname, url=url)
00120         self.setSelfGroups()
00121 
00122     def goOffPose(self, tm=7):
00123         '''
00124         Move arms to the predefined (as member variable) pose where robot can
00125         be safely turned off.
00126 
00127         @type tm: float
00128         @param tm: Second to complete.
00129         '''
00130         for i in range(len(self.Groups)):
00131             self.setJointAnglesOfGroup(self.Groups[i][0], self.OffPose[i], tm,
00132                                        wait=False)
00133         for i in range(len(self.Groups)):
00134             self.seq_svc.waitInterpolationOfGroup(self.Groups[i][0])
00135         self.servoOff(wait=False)
00136 
00137     def goInitial(self, tm=7, wait=True, init_pose_type=0):
00138         '''
00139         Move arms to the predefined (as member variable) "initialized" pose.
00140 
00141         @type tm: float
00142         @param tm: Second to complete.
00143         @type wait: bool
00144         @param wait: If true, SequencePlayer.waitInterpolationOfGroup gets run.
00145                      (TODO: Elaborate what this means...Even after having taken
00146                      a look at its source code I can't tell what it means.)
00147         @type init_pose_type: int
00148         @param init_pose_type: 0: default init pose (specified as _InitialPose)
00149                                1: factory init pose (specified as
00150                                   _InitialPose_Factory)
00151         '''
00152         if init_pose_type == self.INITPOS_TYPE_FACTORY:
00153             _INITPOSE = self._InitialPose_Factory
00154         else:
00155             _INITPOSE = self._InitialPose
00156 
00157         ret = True
00158         for i in range(len(self.Groups)):
00159             # radangles = [x/180.0*math.pi for x in self._InitialPose[i]]
00160             print self.configurator_name, 'self.setJointAnglesOfGroup(', \
00161                   self.Groups[i][0], ',', _INITPOSE[i], ', ', tm, \
00162                   ',wait=False)'
00163             ret &= self.setJointAnglesOfGroup(self.Groups[i][0],
00164                                               _INITPOSE[i],
00165                                               tm, wait=False)
00166         if wait:
00167             for i in range(len(self.Groups)):
00168                 self.seq_svc.waitInterpolationOfGroup(self.Groups[i][0])
00169         return ret
00170 
00171     def getRTCList(self):
00172         '''
00173         @see: HrpsysConfigurator.getRTCList
00174 
00175         @rtype [[str]]
00176         @rerutrn List of available components. Each element consists of a list
00177                  of abbreviated and full names of the component.
00178         '''
00179         return [
00180             ['seq', "SequencePlayer"],
00181             ['sh', "StateHolder"],
00182             ['fk', "ForwardKinematics"],
00183             ['el', "SoftErrorLimiter"],
00184             # ['co', "CollisionDetector"],
00185             ['sc', "ServoController"],
00186             ['log', "DataLogger"]
00187             ]
00188 
00189     # hand interface
00190     # effort: 1~100[%]
00191     # hiro.HandOpen("rhand", effort)
00192     # hiro.HandOpen()        # for both hand
00193     # hiro.HandClose("rhand", effort)
00194     # hiro.HandClose()       # for both hand
00195     #
00196     def HandOpen(self, hand=None, effort=None):
00197         '''
00198         Set the stretch between two fingers of the specified hand as
00199         hardcoded value (100mm), by internally calling self.setHandWidth.
00200 
00201         @type hand: str
00202         @param hand: Name of the hand joint group. In the default 
00203                      setting of HIRONX, hand joint groups are defined
00204                      in member 'HandGroups' where 'lhand' and 'rhand'
00205                      are added.
00206         @type effort: int
00207         '''
00208         self.setHandWidth(hand, 100, effort=effort)
00209 
00210     def HandClose(self, hand=None, effort=None):
00211         '''
00212         Close 2-finger hand, by internally calling self.setHandWidth 
00213         setting 0 width.
00214 
00215         @type hand: str
00216         @param hand: Name of the hand joint group. In the default 
00217                      setting of HIRONX, hand joint groups are defined
00218                      in member 'HandGroups' where 'lhand' and 'rhand'
00219                      are added.
00220         @type effort: int
00221         '''
00222         self.setHandWidth(hand, 0, effort=effort)
00223 
00224     def setHandJointAngles(self, hand, angles, tm=1):
00225         '''
00226         @type hand: str
00227         @param hand: Name of the hand joint group. In the default 
00228                      setting of HIRONX, hand joint groups are defined
00229                      in member 'HandGroups' where 'lhand' and 'rhand'
00230                      are added.
00231         @type angles: OpenHRP::ServoControllerService::dSequence.
00232         @param angles: List of (TODO: document). Elements are in degree.
00233         @param tm: Time to complete the task.
00234         '''
00235         self.sc_svc.setJointAnglesOfGroup(hand, angles, tm)
00236 
00237     def setHandEffort(self, effort=100):
00238         '''
00239         Set maximum torque for all existing hand components.
00240         @type effort: int
00241         '''
00242 
00243         for i in [v for vs in self.HandGroups.values() for v in vs]:  # flatten
00244             self.sc_svc.setMaxTorque(i, effort)
00245 
00246     def setHandWidth(self, hand, width, tm=1, effort=None):
00247         '''
00248         @type hand: str
00249         @param hand: Name of the hand joint group. In the default 
00250                      setting of HIRONX, hand joint groups are defined
00251                      in member 'HandGroups' where 'lhand' and 'rhand'
00252                      are added.
00253         @param width: Max=100.
00254         @param tm: Time to complete the move.
00255         @type effort: int
00256         @param effort: Passed to self.setHandEffort if set. Not set by default.
00257         '''
00258         if effort:
00259             self.setHandEffort(effort)
00260         if hand:
00261             self.setHandJointAngles(hand, self.hand_width2angles(width), tm)
00262         else:
00263             for h in self.HandGroups.keys():
00264                 self.setHandJointAngles(h, self.hand_width2angles(width), tm)
00265 
00266     def moveHand(self, hand, av, tm=1):  # direction av: + for open, - for close
00267         '''
00268         Negate the angle value for {2, 3, 6, 7}th element in av.
00269 
00270         @type hand: str
00271         @param hand: Specifies hand. (TODO: List the possible values. Should be
00272                      listed in setHandJointAngles so just copy from its doc.)
00273         @type av: [int]
00274         @param av: angle of each joint of the specified arm
00275                   (TODO: need verified. Also what's the length of the list?)
00276         @param tm: Time in second to complete the work.
00277         '''
00278         for i in [2, 3, 6, 7]:  # do not change this line if servo is different, change HandGroups
00279             av[i] = -av[i]
00280         self.setHandJointAngles(hand, av, tm)
00281 
00282     def hand_width2angles(self, width):
00283         '''
00284         TODO: Needs documented what this method does.
00285 
00286         @type width: float
00287         @return: None if the given width is invalid.
00288         '''
00289         safetyMargin = 3
00290         l1, l2 = (41.9, 19)  # TODO: What are these variables?
00291 
00292         if width < 0.0 or width > (l1 + l2 - safetyMargin) * 2:
00293             return None
00294 
00295         xPos = width / 2.0 + safetyMargin
00296         a2Pos = xPos - l2
00297         a1radH = math.acos(a2Pos / l1)
00298         a1rad = math.pi / 2.0 - a1radH
00299 
00300         return a1rad, -a1rad, -a1rad, a1rad
00301 
00302     def setSelfGroups(self):
00303         '''
00304         Set to the hrpsys.SequencePlayer the groups of links and joints that
00305         are statically defined as member variables (Groups) within this class.
00306 
00307         That said, override Groups variable if you prefer link and joint
00308         groups set differently.
00309         '''
00310         #TODO: Accept groups variable. The name of the method sounds more
00311         #      natural if it accepts it.
00312         for item in self.Groups:
00313             self.seq_svc.addJointGroup(item[0], item[1])
00314         for k, v in self.HandGroups.iteritems():
00315             if self.sc_svc:
00316                 self.sc_svc.addJointGroup(k, v)
00317 
00318     def getActualState(self):
00319         '''
00320         Returns the physical state of robot.
00321 
00322         @rtype: <a href = "http://hrpsys-base.googlecode.com/svn/doc/df/d17/structOpenHRP_1_1RobotHardwareService_1_1RobotState.html">OpenHRP::RobotHardwareService::RobotState</a>
00323         @return: Robot's hardware status object that contains the following
00324                  variables accessible: angle, command, torque, servoState,
00325                  force, rateGyro, accel, voltage, current. See the api doc
00326                  of the class for more detail. Each variable is accessible by
00327                  like this for example:
00328 
00329                      servostate= robot.getActualState().servoState
00330         '''
00331         #TODO: Handle AttributeError. Typically when RobotHardware is not found,
00332         #      AttributeError: 'NoneType' object has no attribute 'getStatus'
00333 
00334         return self.rh_svc.getStatus()
00335 
00336     def isCalibDone(self):
00337         '''
00338         Check whether joints have been calibrated.
00339         @rtype bool
00340         '''
00341         if self.simulation_mode:
00342             return True
00343         else:
00344             rstt = self.rh_svc.getStatus()
00345             for item in rstt.servoState:
00346                 if not item[0] & 1:
00347                     return False
00348         return True
00349 
00350     def isServoOn(self, jname='any'):
00351         '''
00352         Check whether servo control has been turned on.
00353         @type jname: str
00354         @param jname: Name of a link (that can be obtained by "hiro.Groups"
00355                       as lists of groups).
00356         @rtype bool
00357         '''
00358 
00359         if self.simulation_mode:
00360             return True
00361         else:
00362             s_s = self.getActualState().servoState
00363             if jname.lower() == 'any' or jname.lower() == 'all':
00364                 for s in s_s:
00365                     # print self.configurator_name, 's = ', s
00366                     if (s[0] & 2) == 0:
00367                         return False
00368             else:
00369                 jid = eval('self.' + jname)
00370                 print self.configurator_name, s_s[jid]
00371                 if s_s[jid][0] & 1 == 0:
00372                     return False
00373             return True
00374         return False
00375 
00376     def flat2Groups(self, flatList):
00377         '''
00378         @type flatList: [int]
00379         @param flatList: single dimension list, with its length depends on
00380                          'Groups' variable defined within this class. Excessive
00381                          elements will be dropped (see example below in @return)
00382 
00383                          eg. If the number of joints of the robot is 15,
00384                              len(flatList) should be 15.
00385         @rtype: [[]]
00386         @return: 2-dimensional list that has the same format with
00387                  'Groups' variable.
00388 
00389                  eg.
00390                  ipython> flatlist = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90,
00391                                       100, 110, 120, 130, 140, 150]
00392                  ipython> robot.flat2Groups(flatlist)
00393                  [[0], [10, 20],
00394                   [30, 40, 50, 60, 70, 80],
00395                   [90, 100, 110, 120, 130, 140]]
00396 
00397         '''
00398         retList = []
00399         index = 0
00400         for group in self.Groups:
00401             joint_num = len(group[1])
00402             retList.append(flatList[index: index + joint_num])
00403             index += joint_num
00404         return retList
00405 
00406     def servoOn(self, jname='all', destroy=1, tm=3):
00407         '''
00408         Turn on servo motors at joint specified.
00409         Joints need to be calibrated (otherwise error returns).
00410 
00411         @type jname: str
00412         @param jname: The value 'all' works iteratively for all servos.
00413         @param destroy: Not used.
00414         @type tm: float
00415         @param tm: Second to complete.
00416         @rtype: int
00417         @return: 1 or -1 indicating success or failure, respectively.
00418         '''
00419         # check joints are calibrated
00420         if not self.isCalibDone():
00421             waitInputConfirm('!! Calibrate Encoders with checkEncoders first !!\n\n')
00422             return -1
00423 
00424         # check servo state
00425         if self.isServoOn():
00426             return 1
00427 
00428         # check jname is acceptable
00429         if jname == '':
00430             jname = 'all'
00431 
00432         #self.rh_svc.power('all', SWITCH_ON)  #do not switch on before goActual
00433 
00434         try:
00435             waitInputConfirm(\
00436                 '!! Robot Motion Warning (SERVO_ON) !!\n\n'
00437                 'Confirm RELAY switched ON\n'
00438                 'Push [OK] to switch servo ON(%s).' % (jname))
00439         except:  # ths needs to change
00440             self.rh_svc.power('all', SWITCH_OFF)
00441             raise
00442 
00443         # Need to reset JointGroups.
00444         # See https://code.google.com/p/rtm-ros-robotics/issues/detail?id=277
00445         try:
00446             # remove jointGroups
00447             self.seq_svc.removeJointGroup("larm")
00448             self.seq_svc.removeJointGroup("rarm")
00449             self.seq_svc.removeJointGroup("head")
00450             self.seq_svc.removeJointGroup("torso")
00451         except:
00452             print(self.configurator_name,
00453                   'Exception during servoOn while removing JoingGroup. ' +
00454                   _MSG_ASK_ISSUEREPORT)
00455         try:
00456             # setup jointGroups
00457             self.setSelfGroups()  # restart groups
00458         except:
00459             print(self.configurator_name,
00460                   'Exception during servoOn while removing setSelfGroups. ' +
00461                   _MSG_ASK_ISSUEREPORT)
00462 
00463         try:
00464             self.goActual()  # This needs to happen before turning servo on.
00465             time.sleep(0.1)
00466             self.rh_svc.servo(jname, SWITCH_ON)
00467             time.sleep(2)
00468             # time.sleep(7)
00469             if not self.isServoOn(jname):
00470                 print self.configurator_name, 'servo on failed.'
00471                 raise
00472         except:
00473             print self.configurator_name, 'exception occured'
00474 
00475         try:
00476             angles = self.flat2Groups(map(numpy.rad2deg,
00477                                           self.getActualState().angle))
00478             print 'Move to Actual State, Just a minute.'
00479             for i in range(len(self.Groups)):
00480                 self.setJointAnglesOfGroup(self.Groups[i][0], angles[i], tm,
00481                                            wait=False)
00482             for i in range(len(self.Groups)):
00483                 self.seq_svc.waitInterpolationOfGroup(self.Groups[i][0])
00484         except:
00485             print self.configurator_name, 'post servo on motion trouble'
00486 
00487         # turn on hand motors
00488         print 'Turn on Hand Servo'
00489         if self.sc_svc:
00490             self.sc_svc.servoOn()
00491 
00492         return 1
00493 
00494     def servoOff(self, jname='all', wait=True):
00495         '''
00496         @type jname: str
00497         @param jname: The value 'all' works iteratively for all servos.
00498         @type wait: bool
00499         @rtype: int
00500         @return: 1 = all arm servo off. 2 = all servo on arms and hands off.
00501                 -1 = Something wrong happened.
00502         '''
00503         # do nothing for simulation
00504         if self.simulation_mode:
00505             print self.configurator_name, 'omit servo off'
00506             return 0
00507 
00508         print 'Turn off Hand Servo'
00509         if self.sc_svc:
00510             self.sc_svc.servoOff()
00511         # if the servos aren't on switch power off
00512         if not self.isServoOn(jname):
00513             if jname.lower() == 'all':
00514                 self.rh_svc.power('all', SWITCH_OFF)
00515             return 1
00516 
00517         # if jname is not set properly set to all -> is this safe?
00518         if jname == '':
00519             jname = 'all'
00520 
00521         if wait:
00522             waitInputConfirm(
00523                 '!! Robot Motion Warning (Servo OFF)!!\n\n'
00524                 'Push [OK] to servo OFF (%s).' % (jname))  # :
00525 
00526         try:
00527             self.rh_svc.servo('all', SWITCH_OFF)
00528             time.sleep(0.2)
00529             if jname == 'all':
00530                 self.rh_svc.power('all', SWITCH_OFF)
00531 
00532             # turn off hand motors
00533             print 'Turn off Hand Servo'
00534             if self.sc_svc:
00535                 self.sc_svc.servoOff()
00536 
00537             return 2
00538         except:
00539             print self.configurator_name, 'servo off: communication error'
00540             return -1
00541 
00542     def checkEncoders(self, jname='all', option=''):
00543         '''
00544         Run the encoder checking sequence for specified joints,
00545         run goActual to adjust the direction values, and then turn servos on.
00546 
00547         @type jname: str
00548         @param jname: The value 'all' works iteratively for all servos.
00549         @type option: str
00550         @param option: Possible values are follows (w/o double quote):\
00551                         "-overwrite": Overwrite calibration value.
00552         '''
00553         if self.isServoOn():
00554             waitInputConfirm('Servo must be off for calibration')
00555             return
00556         # do not check encoders twice
00557         elif self.isCalibDone():
00558             waitInputConfirm('System has been calibrated')
00559             return
00560 
00561         self.rh_svc.power('all', SWITCH_ON)
00562         msg = '!! Robot Motion Warning !!\n'\
00563               'Turn Relay ON.\n'\
00564               'Then Push [OK] to '
00565         if option == '-overwrite':
00566             msg = msg + 'calibrate(OVERWRITE MODE) '
00567         else:
00568             msg = msg + 'check '
00569 
00570         if jname == 'all':
00571             msg = msg + 'the Encoders of all.'
00572         else:
00573             msg = msg + 'the Encoder of the Joint "' + jname + '".'
00574 
00575         try:
00576             waitInputConfirm(msg)
00577         except:
00578             print "If you're connecting to the robot from remote, " + \
00579                   "make sure tunnel X (eg. -X option with ssh)."
00580             self.rh_svc.power('all', SWITCH_OFF)
00581             return 0
00582 
00583         print self.configurator_name, 'calib-joint ' + jname + ' ' + option
00584         self.rh_svc.initializeJointAngle(jname, option)
00585         print self.configurator_name, 'done'
00586         self.rh_svc.power('all', SWITCH_OFF)
00587         self.goActual()  # This needs to happen before turning servo on.
00588         time.sleep(0.1)
00589         self.rh_svc.servo(jname, SWITCH_ON)
00590 
00591         # turn on hand motors
00592         print 'Turn on Hand Servo'
00593         if self.sc_svc:
00594             self.sc_svc.servoOn()


hironx_ros_bridge
Author(s): Kei Okada
autogenerated on Mon Oct 6 2014 07:19:42