2024电赛——OPENMV识别三子棋棋盘与黑白棋识别思路(包含获胜判断,AI下棋,串口通信)
飞天扫帚 2024-10-08 12:31:06 阅读 92
OpenMV是国外一家公司生产的,目前国内的代理商是星瞳科技,在星瞳科技的OpenMV官网上提供了许多中文资料,包括一个帮助你快速上手的中文手册,一个可以查询函数库的中文网站,下载IDE的网站,论坛,官方录制的视频教程(根据上手手册讲的),还有售卖的MV的购买界面和详细参数。
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
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, pyb
from pyb import UART, LED
# 初始化UART通信,波特率9600
uart = UART(3, 9600, timeout_char=3000)
# 初始化LED
ir_led = LED(1)
win_led = LED(2)
# 初始化相机
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度图像格式
sensor.set_framesize(sensor.QQVGA) # 设置图像大小为QQVGA
sensor.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)
return
player_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) # 查找白色棋子
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。