00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 import serial
00030 import sys,time
00031 from time import sleep
00032 from threading import RLock
00033 import threading
00034 
00035 
00036 
00037 
00038 import roslib; roslib.load_manifest('zenither')
00039 import rospy
00040 from hrl_msgs.msg import FloatArray
00041 import hrl_lib.rutils as ru
00042 import hrl_lib.util as ut
00043 
00044 import zenither_config as zc
00045 
00046 class ZenitherCollideException(Exception):
00047     def __init__(self):
00048         pass
00049 
00050 class ZenitherClient:
00051     def __init__(self):
00052         self.listener = ru.FloatArrayListener('zenither_client', 'zenither_pose', 10)
00053 
00054     def pose(self, fresh=True):
00055         v = self.listener.read(fresh)
00056         return v[0,0]
00057 
00058 class PoseBroadcast(threading.Thread):
00059     def __init__(self, zenither, frequency=100):
00060         threading.Thread.__init__(self)
00061         try:
00062             rospy.init_node('ZenitherPose')
00063             print 'ZenitherPose: ros is up!'
00064         except rospy.ROSException:
00065             pass
00066 
00067         self.frequency = frequency
00068         self.zenither  = zenither
00069         self.should_run = True
00070         self.pose       = self.zenither.get_position_meters() 
00071 
00072         name = 'zenither_pose'
00073         print 'PoseBroadcast: publishing', name, 'with type FloatArray'
00074         self.channel    = rospy.Publisher(name, FloatArray)
00075         self.start()
00076 
00077     def run(self): 
00078        print 'PoseBroadcast: started.'
00079        while self.should_run and not rospy.is_shutdown(): 
00080            sleep(1.0/self.frequency)
00081            self.pose = self.zenither.get_position_meters()
00082            self.channel.publish(FloatArray(None, [self.pose]))
00083 
00084     def stop(self):
00085        self.should_run = False 
00086        self.join(3) 
00087        if (self.isAlive()): 
00088            raise RuntimeError("PoseBroadcast: unable to stop thread")
00089 
00090 class Zenither(object):
00091     """
00092        Basic Zenither interface
00093        Note: servo controller will crash if two different processes try to write to
00094              the serial interface at the same time
00095     """
00096     zero_bias = -0.004 
00097     servo = None
00098 
00099     def __init__(self, robot, max_command_retries=15, dev="/dev/robot/zenither",pose_read_thread=False):
00100         ''' robot- 'El-E' or 'HRL2'
00101             pose_read_thread - if you want a separate thread that constantly reads zenither height
00102                                and publishes using ROS.
00103         '''
00104 
00105         if robot not in zc.calib:
00106             raise RuntimeError('unknown robot')
00107         self.robot = robot
00108 
00109         self.dev = dev
00110         self.serial_lock = RLock()
00111         self.servo_start(max_command_retries)
00112 
00113         self.calib = zc.calib[robot]
00114         if self.calib['HAS_BRAKE']:
00115             self.brake_engaged = False
00116 
00117             self.estop()
00118 
00119         
00120         if self.get_variable('a') != '1':
00121             print 'WARNING: it seems that the Zenither has been power cycled'
00122             print 'WARNING: please set_origin for the zenither.'
00123             self.calibrated = False
00124         else:
00125             self.calibrated = True
00126 
00127         
00128         
00129         
00130         
00131         for i in range(10):
00132             self.get_motor_temperature_F()
00133 
00134         if pose_read_thread:
00135             self.broadcast()
00136 
00137     def broadcast(self):
00138         self.broadcaster = PoseBroadcast(self)
00139 
00140     def servo_start(self, max_command_retries):
00141         self.serial_lock.acquire()
00142         try:
00143             
00144             self.servo = serial.Serial(self.dev, timeout=0)
00145         except (serial.serialutil.SerialException), e:
00146             raise RuntimeError("Zenither: Serial port not found!\n")
00147         self.max_command_retries = max_command_retries
00148         if(self.servo == None):
00149             raise RuntimeError("Zenither: Serial port not found!\n")
00150 
00151         
00152         self.servo.timeout = 1
00153         check = self.get_status_byte()
00154         if(check == ''):
00155             raise RuntimeError("Animatics Servo isn't responding!\n")
00156             self.servo.close()
00157             self.servo = None
00158         else:
00159             t = self.get_motor_temperature_F()
00160             if self.get_motor_temperature_F() >= 140.0:
00161                 t = self.get_motor_temperature_F()
00162                 mesg = "Animatics Servo is too hot: " + t.__str__() + "F"
00163                 raise RuntimeError(mesg)
00164                 self.servo.close()
00165                 self.servo = None
00166             self.inputs_init()
00167             self.servo.write('o=0\n')        
00168             self.servo.write('E=10000\n')        
00169 
00170             
00171             self.servo.write('A=150\n')      
00172             self.servo.write('V=100000\n')    
00173             self.servo.write('P=2000\n')        
00174             self.servo.write('KG=50\n')      
00175             self.servo.write('F\n')          
00176 
00177         self.last_position = 0.0
00178             
00179         self.serial_lock.release()
00180 
00181     def reset_serial(self):
00182         self.serial_lock.acquire()
00183         self.servo.flushInput()
00184         self.servo.flushOutput()
00185         self.servo.close()
00186         self.servo_start(self.max_command_retries)
00187         self.serial_lock.release()
00188 
00189     def __del__(self):
00190         self.serial_lock.acquire()
00191         self.servo.close()
00192         self.serial_lock.release()
00193 
00194     def __lock_write(self, line):
00195         self.serial_lock.acquire()
00196         self.servo.write(line)
00197         
00198         self.serial_lock.release()
00199 
00200     def reset(self):
00201         self.__lock_write("Z\n")
00202 
00203     def set_timeout(self,timeout):
00204         self.serial_lock.acquire()
00205         self.servo.timeout = timeout
00206         self.serial_lock.release()
00207 
00208     def turn_off(self):
00209         self.__lock_write("OFF\n")
00210 
00211     def get_status_byte(self):
00212         self.serial_lock.acquire()
00213         self.servo.write("RS\n")
00214         s = self.servo.readline(eol='\r')
00215         self.serial_lock.release()
00216         return s
00217 
00218     def get_status_word(self):
00219         self.serial_lock.acquire()
00220         self.servo.write("RW\n")
00221         s = self.servo.readline(eol='\r')
00222         self.serial_lock.release()
00223         return s
00224 
00225     def report_and_clear(self):
00226         self.serial_lock.acquire()
00227         self.servo.write("RCS1\n")
00228         s = self.servo.readline(eol='\r')
00229         self.serial_lock.release()
00230         return s
00231 
00232     def get_mode(self):
00233         self.serial_lock.acquire()
00234         self.servo.write("RMODE\n")
00235         s = self.servo.readline(eol='\r')
00236         self.serial_lock.release()
00237         return s
00238 
00239     def use_position_mode(self):
00240         if self.calib['HAS_BRAKE']:
00241             self.engage_brake_on_without_trajectory()
00242         self.__lock_write("MP\n")
00243 
00244     def use_velocity_mode(self):
00245         if self.calib['HAS_BRAKE']:
00246             self.engage_brake_on_without_trajectory()
00247         self.__lock_write("MV\n")
00248 
00249     def use_torque_mode(self):
00250         self.__lock_write("MT\n")
00251 
00252     def get_factor(self,type='vel_factor'):
00253         
00254         return self.calib[type]
00255 
00256     def set_acceleration(self,a):
00257         '''Sets the acceleration in meters/sec^2'''
00258         factor = self.get_factor(type='acc_factor')
00259         a  = int(round(abs(a/ factor)))
00260         cmd = "A="+str(a)+"\n"
00261         self.__lock_write(cmd)
00262 
00263     def set_velocity(self,v):
00264         '''Sets the velocity in meters/sec'''
00265         factor = self.get_factor(type='vel_factor')
00266         v  = int(round(v/factor))
00267         cmd ="V="+str(v)+"\n"
00268         self.__lock_write(cmd)
00269 
00270     def set_origin(self):
00271         self.__lock_write("O=0\n")
00272         time.sleep(0.1)
00273         self.__lock_write("a 1\n") 
00274         self.calibrated = True
00275 
00276     def go(self):
00277         self.__lock_write("G\n")
00278 
00279     
00280     
00281     
00282     
00283 
00284     def estop(self):
00285         self.__lock_write("S\n")
00286         if self.calib['HAS_BRAKE']:
00287             self.engage_brake_on_without_trajectory()
00288 
00289     def set_pos_absolute(self,p):
00290         '''Sets the absolute position in meters'''
00291         factor = self.get_factor(type='pos_factor')
00292         p = int(round(p/factor))
00293         cmd = "P="+str(p)+"\n"
00294         self.__lock_write(cmd)
00295 
00296     def set_pos_relative(self,p):
00297         '''Sets the relative position in meters'''
00298         factor = self.get_factor(type='pos_factor')
00299         p = int(round(p/factor))
00300         cmd = "D="+str(p)+"\n"
00301         self.__lock_write(cmd)
00302 
00303     
00304     
00305     def get_variable(self,var):
00306         self.serial_lock.acquire()
00307         self.servo.write('R'+var+'\n')
00308         s = self.servo.readline(eol='\r')
00309         self.serial_lock.release()
00310         return s.replace('\r','')
00311 
00312     def get_position(self):
00313         self.serial_lock.acquire()
00314         
00315         self.servo.write("RP\n")
00316         s = self.servo.readline(eol='\r')
00317         self.serial_lock.release()
00318         
00319         return s
00320 
00321     def get_position_meters(self):
00322         if self.calibrated == False:
00323             self.estop()
00324             raise RuntimeError('Zenither not calibrated. Please set_origin.')
00325         val = self.get_position().replace('\r','')
00326         while val == '':
00327             
00328             val = self.get_position().replace('\r','')
00329         
00330 
00331         factor = self.get_factor(type='pos_factor')
00332         p = float(val)*factor + self.calib['ZERO_BIAS']
00333         return p
00334 
00335     def set_torque(self,t):
00336         self.__lock_write("T="+str(t)+"\n")
00337 
00338     
00339     
00340     
00341     
00342     
00343     
00344     
00345     
00346     
00347     
00348     
00349     
00350     
00351     
00352     
00353     
00354     
00355     
00356     
00357     
00358     
00359     
00360     
00361     
00362     
00363     
00364     
00365     
00366     
00367     
00368     
00369     
00370     
00371     
00372     
00373     
00374     
00375     
00376     
00377     
00378     
00379     
00380     
00381 
00382 
00383     def zenith(self, torque=None):
00384         if torque == None:
00385             torque=self.calib['zenith_torque']
00386 
00387         
00388         self.serial_lock.acquire()
00389         if self.robot == 'El-E':
00390             self.servo.write('KGOFF\n')       
00391         self.use_torque_mode()
00392         factor = self.get_factor(type='pos_factor')
00393         self.set_torque(int(torque*factor/abs(factor))) 
00394         if self.calib['HAS_BRAKE']:
00395             self.disengage_brake()
00396         
00397         self.serial_lock.release()
00398 
00399     def nadir(self, torque=None):
00400         self.serial_lock.acquire()
00401         if torque == None:
00402             torque = self.calib['nadir_torque']
00403         if self.robot == 'El-E':
00404             self.servo.write('KGOFF\n')       
00405         self.use_torque_mode()
00406         factor = self.get_factor(type='pos_factor')
00407         
00408         self.set_torque(int(torque*factor/abs(factor))) 
00409         if self.calib['HAS_BRAKE']:
00410             self.disengage_brake()
00411         else:
00412             print 'no brake'
00413         
00414         self.serial_lock.release()
00415 
00416     def torque_move_position(self,height,speed='fast',exception=True):
00417         ''' speed - 'fast','slow','snail'
00418             exception - if True then throws exception in case of collision.
00419             moves till height or if it detects collision.
00420             height - height of the bottom point of carriage above the ground.
00421         '''
00422         
00423         
00424 
00425         position_zenither_coord = height - self.calib['ZERO_BIAS']
00426         position_zenither_coord = self.limit_position(position_zenither_coord)
00427 
00428         if height>self.calib['max_height']:
00429             height = self.calib['max_height']
00430 
00431         if height<self.calib['min_height']:
00432             height = self.calib['min_height']
00433         
00434         curr_height = self.get_position_meters()
00435 
00436         if abs(curr_height-height)<0.002:
00437             return
00438 
00439         if curr_height>height:
00440             if speed=='fast':
00441                 torque=self.calib['down_fast_torque']
00442             elif speed=='slow':
00443                 torque=self.calib['down_slow_torque']
00444             elif speed=='snail':
00445                 torque=self.calib['down_snail_torque']
00446             else:
00447                 print 'zenither.torque_move_position: unknown speed- ',speed
00448                 sys.exit()
00449             self.torque_go_down(height,torque)
00450 
00451         if curr_height<height:
00452             if speed=='fast':
00453                 torque=self.calib['up_fast_torque']
00454             elif speed=='slow':
00455                 torque=self.calib['up_slow_torque']
00456             elif speed=='snail':
00457                 torque=self.calib['up_snail_torque']
00458             else:
00459                 print 'zenither.torque_move_position: unknown speed- ',speed
00460                 sys.exit()
00461             self.torque_go_up(height,torque)
00462         error = height-self.get_position_meters()
00463         if abs(error)>0.005 and exception == True:
00464             raise ZenitherCollideException
00465 
00466     def torque_go_down(self,height,start_torque,dist_list=[0.15,0.05]):
00467         ''' dist list - how much distance to move slow and snail.
00468             height - is in zenither coord. not height of carriage above the
00469                      ground.
00470         '''
00471         
00472         
00473 
00474         slow_torque = self.calib['down_slow_torque']
00475         if self.calib['robot'] == 'HRL2':
00476             slow_torque = self.calib['down_fast_torque']
00477         snail_torque = self.calib['down_snail_torque']
00478 
00479         h_start = self.get_position_meters()
00480         h_start = self.get_position_meters()
00481 
00482         if (h_start-height) < (dist_list[1]+0.01):
00483             self.nadir(snail_torque)
00484             current_torque = snail_torque
00485         elif (h_start-height) < (dist_list[0]+0.02):
00486             self.nadir(slow_torque)
00487             current_torque = slow_torque
00488         else:
00489             self.nadir(start_torque)
00490             current_torque = start_torque
00491 
00492         h_start = self.get_position_meters()
00493         h_start = self.get_position_meters()
00494 
00495         while h_start > height:
00496             time.sleep(0.01)
00497             h_now = self.get_position_meters()
00498             if (h_now-height) < dist_list[1]:
00499                 if current_torque != snail_torque:
00500                     self.nadir(snail_torque)
00501                     current_torque = snail_torque
00502             elif (h_now-height) < dist_list[0]:
00503                 if current_torque != slow_torque:
00504                     self.nadir(slow_torque)
00505                     current_torque = slow_torque
00506 
00507             if h_now == h_start:
00508                 
00509                 break
00510             h_start = h_now
00511 
00512         self.estop()
00513         
00514 
00515     def torque_go_up(self,height,start_torque,dist_list=[0.15,0.05]):
00516         ''' dist list - how much distance to move slow and snail.
00517             height - is in zenither coord. not height of carriage above the
00518                      ground.
00519         '''
00520         
00521         
00522 
00523         slow_torque  = self.calib['up_slow_torque']
00524         snail_torque = self.calib['up_snail_torque']
00525 
00526         h_start = self.get_position_meters()
00527         h_start = self.get_position_meters()
00528 
00529         if (height-h_start) < (dist_list[1]+0.01):
00530             self.zenith(snail_torque)
00531             current_torque = snail_torque
00532         elif (height-h_start) < (dist_list[0]+0.02):
00533             self.zenith(slow_torque)
00534             current_torque = slow_torque
00535         else:
00536             self.zenith(start_torque)
00537             current_torque = start_torque
00538 
00539         h_start = self.get_position_meters()
00540         h_start = self.get_position_meters()
00541 
00542         while h_start < height:
00543             h_now = self.get_position_meters()
00544             time.sleep(0.01)
00545             if (height-h_now) < dist_list[1]:
00546                 if current_torque != snail_torque:
00547                     self.zenith(snail_torque)
00548                     current_torque = snail_torque
00549             elif (height-h_now) < dist_list[0]:
00550                 if current_torque != slow_torque:
00551                     self.zenith(slow_torque)
00552                     current_torque = slow_torque
00553 
00554             if h_now == h_start:
00555                 
00556                 break
00557             h_start = h_now
00558 
00559         self.estop()
00560         
00561 
00562     def go_height_blocking(self, height):
00563         """ takes zenither up or down to height (moves slowly)
00564             blocking call
00565         """
00566         curHeight = self.get_position_meters()
00567         height = height-curHeight
00568         self.go_relative_blocking(height)
00569 
00570     def go_height(self, z, function=None, down_torque=0, up_torque=200):
00571         if self.robot != 'El-E':
00572             raise RuntimeError(
00573                 'This function is unemplemented on robot\'s other than El-E')
00574         
00575         z = ut.bound(z,0.0, .89)
00576 
00577         initial_direction = None
00578         
00579         if not ut.approx_equal(z, self.get_position_meters()):
00580             
00581             if z > self.get_position_meters():
00582                 self.zenith(torque = up_torque)
00583                 initial_direction = 'up'
00584             else:
00585                 self.zenith(torque = down_torque)
00586                 initial_direction = 'down'
00587         else:
00588             return
00589 
00590         there         = False
00591         
00592         while not ut.approx_equal(z, self.get_position_meters()):
00593             if function != None:
00594                 
00595                 ret = function(self.get_position_meters())
00596                 if ret:
00597                     break
00598 
00599             
00600             if initial_direction == 'up' and self.get_position_meters() > z:
00601                 break
00602 
00603             if initial_direction == 'down' and self.get_position_meters() < z:
00604                 break
00605 
00606         self.estop()
00607 
00608     def limit_position(self, position):
00609 
00610         position = ut.unipolar_limit(position, 
00611                                      self.calib['POS_MAX'])
00612         return position
00613 
00614     def limit_velocity(self,velocity):
00615         if velocity == None:
00616             velocity = self.calib['VEL_DEFAULT']
00617             return velocity
00618 
00619         velocity = ut.unipolar_limit(velocity, 
00620                                      self.calib['VEL_MAX'])
00621 
00622         return velocity
00623 
00624     def limit_acceleration(self,acceleration):
00625         if acceleration == None:
00626             acceleration = self.calib['ACC_DEFAULT']
00627             return acceleration
00628 
00629         acceleration = ut.unipolar_limit(acceleration, 
00630                                      self.calib['ACC_MAX'])
00631 
00632         return acceleration
00633 
00634     def move_position(self,position, velocity=None , acceleration=None, 
00635                       blocking = True, relative=False, approximate_pose = True):
00636 
00637         if self.robot != 'El-E' and self.robot != 'test_rig':
00638             raise RuntimeError(
00639                 'This function is unemplemented on robot\'s other than El-E, test_rig')
00640         if self.calib['HAS_BRAKE']:
00641             self.disengage_brake()
00642 
00643         if position>self.calib['max_height']:
00644             position = self.calib['max_height']
00645 
00646         if position<self.calib['min_height']:
00647             position = self.calib['min_height']
00648 
00649         self.use_position_mode()       
00650 
00651         if relative:
00652             position = self.get_position_meters() + position
00653         
00654         position_zenither_coord = position - self.calib['ZERO_BIAS']
00655         position_zenither_coord = self.limit_position(position_zenither_coord)
00656         
00657         acceleration = self.limit_acceleration( acceleration )
00658         velocity = self.limit_velocity( velocity )
00659 
00660         p = self.get_position_meters()
00661 
00662         if p >= position:
00663             
00664             
00665             self.serial_lock.acquire()
00666             self.servo.write('KGON\n')       
00667             self.serial_lock.release()
00668             if velocity > 4.0 or velocity < -4.0:
00669                 velocity = 4.0               
00670 
00671         else:
00672             
00673             
00674             self.serial_lock.acquire()
00675             self.servo.write('KGOFF\n')       
00676             self.serial_lock.release()
00677             if velocity > 3.0 or velocity < -3.0:
00678                 velocity = 3.0               
00679 
00680         self.set_pos_absolute(position_zenither_coord)
00681 
00682         self.set_velocity(velocity)
00683         self.set_acceleration(acceleration)
00684         self.go()
00685 
00686         if blocking:
00687             have_not_moved = 0
00688             
00689             current_pose = self.get_position_meters()
00690             last_pose = None
00691             print 'zenither.move_position: blocking...'
00692             while abs(current_pose - position) > 0.01:
00693                 time.sleep(200/1000.0)
00694                 current_pose = self.get_position_meters()
00695                 
00696             print 'zenither.move_position: done'
00697 
00698         if self.calib['HAS_BRAKE']:
00699             self.engage_brake()
00700 
00701 
00702     def check_mode(self):
00703         pass
00704 
00705     def checkbits(self):
00706         self.serial_lock.acquire()
00707         self.servo.write("c=UCI\n")
00708         self.servo.write("Rc\n")
00709         c = self.servo.readline(eol='\r')
00710         self.servo.write("d=UDI\n")
00711         self.servo.write("Rd\n")
00712         d = self.servo.readline(eol='\r')
00713         self.serial_lock.release()
00714         return [c,d]
00715 
00716     def get_motor_torque(self):
00717         self.serial_lock.acquire()
00718         self.servo.write("t=T\n")
00719         self.servo.write("Rt\n")
00720         t = self.servo.readline(eol='\r')
00721         self.serial_lock.release()
00722         return t
00723 
00724     def get_motor_temperature_F(self):
00725         return self.get_motor_temperature_C()*9.0/5.0 + 32.0
00726 
00727     def get_motor_temperature_C(self):
00728         self.serial_lock.acquire()
00729         self.servo.write("t=TEMP\n")
00730         self.servo.write("Rt\n")
00731         t = float(self.servo.readline(eol='\r'))
00732         self.serial_lock.release()
00733         return t
00734 
00735     def inputs_init(self):
00736         self.serial_lock.acquire()
00737         self.servo.write("UCI\n")
00738         self.servo.write("UDI\n")
00739         self.serial_lock.release()
00740 
00741     def engage_brake(self):
00742         self.serial_lock.acquire()
00743         if self.brake_engaged == False:
00744             self.servo.write("BRKENG\n") 
00745             self.brake_engaged = True
00746         self.serial_lock.release()
00747 
00748     def disengage_brake(self):
00749         self.serial_lock.acquire()
00750         if self.brake_engaged:
00751             self.servo.write("BRKRLS\n") 
00752             self.brake_engaged = False
00753         self.serial_lock.release()
00754 
00755     def engage_brake_when_not_servoing(self):
00756         self.serial_lock.acquire()
00757         if self.brake_engaged == False:
00758             self.servo.write("BRKSRV\n") 
00759                                          
00760             self.brake_engaged = True
00761         self.serial_lock.release()
00762 
00763     def engage_brake_on_without_trajectory(self):
00764         self.serial_lock.acquire()
00765         if self.brake_engaged == False:
00766             self.servo.write("BRKTRJ\n") 
00767                                          
00768             self.brake_engaged = True
00769         self.serial_lock.release()
00770 
00771 
00772 
00773 
00774 
00775 
00776 
00777 
00778 
00779 
00780 
00781 
00782 
00783 
00784 
00785 
00786 
00787 
00788 
00789 
00790 
00791 
00792 
00793