Soma Zero Tutorials
🔍 搜索功能尚未开启,敬请期待。

3.15 视觉对战五子棋实战

一、项目概览:让机械臂完成视觉对战五子棋

🎬 演示视频 · 在 Bilibili 打开

如上面短视频演示的,我们将实现用机械臂完成视觉对战五子棋的应用。

本节课代码:点击下载

1.1 本章要解决的核心问题

本章聚焦三个工程问题:

  • 如何把普通 USB 相机画面变成可用于机器人抓取的平面坐标
  • 如何让机械臂理解“图像中的位置”并准确到达
  • 如何把视觉检测、五子棋 AI 和机械臂控制串成完整闭环

1.2 物料清单

物料 参考图片 购买参考链接
带负压吸盘的六轴机械臂 任意满足条件的机械臂即可
USB 摄像头 720P 以上即可
棋盘格 + 棋子 参考棋盘尺寸:13 路围棋,棋盘尺寸:44 * 47 * 1.2 cm 。参考棋子大小:直径 2.2cm,厚度 0.6cm 。【京东】https://3.cn/2Ie-kzXN 「围棋套装 1913 路初学棋盘」

为什么使用普通 USB RGB 相机,而不是深度相机?

本项目使用的是普通 USB RGB 相机,不是深度相机,原因非常工程化:

  1. 棋盘和储棋区都是平面场景,且棋子高度固定
  2. 我们关心的是平面位置(x、y)与稳定抓取高度(z 预设),不依赖实时深度图
  3. 通过“透视标定 + 示教标定”,已能达到课程所需精度
  4. RGB 相机成本更低、部署更简单、兼容性更好

因此,这是一套“平面视觉 + 坐标映射”的低成本高可复用方案。

二、系统总架构与坐标变换主线

推荐相关课程:

2.1 全流程数据流

完整数据流可以概括为:

相机采集 → 透视校正 → YOLO 检测 → 网格映射/平面坐标 → 示教变换矩阵 → 机械臂执行

同时,AI 模块持续参与:

  • 人类落子检测后更新棋盘状态
  • AI 计算下一步落子点
  • 机械臂从储棋区取子并放到棋盘目标格

2.2 坐标系关系

本项目有两条并行坐标链:

  1. 棋盘链路:棋盘相机像素 → 棋盘平面坐标(mm) → 机械臂基坐标(mm)
  2. 储棋链路:储棋区相机像素 → 储棋平面坐标(mm) → 机械臂基坐标(mm)

他么只是区域不同,原理一模一样,统一可以写成:

[xryrzr1]=T[xpypzp1]

其中:

  • (xp,yp,zp) 是平面坐标系中的点(棋盘或储棋区)
  • (xr,yr,zr) 是机械臂基坐标系中的目标点
  • T 是通过示教点拟合得到的 4×4 变换矩阵

2.3 透视映射的核心直觉

相机通常是斜着拍棋盘,画面中的正方格会变成梯形。透视标定的作用是:

  • 用 4 个角点求出单应矩阵 H
  • 把“斜视图”拉正成“俯视图”
  • 让像素位置可稳定映射到实际尺寸坐标

常用形式为:

s[uv1]=H[uv1]

三、标定逻辑与原理

3.1 标定分两层:相机透视标定 + 示教标定

本项目不是传统工业视觉中的完整手眼标定流程(例如经典AX=XB模型),而是更贴合教学和实操的两步法:

  1. 相机透视标定:解决“看准平面坐标”
  2. 示教标定:解决“到准机械臂坐标”

两步组合后,就能完成从视觉到抓取的坐标闭环。

3.2 相机透视标定(棋盘区与储棋区)

3.2.1 目标

把相机像素中的任意点映射到平面坐标(mm),用于后续网格定位和抓取定位。

3.2.2 操作思路

  • 在图像里点击目标平面的四个角点
  • 指定该平面的物理尺寸(mm)
  • 计算透视矩阵并输出校正图
  • 保存 YAML 参数,供主程序加载

3.2.3 相关脚本

  • calibration_scripts/2.calibrate_perspective_camera_qt.py:棋盘区透视标定
  • calibration_scripts/3.live_cropped_feed.py:棋盘区透视效果验证
  • calibration_scripts/7.calibrate_storage_camera.py:储棋区透视标定
  • calibration_scripts/9.calibrate_storage_hand_eye.py:储棋区透视效果验证

3.2.4 典型输出

  • calibration_scripts/camera_calibration.yaml
  • calibration_scripts/storage_camera_calibration.yaml

包含四角点、平面尺寸、输出分辨率、透视矩阵等信息。

3.3 示教标定(棋盘区与储棋区)

3.3.1 目标

建立“平面坐标系 → 机械臂基坐标系”的刚体映射。

3.3.2 为什么叫示教标定

因为我们采用的是“人工示教点对齐”的方式:

  • 在平面坐标系中选定若干已知点
  • 手动/半自动控制机械臂末端到这些点的真实位置
  • 记录点对后拟合变换矩阵

本质是点到点映射拟合,更容易教学和落地。

3.3.3 相关脚本

  • calibration_scripts/4.calibrate_hand_eye_qt_ros2.py:棋盘区示教标定
  • calibration_scripts/9.calibrate_storage_hand_eye.py:储棋区示教标定
  • calibration_scripts/6.chess_piece_robot_controller.py:整链路联调验证

3.3.4 典型输出

  • calibration_scripts/hand_eye_calibration.yaml
  • calibration_scripts/storage_hand_eye_calibration.yaml

文件中核心是 4×4 变换矩阵 T 及标定点对。

四、运行流程

4.1 准备工作

  • 这个项目对机械臂精度要求较高,使用前需要紧固机械臂一些关节,降低间隙,尤其是:

    • 底座:紧固底座驱动板四个螺丝
    • 腰部:紧固侧边法兰螺丝
  • 使用 Episode1 ros2 包控制机械臂:ros2 run episode_controller interface --ros-args -p init_mode:=0 -p usb_index:=1

    注意,不是我们自己做的 py_episode,很多 topic 不一致。如要用,可自行修改对应名称。

  • 桌面布局要求:

    • 相机俯拍要求覆盖棋盘区域和储棋区(白纸区域)
    • 相机、棋盘格、机械臂固定后,就不要调整他们的相对位置了,否则需要重新标定
主视图 俯拍

4.2 棋盘区相机透视标定

  • 命令 python3 2.calibrate_perspective_camera_qt.py -c {camera_index}

    • 先用尺测量出机械臂末端可触达的棋盘区宽度和高度,比如我的是宽度为 12x10 的棋盘格,尺寸是 272x239 mm,填入 Board size。

      因为机械臂臂展有限,这个尺寸是测试出来的,你可以先随便选择一个,后面再通过示教标定程序试出来合适值

    • 想象你站在机械臂的位置,然后按照 Bottom_left -> Bottom_right -> Top_right -> Top_left 顺序点选棋盘格角点

    • 四点选择后,点击 Transform 按钮,计算变换矩阵,并 Save calibration 自动保存配置信息,比如我的:

      board_corners: - - 0.0 - 0.0 - - 272.0 - 0.0 - - 272.0 - 239.0 - - 0.0 - 239.0 board_size_mm: height: 239 width: 272 camera_resolution: height: 720 width: 1280 corner_names: - Bottom-Left - Bottom-Right - Top-Right - Top-Left output_size: height: 478 width: 544 ...
  • 棋盘区透视效果验证:python3 3.live_cropped_feed.py -c {camera_index}

    • 设置 board Rows/Cols 为你指定的,比如我们的是 11 x 13 (格子数量 +1)
    • 使用 Show grid overlay 覆盖绿色网格检查
    • 绿色线条必须和棋盘格匹配,否则重新透视标定,或者检查设置的参数
    • 绿色线条必须和棋盘格匹配,否则重新透视标定,或者检查设置的参数
    • 绿色线条必须和棋盘格匹配,否则重新透视标定,或者检查设置的参数
    • 放置一些棋子在棋盘区,看一下映射效果
    • precision 调整尽量使用我设置好的值,如果最后映射效果不错,即可关闭

4.3 棋盘区示教标定

  • 因为这个脚本需要使用 ROS2 相关包

    source install/setup.bash cd src/episode_apps/calibration_scripts/ python3 4.calibrate_hand_eye_qt_ros2.py
  • 想象你站在机械臂的位置,按照左下角(P1)、右下角(P2)、左上角(P3)、右上角(P4)顺序调整

    • 可以先将 Step size 调大一些,比如 50mm,然后快速到达指定位置,粗定位,再调小,精定位。最后尽量保证吸盘中心在棋盘格角点中心,类似下图

    • 保存点的坐标数据,然后回到默认位置(非常重要,否则途经点可能撞到棋盘,继续录入下一个点,直到所有 4 点数据全部录入,保存标定数据。

    • 对于已经保存好的数据,需要重新微调(减少减速器间隙影响)

      • 机械臂移动到 Home 位置
      • update 一个点,move to 位置,微调,使吸盘中心在棋盘格角点中心,保存
      • 操作下一个点
      • 保存所有

  • 验证 1:使用 Calculate and Move to Grid position 按钮检查,是否可以让末端移动到指定的棋盘网格。

  • 验证 2:使用 python3 6.chess_piece_robot_controller.py -c {camera_index},点选棋子,查看机械臂末端是否可以移动到棋子上方

4.4 储棋区透视标定

  • 命令:python3 7.calibrate_storage_camera.py -c {camera_index}
    • 类似棋盘区相机透视标定,想象你站在机械臂的位置,然后按照 Bottom_left -> Bottom_right -> Top_right -> Top_left 顺序点选棋盘格角点,将储棋区四个角点选择出来

    • 填写实际的储棋区尺寸,比如我的是 129x179mm

      这个储棋区尺寸也是试出来的(使用下一步的示教标定),如果面积太大了,机械臂可能到达不了(IK 无解)

    • 预览,保存校准数据

4.5 储棋区示教标定

  • 命令:python3 9.calibrate_storage_hand_eye.py -c {camera_index}

  • 类似棋盘区示教标定,想象你站在机械臂的位置,按照左下角(P1)、右下角(P2)、右上角(P3)、左上角(P4)顺序调整

    注意,和棋盘区示教标定不一样的是,最后两个点的顺序是反的,这个没有特殊原因,仅仅是代码写的时候没有同步。

  • 然后类似的,也需要一个个角点微调,最后保存校准数据。

  • 对于已经保存好的数据,需要重新微调(减少减速器间隙影响)

    • 机械臂移动到 Home 位置
    • update 一个点,move to 位置,微调,使吸盘中心在棋盘格角点中心,保存
    • 操作下一个点
    • 保存所有
  • 验证:在检测结果上点选棋子,看看机械臂能否移动到正上面(有一点误差不要紧,感觉能抓起来即可)

    如果这里误差感觉较大,可以重新进行:储棋区透视标定 和 示教标定 这两步,务必保证精度可用再往后走。

4.6 五子棋游戏体验

cd Gomoku_app python3 main.py

  • 这个程序可以设置行数和列数
  • 可以设置谁先手
  • 以及难度

Gomoku 引擎在项目中的角色

Gomoku_app/core_api_demo.py 提供统一接口,屏蔽了内部搜索和评估细节。

上层程序只需要:

  1. 写入人类落子
  2. 获取 AI 推荐落子
  3. 应用落子并检查胜负

这样视觉控制层和博弈算法层实现了解耦。

4.7 运行完整对弈主程序

4.7.1 上位机模式

  • 关闭机械臂 Ros2 控制,使用上位机控制

  • 启动完整下棋程序:python3 12.chess_demo_raw.py -c {camera_index}

    该程序与机械臂的通信,使用的是上位机 TCP 协议,不是 ROS2

    • 鼠标放到摄像头窗口上,快捷键:

      • q:退出
      • g:切换显示绿色网格效果
      • p:打印棋盘状态
      • o: 摆放测试,将储棋区棋子逐个放在棋盘上(从左下角开始)
      • h:让机械臂回到默认等待位置
    • 选择先手,为了提高检测准确度,储棋区只能用黑子

    • 选择难度:因算力有限,一般选择 easy、normal 模式

    • 一轮机器落子包含以下步骤:

      1. 相机采集当前画面
      2. YOLO 检测棋盘和储棋区棋子
      3. 识别人类新落子并更新棋盘状态
      4. AI 计算机器下一步落点
      5. 在储棋区定位可取棋子
      6. 机械臂吸取棋子
      7. 机械臂移动到目标格放置
      8. 检查胜负并进入下一轮

4.7.2 ROS2 模式(默认控制)

  • 关闭上位机,使用 ros2 控制机械臂:ros2 run episode_controller interface --ros-args -p init_mode:=0 -p usb_index:=1
  • 打开 URDF RVIZ:ros2 launch episode1_urdf_1113 launch.py
  • python3 13.chess_demo_ros2.py -c {camera_index} 运行下棋程序(界面与上位机一致)
  • RVIZ 中选择 MarkerArray,并选择对应 topic

  • 正常下棋,RVIZ 会映射状态

4.7.3 ROS2 模式(Moveit)

  • 使用 python3 14.chess_demo_ros2_moveit.py -c {camera_index}
  • 操作与上述方法一致,只不过没有仿真可视化

五、总结

本章核心收获可以概括为三句话:

  1. 使用普通 USB RGB 相机,在平面固定高度场景下同样可以完成稳定抓取任务
  2. 通过“透视标定 + 示教标定”,建立从图像到机械臂动作的完整映射
  3. 将视觉、AI 与控制解耦后,可以在不同控制架构(Raw/ROS2/MoveIt)下复用同一业务逻辑

下一步你可以基于同样框架,把“五子棋”替换成任意“检测-抓取-放置”的平面任务。