66from enum import Enum
77from typing import Union , Tuple , OrderedDict
88
9+
910class RobotiqGripper :
1011 """
1112 Communicates with the gripper directly, via socket with string commands, leveraging string names for variables.
1213 """
14+
1315 # WRITE VARIABLES (CAN ALSO READ)
14- ACT = ' ACT' # act : activate (1 while activated, can be reset to clear fault status)
15- GTO = ' GTO' # gto : go to (will perform go to with the actions set in pos, for, spe)
16- ATR = ' ATR' # atr : auto-release (emergency slow move)
17- ADR = ' ADR' # adr : auto-release direction (open(1) or close(0) during auto-release)
18- FOR = ' FOR' # for : force (0-255)
19- SPE = ' SPE' # spe : speed (0-255)
20- POS = ' POS' # pos : position (0-255), 0 = open
16+ ACT = " ACT" # act : activate (1 while activated, can be reset to clear fault status)
17+ GTO = " GTO" # gto : go to (will perform go to with the actions set in pos, for, spe)
18+ ATR = " ATR" # atr : auto-release (emergency slow move)
19+ ADR = " ADR" # adr : auto-release direction (open(1) or close(0) during auto-release)
20+ FOR = " FOR" # for : force (0-255)
21+ SPE = " SPE" # spe : speed (0-255)
22+ POS = " POS" # pos : position (0-255), 0 = open
2123 # READ VARIABLES
22- STA = ' STA' # status (0 = is reset, 1 = activating, 3 = active)
23- PRE = ' PRE' # position request (echo of last commanded position)
24- OBJ = ' OBJ' # object detection (0 = moving, 1 = outer grip, 2 = inner grip, 3 = no object at rest)
25- FLT = ' FLT' # fault (0=ok, see manual for errors if not zero)
24+ STA = " STA" # status (0 = is reset, 1 = activating, 3 = active)
25+ PRE = " PRE" # position request (echo of last commanded position)
26+ OBJ = " OBJ" # object detection (0 = moving, 1 = outer grip, 2 = inner grip, 3 = no object at rest)
27+ FLT = " FLT" # fault (0=ok, see manual for errors if not zero)
2628
27- ENCODING = ' UTF-8' # ASCII and UTF-8 both seem to work
29+ ENCODING = " UTF-8" # ASCII and UTF-8 both seem to work
2830
2931 class GripperStatus (Enum ):
3032 """Gripper status reported by the gripper. The integer values have to match what the gripper sends."""
33+
3134 RESET = 0
3235 ACTIVATING = 1
3336 # UNUSED = 2 # This value is currently not used by the gripper firmware
3437 ACTIVE = 3
3538
3639 class ObjectStatus (Enum ):
3740 """Object status reported by the gripper. The integer values have to match what the gripper sends."""
41+
3842 MOVING = 0
3943 STOPPED_OUTER_OBJECT = 1
4044 STOPPED_INNER_OBJECT = 2
@@ -51,7 +55,9 @@ def __init__(self):
5155 self ._min_force = 0
5256 self ._max_force = 255
5357
54- def connect (self , hostname : str , port : int , socket_timeout : float = 2.0 ) -> None :
58+ def connect (
59+ self , hostname : str , port : int , socket_timeout : float = 2.0
60+ ) -> None :
5561 """Connects to a gripper at the given address.
5662 :param hostname: Hostname or ip.
5763 :param port: Port.
@@ -75,7 +81,7 @@ def _set_vars(self, var_dict: OrderedDict[str, Union[int, float]]):
7581 cmd = "SET"
7682 for variable , value in var_dict .items ():
7783 cmd += f" { variable } { str (value )} "
78- cmd += ' \n ' # new line is required for the command to finish
84+ cmd += " \n " # new line is required for the command to finish
7985 # atomic commands send/rcv
8086 with self .command_lock :
8187 self .socket .sendall (cmd .encode (self .ENCODING ))
@@ -107,13 +113,15 @@ def _get_var(self, variable: str):
107113 # note some special variables (like FLT) may send 2 bytes, instead of an integer. We assume integer here
108114 var_name , value_str = data .decode (self .ENCODING ).split ()
109115 if var_name != variable :
110- raise ValueError (f"Unexpected response { data } ({ data .decode (self .ENCODING )} ): does not match '{ variable } '" )
116+ raise ValueError (
117+ f"Unexpected response { data } ({ data .decode (self .ENCODING )} ): does not match '{ variable } '"
118+ )
111119 value = int (value_str )
112120 return value
113121
114122 @staticmethod
115123 def _is_ack (data : str ):
116- return data == b' ack'
124+ return data == b" ack"
117125
118126 def _reset (self ):
119127 """
@@ -134,12 +142,13 @@ def rq_reset(gripper_socket="1"):
134142 """
135143 self ._set_var (self .ACT , 0 )
136144 self ._set_var (self .ATR , 0 )
137- while (not self ._get_var (self .ACT ) == 0 or not self ._get_var (self .STA ) == 0 ):
145+ while (
146+ not self ._get_var (self .ACT ) == 0 or not self ._get_var (self .STA ) == 0
147+ ):
138148 self ._set_var (self .ACT , 0 )
139149 self ._set_var (self .ATR , 0 )
140150 time .sleep (0.5 )
141151
142-
143152 def activate (self , auto_calibrate : bool = True ):
144153 """Resets the activation flag in the gripper, and sets it back to one, clearing previous fault flags.
145154 :param auto_calibrate: Whether to calibrate the minimum and maximum positions based on actual motion.
@@ -171,12 +180,18 @@ def rq_activate_and_wait(gripper_socket="1"):
171180 """
172181 if not self .is_active ():
173182 self ._reset ()
174- while (not self ._get_var (self .ACT ) == 0 or not self ._get_var (self .STA ) == 0 ):
183+ while (
184+ not self ._get_var (self .ACT ) == 0
185+ or not self ._get_var (self .STA ) == 0
186+ ):
175187 time .sleep (0.01 )
176188
177189 self ._set_var (self .ACT , 1 )
178190 time .sleep (1.0 )
179- while (not self ._get_var (self .ACT ) == 1 or not self ._get_var (self .STA ) == 3 ):
191+ while (
192+ not self ._get_var (self .ACT ) == 1
193+ or not self ._get_var (self .STA ) == 3
194+ ):
180195 time .sleep (0.01 )
181196
182197 # auto-calibrate position range if desired
@@ -186,7 +201,10 @@ def rq_activate_and_wait(gripper_socket="1"):
186201 def is_active (self ):
187202 """Returns whether the gripper is active."""
188203 status = self ._get_var (self .STA )
189- return RobotiqGripper .GripperStatus (status ) == RobotiqGripper .GripperStatus .ACTIVE
204+ return (
205+ RobotiqGripper .GripperStatus (status )
206+ == RobotiqGripper .GripperStatus .ACTIVE
207+ )
190208
191209 def get_min_position (self ) -> int :
192210 """Returns the minimum position the gripper can reach (open position)."""
@@ -221,26 +239,49 @@ def auto_calibrate(self, log: bool = True) -> None:
221239 :param log: Whether to print the results to log.
222240 """
223241 # first try to open in case we are holding an object
224- (position , status ) = self .move_and_wait_for_pos (self .get_open_position (), 64 , 1 )
225- if RobotiqGripper .ObjectStatus (status ) != RobotiqGripper .ObjectStatus .AT_DEST :
226- raise RuntimeError (f"Calibration failed opening to start: { str (status )} " )
242+ (position , status ) = self .move_and_wait_for_pos (
243+ self .get_open_position (), 64 , 1
244+ )
245+ if (
246+ RobotiqGripper .ObjectStatus (status )
247+ != RobotiqGripper .ObjectStatus .AT_DEST
248+ ):
249+ raise RuntimeError (
250+ f"Calibration failed opening to start: { str (status )} "
251+ )
227252
228253 # try to close as far as possible, and record the number
229- (position , status ) = self .move_and_wait_for_pos (self .get_closed_position (), 64 , 1 )
230- if RobotiqGripper .ObjectStatus (status ) != RobotiqGripper .ObjectStatus .AT_DEST :
231- raise RuntimeError (f"Calibration failed because of an object: { str (status )} " )
254+ (position , status ) = self .move_and_wait_for_pos (
255+ self .get_closed_position (), 64 , 1
256+ )
257+ if (
258+ RobotiqGripper .ObjectStatus (status )
259+ != RobotiqGripper .ObjectStatus .AT_DEST
260+ ):
261+ raise RuntimeError (
262+ f"Calibration failed because of an object: { str (status )} "
263+ )
232264 assert position <= self ._max_position
233265 self ._max_position = position
234266
235267 # try to open as far as possible, and record the number
236- (position , status ) = self .move_and_wait_for_pos (self .get_open_position (), 64 , 1 )
237- if RobotiqGripper .ObjectStatus (status ) != RobotiqGripper .ObjectStatus .AT_DEST :
238- raise RuntimeError (f"Calibration failed because of an object: { str (status )} " )
268+ (position , status ) = self .move_and_wait_for_pos (
269+ self .get_open_position (), 64 , 1
270+ )
271+ if (
272+ RobotiqGripper .ObjectStatus (status )
273+ != RobotiqGripper .ObjectStatus .AT_DEST
274+ ):
275+ raise RuntimeError (
276+ f"Calibration failed because of an object: { str (status )} "
277+ )
239278 assert position >= self ._min_position
240279 self ._min_position = position
241280
242281 if log :
243- print (f"Gripper auto-calibrated to [{ self .get_min_position ()} , { self .get_max_position ()} ]" )
282+ print (
283+ f"Gripper auto-calibrated to [{ self .get_min_position ()} , { self .get_max_position ()} ]"
284+ )
244285
245286 def move (self , position : int , speed : int , force : int ) -> Tuple [bool , int ]:
246287 """Sends commands to start moving towards the given position, with the specified speed and force.
@@ -259,10 +300,19 @@ def clip_val(min_val, val, max_val):
259300 clip_for = clip_val (self ._min_force , force , self ._max_force )
260301
261302 # moves to the given position with the given speed and force
262- var_dict = OrderedDict ([(self .POS , clip_pos ), (self .SPE , clip_spe ), (self .FOR , clip_for ), (self .GTO , 1 )])
303+ var_dict = OrderedDict (
304+ [
305+ (self .POS , clip_pos ),
306+ (self .SPE , clip_spe ),
307+ (self .FOR , clip_for ),
308+ (self .GTO , 1 ),
309+ ]
310+ )
263311 return self ._set_vars (var_dict ), clip_pos
264312
265- def move_and_wait_for_pos (self , position : int , speed : int , force : int ) -> Tuple [int , ObjectStatus ]: # noqa
313+ def move_and_wait_for_pos (
314+ self , position : int , speed : int , force : int
315+ ) -> Tuple [int , ObjectStatus ]: # noqa
266316 """Sends commands to start moving towards the given position, with the specified speed and force, and
267317 then waits for the move to complete.
268318 :param position: Position to move to [min_position, max_position]
@@ -282,10 +332,49 @@ def move_and_wait_for_pos(self, position: int, speed: int, force: int) -> Tuple[
282332
283333 # wait until not moving
284334 cur_obj = self ._get_var (self .OBJ )
285- while RobotiqGripper .ObjectStatus (cur_obj ) == RobotiqGripper .ObjectStatus .MOVING :
335+ while (
336+ RobotiqGripper .ObjectStatus (cur_obj )
337+ == RobotiqGripper .ObjectStatus .MOVING
338+ ):
286339 cur_obj = self ._get_var (self .OBJ )
287340
288341 # report the actual position and the object status
289342 final_pos = self ._get_var (self .POS )
290343 final_obj = cur_obj
291- return final_pos , RobotiqGripper .ObjectStatus (final_obj )
344+ return final_pos , RobotiqGripper .ObjectStatus (final_obj )
345+
346+
347+ def calc_gripper_position (min_pos : int , max_pos : int , percentage : float ) -> int :
348+ """Calculates the gripper position based on a percentage of the range between min and max positions.
349+ :param min_pos: Minimum position of the gripper.
350+ :param max_pos: Maximum position of the gripper.
351+ :param percentage: Percentage of the range to calculate the position for (0.0 to 1.0).
352+ :return: Calculated position as an integer.
353+ """
354+ if not (0.0 <= percentage <= 1.0 ):
355+ raise ValueError ("Percentage must be between 0.0 and 1.0." )
356+ return int (min_pos + (max_pos - min_pos ) * percentage )
357+
358+
359+ if __name__ == "__main__" :
360+ ip = "192.168.50.195"
361+
362+ def log_info (gripper ):
363+ print (
364+ f"Pos: { str (gripper .get_current_position ()): >3} "
365+ f"Open: { gripper .is_open (): <2} "
366+ f"Closed: { gripper .is_closed (): <2} "
367+ )
368+
369+ print ("Creating gripper..." )
370+ gripper = RobotiqGripper ()
371+ print ("Connecting to gripper..." )
372+ gripper .connect (ip , 63352 )
373+ print ("Activating gripper..." )
374+ gripper .activate ()
375+
376+ print ("Testing gripper..." )
377+ gripper .move_and_wait_for_pos (255 , 255 , 255 )
378+ log_info (gripper )
379+ gripper .move_and_wait_for_pos (0 , 255 , 255 )
380+ log_info (gripper )
0 commit comments