robotis_servo.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 #
00003 # Copyright (c) 2009, Georgia Tech Research Corporation
00004 # All rights reserved.
00005 # 
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions are met:
00008 #     * Redistributions of source code must retain the above copyright
00009 #       notice, this list of conditions and the following disclaimer.
00010 #     * Redistributions in binary form must reproduce the above copyright
00011 #       notice, this list of conditions and the following disclaimer in the
00012 #       documentation and/or other materials provided with the distribution.
00013 #     * Neither the name of the Georgia Tech Research Corporation nor the
00014 #       names of its contributors may be used to endorse or promote products
00015 #       derived from this software without specific prior written permission.
00016 # 
00017 # THIS SOFTWARE IS PROVIDED BY GEORGIA TECH RESEARCH CORPORATION ''AS IS'' AND
00018 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00019 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00020 # DISCLAIMED. IN NO EVENT SHALL GEORGIA TECH BE LIABLE FOR ANY DIRECT, INDIRECT,
00021 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00022 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00023 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00024 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00025 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00026 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 #
00028 
00029 ## Controlling Robotis Dynamixel servos from python using the USB2Dynamixel adaptor.
00030 ## author Advait Jain (Healthcare Robotics Lab, Georgia Tech.)
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 #        print 'data[1],data[0]:', data[1],data[0]
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 # instruction includes the command (1 byte + parameters. length = parameters+2)
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 


robotis
Author(s): Travis Deyle, Advait Jain, Marc Killpack, Advisor: Prof. Charlie Kemp, Lab: Healthcare Robotics Lab at Georgia Tech
autogenerated on Wed Nov 27 2013 11:37:53