三子棋装置(电赛24E题)K230/STM32全开源

三子棋装置(电赛24E题)K230/STM32全开源,后续有具体代码参数讲解,帮助大家移植
k230代码

python 复制代码
import time, os, sys

from media.sensor import *
from media.display import *
from media.media import *

from machine import UART
from machine import FPIOA
from machine import Timer

UartFlag=False#串口数据接收标志

# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)

# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)

sensor_id = 2
sensor = None

# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id,width=1920, height=1080)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像和翻转
# 设置不要水平镜像
sensor.set_hmirror(False)
# 设置不要垂直翻转
sensor.set_vflip(False)
sensor.set_framesize(width=800, height=480, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565,要注意有些案例只支持GRAYSCALE格式
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

Display.init(Display.ST7701, width=800, height=480, to_ide=True)

# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
# 丢掉前面50帧数据,防止摄像头还不稳定
for i in range(50):
   sensor.snapshot()

fps = time.clock()

correction_points = [[268, 72], [635, 64],[708, 469], [226, 476]]
taruge_rect= [[57, 412],[411, 411],[411, 29],[55,29]]
# 棋盘数组
# 黑子:X
# 白子:O
# 没有棋子:空字符串
board = [
     [" "," "," "],
     [" "," "," "],
     [" "," "," "],
]

#标准灰度值
std_grayscale_values = [
     [0,0,0],
     [0,0,0],
     [0,0,0],
]

'''
#实际坐标系九宫格中心位置
中心点顺序为从左到右、从上到下:
第一行:中心点1(左)、中心点2(中)、中心点3(右)
第二行:中心点4(左)、中心点5(中)、中心点6(右)
第三行:中心点7(左)、中心点8(中)、中心点9(右)
'''
centers_real=[]
#白色棋子位置
white_positions=[[25,0],[50,0],[75,0],[100,0],[125,0]]
white_cnt=0
#黑色棋子位置
black_positions=[[25,155],[50,155],[75,155],[100,155],[125,155]]
black_cnt=0
'''
获得九宫格灰度
'''
def get_grid_region_grayscale():
    # 1. 对四个顶点排序
    (tl, tr, br, bl) = [[0, 0],[480, 0],[480, 480],[0,480]]

    # 2. 计算大正方形的宽度和高度
    width = max(abs(tr[0]-tl[0]), abs(br[0]-bl[0]))
    height = max(abs(bl[1]-tl[1]), abs(br[1]-tr[1]))

    # 3. 计算每个小格子的宽度和高度
    cell_width = width / 3
    cell_height = height / 3

    # 4. 计算中心区域的大小(按比例)
    region_w = int(cell_width * 0.3)
    region_h = int(cell_height * 0.3)

    # 5. 存储9个格子的灰度值
    grayscale_values = [[0,0,0],
                        [0,0,0],
                        [0,0,0]]

    for i in range(3):  # 行 (y方向)
        for j in range(3):  # 列 (x方向)
            # 计算当前格子的左上角坐标
            cell_x = tl[0] + j * cell_width
            cell_y = tl[1] + i * cell_height

            # 计算中心区域的坐标
            center_x = cell_x + cell_width / 2
            center_y = cell_y + cell_height / 2

            # 提取中心区域 (region_w × region_h)
            x1 = int(center_x - region_w / 2)
            y1 = int(center_y - region_h / 2)
            x2 = int(center_x + region_w / 2)
            y2 = int(center_y + region_h / 2)

            # 防止越界
            x1 = max(0, x1)
            y1 = max(0, y1)
            x2 = min(img.width()-1, x2)
            y2 = min(img.height()-1, y2)

            # 计算该区域的平均灰度
            img.draw_rectangle(x1, y1, x2-x1, y2-y1,color=(255,255,255))
            region = img.get_statistics(roi=(x1, y1, x2-x1, y2-y1))
            grayscale_values[i][j]=region.l_mean()

    return grayscale_values

'''
更新棋盘信息
'''
def update_board():
    global board
    img = sensor.snapshot() # Take a picture and return the image.
    img.gamma_corr(0.8)
    img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    #for n in range(4):
        #img.draw_cross(int(taruge_rect[n][0]),int(taruge_rect[n][1]))
    img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    grayscale_values=get_grid_region_grayscale()
    for i in range(3):  # 行 (y方向)
        for j in range(3):  # 列 (x方向)
            if grayscale_values[i][j]-std_grayscale_values[i][j]>10:
                board[i][j]='O'
            elif grayscale_values[i][j]-std_grayscale_values[i][j]<-10:
                board[i][j]='X'
            else:
                board[i][j]=" "
        print(board[i])
        #print(grayscale_values[i])
    print('......')

'''
初始化标准灰度
'''
def init_std_grayscale_values():
    global std_grayscale_values
    img = sensor.snapshot() # Take a picture and return the image.
    img.gamma_corr(0.8)
    img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    #for n in range(4):
        #img.draw_cross(int(taruge_rect[n][0]),int(taruge_rect[n][1]))
    img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    std_grayscale_values=get_grid_region_grayscale()


"""
计算四边形内 9 宫格的 9 个中心点坐标,同步更新真实坐标系下的坐标
参数:corners: 四边形的 4 个角点,顺序为 [左下, 右下,右上 , 左上]
返回: 9 个中心点的列表,顺序为从左到右、从上到下
    中心点顺序为从左到右、从上到下:
    第一行:中心点1(左)、中心点2(中)、中心点3(右)
    第二行:中心点4(左)、中心点5(中)、中心点6(右)
    第三行:中心点7(左)、中心点8(中)、中心点9(右)
"""
def get_nine_grid_centers(corners):
    global centers_real
    # 提取四个角点
    lb = corners[0]  # 左下 (u=0, v=0)
    rb = corners[1]  # 右下 (u=1, v=0)
    rt = corners[2]  # 右上 (u=1, v=1)
    lt = corners[3]  # 左上 (u=0, v=1)

    # 双线性插值函数
    def bilinear_interp(u, v):
        x = (1-u)*(1-v)*lb[0] + u*(1-v)*rb[0] + u*v*rt[0] + (1-u)*v*lt[0]
        y = (1-u)*(1-v)*lb[1] + u*(1-v)*rb[1] + u*v*rt[1] + (1-u)*v*lt[1]
        return (x, y)

    # 计算9个中心点(从左到右,从上到下)
    tmp=[]
    centers = []
    for v in [5/6, 3/6, 1/6]:  # 从上到下(v=1是顶部,v=0是底部)
        for u in [1/6, 3/6, 5/6]:  # 从左到右(u=0是左侧,u=1是右侧)
            center = bilinear_interp(u, v)
            centers.append(center)
            center_real =(center[1]*13/48+11, center[0]*13/48+11)#x,y反转
            tmp.append(center_real)
    centers_real=tmp
    return centers

"""
输入当前棋盘和执棋颜色,返回最佳下棋位置 (row, col)

参数:
    board: 3x3 的二维列表,表示当前棋盘,例如:
        [["X", " ", "O"],
         [" ", "X", " "],
         ["O", " ", " "]]
    player_color: 0 表示白棋 (O),1 表示黑棋 (X)

返回:
    (row, col): 最佳下棋位置,行列范围 0-2
"""
def get_best_move(board, player_color):
    # 预计算所有可能的赢法(8 种:3行 + 3列 + 2对角线)
    win_patterns = [
        [(0, 0), (0, 1), (0, 2)],  # 第一行
        [(1, 0), (1, 1), (1, 2)],  # 第二行
        [(2, 0), (2, 1), (2, 2)],  # 第三行
        [(0, 0), (1, 0), (2, 0)],  # 第一列
        [(0, 1), (1, 1), (2, 1)],  # 第二列
        [(0, 2), (1, 2), (2, 2)],  # 第三列
        [(0, 0), (1, 1), (2, 2)],  # 主对角线
        [(0, 2), (1, 1), (2, 0)]   # 副对角线
    ]

    def evaluate(b):
        """快速评估当前棋盘是否有玩家获胜"""
        for pattern in win_patterns:
            cells = [b[i][j] for (i, j) in pattern]
            if cells[0] == cells[1] == cells[2] != " ":
                return 1 if cells[0] == "X" else -1
        return 0  # 无胜负或平局

    def minimax(b, depth, alpha, beta, is_maximizing):
        """带 Alpha-Beta 剪枝的极小化极大算法"""
        score = evaluate(b)
        if score != 0:  # 有玩家获胜
            return score
        if all(cell != " " for row in b for cell in row):  # 平局
            return 0

        if is_maximizing:
            max_score = -float("inf")
            for i in range(3):
                for j in range(3):
                    if b[i][j] == " ":
                        b[i][j] = "X"
                        current_score = minimax(b, depth + 1, alpha, beta, False)
                        b[i][j] = " "
                        max_score = max(max_score, current_score)
                        alpha = max(alpha, current_score)
                        if beta <= alpha:  # Alpha-Beta 剪枝
                            break
            return max_score
        else:
            min_score = float("inf")
            for i in range(3):
                for j in range(3):
                    if b[i][j] == " ":
                        b[i][j] = "O"
                        current_score = minimax(b, depth + 1, alpha, beta, True)
                        b[i][j] = " "
                        min_score = min(min_score, current_score)
                        beta = min(beta, current_score)
                        if beta <= alpha:  # Alpha-Beta 剪枝
                            break
            return min_score

    player = "X" if player_color == 1 else "O"
    best_score = -float("inf") if player == "X" else float("inf")
    best_move = (-1, -1)
    alpha = -float("inf")
    beta = float("inf")

    for i in range(3):
        for j in range(3):
            if board[i][j] == " ":
                board[i][j] = player
                score = minimax(board, 0, alpha, beta, player == "O")
                board[i][j] = " "
                if (player == "X" and score > best_score) or (player == "O" and score < best_score):
                    best_score = score
                    best_move = (i, j)
                # 更新 Alpha/Beta
                if player == "X":
                    alpha = max(alpha, best_score)
                else:
                    beta = min(beta, best_score)

    return best_move

'''
找矩形外框
'''
loop=True#False#
last_taruge_rect = [[0,0],[0,0],[0,0],[0,0]]  #记录上一次识别到的定点数据,用来判断数据是否稳定
matching_counts = 0#记录识别稳定不变的次数,达到一定数量则判断识别成功
while(loop):
    img = sensor.snapshot() # Take a picture and return the image.
    #img.gamma_corr(0.8)
    img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(0,0,0),fill=True)    #右侧空白处涂黑
    img.midpoint(2, bias=0.9, threshold=True, offset=10, invert=True)    #凸显黑线
    rr = img.find_rects(threshold=500000)   #找矩形
    if rr:  #如果有目标
        for r in rr:
            img.draw_rectangle(r.rect(), color = (255, 0, 0))   #在屏幕绘制标识框
            taruge_rect = r.corners()  #存储方框顶点坐标
            for n in range(4):#对比方框定点坐标数据的变动情况,判断是否获取成功
                for n2 in range(2):
                    #注意,此处判断阈值为3,如果画面不稳定,可能程序无法向后进行。可以修改阈值。另外还可以增加低通滤波。
                    if abs(taruge_rect[n][n2] - last_taruge_rect[n][n2]) < 30:
                        matching_counts += 1
                        print(matching_counts)
                    else:
                        matching_counts = 0
                        print('识别失败')
            last_taruge_rect = taruge_rect
            if matching_counts > 10:
                loop = False
                print('识别成功')
                #print(taruge_rect)

    #img.draw_string_advanced(50,50,80,"fps:{}".format(fps.fps()*10000),color=(255,0,0))
    Display.show_image(img)
init_std_grayscale_values()
centers=get_nine_grid_centers(taruge_rect)
UartFlag=True#开始接收串口数据
#print(centers)
#print(centers_real)
'''
while(True):
    img = sensor.snapshot() # Take a picture and return the image.
    img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    for i in range(9):
       img.draw_cross(int(centers[i][0]),int(centers[i][1]))

    Display.show_image(img)
'''
def TimCallBack(timer):
    global white_positions,black_positions,white_cnt,black_cnt,UartFlag,board
    if UartFlag:
        data = uart.read()
        if data:
            #通过CanMV IDE K230中的串行终端控制台打印出来
            hex_str = data.hex()  # 转为16进制字符串
            print('收到数字:',hex_str)
            # 提取第一个字节的高低4位
            high_nibble = int(hex_str[0], 16)
            low_nibble = int(hex_str[1], 16)
            print(high_nibble,low_nibble)
            if high_nibble==0:
                uart.write(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")
                white_cnt+=1
                time.sleep_ms(4000)
                uart.write(f"X{centers_real[low_nibble-1][0]:.2f}Y{centers_real[low_nibble-1][1]:.2f}L")
                #print('.....')
                #print(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")
                #print(f"X{centers_real[low_nibble][0]:.2f}Y{centers_real[low_nibble][1]:.2f}L")
            elif high_nibble==1:
                uart.write(f"X{black_positions[black_cnt][0]:.2f}Y{black_positions[black_cnt][1]:.2f}P")
                black_cnt+=1
                time.sleep_ms(4000)
                uart.write(f"X{centers_real[low_nibble-1][0]:.2f}Y{centers_real[low_nibble-1][1]:.2f}L")
            elif high_nibble==5:#执白棋
                update_board()
                x,y=get_best_move(board,0)
                uart.write(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")
                white_cnt+=1
                time.sleep_ms(4000)
                uart.write(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")
                print('.....')
                print(x,y)
                #print(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")
            elif high_nibble==6:#执黑棋
                update_board()
                x,y=get_best_move(board,1)
                uart.write(f"X{black_positions[black_cnt][0]:.2f}Y{black_positions[black_cnt][1]:.2f}P")
                black_cnt+=1
                time.sleep_ms(4000)
                uart.write(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")
                print('.....')
                print(x,y)
                #print(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")
# 创建一个软件定时器实例,-1 表示使用软件定时器
tim = Timer(-1)

# 配置定时器,定时2000毫秒
tim.init(period=2000, mode=Timer.PERIODIC, callback=TimCallBack)

while(True):
    img = sensor.snapshot() # Take a picture and return the image.
    img.gamma_corr(0.8)

    img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑

    img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正
    img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例
    img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑
    '''
    update_board()

    (y,x)=get_best_move(board,1)
    img.draw_cross(80+160*x,80+160*y)
    '''
    Display.show_image(img)
    time.sleep_ms(1000)
相关推荐
嵌入式小李28 分钟前
stm32项目(25)——基于stm32的植物生长箱环境监测系统
stm32·单片机·嵌入式硬件
郭郭的柳柳在学FPGA4 小时前
STM32 APP跳转后无法进入中断
javascript·stm32·嵌入式硬件
sjh21006 小时前
STM32的计数模式和pwm模式
java·stm32·算法
写点什么呢1 天前
Stlink识别不到-安装驱动
stm32·单片机·嵌入式硬件·学习
安庆平.Я1 天前
STM32——时钟系统
stm32·单片机·嵌入式硬件
机器视觉知识推荐、就业指导2 天前
STM32 外设驱动模块四:光敏电阻(LDR) 模块
stm32·单片机·嵌入式硬件
Hello_Embed2 天前
STM32HAL 快速入门(三):从 HAL 函数到寄存器操作 —— 理解 HAL 库的本质
c语言·stm32·单片机·嵌入式硬件·学习
学不动CV了2 天前
FreeRTOS入门知识(初识RTOS任务调度)(三)
c语言·arm开发·stm32·单片机·物联网·算法·51单片机
学习嵌入式的王饱饱2 天前
STM32HAL库 -- 10.DMA外设实战(UART串口+DMA读取传感器数据)
stm32·单片机·dma·uart·hal库