三子棋装置(电赛24E题)K230/STM32全开源,后续有具体代码参数讲解,帮助大家移植
k230代码
pythonimport 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)
三子棋装置(电赛24E题)K230/STM32全开源
小林学习中2025-08-12 8:40
相关推荐
嵌入式小李28 分钟前
stm32项目(25)——基于stm32的植物生长箱环境监测系统郭郭的柳柳在学FPGA4 小时前
STM32 APP跳转后无法进入中断sjh21006 小时前
STM32的计数模式和pwm模式写点什么呢1 天前
Stlink识别不到-安装驱动安庆平.Я1 天前
STM32——时钟系统机器视觉知识推荐、就业指导2 天前
STM32 外设驱动模块四:光敏电阻(LDR) 模块Hello_Embed2 天前
STM32HAL 快速入门(三):从 HAL 函数到寄存器操作 —— 理解 HAL 库的本质学不动CV了2 天前
FreeRTOS入门知识(初识RTOS任务调度)(三)学习嵌入式的王饱饱2 天前
STM32HAL库 -- 10.DMA外设实战(UART串口+DMA读取传感器数据)