2024电赛——OPENMV识别三子棋棋盘与黑白棋识别思路(包含获胜判断,AI下棋,串口通信)

飞天扫帚 2024-10-08 12:31:06 阅读 92

​OpenMV是国外一家公司生产的,目前国内的代理商是星瞳科技,在星瞳科技的OpenMV官网上提供了许多中文资料,包括一个帮助你快速上手的中文手册,一个可以查询函数库的中文网站,下载IDE的网站,论坛,官方录制的视频教程(根据上手手册讲的),还有售卖的MV的购买界面和详细参数。

ba5b79a59e85009f572f8e06ed81b8ca.png

1.识别与ai下棋思路

首先识别棋盘,然后进行识别黑白棋子。然后创建一个二维数组,通过二维数组进行记录棋盘当前状态。

board = [3][3]

# 初始化棋盘状态,0表示空格,1表示黑子,2表示白子

<code># 将棋盘位置编号转换为行列坐标def num_to_position(num): row = 2 - (num - 1) // 3 # 反转行顺序 col = (num - 1) % 3 return row, col# 将行列坐标转换为棋盘位置编号def position_to_num(row, col): return (2 - row) * 3 + (col + 1)

2.识别棋盘并且防止后期光线干扰思路

将棋盘第一次被openmv识别到就进行读取棋盘角点坐标,然后只读取这个棋盘坐标区域内的棋子颜色,不在识别棋盘是否为黑色边框矩形。

初始化棋盘坐标:在第一次识别到棋盘后,保存棋盘的角点坐标。

绘制棋盘区域:使用保存的角点坐标来绘制棋盘区域,并在该区域内读取棋子颜色。

避免重新识别棋盘:在后续帧中,只关注已经确定的棋盘区域,避免重新识别棋盘边框。

# 变量初始化corners = None # 用于存储棋盘角点坐标black_threshold = (6, 100, -39, 18, -31, 43) # 黑色棋子的阈值white_threshold = (30, 47, 76, 47, 23, 56) # 白色棋子的阈值 红色原点board = [[0 for _ in range(3)] for _ in range(3)] # 棋盘状态初始化为3x3的空格子# 插值函数,用于计算角点间的位置def interpolate(p1, p2, factor): return (p1[0] + (p2[0] - p1[0]) * factor, p1[1] + (p2[1] - p1[1]) * factor)

寻找符合棋盘条件的矩形并且记录棋盘四个角点坐标

if first_detection: # 查找矩形 rects = img.find_rects(threshold=10000) max_area = 0 max_rect = None for rect in rects: area = rect.w() * rect.h() if area > max_area: max_area = area max_rect = rect if max_rect: # 确保识别到的矩形符合棋盘的预期尺寸 if 50 < max_rect.w() < 100 and 50 < max_rect.h() < 100: corners = max_rect.corners() # 获取矩形的角点 for corner in corners: img.draw_circle(corner[0], corner[1], 5, color=(255, 0, 0), thickness=2, fill=False) # 绘制角点 for i in range(len(corners)): start_point = corners[i] end_point = corners[(i + 1) % 4] img.draw_line(start_point[0], start_point[1], end_point[0], end_point[1], color=(255, 0, 0)) # 绘制边线 first_detection = False # 标记为已检测到棋盘

3.识别棋盘内的黑白棋并且判断棋盘内是否有获胜

黑白棋子的判断:通过阈值的检测与形状的检测;

1.黑白阈值

black_threshold = (6, 100, -39, 18, -31, 43) # 黑色棋子的阈值white_threshold = (30, 47, 76, 47, 23, 56) # 白色棋子的阈值

2.圆形棋子

black_blobs = img.find_blobs([(0, 10)], roi=(x_min, y_min, roi_width, roi_height), merge=True) # 查找黑色棋子                    white_blobs = img.find_blobs([white_threshold], roi=(x_min, y_min, roi_width, roi_height), merge=True)  # 查找白色棋子 valid_black_blobs = [blob for blob in black_blobs if blob.pixels() > ```python 20 and blob.area() > 50 and blob.roundness() > 0.8] # 筛选有效的黑子 valid_white_blobs = [blob for blob in white_blobs if blob.roundness() > 0.4] # 筛选有效的白子

检查是否有三个连续的棋子(在水平、垂直或对角线方向上),并在图像上绘制一条绿色的连线表示获胜

定义方向directions = [(0, 1), (1, 0), (1, 1), (1, -1)]

定义四个检查方向:

(0, 1):水平右

(1, 0):垂直下

(1, 1):对角线右下

(1, -1):对角线左下

然后通过遍历方向和起始点对每个方向,检查每个位置是否可以作为起始点形成连续的三子连线。

从每个起始点出发,按照当前方向检查下一个位置是否有棋子。

如果有,增加 consecutive_count。如果 consecutive_count 达到 3,表示形成连续的三子连线。

# 检查是否有连续的三子连线def check_continuous_line(img, positions): directions = [(0, 1), (1, 0), (1, 1), (1, -1)] # 定义四个方向 for direction in directions: for i in range(len(positions)): consecutive_count = 1 for j in range(1, len(positions)): if (positions[i][0] + j * direction[0], positions[i][1] + j * direction[1]) in positions: consecutive_count += 1 if consecutive_count == 3: p1 = (positions[i][0], positions[i][1]) p2 = (positions[i][0] + 2 * direction[0], positions[i][1] + 2 * direction[1]) img.draw_line(p1[0], p1[1], p2[0], p2[1], color=(0, 255, 0), thickness=2) # 绘制连线 return True else: break return False

8a95fcbe8f57bee639c5821e6adf2e70.jpeg

4.识别棋盘后对棋盘进行棋格划分与棋格编号标识

<code> # 计算小格子的边界 x_min = max(0, min(int(top_left[0]), int(top_right[0]), int(bottom_left[0]), int(bottom_right[0]))) y_min = max(0, min(int(top_left[1]), int(top_right[1]), int(bottom_left[1]), int(bottom_right[1]))) x_max = min(img.width(), max(int(top_left[0]), int(top_right[0]), int(bottom_left[0]), int(bottom_right[0]))) y_max = min(img.height(), max(int(top_left[1]), int(top_right[1]), int(bottom_left[1]), int(bottom_right[1]))) roi_width = x_max - x_min                roi_height = y_max - y_min           

if len(valid_black_blobs) == 0 and len(valid_white_blobs) == 0: ir_led.on() # 开启红外LED center_x = (x_min + x_max) // 2 center_y = (y_min + y_max) // 2 img.draw_string(center_x - 10, center_y - 10, str(num), color=(255, 0, 0), scale=2) # 在格子中央绘制编号

5.下棋思路,我是通过openmv进行三维数组存储棋盘状态,并且通过算法进行输出ai所下棋格,然后通过串口回传到单片机控制机械臂进行夹取棋子(但该算法带有bug)

以上是一些编写代码的思路,下面附上整个opnmv代码​:

import sensor, image, time, pybfrom pyb import UART, LED# 初始化UART通信,波特率9600uart = UART(3, 9600, timeout_char=3000)# 初始化LEDir_led = LED(1)win_led = LED(2)# 初始化相机sensor.reset()sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度图像格式sensor.set_framesize(sensor.QQVGA) # 设置图像大小为QQVGAsensor.skip_frames(time=4000) # 跳过几帧,等待相机稳定clock = time.clock() # 初始化时钟# 变量初始化corners = None # 用于存储棋盘角点坐标black_threshold = (6, 100, -39, 18, -31, 43) # 黑色棋子的阈值white_threshold = (30, 47, 76, 47, 23, 56) # 白色棋子的阈值 红色原点board = [[0 for _ in range(3)] for _ in range(3)] # 棋盘状态初始化为3x3的空格子# 插值函数,用于计算角点间的位置def interpolate(p1, p2, factor): return (p1[0] + (p2[0] - p1[0]) * factor, p1[1] + (p2[1] - p1[1]) * factor)# 检查是否有连续的三子连线def check_continuous_line(img, positions): directions = [(0, 1), (1, 0), (1, 1), (1, -1)] # 定义四个方向 for direction in directions: for i in range(len(positions)): consecutive_count = 1 for j in range(1, len(positions)): if (positions[i][0] + j * direction[0], positions[i][1] + j * direction[1]) in positions: consecutive_count += 1 if consecutive_count == 3: p1 = (positions[i][0], positions[i][1]) p2 = (positions[i][0] + 2 * direction[0], positions[i][1] + 2 * direction[1]) img.draw_line(p1[0], p1[1], p2[0], p2[1], color=(0, 255, 0), thickness=2) # 绘制连线 return True else: break return False# 将棋盘位置编号转换为行列坐标def num_to_position(num): row = 2 - (num - 1) // 3 # 反转行顺序 col = (num - 1) % 3 return row, col# 将行列坐标转换为棋盘位置编号def position_to_num(row, col): return (2 - row) * 3 + (col + 1)# 检查是否有玩家获胜def check_winner(player): for row in board: if all(s == player for s in row): # 检查行 return True for col in range(3): if all(board[row][col] == player for row in range(3)): # 检查列 return True if all(board[i][i] == player for i in range(3)) or all(board[i][2 - i] == player for i in range(3)): # 检查对角线 return True return False# 检查棋盘是否已满def is_full(): return all(cell != 0 for row in board for cell in row)# AI的移动def ai_move(): for row in range(3): for col in range(3): if board[row][col] == 0: board[row][col] = 2 # AI 放置白子 # 在图像上绘制 AI 的移动(叉子) cell_size = img.width() // 3 center_x = col * cell_size + cell_size // 2 center_y = row * cell_size + cell_size // 2 img.draw_cross(center_x, center_y, 10, color=(0, 255, 0)) # 绘制绿色叉子 # 输出 AI 选择的棋格 xianum = position_to_num(row, col) print(f"AI 下棋到格子: {xianum}") # 通过UART发送AI选择的棋格 FH = bytearray([0x2C, 0x12, xianum, xianum, 0x5B]) uart.write(FH) returnplayer_turn = True # 游戏开始时由玩家先手first_detection = True # 标志位,表示是否为第一次检测棋盘while True: clock.tick() # 记录每次循环的开始时间 img = sensor.snapshot().lens_corr(strength=1.0, zoom=1.0) # 拍摄图像并进行镜头畸变校正 ir_led.off() # 关闭红外LED if first_detection: # 查找矩形 rects = img.find_rects(threshold=10000) max_area = 0 max_rect = None for rect in rects: area = rect.w() * rect.h() if area > max_area: max_area = area max_rect = rect if max_rect: # 确保识别到的矩形符合棋盘的预期尺寸 if 50 < max_rect.w() < 100 and 50 < max_rect.h() < 100: corners = max_rect.corners() # 获取矩形的角点 for corner in corners: img.draw_circle(corner[0], corner[1], 5, color=(255, 0, 0), thickness=2, fill=False) # 绘制角点 for i in range(len(corners)): start_point = corners[i] end_point = corners[(i + 1) % 4] img.draw_line(start_point[0], start_point[1], end_point[0], end_point[1], color=(255, 0, 0)) # 绘制边线 first_detection = False # 标记为已检测到棋盘 if corners: num = 9 black_positions = [] white_positions = [] for row in range(3): for col in range(3): # 计算每个小格子的角点 top_left = interpolate(interpolate(corners[0], corners[3], row / 3.0), interpolate(corners[1], corners[2], row / 3.0), 1 - col / 3.0) top_right = interpolate(interpolate(corners[0], corners[3], row / 3.0), interpolate(corners[1], corners[2], row / 3.0), 1 - (col + 1) / 3.0) bottom_left = interpolate(interpolate(corners[0], corners[3], (row + 1) / 3.0), interpolate(corners[1], corners[2], (row + 1) / 3.0), 1 - col / 3.0) bottom_right = interpolate(interpolate(corners[0], corners[3], (row + 1) / 3.0), interpolate(corners[1], corners[2], (row + 1) / 3.0), 1 - (col + 1) / 3.0) # 计算小格子的边界 x_min = max(0, min(int(top_left[0]), int(top_right[0]), int(bottom_left[0]), int(bottom_right[0]))) y_min = max(0, min(int(top_left[1]), int(top_right[1]), int(bottom_left[1]), int(bottom_right[1]))) x_max = min(img.width(), max(int(top_left[0]), int(top_right[0]), int(bottom_left[0]), int(bottom_right[0]))) y_max = min(img.height(), max(int(top_left[1]), int(top_right[1]), int(bottom_left[1]), int(bottom_right[1]))) roi_width = x_max - x_min roi_height = y_max - y_min if roi_width > 0 and roi_height > 0: ir_led.on() # 开启红外LED cell_stats = img.get_statistics(roi=(x_min, y_min, roi_width, roi_height)) # 获取ROI区域的统计信息 black_blobs = img.find_blobs([(0, 10)], roi=(x_min, y_min, roi_width, roi_height), merge=True) # 查找黑色棋子 white_blobs = img.find_blobs([white_threshold], roi=(x_min, y_min, roi_width, roi_height), merge=True) # 查找白色棋子



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。