├── .gitignore
├── ArmControl.py
├── ChessLogic.py
├── Interface.py
├── LICENSE
├── README.md
├── VisionModule.py
├── audio
├── capture.mp3
├── check.mp3
├── checkmate.mp3
├── excuse.mp3
├── good_luck.mp3
├── goodbye.mp3
├── invalid_move.mp3
├── k_castling.mp3
├── passant.mp3
├── please.mp3
├── promotion.mp3
├── q_castling.mp3
└── reset.mp3
├── games
└── stockfishX64.exe
├── interface_images
├── bclock.png
├── cb_pattern.jpg
├── game_start.png
├── lss_arm_cb.png
├── lss_arm_setup.jpg
├── robot_icon.ico
└── wclock.png
├── lss.py
├── lss_const.py
├── params.txt
├── pieces_images
├── ChessPiecesArray.png
├── bishopb.png
├── bishopw.png
├── blank.png
├── game.pgn
├── kingb.ico
├── kingb.png
├── kingw.png
├── knightb.png
├── knightw.png
├── nbishopb.png
├── nbishopw.png
├── nkingb.png
├── nkingw.png
├── nknightb.png
├── nknightw.png
├── npawnb.png
├── npawnw.png
├── nqueenb.png
├── nqueenw.png
├── nrookb.png
├── nrookw.png
├── pawnb.png
├── pawnw.png
├── queenb.png
├── queenw.png
├── rookb.png
└── rookw.png
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | env/
2 | __pycache__/
3 | params.txt
--------------------------------------------------------------------------------
/ArmControl.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from math import atan2, sqrt, cos, sin, acos, pi, copysign
3 | import time
4 | import os
5 | from gtts import gTTS
6 | import VisionModule as vm
7 | import lss
8 | import lss_const as lssc
9 | import platform
10 | import Interface as inter
11 |
12 | # Change to the correct COM port for your setup
13 | if platform.system() == 'Windows':
14 | port = "COM3"
15 | elif platform.system() == 'Linux':
16 | port = "/dev/ttyUSB0"
17 |
18 | lss.initBus(port,lssc.LSS_DefaultBaud)
19 | base = lss.LSS(1)
20 | shoulder = lss.LSS(2)
21 | elbow = lss.LSS(3)
22 | wrist = lss.LSS(4)
23 | gripper = lss.LSS(5)
24 | allMotors = lss.LSS(254)
25 |
26 | allMotors.setAngularHoldingStiffness(0)
27 | allMotors.setMaxSpeed(100)
28 | base.setMaxSpeed(60)
29 | shoulder.setMotionControlEnabled(0)
30 | elbow.setMotionControlEnabled(0)
31 |
32 | CST_ANGLE_MIN = -90
33 | CST_ANGLE_MAX = 90
34 |
35 | def checkConstraints(value, min, max):
36 | if (value < min):
37 | value = min
38 | if (value > max):
39 | value = max
40 | return (value)
41 |
42 | # Desired positions in x, y, z, gripper aperture
43 | def LSS_IK(targetXYZG):
44 |
45 | d1 = 4.13 # Bottom to shoulder
46 | d2 = 5.61 # Shoulder to elbow
47 | d3 = 6.39 # Elbow to wrist
48 | d4 = 4.52 # Wrist to end of gripper
49 |
50 | x0 = targetXYZG[0]
51 | y0 = targetXYZG[1]
52 | z0 = targetXYZG[2]
53 | g0 = targetXYZG[3]
54 |
55 | # Base angle (degrees)
56 | q1 = atan2(y0,x0) * 180/pi
57 |
58 | # Radius from the axis of rotation of the base in xy plane
59 | xyr = sqrt(x0**2 + y0**2)
60 |
61 | # Pitch angle
62 | q0 = 80
63 |
64 | # Gripper components in xz plane
65 | lx = d4 * cos(q0 * pi/180)
66 | lz = d4 * sin(q0 * pi/180)
67 |
68 | # Wrist coordinates in xz plane
69 | x1 = xyr - lx
70 | z1 = z0 + lz - d1
71 |
72 | # Distance between the shoulder axis and the wrist axis
73 | h = sqrt(x1**2 + z1**2)
74 |
75 | a1 = atan2(z1,x1)
76 | a2 = acos((d2**2 - d3**2 + h**2)/(2 * d2 * h))
77 |
78 | # Shoulder angle (degrees)
79 | q2 = (a1 + a2) * 180/pi
80 |
81 | # Elbow angle (degrees)
82 | a3 = acos((d2**2 + d3**2 - h**2)/(2 * d2 * d3))
83 | q3 = 180 - a3 * 180/pi
84 |
85 | # Wrist angle (degrees) (add 5 deg because of the wrist-gripper offset)
86 | q4 = q0 - (q3 - q2) + 5
87 |
88 | # Add 15 deg because of the shoulder-elbow axis offset
89 | q2 = q2 + 15
90 |
91 | # Return values Base, Shoulder, Elbow, Wrist, Gripper
92 | angles_BSEWG = [ q1, 90-q2, q3-90, q4, g0]
93 |
94 | # Check constraints
95 | for i in range(0,5):
96 | angles_BSEWG[i] = checkConstraints(angles_BSEWG[i], CST_ANGLE_MIN, CST_ANGLE_MAX)
97 |
98 | return(np.dot(10,angles_BSEWG).astype(int))
99 |
100 | def LSSA_moveMotors(angles_BSEWG):
101 |
102 | # If the servos detect a current higher or equal than the value (mA) before reaching the requested positions they will halt and hold
103 | wrist.moveCH(angles_BSEWG[3], 1000)
104 | shoulder.moveCH(angles_BSEWG[1], 1600)
105 | elbow.moveCH(angles_BSEWG[2], 1600)
106 | base.moveCH(angles_BSEWG[0], 1000)
107 | gripper.moveCH(angles_BSEWG[4], 500)
108 |
109 | arrived = False
110 | issue = 0
111 | i = 0
112 |
113 | # Check if they reached the requested position
114 | while arrived == False and issue == 0:
115 | bStat = base.getStatus()
116 | sStat = shoulder.getStatus()
117 | eStat = elbow.getStatus()
118 | wStat = wrist.getStatus()
119 | gStat = gripper.getStatus()
120 | # If a status is None print message if it continues to be None return issue 1
121 | if (bStat is None or sStat is None or eStat is None or wStat is None or gStat is None):
122 | print("- Unknown status")
123 | i = i + 1
124 | if (i >= 5):
125 | print("- Issue detected")
126 | issue = 1
127 | # If the statuses aren't None check their values
128 | else:
129 | # If a servo is Outside limits, Stuck, Blocked or in Safe Mode before it reaches the requested position reset the servos and return issue 1
130 | if (int(bStat)>6 or int(sStat)>6 or int(eStat)>6 or int(wStat)>6 or int(gStat)>6):
131 | print("- Issue detected")
132 | issue = 1
133 | # If all the servos are holding positions check if they have arrived
134 | elif (int(bStat)==6 and int(sStat)==6 and int(eStat)==6 and int(wStat)==6 and int(gStat)==6):
135 | bPos = base.getPosition()
136 | sPos = shoulder.getPosition()
137 | ePos = elbow.getPosition()
138 | wPos = wrist.getPosition()
139 | # If any position is None
140 | if (bPos is None or sPos is None or ePos is None or wPos is None):
141 | print("- Unknown position")
142 | # If they are holding in a different position than the requested one return issue 2
143 | elif (abs(int(bPos)-angles_BSEWG[0])>20 or abs(int(sPos)-angles_BSEWG[1])>50 or abs(int(ePos)-angles_BSEWG[2])>50 or abs(int(wPos)-angles_BSEWG[3])>20):
144 | sStat = shoulder.getStatus()
145 | eStat = elbow.getStatus()
146 | # Re-check shoulder and elbow status and positions
147 | if (int(sStat)==6 and int(eStat)==6):
148 | sPos = shoulder.getPosition()
149 | ePos = elbow.getPosition()
150 | if (sPos is None or ePos is None):
151 | print("- Unknown position")
152 | elif (abs(int(sPos)-angles_BSEWG[1])>40 or abs(int(ePos)-angles_BSEWG[2])>40):
153 | print("- Obstacle detected")
154 | issue = 2
155 | else:
156 | print("- Arrived\n")
157 | arrived = True
158 |
159 | return(arrived, issue)
160 |
161 | def CBtoXY(targetCBsq, params, color):
162 |
163 | wletterWeight = [-4, -3, -2, -1, 1, 2, 3, 4]
164 | bletterWeight = [4, 3, 2, 1, -1, -2, -3, -4]
165 | bnumberWeight = [8, 7, 6, 5, 4, 3, 2, 1]
166 |
167 | if targetCBsq[0] == 'k':
168 | x = 6 * params["sqSize"]
169 | y = 6 * params["sqSize"]
170 | else:
171 | if color: # White -> Robot color = Black
172 | sqletter = bletterWeight[ord(targetCBsq[0]) - 97]
173 | sqNumber = bnumberWeight[int(targetCBsq[1]) - 1]
174 | else: # Black
175 | sqletter = wletterWeight[ord(targetCBsq[0]) - 97]
176 | sqNumber = int(targetCBsq[1])
177 |
178 | x = params["baseradius"] + params["cbFrame"] + params["sqSize"] * sqNumber - params["sqSize"]*1/2
179 | y = params["sqSize"] * sqletter - copysign(params["sqSize"]*1/2,sqletter)
180 |
181 | return(x,y)
182 |
183 | def executeMove(move, params, color, homography, cap, selectedCam):
184 |
185 | allMotors.setColorLED(lssc.LSS_LED_Cyan)
186 | z = params["cbHeight"] + params["pieceHeight"]
187 | angles_rest = (0,-1155,450,1050,0)
188 | gClose = -2
189 | gOpen = -9.5
190 | goDown = 0.6*params["pieceHeight"]
191 | gripState = gOpen
192 | x, y = 0, 0
193 |
194 | for i in range(0,len(move),2):
195 |
196 | # Calculate position and FPC
197 | x0, y0 = x, y
198 | x, y = CBtoXY((move[i],move[i+1]), params, color)
199 | distance = sqrt(((x0-x)**2)+((y0-y)**2))
200 | fpc = int(distance) + 15
201 | shoulder.setFilterPositionCount(fpc)
202 | elbow.setFilterPositionCount(fpc)
203 |
204 | # Go up
205 | angles_BSEWG1 = LSS_IK([x, y, z + 1, gripState])
206 | print("1) MOVE UP")
207 | arrived1,issue1 = LSSA_moveMotors(angles_BSEWG1)
208 | askPermision(angles_BSEWG1, arrived1, issue1, homography, cap, selectedCam)
209 |
210 | # Go down
211 | shoulder.setFilterPositionCount(15)
212 | elbow.setFilterPositionCount(15)
213 | angles_BSEWG2 = LSS_IK([x, y, z - 1 - goDown, gripState])
214 | print("2) GO DOWN")
215 | arrived2,issue2 = LSSA_moveMotors(angles_BSEWG2)
216 | askPermision(angles_BSEWG2, arrived2, issue2, homography, cap, selectedCam)
217 |
218 | if (i/2)%2: # Uneven move (go lower to grab the piece)
219 | gripState = gOpen
220 | goDown = 0.6*params["pieceHeight"]
221 | else: # Even move (go a little higher to drop the piece)
222 | gripState = gClose
223 | goDown = 0.5*params["pieceHeight"]
224 |
225 | # Close / Open the gripper
226 | gripper.moveCH(int(gripState*10), 500)
227 | time.sleep(1)
228 | print("3) CLOSE/OPEN the gripper\n")
229 |
230 | # Go up
231 | angles_BSEWG3 = LSS_IK([x, y, z + 1, gripState])
232 | print("4) GO UP")
233 | arrived3,issue3 = LSSA_moveMotors(angles_BSEWG3)
234 | askPermision(angles_BSEWG3, arrived3, issue3, homography, cap, selectedCam)
235 |
236 | # Go back to resting position and go limp
237 | distance = sqrt(((x)**2)+((y)**2))
238 | fpc = int(distance) + 15
239 | shoulder.setFilterPositionCount(fpc)
240 | elbow.setFilterPositionCount(fpc)
241 | print("5) REST")
242 | moveState,_ = LSSA_moveMotors(angles_rest)
243 | allMotors.limp()
244 | allMotors.setColorLED(lssc.LSS_LED_Black)
245 |
246 | return(moveState)
247 |
248 | def askPermision(angles_BSEWG, arrived, issue, homography, cap, selectedCam):
249 |
250 | angles_rest = (0,-1155,450,1050,0)
251 | sec = 0
252 |
253 | while arrived == False: # If the servos couldn't reach the requested position
254 | if issue == 1:
255 | inter.speak("reset") # If a servo exceeded a limit
256 | allMotors.setColorLED(lssc.LSS_LED_Red) # Set LEDs to color Red
257 | wrist.move(1050) # Send arm to rest position
258 | shoulder.move(-1155)
259 | elbow.move(450)
260 | base.move(0)
261 | time.sleep(2)
262 | allMotors.limp() # Make the arm go limp
263 | allMotors.reset() # Reset the servos
264 | time.sleep(2)
265 | allMotors.confirm()
266 | allMotors.setMaxSpeed(100) # Reconfigure parameters
267 | base.setMaxSpeed(60)
268 | shoulder.setMotionControlEnabled(0)
269 | elbow.setMotionControlEnabled(0)
270 | shoulder.setFilterPositionCount(15)
271 | elbow.setFilterPositionCount(15)
272 | elif issue == 2:
273 | if sec == 0:
274 | inter.speak("excuse") # Play audio asking for permission
275 | else:
276 | inter.speak("please")
277 | print("Please move over")
278 | allMotors.setColorLED(lssc.LSS_LED_Magenta) # Change LEDs to Magenta
279 | LSSA_moveMotors(angles_rest) # Go to resting position
280 | allMotors.limp()
281 | sec = 0
282 | else:
283 | print("OK")
284 |
285 | while arrived == False and sec < 3: # If the servos couldn't reach the requested position and we haven't waited 3 sec
286 | if vm.safetoMove(homography, cap, selectedCam) != 0 or sec == 2: # Check if it is safe to move (vision code) or if we have waited 3 sec
287 | print("- Retrying")
288 | allMotors.setColorLED(lssc.LSS_LED_Cyan) # If it is true we change LEDs back to cyan
289 | arrived,_ = LSSA_moveMotors(angles_BSEWG) # And try moving the motors again
290 | else:
291 | time.sleep(1) # Wait one second
292 | sec = sec + 1
293 |
294 | def winLED(allMotors):
295 | for i in range (0, 4):
296 | for j in range (1, 8): # Loop through each of the 8 LED color (black = 0, red = 1, ..., white = 7)
297 | allMotors.setColorLED(j) # Set the color (session) of the LSS
298 | time.sleep(0.3) # Wait 0.3 seconds per color change
299 | allMotors.setColorLED(lssc.LSS_LED_Black)
--------------------------------------------------------------------------------
/ChessLogic.py:
--------------------------------------------------------------------------------
1 |
2 | '''
3 |
4 | chess.SQUARE_NAMES.index("h8")
5 | print(chess.piece_name(board.piece_type_at(chess.A7)))
6 |
7 | chess.piece_name(board.piece_type_at(chess.SQUARE_NAMES.index("e3")))
8 |
9 | chess.square_file(chess.SQUARE_NAMES.index("e2")) #me da realmente la columna, el resiltado para este ejemplo es 4
10 |
11 | chess.square_rank(chess.SQUARE_NAMES.index("e2")) #Da como resultado la fila, para este ejemplo es 1
12 |
13 | board.piece_type_at(chess.SQUARE_NAMES.index("e3")) == None
14 |
15 | '''
16 |
17 | #CONSTRUIREMOS LAS FUNCIONES BASICAS PARA EL CASO DE JUGAR CON BLANCAS
18 |
19 | #Modificar sistema de juego para en lugar de colocar la jugada, cologar un vector de casillas
20 |
21 | import chess
22 | import chess.engine
23 |
24 | def showCheck(board):
25 | if board.is_check():
26 | print("CHECK!!")
27 |
28 | def sequenceGenerator(uciMove, board):
29 | '''
30 | INPUT:
31 | uciMove -> Move in UCI format to analyze it.
32 | board -> Game state, this is a chess.Board() object.
33 | OUTPUT:
34 | result -> Is empty if a valid move is not detected, if it is detected then it is a dictionary data structure with two values:
35 | "seq": String with a sequence of chess coordinates to guide th robot movement.
36 | "type": String with type of movement, Castling-Promotion-Move-Capture-Passant.
37 | '''
38 | result = {}
39 | move = chess.Move.from_uci(uciMove)
40 | first = uciMove[:2]
41 | second = uciMove[2:4]
42 | last = uciMove[-1]
43 | graveyard = "k0"
44 |
45 | if last < '1' or last > '8': # Promotion Evaluation: Check if last char is a letter.
46 | if board.is_capture(move):
47 | result["seq"] = second + graveyard + first + second
48 | else:
49 | result["seq"] = first + second
50 | result["type"] = "Promotion"
51 | elif board.turn and uciMove == "e1g1" and chess.piece_name(board.piece_type_at(chess.SQUARE_NAMES.index("e1"))) == "king": #White King side castling
52 | result["seq"] = first + second + "h1f1"
53 | result["type"] = "White King Side Castling"
54 | elif board.turn and uciMove == "e1c1" and chess.piece_name(board.piece_type_at(chess.SQUARE_NAMES.index("e1"))) == "king": #White Queen side castling
55 | result["seq"] = first + second + "a1d1"
56 | result["type"] = "White Queen Side Castling"
57 | elif not board.turn and uciMove == "e8g8" and chess.piece_name(board.piece_type_at(chess.SQUARE_NAMES.index("e8"))) == "king": #Black King side castling
58 | result["seq"] = first + second + "h8f8"
59 | result["type"] = "Black King Side Castling"
60 | elif not board.turn and uciMove == "e8c8" and chess.piece_name(board.piece_type_at(chess.SQUARE_NAMES.index("e8"))) == "king": #Black Queen side castling
61 | result["seq"] = first + second + "a8d8"
62 | result["type"] = "White Queen Side Castling"
63 | elif board.is_en_passant(move): # En Passant
64 | if board.turn:
65 | squareFile = chess.square_file(chess.SQUARE_NAMES.index(first))
66 | squareRank = chess.square_rank(chess.SQUARE_NAMES.index(first))
67 | result["seq"] = chess.SQUARE_NAMES[chess.square(squareFile-1,squareRank)] + graveyard + first + second
68 | result["type"] = "Passant"
69 | else:
70 | squareFile = chess.square_file(chess.SQUARE_NAMES.index(first))
71 | squareRank = chess.square_rank(chess.SQUARE_NAMES.index(first))
72 | result["seq"] = chess.SQUARE_NAMES[chess.square(squareFile+1,squareRank)] + graveyard + first + second
73 | result["type"] = "Passant"
74 | elif board.is_capture(move): # Capture move
75 | result["seq"] = second + graveyard + first + second
76 | result["type"] = "Capture"
77 | else: # Normal move
78 | result["seq"] = uciMove
79 | result["type"] = "Move"
80 |
81 | return result
82 |
83 | def moveAnalysis (squares, board):
84 | '''
85 | INPUT:
86 | squares -> Squares detected by the computer vision algorithm.
87 | board -> State of game, this is a chess.Board() object.
88 | OUTPUT:
89 | result -> Is empty if a valid move is not detected, or on the contrary, a dictionary data structure with two values:
90 | "move": String with the valid movement in UCI format, for example "e2e4".
91 | "type": String with type of movement, Castling-Promotion-Move-Capture-Passant.
92 |
93 | NOTE: In case of promotion we must concatenate the new piece to UCI command,
94 | before pushing the move to the board state, we ask the user which is the promoted piece.
95 | '''
96 |
97 | result = {} # Move type, move coordinates
98 | kindOfMove = ""
99 | uciMove = ""
100 |
101 | elements = len(squares)
102 | if elements < 5 and elements > 1: # Lenght verification
103 | legalMoves = board.legal_moves
104 |
105 | if elements == 4: # Clastling evaluation
106 | if board.turn: # White Turn
107 | if ("e1" in squares) and ("h1" in squares): # Queen side castling evaluation
108 | if chess.Move.from_uci("e1g1") in legalMoves:
109 | result["move"] = "e1g1"
110 | result["type"] = "Castling"
111 | if ("e1" in squares) and ("a1" in squares): # Queen side castling evaluation
112 | if chess.Move.from_uci("e1c1") in legalMoves:
113 | result["move"] = "e1c1"
114 | result["type"] = "Castling"
115 | else: # Black Turn
116 | if ("e8" in squares) and ("h8" in squares): # Queen side castling evaluation
117 | if chess.Move.from_uci("e8g8") in legalMoves:
118 | result["move"] = "e8g8"
119 | result["type"] = "Castling"
120 | if ("e8" in squares) and ("a8" in squares): # Queen side castling evaluation
121 | if chess.Move.from_uci("e8c8") in legalMoves:
122 | result["move"] = "e8c8"
123 | result["type"] = "Castling"
124 |
125 | if not result:
126 | moveVector = [squares[0]+squares[1],squares[1]+squares[0]] # Instantiate the list with the two most probable movements
127 | if elements > 2:
128 | moveVector.append(squares[0]+squares[2])
129 | moveVector.append(squares[2]+squares[0])
130 | moveVector.append(squares[1]+squares[2])
131 | moveVector.append(squares[2]+squares[1])
132 | for uciMove in moveVector: # Move Validation
133 | move = chess.Move.from_uci(uciMove)
134 | if move in legalMoves:
135 | result["move"] = uciMove
136 | if board.is_en_passant(move):
137 | result["type"] = "Passant"
138 | elif board.is_capture(move):
139 | result["type"] = "Capture"
140 | else:
141 | result["type"] = "Move"
142 | return result
143 | elif chess.Move.from_uci(uciMove+"q") in legalMoves:
144 | result["move"] = uciMove
145 | result["type"] = "Promotion"
146 | return result
147 |
148 | return result
--------------------------------------------------------------------------------
/Interface.py:
--------------------------------------------------------------------------------
1 | import FreeSimpleGUI as sg
2 | import ChessLogic as cl
3 | import time
4 | import os
5 | import copy
6 | import threading
7 | import time
8 | import cv2
9 | import sys
10 | import json
11 | import VisionModule as vm
12 | import platform
13 | import ArmControl as ac
14 | import lss_const as lssc
15 | import pygame
16 | import pathlib
17 |
18 | try:
19 | from picamera.array import PiRGBArray
20 | from picamera import PiCamera
21 | except:
22 | pass
23 |
24 | '''
25 | styles:
26 |
27 | BlueMono
28 | GreenTan
29 | LightGreen
30 | Black
31 | Dark
32 | '''
33 |
34 | # GLOBAL VARIABLES
35 |
36 | CHESS_PATH = 'pieces_images' # path to the chess pieces
37 |
38 | BLANK = 0 # piece names
39 | PAWNW = 1
40 | KNIGHTW = 2
41 | BISHOPW = 3
42 | ROOKW = 4
43 | QUEENW = 5
44 | KINGW = 6
45 | PAWNB = 7
46 | KNIGHTB = 8
47 | BISHOPB = 9
48 | ROOKB = 10
49 | QUEENB = 11
50 | KINGB = 12
51 |
52 | blank = os.path.join(CHESS_PATH, 'blank.png')
53 | bishopB = os.path.join(CHESS_PATH, 'nbishopb.png')
54 | bishopW = os.path.join(CHESS_PATH, 'nbishopw.png')
55 | pawnB = os.path.join(CHESS_PATH, 'npawnb.png')
56 | pawnW = os.path.join(CHESS_PATH, 'npawnw.png')
57 | knightB = os.path.join(CHESS_PATH, 'nknightb.png')
58 | knightW = os.path.join(CHESS_PATH, 'nknightw.png')
59 | rookB = os.path.join(CHESS_PATH, 'nrookb.png')
60 | rookW = os.path.join(CHESS_PATH, 'nrookw.png')
61 | queenB = os.path.join(CHESS_PATH, 'nqueenb.png')
62 | queenW = os.path.join(CHESS_PATH, 'nqueenw.png')
63 | kingB = os.path.join(CHESS_PATH, 'nkingb.png')
64 | kingW = os.path.join(CHESS_PATH, 'nkingw.png')
65 |
66 | images = {BISHOPB: bishopB, BISHOPW: bishopW, PAWNB: pawnB, PAWNW: pawnW, KNIGHTB: knightB, KNIGHTW: knightW,
67 | ROOKB: rookB, ROOKW: rookW, KINGB: kingB, KINGW: kingW, QUEENB: queenB, QUEENW: queenW, BLANK: blank}
68 |
69 | wclock = os.getcwd() + '/interface_images/wclock.png'
70 | bclock = os.getcwd() + '/interface_images/bclock.png'
71 | FENCODE = ""
72 |
73 | colorTurn = True
74 | graveyard = 'k0'
75 | playerColor = True
76 | playing = False
77 | blackSquareColor = '#B58863'
78 | whiteSquareColor = '#F0D9B5'
79 | Debug = False
80 | sequence = []
81 | state = "stby"
82 | newGameState = "config"
83 | gameTime = 60.00
84 | whiteSide = 0
85 | route = os.getcwd() + '/'
86 | homography = []
87 | prevIMG = []
88 | chessRoute = ""
89 | detected = True
90 | selectedCam = 0
91 | skillLevel = 10 # Chess engine difficulty level
92 | cap = cv2.VideoCapture()
93 | rotMat = vm.np.zeros((2,2))
94 | physicalParams = {"baseradius": 0.00,
95 | "cbFrame": 0.00,
96 | "sqSize": 0.00,
97 | "cbHeight": 0.00,
98 | "pieceHeight": 0.00}
99 |
100 | def systemConfig():
101 | global chessRoute
102 |
103 | if platform.system() == 'Windows':
104 | chessRoute = "games/stockfishX64.exe"
105 | elif platform.system() == 'Linux':
106 | chessRoute = "/usr/games/stockfish"
107 |
108 | # GAME FUNCTIONS
109 |
110 | def pcTurn(board,engine):
111 | global sequence
112 | global state
113 | global homography
114 | global cap
115 | global selectedCam
116 |
117 | command = ""
118 | pcMove = engine.play(board, cl.chess.engine.Limit(time=1))
119 | sequence = cl.sequenceGenerator(pcMove.move.uci(), board)
120 |
121 | window.FindElement(key = "gameMessage").Update(sequence["type"])
122 | if sequence["type"] == "White Queen Side Castling" or sequence["type"] == "Black Queen Side Castling":
123 | command = "q_castling"
124 | elif sequence["type"] == "White King Side Castling" or sequence["type"] == "Black King Side Castling":
125 | command = "k_castling"
126 | elif sequence["type"] == "Capture":
127 | command = "capture"
128 | elif sequence["type"] == "Passant":
129 | command = "passant"
130 | elif sequence["type"] == "Promotion":
131 | command = "promotion"
132 | if command:
133 | speakThread = threading.Thread(target=speak, args=[command], daemon=True)
134 | speakThread.start()
135 |
136 | command = ""
137 | ac.executeMove(sequence["seq"], physicalParams, playerColor, homography, cap, selectedCam)
138 | board.push(pcMove.move)
139 | updateBoard(sequence, board)
140 | if board.is_checkmate():
141 | window.FindElement(key = "robotMessage").Update("CHECKMATE!")
142 | command = "checkmate"
143 | elif board.is_check():
144 | window.FindElement(key = "robotMessage").Update("CHECK!")
145 | command = "check"
146 | if command:
147 | speakThread = threading.Thread(target=speak, args=[command], daemon=True)
148 | speakThread.start()
149 | state = "robotMove"
150 | if board.is_game_over():
151 | playing = False
152 | state = "showGameResult"
153 |
154 | def startEngine():
155 | global engine
156 | global state
157 | global homography
158 |
159 | engine = cl.chess.engine.SimpleEngine.popen_uci(chessRoute)
160 | engine.configure({"Skill Level": skillLevel})
161 | if playerColor == colorTurn:
162 | state = "playerTurn"
163 | else:
164 | state = "pcTurn"
165 |
166 | def playerTurn(board,squares):
167 | result = cl.moveAnalysis(squares, board)
168 | piece = ""
169 | if result:
170 | if result["type"] == "Promotion":
171 | while not piece:
172 | piece = coronationWindow()
173 | result["move"] += piece
174 |
175 | sequence = cl.sequenceGenerator(result["move"], board)
176 | window.FindElement(key = "gameMessage").Update(sequence["type"])
177 | board.push_uci(result["move"])
178 | updateBoard(sequence,board)
179 | return True
180 | else:
181 | return False
182 |
183 | def startGame():
184 | global gameTime
185 | window.FindElement("newGame").Update(disabled=True)
186 | window.FindElement("quit").Update(disabled=False)
187 | window.FindElement(key = "wcount").Update(time.strftime("%H:%M:%S", time.gmtime(gameTime)))
188 | window.FindElement(key = "bcount").Update(time.strftime("%H:%M:%S", time.gmtime(gameTime)))
189 | window.FindElement(key = "clockButton").Update(image_filename=wclock)
190 | window.FindElement(key = "robotMessage").Update("Good Luck!")
191 | window.FindElement(key = "gameMessage").Update("--")
192 |
193 | def quitGame():
194 | window.FindElement("newGame").Update(disabled=False)
195 | window.FindElement("quit").Update(disabled=True)
196 | engine.quit()
197 |
198 | # INTERFACE FUNCTIONS
199 |
200 | def renderSquare(image, key, location):
201 | if (location[0] + location[1]) % 2:
202 | color = blackSquareColor
203 | else:
204 | color = whiteSquareColor
205 | return sg.Button('', image_filename=image, size=(1, 1),
206 | border_width=0, button_color=('white', color),
207 | pad=(0, 0), key=key)
208 |
209 | def redrawBoard(board):
210 | columns = 'abcdefgh'
211 | global playerColor
212 | if playerColor:
213 | sq = 63
214 | for i in range(8):
215 | window.FindElement(key = str(8-i)+"r").Update(" "+str(8-i))
216 | window.FindElement(key = str(8-i)+"l").Update(str(8-i)+" ")
217 | for j in range(8):
218 | window.FindElement(key = columns[j]+"t").Update(columns[j])
219 | window.FindElement(key = columns[j]+"b").Update(columns[j])
220 | color = blackSquareColor if (i + 7-j) % 2 else whiteSquareColor
221 | pieceNum = board.piece_type_at(sq)
222 | if pieceNum:
223 | if not board.color_at(sq):
224 | pieceNum += 6
225 | else:
226 | pieceNum = 0
227 | piece_image = images[pieceNum]
228 | elem = window.FindElement(key=(i, 7-j))
229 | elem.Update(button_color=('white', color),
230 | image_filename=piece_image, )
231 | sq -= 1
232 | else:
233 | sq = 0
234 | for i in range(8):
235 | window.FindElement(key = str(8-i)+"r").Update(" "+str(i+1))
236 | window.FindElement(key = str(8-i)+"l").Update(str(i+1)+" ")
237 | for j in range(8):
238 | window.FindElement(key = columns[j]+"t").Update(columns[7-j])
239 | window.FindElement(key = columns[j]+"b").Update(columns[7-j])
240 | color = blackSquareColor if (i + 7-j) % 2 else whiteSquareColor
241 | pieceNum = board.piece_type_at(sq)
242 | if pieceNum:
243 | if not board.color_at(sq):
244 | pieceNum += 6
245 | else:
246 | pieceNum = 0
247 | piece_image = images[pieceNum]
248 | elem = window.FindElement(key=(i, 7-j))
249 | elem.Update(button_color=('white', color),
250 | image_filename=piece_image, )
251 | sq += 1
252 |
253 | def updateBoard(move, board):
254 | global playerColor
255 | global images
256 | for cont in range(0,len(move["seq"]),4):
257 | squareCleared = move["seq"][cont:cont+2]
258 | squareOcupied = move["seq"][cont+2:cont+4]
259 | scNum = cl.chess.SQUARE_NAMES.index(squareCleared)
260 |
261 | y = cl.chess.square_file(scNum)
262 | x = 7 - cl.chess.square_rank(scNum)
263 | color = blackSquareColor if (x + y) % 2 else whiteSquareColor
264 | if playerColor:
265 | elem = window.FindElement(key=(x, y))
266 | else:
267 | elem = window.FindElement(key=(7-x, 7-y))
268 | elem.Update(button_color=('white', color),
269 | image_filename=blank, )
270 |
271 | if squareOcupied != graveyard:
272 | soNum = cl.chess.SQUARE_NAMES.index(squareOcupied)
273 | pieceNum = board.piece_type_at(soNum)
274 | if not board.color_at(soNum):
275 | pieceNum += 6
276 | y = cl.chess.square_file(soNum)
277 | x = 7 - cl.chess.square_rank(soNum)
278 | color = blackSquareColor if (x + y) % 2 else whiteSquareColor
279 | if playerColor:
280 | elem = window.FindElement(key=(x, y))
281 | else:
282 | elem = window.FindElement(key=(7-x, 7-y))
283 | elem.Update(button_color=('white', color),
284 | image_filename=images[pieceNum], )
285 |
286 | def sideConfig(): # gameState: sideConfig
287 | global newGameState
288 | global state
289 | global whiteSide
290 | global prevIMG
291 | global rotMat
292 | i = 0
293 |
294 | img = vm.drawQuadrants(prevIMG)
295 | imgbytes = cv2.imencode('.png', img)[1].tobytes()
296 |
297 | windowName = "Calibration"
298 | initGame = [[sg.Text('Please select the "white" pieces side', justification='center', pad = (25,(5,15)), font='Any 15')],
299 | [sg.Image(data=imgbytes, key='boardImg')],
300 | [sg.Radio('1-2', group_id='grp', default = True,font='Any 14'), sg.Radio('2-3', group_id='grp',font='Any 14'),sg.Radio('4-3', group_id='grp',font='Any 14'), sg.Radio('1-4', group_id='grp',font='Any 14')],
301 | [sg.Text('_'*30)],
302 | [sg.Button("Back"), sg.Submit("Play")]]
303 | newGameWindow = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, location = (100,50), icon='interface_images/robot_icon.ico').Layout(initGame)
304 |
305 | while True:
306 | button,value = newGameWindow.Read(timeout=100)
307 |
308 | if button == "Play":
309 | newGameState = "initGame"
310 | while value[i] == False and i<4:
311 | i+=1
312 | whiteSide = i
313 | if whiteSide == 0:
314 | theta = 90
315 | elif whiteSide == 1:
316 | theta = 180
317 | elif whiteSide == 2:
318 | theta = -90
319 | elif whiteSide == 3:
320 | theta = 0
321 |
322 | rotMat = vm.findRotation(theta)
323 | prevIMG = vm.applyRotation(prevIMG,rotMat)
324 | break
325 | if button == "Back":
326 | newGameState = "ocupiedBoard"
327 | break
328 | if button in (None, 'Exit'): # MAIN WINDOW
329 | state = "stby"
330 | newGameState = "config"
331 | break
332 |
333 | newGameWindow.close()
334 |
335 | def ocupiedBoard(): # gameState: ocupiedBoard
336 | global newGameState
337 | global state
338 | global selectedCam
339 | global homography
340 | global prevIMG
341 |
342 | windowName = "Calibration"
343 | initGame = [[sg.Text('Place the chess pieces and press Next', justification='center', pad = (25,(5,15)), font='Any 15')],
344 | [sg.Image(filename='', key='boardVideo')],
345 | [sg.Text('_'*30)],
346 | [sg.Button("Back"), sg.Submit("Next")]]
347 | newGameWindow = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, location = (100,50), icon='interface_images/robot_icon.ico').Layout(initGame)
348 |
349 | while True:
350 | button,value = newGameWindow.Read(timeout = 10)
351 |
352 | if detected:
353 | frame = takePIC()
354 | prevIMG = vm.applyHomography(frame,homography)
355 | imgbytes = cv2.imencode('.png', prevIMG)[1].tobytes()
356 | newGameWindow.FindElement('boardVideo').Update(data=imgbytes)
357 |
358 | if button == "Next":
359 | newGameState = "sideConfig"
360 | break
361 | if button == "Back":
362 | newGameState = "calibration"
363 | break
364 | if button in (None, 'Exit'): # MAIN WINDOW
365 | state = "stby"
366 | newGameState = "config"
367 | break
368 |
369 | newGameWindow.close()
370 |
371 | def calibration(): # gameState: calibration
372 | global newGameState
373 | global state
374 | global selectedCam
375 | global homography
376 | global detected
377 |
378 | cbPattern = cv2.imread(route+'interface_images/cb_pattern.jpg', cv2.IMREAD_GRAYSCALE)
379 |
380 | windowName = "Camera calibration"
381 | initGame = [[sg.Text('Please adjust your camera and remove any chess piece', justification='center', pad = (25,(5,15)), font='Any 15', key = "calibrationBoard")],
382 | [sg.Image(filename='', key='boardVideo')],
383 | [sg.Text('_'*30)],
384 | [sg.Button("Back"), sg.Submit("Next")]]
385 | newGameWindow = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, location = (100,50), icon='interface_images/robot_icon.ico').Layout(initGame)
386 |
387 | while True:
388 | button,value = newGameWindow.Read(timeout = 10)
389 |
390 | if detected:
391 | frame = takePIC()
392 | imgbytes = cv2.imencode('.png', frame)[1].tobytes()
393 | newGameWindow.FindElement('boardVideo').Update(data=imgbytes)
394 | homography = []
395 | retIMG, homography = vm.findTransformation(frame,cbPattern)
396 | if retIMG:
397 | newGameWindow.FindElement('calibrationBoard').Update("Camera calibration successful. Please press Next")
398 | else:
399 | newGameWindow.FindElement('calibrationBoard').Update("Please adjust your camera and remove any chess piece")
400 |
401 | if button == "Next" and retIMG:
402 | newGameState = "ocupiedBoard"
403 | break
404 | if button == "Back":
405 | if not selectedCam:
406 | cap.close()
407 | newGameState = "config"
408 | break
409 | if button in (None, 'Exit'): # MAIN WINDOW
410 | state = "stby"
411 | newGameState = "config"
412 | break
413 |
414 | newGameWindow.close()
415 |
416 | def newGameWindow (): # gameState: config
417 | global playerColor
418 | global gameTime
419 | global newGameState
420 | global state
421 | global detected
422 | global cap
423 | global selectedCam
424 | global skillLevel
425 |
426 | windowName = "Configuration"
427 | frame_layout = [[sg.Radio('RPi Cam', group_id='grp', default = True, key = "rpicam"), sg.VerticalSeparator(pad=None), sg.Radio('USB0', group_id='grp', key = "usb0"), sg.Radio('USB1', group_id='grp', key = "usb1")]]
428 |
429 | initGame = [[sg.Text('Game Parameters', justification='center', pad = (25,(5,15)), font='Any 15')],
430 | [sg.CBox('Play as White', key='userWhite', default = playerColor)],
431 | [sg.Spin([sz for sz in range(1, 300)], initial_value=10, font='Any 11',key='timeInput'),sg.Text('Game time (min)', pad=(0,0))],
432 | [sg.Combo([sz for sz in range(1, 11)], default_value=10, key="enginelevel"),sg.Text('Engine skill level', pad=(0,0))],
433 | [sg.Frame('Camera Selection', frame_layout, pad=(0, 10), title_color='white')],
434 | [sg.Text('_'*30)],
435 | [sg.Button("Exit"), sg.Submit("Next")]]
436 | windowNewGame = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, icon='interface_images/robot_icon.ico').Layout(initGame)
437 | while True:
438 | button,value = windowNewGame.Read()
439 | if button == "Next":
440 | if value["rpicam"] == True:
441 | selectedCam = 0
442 | elif value["usb0"] == True:
443 | selectedCam = 1
444 | elif value["usb1"] == True:
445 | selectedCam = 2
446 | cap = initCam(selectedCam)
447 | if detected:
448 | newGameState = "calibration"
449 | playerColor = value["userWhite"]
450 | skillLevel = value["enginelevel"]*2
451 | gameTime = float(value["timeInput"]*60)
452 | break
453 | if button in (None, 'Exit'): # MAIN WINDOW
454 | state = "stby"
455 | break
456 | windowNewGame.close()
457 |
458 | def coronationWindow (): # gameState: config
459 | global playerColor
460 | pieceSelected = ""
461 | rook = rookB
462 | knight = knightB
463 | bishop = bishopB
464 | queen = queenB
465 | if playerColor:
466 | rook = rookW
467 | knight = knightW
468 | bishop = bishopW
469 | queen = queenW
470 |
471 | windowName = "Promotion"
472 | pieceSelection = [[sg.Text('Select the piece for promotion', justification='center', pad = (25,(5,15)), font='Any 15')],
473 | [sg.Button('', image_filename=rook, size=(1, 1),
474 | border_width=0, button_color=('white', "brown"),
475 | pad=((40,0), 0), key="rook"),sg.Button('', image_filename=knight, size=(1, 1),
476 | border_width=0, button_color=('white', "brown"),
477 | pad=(0, 0), key="knight"),sg.Button('', image_filename=bishop, size=(1, 1),
478 | border_width=0, button_color=('white', "brown"),
479 | pad=(0, 0), key="bishop"),sg.Button('', image_filename=queen, size=(1, 1),
480 | border_width=0, button_color=('white', "brown"),
481 | pad=(0, 0), key="queen")]]
482 | windowNewGame = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, icon='interface_images/robot_icon.ico').Layout(pieceSelection)
483 | while True:
484 | button,value = windowNewGame.Read()
485 | if button == "rook":
486 | pieceSelected = "r"
487 | break
488 | if button == "knight":
489 | pieceSelected = "k"
490 | break
491 | if button == "bishop":
492 | pieceSelected = "b"
493 | break
494 | if button == "queen":
495 | pieceSelected = "q"
496 | break
497 | if button in (None, 'Exit'): # MAIN WINDOW
498 | break
499 | windowNewGame.close()
500 | return pieceSelected
501 |
502 | def takePIC():
503 | global selectedCam
504 | global cap
505 | if selectedCam:
506 | for i in range(5): # Clear images stored in buffer
507 | cap.grab()
508 | _ , frame = cap.read() # USB Cam
509 | else:
510 | cap.capture(rawCapture, format="bgr") # RPi Cam
511 | frame = rawCapture.array
512 | rawCapture.truncate(0) # Clear the stream in preparation for the next image
513 |
514 | return frame
515 |
516 | def quitGameWindow ():
517 | global playing
518 | global window
519 | global cap
520 | windowName = "Quit Game"
521 | quitGame = [[sg.Text('Are you sure?',justification='center', size=(30, 1), font='Any 13')], [sg.Submit("Yes", size=(15, 1)),sg.Submit("No", size=(15, 1))]]
522 | if not selectedCam:
523 | cap.close()
524 | if playing:
525 | while True:
526 | windowNewGame = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, icon='interface_images/robot_icon.ico').Layout(quitGame)
527 | button,value = windowNewGame.Read()
528 | if button == "Yes":
529 | playing = False
530 | break
531 | if button in (None, 'Exit', "No"): # MAIN WINDOW
532 | break
533 | windowNewGame.close()
534 |
535 | def mainBoardLayout():
536 | # ------ Menu Definition ------ #
537 | menu_def = [['&Configuration',["&Dimensions","E&xit"]],
538 | ['&Help', 'About'], ]
539 |
540 | # ------ Layout ------ #
541 | # sg.SetOptions(margins=(0,0))
542 | sg.ChangeLookAndFeel('Dark')
543 | # Main board display layout
544 | board_layout = [[sg.T(' '*12)] + [sg.T('{}'.format(a), pad=((0,47),0), font='Any 13', key = a+'t') for a in 'abcdefgh']]
545 | # Loop though board and create buttons with images
546 | for i in range(8):
547 | numberRow = 8-i
548 | row = [sg.T(str(numberRow)+' ', font='Any 13', key = str(numberRow)+"l")]
549 | for j in range(8):
550 | row.append(renderSquare(blank, key=(i,j), location=(i,j)))
551 | row.append(sg.T(' '+str(numberRow), font='Any 13', key = str(numberRow)+"r"))
552 | board_layout.append(row)
553 | # Add labels across bottom of board
554 | board_layout.append([sg.T(' '*12)] + [sg.T('{}'.format(a), pad=((0,47),0), font='Any 13', key = a+'b') for a in 'abcdefgh'])
555 |
556 | frame_layout_game = [
557 | [sg.Button('---', size=(14, 2), border_width=0, font=('courier', 16), button_color=('black', "white"), pad=(4, 4), key="gameMessage")],
558 | ]
559 | frame_layout_robot = [
560 | [sg.Button('---', size=(14, 2), border_width=0, font=('courier', 16), button_color=('black', "white"), pad=(4, 4), key="robotMessage")],
561 | ]
562 | board_controls = [[sg.RButton('New Game', key='newGame', size=(15, 2), pad=(0,(0,7)), font=('courier', 16))],
563 | [sg.RButton('Quit', key='quit', size=(15, 2), pad=(0, 0), font=('courier', 16), disabled = True)],
564 | [sg.Frame('GAME', frame_layout_game, pad=(0, 10), font='Any 12', title_color='white', key = "frameMessageGame")],
565 | [sg.Frame('ROBOT', frame_layout_robot, pad=(0, (0,10)), font='Any 12', title_color='white', key = "frameMessageRobot")],
566 | [sg.Button('White Time', size=(7, 2), border_width=0, font=('courier', 16), button_color=('black', whiteSquareColor), pad=(0, 0), key="wt"),sg.Button('Black Time', font=('courier', 16), size=(7, 2), border_width=0, button_color=('black', blackSquareColor), pad=((7,0), 0), key="bt")],
567 | [sg.T("00:00:00",size=(9, 2), font=('courier', 13),key="wcount",pad = ((4,0),0)),sg.T("00:00:00",size=(9, 2), pad = (0,0), font=('courier', 13),key="bcount")],
568 | [sg.Button('', image_filename=wclock, key='clockButton', pad = ((25,0),0))]]
569 |
570 | layout = [[sg.Menu(menu_def, tearoff=False, key="manubar")],
571 | [sg.Column(board_layout),sg.VerticalSeparator(pad=None),sg.Column(board_controls)]]
572 |
573 | return layout
574 |
575 | def initCam(selectedCam):
576 | global detected
577 | global rawCapture
578 |
579 | button, value = window.Read(timeout=10)
580 | if selectedCam: # USB Cam
581 | cap = cv2.VideoCapture(selectedCam - 1)
582 | cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)
583 | cap.set(cv2.CAP_PROP_FRAME_HEIGHT,480)
584 | if not cap.isOpened():
585 | detected = False
586 | sg.popup_error('USB Video device not found')
587 | else: # RPi Cam
588 | cap = PiCamera()
589 | if not cap:
590 | detected = False
591 | sg.popup_error('RPi camera module not found')
592 | else:
593 | cap.resolution = (640, 480)
594 | rawCapture = PiRGBArray(cap, size=(640, 480))
595 | return cap
596 |
597 | def loadParams():
598 | global physicalParams
599 |
600 | if os.path.isfile('params.txt'):
601 | json_file = open('params.txt')
602 | physicalParams = json.load(json_file)
603 | print(json_file)
604 | else:
605 | outfile = open('params.txt', 'w')
606 | json.dump(physicalParams, outfile)
607 |
608 | def phisicalConfig ():
609 | global physicalParams
610 |
611 | windowName = "Chessboard parameters"
612 | robotParamLayout= [[sg.Text('Insert the physical dimensions in inches',justification='center', font='Any 14', pad=(10,10))],
613 | [sg.Spin([sz/100 for sz in range(1, 1000)], initial_value=physicalParams["baseradius"], font='Any 11'),sg.Text('Base Radius', pad=(0,0))],
614 | [sg.Spin([sz/100 for sz in range(1, 1000)], initial_value=physicalParams["cbFrame"], font='Any 11'),sg.Text('Chess Board Frame', pad=(0,0))],
615 | [sg.Spin([sz/100 for sz in range(1, 1000)], initial_value=physicalParams["sqSize"], font='Any 11'),sg.Text('Square Size', pad=(0,0))],
616 | [sg.Spin([sz/100 for sz in range(1, 1000)], initial_value=physicalParams["cbHeight"], font='Any 11'),sg.Text('Chess Board Height', pad=(0,0))],
617 | [sg.Spin([sz/100 for sz in range(1, 1000)], initial_value=physicalParams["pieceHeight"], font='Any 11'),sg.Text('Tallest Piece Height', pad=(0,0))],
618 | [sg.Text('_'*37)],
619 | [sg.Submit("Save", size=(15, 1)),sg.Submit("Close", size=(15, 1))]]
620 |
621 | while True:
622 | robotParamWindow = sg.Window(windowName, default_button_element_size=(12,1), auto_size_buttons=False, icon='interface_images/robot_icon.ico').Layout(robotParamLayout)
623 | button,value = robotParamWindow.Read()
624 | if button == "Save":
625 | physicalParams = {"baseradius": float(value[0]),
626 | "cbFrame": float(value[1]),
627 | "sqSize": float(value[2]),
628 | "cbHeight": float(value[3]),
629 | "pieceHeight": float(value[4])}
630 | outfile = open('params.txt', 'w')
631 | json.dump(physicalParams, outfile)
632 | break
633 | if button in (None, 'Close'): # MAIN WINDOW
634 | break
635 |
636 | robotParamWindow.close()
637 |
638 | layout = mainBoardLayout()
639 | window = sg.Window('ChessRobot', default_button_element_size=(12,1), auto_size_buttons=False, icon='interface_images/robot_icon.ico').Layout(layout)
640 |
641 | def speak(command):
642 | pygame.mixer.init()
643 | filePath = str(pathlib.Path().absolute())+"/audio/"
644 | pygame.mixer.music.load(filePath+command+".mp3")
645 | pygame.mixer.music.play()
646 |
647 | def main():
648 | global playerColor
649 | global state
650 | global playing
651 | global sequence
652 | global newGameState
653 | global detected
654 | global physicalParams
655 | global moveState
656 | global prevIMG
657 | global rotMat
658 | global homography
659 | global colorTurn
660 |
661 | systemConfig()
662 | loadParams()
663 | interfaceMessage = ""
664 | board = cl.chess.Board()
665 | squares = []
666 | whiteTime = 0
667 | blackTime = 0
668 | refTime = time.time()
669 | board = cl.chess.Board()
670 |
671 | while True :
672 | button, value = window.Read(timeout=100)
673 |
674 | if button in (None, 'Exit') or value["manubar"]=="Exit": # MAIN WINDOW
675 | angles_rest = (0,-1150,450,1100,0)
676 | _ = ac.LSSA_moveMotors(angles_rest)
677 | ac.allMotors.limp()
678 | ac.allMotors.setColorLED(lssc.LSS_LED_Black)
679 | break
680 |
681 | if value["manubar"]=="Dimensions":
682 | if playing:
683 | sg.popup("Please, first quit the game")
684 | else:
685 | phisicalConfig()
686 |
687 | if button =="newGame":
688 | if physicalParams["baseradius"] and physicalParams["cbFrame"] and physicalParams["sqSize"] and physicalParams["cbHeight"] and physicalParams["pieceHeight"]:
689 | state = "startMenu"
690 | else:
691 | sg.popup_error('Please configure the chess board dimensions in the Configuration option of menu bar')
692 |
693 | if button =="quit":
694 | ac.allMotors.setColorLED(lssc.LSS_LED_Black)
695 | quitGameWindow()
696 | if not playing:
697 | state = "showGameResult"
698 |
699 | # PC messages
700 | if playing:
701 | if whiteTime <= 0:
702 | playing = False
703 | state = "showGameResult"
704 | window.FindElement(key = "gameMessage").Update("Time Out\n"+"Black Wins")
705 | elif blackTime <= 0:
706 | playing = False
707 | state = "showGameResult"
708 | window.FindElement(key = "gameMessage").Update("Time Out\n"+ "White Wins")
709 |
710 | if state == "stby": # stby
711 | pass
712 |
713 | elif state == "startMenu": # Start Menu
714 | if newGameState == "config":
715 | newGameWindow()
716 | elif newGameState == "calibration":
717 | calibration()
718 | elif newGameState == "ocupiedBoard":
719 | ocupiedBoard()
720 | elif newGameState == "sideConfig":
721 | sideConfig()
722 | elif newGameState == "initGame":
723 | playing = True
724 | newGameState = "config"
725 | board = cl.chess.Board()
726 | if FENCODE:
727 | board = cl.chess.Board(FENCODE)
728 | colorTurn = board.turn
729 | startGame()
730 | whiteTime = gameTime
731 | blackTime = gameTime
732 | refTime = time.time()
733 | startEngineThread = threading.Thread(target=startEngine, daemon=True)
734 | startEngineThread.start()
735 | speak("good_luck")
736 | state = "stby"
737 | redrawBoard(board)
738 |
739 | elif state == "playerTurn": # Player Turn
740 | if button == "clockButton":
741 | currentIMG = takePIC()
742 | curIMG = vm.applyHomography(currentIMG,homography)
743 | curIMG = vm.applyRotation(curIMG,rotMat)
744 | squares = vm.findMoves(prevIMG, curIMG)
745 | if playerTurn(board, squares):
746 | state = "pcTurn"
747 | if board.is_game_over():
748 | playing = False
749 | state = "showGameResult"
750 | else:
751 | window.FindElement(key = "gameMessage").Update("Invalid move!")
752 | speak("invalid_move")
753 | state = "playerTurn"
754 |
755 | elif state == "pcTurn": # PC turn
756 |
757 | if board.turn:
758 | window.FindElement(key = "clockButton").Update(image_filename=wclock)
759 | else:
760 | window.FindElement(key = "clockButton").Update(image_filename=bclock)
761 |
762 | pcTurnThread = threading.Thread(target=pcTurn, args=(board,engine,), daemon=True)
763 | pcTurnThread.start()
764 | state = "stby" # Wait for the PC move, thread changes the state
765 |
766 | elif state == "robotMove": # Robotic arm turn
767 | previousIMG = takePIC()
768 | prevIMG = vm.applyHomography(previousIMG,homography)
769 | prevIMG = vm.applyRotation(prevIMG,rotMat)
770 | state = "playerTurn"
771 | window.FindElement(key = "robotMessage").Update("---")
772 | if board.turn:
773 | window.FindElement(key = "clockButton").Update(image_filename=wclock)
774 | else:
775 | window.FindElement(key = "clockButton").Update(image_filename=bclock)
776 |
777 | elif state == "showGameResult":
778 | gameResult = board.result()
779 | if gameResult == "1-0":
780 | window.FindElement(key = "gameMessage").Update("Game Over" + "\nWhite Wins")
781 | if not playerColor: # If the player color is black -> robot color is white
782 | ac.winLED(ac.allMotors)
783 | else:
784 | speak("goodbye")
785 | elif gameResult == "0-1":
786 | window.FindElement(key = "gameMessage").Update("Game Over" + "\nBlack Wins")
787 | if playerColor: # If the player color is white -> robot color is black
788 | ac.winLED(ac.allMotors)
789 | else:
790 | speak("goodbye")
791 | elif gameResult == "1/2-1/2":
792 | window.FindElement(key = "gameMessage").Update("Game Over" + "\nDraw")
793 | else:
794 | window.FindElement(key = "gameMessage").Update("Game Over")
795 | window.FindElement(key = "robotMessage").Update("Goodbye")
796 | quitGame()
797 | state = "stby"
798 |
799 | if playing:
800 | dt = time.time() - refTime
801 | if board.turn:
802 | whiteTime = whiteTime - dt
803 | if whiteTime < 0:
804 | whiteTime = 0
805 | refTime = time.time()
806 | window.FindElement(key = "wcount").Update(time.strftime("%H:%M:%S", time.gmtime(whiteTime)))
807 | else:
808 | blackTime = blackTime - dt
809 | if blackTime < 0:
810 | blackTime = 0
811 | refTime = time.time()
812 | window.FindElement(key = "bcount").Update(time.strftime("%H:%M:%S", time.gmtime(blackTime)))
813 |
814 | window.close()
815 |
816 |
817 | if __name__ == "__main__":
818 | main()
819 |
820 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # LSS Chess Robot
6 |
7 | The LSS Chess Robot is a robotic arm that can play chess against you. This is a free and open source project that integrates aspects of robotics, computer vision, and of course chess.
8 |
9 | ## Table of Contents
10 |
11 | - [Requirements](#requirements)
12 | - [Modules List](#modules-list)
13 | - [Getting Started](#getting-started)
14 | - [Install](#install)
15 | - [Run](#run)
16 | - [Authors](#authors)
17 | - [License](#license)
18 | - [Resources](#resources)
19 |
20 | ## Requirements
21 |
22 | ### Hardware
23 |
24 | - Lynxmotion Smart Servo (LSS) 4 DoF Robotic Arm
25 | - Raspberry Pi or Windows PC
26 | - Display
27 | - Keyboard
28 | - Mouse
29 | - Camera (USB or Raspberry Pi Camera Module)
30 | - Lighting
31 | - Chess board and pieces
32 | - Speaker or Headphones (Optional)
33 |
34 | ### Software
35 |
36 | - [OpenCV](https://opencv.org/)
37 | - [Stockfish](https://stockfishchess.org/)
38 | - [Python Chess](https://python-chess.readthedocs.io/en/latest/)
39 | - [FreeSimpleGUI](https://github.com/spyoungtech/FreeSimpleGUI)
40 |
41 | ## Modules List
42 |
43 | - [Vision Module](https://github.com/Robotics-Technology/Chess-Robot/VisionModule.py)
44 | - [Chess Logic](https://github.com/Robotics-Technology/Chess-Robot/ChessLogic.py)
45 | - [Arm Control](https://github.com/Robotics-Technology/Chess-Robot/ArmControl.py)
46 | - [Interface](https://github.com/Robotics-Technology/Chess-Robot/Interface.py)
47 |
48 | ## Getting Started
49 |
50 | To start using this project, proceed to the standard *clone* procedure:
51 |
52 | ```bash
53 | cd
54 | git clone https://github.com/Robotics-Technology/Chess-Robot.git
55 | ```
56 |
57 | ## Install
58 |
59 | - On Windows PC
60 |
61 | 1. Create a virtual environment (recommended):
62 |
63 | ```
64 | python -m venv env
65 | cd env/Scripts
66 | activate
67 | ```
68 |
69 | 2. Install requirements
70 |
71 | ```
72 | pip install -r requirements.txt
73 | ```
74 | - On Raspberry Pi
75 |
76 | 1. Create a virtual environment (recommended):
77 |
78 | ```
79 | python3 -m venv env
80 | source env/bin/activate
81 | ```
82 |
83 | 2. Install requirements
84 |
85 | ```
86 | pip3 install -r requirements.txt
87 | ```
88 |
89 | 3. If you are using the Raspberry Camera Module
90 |
91 | ```
92 | pip3 install "picamera[array]"
93 | ```
94 | Note: To avoid incompatibilities use the versions established in the "requirements", for this it is recommended to use a virtual environment.
95 |
96 | If you have issues with OpenCV on the Raspberry Pi try the following commands:
97 |
98 | ```
99 | sudo apt-get install libatlas-base-dev
100 | sudo apt-get install libjasper-dev
101 | sudo apt-get install libqtgui4
102 | sudo apt-get install libqt4-test
103 | sudo apt-get install libhdf5-dev
104 | sudo apt-get install libhdf5-serial-dev
105 | sudo apt-get install python3-pyqt5
106 | sudo apt-get install stockfish
107 | sudo pip3 install opencv-python==4.5.5.64
108 | sudo pip3 install opencv-contrib-python
109 | ```
110 |
111 | ## Run
112 |
113 | ```
114 | cd
115 | python Interface.py
116 | ```
117 |
118 |
119 |
120 |
121 | Chess Robot Interface
122 |
123 |

124 |
125 | Gameplay Video
126 |
127 | ## Authors
128 |
129 | - [Geraldine Barreto](http://github.com/geraldinebc)
130 | - [Eduardo Nunes](https://github.com/EduardoFNA)
131 |
132 | ## License
133 |
134 | LSS Chess Robot is available under the GNU General Public License v3.0
135 |
136 | ## Contributing
137 |
138 | Anyone is very welcome to contribute. Below is a list of identified improvements.
139 |
140 | ## To do
141 |
142 | - Change the gripper aperture acording to the chess piece type
143 | - Allow re-taking pictures in the case of board obstruction
144 |
145 | ## Limitations
146 |
147 | - The chessboard is limited by the reach of the arm and the squares need to be big enough so the arm gripper doesn’t stumble upon adjacent pieces.
148 | - This project was specifically made to be used with LSS smart servo motors using the LSS serial communication protocol so if you want to use it for a different robotic arm you will need to change how the servos are controlled and change the inverse kinematics parameters to match your specific arm.
149 |
150 | ## Resources
151 |
152 | Every module of the project is explained in the tutorial series available [here](https://www.robotshop.com/community/robots/show/chess-playing-robot/).
153 |
154 | Read more about the LSS Robotic Arm in the [wiki](https://www.robotshop.com/info/wiki/lynxmotion/view/servo-erector-set-robots-kits/ses-v2-robots/ses-v2-arms/lss-4dof-arm/).
155 |
156 | Purchase the LSS arm on [RobotShop](https://www.robotshop.com/en/lynxmotion-smart-servos-articulated-arm.html).
157 |
158 | Official Lynxmotion Smart Servo (LSS) libraries for Python available [here](https://github.com/Lynxmotion/LSS_Library_Python).
159 |
160 | If you want more details about the LSS protocol, go [here](https://www.robotshop.com/info/wiki/lynxmotion/view/lynxmotion-smart-servo/lss-communication-protocol/).
161 |
162 | Have any questions? Ask them on the Robotshop [Community](https://www.robotshop.com/community/).
163 |
--------------------------------------------------------------------------------
/VisionModule.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import string
4 | import time
5 | import os
6 |
7 | try:
8 | from picamera.array import PiRGBArray
9 | from picamera import PiCamera
10 | except:
11 | pass
12 |
13 | def findTransformation(img,cbPattern):
14 |
15 | patternSize = (7,7)
16 | imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
17 |
18 | # Find chessboard corners
19 | retCB, cornersCB = cv2.findChessboardCorners(cbPattern, patternSize, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)
20 | retIMG, cornersIMG = cv2.findChessboardCorners(imgGray, patternSize, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)
21 |
22 | if retIMG == 0:
23 | H = 0
24 | else:
25 | H, _ = cv2.findHomography(cornersIMG, cornersCB) # Find the transformation matrix
26 |
27 | return(retIMG, H)
28 |
29 | def applyRotation(img,R):
30 | if R.any() != 0:
31 | img = cv2.warpAffine(img, R, img.shape[1::-1], flags=cv2.INTER_LINEAR)
32 |
33 | return(img)
34 |
35 | def applyHomography(img,H):
36 |
37 | imgNEW = cv2.warpPerspective(img, H, (400, 400))
38 |
39 | return(imgNEW)
40 |
41 | def drawQuadrants(img):
42 |
43 | # Draw quadrants and numbers on image
44 | imgquad = img.copy()
45 | cv2.line(imgquad, (200, 0), (200, 400), (0,255,0), 3)
46 | cv2.line(imgquad, (0, 200), (400, 200), (0,255,0), 3)
47 | imgquad = cv2.putText(imgquad, '1', (80, 120) , cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 3, cv2.LINE_AA)
48 | imgquad = cv2.putText(imgquad, '2', (280, 120) , cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 3, cv2.LINE_AA)
49 | imgquad = cv2.putText(imgquad, '3', (280, 320) , cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 3, cv2.LINE_AA)
50 | imgquad = cv2.putText(imgquad, '4', (80, 320) , cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 3, cv2.LINE_AA)
51 |
52 | return(imgquad)
53 |
54 | def findRotation(theta):
55 |
56 | if theta != 0:
57 | rotMAT = cv2.getRotationMatrix2D(tuple(np.array((400,400)[1::-1])/2), theta, 1.0)
58 | else:
59 | rotMAT = np.zeros((2,2))
60 |
61 | return(rotMAT)
62 |
63 | def findMoves(img1, img2):
64 |
65 | size = 50
66 | img1SQ = img2SQ = []
67 | largest = [0, 0, 0, 0]
68 | coordinates = [0, 0, 0, 0]
69 | for y in range(0,8*size,size):
70 | for x in range(0,8*size,size):
71 | img1SQ = img1[x:x+size, y:y+size]
72 | img2SQ = img2[x:x+size, y:y+size]
73 | dist = cv2.norm(img2SQ, img1SQ)
74 | for z in range(0,4):
75 | if dist >= largest[z]:
76 | largest.insert(z,dist)
77 | # Save in algebraic chess notation
78 | coordinates.insert(z,(string.ascii_lowercase[int(x/size)]+str(int(y/size+1))))
79 | largest.pop()
80 | coordinates.pop()
81 | break
82 |
83 | # Make threshold with a percentage of the change in color of the biggest two
84 | thresh = (largest[0]+largest[1])/2*(0.5)
85 | for t in range(3,1,-1):
86 | if largest[t] < thresh:
87 | coordinates.pop()
88 |
89 | return(coordinates)
90 |
91 | def safetoMove(H, cap, selectedCam):
92 |
93 | cbPattern = cv2.imread(os.getcwd() + '/' +'interface_images/cb_pattern.jpg', cv2.IMREAD_GRAYSCALE)
94 |
95 | if selectedCam:
96 | for i in range(5): # Clear images stored in buffer
97 | cap.grab()
98 | _ , img = cap.read() # USB Cam
99 | else:
100 | rawCapture = PiRGBArray(cap, size=(640, 480))
101 | cap.capture(rawCapture, format="bgr") # RPi Cam
102 | img = rawCapture.array
103 | rawCapture.truncate(0) # Clear the stream in preparation for the next image
104 |
105 | img = cv2.warpPerspective(img, H, (cbPattern.shape[1], cbPattern.shape[0]))
106 |
107 | # Kmeans algorithm (Map the image to only two colors)
108 | K = 2
109 | criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
110 | Z = np.float32(img.reshape((-1,3)))
111 | ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
112 | center = np.uint8(center)
113 | res = center[label.flatten()].reshape((img.shape))
114 | imgGRAY = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
115 |
116 | # Try to find chessboard corners (if there's an obstacle it won't be able to do so)
117 | retIMG, cornersIMG = cv2.findChessboardCorners(imgGRAY, (7,7), cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)
118 |
119 | return(retIMG)
--------------------------------------------------------------------------------
/audio/capture.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/capture.mp3
--------------------------------------------------------------------------------
/audio/check.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/check.mp3
--------------------------------------------------------------------------------
/audio/checkmate.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/checkmate.mp3
--------------------------------------------------------------------------------
/audio/excuse.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/excuse.mp3
--------------------------------------------------------------------------------
/audio/good_luck.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/good_luck.mp3
--------------------------------------------------------------------------------
/audio/goodbye.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/goodbye.mp3
--------------------------------------------------------------------------------
/audio/invalid_move.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/invalid_move.mp3
--------------------------------------------------------------------------------
/audio/k_castling.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/k_castling.mp3
--------------------------------------------------------------------------------
/audio/passant.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/passant.mp3
--------------------------------------------------------------------------------
/audio/please.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/please.mp3
--------------------------------------------------------------------------------
/audio/promotion.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/promotion.mp3
--------------------------------------------------------------------------------
/audio/q_castling.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/q_castling.mp3
--------------------------------------------------------------------------------
/audio/reset.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/audio/reset.mp3
--------------------------------------------------------------------------------
/games/stockfishX64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/games/stockfishX64.exe
--------------------------------------------------------------------------------
/interface_images/bclock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/bclock.png
--------------------------------------------------------------------------------
/interface_images/cb_pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/cb_pattern.jpg
--------------------------------------------------------------------------------
/interface_images/game_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/game_start.png
--------------------------------------------------------------------------------
/interface_images/lss_arm_cb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/lss_arm_cb.png
--------------------------------------------------------------------------------
/interface_images/lss_arm_setup.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/lss_arm_setup.jpg
--------------------------------------------------------------------------------
/interface_images/robot_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/robot_icon.ico
--------------------------------------------------------------------------------
/interface_images/wclock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/interface_images/wclock.png
--------------------------------------------------------------------------------
/lss.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Author: Sebastien Parent-Charette (support@robotshop.com)
3 | # Version: 1.0.0
4 | # Licence: LGPL-3.0 (GNU Lesser General Public License version 3)
5 | #
6 | # Desscription: A library that makes using the LSS simple.
7 | # Offers support for most Python platforms.
8 | # Uses the Python serial library (pySerial).
9 | ###############################################################################
10 |
11 | ### Import required liraries
12 | import re
13 | import serial
14 | from math import sqrt, atan, acos, fabs
15 |
16 | ### Import constants
17 | import lss_const as lssc
18 |
19 | ### Class functions
20 | def initBus(portName, portBaud):
21 | LSS.bus = serial.Serial(portName, portBaud)
22 | LSS.bus.timeout = 0.1
23 |
24 | def closeBus():
25 | if LSS.bus is not None:
26 | LSS.bus.close()
27 | del LSS.bus
28 |
29 | # Write with optional parameter and modifiers
30 | def genericWrite(id, cmd, param = None, mod = None, value = None, mod2 = None, val2 = None):
31 | if LSS.bus is None:
32 | return False
33 | if mod is None:
34 | if param is None:
35 | LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + lssc.LSS_CommandEnd).encode())
36 | else:
37 | LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + lssc.LSS_CommandEnd).encode())
38 | else:
39 | if mod2 is None:
40 | LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + mod + str(value) + lssc.LSS_CommandEnd).encode())
41 | else:
42 | LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + mod + str(value) + mod2 + str(val2) + lssc.LSS_CommandEnd).encode())
43 | return True
44 |
45 | # Read an integer result
46 | def genericRead_Blocking_int(id, cmd):
47 | if LSS.bus is None:
48 | return None
49 | try:
50 | # Get start of packet and discard header and everything before
51 | c = LSS.bus.read()
52 | while (c.decode("utf-8") != lssc.LSS_CommandReplyStart):
53 | c = LSS.bus.read()
54 | if(c.decode("utf-8") == ""):
55 | break
56 | # Get packet
57 | data = LSS.bus.read_until(lssc.LSS_CommandEnd)
58 | # Parse packet
59 | matches = re.match("(\d{1,3})([A-Z]{1,4})(-?\d{1,18})", data.decode("utf-8"), re.I)
60 | # Check if matches are found
61 | if(matches is None):
62 | return(None)
63 | if((matches.group(1) is None) or (matches.group(2) is None) or (matches.group(3) is None)):
64 | return(None)
65 | # Get values from match
66 | readID = matches.group(1)
67 | readIdent = matches.group(2)
68 | readValue = matches.group(3)
69 | # Check id
70 | if(readID != str(id)):
71 | return(None)
72 | # Check identifier
73 | if(readIdent != cmd):
74 | return(None)
75 | except:
76 | return(None)
77 | # return value
78 | return(readValue)
79 |
80 | # Read a string result
81 | #@classmethod
82 | def genericRead_Blocking_str(id, cmd, numChars):
83 | if LSS.bus is None:
84 | return None
85 | if LSS.bus is None:
86 | return None
87 | try:
88 | # Get start of packet and discard header and everything before
89 | c = LSS.bus.read()
90 | while (c.decode("utf-8") != lssc.LSS_CommandReplyStart):
91 | c = LSS.bus.read()
92 | if(c.decode("utf-8") == ""):
93 | break
94 | # Get packet
95 | data = LSS.bus.read_until(lssc.LSS_CommandEnd)
96 | data = (data[:-1])
97 | # Parse packet
98 | matches = re.match("(\d{1,3})([A-Z]{1,4})(.{" + str(numChars) + "})", data.decode("utf-8"), re.I)
99 | # Check if matches are found
100 | if(matches is None):
101 | return(None)
102 | if((matches.group(1) is None) or (matches.group(2) is None) or (matches.group(3) is None)):
103 | return(None)
104 | # Get values from match
105 | readID = matches.group(1)
106 | readIdent = matches.group(2)
107 | readValue = matches.group(3)
108 | # Check id
109 | if(readID != str(id)):
110 | return(None)
111 | # Check identifier
112 | if(readIdent != cmd):
113 | return(None)
114 | except:
115 | return(None)
116 | # return value
117 | return(readValue)
118 |
119 | class LSS:
120 | # Class attribute
121 | bus = None
122 |
123 | ### Constructor
124 | def __init__(self, id = 0):
125 | self.servoID = id
126 |
127 | ### Attributes
128 | servoID = 0
129 |
130 | ### Functions
131 | #> Actions
132 | def reset(self):
133 | return (genericWrite(self.servoID, lssc.LSS_ActionReset))
134 |
135 | def confirm(self):
136 | return (genericWrite(self.servoID, lssc.LSS_ActionConfirm))
137 |
138 | def limp(self):
139 | return (genericWrite(self.servoID, lssc.LSS_ActionLimp))
140 |
141 | def hold(self):
142 | return (genericWrite(self.servoID, lssc.LSS_ActionHold))
143 |
144 | def move(self, pos):
145 | return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos))
146 |
147 | def moveRelative(self, delta):
148 | return (genericWrite(self.servoID, lssc.LSS_ActionMoveRelative, delta))
149 |
150 | def wheel(self, speed):
151 | return (genericWrite(self.servoID, lssc.LSS_ActionWheel, speed))
152 |
153 | def wheelRPM(self, rpm):
154 | return (genericWrite(self.servoID, lssc.LSS_ActionWheelRPM, rpm))
155 |
156 | def moveT(self, pos, value):
157 | return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ActionParameterTime, value))
158 |
159 | def moveCH(self, pos, value):
160 | return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ModifierCurrentHaltHold, value))
161 |
162 | def moveCHT(self, pos, value, tval):
163 | return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ModifierCurrentHaltHold, value, lssc.LSS_ActionParameterTime, tval))
164 |
165 | def moveCL(self, pos, value):
166 | return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ModifierCurrentLimp, value))
167 |
168 | #> Queries
169 | #def getID(self):
170 | #def getBaud(self):
171 |
172 | def getStatus(self):
173 | genericWrite(self.servoID, lssc.LSS_QueryStatus)
174 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryStatus))
175 |
176 | def getOriginOffset(self, queryType = lssc.LSS_QuerySession):
177 | genericWrite(self.servoID, lssc.LSS_QueryOriginOffset, queryType)
178 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryOriginOffset))
179 |
180 | def getAngularRange(self, queryType = lssc.LSS_QuerySession):
181 | genericWrite(self.servoID, lssc.LSS_QueryAngularRange, queryType)
182 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryAngularRange))
183 |
184 | def getPositionPulse(self):
185 | genericWrite(self.servoID, lssc.LSS_QueryPositionPulse)
186 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryPositionPulse))
187 |
188 | def getPosition(self):
189 | genericWrite(self.servoID, lssc.LSS_QueryPosition)
190 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryPosition))
191 |
192 | def getSpeed(self):
193 | genericWrite(self.servoID, lssc.LSS_QuerySpeed)
194 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QuerySpeed))
195 |
196 | def getSpeedRPM(self):
197 | genericWrite(self.servoID, lssc.LSS_QuerySpeedRPM)
198 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QuerySpeedRPM))
199 |
200 | def getSpeedPulse(self):
201 | genericWrite(self.servoID, lssc.LSS_QuerySpeedPulse)
202 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QuerySpeedPulse))
203 |
204 | def getMaxSpeed(self, queryType = lssc.LSS_QuerySession):
205 | genericWrite(self.servoID, lssc.LSS_QueryMaxSpeed, queryType)
206 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryMaxSpeed))
207 |
208 | def getMaxSpeedRPM(self, queryType = lssc.LSS_QuerySession):
209 | genericWrite(self.servoID, lssc.LSS_QueryMaxSpeedRPM, queryType)
210 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryMaxSpeedRPM))
211 |
212 | def getColorLED(self, queryType = lssc.LSS_QuerySession):
213 | genericWrite(self.servoID, lssc.LSS_QueryColorLED, queryType)
214 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryColorLED))
215 |
216 | def getGyre(self, queryType = lssc.LSS_QuerySession):
217 | genericWrite(self.servoID, lssc.LSS_QueryGyre, queryType)
218 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryGyre))
219 |
220 | # returns 0 if "DIS"
221 | def getFirstPosition(self):
222 | genericWrite(self.servoID, lssc.LSS_QueryFirstPosition)
223 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryFirstPosition))
224 |
225 | # returns true/false based on if QFD returns "DIS" (= False)
226 | def getIsFirstPositionEnabled(self):
227 | genericWrite(self.servoID, lssc.LSS_QueryFirstPosition)
228 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryFirstPosition) is not None)
229 |
230 | def getModel(self):
231 | genericWrite(self.servoID, lssc.LSS_QueryModelString)
232 | return (genericRead_Blocking_str(self.servoID, lssc.LSS_QueryModelString, 7))
233 |
234 | def getSerialNumber(self):
235 | genericWrite(self.servoID, lssc.LSS_QuerySerialNumber)
236 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QuerySerialNumber))
237 |
238 | def getFirmwareVersion(self):
239 | genericWrite(self.servoID, lssc.LSS_QueryFirmwareVersion)
240 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryFirmwareVersion))
241 |
242 | def getVoltage(self):
243 | genericWrite(self.servoID, lssc.LSS_QueryVoltage)
244 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryVoltage))
245 |
246 | def getTemperature(self):
247 | genericWrite(self.servoID, lssc.LSS_QueryTemperature)
248 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryTemperature))
249 |
250 | def getCurrent(self):
251 | genericWrite(self.servoID, lssc.LSS_QueryCurrent)
252 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryCurrent))
253 |
254 | #> Queries (advanced)
255 | def getAngularStiffness(self, queryType = lssc.LSS_QuerySession):
256 | genericWrite(self.servoID, lssc.LSS_QueryAngularStiffness, queryType)
257 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryAngularStiffness))
258 |
259 | def getAngularHoldingStiffness(self, queryType = lssc.LSS_QuerySession):
260 | genericWrite(self.servoID, lssc.LSS_QueryAngularHoldingStiffness, queryType)
261 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryAngularHoldingStiffness))
262 |
263 | def getAngularAcceleration(self, queryType = lssc.LSS_QuerySession):
264 | genericWrite(self.servoID, lssc.LSS_QueryAngularAcceleration, queryType)
265 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryAngularAcceleration))
266 |
267 | def getAngularDeceleration(self, queryType = lssc.LSS_QuerySession):
268 | genericWrite(self.servoID, lssc.LSS_QueryAngularDeceleration, queryType)
269 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryAngularDeceleration))
270 |
271 | def getIsMotionControlEnabled(self):
272 | genericWrite(self.servoID, lssc.LSS_QueryEnableMotionControl)
273 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryEnableMotionControl))
274 |
275 | def getBlinkingLED(self):
276 | genericWrite(self.servoID, lssc.LSS_QueryBlinkingLED)
277 | return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryBlinkingLED))
278 |
279 | #> Configs
280 | def setOriginOffset(self, pos, setType = lssc.LSS_SetSession):
281 | if setType == lssc.LSS_SetSession:
282 | return (genericWrite(self.servoID, lssc.LSS_ActionOriginOffset, pos))
283 | elif setType == lssc.LSS_SetConfig:
284 | return (genericWrite(self.servoID, lssc.LSS_ConfigOriginOffset, pos))
285 |
286 | def setAngularRange(self, delta, setType = lssc.LSS_SetSession):
287 | if setType == lssc.LSS_SetSession:
288 | return (genericWrite(self.servoID, lssc.LSS_ActionAngularRange, delta))
289 | elif setType == lssc.LSS_SetConfig:
290 | return (genericWrite(self.servoID, lssc.LSS_ConfigAngularRange, delta))
291 |
292 | def setMaxSpeed(self, speed, setType = lssc.LSS_SetSession):
293 | if setType == lssc.LSS_SetSession:
294 | return (genericWrite(self.servoID, lssc.LSS_ActionMaxSpeed, speed))
295 | elif setType == lssc.LSS_SetConfig:
296 | return (genericWrite(self.servoID, lssc.LSS_ConfigMaxSpeed, speed))
297 |
298 | def setMaxSpeedRPM(self, rpm, setType = lssc.LSS_SetSession):
299 | if setType == lssc.LSS_SetSession:
300 | return (genericWrite(self.servoID, lssc.LSS_ActionMaxSpeedRPM, rpm))
301 | elif setType == lssc.LSS_SetConfig:
302 | return (genericWrite(self.servoID, lssc.LSS_ConfigMaxSpeedRPM, rpm))
303 |
304 | def setColorLED(self, color, setType = lssc.LSS_SetSession):
305 | if setType == lssc.LSS_SetSession:
306 | return (genericWrite(self.servoID, lssc.LSS_ActionColorLED, color))
307 | elif setType == lssc.LSS_SetConfig:
308 | return (genericWrite(self.servoID, lssc.LSS_ConfigColorLED, color))
309 |
310 | def setGyre(self, gyre, setType = lssc.LSS_SetSession):
311 | if setType == lssc.LSS_SetSession:
312 | return (genericWrite(self.servoID, lssc.LSS_ActionGyreDirection, gyre))
313 | elif setType == lssc.LSS_SetConfig:
314 | return (genericWrite(self.servoID, lssc.LSS_ConfigGyreDirection, gyre))
315 |
316 | def setFirstPosition(self, pos):
317 | return (genericWrite(self.servoID, lssc.LSS_ConfigFirstPosition, pos))
318 |
319 | def clearFirstPosition(self):
320 | return (genericWrite(self.servoID, lssc.LSS_ConfigFirstPosition))
321 |
322 | def setMode(self, mode):
323 | return (genericWrite(self.servoID, lssc.LSS_ConfigMode, mode))
324 |
325 | #> Configs (advanced)
326 | def setAngularStiffness(self, value, setType = lssc.LSS_SetSession):
327 | if setType == lssc.LSS_SetSession:
328 | return (genericWrite(self.servoID, lssc.LSS_ActionAngularStiffness, value))
329 | elif setType == lssc.LSS_SetConfig:
330 | return (genericWrite(self.servoID, lssc.LSS_ConfigAngularStiffness, value))
331 |
332 | def setAngularHoldingStiffness(self, value, setType = lssc.LSS_SetSession):
333 | if setType == lssc.LSS_SetSession:
334 | return (genericWrite(self.servoID, lssc.LSS_ActionAngularHoldingStiffness, value))
335 | elif setType == lssc.LSS_SetConfig:
336 | return (genericWrite(self.servoID, lssc.LSS_ConfigAngularHoldingStiffness, value))
337 |
338 | def setAngularAcceleration(self, value, setType = lssc.LSS_SetSession):
339 | if setType == lssc.LSS_SetSession:
340 | return (genericWrite(self.servoID, lssc.LSS_ActionAngularAcceleration, value))
341 | elif setType == lssc.LSS_SetConfig:
342 | return (genericWrite(self.servoID, lssc.LSS_ConfigAngularAcceleration, value))
343 |
344 | def setAngularDeceleration(self, value, setType = lssc.LSS_SetSession):
345 | if setType == lssc.LSS_SetSession:
346 | return (genericWrite(self.servoID, lssc.LSS_ActionAngularDeceleration, value))
347 | elif setType == lssc.LSS_SetConfig:
348 | return (genericWrite(self.servoID, lssc.LSS_ConfigAngularDeceleration, value))
349 |
350 | def setMotionControlEnabled(self, value):
351 | return (genericWrite(self.servoID, lssc.LSS_ActionEnableMotionControl, value))
352 |
353 | def setBlinkingLED(self, state):
354 | return (genericWrite(self.servoID, lssc.LSS_ConfigBlinkingLED, state))
355 |
356 | def setFilterPositionCount(self, value):
357 | return (genericWrite(self.servoID, lssc.LSS_FilterPositionCount, value))
358 |
359 | ### EOF ######################################################################
--------------------------------------------------------------------------------
/lss_const.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Author: Sebastien Parent-Charette (support@robotshop.com)
3 | # Version: 1.0.0
4 | # Licence: LGPL-3.0 (GNU Lesser General Public License version 3)
5 | #
6 | # Desscription: A library that makes using the LSS simple.
7 | # Offers support for most Python platforms.
8 | # Uses the Python serial library (pySerial).
9 | ###############################################################################
10 |
11 | ### List of constants
12 |
13 | # Bus communication
14 | LSS_DefaultBaud = 115200
15 | LSS_MaxTotalCommandLength = (30 + 1) # ex: #999XXXX-2147483648\r Adding 1 for end string char (\0)
16 | # # ex: #999XX000000000000000000\r
17 | LSS_Timeout = 100 # in ms
18 | LSS_CommandStart = "#"
19 | LSS_CommandReplyStart = "*"
20 | LSS_CommandEnd = "\r"
21 | LSS_FirstPositionDisabled = "DIS"
22 |
23 | # LSS constants
24 | LSS_ID_Default = 0
25 | LSS_ID_Min = 0
26 | LSS_ID_Max = 250
27 | LSS_Mode255ID = 255
28 | LSS_BroadcastID = 254
29 |
30 | # Read/write status
31 | LSS_CommStatus_Idle = 0
32 | LSS_CommStatus_ReadSuccess = 1
33 | LSS_CommStatus_ReadTimeout = 2
34 | LSS_CommStatus_ReadWrongID = 3
35 | LSS_CommStatus_ReadWrongIdentifier = 4
36 | LSS_CommStatus_ReadWrongFormat = 5
37 | LSS_CommStatus_ReadNoBus = 6
38 | LSS_CommStatus_ReadUnknown = 7
39 | LSS_CommStatus_WriteSuccess = 8
40 | LSS_CommStatus_WriteNoBus = 9
41 | LSS_CommStatus_WriteUnknown = 10
42 |
43 | # LSS status
44 | LSS_StatusUnknown = 0
45 | LSS_StatusLimp = 1
46 | LSS_StatusFreeMoving = 2
47 | LSS_StatusAccelerating = 3
48 | LSS_StatusTravelling = 4
49 | LSS_StatusDecelerating = 5
50 | LSS_StatusHolding = 6
51 | LSS_StatusOutsideLimits = 7
52 | LSS_StatusStuck = 8 #cannot move at current speed setting
53 | LSS_StatusBlocked = 9 #same as stuck but reached maximum duty and still can't move
54 | LSS_StatusSafeMode = 10
55 | LSS_StatusLast = 11
56 |
57 | # LSS models
58 | LSS_ModelHighTorque = 0
59 | LSS_ModelStandard = 1
60 | LSS_ModelHighSpeed = 2
61 | LSS_ModelUnknown = 3
62 |
63 | LSS_ModelHT1 = "LSS-HT1"
64 | LSS_ModelST1 = "LSS-ST1"
65 | LSS_ModelHS1 = "LSS-HS1"
66 |
67 | # Parameter for query
68 | LSS_QuerySession = 0
69 | LSS_QueryConfig = 1
70 | LSS_QueryInstantaneousSpeed = 2
71 | LSS_QueryTargetTravelSpeed = 3
72 |
73 | # Parameter for setter
74 | LSS_SetSession = 0
75 | LSS_SetConfig = 1
76 |
77 | # Parameter for Serial/RC mode change
78 | LSS_ModeSerial = 0
79 | LSS_ModePositionRC = 1
80 | LSS_ModeWheelRC = 2
81 |
82 | # Parameter for gyre direction
83 | LSS_GyreClockwise = 1
84 | LSS_GyreCounterClockwise = -1
85 |
86 | # LED colors
87 | LSS_LED_Black = 0
88 | LSS_LED_Red = 1
89 | LSS_LED_Green = 2
90 | LSS_LED_Blue = 3
91 | LSS_LED_Yellow = 4
92 | LSS_LED_Cyan = 5
93 | LSS_LED_Magenta = 6
94 | LSS_LED_White = 7
95 |
96 | # Commands - actions
97 | LSS_ActionReset = "RESET"
98 | LSS_ActionConfirm = "CONFIRM"
99 | LSS_ActionLimp = "L"
100 | LSS_ActionHold = "H"
101 | LSS_ActionParameterTime = "T"
102 | LSS_ActionParameterSpeed = "S"
103 | LSS_ActionMove = "D"
104 | LSS_ActionMoveRelative = "MD"
105 | LSS_ActionWheel = "WD"
106 | LSS_ActionWheelRPM = "WR"
107 |
108 | # Commands - actions settings
109 | LSS_ActionOriginOffset = "O"
110 | LSS_ActionAngularRange = "AR"
111 | LSS_ActionMaxSpeed = "SD"
112 | LSS_ActionMaxSpeedRPM = "SR"
113 | LSS_ActionColorLED = "LED"
114 | LSS_ActionGyreDirection = "G"
115 |
116 | # Commands - modifiers
117 | LSS_ModifierCurrentHaltHold = "CH"
118 | LSS_ModifierCurrentLimp = "CL"
119 |
120 | # Commands - actions advanced settings
121 | LSS_ActionAngularStiffness = "AS"
122 | LSS_ActionAngularHoldingStiffness = "AH"
123 | LSS_ActionAngularAcceleration = "AA"
124 | LSS_ActionAngularDeceleration = "AD"
125 | LSS_ActionEnableMotionControl = "EM"
126 | LSS_FilterPositionCount = "FPC"
127 |
128 | # Commands - queries
129 | LSS_QueryStatus = "Q"
130 | LSS_QueryOriginOffset = "QO"
131 | LSS_QueryAngularRange = "QAR"
132 | LSS_QueryPositionPulse = "QP"
133 | LSS_QueryPosition = "QD"
134 | LSS_QuerySpeed = "QWD"
135 | LSS_QuerySpeedRPM = "QWR"
136 | LSS_QuerySpeedPulse = "QS"
137 | LSS_QueryMaxSpeed = "QSD"
138 | LSS_QueryMaxSpeedRPM = "QSR"
139 | LSS_QueryColorLED = "QLED"
140 | LSS_QueryGyre = "QG"
141 | LSS_QueryID = "QID"
142 | LSS_QueryBaud = "QB"
143 | LSS_QueryFirstPosition = "QFD"
144 | LSS_QueryModelString = "QMS"
145 | LSS_QuerySerialNumber = "QN"
146 | LSS_QueryFirmwareVersion = "QF"
147 | LSS_QueryVoltage = "QV"
148 | LSS_QueryTemperature = "QT"
149 | LSS_QueryCurrent = "QC"
150 |
151 | # Commands - queries advanced
152 | LSS_QueryAngularStiffness = "QAS"
153 | LSS_QueryAngularHoldingStiffness = "QAH"
154 | LSS_QueryAngularAcceleration = "QAA"
155 | LSS_QueryAngularDeceleration = "QAD"
156 | LSS_QueryEnableMotionControl = "QEM"
157 | LSS_QueryBlinkingLED = "QLB"
158 |
159 | # Commands - configurations
160 | LSS_ConfigID = "CID"
161 | LSS_ConfigBaud = "CB"
162 | LSS_ConfigOriginOffset = "CO"
163 | LSS_ConfigAngularRange = "CAR"
164 | LSS_ConfigMaxSpeed = "CSD"
165 | LSS_ConfigMaxSpeedRPM = "CSR"
166 | LSS_ConfigColorLED = "CLED"
167 | LSS_ConfigGyreDirection = "CG"
168 | LSS_ConfigFirstPosition = "CFD"
169 | LSS_ConfigMode = "CRC"
170 |
171 | # Commands - configurations advanced
172 | LSS_ConfigAngularStiffness = "CAS"
173 | LSS_ConfigAngularHoldingStiffness = "CAH"
174 | LSS_ConfigAngularAcceleration = "CAA"
175 | LSS_ConfigAngularDeceleration = "CAD"
176 | LSS_ConfigBlinkingLED = "CLB"
177 |
178 | ### EOF #######################################################################
179 |
--------------------------------------------------------------------------------
/params.txt:
--------------------------------------------------------------------------------
1 | {"baseradius": 0.0, "cbFrame": 0.0, "sqSize": 0.0, "cbHeight": 0.0, "pieceHeight": 0.0}
2 |
--------------------------------------------------------------------------------
/pieces_images/ChessPiecesArray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/ChessPiecesArray.png
--------------------------------------------------------------------------------
/pieces_images/bishopb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/bishopb.png
--------------------------------------------------------------------------------
/pieces_images/bishopw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/bishopw.png
--------------------------------------------------------------------------------
/pieces_images/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/blank.png
--------------------------------------------------------------------------------
/pieces_images/game.pgn:
--------------------------------------------------------------------------------
1 | [Event "Wch U12"]
2 | [Site "Duisburg"]
3 | [Date "1992.??.??"]
4 | [Round "1"]
5 | [White "Malakhov, Vladimir"]
6 | [Black "Ab Rahman, M."]
7 | [Result "1-0"]
8 | [WhiteElo ""]
9 | [BlackElo ""]
10 | [ECO "A05"]
11 |
12 | 1.Nf3 Nf6 2.b3 g6 3.Bb2 Bg7 4.g3 d6 5.Bg2 O-O 6.O-O c6 7.d3 e5 8.c4 Ne8 9.Nbd2 f5
13 | 10.Qc2 Na6 11.c5 Nxc5 12.Nxe5 Qe7 13.d4 Na6 14.Qc4+ Kh8 15.Nef3 Be6 16.Qc3 f4
14 | 17.gxf4 Rxf4 18.Qe3 Rf8 19.Ng5 Nec7 20.Nc4 Rae8 21.Nxe6 Qxe6 22.Qxe6 Rxe6
15 | 23.e3 d5 24.Ne5 g5 25.Ba3 Rff6 26.Bh3 Re8 27.Bd7 Rd8 28.Be7 Rxd7 29.Bxf6 1-0
16 |
17 |
18 | [Event "Wch U12"]
19 | [Site "Duisburg"]
20 | [Date "1992.??.??"]
21 | [Round "2"]
22 | [White "Malakhov, Vladimir"]
23 | [Black "Berescu, Alin"]
24 | [Result "1-0"]
25 | [WhiteElo ""]
26 | [BlackElo ""]
27 | [ECO "D05"]
28 |
29 | 1.d4 Nf6 2.Nd2 d5 3.Ngf3 e6 4.e3 c5 5.c3 Nbd7 6.Bd3 Bd6 7.O-O O-O 8.Re1 b6
30 | 9.e4 dxe4 10.Nxe4 Be7 11.Ne5 Bb7 12.Ng5 g6 13.Qe2 Nxe5 14.dxe5 Nh5 15.Ne4 Qd5
31 | 16.f4 Rfd8 17.Bc2 Qc6 18.Be3 Rd7 19.Rad1 Rad8 20.Rxd7 Rxd7 21.Nd2 Ng7 22.Be4 Qc8
32 | 23.g4 Qd8 24.Bxb7 Rxb7 25.Ne4 Rd7 26.c4 h5 27.h3 h4 28.Kh2 Ne8 29.f5 Qc7
33 | 30.Bf4 Rd4 31.Qf2 Rxc4 32.f6 Qb7 33.Ng5 Bf8 34.b3 Rc3 35.Qd2 Rf3 36.Nxf3 Qxf3
34 | 37.Qe3 Qd5 38.Qe4 Qd7 39.Qf3 Nc7 40.Rd1 Nd5 41.Bg5 Qc7 42.Re1 b5 43.Qd1 c4
35 | 44.Qc1 Bb4 45.Bd2 Bxd2 46.Qxd2 Nxf6 47.bxc4 bxc4 48.Qd6 Qa5 49.Rf1 Nd5 50.Qd7 Qd2+
36 | 51.Kh1 f5 52.exf6 1-0
37 |
--------------------------------------------------------------------------------
/pieces_images/kingb.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/kingb.ico
--------------------------------------------------------------------------------
/pieces_images/kingb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/kingb.png
--------------------------------------------------------------------------------
/pieces_images/kingw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/kingw.png
--------------------------------------------------------------------------------
/pieces_images/knightb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/knightb.png
--------------------------------------------------------------------------------
/pieces_images/knightw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/knightw.png
--------------------------------------------------------------------------------
/pieces_images/nbishopb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nbishopb.png
--------------------------------------------------------------------------------
/pieces_images/nbishopw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nbishopw.png
--------------------------------------------------------------------------------
/pieces_images/nkingb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nkingb.png
--------------------------------------------------------------------------------
/pieces_images/nkingw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nkingw.png
--------------------------------------------------------------------------------
/pieces_images/nknightb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nknightb.png
--------------------------------------------------------------------------------
/pieces_images/nknightw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nknightw.png
--------------------------------------------------------------------------------
/pieces_images/npawnb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/npawnb.png
--------------------------------------------------------------------------------
/pieces_images/npawnw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/npawnw.png
--------------------------------------------------------------------------------
/pieces_images/nqueenb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nqueenb.png
--------------------------------------------------------------------------------
/pieces_images/nqueenw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nqueenw.png
--------------------------------------------------------------------------------
/pieces_images/nrookb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nrookb.png
--------------------------------------------------------------------------------
/pieces_images/nrookw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/nrookw.png
--------------------------------------------------------------------------------
/pieces_images/pawnb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/pawnb.png
--------------------------------------------------------------------------------
/pieces_images/pawnw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/pawnw.png
--------------------------------------------------------------------------------
/pieces_images/queenb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/queenb.png
--------------------------------------------------------------------------------
/pieces_images/queenw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/queenw.png
--------------------------------------------------------------------------------
/pieces_images/rookb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/rookb.png
--------------------------------------------------------------------------------
/pieces_images/rookw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EDGE-tronics/Chess-Robot/0d38471bd8d453376441b8f6c57b371bccf1fdaa/pieces_images/rookw.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | altgraph==0.17
2 | numpy==1.22.0
3 | PyInstaller==5.13.1
4 | pyserial==3.4
5 | python-chess==0.30.1
6 | gTTS==2.3.1
7 | pygame==2.1.3
8 | pefile==2024.8.26
9 | FreeSimpleGUI==5.2.0.post1
10 | opencv-python
--------------------------------------------------------------------------------