-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathabb.py
905 lines (745 loc) · 26.8 KB
/
abb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 28 12:07:54 2019
@author: NEMEC
"""
from math import pi, sin, cos, atan2, asin, copysign
from enum import IntEnum
from typing import List
import socket
import threading
import struct
from time import sleep
class Position:
"""
Cartesian position (x, y, z) in [mm]
"""
x:float
y:float
z:float
def __init__(self, x:float=0, y:float=0, z:float=0) :
self.x = x
self.y = y
self.z = z
@staticmethod
def fromList(raw:list):
""" Creates position from list of variables. """
return Position(raw[0], raw[1], raw[2])
def toList(self):
""" Converts to list """
return [self.x, self.y, self.z]
# TODO casting to some nice format
class EulerAngles:
"""
Z-Y-X Euler angles (roll, pitch, yaw) in radians
"""
roll:float
pitch:float
yaw:float
def __init__(self, roll:float=0, pitch:float=0, yaw:float=0) :
self.roll = roll
self.pitch = pitch
self.yaw = yaw
def toQuaternion(self):
""" Converts Euler angles to quaternion """
# code taken from https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
cy = cos(self.yaw * 0.5)
sy = sin(self.yaw * 0.5)
cp = cos(self.pitch * 0.5)
sp = sin(self.pitch * 0.5)
cr = cos(self.roll * 0.5)
sr = sin(self.roll * 0.5)
w = (cy * cp * cr + sy * sp * sr)
x = (cy * cp * sr - sy * sp * cr)
y = (sy * cp * sr + cy * sp * cr)
z = (sy * cp * cr - cy * sp * sr)
return Quaternion(w, x, y, z)
@staticmethod
def fromList(raw:List[float]):
""" Creates Euler angles from three values in array """
return EulerAngles(list[0], list[1], list[2])
def toList(self):
""" Converts to list """
return [self.roll, self.pitch, self.yaw]
class Quaternion:
"""
Rotational quaternion (w, x, y, z) or (q1, q2, q3, q4) in ABB RobotStudio
"""
w : float
x : float
y : float
z : float
def __init__(self, w:float=1, x:float=0, y:float=0, z:float=0) :
self.w = w;
self.x = x;
self.y = y;
self.z = z;
def toEulerAngles(self):
""" Creates Euler angles from quaternion """
# roll (x-axis rotation)
sinr_cosp = 2.0 * (self.w * self.x + self.y * self.z)
cosr_cosp = 1.0 - 2.0 * (self.x * self.x + self.y * self.y)
roll = atan2(sinr_cosp, cosr_cosp)
# pitch (y-axis rotation)
sinp = 2.0 * (self.w * self.y - self.z * self.x)
if abs(sinp) >= 1:
pitch = copysign(pi / 2, sinp) # use 90 degrees if out of range
else:
pitch = asin(sinp)
# yaw (z-axis rotation)
siny_cosp = 2.0 * (self.w * self.z + self.x * self.y)
cosy_cosp = 1.0 - 2.0 * (self.y * self.y + self.z * self.z)
yaw = atan2(siny_cosp, cosy_cosp)
return EulerAngles(roll, pitch, yaw)
@staticmethod
def fromList(raw:List[float]):
""" Create quaternion from list of values """
return Quaternion(raw[0], raw[1], raw[2], raw[3])
def toList(self):
""" converts to list """
return [self.w, self.x, self.y, self.z]
class Conf:
"""
Represents robot configuration (quadrants of joints) (cf1, cf4, cf6, cfx)
"""
cf1:float
cf4:float
cf6:float
cfx:float
def __init__(self, cf1:float=0, cf4:float=0, cf6:float=0, cfx:float=0):
self.cf1 = cf1
self.cf4 = cf4
self.cf6 = cf6
self.cfx = cfx
@staticmethod
def fromList(raw:List[float]):
""" creates coniguration from raw data """
return Conf(raw[0], raw[1], raw[2], raw[3])
def toList(self):
""" converts to list """
return [self.cf1, self.cf4, self.cf6, self.cfx]
class Speed:
"""
Represents speed settings of the robot
Attributes
----------
v_tcp : float
tool linear speed [mm/s]
v_ori : float
joint rotation speed [deg/s]
v_leax : float
linear external axis speed [mm/s]
v_reax : float
rotating external axis speed [deg/s]
"""
v_tcp:float
v_ori:float
v_leax:float
v_reax:float
def __init__(self, tcp:float=100, ori:float=500, leax:float=5000, reax:float=1000):
""" Default speed is v100 """
self.v_tcp = tcp
self.v_ori = ori
self.v_leax = leax
self.v_reax = reax
def __float__(self):
""" returns TCP speed """
return self.v_tcp
def toList(self):
""" converts to list """
return [self.v_tcp, self.v_ori, self.v_leax, self.v_reax]
class Zone:
"""
Represents zone settings
Attributes
----------
finep : bool
true when it is a fine point, false when fly-by point
pzone_tcp : float
path zone of the TCP [mm]
pzone_ori : float
path zone for orientation [mm]
pzone_eax : float
path zone for external axes [mm]
zone_ori : float
zone orientation [deg]
zone_leax : float
zone for linear external axes [mm]
zone_reax : float
zone for rotating external axes [deg]
"""
finep:bool
pzone_tcp:float
pzone_ori:float
pzone_eax:float
zone_ori:float
zone_leax:float
zone_reax:float
def __init__(self, z:float=100):
""" Simplified constructor. Default z100. Give negative zone for "fine" zone. """
if z < 0 :
# fine
self.finep = True
self.pzone_tcp = 0
self.pzone_ori = 0
self.pzone_eax = 0
self.zone_ori = 0
self.zone_leax = 0
self.zone_reax = 0
elif z < 1 :
# z0 is special
self.finep = False
self.pzone_tcp = 0
self.pzone_ori = 0.3
self.pzone_eax = 0.3
self.zone_ori = 0.03
self.zone_leax = 0.3
self.zone_reax = 0.03
else:
# simmilar to zX in robotstudio, but not precisely the same
self.finep = False
self.pzone_tcp = z
self.pzone_ori = 1.5 * z
self.pzone_eax = 1.5 * z
self.zone_ori = 0.15 * z
self.zone_leax = 1.5 * z
self.zone_reax = 0.15 * z
def __float__(self):
""" returns zX value """
if self.finep:
return -1
else:
return self.pzone_tcp
@staticmethod
def fine():
""" returns fine zone """
return Zone(-1.0)
def toList(self):
""" converts to list """
return [1.0 if self.finep else 0.0, self.pzone_tcp, self.pzone_ori, self.pzone_eax, self.zone_ori, self.zone_leax, self.zone_reax]
class LinearTarget:
"""
Target for linear movement
Attributes
----------
trans : Position
Translation of the target w.r.t. workobject
rot : Quaternion
Rotation of the target w.r.t. workobject
robconf : Conf
Configuration of the robot
extax : List[float]
Position of the external axes [deg or mm]
speed : Speed
Speed settings
zone:Zone
Zone settings
"""
trans:Position
rot:Quaternion
robconf:Conf
extax:List[float]
speed:Speed
zone:Zone
def __init__(self):
self.trans = Position()
self.rot = Quaternion()
self.robconf = Conf()
self.extax = [0, 0, 0, 0, 0, 0]
self.speed = Speed()
self.zone = Zone()
class JointTarget:
"""
Target for joint movement
Attributes
----------
joints : List[float]
Positions of all axes (6x internal, 6x external) [deg or mm]
speed: Speed
Speed settings
zone : Zone
Zone settings
"""
joints:List[float]
speed:Speed
zone:Zone
def __init__(self):
self.joints = [0,0,0,0,0,0,0,0,0,0,0,0]
self.speed = Speed()
self.zone = Zone()
class Load:
"""
Payload data
Attributes
----------
mass : float
weight of the load [kg]
cog : Position
position of the Centrum of Gravity w.r.t. tool coordinate system
aom : Quaternion
axes of moment
ix : float
moment of inertia around x-axis [kg.m2]
iy : float
moment of inertia around y-axis [kg.m2]
iz : float
moment of inertia around z-axis [kg.m2]
"""
def __init__(self):
self.mass = 0.001
self.cog = Position(0, 0, 0)
self.aom = Quaternion(1, 0, 0, 0)
self.ix = 0
self.iy = 0
self.iz = 0
@staticmethod
def fromList(raw:List[float]):
""" creates load data from array of 11 floats """
ld = Load()
ld.mass = raw[0]
ld.cog = Position.fromList(raw[1:4])
ld.aom = Quaternion.fromList(raw[4:8])
ld.ix = raw[8]
ld.iy = raw[9]
ld.iz = raw[10]
return ld
def toList(self):
""" converts load data to array """
return [self.mass] + self.cog.toList() + self.aom.toList() + [self.ix, self.iy, self.iz]
class Tool:
"""
Tool data
Attributes
----------
robhold : bool
True when robot holds the tool
trans : Position
Position of the TCP w.r.t. tool0
rot : Quaternion
rotation of TCP w.r.t. tool0
load : Load
load of the tool itself
"""
robhold:bool
trans:Position
rot:Quaternion
load:Load
def __init__(self):
self.robhold = True
self.trans = Position()
self.rot = Quaternion()
self.load = Load()
def toList(self):
""" coverts tool data to list """
return [1.0 if self.robhold else 0.0] + self.trans.toList() + self.rot.toList() + self.load.toList()
@staticmethod
def fromList(raw:List[float]):
""" creates tool data from raw array """
tool = Tool()
tool.robhold = raw[0] > 0
tool.trans = Position.fromList(raw[1:4])
tool.rot = Quaternion.fromList(raw[4:8])
tool.load = Load.fromList(raw[8:19])
class Workobject:
"""
Workobject data
Attributes
----------
robhold : bool
True when robot holds the workobject and the tool is static.
trans : Position
position of the object frame w.r.t. global coordinates
rot : Quaternion
rotation of the object frame w.r.t. global coordinates
"""
robhold:bool
trans:Position
rot:Quaternion
def __init__(self):
self.robhold = False
self.trans = Position()
self.rot = Quaternion()
def toList(self):
""" converts workobject to list """
return [1.0 if self.robhold else 0.0] + self.trans.toList() + self.rot.toList()
class GripStatusCode(IntEnum):
""" status code of the gripper """
Ready = 0x00
Error = 0x01
FreeMoveInward = 0x02
FreeMoveOutward = 0x03
MoveInward = 0x04
MoveOutward = 0x05
ActionCompleted = 0x06
ForcingInward = 0x07
ForcingOutward = 0x08
KeepsObject = 0x09
Calibrating = 0x0A
JogOpen = 0x0B
JogClose = 0x0C
ChangingChirality = 0x0F
AgileInward = 0x10
AgileOutward = 0x11
class GripDirection(IntEnum):
""" direction of the gripping """
Outward = 0x00
Inward = 0x01
class MoveStatus(IntEnum):
""" status of the movement """
Normal = 0x00
Paused = 0x01
Collision = 0x02
class RequestCode(IntEnum):
""" internal: do not use """
CMD_Ping = 0x00
CMD_MoveL = 0x01
CMD_MoveAbsJ = 0x02
CMD_StopNow = 0x03
CMD_Wait = 0x04
CMD_ReadState = 0x05
CMD_Pause = 0x06
CMD_Resume = 0x07
CMD_SetWobj = 0x08
CMD_SetTool = 0x09
CMD_GripInit = 0x0A
CMD_GripMoveTo = 0x0B
CMD_GripSmart = 0x0C
CMD_GripStopNow = 0x0D
CMD_GripBlow = 0x0E
CMD_GripVacuum = 0x0F
CMD_GripRead = 0x10
class ResponseCode(IntEnum):
""" internal: do not use"""
RES_OK = 0x00
RES_State = 0x01
RES_Overflow = 0x02
RES_InvalidRequest = 0x03
RES_NotReachable = 0x04
RES_GripState = 0x05
class Status:
"""
Status of the robotic arm
Attributes
----------
joints : List[float]
Postion of all axes (internal and external) [mm or deg]
trans : Position
Postion of the TCP (Tool Center Point) w.r.t. workobject
rot : Quaternion
Rotation of the TCP w.r.t. workobject
robconf : Conf
Configuration of the robot axes
moveState : MoveStatus
status of the movement
"""
joints:List[float]
trans:Position
rot:Quaternion
robconf:Conf
moveState:MoveStatus
def __init__(self):
self.joints = [0,0,0,0,0,0,0,0,0,0,0,0]
self.trans = Position()
self.rot = Quaternion()
self.robconf = Conf()
self.moveState = MoveStatus.Normal
class GripStatus:
"""
Status of the Smart gripper
Attributes
----------
code : GripStatusCode
Status code of the gripper
pos : float
position of the gripper in mm
pressure1 : float
pressure on vacuum channel 1
pressure2 : float
pressure on vacuum channel 2
"""
code:GripStatusCode
pos:float
pressure1:float
pressure2:float
def __init__(self):
self.code = GripStatusCode.Ready
self.pos = 0.0
self.pressure1 = 0.0
self.pressure2 = 0.0
class Arm:
""" Represents one robotic arm optionally with Smart Gripper """
ep:socket.socket
lock:threading.Semaphore
lastRequestID:int
lastState:Status
lastGripState:GripStatus
host:str
port:int
START_BYTE = 0x23
CACHE_SIZE = 256
HEADER_SIZE = 6
def __init__(self, host:str, port:int):
""" Creates new Arm interface but does not connects yet """
self.host = host
self.port = port
self.lock = threading.Semaphore()
self.lastRequestID = 0
self.lastState = Status()
self.lastGripState = GripStatus()
self.ep = None
def __del__(self):
self.Disconnect()
def Connect(self):
""" Connects to the robotic arm """
self.Disconnect()
sleep(0.5)
self.ep = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ep.connect((self.host, self.port))
self.ep.settimeout(1.0)
def Disconnect(self):
""" Closes and deletes the connection """
if self.ep != None:
self.ep.close()
del self.ep
self.ep = None
def parseResponse(self) -> bool:
""" internal: do not use """
while True:
# receive response
raw = self.ep.recv(Arm.CACHE_SIZE)
if len(raw) < Arm.HEADER_SIZE:
#print("Invalid header length " + str(len(raw)))
continue
# unpack header
header = struct.unpack('<BBHH', raw[:Arm.HEADER_SIZE])
if header[0] != Arm.START_BYTE:
#print("invalid start byte")
continue
if header[2] != self.lastRequestID:
#print("invalid response ID")
continue
resCode = header[1]
argsNum = header[3] // 4
if len(raw) != Arm.HEADER_SIZE + argsNum * 4:
#print("invalid message length")
return False
# get args
if argsNum > 0:
args = struct.unpack('<' + str(argsNum) + 'f', raw[Arm.HEADER_SIZE:])
else:
args = []
if resCode == ResponseCode.RES_OK:
return True
if resCode == ResponseCode.RES_State:
if argsNum == 24 :
self.lastState.joints = args[0:12]
self.lastState.trans = Position.fromList(args[12:15])
self.lastState.rot = Quaternion.fromList(args[15:19])
self.lastState.robconf = Conf.fromList(args[19:23])
self.lastState.moveState = int(args[23])
return True
else:
print("invalid number of arguments")
return False
if resCode == ResponseCode.RES_GripState:
if argsNum == 4:
self.lastGripState.code = int(args[0])
self.lastGripState.pos = args[1]
self.lastGripState.pressure1 = args[2]
self.lastGripState.pressure2 = args[3]
return True
else:
return False
if resCode == ResponseCode.RES_NotReachable:
print("point is not reachable")
return False
else:
return False # unknown or negative result code
#endwhile
def sendRequest(self, code:RequestCode, args:List[float]=[]) -> bool:
""" internal: do not use """
self.lock.acquire()
while True:
try:
# increment ID of the message
self.lastRequestID += 1;
if self.lastRequestID > 0xFFFF :
self.lastRequestID = 0
# create packet
argNum = len(args);
raw = struct.pack('<BBHH' + str(argNum) + 'f', Arm.START_BYTE, code, self.lastRequestID, argNum * 4, *args);
# send data
self.ep.sendall(raw)
# process response
return self.parseResponse()
except ConnectionError:
self.Connect()
finally:
self.lock.release()
def MoveTo(self, target) -> bool:
""" Adds new target to the trajectory. Use JointTarget or LinearTarget """
if type(target) is JointTarget:
args = 23*[None]
args[0:12] = target.joints
args[12:16] = target.speed.toList()
args[16:23] = target.zone.toList()
return self.sendRequest(RequestCode.CMD_MoveAbsJ, args)
elif type(target) is LinearTarget:
args = 28*[None]
args[0:3] = target.trans.toList()
args[3:7] = target.rot.toList()
args[7:11] = target.robconf.toList()
args[11:17] = target.extax
args[17:21] = target.speed.toList()
args[21:28] = target.zone.toList()
return self.sendRequest(RequestCode.CMD_MoveL, args)
else:
return False
def Stop(self) -> bool:
""" Stops movement and clears trajectory """
return self.sendRequest(RequestCode.CMD_StopNow)
def Pause(self) -> bool:
""" Pauses the movement """
res = self.sendRequest(RequestCode.CMD_Pause)
if res: self.lastState.moveState = MoveStatus.Paused
return res
def Resume(self) -> bool :
""" Resumes the movement """
res = self.sendRequest(RequestCode.CMD_Resume)
if res: self.lastState.moveState = MoveStatus.Normal
return res
def IsPaused(self) -> bool:
""" Returns True when movement is paused """
return (self.lastState.moveState == MoveStatus.Paused)
def ChangeTool(self, tool: Tool) -> bool :
""" Updates tool data """
args = tool.toList()
return self.sendRequest(RequestCode.CMD_SetTool, args)
def ChangeWorkobject(self, wobj: Workobject) -> bool :
""" Updates workobject """
args = wobj.toList()
return self.sendRequest(RequestCode.CMD_SetWobj, args)
def Test(self) -> bool:
""" Tests connection """
return self.sendRequest(RequestCode.CMD_Ping)
def Read(self) -> Status:
""" Reads current state of the robot """
if self.sendRequest(RequestCode.CMD_ReadState):
return self.lastState
else:
raise Exception("abb.Arm: unable to read state")
class SmartGripper:
""" Interface for YuMi Smart Gripper """
arm:Arm
def __init__(self, arm:Arm):
""" Creates new instance of Smart Gripper bound with given robotic arm """
self.arm = arm
def Calibrate(self) -> bool:
""" Initializes Smart Gripper and starts its callibration. Run this before any GripX function. """
return self.arm.sendRequest(RequestCode.CMD_GripInit)
def MoveTo(self, pos:float, noWait:bool=False) -> bool:
""" Moves gripper to the given position. Optionaly does not wait until done. """
if pos < 0.0 or pos > 25.0:
raise ValueError("grip position out of range")
args = [pos, 1.0 if noWait else 0.0]
return self.arm.sendRequest(RequestCode.CMD_GripMoveTo, args)
def GripSmart(self, force:float=20, pos:float=0, tol:float=2, direct:GripDirection=GripDirection.Inward, noWait:bool=False) -> bool:
""" Grips considering the given maximal force, position, tolerance. Optionaly does not wait until done. """
if pos < 0.0 or pos > 25.0:
raise ValueError("grip position out of range (0, 25) mm")
if force < 0.0 or force > 20.0:
raise ValueError("grip force out of range (0, 20) N")
if tol < 0.0 or tol > 25.0:
raise ValueError("grip tolerange out of range (0, 25) mm")
args = [float(direct), force, pos, tol, 1.0 if noWait else 0.0]
return self.arm.sendRequest(RequestCode.CMD_GripSmart, args)
def Stop(self) -> bool:
""" Stops gripper immediately """
return self.arm.sendRequest(RequestCode.CMD_GripStopNow)
def Blow(self, channel:int, enable:bool) -> bool:
""" Starts/stops blowing on given channel (1 or 2) """
args = [1.0 if enable else 0.0, float(channel)]
return self.arm.sendRequest(RequestCode.CMD_GripBlow, args)
def Vacuum(self, channel:int, enable:bool, pressureMax:float=110) -> bool:
""" Starts / stops given vacuum channel (1 or 2) with given maximum pressure. """
if pressureMax < 0 or pressureMax > 110:
raise ValueError("maximal pressure out of range (0, 110) kPa")
args = [1.0 if enable else 0.0, float(channel), pressureMax]
return self.arm.sendRequest(RequestCode.CMD_GripVacuum, args)
def Read(self) -> GripStatus:
""" Read current state of the smart gripper """
if self.arm.sendRequest(RequestCode.CMD_GripRead):
return self.arm.lastGripState
else:
raise Exception("abb.SmartGripper: unable to read state")
class YuMi:
""" ABB YuMi robot """
LeftArm:Arm
RightArm:Arm
LeftHand:SmartGripper
RightHand:SmartGripper
LEFT_ARM_PORT = 10010
RIGHT_ARM_PORT = 10020
def __init__(self, host:str):
""" creates interface to YuMi robot at given IP address, but does not connect to it """
self.LeftArm = Arm(host, YuMi.LEFT_ARM_PORT)
self.RightArm = Arm(host, YuMi.RIGHT_ARM_PORT)
# these two lines makes perfect sense to ABB (facepalm)
self.LeftHand = SmartGripper(self.RightArm)
self.RightHand = SmartGripper(self.LeftArm)
def __del__(self):
self.Disconnect()
def Connect(self):
""" Connects to the controller """
self.LeftArm.Connect()
self.RightArm.Connect()
def Disconnect(self):
""" Disconnects from the controller """
self.LeftArm.Disconnect()
self.RightArm.Disconnect()
def Stop(self) -> bool:
""" Stops movement of the robot """
return (self.LeftArm.Stop() and self.RightArm.Stop())
def Pause(self) -> bool:
""" Pauses movement of both arms """
return (self.LeftArm.Pause() and self.RightArm.Pause())
def Resume(self) -> bool:
""" Resumes movement of both arms """
return (self.LeftArm.Resume() and self.RightArm.Resume())
def IsPaused(self) -> bool:
""" Returns true when both arms are paused """
return (self.LeftArm.IsPaused() and self.RightArm.IsPaused())
def Test(self) -> bool:
""" Returns true when both arms respond """
return (self.LeftArm.Test() and self.RightArm.Test())
def InitGrippers(self) -> bool:
""" Setups grippers """
if not(self.LeftHand.Calibrate()) or not(self.RightHand.Calibrate()):
print("unable to calibrate grippers")
return False
# set tool data
tool = Tool()
tool.robhold = True
tool.trans = Position.fromList([0,0,114.2])
tool.rot = Quaternion.fromList([1,0,0,0])
tool.load = Load.fromList([0.229,7.9,12.4,48.7,1,0,0,0,0.00021,0.00023,0.00008])
if not(self.LeftArm.ChangeTool(tool)) or not(self.RightArm.ChangeTool(tool)):
print("unable to set tooldata")
return False
# everything is OK
return True
def MoveHome(self) -> bool:
""" moves to home position """
left_home = JointTarget()
left_home.joints = [0,-130,30,0,40,0,135,0,0,0,0,0]
left_home.speed = Speed(50)
left_home.zone = Zone.fine()
right_home = JointTarget()
right_home.joints = [0,-130,30,0,40,0,-135,0,0,0,0,0]
right_home.speed = Speed(50)
right_home.zone = Zone.fine()
if not(self.LeftArm.MoveTo(left_home)):
return False
if not(self.RightArm.MoveTo(right_home)):
return False
return True