Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
电赛
RA8-M85-vision-board
电赛E题——三子棋装置
发布于 2024-07-31 14:55:42 浏览:1362
订阅该版
# 电赛E题——三子棋装置 [toc] ## 题目简述 一、 任务 设计并制作三子棋游戏装置,能够控制机械臂或其他机构放置棋子,实现人 机对弈。图 1 所示的三子棋棋盘和棋子位置示意图中,棋盘由黑色实线围成 9 个方格,人、机分别从棋子放置处拾取棋子并放置到方格中,先将己方的 3 个棋 子连成一线(横连、竖连、斜连皆可)即获胜。 ![111.png](https://oss-club.rt-thread.org/uploads/20240731/823ff7082c7d461b439aad621f18daa0.png.webp) 二、 要求 (1)装置能将任意 1 颗黑棋子放置到 5 号方格中。(5 分) (2)装置能将任意 2 颗黑棋子和 2 颗白棋子依次放置到指定方格中。(20 分) (3)将棋盘绕中心±45°范围内旋转后,装置能将任意 2 颗黑棋子和 2 颗 白棋子依次放置到指定方格中。(20 分) (4)装置执黑棋先行与人对弈(第 1 步方格可设置),若人应对的第 1 步白 棋有错误,装置能获胜。(20 分) (5)人执黑棋先行,装置能正确放置白棋子以保持不输棋。(20 分) (6)对弈过程中,若人将装置下过的 1 颗棋子变动位置,装置能自动发现 并将该棋子放置回原来位置。(10 分) ## Vision Broad开发板简述 开发板描述: Vision Board搭载全球首颗 480 MHz Arm Cortex-M85芯片,拥有Helium和TrustZone技术的加持。SDK包里集成了OpenMV机器视觉例程,配合MicroPython 解释器,使其可以流畅地开发机器视觉应用。Vision Board具有以下主要特点: **强大的处理性能:**Vision Board搭载了全球首款基于 ARM Cortex-M85 架构的瑞萨电子RA8 MCU,6.39 CoreMark/MHz,可以快速而高效地运行机器视觉算法,实现图像处理、等功能。 **完整支持OpenMV生态:**Vision Board完全兼容OpenMV开发平台,用户可以直接使用OpenMV IDE进行编程和开发,利用丰富的API和库函数进行图像处理和计算机视觉任务。 **友好的学习环境:**提供简洁易用的开发环境和示例项目,带领初学者快速上手机器视觉、GUI等应用。同时具有丰富的文档资料供给开发者学习。 **可扩展性:**丰富的扩展接口,如摄像头、MIPI-DSI、RGB666、树莓派兼容IO、BTB拓展接口等,用户可以根据自己的需求连接其他外设或模块,如以太网、CAN/LIN、传感器、执行器等,实现更复杂的应用场景。 ![postcard.png](https://oss-club.rt-thread.org/uploads/20240731/3af22f5a5c13f8d5185ebb4efc2674e7.png.webp) 板载资源: ![222.png](https://oss-club.rt-thread.org/uploads/20240731/ca9f2c04fb5df512e8c5a40fcc02fc74.png.webp) 外设支持情况: ![333.png](https://oss-club.rt-thread.org/uploads/20240731/90afc876c4e2518bc6fb852a09c9f92d.png.webp) 支持的 IDE:RT-Thread Studio、MDKV538。OpenMV工程目前仅支持MDK开发 ### **资料获取** 网盘种主要包括RA8开发所需要的**常用开发工具**:[百度网盘资料链接](https://pan.baidu.com/s/1O38xjFGV2K1RP7tP1WlcvA?pwd=ra8v) **SDK 仓库地址(建议使用Github源):** https://github.com/RT-Thread-Studio/sdk-bsp-ra8d1-vision-board https://gitee.com/RT-Thread-Studio-Mirror/sdk-bsp-ra8d1-vision-board ### **视频教程** 【手把手带你入门 Vision Board 开发板】 https://www.bilibili.com/video/BV1Z6421M7We/?share_source=copy_web&vd_source=f952b354df3eb7cfe3fe8e8794d3ab70 ### 环境搭建 环境搭建请参考此文档:[Vision Board 环境搭建文档](https://docs.qq.com/doc/DY2hkbVdiSGV1S3JM) ## 三子棋思路 ### 找棋盘 **生成居中的九宫格区域** 函数 `generate_centered_rois` 的目的是生成一个九宫格的区域,每个区域都包含棋盘上的一个方块,并且这些区域在整个图像中居中。函数的参数包括图像的宽度和高度、每个方块的边长 `b` 以及感兴趣区域(ROI)的边长 `k`。 **函数定义** ``` def generate_centered_rois(width, height, b, k): rois = [] # 计算每个ROI中心的位置偏移 offset = (b - k) // 2 # 计算整个3x3矩阵的宽度和高度 total_width = 3 * b total_height = 3 * b # 计算左上角的起始点,使矩阵居中 start_x = (width - total_width) // 2 start_y = (height - total_height) // 2 for i in range(3): row = [] for j in range(3): x_center = start_x + j * b + b // 2 y_center = start_y + i * b + b // 2 x = x_center - k // 2 y = y_center - k // 2 row.append((x, y, k, k)) rois.append(row) return rois ``` **步骤解析** 1. **初始化区域列表**: ``` rois = [] ``` `rois` 是一个列表,用于存储每个 ROI 的坐标和大小。 2. **计算每个 ROI 中心的位置偏移**: ``` offset = (b - k) // 2 ``` 偏移量 `offset` 是为了确保 ROI 在每个方块的中心。`b` 是方块的边长,而 `k` 是 ROI 的边长。 3. **计算整个九宫格的总宽度和高度**: ``` total_width = 3 * b total_height = 3 * b ``` 因为九宫格有 3 行 3 列,每个方块的边长为 `b`,所以总宽度和高度分别是 `3 * b`。 4. **计算左上角的起始点**: ``` start_x = (width - total_width) // 2 start_y = (height - total_height) // 2 ``` 为了使九宫格在图像中居中,我们计算左上角的起始点 `start_x` 和 `start_y`。它们分别是图像宽度和高度减去九宫格的总宽度和高度的一半。 5. **生成九宫格区域**: ``` for i in range(3): row = [] for j in range(3): x_center = start_x + j * b + b // 2 y_center = start_y + i * b + b // 2 x = x_center - k // 2 y = y_center - k // 2 row.append((x, y, k, k)) rois.append(row) ``` - 使用双重循环生成每个 ROI 的坐标。 - `x_center` 和 `y_center` 计算每个方块的中心位置。 - `x` 和 `y` 是 ROI 的左上角坐标,减去 `k // 2` 是为了将 ROI 的中心对齐到方块的中心。 - 最后,将每个 ROI 添加到 `rois` 列表中。 6. **返回 ROI 列表**: ``` return rois ``` **视觉化解释** 假设图像的宽度和高度分别为 320 和 240,每个方块的边长 `b` 为 43,每个 ROI 的边长 `k` 为 10。九宫格将会在图像中居中,每个 ROI 将位于方块的中心,形成一个规则的 3x3 矩阵。 ### 找棋子 棋子识别的关键在于处理图像并根据灰度值来判断每个方块中是否存在棋子,以及棋子的颜色是黑色还是白色。这个过程在 `while` 循环中进行。 **代码段** ``` while(True): clock.tick() img = sensor.snapshot().lens_corr(1.8) # 图像识别得到棋盘数组 for y in range(len(rois)): for x in range(len(rois[y])): gray = img.get_statistics(roi=rois[y][x]).mean() if gray < 100: board[y][x] = "X" elif gray > 200: board[y][x] = "O" else: board[y][x] = " " ``` **步骤解析** 1. **抓取图像和镜头矫正**: ``` img = sensor.snapshot().lens_corr(1.8) ``` 使用 Vision Broad的摄像头捕获当前图像,并进行镜头矫正,以减少镜头畸变。 2. **遍历所有 ROI(感兴趣区域)**: ``` for y in range(len(rois)): for x in range(len(rois[y])): ``` 使用双重循环遍历之前生成的九宫格区域 `rois`。 3. **计算灰度值的平均值**: ``` gray = img.get_statistics(roi=rois[y][x]).mean() ``` 对每个 ROI 计算灰度值的平均值。`img.get_statistics(roi=rois[y][x])` 返回一个统计对象,包含了该 ROI 内的像素统计数据。通过 `mean()` 方法可以得到该区域的平均灰度值。 4. **判断灰度值来确定棋子**: ``` if gray < 100: board[y][x] = "X" elif gray > 200: board[y][x] = "O" else: board[y][x] = " " ``` - 如果灰度值小于 100,则认为该区域有一个黑色棋子(`"X"`)。 - 如果灰度值大于 200,则认为该区域有一个白色棋子(`"O"`)。 - 否则,该区域为空(`" "`)。 ### 博弈策略计算 #### minimax 函数 `minimax` 函数使用递归的方法评估所有可能的棋步,并为每个棋步计算一个分数,以确定最佳移动。 ``` def minimax(board, depth, is_maximizing): computer = 'X' player = 'O' if check_win(board, computer): return 10 - depth if check_win(board, player): return depth - 10 if check_draw(board): return 0 if is_maximizing: best_score = float('-inf') for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = computer score = minimax(board, depth + 1, False) board[i][j] = ' ' best_score = max(score, best_score) return best_score else: best_score = float('inf') for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = player score = minimax(board, depth + 1, True) board[i][j] = ' ' best_score = min(score, best_score) return best_score ``` **步骤解析** 1. **定义变量**: ``` computer = 'X' player = 'O' ``` 定义机器人棋子为 'X',玩家棋子为 'O'。 2. **检查终止条件**: ``` if check_win(board, computer): return 10 - depth if check_win(board, player): return depth - 10 if check_draw(board): return 0 ``` - 如果机器人赢了,返回一个正分(10 减去深度,越快赢分数越高)。 - 如果玩家赢了,返回一个负分(深度减去 10,越快输分数越低)。 - 如果平局,返回 0 分。 3. **递归搜索**: - **机器人最大化得分**: ``` if is_maximizing: best_score = float('-inf') for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = computer score = minimax(board, depth + 1, False) board[i][j] = ' ' best_score = max(score, best_score) return best_score ``` - 初始化 `best_score` 为负无穷。 - 遍历每个空位置,模拟机器人下棋。 - 递归调用 `minimax` 计算对方的最佳应对分数。 - 恢复棋盘状态,更新 `best_score`。 - 返回最佳分数。 - **玩家最小化得分**: ``` else: best_score = float('inf') for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = player score = minimax(board, depth + 1, True) board[i][j] = ' ' best_score = min(score, best_score) return best_score ``` - 初始化 `best_score` 为正无穷。 - 遍历每个空位置,模拟玩家下棋。 - 递归调用 `minimax` 计算机器人的最佳应对分数。 - 恢复棋盘状态,更新 `best_score`。 - 返回最佳分数。 #### computer_move 函数 `computer_move` 函数用来确定机器人下一步应该下到哪里。 ``` def computer_move(board): if board == [ [" "," "," "], [" "," "," "], [" "," "," "] ]: return 1,1 best_score = float('-inf') move = (-1, -1) for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = 'X' score = minimax(board, 0, False) board[i][j] = ' ' if score > best_score: best_score = score move = (i, j) if move != (-1, -1): print(f"Computer places X at ({move[0]}, {move[1]})") return move[0], move[1] ``` **步骤解析** 1. **初始化变量**: ``` best_score = float('-inf') move = (-1, -1) ``` 初始化 `best_score` 为负无穷,用来追踪最佳分数。`move` 用来记录最佳移动位置。 2. **遍历棋盘找空位**: ``` for i in range(SIZE): for j in range(SIZE): if board[i][j] == ' ': board[i][j] = 'X' score = minimax(board, 0, False) board[i][j] = ' ' if score > best_score: best_score = score move = (i, j) ``` - 遍历每个位置,如果是空位,模拟机器人在此下棋。 - 调用 `minimax` 计算此移动的分数。 - 恢复棋盘状态。 - 如果当前移动的分数大于 `best_score`,更新 `best_score` 和 `move`。 3. **返回最佳移动位置**: ``` if move != (-1, -1): print(f"Computer places X at ({move[0]}, {move[1]})") return move[0], move[1] ``` 如果找到最佳移动位置,返回该位置的坐标。 **总结** - **Minimax 算法**:通过递归搜索所有可能的棋步,评估每个棋步的得分,最大化机器人的得分,最小化玩家的得分。 - **确定最佳移动**:遍历所有可能的移动,调用 `minimax` 算法计算每个移动的得分,并选择得分最高的移动。 ### 赢者检测 赢者检测是通过 `check_win` 函数实现的。该函数会检查棋盘的行、列和对角线,判断某个玩家是否已经获胜。 **check_win 函数** ``` def check_win(board, player): for i in range(SIZE): if all(board[i][j] == player for j in range(SIZE)) or \ all(board[j][i] == player for j in range(SIZE)): return True if all(board[i][i] == player for i in range(SIZE)) or \ all(board[i][SIZE - 1 - i] == player for i in range(SIZE)): return True return False ``` #### 步骤解析 1. **检查行和列**: ``` for i in range(SIZE): if all(board[i][j] == player for j in range(SIZE)) or \ all(board[j][i] == player for j in range(SIZE)): return True ``` - 遍历每一行,检查该行是否全部是当前玩家的棋子。如果是,返回 `True`。 - 遍历每一列,检查该列是否全部是当前玩家的棋子。如果是,返回 `True`。 2. **检查对角线**: ``` if all(board[i][i] == player for i in range(SIZE)) or \ all(board[i][SIZE - 1 - i] == player for i in range(SIZE)): return True ``` - 检查主对角线(从左上到右下),是否全部是当前玩家的棋子。如果是,返回 `True`。 - 检查副对角线(从右上到左下),是否全部是当前玩家的棋子。如果是,返回 `True`。 3. **未找到获胜者**: ``` return False ``` - 如果没有找到任何一行、一列或对角线全部是当前玩家的棋子,返回 `False`。 ![7月31日(1) 00_00_00-00_00_30.gif](https://oss-club.rt-thread.org/uploads/20240731/82c567ddc40ed3d687a347db514b10bd.gif) 完整代码: ``` import sensor, image, time, lcd from pyb import Pin # 初始化传感器和LCD def init_sensor_and_lcd(): sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time=2000) sensor.set_hmirror(True) sensor.set_vflip(True) lcd.init() # 生成居中的九宫格区域 def generate_centered_rois(width, height, b, k): rois = [] offset = (b - k) // 2 total_width = 3 * b total_height = 3 * b start_x = (width - total_width) // 2 start_y = (height - total_height) // 2 for i in range(3): row = [] for j in range(3): x_center = start_x + j * b + b // 2 y_center = start_y + i * b + b // 2 x = x_center - k // 2 y = y_center - k // 2 row.append((x, y, k, k)) rois.append(row) return rois # 初始化变量 def init_variables(): distance = 43 block = 10 rois = generate_centered_rois(sensor.width(), sensor.height(), distance, block) board = [[" " for _ in range(3)] for _ in range(3)] return rois, board, block # 主函数 def main(): init_sensor_and_lcd() clock = time.clock() rois, board, block = init_variables() while True: clock.tick() img = sensor.snapshot().lens_corr(1.8) # 更新棋盘状态 update_board_from_image(img, rois, board) print_board(board) draw_board(img, rois, board, block) # 游戏逻辑 if check_win(board, 'O'): print("你赢啦!") elif check_win(board, 'X'): print("我赢啦!") elif check_draw(board): print("平局啦!") elif check_turn(board) == "X": line, row = computer_move(board) img.draw_cross(int(rois[line][row][0] + block / 2), int(rois[line][row][1] + block / 2), size=block, color=0) sensor.flush() else: print("该你下了!") imgs = img.to_rgb565() lcd.display(imgs) # 从图像更新棋盘状态 def update_board_from_image(img, rois, board): for y in range(len(rois)): for x in range(len(rois[y])): gray = img.get_statistics(roi=rois[y][x]).mean() if gray < 100: board[y][x] = "X" elif gray > 200: board[y][x] = "O" else: board[y][x] = " " # 打印当前棋盘状态 def print_board(board): for line in board: print(line) print() # 画出当前棋盘状态 def draw_board(img, rois, board, block): for y in range(len(rois)): for x in range(len(rois[y])): color = 127 # 默认颜色为灰色,表示空 if board[y][x] == "X": color = 255 # 白色 elif board[y][x] == "O": color = 0 # 黑色 img.draw_rectangle(rois[y][x], color=color) # 检查赢了吗 def check_win(board, player): for i in range(3): if all(board[i][j] == player for j in range(3)) or all(board[j][i] == player for j 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 check_draw(board): return all(board[i][j] != ' ' for i in range(3) for j in range(3)) # 计算策略得分 def minimax(board, depth, is_maximizing): computer = 'X' player = 'O' if check_win(board, computer): return 10 - depth if check_win(board, player): return depth - 10 if check_draw(board): return 0 if is_maximizing: best_score = float('-inf') for i in range(3): for j in range(3): if board[i][j] == ' ': board[i][j] = computer score = minimax(board, depth + 1, False) board[i][j] = ' ' best_score = max(score, best_score) return best_score else: best_score = float('inf') for i in range(3): for j in range(3): if board[i][j] == ' ': board[i][j] = player score = minimax(board, depth + 1, True) board[i][j] = ' ' best_score = min(score, best_score) return best_score # 计算下一步位置 def computer_move(board): if board == [[" " for _ in range(3)] for _ in range(3)]: return 1, 1 best_score = float('-inf') move = (-1, -1) for i in range(3): for j in range(3): if board[i][j] == ' ': board[i][j] = 'X' score = minimax(board, 0, False) board[i][j] = ' ' if score > best_score: best_score = score move = (i, j) if move != (-1, -1): print(f"Computer places X at ({move[0]}, {move[1]})") return move[0], move[1] # 检查该谁走了 def check_turn(board): x_count = sum(row.count("X") for row in board) o_count = sum(row.count("O") for row in board) return "X" if x_count == o_count else "O" if __name__ == "__main__": main() ```
7
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
wafu
这家伙很懒,什么也没写!
文章
2
回答
0
被采纳
0
关注TA
发私信
相关文章
1
有人做了rttread在MSP432p4上的移植吗?电赛想用
2
第十三届研电赛RT-Thread企业专项奖发布通知
3
Vision Board 兩個專案編譯問題
4
Vision-Board例程编译后不通过
5
Vision Board 示例7编译失败问题
6
Vision board OpenMV的摄像头画面有干扰条纹噪声
7
micropython能否增加一个tiny版本的yolov2目标检测功能?
8
Vision Board问题反馈:采集帧率低
9
RT-Thread Studio 编译OpenMV 工程出错 (RA8 Vision Board)
10
Vision Board如何接入外部摄像头
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
SFUD
msh
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
549
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部