Gr33rapport2021 02
Gr33rapport2021 02
PiChess
Voice Controlled Robotic Chess Player
AXEL SERNELIN
TRITA-ITM-EX 2021:51
Abstract
The purpose of this bachelor’s thesis was to create a
robot that could play chess through voice recognition and
robotics. The two areas to be investigated were the robot’s
precision and speed. The reason for building a robot arm
of the SCARA type was that it can easily pick up and
place pieces with reach over the entire chessboard. The
robot arm is controlled from a Raspberry Pi 4 and is moved
by two Dynamixel AX-12a servomotors. To pick up chess
pieces, a continuous 360-degree servomotor was used to lift
an electromagnet which was mounted on a gear rack. A
USB microphone was used to collect what move the player
indicated. The Stockfish chess engine was used to generate
moves for the robot.
The parts of the robot that had the greatest impact
on precision were the stability of the aluminum profiles,
the gear ratio between the gears that transmit torque to
the arm, the gear mesh contact ratio and the size of the
electromagnet. The time it took to complete a move could
be reduced by increasing the speed of the motors when
a chess piece was not attached to the electromagnet, and
using a larger gear in the RC servo that raises and lowers
the electromagnet.
Keywords— Mechatronics, Raspberry Pi, Chess, SCARA,
Dynamixel
Referat
Röststyrd Robotisk Schackspelare
Syftet med det här kandidatexamensarbetet var att ska-
pa en robot som genom röstigenkänning och robotik kun-
de spela schack. De två områden som skulle undersökas
var robotens precision och hastighet. Anledningen till att
bygga en robotarm av SCARA typ var att den enkelt kan
plocka upp och ställa ner pjäser med räckvidd över hela
schackbrädet. Robotarmen styrs från en Raspberry Pi 4
och drivs av två stycken Dynamixel AX-12a servomotorer.
För att plocka upp schackpjäser användes en kontinuerlig
360-graders servomotor som lyfte en elektromagnet monte-
rad på en kuggstång. En USB mikrofon användes för att
samla in vad spelaren angav för drag. Schackmotorn Stock-
fish användes för att generera drag åt roboten.
De delar på roboten som hade störst inverkan på pre-
cision var stabiliteten i aluminiumprofilerna, utväxlingen
mellan kugghjulen som överför moment till armen, anligg-
ningsytan vid kuggingreppen och storlek av elektromagnet.
Tiden det tog att genomföra ett drag gick att minska genom
att öka hastigheten på motorerna då en schackpjäs inte satt
fast på elektromagneten, samt använda ett större kugghjul
hos RC servot som höjer och sänker elektromagneten.
Nyckelord— Mekatronik, Raspberry Pi, Schack, SCA-
RA, Dynamixel
Acknowledgements
We would like to thank our supervisor Nihad Subasic for guiding and helping us
throughout this project. We would also like to thank Staffan Qvarnström for helping
us acquiring parts, especially the FTDI cable. A special thanks to Richard Sernelin
for letting us use his personal workshop and helping us creating customized parts
necessary for the project. Without any of them, this project would not be possible.
1 Introduction 1
1.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2 Theory 3
2.1 SCARA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2.1 Raspberry Pi 4 . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2.2 ArbotiX-M Robocontroller . . . . . . . . . . . . . . . . . . . 3
2.2.3 Dynamixel AX-12A Servomotor . . . . . . . . . . . . . . . . . 5
2.2.4 Electromagnet . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2.5 FTDI cable . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2.6 RC Servo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3.1 Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3.2 Stockfish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3.3 PyPose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3 Demonstrator 9
3.1 Construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.1 Robotic Arm . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3.1 Main program . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3.2 Chess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3.3 Speech . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3.4 Movement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3.5 Gripper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4 Results 15
Bibliography 19
Appendices 21
B Python Code 23
C Acumen 35
List of Figures
RC Radio Control
Introduction
This project considers the construction of a robotic arm that can be remotely con-
trolled through voice activation. The goal is for the robotic arm to be able to play a
game of chess, using the arm to execute moves by physically picking up and putting
down chess pieces.
Today, society is constantly figuring out new ways to help and support people
living with disabilities. New technology is being developed to tackle these problems,
making life easier for disabled people. One of these technologies is robotics.
Robotics can be used to support those living with physical disabilities and more
so, voice-controlled robotics can cover an even wider range of people.
1.1 Background
Inspiration for this project was initially taken from the 18th century chess-playing
machine known as The Turk, Mechanical Turk or Automaton Chess Player.
This machine was believed to be completely automated and defeated challengers all
over the world. However, the machine was actually a hoax since a human player
was hidden underneath the desk, executing the machines moves manually [1].
The following projects has been used as sources of inspiration for the construc-
tion of the arm.
• Checkmate [2] which uses frame-like construction to move along the x- and
y-axis, magnetic sensors to detect the chess pieces and an electromagnet to
grab them.
1.2 Purpose
The following questions are to be investigated and answered in this thesis:
1
CHAPTER 1. INTRODUCTION
• How can the robot arm complete a move faster without losing precision?
1.3 Scope
Due to limited resources and time, some limitations have been formulated:
• The voice recognition will use an already existing software package for the
Raspberry Pi.
• A virtual chess board package will be used for handling chess actions.
• The robot will use the open-source chess engine Stockfish [4] to calculate
moves.
Since the purpose of this thesis was to research how well the robot arm can move
chess pieces it was not of significant importance to do the above listed, from scratch.
1.4 Method
In order to answer the research questions, a robotic prototype was constructed,
installed and programmed.
First, theoretical research was made to figure out how a robotic arm could be
constructed and which components and parts were necessary. An early simulation
of the robots lifting movement was made using Acumen [5] to understand how this
was supposed to be accomplished practically (a picture of the simulation, Figure
C.1, and the code necessary to produce the simulation can be found in Appendix C
on page 35).
Second, the robotic arm was built, this was done using a trial and error -
approach. Different parts were purchased and some custom made to test either
parts limitation until their purpose was considered satisfied.
Third, programming of the robot, this was also done using a trial and error
approach but some more theoretical research had to be made to finalize the code.
Fourth, testing, analyzing, fine tuning and result recording. To answer the
research questions, some calibration first had to be made and later the robot was
fine tuned whilst documenting the results of the robot. This was then used to
answer the research questions.
2
Chapter 2
Theory
2.1 SCARA
SCARA, an acronym for Selective Compliance Articulated Robot Arm, is a solution
for making the robot arm compliant in the X-Y axis. This means that the arm can
move freely across an area decided by the length of two jointed arms.
Movement in the Z axis can be obtained either by altering the base of the arm
or by using a rod, which can be altered, at the end of the second arm [6].
2.2 Hardware
In this section the hardware of the robot are explained.
2.2.1 Raspberry Pi 4
Raspberry Pi is a series of small single-boarded computers, the latest to the addition
being the Raspberry Pi 4. The Raspberry Pi 4 runs on Raspberry Pi OS (previously
known as Raspbian), a Linux based operating system, which gives it the capabilities
to run more advanced tasks than that of a microcontroller. It can run programs
developed for Linux operating system and programmed using advanced languages
such as Python, Java, C++, etc [7].
A full overview of the Raspberry Pi 4 can be seen in Figure 2.2 on page 4.
3
CHAPTER 2. THEORY
electrical current and handles data packages sent and received from the servo motors
[8].
An overview of the ArbotiX-M Robocontroller can be seen in Figure 2.1.
4
2.2. HARDWARE
2.2.4 Electromagnet
An electromagnet is a type of magnet in which the magnetic field is produced by
an electric current [13]. Electromagnets can be switched on and off which allows for
applications such as attracting a magnetic material on demand.
2.2.6 RC Servo
RC Servos are small actuators commonly used in remote controls or robotics because
of its ability to rotate and maintain a certain position or angle while still being small
in size.
A common RC servo consist of a motor, a gearbox, a position sensor, an error
amplifier, a motor driver and a circuit. A block diagram for a common RC servo
can be seen in Figure 2.4.
The RC servo is connected through three wires; power (+5V), ground and a
signal wire [15].
5
CHAPTER 2. THEORY
Figure 2.5: Servo motor PWN duty cycle and frequency requirement [16].
6
2.3. SOFTWARE
2.3 Software
In this section the software used will be explained.
2.3.1 Python
Python is an interpreted, general-purpose and high-level programming language. It
is used for programming and software development such as web development, back
end development, writing system scripts and data sciences [17].
2.3.2 Stockfish
Stockfish is a free and open-source chess engine, which evaluates chess positions by
developing a tree of all legal moves for the user chosen depth. The engine then
looks at all of these board positions and decides the best move considering things
such as control of the center, vulnerability of the king to check, vulnerability of the
opponent’s queen, and a multitude of other parameters. Stockfish can be set to play
with varying level of skill, which is done by limiting the computing time and search
depth. If Stockfish is given enough time it will be unbeatable by any human chess
player [4].
2.3.3 PyPose
Since the ArbotiX-M robocontroller is based on an Arduino, there is no way of
controlling it with Python from a computer by default. The PyPose project is a
program which provides the user a way of sending data packages to the ArbotiX-M
from Python. By uploading the PyPose program to the microcontroller, it will act as
a pass through so the Raspberry Pi can communicate directly with the servo motors.
The PyPose project provides the Raspberry Pi with a driver which effectively allows
you to get and set registers on any connected Dynamixel device via a FTDI-USB
cable [18].
7
Chapter 3
Demonstrator
This chapter considers the construction of the robotic prototype used in this project.
3.1 Construction
The construction of the prototype consists of a robotic arm that stands on a wooden
box which is attached to a plywood table. The chessboard is painted on the plywood
table and is at a set distance from the robotic arm.
An overhead picture of the robot arm can be seen in Figure 3.1 on page 10.
3.2 Hardware
The Raspberry Pi [7] acts as the brain of the robot. All necessary coding for
controlling the servomotors, as well as running the chess engine and voice recognition
programs, is done from here.
The microcontroller, ArbotiX-M [8], acts as a middleman between the Raspberry
Pi and the Dynamixel AX-12A [11] servomotors. The microcontroller is required
9
CHAPTER 3. DEMONSTRATOR
to control the servomotors as they run on arduino based programming and for its
higher power demand (12V).
To position the arm at the desired x- and y-coordinate the Dynamixel Ax-12A
servomotors are used. Because of their ability to receive and store position and
speed data, they are very effective for calibrating the coordinates of each square on
the chessboard, as well as regulating speed for achieving high precision.
For picking up and dropping off chess pieces, an electromagnet is attached to a
gear rack which is intermeshed with the cogwheel being controlled by the RC servo.
The RC servo used is actually a continuous rotation servo. What this means is
that instead of maintaining a position when applied with a pulse signal, the servo
will continuously rotate clockwise or counterclockwise depending on the pulse signal.
Because of this, measurements are made to adjust how long the servo should rotate
for the electromagnet to reach a certain chess piece.
When a chess piece is to be lifted, the servo will rotate until a custom made limit
switch receives signal. The limit switch is made using a screw that is connected to
power and located on the gear rack, and a copperplate connected to ground and
located on the bottom rear end of the upper aluminum profile. When the screw
touches the copper plate, the circuit is complete and the switch receives signal which
is programmed to stop the servo from rotating further.
The final component is a USB microphone connected directly to the Raspberry
Pi. This is to be used by the human player to decide his/her move.
A complete list and wiring diagram, Figure 3.4, of the included electronic com-
ponents can be found on page 12.
10
3.2. HARDWARE
(a) Lower arm axle and bearing housings. (b) Overhead picture of lower arm gears.
(a) Upper arm axle and bearing housings. (b) Overhead picture of upper arm gears.
11
CHAPTER 3. DEMONSTRATOR
Below follows a list of all hardware components used, thse are connected as seen
in Figure 3.4.
2. 5V Power Supply
4. USB Microphone
5. Limit Switch
6. RC Servo
7. Electromagnet
8. ArbotiX-M Robocontroller
9. FTDI Cable
11. Raspberry Pi 4
Figure 3.4: A wiring diagram of all the hardware components, made using Fritzing
[19].
12
3.3. SOFTWARE
3.3 Software
All software is written in Python on the Raspberry Pi [7].
3.3.2 Chess
To be able to play chess versus a computer we imported Stockfish [4]. Given any
chess position Stockfish will return the best possible move. This is used to determine
the robots move which is then sent to the movement class. To be able to set up a
virtual chess board a chess package was imported. This allowed for an easy way to
handle chess related actions such as a check for legal moves and look for a check
mate.
3.3.3 Speech
Using the USB microphone, the Raspberry Pi [7] listens to what the human player
says and then searches the text string for a chess piece and a chess board coordinate.
This is then later translated into a legal chess move, such as ”g1f3” using the
imported chess package. If no legal move is detected, the program listens for a new
input by the player.
3.3.4 Movement
Given a chess move, this part will determine if its a capture, normal move or a
castling move and then move the arm to the correct position. All the motor values
for each position are stored in a dictionary which makes them easy to retrieve.
3.3.5 Gripper
To pick up a piece, the program lowers the gripper to the corresponding piece height
and then turns the electromagnet on. To drop off a piece the electromagnet turns
off. The electromagnet is switched on using a control signal from a Raspberry Pi
GPIO pin which turns on a solid state relay. This allows for a higher voltage than
what the Raspberry Pi can output.
13
Chapter 4
Results
The parts of the robot that had the most impact on precision were the following:
• Ratio between the gears used for transmitting rotational motion from servo
to arm.
The speed of the robot arm was increased whilst not carrying any chess piece to
improve the overall speed. For white to castle on the king side it took 33 seconds
to fully complete the move compared to 35 seconds before. When a larger gear was
installed on the RC servo it increased the pickup speed and thus decreasing the time
required by 2 seconds making it a final 31 seconds to castle the king and rook.
15
Chapter 5
This chapter discusses the results and research questions, concludes the project and
suggests future development.
5.1 Discussion
The aluminum profiles used in this project had one flaw which quite severely reduced
the precision, it had one face open. Because two bearing housings had to be attached
on a face surface of the profile, it was necessary to turn the profile with the open
side facing sideways. This made the arms, especially the lower one, twist and bend
when more weight was added across the robot arm. This resulted in swaying and
the electromagnet not having a perfect vertical facing.
The cogwheel attached to the top of each axle was too big when compared to
the gear on the servos. The high gear ratio led to less precise movement as a small
rotation from the servo leads to a rather big motion in the arm.
The gear mesh contact ratio, meaning how much of the cogs are in touch with
each other, had a great impact on precision. Low ratio would lead to sway and a
much less smooth movement in the arm. This was resolved using slide-able engine
mounts for the Dynamixel AX-12A servos so the gears could first be pushed in place
before tightening the screws holding the mount.
Even though the coordinates of each chessboard square was finely calibrated, it
was discovered that the pin on the chess pieces would attach to the electromagnet
a little bit different every time. This led to the placement of chess pieces would get
worse for every additional move. This was, however, quite easily resolved using a
plastic cone at the tip of the electromagnet. The cone would help guide the chess
piece toward the center of the magnet, resulting in consistent pick up and drop off.
To increase the speed, or rather decrease the time taken, of a move at no cost
in precision, two applications proved to be especially effective.
First, increasing the Dynamixel AX-12A rotation speed by 100% when it was not
currently moving a chess piece. This is important since if the speed was increased
too high when a chess piece was being lifted, the electromagnet would loose its grip
17
CHAPTER 5. DISCUSSION & CONCLUSION
and the piece would drop. If the speed was increased by more than 100%, the torque
would be too high, leading to the gears slipping, displacing the arm and lowering
precision significantly.
Second, changing the cogwheel of the RC servo to a larger one. Since the RC
servo is simply used to raising or lowering the electromagnet, it did not matter as
much to have precise movement. A larger cogwheel leads to a higher linear speed
in the gear rack and thus decreasing its travel time.
5.2 Conclusion
The research questions were answered and the project can be considered successful.
The robot was able to recognize a move and physically execute it, however, there
are room for improvement.
The points brought up in the discussion would lead to a much faster and precise
movement, if resolved. Also, it was found that increasing the surface area of the
pins located at the top of each chess piece reduced sway of the piece and risk of
dropping due to heavy load.
18
Bibliography
[12] Trossen robotics, Dynamixel ax-12a robot actuator, Available at: https:
//www.trossenrobotics.com/dynamixel- ax- 12- robot- actuator.aspx
[Accessed 04/20/2021].
[13] R. Nave. Hyperphysics, Electromagnet, Available at: http://hyperphysics. phy-
astr.gsu.edu/hbase/magnetic/elemag.html [Accessed 02/13/2021].
[14] Ftdi chip, Ttl-232r-5v, Available at: https://ftdichip.com/products/
ttl-232r-5v/ [Accessed 02/13/2021].
[15] kh86. (2011). Cytron technologies, How rc servo works?, Available at: https:
/ / tutorial . cytron . io / 2011 / 09 / 19 / how - rc - servo - works/ [Accessed
03/15/2021].
[16] (2020). Engineers garage, Controlling servo motor with stm32f103 microcon-
troller using stm32cubemx code configurator by stmicroelectronics and keil
uvision 5 ide for cortex m1 series microcontrollers, Available at: https://
www.engineersgarage.com/electronic- projects/interfacing- servo-
motor-with-stm32/ [Accessed 03/15/2021].
[17] Python, What is python? executive summary, Available at: https://www.
python.org/doc/essays/blurb/ [Accessed 02/10/2021].
[18] Arbotix robocontroller, Pypose introduction, Available at: http://vanadiumlabs.
github.io/pypose/ [Accessed 02/20/2021].
[19] Fritzing, Electronics made easy, Available at: https://fritzing.org/
[Accessed 04/25/2021].
[20] Creatly, Your visual workspace, Available at: https : / / creately . com
[Accessed 04/25/2021].
20
Appendix A
21
APPENDIX A. PICHESS STATE DIAGRAM
22
Appendix B
Python Code
23
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 ################# Main File #################
12
13 from stockfish_engine import StockfishEngine
14 from speech_engine import SpeechEngine
15 from movement_engine import MovementEngine
16 from arm_movement import ArmMovement
17 import chess
18
19
20 board = chess.Board()
21 stockfish = StockfishEngine()
22 mic = SpeechEngine()
23 movement = MovementEngine()
24 arm = ArmMovement()
25
26 f = open("last_position.txt", "w") # saves the last position in case of a crash
27 arm._is_moving()# needs to call this function for the arms to be able to set speed
28 move = None
29 run = True
30 while run:
31 while move is None: # looks for a legal move until found
32 [piece, square] = mic.get_square_and_piece() # gets the piece and square from
microphone
33 if piece and square is not None: # if both were found it tests if there is a
legal move with those two conditions
34 move = stockfish.get_player_move(board, piece, chess.WHITE, square)
35 movement.execute_move(move, board) # executes the move with robot arm when a
legal one is found
36 board.push(move) # updates the virtual board
37 f.write(board.fen()) # writes last position to file
38 # print(board) if you wanna se the board in the console
39 move = stockfish.get_best_engine_move(board) # gets stockfish response move
40 movement.execute_move(move, board) # executes the move with robot arm
41 board.push(move) # updates virtual board
42 f.write(board.fen()) # writes last position to file
43 # print(board)
44 move = None # resets move to None
45 if board.is_game_over() # if game is over, the program shuts off
46 run = False
47 f.close()
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 from driver import Driver
12 from ax12 import *
13
14 servo_1 = 1 # id for dynamixel servos
15 servo_2 = 2
16 servos = [servo_2, servo_1]
17 default_speed = [50, 50]
18
19 def _register_bytes_to_value(register_bytes): # takes the bytes given from dynamixel
and returns it as a value
20 return register_bytes[0] + (register_bytes[1]<<8)
21
22 class ArmMovement: # handles movement of the dynamixel servos which controls the arm
23 def __init__(self, port="/dev/ttyUSB0"):
24 self.coordinates_dict = {"a8" : [790, 570], "b8" : [735, 635],
25 "a7" : [760, 530], "b7" : [705, 595],
26 "a6" : [745, 480], "b6" : [685, 545],
27 "a5" : [740, 420], "b5" : [665, 485],
28 "a4" : [735, 355], "b4" : [665, 425],
29 "a3" : [750, 270], "b3" : [680, 340],
30 "a2" : [765, 175], "b2" : [700, 250],
31 "a1" : [800, 55], "b1" : [715, 155],
32 "c8" : [680, 705], "d8" : [610, 765],
33 "c7" : [645, 660], "d7" : [570, 720],
34 "c6" : [620, 605], "d6" : [550, 665],
35 "c5" : [610, 550], "d5" : [540, 600],
36 "c4" : [610, 480], "d4" : [540, 535],
37 "c3" : [620, 400], "d3" : [550, 450],
38 "c2" : [635, 315], "d2" : [570, 370],
39 "c1" : [665, 210], "d1" : [600, 270],
40 "e8" : [535, 825], "f8" : [440, 880],
41 "e7" : [495, 775], "f7" : [410, 815],
42 "e6" : [475, 715], "f6" : [395, 755],
43 "e5" : [470, 650], "f5" : [400, 685],
44 "e4" : [475, 575], "f4" : [405, 610],
45 "e3" : [485, 500], "f3" : [425, 530],
46 "e2" : [505, 415], "f2" : [450, 445],
47 "e1" : [540, 315], "f1" : [480, 345],
48 "g8" : [330, 920], "h8" : [200, 945],
49 "g7" : [310, 850], "h7" : [205, 875],
50 "g6" : [305, 790], "h6" : [215, 805],
51 "g5" : [315, 715], "h5" : [240, 730],
52 "g4" : [335, 640], "h4" : [270, 655],
53 "g3" : [360, 555], "h3" : [300, 575],
54 "g2" : [390, 470], "h2" : [330, 490],
55 "g1" : [425, 370], "h1" : [375, 390]
56 } # stores all the servo motor values for each
individual chess board square
57
58 self.driver = Driver(port=port) # opens up the driver on the given usb port
via the FTDI cable. Uses serial communication
59
60 def get_coordinate(self, square): # returns the servo motor values of a given
square
61 return(self.coordinates_dict.get(square, None)) # returns None if invalid
square
62
63 def move_to_square(self, square, speed): # moves the arm to a given square
64 self.move(self.get_coordinate(square), speed)
65
66 def home_position(self):
67 speed = [100, 100] # moves the arm to the home position
68 self.move([80, 680], speed)
69
70 def drop_position(self): # moves the arm to the drop piece position
71 self.move([900, 150], default_speed)
72
73 def startup_calibrate(self): # turns the motors to its end position for
calibration use
74 self.move([0, 1023], default_speed)
75
76 def current_position(self): # returns both servos positions
77 return self._values_for_register(P_PRESENT_POSITION_L) # register id from
ax12
78
79 def _values_for_register(self, register): # returns two values given what
register id you request
80 return [_register_bytes_to_value(self.driver.getReg(index, register, 2)) for
index in servos]
81
82 def _is_moving(self): # returns True if any of the servos is moving, else False
83 return any([self.driver.getReg(index, P_MOVING, 1) == 1 for index in servos])
84
85 def set_speed(self, speed): # sets the moving speed of both servos
86 for i in servos:
87 self.driver.setReg(i, P_GOAL_SPEED_L, [speed[i%2]%256, speed[i%2]>>8])
88
89 def move(self, goal_position, speed): # moves the servos to a given position with
a default speed
90 self.set_speed(speed)
91 for i in servos:
92 self.driver.setReg(i, P_GOAL_POSITION_L, [goal_position[i%2]%256,
goal_position[i%2]>>8])
93
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 import RPi.GPIO as GPIO
12 from time import sleep
13 import chess
14
15 electromagnet_pin = 21 # GPIO pin id's
16 servo_pin = 17
17 sensor_pin = 4
18
19 pieces = {
20 'p': 2.6,
21 'r': 2.7,
22 'n': 2.4,
23 'b': 2.2,
24 'k': 1.9,
25 'q': 2
26 } # corresponds to the height of the piece (seconds)
27
28 class Gripper(object): # handles movement of the gripper
29 def __init__(self):
30 GPIO.setmode(GPIO.BCM) # uses Broadcom SOC channel pin numbering, use
GPIO.BOARD for "normal numbering"
31 GPIO.setwarnings(False) # disables consol warning messages
32 GPIO.setup(servo_pin, GPIO.OUT) # sets pins to outputs
33 GPIO.setup(electromagnet_pin, GPIO.OUT)
34 GPIO.setup(sensor_pin, GPIO.IN) # sets sensor pin as input
35
36 def move_down(self, t): # moves the gripper down for t seconds
37 pwm = GPIO.PWM(servo_pin, 50) # 50 Hz
38 pwm.start(0) # starts pwm signal
39 pwm.ChangeDutyCycle(1.5) # down motion
40 sleep(t) # waits for t seconds
41 pwm.ChangeDutyCycle(0) # stops servo
42 pwm.stop() # stops pwm signal
43
44 def move_up(self): # moves the gripper up until the sensor at pin 4 is HIGH
45 pwm = GPIO.PWM(servo_pin, 50) # 50 Hz
46 pwm.start(0) # starts pwm signal
47 pwm.ChangeDutyCycle(12.5) # up motion
48 while GPIO.input(sensor_pin) == GPIO.LOW: # waits for signal
49 # print("Waiting for sensor...")
50 pass
51 pwm.ChangeDutyCycle(0) # stops servo
52 pwm.stop() # stops pwm signal
53
54 def electromagnet(self, on): # turns on/off magnet
55 output = GPIO.HIGH if on else GPIO.LOW # if true then high otherwise low.
Controls the solid state relay which is wired up to the electromagnet
56 GPIO.output(electromagnet_pin, output)
57
58 def pickup(self, piece): # picks up a piece
59 t = pieces[piece] # gets the height of the piece to pickup (seconds)
60 self.move_down(t) # moves down
61 sleep(0.2)
62 self.electromagnet(True) # turns on the electromagnet
63 sleep(0.2)
64 self.move_up() # moves all the way up
65
66 def dropoff(self, piece): # drops off a piece
67 t = pieces[piece] # gets the height of the piece to pickup (seconds)
68 self.move_down(t) # moves down
69 sleep(0.2)
70 self.electromagnet(False) # turns off the electromagnet
71 sleep(0.2)
72 self.move_up() # moves all the way up
73
74 def discard_piece(self): # discards a captured piece
75 self.move_down(0.5) # moves down for 0.5s
76 sleep(0.2)
77 self.electromagnet(False) # turns off electromagnet
78 self.move_up() # moves all the way up
79
80 def go_to_idle(self): # puts the gripper at a distance closer to the pieces to
save time
81 move_down(1) # moves down for x seconds
82
83 def cleanup(self): # clears the gpio pins
84 GPIO.cleanup()
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 from arm_movement import ArmMovement
12 from gripper import Gripper
13 import chess
14
15 default_speed = [40, 40] # slow speed
16 fast_speed = [80, 80] # fast speed
17
18 class MovementEngine: # handles logic to decide how the arm should move given a move
19 def __init__(self):
20 self.gripper = Gripper()
21 self.arm = ArmMovement()
22
23 def convert_to_square(self, square): # converts a square string to a chess.SQUARE
integer
24 file = ord(square[0]) - 97 # converts the file string to the corresponing
file id
25 rank = ord(square[1]) - 49 # converts the rank string to the corresponing
rank id
26 return chess.square(file,rank) # returns the chess square id given the file
and rank
27
28 def piece_at(self, square, board): # returns the current piece at a given square
in a given board position
29 piece = board.piece_at(self.convert_to_square(square)) # gets the chess.PIECE
on given square
30 if piece is not None:
31 return str(piece).lower() # returns as a lowercase string if not None
32 else:
33 return None # returns None if piece is None
34
35 def capture(self, move, board): # handles the movement if the chess move is a
capture
36 goal_square = move[-2:] # gets the square where the captured piece is
37 piece = self.piece_at(goal_square, board) # gets the piece which is captured
38 self.arm.move_to_square(goal_square, fast_speed) # moves to the captured
piece
39 while self.arm._is_moving(): # waits until its at its destination
40 # print("Moving to: " + goal_square)
41 pass
42 self.gripper.pickup(piece) # picks up the captured piece
43 self.arm.drop_position() # moves the arm to the discard piece position
44 while self.arm._is_moving(): # waits until its at its destination
45 # print("Moving to drop position...")
46 pass
47 self.gripper.discard_piece() # gripper drops piece
48 self.make_move(move, board) # after piece is discarded it makes a normal
move.
49 pass
50
51 def make_move(self, move, board): # handles the movement if the chess move is a
normal move
52 start_square = move[:2] # gets the square where the piece is
53 goal_square = move[-2:] # gets the square where the piece is going to
54 piece = self.piece_at(start_square, board) # gets the piece which is to be
moved
55 self.arm.move_to_square(start_square, fast_speed) # moves the arm to the
piece
56 while self.arm._is_moving(): # waits until its at its destination
57 # print("Moving to: " + start_square)
58 pass
59 self.gripper.pickup(piece) # gripper picks up the piece
60 self.arm.move_to_square(goal_square, default_speed) # arm moves to the square
where it is to be placed
61 while self.arm._is_moving(): # waits until its at its destination
62 # print("Moving to: " + goal_square)
63 pass
64 self.gripper.dropoff(piece) # gripper drops off the piece
65 self.arm.home_position() # moves back to home position
66
67 def castle(self, move, board):
68 move_string = str(move) # converts move to a string
69 if board.is_kingside_castling(move):
70 if board.turn: # if its white and kingside castle
71 self.make_move(move_string, board) # makes the king move
72 rook_move = "h1f1"
73 self.make_move(rook_move, board) # makes the rook move
74 else: # if its black and kingside castle
75 self.make_move(move_string, board) # makes the king move
76 rook_move = "h8f8"
77 self.make_move(rook_move, board) # makes the rook move
78
79 elif board.is_queenside_castling(move):
80 if board.turn: # if its white and queenside castle
81 self.make_move(move_string, board) # makes the king move
82 rook_move = "a1d1"
83 self.make_move(rook_move, board) # makes the rook move
84 else: # if its black and queenside castle
85 self.make_move(move_string, board) # makes the king move
86 rook_move = "a8d8"
87 self.make_move(rook_move, board) # makes the roob move
88
89 def is_capture(self, move, board): # checks if the chess move is a capture or not
90 goal_square = move[-2:] # gets the destination square of the move
91 return True if self.piece_at(goal_square, board) is not None else False #if
the square where the piece is to be placed is not empty, it is a capture
92
93 def execute_move(self, move, board): # decides what type of move it is and then
executes it
94 move_string = str(move) # converts move to a string
95 if board.is_castling(move): # if castle
96 self.castle(move, board)
97 elif self.is_capture(move_string, board): # if capture
98 self.capture(move_string, board)
99 else:
100 self.make_move(move_string, board) #else normal move
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 from chess import uci
12
13 squares = ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1',
14 'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
15 'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
16 'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
17 'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
18 'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
19 'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
20 'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8'] # list which stores all
squares
21
22 class StockfishEngine(object): #handles chess moves and positions
23 def __init__(self):
24 self._engine = uci.popen_engine('stockfish') #opens up an instance of the
stockfish engine
25 self._engine.uci()
26
27 def get_best_engine_move(self, board): # gets stockfish engine's best move given
the current position
28 self._engine.position(board) # feeds the board to stockfish
29 result = self._engine.go(movetime=1000) # lets stockfish calculate all the
possible lines for 1000ms
30 return result.bestmove # returns the best move in the current position
31
32 def get_current_piece(self, board, square): # returns what piece is at requested
square and converts it to a lowercase lette.
33 return str(board.piece_at(square)).lower()
34
35 def try_legal_move(self, board, suggested_move): # returns the move if its legal,
otherwise returns None
36 legal_moves_dict = {str(move) : move for move in board.legal_moves } #
creates a dictionary with moves in a string format as key and chess moves as item
37 move = legal_moves_dict.get(suggested_move, None) # returns None if non legal
move
38 return move # retuns the move
39
40 def square_name(self, square): # given a squares id (0-63, integer), it returns
the name of the square as a string
41 return squares[square]
42
43 def get_pieces(self, board, piece_type, piece_color): # gets all pieces and their
positions on the board and returns a list
44 return [square for square in board.pieces(piece_type, piece_color)]
45
46 def get_player_move(self, board, piece_type, piece_color, destination_square): #
takes a piecetype and a destination and returns the legal move which it can do
47 moves = []
48 pieces_on_board = self.get_pieces(board, piece_type, piece_color) # gets the
pieces of the given types positions on the board
49 for square in pieces_on_board:
50 square = self.square_name(square) # gets the square names of the squares
51 move = self.try_legal_move(board, square+destination_square) # tries
which of the moves is a legal move
52 if move is not None:
53 moves.append(move) # appends the move to a list if its not None
54 return moves[0] if moves else None #returns the first legal move, else None
1 # Voice controlled robotic chess player
2 # R'o'ststyrd robotisk schackspelare
3 # Datum : 2021 - 05 - 09
4 # Written by : Axel Sernelin and Oscar de Brito Lingman
5 # Examinator : Nihad Subasic
6 # TRITA-ITM-EX 2021:51
7 # Kurskod : MF133X
8
9 # Bachelor's Thesis in Mechatronics at KTH
10
11 import speech_recognition as sr
12 import chess
13 import re # regular expression package
14
15 pieces_dict = {
16 "knight" : chess.KNIGHT,
17 "knights" : chess.KNIGHT,
18 "nights" : chess.KNIGHT,
19 "night" : chess.KNIGHT,
20 "nite" : chess.KNIGHT,
21 "light" : chess.KNIGHT,
22 "wright" : chess.KNIGHT,
23 "height" : chess.KNIGHT,
24 "fight" : chess.KNIGHT,
25 "sight" : chess.KNIGHT,
26 "site" : chess.KNIGHT,
27 "horse" : chess.KNIGHT,
28 "whores" : chess.KNIGHT,
29 "9th" : chess.KNIGHT,
30 "nike" : chess.KNIGHT,
31 "might" : chess.KNIGHT,
32
33 "pawn" : chess.PAWN,
34 "pan" : chess.PAWN,
35 "vaughn" : chess.PAWN,
36 "paw" : chess.PAWN,
37 "prawn" : chess.PAWN,
38 "lawn" : chess.PAWN,
39 "dawn" : chess.PAWN,
40 "shaun" : chess.PAWN,
41 "spawn" : chess.PAWN,
42 "yawn" : chess.PAWN,
43 "pawns" : chess.PAWN,
44 "poem" : chess.PAWN,
45 "home" : chess.PAWN,
46 "on" : chess.PAWN,
47
48 "queen" : chess.QUEEN,
49 "wien" : chess.QUEEN,
50 "queens" : chess.QUEEN,
51
52 "rook" : chess.ROOK,
53 "rooks" : chess.ROOK,
54 "route" : chess.ROOK,
55 "rock" : chess.ROOK,
56 "roof" : chess.ROOK,
57 "brook" : chess.ROOK,
58 "brooke" : chess.ROOK,
59 "crook" : chess.ROOK,
60
61 "king" : chess.KING,
62 "kings" : chess.KING,
63 "kong" : chess.KING,
64 "kill" : chess.KING,
65 "cling" : chess.KING,
66 "kin" : chess.KING,
67
68 "bishop" : chess.BISHOP,
69 "bishops" : chess.BISHOP,
70 "shop" : chess.BISHOP,
71 "chop" : chess.BISHOP,
72 "shops" : chess.BISHOP
73 } #dictionary with similair sounding words
74
75 class SpeechEngine: #handles voice input and identifying the player move
76 def __init__(self):
77 self.r = sr.Recognizer()
78 self.m = sr.Microphone()
79
80 def listen_for_move(self):
81 text_string = None
82 with self.m as source: self.r.adjust_for_ambient_noise(source) # adjusts for
ambient noise
83 print("Listening...")
84 with self.m as source: audio = self.r.listen(source)
85 print("Got it! Analyzing move...")
86 try:
87 value = self.r.recognize_google(audio) # recognizes speech using Google
Speech Recognition
88 if str is bytes: # special handling to correctly print unicode
characters to standard output
89 text_string = format(value).encode("utf-8")
90 else:
91 text_string = format(value)
92 except sr.UnknownValueError: # error handling
93 print("Oops! Didn't catch that move, try again")
94 except sr.RequestError as e:
95 print("Uh oh! Couldn't request results from Google Speech Recognition
service; {0}".format(e))
96 return(text_string.lower() if text_string else None) # returns a lowercase
string if its not None
97
98 def piece_finder(self, text_string): # gets the chess piece from the recorded
text string
99 string_split = text_string.split() # splits the string into a list
100 for word in string_split: # for each word in the string it checks if its in
the dictionary
101 piece = pieces_dict.get(word, None)
102 if piece is not None: # if theres a match, it returns the piece
103 return piece
104
105 def square_finder(self, text_string): # gets the square where the piece is to
move to
106 square = re.search(r'[a-h]+[1-8]', text_string) # re expression to find a
coordinate in the string
107 if square is not None:
108 return square.group() # returns the square if its found
109 else:
110 return None
111
Appendix C
Acumen
The following pages include the simulation code which was used in Acumen [5].
35
1 //Acumen simulation av hur en schackpjäs rör sig i vår robot
2 //Skapad av Axel Sernelin och Oscar de Brito Lingman
3 //ME133X Grupp 33
4 //Vi vet inte hur man ska få ett object att rotera kring en fix punkt så vi kunde
inte göra robotarmarna.
5 model Main(simulator) =
6 initially
7 c1 = create Cyl((0,0,0)), //skapar upp cylindern
8 c2 = create Chess(), //skapar upp brädet
9 x1=0, x1'=0, //sätter start position och hastigheter för de olika riktningarna
10 y1=0, y1'=0,
11 z1=2.75, z1'=0
12 always
13 if x1<3 //när x1 positionen så stannar den
14 then x1' = 1
15 else
16 x1' = 0,
17 if x1>=3 && y1 < 2 //när x1 positionen är uppnådd så startar y1 och kör tills y1 är i
position 2
18 then y1' = 1
19 else
20 y1' = 0,
21 if x1>=3 && y1 >= 2 && z1 > 0.75 //när x1 och y2 är i rätt position startar z1
hastigheten tills z1 positionen är på 0.75
22 then z1' = -1
23 else
24 z1' = 0,
25 c1.pos = (x1,y1,z1) //sätter cylinderns position
26 model Cyl(pos) =
27 initially
28 _3D = (),_Plot=()
29 always
30 _3D = (Cylinder center = pos + (0,0,0) //skapar cylinder med olika attribut och
position pos som justeras från huvudprogrammet
31 color = red
32 length = 1.5
33 radius = 0.25
34 rotation = (pi/2,0,0)
35 )
36 model Chess() =
37 initially
38 _3D = (
39 (Box center = (0,0,0) //skapar rutor som på ett schackbräde, ett block per ruta.
40 color = black
41 length = 1
42 width = 1
43 height = 0.1
44 )
45 (Box center = (1,0,0)
46 color = white
47 length = 1
48 width = 1
49 height = 0.1
50 )
51 (Box center = (2,0,0)
52 color = black
53 length = 1
54 width = 1
55 height = 0.1
56 )
57 (Box center = (3,0,0)
58 color = white
59 length = 1
60 width = 1
61 height = 0.1
62 )
63 (Box center = (4,0,0)
64 color = black
65 length = 1
66 width = 1
67 height = 0.1
68 )
69 (Box center = (0,1,0)
70 color = white
71 length = 1
72 width = 1
73 height = 0.1
74 )
75 (Box center = (0,2,0)
76 color = black
77 length = 1
78 width = 1
79 height = 0.1
80 )
81 (Box center = (0,3,0)
82 color = white
83 length = 1
84 width = 1
85 height = 0.1
86 )
87 (Box center = (0,4,0)
88 color = black
89 length = 1
90 width = 1
91 height = 0.1
92 )
93 (Box center = (1,1,0)
94 color = black
95 length = 1
96 width = 1
97 height = 0.1
98 )
99 (Box center = (1,2,0)
100 color = white
101 length = 1
102 width = 1
103 height = 0.1
104 )
105 (Box center = (1,3,0)
106 color = black
107 length = 1
108 width = 1
109 height = 0.1
110 )
111 (Box center = (1,4,0)
112 color = white
113 length = 1
114 width = 1
115 height = 0.1
116 )
117 (Box center = (2,1,0)
118 color = white
119 length = 1
120 width = 1
121 height = 0.1
122 )
123 (Box center = (2,2,0)
124 color = black
125 length = 1
126 width = 1
127 height = 0.1
128 )
129 (Box center = (2,3,0)
130 color = white
131 length = 1
132 width = 1
133 height = 0.1
134 )
135 (Box center = (2,4,0)
136 color = black
137 length = 1
138 width = 1
139 height = 0.1
140 )
141 (Box center = (3,1,0)
142 color = black
143 length = 1
144 width = 1
145 height = 0.1
146 )
147 (Box center = (3,2,0)
148 color = white
149 length = 1
150 width = 1
151 height = 0.1
152 )
153 (Box center = (3,3,0)
154 color = black
155 length = 1
156 width = 1
157 height = 0.1
158 )
159 (Box center = (3,4,0)
160 color = white
161 length = 1
162 width = 1
163 height = 0.1
164 )
165 (Box center = (4,1,0)
166 color = white
167 length = 1
168 width = 1
169 height = 0.1
170 )
171 (Box center = (4,2,0)
172 color = black
173 length = 1
174 width = 1
175 height = 0.1
176 )
177 (Box center = (4,3,0)
178 color = white
179 length = 1
180 width = 1
181 height = 0.1
182 )
183 (Box center = (4,4,0)
184 color = black
185 length = 1
186 width = 1
187 height = 0.1
188 ))
189
190
TRITA-KTH-ITM 2021:51
www.kth.se