MaixCam 井字棋机器人:视觉、决策与机械控制
MaixCam_Tic_Tac_Toe_2024 是我为 2024 年电赛 E 题参与设计和构建的井字棋机器人。仓库里的相机视觉、棋盘状态、博弈决策、交互界面和串口控制共同组成一套完整系统,不是只训练了一个棋子检测模型。
这个仓库要解决什么问题
机器人要在真实棋盘上与人对弈。它必须找到棋盘和棋子,判断对手本轮新增或移动了哪颗棋子,计算自己的落点,再控制 XYZ 三轴滑台完成取棋和落子。不同游戏模式还需要在屏幕上选择、显示和复位。
这里任何一层出错,最后都会表现成“机器人下错了”。视觉识别正确但坐标映射偏了,机械臂仍会落错;Minimax 计算正确但棋盘状态读错,决策也没有意义。因此我从一开始就按感知、状态、决策、控制和交互五部分组织方案。
我采用的整体方案
相机感知使用传统计算机视觉与 YOLOv5 组合。OpenCV 负责规则明确的棋盘轮廓、透视校正和九宫格定位,YOLO 处理背景变化更大的棋子。模型文件和 MUD 配置随仓库保存,MaixCam 可以直接加载推理。
感知结果不会直接变成串口命令,而是先转换成统一棋盘数组。BoardChangeDetector 比较前后两次稳定状态,区分新增、移动和无变化;游戏逻辑再根据当前棋局运行 Minimax,输出目标格编号。
控制层把格子编号映射为滑台坐标,通过 UART 与 XYZ 三轴机构通信。EZGUI.py 提供设备端界面组件,主程序组织模式选择、相机循环、状态显示和整局流程。api_new.py、board_checker_lite.py 则保留视觉和棋盘检查的拆分实现,方便单独调试。
整个代码架构和模块衔接都有我的参与。我要解决的是从相机画面一直到机械动作的闭环,而不是让某个算法在离线图片上单独运行。
棋盘和棋子为什么分开识别
棋盘是规则四边形,适合用 OpenCV 找轮廓、筛选面积和形状,确定四角后做透视变换。校正后的棋盘可以稳定划分成九格,格子中心也有明确几何意义。
棋子外观和现场背景更容易变化,尤其是棋盘外待抓取的棋子。我把这部分交给 YOLOv5,再将检测框中心映射到棋盘或取棋区域。两类方法的输出最终都转换成同一套位置定义。
这种组合方便现场排错。棋盘框不稳就检查阈值、轮廓和四角;棋子漏检就检查模型置信度和数据;格子判断错误则单独查看透视后的中心映射。所有问题都塞进一个模型,反而很难在比赛时间内定位。
状态检测怎么判断移动棋子
题目要求识别违规移动。单帧只能告诉我“现在每格是什么”,无法说明它从哪里来。BoardChangeDetector 因此保存上一次确认的棋盘,再和当前稳定结果比较。
如果空格变成某一方棋子,这是普通新增;如果原位置变空、另一位置出现同色棋子,就需要标记为移动;画面没有形成可信变化时,系统保持上一状态,不急着触发下一步。
这层状态缓冲还能抵抗手部遮挡、曝光变化和机械振动产生的短暂异常帧。机器人处理的是连续过程,不能把每一帧都当成一局全新的棋。
从 Minimax 到三轴滑台
井字棋状态空间很小,我使用 Minimax 搜索最佳落点。算法输入标准棋盘数组,输出格子编号,不接触相机坐标和串口细节。
控制层再完成棋盘格、图像坐标和机械坐标之间的映射,通过 UART 发送取棋、移动和落子指令。GUI 负责模式与状态显示,但不参与落点计算。这样出现偏差时,可以分别检查棋盘识别、决策结果、映射参数和机械执行。
仓库中仍有根据现场装置标定后写入的坐标参数。这对比赛版本是有效取舍,保证能在固定机位快速运行,但相机或机械结构改变后需要重新调整。
截至这次提交的复盘
当前版本已经跑通相机取图、棋盘校正、棋子检测、状态变化判断、Minimax 决策、UART 控制和设备界面这条链路。MaixPy、OpenCV、NumPy、YOLOv5、自写 GUI 和机械通信在同一个主循环里协同工作。
接下来最值得完善的是标定。可以把参考点选择、映射矩阵计算和参数保存做成独立流程,并为视觉、决策和串口分别保留可回放日志。这样更换机位后不必直接修改主程序。
这次比赛项目让我确认,机器人系统的准确性来自整条链路。模型只是感知的一部分,状态管理、坐标定义和模块之间的数据边界,同样决定它能不能稳定完成一局棋。




