Robô de Jogo da Velha
Tutorial em vídeo: Tutorial em vídeo OpenMV da SingTown - Robô braço robótico de jogo da velha 2024 Concurso Eletrônico
O robô de jogo da velha é dividido em três etapas: 1. Reconhecimento de imagem, para obter a matriz de peças. 2. Algoritmo de estratégia de jogo. 3. Controle do braço robótico para pegar e posicionar as peças.
No hardware, usamos o OpenMV4 H7 da SingTown. Todo o código é executado no próprio OpenMV! Isso inclui o controle do braço robótico, o controle do relé, o algoritmo de estratégia de jogo e o algoritmo de reconhecimento de imagem.
O braço robótico é impresso em 3D e usa 3 servomotores, que podem ser controlados diretamente pela placa de expansão de servos do OpenMV.
A captura das peças é feita com um eletroímã, já que aproveitei 10 baterias tipo botão como peças. O OpenMV pode controlar o eletroímã por meio de um relé.
- O reconhecimento de imagem é bem simples: primeiro obtemos a imagem em escala de cinza e, em seguida, coletamos as informações de cor dentro das 9 regiões do tabuleiro, usando a função statistics do OpenMV. Com base nos valores de cinza, é possível determinar se a casa tem uma peça preta, uma peça branca ou está vazia.
- Para calcular a próxima jogada, meu bom amigo, o professor ChatGPT, me disse para usar o algoritmo minimax e já escreveu essa parte do código para mim.
- O controle do braço robótico também é relativamente simples: obtemos a posição do braço tanto na área de espera de captura das peças quanto em cada uma das 9 posições do tabuleiro. Normalmente isso pode ser feito com uma função de ensino (teaching), mas como meu braço robótico foi feito às pressas em apenas um dia, defini as posições diretamente no código.
# robot.py
import time
from servo import Servos
from machine import SoftI2C, Pin
import math
# Controla o relé, que por sua vez controla o eletroímã
pin1 = Pin('P1', Pin.OUT_PP, Pin.PULL_NONE)
pin1.value(0)
# Placa de expansão de servos PCA9685
i2c = SoftI2C(sda=Pin('P5'), scl=Pin('P4'))
servo = Servos(i2c, address=0x40, freq=50, min_us=650, max_us=2800, degrees=180)
# Posição inicial dos três servomotores
servo.position(0, 0)
servo.position(1, 90)
servo.position(2, 90)
# Variável global, usada para movimento suave
servo_positions = [0,90,90]
# Controla o movimento de um servomotor
def move(index, angle):
servo.position(index, angle)
servo_positions[index] = angle
# Controla o movimento dos três servomotores
def move_list(angle_list):
print(angle_list)
move(0, int(angle_list[0]))
move(1, int(angle_list[1]))
move(2, int(angle_list[2]))
# Controla o movimento lento dos três servomotores
def slow_move_to(angle_list):
init_positions = servo_positions.copy()
d0 = angle_list[0] - init_positions[0]
d1 = angle_list[1] - init_positions[1]
d2 = angle_list[2] - init_positions[2]
dm = int(max(abs(d0), abs(d1), abs(d2)))
if dm == 0:
return
for i in range(dm+1):
move_list([init_positions[0]+i*d0/dm,
init_positions[1]+i*d1/dm,
init_positions[2]+i*d2/dm])
time.sleep_ms(40)
# Define a posição da área de captura das peças
PICK = [[74,128,19], [82,128,17], [90,125,14], [98,128,17], [106,128,19]]
# Posição elevada do braço na área de captura
HIGH_PICK = [90,95,55]
# Posições de colocação no tabuleiro
BOARD = [
[[82,150,55], [82,140,40], [81,133,30]],
[[90,150,55], [90,140,40], [90,133,30]],
[[98,150,55], [98,140,40], [99,133,30]]
]
# Posição acima do tabuleiro para posicionar a peça
HIGH_BOARD = [90,120,70]
# Pega a peça e a coloca em x,y
def pick_and_place(x,y):
slow_move_to(HIGH_PICK)
time.sleep_ms(500)
slow_move_to(PICK[2])
time.sleep_ms(500)
slow_move_to(HIGH_PICK)
time.sleep_ms(500)
slow_move_to(HIGH_BOARD)
time.sleep_ms(500)
slow_move_to(BOARD[y][x])
time.sleep_ms(500)
pin1.value(1) # liga o relé
time.sleep_ms(500)
slow_move_to(HIGH_PICK)
pin1.value(0) # desliga o relé
time.sleep_ms(500)
slow_move_to([0,90,90])
if __name__ == "__main__":
# usado para teste de calibração
time.sleep_ms(1)
for order in [
BOARD[1][1], BOARD[0][0], BOARD[1][0],
BOARD[2][0], BOARD[2][1], BOARD[2][2],
BOARD[1][2], BOARD[0][2], BOARD[0][1],
]:
slow_move_to(HIGH_BOARD)
time.sleep_ms(500)
slow_move_to(order)
time.sleep_ms(500)
#slow_move_to(BOARD[0][2])
#for x in PICK:
#slow_move_to(HIGH_PICK)
#time.sleep_ms(500)
#slow_move_to(x)
#time.sleep_ms(500)
slow_move_to([0,90,90])
# chess.py
SIZE = 3
# Verifica se alguém venceu
def check_win(board, player):
# Check rows and columns
for i in range(SIZE):
if all(board[i][j] == player for j in range(SIZE)) or \
all(board[j][i] == player for j in range(SIZE)):
return True
# Check diagonals
if all(board[i][i] == player for i in range(SIZE)) or \
all(board[i][SIZE - 1 - i] == player for i in range(SIZE)):
return True
return False
# Verifica se houve empate
def check_draw(board):
return all(board[i][j] != ' ' for i in range(SIZE) for j in range(SIZE))
# Calcula a pontuação da estratégia
def minimax(board, depth, is_maximizing):
computer = 'X'
player = 'O'
if check_win(board, computer):
return 10 - depth
if check_win(board, player):
return depth - 10
if check_draw(board):
return 0
if is_maximizing:
best_score = float('-inf')
for i in range(SIZE):
for j in range(SIZE):
if board[i][j] == ' ':
board[i][j] = computer
score = minimax(board, depth + 1, False)
board[i][j] = ' '
best_score = max(score, best_score)
return best_score
else:
best_score = float('inf')
for i in range(SIZE):
for j in range(SIZE):
if board[i][j] == ' ':
board[i][j] = player
score = minimax(board, depth + 1, True)
board[i][j] = ' '
best_score = min(score, best_score)
return best_score
# Calcula a próxima posição
def computer_move(board):
if board == [
[" "," "," "],
[" "," "," "],
[" "," "," "]
]:
return 1,1
best_score = float('-inf')
move = (-1, -1)
for i in range(SIZE):
for j in range(SIZE):
if board[i][j] == ' ':
board[i][j] = 'X'
score = minimax(board, 0, False)
board[i][j] = ' '
if score > best_score:
best_score = score
move = (i, j)
if move != (-1, -1):
# board[move[0]][move[1]] = 'X'
print(f"Computer places X at ({move[0]}, {move[1]})")
return move[0], move[1]
# Verifica de quem é a vez
def check_turn(board):
x_count = sum(row.count("X") for row in board)
o_count = sum(row.count("O") for row in board)
return "X" if x_count == o_count else "O"
# main.py
import csi, image, time
csi0 = csi.CSI()
from pyb import Pin
import robot
import chess
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
csi0.snapshot(time = 2000)
clock = time.clock()
# Botão de toque
pin0 = Pin('P0', Pin.IN, Pin.PULL_UP)
distance = 43
block = 10
# Gera as posições das regiões do tabuleiro 3x3
def generate_centered_rois(width, height, b, k):
rois = []
# Calcula o deslocamento do centro de cada ROI
offset = (b - k) // 2
# Calcula a largura e a altura de toda a matriz 3x3
total_width = 3 * b
total_height = 3 * b
# Calcula o ponto inicial superior esquerdo para centralizar a matriz
start_x = (width - total_width) // 2
start_y = (height - total_height) // 2
for i in range(3):
row = []
for j in range(3):
x_center = start_x + j * b + b // 2
y_center = start_y + i * b + b // 2
x = x_center - k // 2
y = y_center - k // 2
row.append((x, y, k, k))
rois.append(row)
return rois
# Posições das regiões do tabuleiro 3x3
rois = generate_centered_rois(csi0.width(), csi0.height(), distance, block)
# Matriz do tabuleiro
# Peça preta: X
# Peça branca: O
# Sem peça: string vazia
board = [
[" "," "," "],
[" "," "," "],
[" "," "," "],
]
# Aguarda o botão ser pressionado e solto
def wait_key():
while pin0.value():
img = csi0.snapshot().lens_corr(1.8)
for y in range(len(rois)):
for x in range(len(rois[y])):
img.draw_rectangle(rois[y][x])
while not pin0.value():
time.sleep_ms(1)
while(True):
clock.tick()
wait_key()
img = csi0.snapshot().lens_corr(1.8)
# Reconhecimento de imagem para obter a matriz do tabuleiro
for y in range(len(rois)):
for x in range(len(rois[y])):
gray = img.get_statistics(roi=rois[y][x]).mean
if gray < 100:
board[y][x] = "X"
elif gray > 200:
board[y][x] = "O"
else:
board[y][x] = " "
# Imprime a matriz atual do tabuleiro
for line in board:
print(line)
print()
# Desenha a matriz do tabuleiro
for y in range(len(rois)):
for x in range(len(rois[y])):
if board[y][x] == "X":
color = 255
elif board[y][x] == "O":
color = 0
elif board[y][x] == " ":
color = 127
img.draw_rectangle(rois[y][x], color=color)
# Estratégia de jogo
if chess.check_win(board, 'O'):
print("你赢啦!")
elif chess.check_win(board, 'X'):
print("我赢啦!")
elif chess.check_draw(board):
print("平局啦!")
elif chess.check_turn(board) == "X":
# Calcula onde colocar a próxima peça
line,row = chess.computer_move(board)
# Desenha uma cruz na posição alvo do tabuleiro
img.draw_cross((int(rois[line][row][0]+block/2), int(rois[line][row][1]+block/2)), size=block, color=0)
csi0.flush()
# O robô pega e posiciona a peça
robot.pick_and_place(row, line)
csi0.flush()
elif chess.check_turn(board) == "O":
print("该你下了!")