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

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

 

ba5b79a59e85009f572f8e06ed81b8ca.png

1.识别与ai下棋思路

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

board = [3][3]

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

# 将棋盘位置编号转换为行列坐标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.识别棋盘后对棋盘进行棋格划分与棋格编号标识

 # 计算小格子的边界                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)  # 查找白色棋子

 

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值