Login
A   A   A  
welcome

 Album 

米Teen唔鐘意玩現成品,喜歡由零件開始砌。99次失敗唔係問題,最緊要係有1次成功!

米Teen總部設在聖公會白約翰會督中學506室,在午膳後和放學後集會。有興趣參與的同學,請與會長或導師聯絡。

米Teen專頁

506室內 2020-11-04 21:43:57
溫度 26℃ 濕度 95%
街景 2020-11-04 21:41:52
街景
5樓室外 2020-11-04 21:43:43
溫度 22℃ 濕度 95%
街景 2020-11-04 21:34:34
街景

同Dobot機械臂玩過三關

icon

Home>製作項目...>同Dobot機械臂玩...

用Python程式控制Dobot機械臂同你玩過三關。用Python程式編寫過三關戰術,控制Dobot機械臂執放與移動棋子,透過Webcam送回的影像偵測棋盤和棋子,辨認人類對手的棋路,讓Dobot機械臂與人對弈。本製作被邀請在2019年6月的香港電訊5G科技嘉年華展出。

系統規格如下:

編程語言:Python
機械臂:Dobot Magician
影像:Webcam

開發程序

  1. 用Python控制Dobot移動及使用吸咀運送棋子
  2. 用Python編寫過三關戰術
  3. 用Python分析WebCam傳送回來的影像
  4. 用Python辨認棋盤上的棋子
  5. 用Python控制流程

項目負責同學:4C洪子洛

用Python程式控制Dobot機械臂搬動棋子的試驗

剛完成了玩過三關的程式,還要debug,稍後再優化程序。

Author: HUNG TSZ LOK (SKH Bishop Baker Secondary School, class 4C)

Last modified: 2019 June

1
#pip install pydobot
2
import cv2
3
import random
4
import time
5
import numpy as np
6
from pydobot import Dobot
7
8
# connect and setup DOBOT
9
d = Dobot('COM5', 0)
10
d._set_end_effector_suction_cup(False)
11
d._set_ptp_coordinate_params(150, 150)
12
d._set_ptp_common_params(150, 100)
13
14
15
print('start')
16
movex = {
17
    '000000000': [0, 4, 2, 4, 6, 8], '100020000': [8], '001020000': [6],
18
    '000020100': [2], '000020001': [0],  '020010000': [0, 2],
19
    '000210000': [0, 6], '000012000': [2, 8], '000010020': [6, 8],
20
    '120010002': [3, 6], '021010200': [5, 8], '002210100': [7, 8],
21
    '100210002': [1, 2], '001012200': [1], '200012001': [7], '200010021': [5],
22
    '002010120': [3], '000020000': [0, 2, 6, 8], '200000000': [4],
23
    '002000000': [4], '000000200': [4], '000000002': [4],
24
    '002010200': [1, 3, 5, 7], '200010002': [1, 3, 5, 7], '000200100': [8],
25
    '000000021': [2], '001002000': [0], '120000000': [6], '000200121': [2],
26
    '001002021': [0], '121002000': [6], '120200100': [8], '000000120': [0],
27
    '000002001': [6], '021000000': [8], '100200000': [2], '100200120': [2],
28
    '000002121': [0], '021002001': [6], '121200000': [8]}
29
30
31
class Checkerboard:
32
    def __init__(
33
        self, test=0, z=-70, x=265.3, y=45.5, x_offset=-35.8, y_offset=-39,
34
        home=[140, 100, 0], first=1, r_value=1500, b_value=1500, upr=0.0,
35
            cx=50, cy=50):
36
        self.test = test
37
        self.r_value = r_value  # color area threshold
38
        self.b_value = b_value  # ''
39
40
        self.round = 0  # moved count
41
        self.first = first  # is bot first
42
        self.blankcount = 0
43
        self.x, self.y, self.z = x, y, z  # grid #1 coordinates (top left of Dobot #1)
44
45
        # ** x, y, x_offset, y_offset must be as precise as possible for Dobot #1.
46
47
        self.upr = upr  # unit pixel ratio
48
        self.cx, self.cy = cx, cy  # grid #1 center image coordinates (normally (55, 55))
49
        self.x_offset, self.y_offset = x_offset, y_offset
50
        self.xupr, self.yupr = self.x_offset / 95, self.y_offset / 95  # unit pixel ratio
51
        self.cap = cv2.VideoCapture(0)
52
        self.home = home  # home position
53
        self.sit = 0  # situation id
54
        self.pos = [(self.x, self.y+self.y_offset*3),
55
                    (self.x+self.x_offset, self.y+self.y_offset*3),
56
                    (self.x+2*x_offset, self.y+self.y_offset*3),
57
                    (self.x, self.y-self.y_offset),
58
                    (self.x+self.x_offset, self.y-self.y_offset),
59
                    (self.x+2*self.x_offset, self.y-self.y_offset)]  # default chess coordinates
60
        self.checkattemp = 0  # saving checks
61
        self.checkerboard = '0' * 9  # checkerboard with color id
62
        self.checkerboardchecks = ['0' * 9] * 3  # list of self.checkattemp
63
        self.grid = np.array([[0, 100, 200, 300], [0, 100, 200, 300]])  # cropping
64
65
    def c(self):  # capture the checkerboard
66
        d._set_ptp_cmd(self.home[0], self.home[1], self.home[2], 0, 0x02)
67
        self.frame = self.cap.read()[1]
68
69
    def com(self):  # compare new and old checkerboard
70
        if (sum(map(lambda c0, c1, c2: c0 == c1 == c2,
71
                    self.checkerboardchecks[0], self.checkerboardchecks[1],
72
                    self.checkerboardchecks[2])) == 9):  # 3 same check results of new
73
            different = sum(map(lambda c, c0: c != c0, self.checkerboard,
74
                                self.checkerboardchecks[0]))  # difference of old and new
75
            if different == 1:
76
                if (
77
                    sum([not int(x) for x in self.checkerboard if x == '0']
78
                        ) - sum([not int(x) for x in self.checkerboardchecks[0]
79
                                 if x == '0']) == 1):  # grid with chess different count = -1
80
                    return True
81
                else:
82
                    return False
83
            elif different == 0:
84
                return False
85
            else:
86
                pass
87
        else:
88
            return False
89
90
    def cnt(self, src, lmask, umask, lmask1=0, umask1=0, getCenter=False):  # find contour
91
        blurred = cv2.GaussianBlur(src, (5, 5), 0)
92
        hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
93
94
        mask = cv2.inRange(hsv, lmask, umask)
95
        if lmask1 + umask1 is not 0:
96
            mask1 = cv2.inRange(hsv, lmask1, umask1)
97
            mask = mask + mask1
98
        contours = cv2.findContours(mask, cv2.RETR_EXTERNAL,
99
                                    cv2.CHAIN_APPROX_NONE)[1]
100
        if len(contours) > 0:
101
            contour = max(contours, key=cv2.contourArea)  # only return the contour with max area
102
            area = cv2.contourArea(contour)
103
            if getCenter:
104
                Moments = cv2.moments(contour)
105
                if Moments['m00'] != 0:
106
                    cX = int(Moments['m10'] / Moments['m00'])
107
                    cY = int(Moments['m01'] / Moments['m00'])
108
                else:
109
                    cX, cY = (self.cx, self.cy)
110
                return contour, area, (cX, cY)
111
            else:
112
                return contour, area
113
        else:
114
            if getCenter:
115
                return 0, 0, (False, False)
116
            else:
117
                return 0, 0
118
119
    def cntcenter(self, src, threshold, lmask, umask, lmask1=0, umask1=0):  # find contour of all placed chess and get their centers
120
        blurred = cv2.GaussianBlur(src, (5, 5), 0)
121
        hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
122
123
        mask = cv2.inRange(hsv, lmask, umask)
124
        if lmask1 + umask1 is not 0:
125
            mask1 = cv2.inRange(hsv, lmask1, umask1)
126
            mask = mask + mask1
127
        contours = cv2.findContours(mask, cv2.RETR_EXTERNAL,
128
                                    cv2.CHAIN_APPROX_NONE)[1]
129
130
        self.centers = []
131
        if len(contours) > 0:
132
            for contour in contours:
133
                if cv2.contourArea(contour) < threshold:
134
                    continue
135
                else:
136
                    Moments = cv2.moments(contour)
137
                    cX = int(Moments['m10'] / Moments['m00'])
138
                    cY = int(Moments['m01'] / Moments['m00'])
139
                    center = (cX, cY)
140
                    self.centers.append(center)
141
        return self.centers
142
143
    def transform(self):  # matrix perspective transform of image
144
        global initstat, pts1
145
        contour, area = self.cnt(self.frame, np.array([0, 0, 0]),
146
                                 np.array([255, 150, 75]))
147
        print('board area = ', area)
148
        if area < 10000:
149
            return True
150
            pass
151
152
        epsilon = 0.05*cv2.arcLength(contour, True)
153
        approx = cv2.approxPolyDP(contour, epsilon, True)
154
155
        if initstat or True:
156
            pts1 = np.float32(approx)
157
            initstat = False
158
        pts2 = np.float32([[0, 0], [0, 300], [300, 300], [300, 0]])
159
        M = cv2.getPerspectiveTransform(pts1, pts2)
160
        self.frame = cv2.warpPerspective(self.frame, M, (300, 300))
161
162
        self.grids = []
163
        for i in range(9):
164
            self.grids.append(
165
                self.frame[self.grid[0, i // 3]:self.grid[0, i // 3+1],
166
                           self.grid[1, i % 3]:self.grid[1, i % 3+1]])
167
168
    def check(self, delay=0, startc=0, getCenter=0):  # recognize the checkerboard
169
        global initstat
170
        self.c()
171
        try:
172
            if self.transform():
173
                return False
174
        except cv2.error:
175
            initstat = True
176
            print('camera blocked')
177
            return False
178
179
        '''if startc:
180
            return np.std(self.frame, 2).std() < 12'''
181
        self.centers = {}
182
        self.checkattemp += 1
183
        checking = ''
184
        print('red area    blue area')
185
        for gn, i in zip(self.grids, range(9)):
186
            # blue
187
            lower_blue = np.array([90, 100, 90])
188
            upper_blue = np.array([110, 255, 255])
189
            # red
190
            lower_red = np.array([0, 100, 90])
191
            upper_red = np.array([10, 255, 255])
192
            lower_red1 = np.array([170, 100, 90])
193
            upper_red1 = np.array([180, 255, 255])
194
195
            if getCenter:
196
                self.blue_centers = self.cntcenter(
197
                    self.frame, self.b_value, lower_blue, upper_blue)
198
199
                self.red_centers = self.cntcenter(
200
                    self.frame, self.r_value, lower_red, upper_red, lower_red1, upper_red1,)
201
202
                print(self.blue_centers, '\n', self.red_centers)
203
                return self.blue_centers, self.red_centers
204
            else:
205
                contour_blue, area_blue = self.cnt(gn, lower_blue, upper_blue)
206
                contour_red, area_red = self.cnt(
207
                    gn, lower_red, upper_red, lower_red1, upper_red1)
208
                print(area_red, area_blue, sep='  ')
209
210
            if area_red > self.r_value and area_blue < self.b_value:
211
                checking += '1'
212
            elif area_red < self.r_value and area_blue > self.b_value:
213
                checking += '2'
214
            elif area_red < self.r_value and area_blue < self.b_value:
215
                checking += '0'
216
            else:
217
                checking += '3'
218
219
        print('checking = ', checking)
220
        if startc:
221
            if checking == '000000000':
222
                return True
223
        self.checkerboardv = np.array(
224
            [int(x) for x in self.checkerboard]).reshape(3, 3)
225
        if '3' in checking:
226
            return False
227
        if checking == '000000000' and self.first == 1 and self.round == 0:
228
            self.checkerboard = checking
229
            return True
230
        else:
231
            self.checkerboardchecks[self.checkattemp % 3] = checking
232
        if checking == '000000000':
233
            if self.checkerboard != '000000000':
234
                self.blankcount += 1
235
        else:
236
            self.blankcount = 0
237
        if self.com():
238
            self.checkerboard = checking
239
            return True
240
        else:
241
            return False
242
243
    def rows(self):  # divide the checkerboard into 8 rows
244
        self.checkerboardv = np.array(
245
            [int(x) for x in self.checkerboard]).reshape(3, 3)
246
        self.r = list()
247
        self.r.append((self.checkerboardv[0], 0))
248
        self.r.append((self.checkerboardv[1], 1))
249
        self.r.append((self.checkerboardv[2], 2))
250
        self.r.append((self.checkerboardv[:, 0], 3))
251
        self.r.append((self.checkerboardv[:, 1], 4))
252
        self.r.append((self.checkerboardv[:, 2], 5))
253
        self.r.append((self.checkerboardv.diagonal(), 6))
254
        self.r.append((np.flip(self.checkerboardv, 1).diagonal(), 7))
255
256
    def situation(self):
257
        self.sit = 0
258
        self.rows()
259
        for rn in self.r:
260
            if np.count_nonzero(rn[0] == 2) == 3:  # lose
261
                self.sit = 7
262
                return self.sit
263
            elif np.count_nonzero(rn[0] == 1) == 3:  # win
264
                self.sit = 8
265
                return self.sit
266
        if np.count_nonzero(self.checkerboardv) == 9:  # draw
267
            self.sit = 6
268
            return self.sit
269
270
        if self.sit == 0:
271
            for rn in self.r:
272
                if np.count_nonzero(rn[0] == 0):  # 0xx
273
                    self.sit = 0
274
                    self.move = [rn[1], int(np.where(rn[0] == 0)[0][0])]
275
                else:
276
                    pass
277
278
        for rn in self.r:
279
            if (np.count_nonzero(
280
                rn[0] == 2) == 2 and np.count_nonzero(
281
                    rn[0] == 0) == 1):  # 220
282
                self.sit = 1
283
                self.move = [rn[1], int(np.where(rn[0] == 0)[0][0])]
284
                break
285
286
        for rn in self.r:
287
            if (np.count_nonzero(
288
                rn[0] == 0) == 1 and np.count_nonzero(
289
                    rn[0] == 1) == 2):  # 110
290
                self.sit = 2
291
                self.move = [rn[1], int(np.where(rn[0] == 0)[0][0])]
292
                break
293
294
        if self.checkerboard in movex:
295
            self.move = random.choice(movex[self.checkerboard])
296
            self.move = [self.move // 3, self.move % 3]
297
        else:
298
            if self.move[0] in range(0, 3):
299
                pass
300
            elif self.move[0] in range(3, 6):
301
                self.move = [self.move[1], self.move[0] - 3]
302
            elif self.move[0] == 6:
303
                self.move = [self.move[1], self.move[1]]
304
            elif self.move[0] == 7:
305
                self.move = [self.move[1], 2 - self.move[1]]
306
307
        return self.sit
308
309
    def intention(self):
310
        self.coor = [self.x + self.x_offset * self.move[0],
311
                     self.y + self.y_offset * self.move[1]]
312
        print(self.move, self.coor, sep='   ')
313
        return self.coor
314
315
    def m(self):  # move
316
        # get
317
        d._set_end_effector_suction_cup(False)
318
        d._set_ptp_cmd(160, 0, 0, 0, 0x02)  # #
319
        d._set_ptp_cmd(self.pos[self.round][0], self.pos[self.round][1],
320
                       self.z, 0, 0x00)
321
        d._set_end_effector_suction_cup(True)
322
        time.sleep(1)
323
324
        # place
325
        d._set_ptp_cmd(self.coor[0], self.coor[1], self.z, 0, 0x00)
326
        d._set_end_effector_suction_cup(False)
327
        d._set_ptp_cmd(self.coor[0], self.coor[1], self.z + 20, 0, 0x02)
328
        d._set_ptp_cmd(self.home[0], self.home[1], self.home[2], 0, 0x02)
329
330
        self.checkerboard = self.checkerboard[
331
            :self.move[0] * 3 + self.move[1]] + '1' + self.checkerboard[
332
            self.move[0] * 3 + self.move[1] + 1:]
333
        self.round += 1
334
335
    def start(self):  # start game setup
336
        d._set_ptp_cmd(self.home[0], self.home[1], self.home[2], 0, 0x02)
337
        time.sleep(3)
338
        while True:
339
            if self.check(0, 1):
340
                print('bot first = ', self.first)
341
                return 'Game Start'
342
            else:
343
                print('please clear the board.')
344
            time.sleep(1.5)
345
346
    def end(self):  # end game and clear checkerboard
347
        if self.sit < 5:
348
            #
349
            pass
350
        else:
351
            while self.check(getCenter=True) is False:
352
                pass
353
            self.reds = np.where(self.checkerboardv.reshape(9) == 1)[0]
354
            self.blues = np.where(self.checkerboardv.reshape(9) == 2)[0]
355
            if self.sit == 9:
356
                print('interrupted')
357
358
            if self.sit == 8:
359
                print('win')
360
361
            elif self.sit == 7:
362
                print('lose')
363
364
            elif self.sit == 6:
365
                print('draw')
366
367
            d._set_ptp_cmd(160, 0, 0, 0, 0x02)  # #
368
            for i, j in zip(self.red_centers, range(len(self.red_centers))):  # red
369
                # get
370
                self.coor = [self.x + (i[1] - self.cx) * self.xupr,
371
                             self.y + (i[0] - self.cy) * self.yupr]
372
373
                d._set_end_effector_suction_cup(False)
374
                d._set_ptp_cmd(self.coor[0], self.coor[1], self.z, 0, 0x00)
375
                d._set_end_effector_suction_cup(True)
376
                time.sleep(1)
377
378
                # place
379
                d._set_ptp_cmd(self.pos[j][0], self.pos[j][1], self.z, 0, 0x00)
380
                d._set_end_effector_suction_cup(False)
381
382
            for i, j in zip(self.blue_centers, range(len(self.blue_centers))):  # blue
383
                # get
384
                self.coor = [self.x + (i[1] - self.cx) * self.xupr,
385
                             self.y + (i[0] - self.cy) * self.yupr]
386
387
                d._set_end_effector_suction_cup(False)
388
                d._set_ptp_cmd(self.coor[0], self.coor[1], self.z, 0, 0x00)
389
                d._set_end_effector_suction_cup(True)
390
                time.sleep(1)
391
392
                # place
393
                d._set_ptp_cmd(self.pos[-1][0], self.pos[-1][1], self.z, 0, 0x00)
394
395
                d._set_end_effector_suction_cup(False)
396
397
            d._set_ptp_cmd(self.pos[-1][0], self.pos[-1][1], self.z + 20, 0,
398
                           0x02)
399
            d._set_ptp_cmd(self.home[0], self.home[1], self.home[2], 0, 0x02)
400
            self.first = True if self.sit == 9 else not self.first
401
            self.sit == 0
402
            self.checkerboardv, self.checkerboard = np.array(
403
                [[0, 0, 0], [0, 0, 0], [0, 0, 0]]), '0' * 9
404
405
            self.start()
406
            self.checkerboard = '0' * 9
407
            self.checkerboardchecks = ['0' * 9] * 3
408
            self.checkattemp = 0
409
            self.round = 0
410
            self.blankcount = 0
411
412
413
# main
414
def main():
415
    global board
416
    board = Checkerboard(1)
417
    board.start()
418
    while True:
419
        time.sleep(0.5)
420
        print(board.checkerboard)
421
        try:
422
            if board.round % 2 != board.first or True:  # turn
423
                if board.check():
424
                    # cv2.imshow('frame', board.frame)
425
                    # board.situation()
426
                    # board.intention()
427
                    if board.situation() < 5:
428
                        board.intention()
429
                        board.m()
430
                        time.sleep(6)
431
                    elif board.sit > 5:
432
                        board.end()
433
                        time.sleep(5 + board.round * 10)
434
                else:
435
                    pass
436
                if board.situation() > 5:
437
                    board.end()
438
            if board.blankcount >= 10:
439
                board.sit = 9
440
                board.end()
441
442
        except KeyboardInterrupt:
443
            return None
444
445
446
if __name__ == '__main__':
447
    initstat = True
448
    main()
449
    pass

Download source code

welcome

 Album 

米Teen唔鐘意玩現成品,喜歡由零件開始砌。99次失敗唔係問題,最緊要係有1次成功!

米Teen總部設在聖公會白約翰會督中學506室,在午膳後和放學後集會。有興趣參與的同學,請與會長或導師聯絡。

米Teen專頁

506室內 2020-11-04 21:43:57
溫度 26℃ 濕度 95%
街景 2020-11-04 21:41:52
街景
5樓室外 2020-11-04 21:43:43
溫度 22℃ 濕度 95%
街景 2020-11-04 21:34:34
街景
 ⇧