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