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
00030
00031
00032
00033
00034 import serial
00035 import time
00036 import sys, optparse
00037 import servo_config as sc
00038 import math
00039
00040 class robotis_servo():
00041 ''' class to use a robotis servo.
00042 '''
00043 def __init__(self, dev_name, servo_id, baudrate=57600,
00044 max_speed=math.radians(50)):
00045 ''' dev_name - name of serial device of the servo controller (e.g. '/dev/robot/servo0')
00046 servo_id - 2,3,4 ... (2 to 253)
00047 baudrate - for the servo controller.
00048 max_speed - max allowable speed for the servo (radians/sec)
00049 '''
00050 self.dev_name = dev_name
00051 self.servo_dev = None
00052 self.__open_serial(baudrate)
00053
00054 print 'WARNING: robotis_servo.py is being deprecated.'
00055
00056 self.servo_id = servo_id
00057 self.home_encoder_value = sc.servo_param[servo_id]['home_encoder']
00058
00059 if sc.servo_param[servo_id].has_key('max_ang'):
00060 self.max_ang = sc.servo_param[servo_id]['max_ang']
00061 else:
00062 self.max_ang = math.radians( 95.0 )
00063
00064 if sc.servo_param[servo_id].has_key('min_ang'):
00065 self.min_ang = sc.servo_param[servo_id]['min_ang']
00066 else:
00067 self.min_ang = math.radians( -95.0 )
00068
00069 if sc.servo_param[servo_id].has_key('flipped'):
00070 self.flipped = True
00071 else:
00072 self.flipped = False
00073
00074 if self.read_location(3) == None:
00075 print 'robotis_servo.robotis_servo.__init__: Wrong servo ID- ', self.servo_id
00076
00077 self.fast_angvel = max_speed
00078
00079 def is_moving(self):
00080 ''' returns True if servo is moving.
00081 '''
00082 data,err = self.read_location(0x2e,1)
00083 return data[0]!=0
00084
00085 def read_voltage(self):
00086 ''' returns voltage (Volts)
00087 '''
00088 data,err = self.read_location(0x2a,1)
00089 return data[0]/10.
00090
00091 def read_temperature(self):
00092 ''' returns the temperature (Celcius)
00093 '''
00094 data,err = self.read_location(0x2b,1)
00095 return data[0]
00096
00097 def read_angle(self):
00098 ''' returns the current servo angle (radians)
00099 '''
00100 data,err = self.read_location(0x24,2)
00101 ang = (data[0]+data[1]*256-self.home_encoder_value)/1024. * 300.
00102 if self.flipped:
00103 ang = ang*-1.0
00104 return math.radians(ang)
00105
00106 def print_encoder_value(self):
00107 ''' position in encoder ticks
00108 '''
00109 data,err = self.read_location(0x24,2)
00110 enc_val = data[0]+data[1]*256
00111 print 'encoder_value:', enc_val
00112
00113 def read_load(self):
00114 ''' number proportional to the torque applied by the servo.
00115 sign etc. might vary with how the servo is mounted.
00116 '''
00117 data,err = self.read_location(0x28,2)
00118 load = data[0]+(data[1]>>6)*256
00119
00120 if data[1]>>2 & 1 == 0:
00121 return -load
00122 else:
00123 return load
00124
00125 def __torque_enable(self, n):
00126 self.write_location(0x18, [n])
00127
00128 def enable_torque(self):
00129 self.__torque_enable(1)
00130
00131 def disable_torque(self):
00132 self.__torque_enable(0)
00133
00134 def __move_to_encoder(self, n):
00135 ''' move to encoder position n
00136 '''
00137 hi,lo = n/256,n%256
00138 self.write_location(0x1e, [lo,hi])
00139
00140 def move_angle(self, ang, angvel=None,blocking=True):
00141 ''' move to angle (radians)
00142 '''
00143 if angvel == None:
00144 angvel = self.fast_angvel
00145
00146 if angvel>self.fast_angvel:
00147 print 'robotis_servo.move_angle: angvel too high - %.2f deg/s'%(math.degrees(angvel))
00148 print 'ignoring move command.'
00149 return
00150
00151 if ang > self.max_ang or ang < self.min_ang:
00152 print 'robotis_servo.move_angle: angle out of range- ', math.degrees(ang)
00153 return
00154 self.set_angvel(angvel)
00155 time.sleep(0.05)
00156
00157 deg = math.degrees(ang)
00158 if self.flipped:
00159 deg = deg * -1.0
00160 enc_ticks = int(round(deg/0.29))
00161 enc_ticks += self.home_encoder_value
00162 self.__move_to_encoder(enc_ticks)
00163
00164 if blocking == True:
00165 time.sleep(0.05)
00166 while(self.is_moving()):
00167 continue
00168 else:
00169 time.sleep(0.05)
00170
00171
00172 def set_angvel(self, angvel):
00173 ''' angvel - in rad/sec
00174 '''
00175 rpm = angvel/(2*math.pi)*60
00176 angvel_enc = int(round(rpm/0.111))
00177 hi,lo = angvel_enc/256,angvel_enc%256
00178 self.write_location(0x20, [lo,hi])
00179
00180 def write_id(self, id):
00181 ''' changes the servo id
00182 '''
00183 self.write_location(0x03,[id])
00184
00185 def write_location(self, address, data):
00186 ''' writes data at the address.
00187 data = [n1,n2 ...] list of numbers.
00188 '''
00189 msg = [0x03,address]+data
00190 self.send_instruction(msg,self.servo_id)
00191
00192 def read_location(self, address, nBytes=1):
00193 ''' reads nBytes from address on the servo.
00194 returns [n1,n2 ...], error
00195 list of parameters, error byte.
00196 '''
00197 msg = [0x02,address,nBytes]
00198 self.send_instruction(msg,self.servo_id)
00199 s = self.read_serial(6+nBytes)
00200 if s == '':
00201 print 'robotis_servo.read_location: Could not read from the servo.'
00202 print 'Ensure that the 3-way switch on the USB2Dynamixel is at RS485.'
00203 print 'Exiting...'
00204 sys.exit(0)
00205 l = [ord(a) for a in s[4:-1]]
00206 if l == []:
00207 return None
00208 return l[1:], l[0]
00209
00210 def __calc_checksum(self, msg):
00211 chksum = 0
00212 for m in msg:
00213 chksum += m
00214 chksum = (~chksum)%256
00215 return chksum
00216
00217 def send_instruction(self, instruction, id):
00218 msg = [id,len(instruction)+1]+instruction
00219 chksum = self.__calc_checksum(msg)
00220 msg = [0xff,0xff]+msg+[chksum]
00221 self.__send_serial(msg)
00222
00223 def __send_serial(self, msg):
00224 """ sends the command to the servo
00225 """
00226 str = ''
00227 for m in msg:
00228 str += chr(m)
00229
00230 self.servo_dev.flushInput()
00231 self.servo_dev.write(str)
00232
00233 def read_serial(self, nBytes=1):
00234 rep = self.servo_dev.read(nBytes)
00235 return rep
00236
00237 def __open_serial(self, baudrate):
00238 try:
00239 self.servo_dev = serial.Serial(self.dev_name, timeout=1.)
00240 self.servo_dev.setBaudrate(baudrate)
00241 self.servo_dev.setParity('N')
00242 self.servo_dev.setStopbits(1)
00243 self.servo_dev.open()
00244
00245 self.servo_dev.flushOutput()
00246 self.servo_dev.flushInput()
00247
00248 except (serial.serialutil.SerialException), e:
00249 raise RuntimeError("robotis_servo: Serial port not found!\n")
00250 if(self.servo_dev == None):
00251 raise RuntimeError("robotis_servo: Serial port not found!\n")
00252
00253
00254
00255 if __name__ == '__main__':
00256
00257 p = optparse.OptionParser()
00258 p.add_option('-d', action='store', type='string', dest='servo_dev_name',
00259 default='/dev/robot/servo0', help='servo device string. [default= /dev/robot/servo0]')
00260 p.add_option('--ang', action='store', type='float', dest='ang',
00261 help='angle to move the servo to (degrees).')
00262 p.add_option('--ang_vel', action='store', type='float', dest='ang_vel',
00263 help='angular velocity. (degrees/sec) [default = 50]', default=50)
00264 p.add_option('--id', action='store', type='int', dest='id',
00265 help='id of servo to connect to, [default = 2]', default=2)
00266
00267 opt, args = p.parse_args()
00268 servo_dev_name = opt.servo_dev_name
00269 ang = opt.ang
00270 ang_vel = opt.ang_vel
00271 id = opt.id
00272
00273 servo = robotis_servo(servo_dev_name,id)
00274 servo.move_angle(math.radians(ang), math.radians(ang_vel))
00275 time.sleep(0.5)
00276 print 'Servo angle:', math.degrees(servo.read_angle())
00277
00278