pyHand_api.py
Go to the documentation of this file.
1 # pyHand_api.py
2 #
3 # ~~~~~~~~~~~~
4 #
5 #
6 # pyHand_api based on pyHand_api.py from Barrett Technology Hand Communications
7 #
8 # ~~~~~~~~~~~~
9 #
10 # Software License Agreement (BSD License)
11 #
12 # Copyright (c) 2014, Robotnik Automation SLL
13 # All rights reserved.
14 #
15 # Redistribution and use in source and binary forms, with or without
16 # modification, are permitted provided that the following conditions
17 # are met:
18 #
19 # * Redistributions of source code must retain the above copyright
20 # notice, this list of conditions and the following disclaimer.
21 # * Redistributions in binary form must reproduce the above
22 # copyright notice, this list of conditions and the following
23 # disclaimer in the documentation and/or other materials provided
24 # with the distribution.
25 # * Neither the name of Robotnik Automation SSL nor the names of its
26 # contributors may be used to endorse or promote products derived
27 # from this software without specific prior written permission.
28 #
29 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
33 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
35 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
39 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 # POSSIBILITY OF SUCH DAMAGE.
41 
42 
43 from pcan_python.pcan_library import *
44 
45 
46 from puck_properties_consts import*
47 from ctypes import *
48 import time
49 import rospy
50 
51 BASE_TYPE = 0
52 TIP_TYPE = 1
53 SPREAD_TYPE = 2
54 BASE_LIMIT = 140.0
55 TIP_LIMIT = 48.0
56 SPREAD_LIMIT = 180.0
57 
58 HAND_GROUP = 0x405
59 FORCE_DATA_GROUP = 0x400
60 
61 # CAN MSG IDs
62 F1_POSITION = 0x563
63 F2_POSITION = 0x583
64 F3_POSITION = 0x5A3
65 SPREAD_POSITION = 0x5C3
66 
67 F1_STRAIN = 0x566
68 F2_STRAIN = 0x586
69 F3_STRAIN = 0x5A6
70 SPREAD_STRAIN = 0x5C6
71 STRAIN_ID = 0x99
72 
73 F1_MOTOR_TEMP = 0x566
74 F2_MOTOR_TEMP = 0x586
75 F3_MOTOR_TEMP = 0x5A6
76 SPREAD_MOTOR_TEMP = 0x5C6
77 MOTOR_TEMP_ID = 0x89
78 
79 F1_MOTOR_THERM = 0x566
80 F2_MOTOR_THERM = 0x586
81 F3_MOTOR_THERM = 0x5A6
82 SPREAD_MOTOR_THERM = 0x5C6
83 MOTOR_THERM_ID = 0x94
84 
85 F1_TACT = 0x569
86 F2_TACT = 0x589
87 F3_TACT = 0x5A9
88 PALM_TACT = 0x5C9
89 TACT_ID = 0x40
90 
91 FT_SENSOR_FORCE = 0x50A
92 FT_SENSOR_TORQUE = 0x50B
93 
94 CAN_DELAY = 0.025
95 INIT_ATTEMPTS = 5
96 
97 #==========================CAN_STUFF=======================================
98 
99 # Class pyHand based on library pyHand_api.py
100 class pyHand:
101 
102  def __init__(self, port = '/dev/pcan32'):
103  '''
104  Creates a PCANBasic object
105 
106  @param port: CAN port used to communicate with the hand
107  @type connection: string
108  '''
109 
110  self.PCAN = PCANBasic(port)
111 
112  self.motor_positions = {FINGER1: {'encoder': [0, 0], 'position': [0.0, 0.0]}, FINGER2: {'encoder': [0, 0], 'position': [0.0, 0.0]}, FINGER3: {'encoder': [0, 0], 'position': [0.0, 0.0]},
113  SPREAD: {'encoder': [0, 0], 'position': [0.0, 0.0]}}
114  self.strain = {FINGER1: 0, FINGER2: 0, FINGER3: 0, SPREAD: 0}
115  self.temp = {FINGER1:{ 'temp': 0.0, 'therm': 0.0}, FINGER2: { 'temp': 0.0, 'therm': 0.0}, FINGER3: { 'temp': 0.0, 'therm': 0.0},
116  SPREAD: { 'temp': 0.0, 'therm': 0.0}}
117  self.tactile_sensor = {FINGER1: {'values': range(0,24), 'data': range(0,5)}, FINGER2: {'values': range(0,24), 'data': range(0,5)},
118  FINGER3: {'values': range(0,24), 'data': range(0,5)}, SPREAD: {'values': range(0,24), 'data': range(0,5)}}
119 
120  self.ft_sensor = {'force': [0.0, 0.0, 0.0], 'torque': [0.0, 0.0, 0.0]}
121 
122  def check_error(self, connection,result,location_of_error):
123  '''
124  Checks error on the CAN Bus.
125 
126  @param connection: The connection on which the CAN is talking. For example, PCANBasic()
127  @type connection: PCANBasic
128  @param result: The number corresponding to the error returned.
129  @type resulr: int
130  @param location_of_error: A description of where the error occurred.
131  @type location_of_error: str
132  '''
133  if result == PCAN_ERROR_OK:
134  pass
135  else:
136  raise Exception("Error Number: " + hex(result) + " while attempting to " + str(location_of_error) + "\n" + connection.GetErrorText(result)[1])
137 
138  def enum(self):
139  '''
140  Finds and returns all of the pucks that are attached to the bus.
141 
142  @rtype: Array[]
143  @return: An array containing the pucks attached to the bus.
144  '''
145  pucks = []
146  for i in range(32):
147  try:
148  self.get_property(i,1)
149  pucks.append(i)
150  except:
151  pass
152  return pucks
153 
154  def can_reset(self):
155  '''
156  Resets the CAN connection.
157  Note that this may cause a loss of data, but may also clear unwanted data. Utilize as needed.
158  '''
159  reset_result=self.PCAN.Reset(PCAN_USBBUS1)
160  try:
161  self.check_error(self.PCAN,reset_result,"reset")
162  except Exception, e:
163  rospy.logerr('pyHand: can_reset: error: %s', e)
164  return False
165  time.sleep(CAN_DELAY)
166  return True
167 
168  def can_status(self):
169  '''
170  Returns the status of the CAN connection as specified in PCANBasic's GetStatus method.
171  '''
172  status_result=self.PCAN.GetStatus(PCAN_USBBUS1)
173  time.sleep(CAN_DELAY)
174  return status_result
175 
176  def can_init(self):
177  '''
178  Initializes the CAN connection. Note that this does not initialize the hand itself.
179  '''
180  # initialize self.PCAN bus
181  init_result=self.PCAN.Initialize(PCAN_USBBUS1, PCAN_BAUD_1M)
182  self.check_error(self.PCAN,init_result,"initialize")
183  time.sleep(CAN_DELAY)
184 
185  def can_uninit(self):
186  '''
187  Uninitializes the CAN connection. Rarely used.
188  '''
189  uninit_result=self.PCAN.Uninitialize(PCAN_USBBUS1)
190  self.check_error(self.PCAN,uninit_result,"uninitialize")
191 
192  #============================INIT_STUFF==================================
193 
194  def initialize(self):
195  '''
196  Wakes up pucks and initializes the CAN.
197  This function must be implemented at the beginning of a program for this library to properly work.
198 
199  @rtype: ROLE
200  @return: First finger's ROLE property if initialization is successful. Otherwise it returns False.
201  '''
202  try:
203  self.can_init()
204  #Reset Can bus
205  self.can_reset()
206  time.sleep(CAN_DELAY)
207  # wake up pucks by setting the STAT(5) property to READY(2)
208  self.set_property(0x400, 5, 2)
209  time.sleep(1)
210  self.can_reset()
211 
212  return True
213  except Exception, e:
214  rospy.logerr('pyHand: Initialize: Error: %s', e)
215  return False
216 
217  def init_hand(self):
218  '''
219  Initialize all pucks in the hand.
220 
221  @rtype: Boolean
222  @return: Succesfully Initialized Hand?
223  '''
224  try:
225  #write 13 to the command property (CMD-29) of each of the pucks.
226  self.init_finger(FINGER1)
227  self.init_finger(FINGER2)
228  self.init_finger(FINGER3)
229  time.sleep(3)
230  self.init_finger(SPREAD)
231  time.sleep(2)
232  self.can_reset()
233  except Exception, e:
234  rospy.logerr('pyHand: init_hand: error: %s', e)
235  self.can_reset()
236  return False
237 
238  attempts = 0
239 
240  while attempts < INIT_ATTEMPTS:
241  try:
242  self.get_property(FINGER1, ROLE)
243  return True
244  except Exception, e:
245  rospy.logerr('pyHand: init_hand: error: (%d) %s', attempts, e)
246  attempts += 1
247 
248  return False
249 
250 
251  def init_finger(self, msgID):
252  '''
253  Sends a command to the puck to wake up the motor attatched to the puck.
254 
255  @param msgID: The id of the finger to initialize.
256  @type msgID: int
257  '''
258  self.set_property(msgID, CMD, CMD_HI)
259 
260 
261  def initialize_fts(self):
262  '''
263  Wake pucks and tare sensor.
264  '''
265  self.set_property(FTS, STAT, 2)
266  self.tare_fts()
267 
268  #============================SET_AND_GET_STUFF=========================
269 
270  def read_msg(self):
271  '''
272  Read a general message from PCAN_USBBUS1
273  Typically, msg[1] (where msg is the thing returned), contains the pertinent information.
274 
275  @rtype: (TPCANStatus, TPCANMsg, TPCANTimestamp)
276  @return: A tuple containing the status, message, and timestamp.
277  '''
278  return self.PCAN.Read(PCAN_USBBUS1)
279 
280  #def write_msg(self, msgID, data, delay=.015):
281 
282  def write_msg(self, msgID, data, delay=.002):
283  '''
284  Send a general message to PCAN_USBBUS1. This can be a get, set, or even garbage.
285 
286  @param msgID: The puck or group to which the message will be sent.
287  @type msgID: int
288  @param data: The array containing the data for the TPCANMsg.
289  @type data: Array[]
290  @param delay: The time delay to wait for the message to be written.
291  @type delay: float
292  @rtype: TPCANStatus
293  @return: Status of the self.PCAN bus.
294  '''
295  msg = TPCANMsg()
296  msg.ID = msgID
297  msg.LEN = len(data)
298  msg.MSGTYPE = PCAN_MESSAGE_STANDARD
299  for j in range(0, len(data)):
300  msg.DATA[j] = data[j]
301  stat = self.PCAN.Write(PCAN_USBBUS1, msg)
302  self.check_error(self.PCAN,stat,"write")
303  time.sleep(delay)
304  return stat
305 
306 
307  def send_msg(self, msgID, data):
308  '''
309  Send a general message to PCAN_USBBUS1. This can be a get, set, or even garbage.
310  It does not apply any sleep
311 
312  @param msgID: The puck or group to which the message will be sent.
313  @type msgID: int
314  @param data: The array containing the data for the TPCANMsg.
315  @type data: Array[]
316 
317  @return the return status after writing in the bus
318  '''
319  msg = TPCANMsg()
320  msg.ID = msgID
321  msg.LEN = len(data)
322  msg.MSGTYPE = PCAN_MESSAGE_STANDARD
323  for j in range(0, len(data)):
324  msg.DATA[j] = data[j]
325  stat = self.PCAN.Write(PCAN_USBBUS1, msg)
326  self.check_error(self.PCAN,stat,"write")
327 
328  return stat
329 
330  def set_property(self, msgID, propID, value):
331  '''
332  Set property to a given value.
333 
334  @param msgID: The puck or group whose property will be set.
335  @type msgID: int
336  @param propID: The number corresponding to the property to be set.
337  @type propID: int
338  @param value: The value to which the property will be set.
339  @type value: int
340  '''
341  is32bits = [48, 50, 52, 54, 56, 58, 66, 68, 74, 88, 96, 98]
342  if propID in is32bits:
343  self.set_32(msgID, propID, value)
344  else:
345  self.set_16(msgID, propID, value)
346 
347  def set_32(self, msgID, propID, value):
348  '''
349  Set property to a given value for a 32 bit property.
350  Avoid usage of this method. Use self.set_property instead.
351 
352  @param msgID: The puck or group whose property will be set.
353  @type msgID: int
354  @param propID: The number corresponding to the property to be set.
355  @type propID: int
356  @param value: The value to which the property will be set.
357  @type value: int
358  '''
359  data = [0x80+propID, 0, value%0x100, int(value/0x100)%0x100, int(value/0x10000)%0x100, int(value/0x1000000)]
360  #self.write_msg(msgID, data)
361  return self.send_msg(msgID, data)
362 
363  def set_16(self, msgID, propID, value):
364  '''
365  Set property to a given value for a 16 bit property.
366  Avoid usage of this method. Use self.set_property instead.
367 
368  @param msgID: The puck or group whose property will be set.
369  @type msgID: int
370  @param propID: The number corresponding to the property to be set.
371  @type propID: int
372  @param value: The value to which the property will be set.
373  @type value: int
374  '''
375  data = [0x80+propID, 0, value%256, int(value/256)]
376  #self.write_msg(msgID, data)
377  return self.send_msg(msgID, data)
378 
379 
380  # TODO: Allow a "GET" from a group.
381  def get_property(self, msgID, propID):
382  '''
383  Get property from pucks in msgID.
384 
385  @param msgID: The puck whose property will be read from.
386  @type msgID: int
387  @param propID: The property be read from.
388  @type propID: int
389  @rtype: int
390  @return: The value held in the property.
391  '''
392  is32bits = [48, 50, 52, 54, 56, 58, 66, 68, 74, 88, 96, 98]
393  if propID in is32bits:
394  return self.get_32(msgID, propID)
395  else:
396  if propID == TACT:
397  return self.get_tact(msgID)
398  return self.get_16(msgID, propID)
399 
400 
401  def get_32(self, msgID, propID):
402  '''
403  Gets a 32 bit property. Please use get_property instead of this method where applicable.
404 
405  @param msgID: The puck whose property will be read from.
406  @type msgID: int
407  @param propID: The property be read from.
408  @type propID: int
409  @rtype: int
410  @return: The value held in the property.
411  '''
412  self.write_msg(msgID, [propID])
413  #time.sleep(0.005)
414  read_result = self.PCAN.Read(PCAN_USBBUS1)
415  self.check_error(self.PCAN, read_result[0], "read")
416  data = read_result[1].DATA
417  value = (0x1000000 * data[5]) + (0x0010000 * data[4]) + (0x0000100 * data[3]) + (0x0000001 * data[2])
418  return value
419 
420 
421  def get_16(self, msgID, propID):
422  '''
423  Gets a 16 bit property. Please use get_property instead of this method where applicable.
424 
425  @param msgID: The puck whose property will be read from.
426  @type msgID: int
427  @param propID: The property be read from.
428  @type propID: int
429  @rtype: int
430  @return: The value held in the property.
431  '''
432  self.write_msg(msgID, [propID])
433  #time.sleep(0.05)
434  #time.sleep(0.005)
435  read_result = self.PCAN.Read(PCAN_USBBUS1)
436  self.check_error(self.PCAN, read_result[0], "read")
437  data = read_result[1].DATA
438  value =(0x0000100 * data[3]) + (0x0000001 * data[2])
439  return value
440 
441 
442 
443  def save_property(self, msgID, propID):
444  '''
445  Save a property.
446 
447  @param msgID: The puck or group to have its property saved.
448  @type msgID: int
449  @param propID: The property to be saved.
450  @type propID: int
451  '''
452  self.set_property(msgID, SAVE, propID)
453 
454 
455  def load_property(self, msgID, propID):
456  '''
457  Load a property's value for puck's flash memory.
458 
459  @param msgID: The puck or group to have its property loaded.
460  @type msgID: int
461  @param propID: The property to be loaded.
462  @type propID: int
463  '''
464  self.set_property(msgID, LOAD, propID)
465 
466  def get_prop_quick(self, msgID,propID,speed):
467  '''
468  Gets a property timed at a certain rate.
469 
470  @param msgID: The puck or group to have its property gotten.
471  @type msgID: int
472  @param propID: The property to be saved.
473  @type propID: int
474  @param speed: The time delay for the get.
475  @type speed: float
476  '''
477  self.write_msg(msgID, [propID],speed)
478  read_result=self.read_msg_resilient(msgID,propID)
479  self.check_error(self.PCAN, read_result[0], "read")
480  data = read_result[1].DATA
481  value = (0x1000000 * data[5]) + (0x0010000 * data[4]) + (0x0000100 * data[3]) + (0x0000001 * data[2])
482  return value
483 
484  def read_msg_resilient(self, expect_puck,expect_prop,max_recurse=10,counter=0):
485  '''
486  Reads message given the puckID and the propertyID.
487  It will read as normal, until it gets some expected output from the puck.
488 
489  @param expect_puck: The puck to read from.
490  @type expect_puck: int
491  @param expect_prop: The property read from.
492  @type expect_prop: int
493  @param max_recurse: The most number of times to repeat the get.
494  @type max_recurse: int
495  @param counter: Used internally. Do not set.
496  @type counter: int
497  @rtype: int
498  @return: The value held in the property of the given puck.
499  '''
500  counter+=1
501  response=self.PCAN.Read(PCAN_USBBUS1)
502  received_prop=response[1].DATA[0]-128
503  received_puck=(response[1].ID-1024)>>5
504 
505  if (received_prop==expect_prop) and (received_puck==expect_puck):
506  return response
507  else:
508  print "a"
509  if counter<max_recurse:
510  return self.read_msg_resilient(expect_puck,expect_prop,counter=counter)
511  else:
512  raise Exception("Missed message")
513 
514  def get_role(self, msgID):
515  '''
516  Read from ROLE property and return something that makes sense.
517  Returns an array of holding the following data:
518 
519  [4-bit Product Identifier,
520  Internal Thermistor,
521  20 MHz (vs 32 MHz),
522  Hall Motor Encoder,
523  Enc Motor Encoder,
524  Strain Gauge,
525  IMU for Force-Torque Sensor,
526  Optical Motor Encoder]
527 
528  @param msgID: The puck to get the ROLE from.
529  @type msgID: int
530  @rtype: Array[int,bool,bool,bool,bool,bool,bool,bool]
531  @return: An array holding the above values.
532  '''
533  role = self.get_property(msgID, 1)
534  data= [0,0,0,0,0,0,0,0,0]
535  data[0] = role%16 # 4-bit Product Identifier
536  data[1] = int(role/2**6)%2==1 # Internal Thermistor Bit
537  data[2] = int(role/2**7)%2==1 # 20 MHz (vs 32 MHz)
538  data[3] = int(role/2**8)%2==1 # Is there a Dig+Ser Motor Encoder?
539  data[4] = int(role/2**9)%2==1 # Is there a Hall Motor Encoder?
540  data[5] = int(role/2**10)%2==1 # Is there an Enc Motor Encoder?
541  data[6] = int(role/2**11)%2==1 # Is there a Strain Gauge?
542  data[7] = int(role/2**12)%2==1 # IMU for Force-Torque Sensor
543  data[8] = int(role/2**13)%2==1 # Optical Motor Encoder
544  return data
545 
546  def get_mode(self, msgID):
547  '''
548  Read from MODE property, and return a tuple with the number corresponding to the mode, along with
549  the string name of the mode.
550 
551  @param msgID: The puck from which to get the mode.
552  @type msgID: int
553  @rtype: Tuple(int, str)
554  @return: A tuple with the number and name of the mode.
555  '''
556  m = self.get_property(msgID, MODE)
557  if m==MODE_IDLE:
558  return (MODE_IDLE, "IDLE")
559  elif m==MODE_TORQUE:
560  return (MODE_TORQUE, "TORQUE")
561  elif m==MODE_PID:
562  return (MODE_PID, "PID")
563  elif m==MODE_VEL:
564  return (MODE_VEL, "VEL")
565  elif m==MODE_TRAP:
566  return (MODE_TRAP, "TRAP")
567  else:
568  print "Invalid get_mode() operation: "+str(m)
569  return (m, "???")
570 
571  def set_mode(self, msgID, value):
572  '''
573  Set the mode property using either strings or numbers.
574 
575  @param msgID: The puck or group to set the mode.
576  @type msgID: int
577  @param value: The value to which the mode should be set.
578  @type value: int
579  '''
580  modes = {"IDLE":MODE_IDLE, "TORQUE":MODE_TORQUE, "PID":MODE_PID, "VEL":MODE_VEL, "TRAP":MODE_TRAP}
581  m = value
582  if m in modes:
583  m = modes[m]
584  self.set_property(msgID, MODE, m)
585 
586  # I'm sorry that this method is so unbearably long, but it won't compile otherwise because of circular imports TT_TT
587  def set_puck_like(self, puckID, virtID):
588  '''
589  Set the puck to have all the default properties of the indicated puck ID.
590 
591  @param puckID: The original puck to change.
592  @type puckID: int
593  @param virtID: The ID of the puck to load defaults from.
594  @type virtID: int
595  '''
596  self.set_property(puckID, MODE, MODE_IDLE)
597  # Set universal puck properties. (taterDefs[])
598  # if virtID in range(1,8)
599  # self.set_property(puckID, TIE, 0)
600  # self.set_property(puckID, ACCEL, 100)
601  # self.set_property(puckID, AP, 0)
602  # self.set_property(puckID, CT, 4096)
603  # self.set_property(puckID, OT, 0)
604  # self.set_property(puckID, CTS, 4096)
605  # self.set_property(puckID, DP, 0)
606  # self.set_property(puckID, MV, 100)
607  # self.set_property(puckID, MCV, 100)
608  # self.set_property(puckID, MOV, 100)
609  # self.set_property(puckID, OT, 0)
610  # self.set_property(puckID, HOLD, 0)
611  # self.set_property(puckID, TSTOP, 0)
612  # self.set_property(puckID, OTEMP, 60)
613  # self.set_property(puckID, PTEMP, 0)
614  # self.set_property(puckID, DS, -256)
615  # self.set_property(puckID, KP, 2000)
616  # self.set_property(puckID, KD, 8000)
617  # self.set_property(puckID, KI, 0)
618  #wamDefaultMT has yet to be implemented correctly. The interns coding this don't really care about the WAM, so this will be put off until someone does.
619 
620  #Set Barrett Hand Defaults
621  is_280 = self.get_property(puckID, HALLH)==7 #Identifier for 280 version
622  if virtID in [FINGER1, FINGER2, FINGER3, SPREAD]:
623  self.set_property(puckID, JIDX, virtID-3)
624  self.save_property(puckID, JIDX)
625  self.set_property(puckID, PIDX, virtID-10)
626  self.save_property(puckID, PIDX)
627  self.set_property(puckID, TIE, 0)
628  self.save_property(puckID, TIE)
629  self.set_property(puckID, ACCEL, 200)
630  self.save_property(puckID, ACCEL)
631  #self.set_property(puckID, AP, 0)
632  #self.save_property(puckID, AP)
633  self.set_property(puckID, OT, 0)
634  self.save_property(puckID, OT)
635  self.set_property(puckID, CTS, 4096)
636  self.save_property(puckID, CTS)
637  self.set_property(puckID, MT, 2200)
638  self.save_property(puckID, MT)
639  self.set_property(puckID, MCV, 200)
640  self.save_property(puckID, MCV)
641  self.set_property(puckID, MOV, 200)
642  self.save_property(puckID, MOV)
643  self.set_property(puckID, OTEMP, 60)
644  self.save_property(puckID, OTEMP)
645  self.set_property(puckID, PTEMP, 0)
646  self.save_property(puckID, PTEMP)
647  self.set_property(puckID, POLES, 6)
648  self.save_property(puckID, POLES)
649  self.set_property(puckID, IKCOR, 102)
650  self.save_property(puckID, IKCOR)
651  self.set_property(puckID, IOFF, 0)
652  self.save_property(puckID, IOFF)
653  self.set_property(puckID, IVEL, -75)
654  self.save_property(puckID, IVEL)
655  self.set_property(puckID, DS, 25600)
656  self.save_property(puckID, DS)
657  self.set_property(puckID, KI, 0)
658  self.save_property(puckID, KI)
659  self.set_property(puckID, IPNM, 20000)
660  self.save_property(puckID, IPNM)
661  self.set_property(puckID, GRPA, 0)
662  self.save_property(puckID, GRPA)
663  self.set_property(puckID, GRPB, 7)
664  self.save_property(puckID, GRPB)
665  self.set_property(puckID, GRPC, 5)
666  self.save_property(puckID, GRPC)
667  self.set_property(puckID, IKI, 204)
668  self.save_property(puckID, IKI)
669  self.set_property(puckID, IKP, 500)
670  self.save_property(puckID, IKP)
671  if virtID == SPREAD:
672  self.set_property(puckID, CT, 35950)
673  self.save_property(puckID, CT)
674  self.set_property(puckID, DP, 17975)
675  self.save_property(puckID, DP)
676  self.set_property(puckID, MV, 50)
677  self.save_property(puckID, MV)
678  self.set_property(puckID, HSG, 0)
679  self.save_property(puckID, HSG)
680  self.set_property(puckID, LSG, 0)
681  self.save_property(puckID, LSG)
682  self.set_property(puckID, HOLD, 1)
683  self.save_property(puckID, HOLD)
684  self.set_property(puckID, TSTOP, 150)
685  self.save_property(puckID, TSTOP)
686  self.set_property(puckID, KP, 1000)
687  self.save_property(puckID, KP)
688  self.set_property(puckID, KD, 10000)
689  self.save_property(puckID, KD)
690  else:
691  self.set_property(puckID, CT, 195000)
692  self.save_property(puckID, CT)
693  self.set_property(puckID, DP, 45000)
694  self.save_property(puckID, DP)
695  self.set_property(puckID, MV, 200)
696  self.save_property(puckID, MV)
697  self.set_property(puckID, HSG, 0)
698  self.save_property(puckID, HSG)
699  self.set_property(puckID, LSG, 0)
700  self.save_property(puckID, LSG)
701  self.set_property(puckID, HOLD, 0)
702  self.save_property(puckID, HOLD)
703  self.set_property(puckID, TSTOP, 50)
704  self.save_property(puckID, TSTOP)
705  self.set_property(puckID, KP, 500)
706  self.save_property(puckID, KP)
707  self.set_property(puckID, KD, 2500)
708  self.save_property(puckID, KD)
709  else:
710  print "Invalid Puck Id for Hand"
711 
712  #============================MOVING_STUFF================================
713 
714  def set_hand_targets(self, f1_target, f2_target, f3_target, sp_target):
715  '''
716  Given fingers and spread target values, move the hand to that position.
717  Will mainly be used to load user-defined hand positions.
718  Takes Barrett Units as inputs.
719 
720  @param f1_target: The position (in encoder ticks) for finger 1 to move to.
721  @type f1_target: int
722  @param f2_target: The position for finger 2 to move to.
723  @type f2_target: int
724  @param f3_target: The position for finger 3 to move to.
725  @type f3_target: int
726  @param sp_target: The position for spread to move to.
727  @type sp_target: int
728  '''
729  self.set_property(FINGER1, DP, f1_target)
730  self.set_property(FINGER2, DP, f2_target)
731  self.set_property(FINGER3, DP, f3_target)
732  tar = self.get_position(SPREAD)
733  self.set_property(SPREAD, DP, tar)
734  self.set_property(0x405, CMD, CMD_MOVE)
735  time.sleep(2)
736  self.set_property(SPREAD, DP, sp_target)
737  self.set_property(SPREAD, CMD, CMD_MOVE)
738  time.sleep(1)
739 
740  def move_to(self, puckID, target, autowait=True):
741  '''
742  Move the motor to a specific position.
743 
744  @param puckID: The puck to move.
745  @type puckID: int
746  @param target: The end position to move to.
747  @type target: int
748  @param autowait: Does the program wait until the motor is done moving?
749  @type autowait: bool
750  '''
751  self.set_property(puckID, M, target)
752  if autowait:
753  self.wait_done_moving([puckID])
754 
755  def done_moving(self, motors_to_check=ALL_FINGERS):
756  '''
757  Checks a given list of motors once to see if they have stopped moving, and if so, it returns true
758 
759  @param motors_to_check: A list of motors to check.
760  @type motors_to_check: Array[*int]
761  @rtype: bool
762  @return: Whether or not the motors are done moving.
763  '''
764  for FINGER in motors_to_check:
765  if (self.get_mode(FINGER)[1]!="IDLE" and (self.get_mode(FINGER)[1]!="PID" or self.get_property(FINGER,77)==0)):
766  return False
767  return True
768 
769  # TODO: Ensure that TSTOP > 0. Otherwise, this may be an infinite loop.
770  def wait_done_moving(self, motors_to_check=ALL_FINGERS):
771  '''
772  Waits until the given list of motors have all stopped moving.
773 
774  @param motors_to_check: A list of motors to wait for.
775  @type motors_to_check: Array[*int]
776  '''
777  while(not self.done_moving(motors_to_check)):
778  time.sleep(CAN_DELAY)
779 
780  def detect_breakaway(self, finger):
781  '''
782 
783  @return: True if the finger has broken away, False if it hasn't
784  '''
785  tup = self.get_packed_position(finger)
786  ratio = tup[0]/tup[1]
787  return (ratio>3)
788 
789  def open_grasp(self):
790  '''
791  Opens all fingers to the position encoded by Open Target (OT)
792  '''
793  self.set_property(0x405, TSTOP, 50)
794  self.set_property(SPREAD, TSTOP, 150)
795 
796  open_target=self.get_property(FINGER1,OT)
797  self.set_property(FINGER1,DP,open_target)
798 
799  open_target=self.get_property(FINGER2,OT)
800  self.set_property(FINGER2,DP,open_target)
801 
802  open_target=self.get_property(FINGER3,OT)
803  self.set_property(FINGER3,DP,open_target)
804 
805  spread_stay=self.get_position(SPREAD)
806  self.set_property(SPREAD,DP,spread_stay)
807 
808  self.set_property(0x405,CMD,CMD_MOVE)
809  self.wait_done_moving(GRASP)
810 
811  def close_grasp(self):
812  '''
813  Closes all fingers to the position encoded by Close Target (CT).
814  '''
815  self.set_property(0x405, TSTOP, 50)
816  self.set_property(SPREAD, TSTOP, 150)
817 
818  close_target=self.get_property(FINGER1,CT)
819  self.set_property(FINGER1,DP,close_target)
820 
821  close_target=self.get_property(FINGER2,CT)
822  self.set_property(FINGER2,DP,close_target)
823 
824  close_target=self.get_property(FINGER3,CT)
825  self.set_property(FINGER3,DP,close_target)
826 
827  spread_stay=self.get_position(SPREAD)
828  self.set_property(SPREAD,DP,spread_stay)
829 
830  self.set_property(0x405,CMD,CMD_MOVE)
831  self.wait_done_moving(GRASP)
832 
833  def open_spread(self):
834  '''
835  Open spread to position determined by Open Target (OT).
836  '''
837  self.set_property(SPREAD, TSTOP, 150)
838  self.set_property(SPREAD, CMD, CMD_OPEN)
839  self.wait_done_moving([SPREAD])
840 
841  def close_spread(self):
842  '''
843  Close spread to position determined by Close Target (CT).
844  '''
845  self.set_property(SPREAD, CMD, CMD_CLOSE)
846  self.wait_done_moving([SPREAD])
847 
848  def open_finger(self, puckID, autowait=True):
849  '''
850  Open finger and wait for completion.
851 
852  @param puckID: Finger to be opened.
853  @type puckID: int
854  @param autowait: calls wait_done_moving if True. Defaults to True.
855  @type autowait: bool
856  '''
857  if puckID in [FINGER1, FINGER2, FINGER3]:
858  self.set_property(puckID, TSTOP, 50)
859  if puckID == SPREAD:
860  self.set_property(puckID, TSTOP, 150)
861  self.set_property(puckID, CMD, CMD_OPEN)
862  if autowait:
863  self.wait_done_moving([puckID])
864 
865  def close_finger(self, puckID, autowait=True):
866  '''
867  Close finger and wait for completion.
868 
869  @param puckID: Finger to be closed.
870  @type puckID: int
871  @param autowait: calls wait_done_moving if True. Defaults to True.
872  @type autowait: bool
873  '''
874  if puckID in [FINGER1, FINGER2, FINGER3]:
875  self.set_property(puckID, TSTOP, 50)
876  if puckID == SPREAD:
877  self.set_property(puckID, TSTOP, 150)
878  self.set_property(puckID, CMD, CMD_CLOSE)
879  if autowait:
880  self.wait_done_moving([puckID])
881 
882  def move_grasp(self, position = -1):
883  '''
884  Moves all fingers to input argument or default position (50).
885 
886  @param position: position of fingers. Defaults to -1. Valid position range is from 0-195,000 encoder counts.
887  @type position: int
888  '''
889  default = self.get_position(SPREAD)
890  self.set_property(SPREAD, DP, default)
891  if(position != -1):
892  self.set_property(FINGER1, DP, position)
893  self.set_property(FINGER2, DP, position)
894  self.set_property(FINGER3, DP, position)
895  self.move()
896 
897  def move(self):
898  '''
899  Moves all fingers/spread to their default.
900  '''
901  self.set_property(0x405, CMD, CMD_MOVE)
902  self.wait_done_moving(GRASP)
903 
904  def open_all(self):
905  '''
906  Opens every fingers at once. Mainly used in DEMO. WARNING: can be dangerous because it may cause fingers to collide if the hand is in an unknown position.
907  '''
908  open_target = self.get_property(FINGER1, OT)
909  self.set_property(FINGER1, DP, open_target)
910  open_target = self.get_property(FINGER2, OT)
911  self.set_property(FINGER2, DP, open_target)
912  open_target = self.get_property(FINGER3, OT)
913  self.set_property(FINGER3, DP, open_target)
914  open_target = self.get_property(SPREAD, OT)
915  self.set_property(SPREAD, DP, open_target)
916  self.move()
917 
918  def close_all(self):
919  '''
920  Closes every finger at once. Mainly for use in DEMO. WARNING: can be dangerous because it may cause fingers to collide if the hand is in an unknown position.
921  '''
922  close_target = self.get_property(FINGER1, CT)
923  self.set_property(FINGER1, DP, close_target)
924  close_target = self.get_property(FINGER2, CT)
925  self.set_property(FINGER2, DP, close_target)
926  close_target = self.get_property(FINGER3, CT)
927  self.set_property(FINGER3, DP, close_target)
928  close_target = self.get_property(SPREAD, CT)
929  self.set_property(SPREAD, DP, close_target)
930  self.move()
931 
932  #============================INCREMENTALLY_MOVE_STUFF========================
933 
934  def open_grasp_step(self, step=0):
935  '''
936  Open grasp by input increment.
937 
938  @param step: size of increment in encoder counts. Defaults to 0.
939  @type step: int
940  '''
941  self.open_finger_step(FINGER1, step, False)
942  self.open_finger_step(FINGER2, step, False)
943  self.open_finger_step(FINGER3, step, False)
944  self.wait_done_moving(GRASP)
945 
946  def close_grasp_step(self, step=0):
947  '''
948  Close grasp by input decrement.
949 
950  @param step: size of decrement in encoder counts. Defaults to 0.
951  @type step: int
952  '''
953  self.close_finger_step(FINGER1, step, False)
954  self.close_finger_step(FINGER2, step, False)
955  self.close_finger_step(FINGER3, step, False)
956  self.wait_done_moving(GRASP)
957 
958  def open_spread_step(self, step=-1):
959  '''
960  Open spread by input increment.
961 
962  @param step: size of increment in encoder counts. Defaults to -1.
963  @type step: int
964  '''
965  if step == -1:
966  step = self.get_property(SPREAD, 60)
967  self.open_finger_step(SPREAD, step)
968 
969  def close_spread_step(self, step=-1):
970  '''
971  Close spread by input decrement.
972 
973  @param step: size of decrement in encoder counts. Defaults to -1.
974  @type step: int
975  '''
976  if step == -1:
977  step = self.get_property(SPREAD, 60)
978  self.close_finger_step(SPREAD, step)
979 
980  def open_finger_step(self, puckID, step=-1, autowait=True):
981  '''
982  Open finger by input increment.
983 
984  @param puckID: Finger to be opened.
985  @type puckID: int
986  @param step: size of increment in encoder counts. Defaults to -1.
987  @type step: int
988  @param autowait: calls wait_done_moving if True. Defaults to True.
989  @type autowait: bool
990  '''
991  if step == -1:
992  step = self.get_property(puckID, 60)
993  self.set_property(puckID, DS, step)
994  self.set_property(puckID, CMD, CMD_IO)
995  if autowait:
996  self.wait_done_moving([puckID])
997 
998  def close_finger_step(self, puckID, step=-1, autowait=True):
999  '''
1000  Close finger by input decrement.
1001 
1002  @param puckID: Finger to be closed.
1003  @type puckID: int
1004  @param step: size of decrement in encoder counts. Defaults to -1.
1005  @type step: int
1006  @param autowait: calls wait_done_moving if True. Defaults to True.
1007  @type autowait: bool
1008  '''
1009  if step == -1:
1010  step = self.get_property(puckID, 60)
1011  self.set_property(puckID, DS, step)
1012  self.set_property(puckID, CMD, CMD_IC)
1013  if autowait:
1014  self.wait_done_moving([puckID])
1015 
1016  #===========================NON_TRIVIAL FUNCTIONS=============================
1018  self.write_msg(msgID, [P])
1019  read_result=self.PCAN.Read(PCAN_USBBUS1)
1020  return read_result
1021 
1022  def get_velocity(self, msgID):
1023  '''
1024  Returns velocity values of finger when in motion. Mostly returns garbage. It's used to help tell when finger is stopped or near to it.
1025 
1026  @param msgID: The puck or group to get velocity.
1027  @type msgID: int
1028  @rtype: float
1029  @return: A (garbage) value representing the approximate velocity of the finger.
1030  '''
1031  packet1=self.get_full_pos_packet(msgID)
1032  packet2=self.get_full_pos_packet(msgID)
1033 
1034  error1=packet1[0]
1035  error2=packet2[0]
1036 
1037  msg1=packet1[1]
1038  msg2=packet2[1]
1039 
1040  data1=msg1.DATA
1041  data2=msg2.DATA
1042 
1043  time1=packet1[2]
1044  time2=packet2[2]
1045 
1046  stamp1=time1.micros + 1000 * time1.millis + 0xFFFFFFFF * 1000 * time1.millis_overflow
1047  stamp2=time2.micros + 1000 * time2.millis + 0xFFFFFFFF * 1000 * time2.millis_overflow
1048 
1049  delta=(stamp2-stamp1)/1000000.0
1050 
1051  self.check_error(self.PCAN,error1,"reading position for fake get_velocity")
1052  self.check_error(self.PCAN,error2,"reading position for fake get_velocity")
1053 
1054  val1=(0x0000100 * data1[3]) + (0x0000001 * data1[2])
1055  val2=(0x0000100 * data2[3]) + (0x0000001 * data2[2])
1056 
1057  return (val2-val1)/delta
1058 
1059  def get_temp(self, msgID):
1060  '''
1061  Gets temperature value for all pucks in msgID.
1062 
1063  @param msgID: The puck or group to get temp.
1064  @type msgID: int
1065  @rtype: int
1066  @return: The value of the TEMP property.
1067  '''
1068  return self.temp[msgID]['temp']
1069 
1070 
1071  def read_temp(self, msgID):
1072  '''
1073  Sends a message to get the temperature value for all pucks in msgID.
1074 
1075  @param msgID: The puck or group to get temp.
1076  @type msgID: int
1077  @rtype: int
1078  @return: The value of the TEMP property.
1079  '''
1080  return self.send_msg(msgID, [TEMP])
1081 
1082  def get_therm(self, msgID):
1083  '''
1084  Gets motor temperature value for all pucks in msgID.
1085 
1086  @param msgID: The puck or group to get motor temperature.
1087  @text msgID: int
1088  @rtype: int
1089  @return: The value of the THERM property.
1090  '''
1091  return self.temp[msgID]['therm']
1092 
1093  def read_therm(self, msgID):
1094  '''
1095  Gets motor temperature value for all pucks in msgID.
1096 
1097  @param msgID: The puck or group to get motor temperature.
1098  @text msgID: int
1099  @rtype: int
1100  @return: The value of the THERM property.
1101  '''
1102  return self.send_msg(msgID, [THERM])
1103 
1104  def get_top_tact(self, msgID):
1105  '''
1106  Unpack the top10 values from TACT.
1107  Returns a dictionary with 10 items like (sensor number):(tact value).
1108 
1109  @param msgID: The puck or group to get top 10 tactile data.
1110  @type msgID: int
1111  @rtype: Dictionary{sensorID:value}
1112  @return topVals: Dictionary of the top 10 tactile array sensor values.
1113  '''
1114  # Set TACT(106) to top10 mode (1)
1115  self.set_property(msgID, TACT, TACT_10)
1116  self.write_msg(msgID, [TACT]) #GET TACT
1117  read_result=self.PCAN.Read(PCAN_USBBUS1)
1118  self.check_error(self.PCAN,read_result[0],"reading top ten tactile values")
1119  #output is mapped to here.
1120  output = read_result[1].DATA
1121  #parsing this output
1122  top10 = output[0] * 0x10000 + output[1] * 0x100 + output[2] * 0x1
1123  topVals = {}
1124  data = [output[3]/(0x10), output[3]%(0x10), output[4]/(0x10), output[4]%(0x10), output[5]/(0x10), output[5]%(0x10)]
1125  count=0
1126  for sensor in range(0, 24):
1127  if top10%2 == 1:
1128  #print 'sensor = %d, count = %d'%(sensor, count)
1129  topVals[sensor] = data[count]
1130  count+=1
1131  top10 = top10/2
1132  # Each bit represents one of the top 10 pressures for purposes of efficiency. top10 has the last bit sliced
1133  return topVals
1134 
1135  def read_full_tact(self, msgID):
1136  '''
1137  Read all tactile sensors
1138  '''
1139  return self.set_property(msgID, TACT, TACT_FULL)
1140 
1141  def get_full_tact(self, msgID):
1142  '''
1143  Unpack all tactile sensor values in an array.
1144 
1145  @param msgID: The puck or group to get full tactile array sensor data.
1146  @type msgID: int
1147  @rtype: Array[*data]
1148  @return: An array containing the tactile data from a given puck.
1149  '''
1150  # Set TACT(106) to full mode (2)
1151  '''self.set_property(msgID, TACT, TACT_FULL)
1152  #self.write_msg(msgID, [TACT])
1153  output = [0,0,0,0,0]
1154  read_result = self.PCAN.Read(PCAN_USBBUS1)
1155  self.check_error(self.PCAN,read_result[0],"reading full tactile data")
1156  read_result2 = self.PCAN.Read(PCAN_USBBUS1)
1157  self.check_error(self.PCAN,read_result2[0],"reading full tactile data")
1158  read_result3 = self.PCAN.Read(PCAN_USBBUS1)
1159  self.check_error(self.PCAN,read_result3[0],"reading full tactile data")
1160  read_result4 = self.PCAN.Read(PCAN_USBBUS1)
1161  self.check_error(self.PCAN,read_result4[0],"reading full tactile data")
1162  read_result5 = self.PCAN.Read(PCAN_USBBUS1)
1163  self.check_error(self.PCAN,read_result5[0],"reading full tactile data")
1164 
1165  output[0] = read_result[1].DATA
1166  output[1] = read_result2[1].DATA
1167  output[2] = read_result3[1].DATA
1168  output[3] = read_result4[1].DATA
1169  output[4] = read_result5[1].DATA
1170 
1171  #print 'Init: ID1 = %x, ID2 = %x, ID3 = %x, ID4 = %x, ID5 = %x'%(read_result[1].ID, read_result2[1].ID, read_result3[1].ID, read_result4[1].ID, read_result5[1].ID)
1172  tactileVals = range(0,24)
1173  index_ = 0
1174  for data in output:
1175  index_ = int(data[0]/16) * 5
1176  #print 'index = %d, data[0] = %x'%(index_, data[0])
1177  # Get the bits and then unpack them.
1178  tactileVals[index_ + 0] = round(((data[0]%0x10)*0x100 + data[1])/256.0,2)
1179  tactileVals[index_ + 1] = round((data[2]*0x10 + int(data[3]/0x10))/256.0,2)
1180  tactileVals[index_ + 2] = round(((data[3]%0x10)*0x100 + data[4])/256.0,2)
1181  tactileVals[index_ + 3] = round((data[5]*0x10 + int(data[6]/0x10))/256.0,2)
1182  if index_ != 20:
1183  tactileVals[index_ + 4] = round(((data[6]%0x10)*0x100 + data[7])/256.0,2)
1184  #print 'Return OK : %s'%(tactileVals)
1185  return tactileVals'''
1186 
1187  return self.tactile_sensor[msgID]['values']
1188 
1189 
1190  def get_tact(self, msgID, topOrFull="TOP10"):
1191  '''
1192  Obtain and interpret tactile sensor data.
1193 
1194  @param msgID: The puck or group to get full or top 10 tactile array sensor data.
1195  @type msgID: int
1196  @param topOrFull: To get full data, enter "FULL". To get the top 10 values, enter "TOP10". Or anything else, really.
1197  @type topOrFull: str
1198  @return
1199  '''
1200  if topOrFull == "FULL":
1201  return self.get_full_tact(msgID)
1202  else:
1203  return self.get_top_tact(msgID)
1204 
1205  def set_velocity(self, puckID, velocity):
1206  '''
1207  Set the velocity and make the motor move.
1208 
1209  @param puckID: The ID of the puck to set the velocity of.
1210  @type puckID: int
1211  @param velocity: The velocity (in cts/ms) of the motor.
1212  @type velocity: int
1213  '''
1214  #First set TSTOP to 0.
1215  self.set_property(puckID, TSTOP, 0)
1216  #Set Velocity
1217  self.set_property(puckID, V, velocity)
1218  #Set mode to allow the puck to move.
1219  self.set_property(puckID, MODE, MODE_VEL)
1220 
1221 
1222  def get_strain(self, msgID):
1223  '''
1224  Gets the fingertip torque sensor value.
1225 
1226  @param msgID: The puck or group to get fingertip torque sensor data.
1227  @type msgID: int
1228  @rtype: int
1229  @return: Strain Gauge Reading
1230  '''
1231  return self.strain[msgID]
1232 
1233 
1234  def read_strain(self, msgID):
1235  '''
1236  Sends the message to get the fingertip torque sensor value.
1237 
1238  @param msgID: The puck or group to get fingertip torque sensor data.
1239  @type msgID: int
1240  @rtype: int
1241  @return: CAN status
1242  '''
1243  return self.send_msg(msgID, [SG])
1244 
1245 
1246 
1247  def onescomp(self, binstr):
1248  return ''.join('1' if b=='0' else '0' for b in binstr)
1249 
1250  def twoscomp(self, number):
1251  binstr= bin(number)[2:]
1252  a= bin(int(self.onescomp(binstr),2)+1)[2:]
1253  return -1*int(a,2)
1254 
1255  def twoscomp2(self, number, bits):
1256  '''
1257  Returns the two's complement of a number with a certain amount of bits.
1258 
1259  @param number: The number to take the two's complement of.
1260  @type number: int
1261  @param bits: The size of the integer in bits.
1262  @type bits: int
1263  '''
1264  return -(1<<bits) + number
1265 
1266 
1267  def get_position(self, msgID, depth=0):
1268  '''
1269  Get packed position data and return it.
1270 
1271  @param msgID: The puck or group to get position data.
1272  @type msgID: int
1273  @rtype: int
1274 
1275  @param depth: number of times get message was retried.
1276 
1277  @return: The position of the finger in encoder counts.
1278  '''
1279  if depth!=0:
1280  self.write_msg(msgID, [P],.009)
1281  read_result=self.PCAN.Read(PCAN_USBBUS1)
1282  try:
1283  self.check_error(self.PCAN,read_result[0],"getting position data")
1284  received_puck=(read_result[1].ID-1024)>>5
1285  if received_puck!=msgID:
1286  raise Exception("Did not read expected MSGID")
1287  except:
1288  if depth>10:
1289  raise Exception("Failure to get position data.")
1290  else:
1291  return self.get_position(msgID, depth+1)
1292 
1293  output = read_result[1].DATA
1294  temp=(output[0]-0x80)*0x10000 + output[1] * 0x100 + output[2]
1295 
1296  if (temp & 0b1000000000000000000000):
1297  return self.twoscomp(temp)
1298  else:
1299  return temp
1300 
1301  def get_packed_position(self, msgID):
1302  '''
1303  Get packed position data and return both P and JP.
1304 
1305  @param msgID: The puck or group to get position data.
1306  @type msgID: int
1307  @rtype: (int, int)
1308 
1309  @return: The position and joint position of the finger in encoder counts.
1310  Position in radians
1311  '''
1312 
1313  return self.motor_positions[msgID]['position']
1314 
1315 
1316  def read_packed_position(self, msgID):
1317  '''
1318  Get packed position data and return both P and JP.
1319 
1320  @param msgID: The puck or group to get position data.
1321  @type msgID: int
1322  @rtype: (int, int)
1323 
1324  @return: sends a msg to read the position.
1325  '''
1326 
1327  return self.send_msg(msgID, [P])
1328 
1329 
1331  '''
1332  Reads and process all the msgs in the bus
1333  Depending on the CAN id, it'll use different methods
1334  '''
1335  ret = 0
1336  # Reads a can msg
1337  msg = self.read_msg()
1338 
1339  # No read messages
1340  if msg[0] != PCAN_ERROR_OK:
1341  ret = -1
1342 
1343  while msg[0] == PCAN_ERROR_OK:
1344 
1345  can_id = msg[1].ID
1346  #rospy.loginfo('pyHand:process_can_messages: message %x:%x', can_id, msg[1].DATA[0])
1347 
1348  if can_id in [F1_POSITION, F2_POSITION, F3_POSITION, SPREAD_POSITION]:
1349  self.process_packed_position(msg[1])
1350  elif can_id in [F1_STRAIN, F2_STRAIN, F3_STRAIN, SPREAD_STRAIN] and msg[1].DATA[0] == STRAIN_ID:
1351  self.process_strain(msg[1])
1352  elif can_id in [F1_MOTOR_TEMP, F2_MOTOR_TEMP, F3_MOTOR_TEMP, SPREAD_MOTOR_TEMP] and msg[1].DATA[0] == MOTOR_TEMP_ID:
1353  self.process_motor_temp(msg[1])
1354  elif can_id in [F1_MOTOR_THERM, F2_MOTOR_THERM, F3_MOTOR_THERM, SPREAD_MOTOR_THERM] and msg[1].DATA[0] == MOTOR_THERM_ID:
1355  self.process_motor_therm(msg[1])
1356  elif can_id in [F1_TACT, F2_TACT, F3_TACT, PALM_TACT]:
1357  self.process_full_tact(msg[1])
1358  elif can_id == FT_SENSOR_FORCE:
1359  self.process_force(msg[1])
1360  elif can_id == FT_SENSOR_TORQUE:
1361  self.process_torque(msg[1])
1362  else:
1363  rospy.logwarn('pyHand:process_can_messages: unknown message %x:[%d %d %d %d %d %d %d %d]', can_id, msg[1].DATA[0], msg[1].DATA[1],
1364  msg[1].DATA[2], msg[1].DATA[3], msg[1].DATA[4], msg[1].DATA[5], msg[1].DATA[6], msg[1].DATA[7])
1365 
1366  msg = self.read_msg()
1367 
1368  return ret
1369 
1370  def process_packed_position(self, msg):
1371  '''
1372  Process the CAN msgs and saves the position depending on the MSG ID
1373  '''
1374  data = msg.DATA
1375  pos = (data[0]-0x80)*0x10000 + data[1]*0x100 + data[2]
1376  jpos= (data[3]-0x80)*0x10000 + data[4]*0x100 + data[5]
1377  pos = self.twoscomp(pos) if pos & 0b1000000000000000000000 else pos
1378  jpos= self.twoscomp(jpos) if jpos & 0b1000000000000000000000 else jpos
1379 
1380  if msg.ID == F1_POSITION:
1381  self.motor_positions[FINGER1]['encoder'][0] = pos
1382  self.motor_positions[FINGER1]['encoder'][1] = jpos
1383  self.motor_positions[FINGER1]['position'][0] = self.enc_to_rad(pos, BASE_TYPE)
1384  self.motor_positions[FINGER1]['position'][1] = self.enc_to_rad(jpos, BASE_TYPE)
1385  #print 'F1'
1386 
1387  elif msg.ID == F2_POSITION:
1388  self.motor_positions[FINGER2]['encoder'][0] = pos
1389  self.motor_positions[FINGER2]['encoder'][1] = jpos
1390  self.motor_positions[FINGER2]['position'][0] = self.enc_to_rad(pos, BASE_TYPE)
1391  self.motor_positions[FINGER2]['position'][1] = self.enc_to_rad(jpos, BASE_TYPE)
1392  #print 'F2'
1393 
1394  elif msg.ID == F3_POSITION:
1395  self.motor_positions[FINGER3]['encoder'][0] = pos
1396  self.motor_positions[FINGER3]['encoder'][1] = jpos
1397  self.motor_positions[FINGER3]['position'][0] = self.enc_to_rad(pos, BASE_TYPE)
1398  self.motor_positions[FINGER3]['position'][1] = self.enc_to_rad(jpos, BASE_TYPE)
1399  #print 'F3 = %f, %f'%(self.motor_positions[FINGER3]['position'][0], self.motor_positions[FINGER3]['position'][1])
1400  #print 'F3'
1401 
1402  elif msg.ID == SPREAD_POSITION:
1403  self.motor_positions[SPREAD]['encoder'][0] = pos
1404  self.motor_positions[SPREAD]['encoder'][1] = jpos
1405  self.motor_positions[SPREAD]['position'][0] = self.enc_to_rad(pos, SPREAD_TYPE)
1406  self.motor_positions[SPREAD]['position'][1] = self.enc_to_rad(jpos, SPREAD_TYPE)
1407  #print 'SPREAD = %f, %f'%(self.motor_positions[SPREAD]['position'][0], self.motor_positions[SPREAD]['position'][1])
1408 
1409 
1410  def process_strain(self, msg):
1411  '''
1412  Process the msg and extract the strain value depending on the CAN ID
1413  '''
1414  data = msg.DATA
1415  value =(0x0000100 * data[3]) + (0x0000001 * data[2])
1416 
1417  if msg.ID == F1_STRAIN:
1418  self.strain[FINGER1] = value
1419  if msg.ID == F2_STRAIN:
1420  self.strain[FINGER2] = value
1421  if msg.ID == F3_STRAIN:
1422  self.strain[FINGER3] = value
1423  if msg.ID == SPREAD_STRAIN:
1424  self.strain[SPREAD] = value
1425 
1426  def process_motor_temp(self, msg):
1427  '''
1428  Process the msg and extract the temperature of the motor puck on the CAN ID
1429  '''
1430  data = msg.DATA
1431  value =(0x0000100 * data[3]) + (0x0000001 * data[2])
1432 
1433  if msg.ID == F1_MOTOR_TEMP:
1434  self.temp[FINGER1]['temp'] = value
1435 
1436  if msg.ID == F2_MOTOR_TEMP:
1437  self.temp[FINGER2]['temp'] = value
1438 
1439  if msg.ID == F3_MOTOR_TEMP:
1440  self.temp[FINGER3]['temp'] = value
1441 
1442  if msg.ID == SPREAD_MOTOR_TEMP:
1443  self.temp[SPREAD]['temp'] = value
1444  #print 'Motor Temp S'
1445 
1446  def process_motor_therm(self, msg):
1447  '''
1448  Process the msg and extract the temperature of the motor on the CAN ID
1449  '''
1450  data = msg.DATA
1451  value =(0x0000100 * data[3]) + (0x0000001 * data[2])
1452 
1453  if msg.ID == F1_MOTOR_THERM:
1454  self.temp[FINGER1]['therm'] = value
1455 
1456  if msg.ID == F2_MOTOR_THERM:
1457  self.temp[FINGER2]['therm'] = value
1458 
1459  if msg.ID == F3_MOTOR_THERM:
1460  self.temp[FINGER3]['therm'] = value
1461 
1462  if msg.ID == SPREAD_MOTOR_THERM:
1463  self.temp[SPREAD]['therm'] = value
1464  #print 'Motor Temp S'
1465 
1466  def process_full_tact(self, msg):
1467  '''
1468  Process and saves all the messages containing the tactile information
1469  '''
1470 
1471  data = msg.DATA
1472 
1473  if msg.ID == F1_TACT:
1474  if data[0] >= 0x00 and data[0] < 0x10:
1475  self.tactile_sensor[FINGER1]['data'][0] = data
1476  if data[0] >= 0x10 and data[0] < 0x20:
1477  self.tactile_sensor[FINGER1]['data'][1] = data
1478  if data[0] >= 0x20 and data[0] < 0x30:
1479  self.tactile_sensor[FINGER1]['data'][2] = data
1480  if data[0] >= 0x30 and data[0] < 0x40:
1481  self.tactile_sensor[FINGER1]['data'][3] = data
1482  if data[0] >= 0x40:
1483  self.tactile_sensor[FINGER1]['data'][4] = data
1484  self.tactile_sensor[FINGER1]['values'] = self.process_tactile_data(self.tactile_sensor[FINGER1]['data'])
1485  if msg.ID == F2_TACT:
1486  if data[0] >= 0x00 and data[0] < 0x10:
1487  self.tactile_sensor[FINGER2]['data'][0] = data
1488  if data[0] >= 0x10 and data[0] < 0x20:
1489  self.tactile_sensor[FINGER2]['data'][1] = data
1490  if data[0] >= 0x20 and data[0] < 0x30:
1491  self.tactile_sensor[FINGER2]['data'][2] = data
1492  if data[0] >= 0x30 and data[0] < 0x40:
1493  self.tactile_sensor[FINGER2]['data'][3] = data
1494  if data[0] >= 0x40:
1495  self.tactile_sensor[FINGER2]['data'][4] = data
1496  self.tactile_sensor[FINGER2]['values'] = self.process_tactile_data(self.tactile_sensor[FINGER2]['data'])
1497  if msg.ID == F3_TACT:
1498  if data[0] >= 0x00 and data[0] < 0x10:
1499  self.tactile_sensor[FINGER3]['data'][0] = data
1500  if data[0] >= 0x10 and data[0] < 0x20:
1501  self.tactile_sensor[FINGER3]['data'][1] = data
1502  if data[0] >= 0x20 and data[0] < 0x30:
1503  self.tactile_sensor[FINGER3]['data'][2] = data
1504  if data[0] >= 0x30 and data[0] < 0x40:
1505  self.tactile_sensor[FINGER3]['data'][3] = data
1506  if data[0] >= 0x40:
1507  self.tactile_sensor[FINGER3]['data'][4] = data
1508  self.tactile_sensor[FINGER3]['values'] = self.process_tactile_data(self.tactile_sensor[FINGER3]['data'])
1509  if msg.ID == PALM_TACT:
1510  if data[0] >= 0x00 and data[0] < 0x10:
1511  self.tactile_sensor[SPREAD]['data'][0] = data
1512  if data[0] >= 0x10 and data[0] < 0x20:
1513  self.tactile_sensor[SPREAD]['data'][1] = data
1514  if data[0] >= 0x20 and data[0] < 0x30:
1515  self.tactile_sensor[SPREAD]['data'][2] = data
1516  if data[0] >= 0x30 and data[0] < 0x40:
1517  self.tactile_sensor[SPREAD]['data'][3] = data
1518  if data[0] >= 0x40:
1519  self.tactile_sensor[SPREAD]['data'][4] = data
1520  self.tactile_sensor[SPREAD]['values'] = self.process_tactile_data(self.tactile_sensor[SPREAD]['data'])
1521 
1522  def process_tactile_data(self, data_array):
1523  '''
1524  Process the array of data and returns a tactile array
1525  '''
1526  tactileVals = range(0,24)
1527  index_ = 0
1528  for data in data_array:
1529  index_ = int(data[0]/16) * 5
1530  #print 'index = %d, data[0] = %x'%(index_, data[0])
1531  # Get the bits and then unpack them.
1532  tactileVals[index_ + 0] = round(((data[0]%0x10)*0x100 + data[1])/256.0,2)
1533  tactileVals[index_ + 1] = round((data[2]*0x10 + int(data[3]/0x10))/256.0,2)
1534  tactileVals[index_ + 2] = round(((data[3]%0x10)*0x100 + data[4])/256.0,2)
1535  tactileVals[index_ + 3] = round((data[5]*0x10 + int(data[6]/0x10))/256.0,2)
1536  if index_ != 20:
1537  tactileVals[index_ + 4] = round(((data[6]%0x10)*0x100 + data[7])/256.0,2)
1538  #print 'Return OK : %s'%(tactileVals)
1539  return tactileVals
1540 
1541  def process_force(self, msg):
1542  '''
1543  Process the CAN msgs and saves the position depending on the MSG ID
1544  '''
1545  data = msg.DATA
1546 
1547  val = data[1] * 0x100 + data[0] #raw data value
1548  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1549  self.ft_sensor['force'][0] = round(val/256.0, 2)
1550 
1551  val = data[3] * 0x100 + data[2]
1552  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1553  self.ft_sensor['force'][1] = round(val/256.0, 2)
1554 
1555  val = data[5] + 0x100 + data[4] - 0x100
1556  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1557  self.ft_sensor['force'][2] = round(val/256.0, 2)
1558 
1559 
1560  def process_torque(self, msg):
1561  '''
1562  Process the CAN msgs and saves the position depending on the MSG ID
1563  '''
1564  data = msg.DATA
1565 
1566  val = data[1] * 0x100 + data[0] #raw data value
1567  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1568  self.ft_sensor['torque'][0] = round(val/4096.0, 3)
1569 
1570  val = data[3] * 0x100 + data[2]
1571  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1572  self.ft_sensor['torque'][1] = round(val/4096.0, 3)
1573 
1574  val = data[5] + 0x100 + data[4] - 0x100
1575  val = val if val < 0x8000 else self.twoscomp2(val, 16)
1576  self.ft_sensor['torque'][2] = round(val/4096.0, 3)
1577 
1578 
1579  def tare_fts(self):
1580  '''
1581  Tare the FTS sensor.
1582  '''
1583  self.set_property(FTS, FTS_FT, 0)
1584 
1585  def get_fts(self):
1586  '''
1587  Returns the FTS values
1588  '''
1589  return self.ft_sensor
1590 
1591  def new_temp_mail(self, fingers_to_change):
1592  former_mailbox_c={}
1593  for finger in fingers_to_change:
1594  former_mailbox_c[finger]=self.get_property(finger,GRPC)
1595  self.set_property(finger,GRPC,12)
1596  return former_mailbox_c
1597 
1598  def revert_temp_mail(self, fingers_to_change,former):
1599  for finger in fingers_to_change:
1600  former_mailbox_value=former[finger]
1601  self.set_property(finger,GRPC,former_mailbox_value)
1602  #==========================ANGLE_CONVERSIONS=========================
1603 
1604  def enc_to_per(self, enc):
1605  '''
1606  Given an angle in encoder counts, return the percentage of the angle that represents.
1607 
1608  @param enc: Encoder counts.
1609  @type enc: int
1610  @return: Percentage
1611  @rtype: float
1612  '''
1613  per = enc/1950.0
1614  return round(per, 2)
1615 
1616  def enc_to_rad(self, enc, type = BASE_TYPE):
1617  '''
1618  Given an angle in encoder counts, return the radian measure of the angle that represents.
1619 
1620  @param enc: Encoder counts.
1621  @type enc: int
1622  @return: Radians
1623  @rtype: float
1624  '''
1625  motion_limit = BASE_LIMIT
1626  tics = MAX_ENCODER_TICKS
1627 
1628  if type == TIP_TYPE:
1629  motion_limit = TIP_LIMIT
1630  tics = MAX_FINGERTIP_TICKS
1631  elif type == SPREAD_TYPE:
1632  motion_limit = SPREAD_LIMIT
1633  tics = MAX_SPREAD_TICKS
1634 
1635  PI = 3.141592653589
1636  rad = enc * (motion_limit*PI/180)/tics
1637  return round(rad,2)
1638 
1639  def enc_to_deg(self, enc):
1640  '''
1641  Given an angle in encoder counts, return the degree measure of the angle that represents.
1642 
1643  @param enc: Encoder counts.
1644  @type enc: int
1645  @return: Degrees
1646  @rtype: float
1647  '''
1648  deg = enc * 140/MAX_ENCODER_TICKS
1649  return round(deg,2)
1650 
1651  def per_to_enc(self, per):
1652  '''
1653  Given a percentage of an angle, return it in encoder counts.
1654 
1655  @param per: Percentage
1656  @type per: float
1657  @return: Encoder counts
1658  @rtype: int
1659  '''
1660  enc = per * 1950.0
1661  return int(enc)
1662 
1663  def rad_to_enc(self, rad, type = BASE_TYPE):
1664  '''
1665  Given the readian measure of an angle, return it in encoder counts.
1666 
1667  @param rad: Radians
1668  @type rad: float
1669  @return: Encoder counts
1670  @rtype: int
1671  '''
1672  motion_limit = BASE_LIMIT
1673  tics = MAX_ENCODER_TICKS
1674 
1675  if type == TIP_TYPE:
1676  motion_limit = TIP_LIMIT
1677  tics = MAX_FINGERTIP_TICKS
1678  elif type == SPREAD_TYPE:
1679  motion_limit = SPREAD_LIMIT
1680  tics = MAX_SPREAD_TICKS
1681 
1682  PI = 3.141592653589
1683  enc = rad / ((motion_limit*PI/180)/tics)
1684  return int(enc)
1685 
1686  def deg_to_enc(self, deg):
1687 
1688  '''
1689  Given a degree measure of an angle, return it in encoder counts.
1690 
1691  @param deg: Degrees
1692  @type deg: float
1693  @return: Encoder counts
1694  @rtype: int
1695  '''
1696  enc = deg * 195000.0/140
1697  return int(enc)
1698 
1700 
1701  result_ = self.read_msg()
1702 
1703  while result_[0] == 0:
1704  result_ = self.read_msg()
1705 
1707  '''
1708  Read all tactile sensors
1709  '''
1710  return self.send_msg(FTS, [FTS_FT])
1711  #return self.get_property(FTS, FTS_FT)
1712 
def enc_to_rad(self, enc, type=BASE_TYPE)
Definition: pyHand_api.py:1616
def send_msg(self, msgID, data)
Definition: pyHand_api.py:307
def process_packed_position(self, msg)
Definition: pyHand_api.py:1370
def read_packed_position(self, msgID)
Definition: pyHand_api.py:1316
def twoscomp2(self, number, bits)
Definition: pyHand_api.py:1255
def get_packed_position(self, msgID)
Definition: pyHand_api.py:1301
def move_to(self, puckID, target, autowait=True)
Definition: pyHand_api.py:740
def open_spread_step(self, step=-1)
Definition: pyHand_api.py:958
def read_msg_resilient(self, expect_puck, expect_prop, max_recurse=10, counter=0)
Definition: pyHand_api.py:484
def get_prop_quick(self, msgID, propID, speed)
Definition: pyHand_api.py:466
def move_grasp(self, position=-1)
Definition: pyHand_api.py:882
def init_hand(self)
Definition: pyHand_api.py:217
def get_tact(self, msgID, topOrFull="TOP10")
Definition: pyHand_api.py:1190
def set_velocity(self, puckID, velocity)
Definition: pyHand_api.py:1205
def get_full_pos_packet(msgID)
Definition: pyHand_api.py:1017
def close_finger_step(self, puckID, step=-1, autowait=True)
Definition: pyHand_api.py:998
def can_status(self)
Definition: pyHand_api.py:168
def init_finger(self, msgID)
Definition: pyHand_api.py:251
def get_property(self, msgID, propID)
Definition: pyHand_api.py:381
def detect_breakaway(self, finger)
Definition: pyHand_api.py:780
def process_tactile_data(self, data_array)
Definition: pyHand_api.py:1522
def move(self)
Definition: pyHand_api.py:897
def load_property(self, msgID, propID)
Definition: pyHand_api.py:455
def open_all(self)
Definition: pyHand_api.py:904
def set_puck_like(self, puckID, virtID)
Definition: pyHand_api.py:587
def rad_to_enc(self, rad, type=BASE_TYPE)
Definition: pyHand_api.py:1663
def process_full_tact(self, msg)
Definition: pyHand_api.py:1466
def read_strain(self, msgID)
Definition: pyHand_api.py:1234
def close_grasp(self)
Definition: pyHand_api.py:811
def get_mode(self, msgID)
Definition: pyHand_api.py:546
def per_to_enc(self, per)
Definition: pyHand_api.py:1651
def get_therm(self, msgID)
Definition: pyHand_api.py:1082
def get_strain(self, msgID)
Definition: pyHand_api.py:1222
def process_motor_temp(self, msg)
Definition: pyHand_api.py:1426
def close_spread_step(self, step=-1)
Definition: pyHand_api.py:969
def open_spread(self)
Definition: pyHand_api.py:833
def save_property(self, msgID, propID)
Definition: pyHand_api.py:443
def read_full_force_torque(self)
Definition: pyHand_api.py:1706
def get_role(self, msgID)
Definition: pyHand_api.py:514
def process_strain(self, msg)
Definition: pyHand_api.py:1410
def read_therm(self, msgID)
Definition: pyHand_api.py:1093
def read_msg(self)
Definition: pyHand_api.py:270
def enc_to_per(self, enc)
Definition: pyHand_api.py:1604
def clean_read_buffer(self)
Definition: pyHand_api.py:1699
def process_torque(self, msg)
Definition: pyHand_api.py:1560
def set_16(self, msgID, propID, value)
Definition: pyHand_api.py:363
def process_motor_therm(self, msg)
Definition: pyHand_api.py:1446
def initialize_fts(self)
Definition: pyHand_api.py:261
def close_finger(self, puckID, autowait=True)
Definition: pyHand_api.py:865
def new_temp_mail(self, fingers_to_change)
Definition: pyHand_api.py:1591
def get_position(self, msgID, depth=0)
Definition: pyHand_api.py:1267
def get_fts(self)
Definition: pyHand_api.py:1585
def check_error(self, connection, result, location_of_error)
Definition: pyHand_api.py:122
def open_finger_step(self, puckID, step=-1, autowait=True)
Definition: pyHand_api.py:980
def close_spread(self)
Definition: pyHand_api.py:841
def close_grasp_step(self, step=0)
Definition: pyHand_api.py:946
def open_finger(self, puckID, autowait=True)
Definition: pyHand_api.py:848
def open_grasp_step(self, step=0)
Definition: pyHand_api.py:934
def onescomp(self, binstr)
Definition: pyHand_api.py:1247
def set_property(self, msgID, propID, value)
Definition: pyHand_api.py:330
def get_temp(self, msgID)
Definition: pyHand_api.py:1059
def enc_to_deg(self, enc)
Definition: pyHand_api.py:1639
def process_can_messages(self)
Definition: pyHand_api.py:1330
def initialize(self)
Definition: pyHand_api.py:194
def open_grasp(self)
Definition: pyHand_api.py:789
def read_full_tact(self, msgID)
Definition: pyHand_api.py:1135
def twoscomp(self, number)
Definition: pyHand_api.py:1250
def get_32(self, msgID, propID)
Definition: pyHand_api.py:401
def tare_fts(self)
Definition: pyHand_api.py:1579
def process_force(self, msg)
Definition: pyHand_api.py:1541
def get_16(self, msgID, propID)
Definition: pyHand_api.py:421
def get_full_tact(self, msgID)
Definition: pyHand_api.py:1141
def get_velocity(self, msgID)
Definition: pyHand_api.py:1022
def write_msg(self, msgID, data, delay=.002)
Definition: pyHand_api.py:282
def read_temp(self, msgID)
Definition: pyHand_api.py:1071
def can_reset(self)
Definition: pyHand_api.py:154
def can_uninit(self)
Definition: pyHand_api.py:185
def set_hand_targets(self, f1_target, f2_target, f3_target, sp_target)
Definition: pyHand_api.py:714
def wait_done_moving(self, motors_to_check=ALL_FINGERS)
Definition: pyHand_api.py:770
def can_init(self)
Definition: pyHand_api.py:176
def __init__(self, port='/dev/pcan32')
Definition: pyHand_api.py:102
def deg_to_enc(self, deg)
Definition: pyHand_api.py:1686
def set_mode(self, msgID, value)
Definition: pyHand_api.py:571
def done_moving(self, motors_to_check=ALL_FINGERS)
Definition: pyHand_api.py:755
def close_all(self)
Definition: pyHand_api.py:918
def enum(self)
Definition: pyHand_api.py:138
def get_top_tact(self, msgID)
Definition: pyHand_api.py:1104
def set_32(self, msgID, propID, value)
Definition: pyHand_api.py:347
def revert_temp_mail(self, fingers_to_change, former)
Definition: pyHand_api.py:1598


bhand_controller
Author(s): Román Navarro , Jorge Ariño
autogenerated on Thu Aug 1 2019 03:30:51