RobotArm OpenMV 机械臂 — 三子棋教程

星瞳科技OpenMV视频教程 - RobotArm机械臂三子棋教程!

1. 简介

本教程演示如何使用 OpenMV 摄像头配合机械臂,实现三子棋的人机对弈。

项目代码库:OpenMV-Robot-Arm-Project-Tic-Tac-Toe Electromagnet

2. 例程文件说明

例程包含如下文件:

  • chess.py:三子棋解算代码,用于判断走棋和输赢
  • color_model.tflitelabels_color.txt:对棋子进行识别的神经网络分类模型
  • move.py:机械臂移动代码,主要负责抓取和放置
  • main.py:三子棋例程的主程序
  • Robot_Arm.py:机械臂模块
  • PDF文件:场景地图,A4纸打印

3. 使用电磁铁夹具

此例程使用电磁铁夹具:

提示:具体的夹具更换教程可查看夹具更换教程视频。

4. 场景准备

  1. 机械臂放置

将机械臂放置于地图的方框中央,机械臂底座与方框对齐:

  1. 棋盘放置

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

  1. 棋子摆放

注意:白棋在左,黑棋在右。

5. OpenMV 代码调试

  1. 文件准备

chess.pycolor_model.tflitelabels_color.txtmove.pymain.py 复制到 SD 卡目录中,运行 main.py

  1. 摄像头视角调整

上电运行代码,机械臂复位后,按下 3 号按键,摄像头视角如下图所示:存在识别框偏移现象。

参数配置

需要调整的参数如下所示:

# 九宫格参数配置
distance = 50   # 九宫格中每个方格的间距(像素)
block = 32      # 每个检测区域的边长(像素)  
ShiftX = 27     # 九宫格整体在X轴上的偏移量(像素)
ShiftY = -3     # 九宫格整体在Y轴上的偏移量(像素)

调整这 4 个参数,使其和棋盘九宫格一一对应即可:

6. 程序运行

注意:本三子棋实例均为黑棋先行。

  1. 操作步骤

  2. 运行代码,按下 1 号按键,机械臂设置为黑棋

  3. 运行代码,按下 2 号按键,机械臂设置为白棋
  4. 用户方每完成一步,需要按下按键告知机械臂完成走棋
  5. 对局结束后再次按动开关可重新开始

  6. 操作流程

遵循以下流程:

7. 核心部分讲解及问题解决

  1. 识别方案

在此例程中,对棋子使用 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

说明:进行多次识别,输出出现次数最多的结果。

  1. 九宫格检测框

九宫格检测框的作用是划分出各个三子棋的检测区域,类似于 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)

参数调整说明

  • 通过修改 ShiftXShiftY 来调整检测框的整体偏移
  • 修改 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 中代码不推荐修改,了解即可。

  1. move.py 模块说明

move.py 中写有机械臂的取三子棋和放三子棋的程序,便于在 main.py 中调用运行。当运行例程出现误差时,可以调整 move.py 中的代码解决此类问题。

  1. 常见问题:按键不准确现象

按键使用 AD 检测的方式,并不是采用数字采样,存在抖动现象,具体详见基础教程第 3.1 点。

results matching ""

    No results matching ""