Robot de Tres en Raya

Tutorial en video: Tutorial en video de OpenMV de Singtown Technology - Robot de brazo robótico de tres en raya, Concurso de Electrónica 2024

El robot de tres en raya se divide en tres pasos: 1. Reconocimiento de imagen, para obtener la matriz de fichas. 2. Algoritmo de estrategia de juego. 3. Control del brazo robótico para agarrar y colocar las fichas.

En cuanto al hardware, utilizamos el OpenMV4 H7 de Singtown Technology. ¡Todo el código se ejecuta en el OpenMV! Esto incluye el control del brazo robótico, el control del relé, el algoritmo de estrategia de juego y el algoritmo de reconocimiento de imagen.

El brazo robótico se imprimió directamente en 3D y utiliza 3 servomotores, que se pueden controlar directamente a través de la placa de expansión de servos de OpenMV.

La recogida de las fichas se realiza mediante un electroimán, ya que aproveché para usar 10 pilas de botón como fichas. OpenMV puede controlar el electroimán a través de un relé.

  1. El reconocimiento de imagen es muy sencillo: primero se obtiene la imagen en escala de grises, y luego se obtiene la información de color en las 9 zonas del tablero, lo cual se realiza en OpenMV usando get_statistics. A partir de la información de escala de grises, se puede determinar si hay una ficha negra, una ficha blanca o si la casilla está vacía.
  2. Para calcular cuál debe ser el siguiente movimiento, mi buen amigo el profesor ChatGPT me dijo que usara el algoritmo minimax, y me escribió el código de esta parte.
  3. El control del brazo robótico también es bastante sencillo: se obtiene la posición del brazo en la zona de espera para recoger fichas y en cada una de las 9 zonas de colocación del tablero. Normalmente esto se puede hacer mediante una función de enseñanza (teaching), pero como mi brazo robótico se hizo con mucha prisa en solo un día, las posiciones se determinaron directamente a través del código.
# robot.py

import time
from servo import Servos
from machine import SoftI2C, Pin
import math

# Controla el relé; el relé controla el electroimán
pin1 = Pin('P1', Pin.OUT_PP, Pin.PULL_NONE)
pin1.value(0)

# Placa de expansión 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)

# Posición inicial de los tres servos
servo.position(0, 0)
servo.position(1, 90)
servo.position(2, 90)

# Variable global, usada para el movimiento lento
servo_positions = [0,90,90]


# Controla el movimiento de un servo
def move(index, angle):
    servo.position(index, angle)
    servo_positions[index] = angle


# Controla el movimiento de los tres servos
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 el movimiento lento de los tres servos
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)


# Establece la posición de la zona de recogida de fichas
PICK = [[74,128,19], [82,128,17], [90,125,14], [98,128,17], [106,128,19]]
# Posición elevada del brazo robótico en la zona de recogida
HIGH_PICK = [90,95,55]

# Posiciones de colocación en el tablero
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]]
]

# Posición por encima del tablero para colocar fichas
HIGH_BOARD = [90,120,70]


# Recoge una ficha y la coloca en 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) # Enciende el relé
    time.sleep_ms(500)

    slow_move_to(HIGH_PICK)
    pin1.value(0) # Apaga el relé
    time.sleep_ms(500)

    slow_move_to([0,90,90])


if __name__ == "__main__":
# Para pruebas de calibración

    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

# Comprueba si alguien ha ganado
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

# Comprueba si hay empate
def check_draw(board):
    return all(board[i][j] != ' ' for i in range(SIZE) for j in range(SIZE))


# Calcula la puntuación de la estrategia
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 la posición del siguiente movimiento
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]


# Comprueba a quién le toca jugar
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()


# Interruptor táctil
pin0 = Pin('P0', Pin.IN, Pin.PULL_UP)


distance = 43
block = 10


# Genera las posiciones de las zonas de la cuadrícula 3x3
def generate_centered_rois(width, height, b, k):
    rois = []

    # Calcula el desplazamiento del centro de cada ROI
    offset = (b - k) // 2

    # Calcula el ancho y alto de toda la matriz 3x3
    total_width = 3 * b
    total_height = 3 * b

    # Calcula el punto de inicio superior izquierdo para centrar la 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


# Posiciones de las zonas de la cuadrícula 3x3
rois = generate_centered_rois(csi0.width(), csi0.height(), distance, block)


# Matriz del tablero
# Ficha negra: X
# Ficha blanca: O
# Sin ficha: cadena vacía
board = [
     [" "," "," "],
     [" "," "," "],
     [" "," "," "],
]


# Espera a que se presione y suelte el interruptor
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)


    # El reconocimiento de imagen obtiene la matriz del tablero
    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 la matriz actual del tablero
    for line in board:
        print(line)
    print()


    # Dibuja la matriz del tablero
    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)


    # Estrategia de juego
    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 dónde colocar la siguiente ficha
        line,row = chess.computer_move(board)
        # Dibuja una cruz en la posición objetivo del tablero
        img.draw_cross((int(rois[line][row][0]+block/2), int(rois[line][row][1]+block/2)), size=block, color=0)
        csi0.flush()
        # El robot recoge y coloca la ficha
        robot.pick_and_place(row, line)
        csi0.flush()
    elif chess.check_turn(board) == "O":
        print("该你下了!")

results matching ""

    No results matching ""