Merge branch 'main' of https://github.com/Sharwin24/toha into main
This commit is contained in:
commit
d853b62d4a
5 changed files with 17 additions and 260 deletions
|
@ -43,6 +43,7 @@ The Arduino Nano handles I/O control for the 4 button panels which enable player
|
||||||
<img src="system_overview.png" alt="System Overview" style="border-radius: 15px;">
|
<img src="system_overview.png" alt="System Overview" style="border-radius: 15px;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
## Design Reviews
|
## Design Reviews
|
||||||
|
|
||||||
### Card Dealer (Turret)
|
### Card Dealer (Turret)
|
||||||
|
@ -57,4 +58,5 @@ The Arduino Nano handles I/O control for the 4 button panels which enable player
|
||||||
|
|
||||||
### Button Panel
|
### Button Panel
|
||||||
|
|
||||||
### Game Display
|
### Game Display
|
||||||
|
-->
|
|
@ -50,258 +50,4 @@ Resetting a chess board after a game is such a simple task for humans it is ofte
|
||||||
|
|
||||||
The blue squares outlining the main board are reserved for captured pieces (and the extra queen). Whenever a capture occurs, the robot places the captured piece into the corresponding square.
|
The blue squares outlining the main board are reserved for captured pieces (and the extra queen). Whenever a capture occurs, the robot places the captured piece into the corresponding square.
|
||||||
|
|
||||||
When solving for an optimal path using a state-space exploration with A*, the space rapidly expands and is infeasible to solve. Our approach was to use a greedy algorithm minimizing axes movement.
|
When solving for an optimal path using a state-space exploration with A*, the space rapidly expands and is infeasible to solve. Our approach was to use a [greedy algorithm minimizing axes movement](https://github.com/Connor205/Chess-Robot-NURobotics/blob/main/mk1/resetFinder.py).
|
||||||
|
|
||||||
```python
|
|
||||||
from math import sqrt
|
|
||||||
from CapturedPieceManagement import CapturedPieceManagement
|
|
||||||
import chess
|
|
||||||
import Arduino
|
|
||||||
|
|
||||||
whiteSetup = [
|
|
||||||
list("RNBQQBNR"),
|
|
||||||
list("PPPPPPPP"),
|
|
||||||
]
|
|
||||||
blackSetup = [
|
|
||||||
["r", "p"],
|
|
||||||
["n", "p"],
|
|
||||||
["b", "p"],
|
|
||||||
["q", "p"],
|
|
||||||
["q", "p"],
|
|
||||||
["b", "p"],
|
|
||||||
["n", "p"],
|
|
||||||
["r", "p"],
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def calculateMoveDistance(m):
|
|
||||||
if m[0] == "board":
|
|
||||||
return chess.square_distance(m[1], m[2])
|
|
||||||
if m[0] == "white":
|
|
||||||
return 2 - m[2] + chess.square_distance(chess.square(m[1], 7), m[3])
|
|
||||||
if m[0] == "black":
|
|
||||||
if m[1] == -1:
|
|
||||||
return 1 + chess.square_distance(chess.square(0, m[2]), m[3])
|
|
||||||
else:
|
|
||||||
return 1 + chess.square_distance(chess.square(7, m[2]), m[3])
|
|
||||||
|
|
||||||
|
|
||||||
def getSuccessors(fen, white, black):
|
|
||||||
successors = []
|
|
||||||
b: chess.Board = chess.Board(fen)
|
|
||||||
empty_squares = [x for x in chess.SQUARES if b.piece_at(x) is None]
|
|
||||||
for i in range(64):
|
|
||||||
p = b.piece_at(i)
|
|
||||||
if p is not None and init_board.piece_at(i) != p:
|
|
||||||
min_dist = 100
|
|
||||||
min_square = None
|
|
||||||
for j in empty_squares:
|
|
||||||
if init_board.piece_at(j) == p:
|
|
||||||
d = sqrt((chess.square_file(i) - chess.square_file(j))**2 +
|
|
||||||
(chess.square_rank(i) - chess.square_rank(j))**2)
|
|
||||||
if d <= min_dist:
|
|
||||||
min_dist = d
|
|
||||||
min_square = j
|
|
||||||
if min_square is not None:
|
|
||||||
new_board = b.copy()
|
|
||||||
new_board.set_piece_at(min_square, p)
|
|
||||||
new_board.remove_piece_at(i)
|
|
||||||
successors.append(((new_board.fen(), white, black),
|
|
||||||
("board", i, min_square), min_dist))
|
|
||||||
# White takens
|
|
||||||
for x in range(8):
|
|
||||||
for y in range(2):
|
|
||||||
min_dist = 100
|
|
||||||
min_square = None
|
|
||||||
if x == 3 and y == 0:
|
|
||||||
continue
|
|
||||||
if white[y, x] == 1:
|
|
||||||
p = whiteSetup[y][x]
|
|
||||||
for e in empty_squares:
|
|
||||||
if str(init_board.piece_at(e)) == p:
|
|
||||||
d = calculateMoveDistance(("white", x, y, e))
|
|
||||||
if d <= min_dist:
|
|
||||||
min_dist = d
|
|
||||||
min_square = e
|
|
||||||
if min_square is not None:
|
|
||||||
new_board = b.copy()
|
|
||||||
new_board.set_piece_at(min_square,
|
|
||||||
init_board.piece_at(min_square))
|
|
||||||
newWhite = white.copy()
|
|
||||||
newWhite[y, x] = 0
|
|
||||||
successors.append(((new_board.fen(), newWhite, black),
|
|
||||||
("white", x, y, min_square), min_dist))
|
|
||||||
# Black takens
|
|
||||||
for x in [-1, 1]:
|
|
||||||
for y in range(8):
|
|
||||||
min_dist = 100
|
|
||||||
min_square = None
|
|
||||||
if x == -1 and y == 3:
|
|
||||||
continue
|
|
||||||
if x == -1:
|
|
||||||
blackCoord = 0
|
|
||||||
else:
|
|
||||||
blackCoord = 1
|
|
||||||
if black[y, blackCoord] == 1:
|
|
||||||
p = blackSetup[y][blackCoord]
|
|
||||||
for e in empty_squares:
|
|
||||||
# print(e, init_board.piece_at(e))
|
|
||||||
if str(init_board.piece_at(e)) == p:
|
|
||||||
d = calculateMoveDistance(("black", x, y, e))
|
|
||||||
if d <= min_dist:
|
|
||||||
min_dist = d
|
|
||||||
min_square = e
|
|
||||||
if min_square is not None:
|
|
||||||
new_board = b.copy()
|
|
||||||
new_board.set_piece_at(min_square,
|
|
||||||
init_board.piece_at(min_square))
|
|
||||||
newBlack = black.copy()
|
|
||||||
newBlack[y, x] = 0
|
|
||||||
successors.append(((new_board.fen(), white, newBlack),
|
|
||||||
("black", x, y, min_square), min_dist))
|
|
||||||
|
|
||||||
return successors
|
|
||||||
|
|
||||||
|
|
||||||
startingFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
|
|
||||||
|
|
||||||
|
|
||||||
def findPathGreedy(fen, captured: CapturedPieceManagement):
|
|
||||||
currentSquare = 56
|
|
||||||
currentFen = fen
|
|
||||||
w = captured.white
|
|
||||||
b = captured.black
|
|
||||||
moves = []
|
|
||||||
while (currentFen.split(" ")[0] != startingFen):
|
|
||||||
print("Fen: " + currentFen)
|
|
||||||
print("White: " + str(w))
|
|
||||||
print("Black: " + str(b))
|
|
||||||
possibleMoves = getSuccessors(currentFen, w, b)
|
|
||||||
bestCost = 100
|
|
||||||
if len(possibleMoves) == 0:
|
|
||||||
return False
|
|
||||||
for m in possibleMoves:
|
|
||||||
if m[1][0] == "board":
|
|
||||||
dstToStart = chess.square_distance(m[1][1], currentSquare)
|
|
||||||
if m[1][0] == "white":
|
|
||||||
dstToStart = calculateMoveDistance(
|
|
||||||
("white", m[1][1], m[1][2], m[1][3]))
|
|
||||||
if m[1][0] == "black":
|
|
||||||
dstToStart = calculateMoveDistance(
|
|
||||||
("black", m[1][1], m[1][2], m[1][3]))
|
|
||||||
cost = m[2] + dstToStart
|
|
||||||
if cost < bestCost:
|
|
||||||
bestCost = cost
|
|
||||||
bestMove = m
|
|
||||||
|
|
||||||
currentSquare = bestMove[1][-1]
|
|
||||||
currentFen = bestMove[0][0]
|
|
||||||
w = bestMove[0][1]
|
|
||||||
b = bestMove[0][2]
|
|
||||||
moves.append((bestMove[1], bestMove[0][0]))
|
|
||||||
print(bestMove[1])
|
|
||||||
return moves
|
|
||||||
|
|
||||||
|
|
||||||
def convertSquareToCoordinates(square: str):
|
|
||||||
return [ord(square[0]) - ord('a'), 8 - int(square[1])]
|
|
||||||
|
|
||||||
|
|
||||||
def getAdjacentSquares(square: int):
|
|
||||||
return [square + 1, square - 1, square + 8, square - 8]
|
|
||||||
|
|
||||||
|
|
||||||
def isMoveLift(fen, move):
|
|
||||||
b = chess.Board(fen)
|
|
||||||
if move[0] == "board":
|
|
||||||
# Check to see if there are pieces between start and end square
|
|
||||||
|
|
||||||
start = move[1]
|
|
||||||
end = move[2]
|
|
||||||
minimum = min(start, end)
|
|
||||||
maximum = max(start, end)
|
|
||||||
offset = maximum - minimum
|
|
||||||
if offset % 8 == 0:
|
|
||||||
for i in range(minimum + 8, maximum, 8):
|
|
||||||
if b.piece_at(i) is not None:
|
|
||||||
return False
|
|
||||||
if chess.square_file(start) == chess.square_file(end):
|
|
||||||
for i in range(minimum + 1, maximum):
|
|
||||||
if b.piece_at(i) is not None:
|
|
||||||
return False
|
|
||||||
if offset % 9 == 0:
|
|
||||||
for i in range(minimum + 9, maximum, 9):
|
|
||||||
if b.piece_at(i) is not None:
|
|
||||||
return False
|
|
||||||
for s in getAdjacentSquares(i):
|
|
||||||
if b.piece_at(s) is not None:
|
|
||||||
return False
|
|
||||||
if offset % 7 == 0:
|
|
||||||
for i in range(minimum + 7, maximum, 7):
|
|
||||||
if b.piece_at(i) is not None:
|
|
||||||
return False
|
|
||||||
for s in getAdjacentSquares(i):
|
|
||||||
if b.piece_at(s) is not None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def reset_game_board(fen, captured: CapturedPieceManagement):
|
|
||||||
a = Arduino.Arduino("/dev/cu.usbmodem142101")
|
|
||||||
a.waitForReady()
|
|
||||||
path = findPathGreedy(fen, captured)
|
|
||||||
if path is False:
|
|
||||||
print("Unable to find path")
|
|
||||||
return False
|
|
||||||
for m, f in path:
|
|
||||||
if m[0] == "board":
|
|
||||||
startSquare = convertSquareToCoordinates(chess.square_name(m[1]))
|
|
||||||
endSquare = convertSquareToCoordinates(chess.square_name(m[2]))
|
|
||||||
a.sendCommand("move", [startSquare[0], startSquare[1]])
|
|
||||||
a.waitForReady()
|
|
||||||
if isMoveLift(f, m):
|
|
||||||
a.sendCommand("pickup", [])
|
|
||||||
else:
|
|
||||||
a.sendCommand("smallPickup", [])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("move", [endSquare[0], endSquare[1]])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("drop", [])
|
|
||||||
a.waitForReady()
|
|
||||||
if m[0] == "white":
|
|
||||||
a.sendCommand("moveWhiteTaken", [m[1], m[2]])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("pickupTaken", [])
|
|
||||||
endSquare = convertSquareToCoordinates(chess.square_name(m[3]))
|
|
||||||
a.sendCommand("move", [endSquare[0], endSquare[1]])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("drop", [])
|
|
||||||
a.waitForReady()
|
|
||||||
if m[0] == "black":
|
|
||||||
a.sendCommand("moveBlackTaken", [m[1], m[2]])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("pickupTaken", [])
|
|
||||||
endSquare = convertSquareToCoordinates(chess.square_name(m[3]))
|
|
||||||
a.sendCommand("move", [endSquare[0], endSquare[1]])
|
|
||||||
a.waitForReady()
|
|
||||||
a.sendCommand("drop", [])
|
|
||||||
a.waitForReady()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
cap = CapturedPieceManagement()
|
|
||||||
b = chess.Board()
|
|
||||||
# Testing
|
|
||||||
b.set_board_fen("r3k1nr/p4pNp/n7/1p1pP2P/6P1/3P1Q2/PRP1K3/q5bR")
|
|
||||||
cap.placePiece("black", "pawn")
|
|
||||||
cap.placePiece("black", "pawn")
|
|
||||||
cap.placePiece("black", "pawn")
|
|
||||||
cap.placePiece("black", "bishop")
|
|
||||||
cap.placePiece("white", "pawn")
|
|
||||||
cap.placePiece("white", "pawn")
|
|
||||||
cap.placePiece("white", "bishop")
|
|
||||||
cap.placePiece("white", "bishop")
|
|
||||||
cap.placePiece("white", "knight")
|
|
||||||
print(findPathGreedy(b.fen(), cap))
|
|
||||||
reset_game_board(b.fen(), cap)
|
|
||||||
```
|
|
||||||
|
|
BIN
content/posts/robot-arm-edu/assembly_instructions.png
Normal file
BIN
content/posts/robot-arm-edu/assembly_instructions.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 481 KiB |
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Educational Robot Arm"
|
title: "Educational Robot Arm"
|
||||||
date: 2024-01-05T09:00:00+00:00
|
date: 2024-01-05T09:00:00+00:00
|
||||||
description: Introduction to Sample Post
|
description: Educational Kit for Introductory Robotics
|
||||||
hero: images/kavar_background.jpg
|
hero: images/kavar_background.jpg
|
||||||
author:
|
author:
|
||||||
image: /images/sharwin_portrait.jpg
|
image: /images/sharwin_portrait.jpg
|
||||||
|
@ -10,6 +10,15 @@ menu:
|
||||||
name: Educational Robot Arm
|
name: Educational Robot Arm
|
||||||
identifier: robot-arm-edu
|
identifier: robot-arm-edu
|
||||||
weight: 4
|
weight: 4
|
||||||
tags: ["Basic", "Multi-lingual"]
|
tags: ["3D Printing", "Arduino", "C++", "MATLAB"]
|
||||||
# categories: ["Basic"]
|
# categories: ["Basic"]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
An educational kit designed to teach the fundamentals of kinematics and dynamics. The kit is intended to accompany the course ME3460: Robot Dynamics & Control at Northeastern University. The kit is a physical representation of the final project, which is currently done purely through MATLAB simulation.
|
||||||
|
|
||||||
|
## Kit Design
|
||||||
|
The entire kit is composed of 3D printed parts, and off-the-shelf hardware/electronics. Students can assemble the kit without any soldering and with minimal tools.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="assembly_instructions.png" alt="Assembly Instructions" style="border-radius: 15px;">
|
||||||
|
</div>
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
{{ range $customMenus }}
|
{{ range $customMenus }}
|
||||||
{{ if (not .hideFromNavbar) }}
|
{{ if (not .hideFromNavbar) }}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ .url }}">{{ .name }}</a>
|
<a class="nav-link" href="{{ .url }}" target="_blank">{{ .name }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue