RobotArm OpenMV 机械臂 — 三子棋教程
星瞳科技OpenMV视频教程 - RobotArm机械臂三子棋教程!
1. 简介
本教程演示如何使用 OpenMV 摄像头配合机械臂,实现三子棋的人机对弈。
项目代码库:OpenMV-Robot-Arm-Project-Tic-Tac-Toe Electromagnet
2. 例程文件说明
例程包含如下文件:
- chess.py:三子棋解算代码,用于判断走棋和输赢
- color_model.tflite 和 labels_color.txt:对棋子进行识别的神经网络分类模型
- move.py:机械臂移动代码,主要负责抓取和放置
- main.py:三子棋例程的主程序
- Robot_Arm.py:机械臂模块
- PDF文件:场景地图,A4纸打印

3. 使用电磁铁夹具
此例程使用电磁铁夹具:

提示:具体的夹具更换教程可查看夹具更换教程视频。
4. 场景准备
- 机械臂放置
将机械臂放置于地图的方框中央,机械臂底座与方框对齐:

- 棋盘放置
棋盘放置于A4纸九宫格上方:

- 棋子摆放

注意:白棋在左,黑棋在右。
5. OpenMV 代码调试
- 文件准备
将 chess.py、color_model.tflite、labels_color.txt、move.py、main.py 复制到 SD 卡目录中,运行 main.py。
- 摄像头视角调整
上电运行代码,机械臂复位后,按下 3 号按键,摄像头视角如下图所示:存在识别框偏移现象。

参数配置
需要调整的参数如下所示:
# 九宫格参数配置
distance = 50 # 九宫格中每个方格的间距(像素)
block = 32 # 每个检测区域的边长(像素)
ShiftX = 27 # 九宫格整体在X轴上的偏移量(像素)
ShiftY = -3 # 九宫格整体在Y轴上的偏移量(像素)
调整这 4 个参数,使其和棋盘九宫格一一对应即可:

6. 程序运行
注意:本三子棋实例均为黑棋先行。
操作步骤
运行代码,按下 1 号按键,机械臂设置为黑棋
- 运行代码,按下 2 号按键,机械臂设置为白棋
- 用户方每完成一步,需要按下按键告知机械臂完成走棋
对局结束后再次按动开关可重新开始
操作流程
遵循以下流程:

7. 核心部分讲解及问题解决
- 识别方案
在此例程中,对棋子使用 AI 神经网络进行识别。与码垛例程中不同的是,在三子棋例程中使用的是分类模型,而不是目标检测模型。
模型训练
模型可以使用 Impulse Edge 训练:
也可以使用星瞳 AI 训练器训练:
加载模型及归一化
with open('labels.txt','r') as file:
labels = [line.strip()for line in file if line.strip()]
print(labels)
model = ml.Model("model.tflite", load_to_fb=True)
print(model)
norm = ml.Normalization(scale = (0.0,1.0))
循环分类识别代码
下方代码为分类识别的代码:
def Net(img2):
results = [] # 存储识别结果
# 进行多次识别
for _ in range(30):
input = [norm(img2)] # scale 0~255 to 0.0~1.0
scores = model.predict(input)[0].flatten().tolist()
# 找到最高分标签
max_score = 0
max_label = None
for label, score in zip(labels, scores):
if score > max_score:
max_score = score
max_label = label
# 仅记录置信度高的结果
if max_score > 0.8:
results.append(max_label)
# 计算众数(最频繁出现的标签)
if not results:
return None
# 使用简单计数找出出现次数最多的标签
count_dict = {}
for label in results:
count_dict[label] = count_dict.get(label, 0) + 1
# 找出最高频率
max_count = 0
best_label = None
for label, count in count_dict.items():
if count > max_count:
max_count = count
best_label = label
return best_label
说明:进行多次识别,输出出现次数最多的结果。
- 九宫格检测框
九宫格检测框的作用是划分出各个三子棋的检测区域,类似于 ROI。调用 AI 神经网络对单独格子进行识别,连续 9 次,即可得到完整九宫格中落子的情况。
代码解析
generate_centered_rois 传入四个参数,分别为:
- 原图像的宽
- 原图像的高
- 检测框与检测框的距离
- 检测框的边长
# 九宫格参数配置
distance = 50 # 九宫格中每个方格的间距(像素)
block = 32 # 每个检测区域的边长(像素)
ShiftX = 27 # 九宫格整体在X轴上的偏移量(像素)
ShiftY = -3 # 九宫格整体在Y轴上的偏移量(像素)
# 生成九宫格的区域位置
def generate_centered_rois(width, height, b, k):
rois = []
# 计算整个3x3矩阵的宽度和高度
total_width = 3 * b
total_height = 3 * b
# 计算左上角的起始点,使矩阵居中
start_x = (width - total_width) // 2 + ShiftX
start_y = (height - total_height) // 2 + ShiftY
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
# 九宫格的区域位置
rois = generate_centered_rois(sensor.width(), sensor.height(), distance, block)
参数调整说明
- 通过修改
ShiftX和ShiftY来调整检测框的整体偏移 - 修改
block可以更改检测框的大小 修改
distance可以更改检测框间距三子棋解算代码解析
步骤 1:获取棋盘状态
首先会通过 get_color 函数,根据 AI 神经网络的识别结果,更新全局变量 board 中的数据:
def get_color():
# 图像识别得到棋盘数组
for y in range(len(rois)):
for x in range(len(rois[y])):
img2 = img.copy(roi=rois[y][x])
label = Net(img2)
print(label)
if label == "black":
board[y][x] = "black"
elif label == "white":
board[y][x] = "white"
else:
board[y][x] = None
步骤 2:判断胜负
调用 check_winner,根据 board 的数据,判断当前棋局的胜负情况:
if chess.check_winner(board) != None:
while True:
key = robot.ad_key_control()
print("当前胜利者为:",chess.check_winner(board))
img.draw_string(0,180,"WINNER:"+chess.check_winner(board),color=(255,0,0))
lcd.write(img.copy(hint=image.ROTATE_90))
if key !=0 and key != None :
machine.reset()
break
说明:
- 通过
if判断,若出现平局、机械臂胜、用户胜其中一个情况时,将会进入while循环 - 在终端循环打印胜者,LCD 屏幕上也会输出胜者
- 此时可以按下任意按键重启机械臂,重新开启棋局
步骤 3:AI 落子判断
若没有出现胜负已分的情况,将会进行下一步。
调用 get_current_player,判断当前轮到谁:
# 判断当前轮到谁
player = chess.get_current_player(board)
print("当前轮到:", player)
# 如果是AI执子(如white),则AI落子
if player == "black":
move_pos = chess.best_move(board, player)
print("AI选择位置:", move_pos)
if move_pos is not None:
y, x = move_pos
board[y][x] = player
move.get_piece_black(robot,Actuator,n)
move.move2pan(robot,Actuator,y,x)
n = n + 1
else:
# 这里可以等待人类输入落子(如通过按键或界面)
pass
while True:
key = robot.ad_key_control()
if key !=0 and key != None :
break
说明:用户(或机械臂)完成落子之后,会进入一个 while 循环等待按键指令,当按下按键才会跳出,进行下一轮落子。
步骤 4:棋局解算算法
chess.py 中,内部写有棋局解算算法,采用 minimax 算法搜索,解算棋局情况。
注意:
chess.py中代码不推荐修改,了解即可。
- move.py 模块说明
move.py 中写有机械臂的取三子棋和放三子棋的程序,便于在 main.py 中调用运行。当运行例程出现误差时,可以调整 move.py 中的代码解决此类问题。
- 常见问题:按键不准确现象
按键使用 AD 检测的方式,并不是采用数字采样,存在抖动现象,具体详见基础教程第 3.1 点。