arbotix.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright (c) 2008-2011 Vanadium Labs LLC.
4 # All right reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 # * Neither the name of Vanadium Labs LLC nor the names of its
15 # contributors may be used to endorse or promote products derived
16 # from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL VANADIUM LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 # Author: Michael Ferguson
30 
31 
32 
33 import serial, time, sys, threading
34 from arbotix_python.ax12 import *
35 from struct import unpack, pack
36 
37 
38 class ArbotiXException(Exception):
39  pass
40 
41 
42 class ArbotiX:
43 
44 
54  def __init__(self, port="/dev/ttyUSB0", baud=115200, timeout=0.1, open_port=True):
55  self._mutex = threading._allocate_lock()
56  self._ser = serial.Serial()
57 
58  self._ser.port = port
59  self._ser.baudrate = baud
60  self._ser.timeout = timeout
61 
62  if open_port:
63  self._ser.open()
64 
65 
66  self.error = 0
67 
68  def __write__(self, msg):
69  try:
70  self._ser.write(msg)
71  except serial.SerialException as e:
72  self._mutex.release()
73  raise ArbotiXException(e)
74 
75  def openPort(self):
76  self._ser.close()
77  try:
78  self._ser.open()
79  except serial.SerialException as e:
80  raise ArbotiXException(e)
81 
82  def closePort(self):
83  self._ser.close()
84 
85 
90  def getPacket(self, mode, id=-1, leng=-1, error=-1, params = None):
91  try:
92  d = self._ser.read()
93  except Exception as e:
94  print(e)
95  return None
96  # need a positive byte
97  if not d or d == '':
98  return None
99 
100  # now process our byte
101  if mode == 0: # get our first 0xFF
102  if d == b'\xff':
103  return self.getPacket(1)
104  else:
105  return self.getPacket(0)
106  elif mode == 1: # get our second 0xFF
107  if d == b'\xff':
108  return self.getPacket(2)
109  else:
110  return self.getPacket(0)
111  elif mode == 2: # get id
112  if d != b'\xff':
113  return self.getPacket(3, ord(d))
114  else:
115  return self.getPacket(0)
116  elif mode == 3: # get length
117  return self.getPacket(4, id, ord(d))
118  elif mode == 4: # read error
119  self.error = d
120  if leng == 2:
121  return self.getPacket(6, id, leng, ord(d), list())
122  else:
123  return self.getPacket(5, id, leng, ord(d), list())
124  elif mode == 5: # read params
125  params.append(ord(d))
126  if len(params) + 2 == leng:
127  return self.getPacket(6, id, leng, error, params)
128  else:
129  return self.getPacket(5, id, leng, error, params)
130  elif mode == 6: # read checksum
131  checksum = id + leng + error + sum(params) + ord(d)
132  if checksum % 256 != 255:
133  return None
134  return params
135  # fail
136  return None
137 
138 
149  def execute(self, index, ins, params, ret=True):
150  values = None
151  self._mutex.acquire()
152  try:
153  self._ser.flushInput()
154  except Exception as e:
155  print(e)
156  length = 2 + len(params)
157  checksum = 255 - ((index + length + ins + sum(params))%256)
158  packet = bytearray()
159  packet.append(0xFF)
160  packet.append(0xFF)
161  packet.append(index)
162  packet.append(length)
163  packet.append(ins)
164  self.__write__(packet)
165  for val in params:
166  self.__write__(bytes([val]))
167  self.__write__(bytes([checksum]))
168  if ret:
169  values = self.getPacket(0)
170  self._mutex.release()
171  return values
172 
173 
182  def read(self, index, start, length):
183  values = self.execute(index, AX_READ_DATA, [start, length])
184  if values == None:
185  return -1
186  else:
187  return values
188 
189 
198  def write(self, index, start, values):
199  self.execute(index, AX_WRITE_DATA, [start] + values)
200  return self.error
201 
202 
208  def syncWrite(self, start, values):
209  output = list()
210  for i in values:
211  output = output + i
212  length = len(output) + 4 # length of overall packet
213  lbytes = len(values[0])-1 # length of bytes to write to a servo
214  self._mutex.acquire()
215  try:
216  self._ser.flushInput()
217  except:
218  pass
219  packet = bytearray()
220  packet.append(0xFF)
221  packet.append(0xFF)
222  packet.append(254)
223  packet.append(length)
224  packet.append(AX_SYNC_WRITE)
225  self.__write__(packet)
226  self.__write__(bytes([start])) # start address
227  self.__write__(bytes([lbytes])) # bytes to write each servo
228  for i in output:
229  self.__write__(bytes([i]))
230  checksum = 255 - ((254 + length + AX_SYNC_WRITE + start + lbytes + sum(output))%256)
231  self.__write__(bytes([checksum]))
232  self._mutex.release()
233 
234 
243  def syncRead(self, servos, start, length):
244  return self.execute(0xFE, AX_SYNC_READ, [start, length] + servos )
245 
246 
253  def setBaud(self, index, baud):
254  return self.write(index, P_BAUD_RATE, [baud, ])
255 
256 
261  def getReturnLevel(self, index):
262  try:
263  return int(self.read(index, P_RETURN_LEVEL, 1)[0])
264  except:
265  return -1
266 
267 
274  def setReturnLevel(self, index, value):
275  return self.write(index, P_RETURN_LEVEL, [value])
276 
277 
282  def enableTorque(self, index):
283  return self.write(index, P_TORQUE_ENABLE, [1])
284 
285 
290  def disableTorque(self, index):
291  return self.write(index, P_TORQUE_ENABLE, [0])
292 
293 
300  def setLed(self, index, value):
301  return self.write(index, P_LED, [value])
302 
303 
310  def setPosition(self, index, value):
311  return self.write(index, P_GOAL_POSITION_L, [value%256, value>>8])
312 
313 
320  def setSpeed(self, index, value):
321  return self.write(index, P_GOAL_SPEED_L, [value%256, value>>8])
322 
323 
328  def getPosition(self, index):
329  values = self.read(index, P_PRESENT_POSITION_L, 2)
330  try:
331  return int(values[0]) + (int(values[1])<<8)
332  except:
333  return -1
334 
335 
340  def getSpeed(self, index):
341  values = self.read(index, P_PRESENT_SPEED_L, 2)
342  try:
343  return int(values[0]) + (int(values[1])<<8)
344  except:
345  return -1
346 
347 
352  def getGoalSpeed(self, index):
353  values = self.read(index, P_GOAL_SPEED_L, 2)
354  try:
355  return int(values[0]) + (int(values[1])<<8)
356  except:
357  return -1
358 
359 
364  def getVoltage(self, index):
365  try:
366  return int(self.read(index, P_PRESENT_VOLTAGE, 1)[0])/10.0
367  except:
368  return -1
369 
370 
375  def getTemperature(self, index):
376  try:
377  return int(self.read(index, P_PRESENT_TEMPERATURE, 1)[0])
378  except:
379  return -1
380 
381 
386  def isMoving(self, index):
387  try:
388  d = self.read(index, P_MOVING, 1)[0]
389  except:
390  return True
391  return d != 0
392 
393 
396  def enableWheelMode(self, index):
397  self.write(index, P_CCW_ANGLE_LIMIT_L, [0,0])
398 
399 
407  def disableWheelMode(self, index, resolution=10):
408  resolution = (2 ** resolution) - 1
409  self.write(index, P_CCW_ANGLE_LIMIT_L, [resolution%256,resolution>>8])
410 
411 
412  FORWARD = 0
413 
414  BACKWARD = 1
415 
416 
425  def setWheelSpeed(self, index, direction, speed):
426  if speed > 1023:
427  speed = 1023
428  if direction == self.FORWARD:
429  # 0~1023 is forward, it is stopped by setting to 0 while rotating to CCW direction.
430  self.write(index, P_GOAL_SPEED_L, [speed%256, speed>>8])
431  else:
432  # 1024~2047 is backward, it is stopped by setting to 1024 while rotating to CW direction.
433  speed += 1024
434  self.write(index, P_GOAL_SPEED_L, [speed%256, speed>>8])
435 
436 
438 
439 
440  LOW = 0
441 
442  HIGH = 0xff
443 
444  INPUT = 0
445 
446  OUTPUT = 0xff
447 
448  # ArbotiX-specific register table
449  # We do Model, Version, ID, Baud, just like the AX-12
450 
451  REG_DIGITAL_IN0 = 5
452  REG_DIGITAL_IN1 = 6
453  REG_DIGITAL_IN2 = 7
454  REG_DIGITAL_IN3 = 8
455 
456  REG_RESCAN = 15
457  # 16, 17 = RETURN, ALARM
458 
460  ANA_BASE = 18
461 
463  SERVO_BASE = 26
464  # Address 46 is Moving, just like an AX-12
465  REG_DIGITAL_OUT0 = 47
466 
467 
468  def rescan(self):
469  self.write(253, self.REG_RESCAN, [1,])
470 
471 
478  def getAnalog(self, index, leng=1):
479  try:
480  val = self.read(253, self.ANA_BASE+int(index), leng)
481  return sum(val[i] << (i * 8) for i in range(leng))
482  except:
483  return -1
484 
485 
490  def getDigital(self, index):
491  try:
492  if index < 32:
493  x = self.read(253, self.REG_DIGITAL_IN0 + int(index/8), 1)[0]
494  else:
495  return -1
496  except:
497  return -1
498  if x & (2**(index%8)):
499  return 255
500  else:
501  return 0
502 
503 
512  def setDigital(self, index, value, direction=0xff):
513  if index > 31: return -1
514  if value == 0 and direction > 0:
515  self.write(253, self.REG_DIGITAL_OUT0 + int(index), [1])
516  elif value > 0 and direction > 0:
517  self.write(253, self.REG_DIGITAL_OUT0 + int(index), [3])
518  elif value > 0 and direction == 0:
519  self.write(253, self.REG_DIGITAL_OUT0 + int(index), [2])
520  else:
521  self.write(253, self.REG_DIGITAL_OUT0 + int(index), [0])
522  return 0
523 
524 
532  def setServo(self, index, value):
533  if index > 7: return -1
534  if value != 0 and (value < 500 or value > 2500):
535  print("ArbotiX Error: Servo value out of range:", value)
536  else:
537  self.write(253, self._SERVO_BASE + 2*index, [value%256, value>>8])
538  return 0
539 
arbotix_python.arbotix.ArbotiX.rescan
def rescan(self)
Force the ArbotiX2 to rescan the Dynamixel busses.
Definition: arbotix.py:468
arbotix_python.arbotix.ArbotiX.ANA_BASE
int ANA_BASE
Register address of first analog port (read only).
Definition: arbotix.py:460
arbotix_python.arbotix.ArbotiX.REG_RESCAN
int REG_RESCAN
Register address for triggering rescan.
Definition: arbotix.py:456
arbotix_python.arbotix.ArbotiX.error
error
The last error level read back.
Definition: arbotix.py:66
arbotix_python.arbotix.ArbotiXException
ArbotiX errors.
Definition: arbotix.py:38
arbotix_python.arbotix.ArbotiX.getPacket
def getPacket(self, mode, id=-1, leng=-1, error=-1, params=None)
Read a dynamixel return packet in an iterative attempt.
Definition: arbotix.py:90
arbotix_python.arbotix.ArbotiX.disableTorque
def disableTorque(self, index)
Turn on the torque of a servo.
Definition: arbotix.py:290
arbotix_python.arbotix.ArbotiX.getReturnLevel
def getReturnLevel(self, index)
Get the return level of a device.
Definition: arbotix.py:261
arbotix_python.ax12
Definition: ax12.py:1
arbotix_python.arbotix.ArbotiX.setPosition
def setPosition(self, index, value)
Set the position of a servo.
Definition: arbotix.py:310
arbotix_python.arbotix.ArbotiX.syncRead
def syncRead(self, servos, start, length)
Read values of registers on many servos.
Definition: arbotix.py:243
arbotix_python.arbotix.ArbotiX.getDigital
def getDigital(self, index)
Get the value of an digital input pin.
Definition: arbotix.py:490
arbotix_python.arbotix.ArbotiX.__init__
def __init__(self, port="/dev/ttyUSB0", baud=115200, timeout=0.1, open_port=True)
Constructs an ArbotiX instance, optionally opening the serial connection.
Definition: arbotix.py:54
arbotix_python.arbotix.ArbotiX.__write__
def __write__(self, msg)
Definition: arbotix.py:68
arbotix_python.arbotix.ArbotiX.setDigital
def setDigital(self, index, value, direction=0xff)
Get the value of an digital input pin.
Definition: arbotix.py:512
arbotix_python.arbotix.ArbotiX.setBaud
def setBaud(self, index, baud)
Set baud rate of a device.
Definition: arbotix.py:253
arbotix_python.arbotix.ArbotiX.enableTorque
def enableTorque(self, index)
Turn on the torque of a servo.
Definition: arbotix.py:282
arbotix_python.arbotix.ArbotiX.setLed
def setLed(self, index, value)
Set the status of the LED on a servo.
Definition: arbotix.py:300
arbotix_python.arbotix.ArbotiX.FORWARD
int FORWARD
Direction definition for setWheelSpeed.
Definition: arbotix.py:412
arbotix_python.arbotix.ArbotiX.closePort
def closePort(self)
Definition: arbotix.py:82
arbotix_python.arbotix.ArbotiX.write
def write(self, index, start, values)
Write values to registers.
Definition: arbotix.py:198
arbotix_python.arbotix.ArbotiX.openPort
def openPort(self)
Definition: arbotix.py:75
arbotix_python.arbotix.ArbotiX.REG_DIGITAL_IN0
int REG_DIGITAL_IN0
Register base address for reading digital ports.
Definition: arbotix.py:451
arbotix_python.arbotix.ArbotiX.getSpeed
def getSpeed(self, index)
Get the speed of a servo.
Definition: arbotix.py:340
arbotix_python.arbotix.ArbotiX.setServo
def setServo(self, index, value)
Set the position of a hobby servo.
Definition: arbotix.py:532
arbotix_python.arbotix.ArbotiX.setReturnLevel
def setReturnLevel(self, index, value)
Set the return level of a device.
Definition: arbotix.py:274
arbotix_python.arbotix.ArbotiX.getGoalSpeed
def getGoalSpeed(self, index)
Get the goal speed of a servo.
Definition: arbotix.py:352
arbotix_python.arbotix.ArbotiX.syncWrite
def syncWrite(self, start, values)
Write values to registers on many servos.
Definition: arbotix.py:208
arbotix_python.arbotix.ArbotiX.getVoltage
def getVoltage(self, index)
Get the voltage of a device.
Definition: arbotix.py:364
arbotix_python.arbotix.ArbotiX.getPosition
def getPosition(self, index)
Get the position of a servo.
Definition: arbotix.py:328
arbotix_python.arbotix.ArbotiX.disableWheelMode
def disableWheelMode(self, index, resolution=10)
Put a servo into servo mode.
Definition: arbotix.py:407
arbotix_python.arbotix.ArbotiX.enableWheelMode
def enableWheelMode(self, index)
Put a servo into wheel mode (continuous rotation).
Definition: arbotix.py:396
arbotix_python.arbotix.ArbotiX.setSpeed
def setSpeed(self, index, value)
Set the speed of a servo.
Definition: arbotix.py:320
arbotix_python.arbotix.ArbotiX.setWheelSpeed
def setWheelSpeed(self, index, direction, speed)
Set the speed and direction of a servo which is in wheel mode (continuous rotation).
Definition: arbotix.py:425
arbotix_python.arbotix.ArbotiX.getTemperature
def getTemperature(self, index)
Get the temperature of a device.
Definition: arbotix.py:375
arbotix_python.arbotix.ArbotiX.execute
def execute(self, index, ins, params, ret=True)
Send an instruction to the device.
Definition: arbotix.py:149
arbotix_python.arbotix.ArbotiX._ser
_ser
Definition: arbotix.py:56
arbotix_python.arbotix.ArbotiX._mutex
_mutex
Definition: arbotix.py:55
arbotix_python.arbotix.ArbotiX.isMoving
def isMoving(self, index)
Determine if a device is moving.
Definition: arbotix.py:386
arbotix_python.arbotix.ArbotiX.read
def read(self, index, start, length)
Read values of registers.
Definition: arbotix.py:182
arbotix_python.arbotix.ArbotiX
This class controls an ArbotiX, USBDynamixel, or similar board through a serial connection.
Definition: arbotix.py:42
arbotix_python.arbotix.ArbotiX.REG_DIGITAL_OUT0
int REG_DIGITAL_OUT0
Definition: arbotix.py:465
arbotix_python.arbotix.ArbotiX.getAnalog
def getAnalog(self, index, leng=1)
Get the value of an analog input pin.
Definition: arbotix.py:478


arbotix_python
Author(s): Michael Ferguson
autogenerated on Tue Mar 1 2022 23:48:25