
1. 项目缘起与整体设计思路1.1 为什么要做一块会自己走棋的棋盘下棋这件事很多人卡在同一个坎上想找个对手练手要么得拉人陪要么得对着屏幕点鼠标。屏幕下棋最大的问题是割裂感——你盯着显示器手在键盘上敲坐标眼睛和手不在同一个空间里脑子里的棋盘和屏幕上的棋盘对不上号。尤其是练开局和残局的时候这种割裂感会严重拖慢你的思考节奏。Chessgame 2.0 这个项目要解决的就是这个问题把 Stockfish 这个顶级开源引擎塞进一块实体棋盘里让棋子自己走。你在真实棋盘上落子机器识别你的走法引擎算出应对然后由机械结构把棋子挪到目标格。整个过程不需要你碰任何屏幕眼睛、手、棋盘三者是统一的。这个项目适合谁如果你玩过 Arduino、摸过 3D 打印机、对 CNC 或者写字机这类 XY 平台有点概念那这个项目对你来说就是“跳一跳够得着”的难度。它涉及的核心技术栈包括Stockfish 引擎做棋力计算、CoreXY 结构做平面定位、Arduino做底层运动控制、Raspberry Pi做上层调度和引擎通信。这几个东西单独拎出来都不算新但把它们捏在一起让一块木板上的棋子自己动起来这个组合本身就很有意思。我最早是在一个创客社区看到有人用磁力吸附的方式做自动棋盘当时觉得这个思路很妙——棋子底部嵌磁铁棋盘下面藏一个带电磁铁的 XY 滑台滑台移到棋子下方电磁铁通电吸住棋子拖到目标位置断电释放。整个过程棋子表面看不到任何机械连接视觉上就是“棋子自己飘过去了”。Chessgame 2.0 基本沿用了这个思路但在结构精度和控制逻辑上做了不少优化。1.2 核心方案选型为什么是 CoreXY 而不是其他结构做平面定位可选的结构有好几种笛卡尔式两个垂直的直线模组、并联臂三角洲、极坐标旋转臂、CoreXY。这个项目选 CoreXY理由很实在。笛卡尔式最直观X 轴模组驮着 Y 轴模组结构简单但问题是运动惯量大——Y 轴电机得跟着 X 轴一起跑速度上不去而且占空间。三角洲速度快但工作空间是圆形的棋盘是方的四个角够不着直接排除。极坐标结构紧凑但坐标变换复杂精度在边缘区域掉得厉害。CoreXY 的精髓在于两个电机都固定在框架上不参与移动通过同步带的交叉缠绕让两个电机的旋转组合出平面上的任意位移。这样做的好处是运动部件只有滑台和皮带惯量小加速度可以给得很高而且电机不动走线不会因为长期弯折而疲劳断裂。对于棋盘这种需要频繁快速移动的场景CoreXY 的响应速度优势很明显。具体到参数我用的皮带是 GT2 规格齿距 2mm配合 20 齿的同步轮电机转一圈走 40mm。步进电机是 1.8 度步距角也就是一圈 200 步细分设到 16那么一个微步对应 40mm / (200 * 16) 0.0125mm。这个精度对于棋盘格来说绰绰有余——标准棋盘格边长 50mm 左右0.0125mm 的定位误差肉眼根本看不出来。注意CoreXY 的皮带缠绕方式有固定套路绕错了会导致两个电机同向运动而不是差动。绕之前一定要对着标准图确认一遍我第一版就是绕反了调试了半天以为固件有问题。1.3 系统分层谁负责什么整个系统分成三层各司其职决策层是 Raspberry Pi跑的是 Linux 系统上面装 Stockfish 引擎。Pi 负责维护棋局状态、接收玩家走法、调用引擎计算、把引擎的应对走法翻译成坐标指令发给下层。为什么用 Pi 而不是直接用 Arduino因为 Stockfish 需要一定的算力和内存Arduino 那点资源跑不动而且 Pi 上可以用 Python 的 chess 库来管理棋局规则省去大量手写逻辑。控制层是 Arduino负责接收 Pi 发来的坐标指令做 CoreXY 的逆解运算生成两个步进电机的脉冲序列同时控制电磁铁的通断和 Z 轴的抬升。Arduino 的优势是实时性好脉冲时序稳定不会因为操作系统调度而抖动。执行层就是机械本体两个步进电机、同步带、直线导轨、电磁铁、Z 轴舵机或丝杆。这一层是纯物理的精度靠机械装配保证。三层之间通过串口通信Pi 发指令Arduino 回状态。协议很简单就是自定义的 ASCII 字符串比如MOVE X120 Y80表示移动到坐标 (120, 80)MAG ON表示吸住棋子MAG OFF表示释放。这种纯文本协议的好处是调试方便你可以直接用串口监视器手动发指令测试机械结构。2. 核心细节解析与实操要点2.1 Stockfish 引擎的集成与调用Stockfish 是一个 UCI 协议的国际象棋引擎它的工作方式是你给它一个局面FEN 字符串它返回一个最佳走法。在 Raspberry Pi 上你可以直接用apt安装也可以下载源码编译。我建议用apt装省事sudo apt update sudo apt install stockfish装完之后Stockfish 的可执行文件通常在/usr/games/stockfish。你可以手动跑一下测试echo -e uci\nposition startpos\ngo depth 10\nquit | /usr/games/stockfish它会输出一堆分析信息最后给出bestmove开头的行那就是引擎推荐的走法。在 Python 里调用 Stockfish最方便的是用python-chess库它内置了引擎管理功能import chess import chess.engine engine chess.engine.SimpleEngine.popen_uci(/usr/games/stockfish) board chess.Board() result engine.play(board, chess.engine.Limit(time1.0)) print(result.move) engine.quit()这段代码的意思是让引擎在 1 秒内思考返回它认为最好的走法。result.move是一个chess.Move对象你可以用move.from_square和move.to_square拿到起始格和目标格的索引再转换成棋盘坐标。这里有个关键点棋盘坐标和机械坐标的映射。国际象棋的格子用a1到h8表示a1 是左下角。你需要建立一个映射表把a1对应到机械坐标的某个点比如 (25, 25)然后每个格子间隔 50mm。这个映射关系一旦确定就不能随便改否则机械走位会全乱。实操心得我建议在棋盘上贴一层坐标纸先用笔标出每个格子的中心点然后手动控制滑台走到每个点记录下实际坐标。这样得到的映射表比纯计算更准因为机械装配总有误差。2.2 CoreXY 运动学逆解与步进电机控制CoreXY 的核心是两个电机的旋转量如何组合出平面位移。假设滑台要从当前位置移动到目标位置位移量为 (dx, dy)那么两个电机的转动量分别是电机 Adx dy电机 Bdx - dy这个公式是 CoreXY 的基础推导过程涉及皮带的几何关系这里不展开你只需要记住这个结论。实际代码里你需要把位移量转换成步数float steps_per_mm 200.0 * 16.0 / 40.0; // 80 步/mm long stepsA (dx dy) * steps_per_mm; long stepsB (dx - dy) * steps_per_mm;然后生成脉冲序列。Arduino 上控制步进电机最常用的是AccelStepper库它支持加减速能避免高速启停导致的丢步#include AccelStepper.h AccelStepper stepperA(AccelStepper::DRIVER, STEP_A_PIN, DIR_A_PIN); AccelStepper stepperB(AccelStepper::DRIVER, STEP_B_PIN, DIR_B_PIN); void moveTo(float x, float y) { float dx x - currentX; float dy y - currentY; long stepsA (dx dy) * steps_per_mm; long stepsB (dx - dy) * steps_per_mm; stepperA.move(stepsA); stepperB.move(stepsB); while (stepperA.distanceToGo() ! 0 || stepperB.distanceToGo() ! 0) { stepperA.run(); stepperB.run(); } currentX x; currentY y; }这段代码里move()是相对移动run()是让电机走一步。while循环会一直跑到两个电机都到位。实际使用中你需要设置合适的速度和加速度stepperA.setMaxSpeed(1000); stepperA.setAcceleration(500); stepperB.setMaxSpeed(1000); stepperB.setAcceleration(500);速度单位是步/秒1000 步/秒对应 12.5mm/s这个速度对于棋盘来说够用了。加速度设 500 步/秒²启停比较柔和不会丢步。注意CoreXY 的两个电机在运动时是联动的你不能单独控制某一个电机走直线。所有移动都必须通过moveTo()这样的函数来协调。如果你直接给某个电机发脉冲滑台会走斜线不是你想要的方向。2.3 电磁铁与 Z 轴的控制逻辑电磁铁负责吸住棋子Z 轴负责把电磁铁抬升到贴近棋盘底部的位置。为什么需要 Z 轴因为电磁铁的吸力随距离衰减很快如果电磁铁离棋子太远吸不住如果一直贴着棋盘底部移动时会摩擦噪音大且磨损快。所以正常移动时电磁铁降下来吸住棋子后抬升一点点让棋子稍微离开棋盘表面减少摩擦。Z 轴可以用舵机或者小型丝杆步进电机。舵机便宜、控制简单但行程有限通常只有 180 度对应几毫米的升降。丝杆步进电机精度高但成本也高。我用的是 9g 舵机通过一个简单的连杆机构把旋转转换成升降行程大概 5mm够用了。控制逻辑是这样的滑台移动到棋子正下方舵机转到低位电磁铁贴近棋盘底部电磁铁通电吸住棋子舵机转到高位棋子被微微抬起滑台移动到目标格舵机转到低位棋子落回棋盘电磁铁断电释放棋子舵机回到中位准备下一次移动这个顺序不能乱尤其是电磁铁的通断时机。如果棋子还没落稳就断电棋子可能会歪如果滑台还没到位就抬升棋子会被拖拽。void pickAndPlace(float fromX, float fromY, float toX, float toY) { moveTo(fromX, fromY); servo.write(SERVO_DOWN); delay(200); digitalWrite(MAG_PIN, HIGH); delay(300); servo.write(SERVO_UP); delay(200); moveTo(toX, toY); servo.write(SERVO_DOWN); delay(200); digitalWrite(MAG_PIN, LOW); delay(200); servo.write(SERVO_MID); }实操心得电磁铁的吸力要匹配棋子的重量。我一开始用的电磁铁吸力太小棋子走到一半掉了。后来换了一个 12V 的吸盘式电磁铁吸力大概 5kg吸一个 20g 的棋子绰绰有余。但吸力太大也有问题释放时棋子会被弹一下所以断电后要等一小会儿再移开滑台。3. 实操过程与核心环节实现3.1 机械结构的搭建与校准机械结构是整个项目的基础装不好后面全是坑。我的搭建顺序是先装框架再装导轨然后装皮带和电机最后装滑台和电磁铁。框架我用的是 2020 铝型材切成四根 500mm 和四根 400mm用角件连接成一个矩形。为什么选铝型材因为强度够、重量轻、加工方便而且上面有滑槽可以直接固定导轨和电机座不需要额外打孔。导轨用的是 MGN12 线性导轨两条分别固定在框架的左右两侧。滑台横跨两条导轨通过滑块连接。皮带走的是 CoreXY 的标准路径两个电机固定在框架的同一侧皮带从电机出来绕过几个惰轮交叉缠绕到滑台上。具体的绕法我画了个草图但这里没法贴图你可以搜“CoreXY belt path”找标准图。校准分两步机械校准和软件校准。机械校准是确保两条导轨平行、皮带张力均匀。我的做法是先不装皮带手动推滑台感觉阻力是否均匀。如果某个位置特别紧说明导轨不平行需要调整。然后装上皮带用手指拨动皮带感受张力。两边皮带的张力要尽量一致否则滑台会跑偏。软件校准是确定步数/mm 的准确值。理论值是 80 步/mm但实际因为皮带张力、轮径误差等因素会有偏差。我的校准方法是让滑台从 (0,0) 走到 (100,0)用尺子量实际走了多少。如果实际走了 98mm那么实际步数/mm 就是 80 * 100 / 98 81.63。把这个值写回代码再测一次直到误差小于 0.5mm。float steps_per_mm 81.63; // 校准后的值注意校准要在棋盘安装之前做因为棋盘装上之后滑台的运动范围会受限有些位置够不到。校准的时候让滑台走全行程确保每个角落都能到达。3.2 棋子识别方案的选择与实现棋子识别是这个项目里比较头疼的部分。你需要知道玩家把哪个棋子从哪个格子移到了哪个格子。方案有好几种方案一霍尔传感器阵列。每个格子下面放一个霍尔传感器棋子底部嵌磁铁。棋子移动时传感器检测到磁场变化就能知道哪个格子有棋子、哪个格子空了。这个方案精度高但成本也高——64 个格子需要 64 个传感器布线是个噩梦。方案二摄像头 图像识别。在棋盘上方装一个摄像头拍下棋盘照片用 OpenCV 做图像处理识别每个格子的棋子类型和位置。这个方案成本低但算法复杂光照变化会影响识别率。方案三RFID 标签。每个棋子底部贴 RFID 标签棋盘下面装 RFID 读卡器。这个方案识别准确率高但读卡器价格不便宜而且多个标签同时靠近时可能冲突。我最终选了方案一和方案二的混合用霍尔传感器检测格子上有没有棋子用摄像头识别棋子类型。这样霍尔传感器只需要检测“有/无”不需要区分棋子类型电路简单很多。摄像头只在玩家走棋后拍一次识别移动前后的差异确定是哪两个格子发生了变化。霍尔传感器的型号是 A3144数字输出有磁场时输出低电平无磁场时输出高电平。我把 64 个传感器排成 8x8 的矩阵用 74HC595 移位寄存器来读取减少引脚占用。Arduino 只需要 3 个引脚就能读取全部 64 个传感器的状态。// 读取霍尔传感器矩阵 byte sensorState[8]; void readSensors() { digitalWrite(LATCH_PIN, LOW); digitalWrite(LATCH_PIN, HIGH); for (int i 0; i 8; i) { sensorState[i] shiftIn(DATA_PIN, CLOCK_PIN, MSBFIRST); } }每个字节的每一位对应一个格子的状态。1 表示有棋子0 表示无棋子。这样一次读取就能拿到整个棋盘的状态。实操心得霍尔传感器对磁铁的极性有要求磁铁的南极和北极朝向不同传感器可能不触发。我一开始磁铁装反了传感器一直输出高电平以为传感器坏了。后来把磁铁翻了个面就好了。建议在焊接传感器之前先用一个磁铁测试一下触发方向。3.3 完整走棋流程的代码实现整个走棋流程是这样的玩家在实体棋盘上走棋霍尔传感器检测到变化Arduino 把变化上报给 PiPi 更新棋局状态调用 Stockfish 计算应对Pi 把应对走法转换成坐标指令发给 ArduinoArduino 控制机械结构完成走棋重复 1-5Pi 端的 Python 代码大致如下import chess import chess.engine import serial board chess.Board() engine chess.engine.SimpleEngine.popen_uci(/usr/games/stockfish) ser serial.Serial(/dev/ttyUSB0, 115200) def wait_for_player_move(): while True: line ser.readline().decode().strip() if line.startswith(MOVE): parts line.split() from_sq parts[1] to_sq parts[2] move chess.Move.from_uci(from_sq to_sq) if move in board.legal_moves: board.push(move) return move def make_engine_move(): result engine.play(board, chess.engine.Limit(time1.0)) move result.move board.push(move) from_sq chess.square_name(move.from_square) to_sq chess.square_name(move.to_square) ser.write(fMOVE {from_sq} {to_sq}\n.encode()) while not board.is_game_over(): wait_for_player_move() make_engine_move()Arduino 端收到MOVE e2 e4这样的指令后查表得到起始格和目标格的机械坐标然后调用pickAndPlace()完成走棋。void handleMove(String from, String to) { float fromX squareToX(from); float fromY squareToY(from); float toX squareToX(to); float toY squareToY(to); pickAndPlace(fromX, fromY, toX, toY); }squareToX()和squareToY()就是前面说的映射函数把a1到h8转换成毫米坐标。注意吃子的时候被吃的棋子需要先移出棋盘。我的做法是在棋盘旁边放一个“墓地”区域吃子时先把被吃的棋子拖到墓地再走自己的棋子。这个逻辑需要在 Pi 端处理因为 Pi 知道棋局规则知道哪一步是吃子。4. 常见问题与排查技巧实录4.1 机械类问题排查机械问题是最常见的也是最容易让人抓狂的。我整理了一个速查表现象可能原因排查方法解决方案滑台走斜线CoreXY 皮带绕错检查皮带路径是否交叉按标准图重新绕丢步加速度太大降低加速度测试设到 300-500 步/秒²定位偏差步数/mm 不准走 100mm 量实际距离重新校准皮带异响张力不均拨动皮带感受张力调整惰轮位置滑台卡顿导轨不平行手动推滑台感受阻力调整导轨固定螺丝丢步是最隐蔽的问题因为有时候丢一步你看不出来但走几步之后误差就累积了。我的经验是宁可慢一点也不要丢步。把加速度设低一点速度设到 800 步/秒左右基本不会丢步。还有一个坑是皮带松弛。皮带用久了会拉长张力下降导致定位不准。我建议每跑几百局就检查一下皮带张力松了就重新张紧。4.2 电气类问题排查电气问题通常表现为电机不转、电磁铁不吸、传感器不响应。电机不转先检查驱动器有没有使能。很多步进电机驱动器有一个 EN 引脚低电平使能高电平禁用。如果你没接这个引脚驱动器可能默认禁用电机就不会转。我的做法是把 EN 引脚直接接地让它一直使能。电磁铁不吸先量电压。电磁铁通常需要 12V 或 24V如果你用 5V 供电吸力会严重不足。另外电磁铁是感性负载断电时会产生反向电动势可能击穿 Arduino 的 IO 口。所以一定要加一个续流二极管比如 1N4007并联在电磁铁两端。传感器不响应先检查供电。霍尔传感器通常需要 5V 供电输出是开漏的需要上拉电阻。如果你直接接到 Arduino 的 IO 口可能因为上拉不足而读不到低电平。加一个 10k 的上拉电阻到 5V 就能解决。实操心得我建议在电磁铁的电源线上串一个电流表实时监控电流。正常吸合时电流大概 0.5A如果电流突然变大说明电磁铁短路了如果电流为零说明断路了。这个习惯帮我省了很多排查时间。4.3 软件类问题排查软件问题最让人头疼因为你看不到摸不着。最常见的几个串口通信失败。Pi 和 Arduino 之间的串口通信经常因为波特率不匹配或者串口被占用而失败。我的做法是在 Pi 上先用minicom或者screen手动测试串口确认能收发数据再跑 Python 脚本。另外Pi 的串口默认是给控制台用的你需要在raspi-config里关掉串口控制台才能用/dev/ttyAMA0。Stockfish 返回非法走法。这种情况通常是因为棋局状态和引擎状态不同步。比如你手动改了棋盘但没有告诉引擎。解决方法是每次调用引擎之前用board.fen()把当前局面发给引擎确保引擎看到的是最新状态。坐标映射错误。这个问题的表现是棋子走到错误的位置。排查方法是打印出每个格子的机械坐标和实际位置对比。我建议在棋盘上贴标签写上每个格子的坐标调试的时候一目了然。# 打印坐标映射表 for square in chess.SQUARES: name chess.square_name(square) x squareToX(name) y squareToY(name) print(f{name}: ({x}, {y}))这个表打出来之后你可以手动控制滑台走到每个坐标检查是否对准格子中心。4.4 棋局逻辑类问题排查棋局逻辑的问题通常和特殊走法有关王车易位、吃过路兵、兵升变。这些走法在机械执行时和普通走法不一样。王车易位涉及两个棋子移动王和车。你的代码需要检测到这是易位然后分别移动王和车。吃过路兵涉及三个格子起始格、目标格、被吃兵所在的格。兵升变涉及棋子替换兵走到最后一排后需要换成后、车、象或马。这些逻辑在python-chess库里都有现成的判断你只需要在走棋之前检查move对象的属性if board.is_castling(move): # 处理易位 pass elif board.is_en_passant(move): # 处理吃过路兵 pass elif move.promotion: # 处理升变 pass注意兵升变的时候你需要准备额外的棋子放在棋盘旁边。当兵走到最后一排时机械结构需要先把兵拿走然后把升变后的棋子放上去。这个操作比较麻烦我建议在软件里限制升变只能升后简化机械操作。5. 进阶优化与扩展玩法5.1 提升走棋速度的几个技巧默认的走棋速度是 12.5mm/s走一步大概需要 4-5 秒。如果你觉得慢可以尝试以下优化提高加速度。把加速度从 500 提到 1000启停更快但要注意丢步。我的经验是如果皮带张力够1000 是可以的。优化路径。CoreXY 走斜线比走直角快因为两个电机同时工作。你可以让滑台走对角线到目标附近再走直线微调。这样比纯直角路径快 20% 左右。预判下一步。Stockfish 计算需要时间你可以在玩家走棋的同时就让引擎开始思考。这样玩家走完引擎的应对已经算好了直接执行。并行操作。电磁铁的通断和滑台移动可以部分重叠。比如滑台还在移动时电磁铁就可以提前通电到位后直接吸住省去等待时间。5.2 联网对战与远程控制如果你想让这块棋盘和远方的朋友对战可以加一个联网模块。Pi 上跑一个 WebSocket 服务把棋局状态同步到网页上。对方在网页上走棋你的棋盘上就会自动走对应的步子。这个功能的实现思路是Pi 作为服务端维护一个棋局对象。本地玩家走棋时Pi 把走法广播给所有连接的客户端。远程玩家走棋时Pi 收到走法更新棋局然后控制机械结构走棋。import asyncio import websockets async def handler(websocket, path): async for message in websocket: move chess.Move.from_uci(message) if move in board.legal_moves: board.push(move) await websocket.send(board.fen()) make_engine_move() start_server websockets.serve(handler, 0.0.0.0, 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()这个方案的好处是你不需要公网 IP只要 Pi 能上网对方就能通过网页连过来。但要注意加一个简单的认证防止陌生人乱走棋。5.3 棋谱记录与复盘功能每一局棋都可以自动保存成 PGN 格式这是国际象棋的标准棋谱格式。python-chess库支持直接导出game chess.pgn.Game() node game for move in board.move_stack: node node.add_variation(move) with open(game.pgn, w) as f: f.write(str(game))保存下来的 PGN 文件可以用任何国际象棋软件打开比如 Lichess 或者 Chess.com 的分析工具。你可以复盘每一局看看哪里走错了引擎推荐的走法是什么。我个人的习惯是每局结束后让 Stockfish 分析一遍把失误的地方标出来。这样下棋不只是娱乐还是一个学习过程。5.4 外观美化与棋子改造机械结构做好之后外观也很重要。我用 3D 打印做了棋盘的边框把铝型材和导轨都藏起来只露出棋盘表面。棋子是标准比赛用棋底部钻了一个 5mm 的孔嵌入钕磁铁用环氧树脂固定。如果你想让棋子看起来更高级可以试试在棋盘表面贴一层绒布。绒布的好处是减少棋子移动时的噪音而且看起来更有质感。但要注意绒布的厚度太厚了会影响电磁铁的吸力。我用的绒布厚度是 1mm吸力损失大概 10%可以接受。实操心得磁铁的安装深度很关键。磁铁太靠上棋子底部会凸出来放不稳太靠下吸力不够。我的做法是在棋子底部钻一个深度刚好等于磁铁厚度的孔磁铁嵌入后和底面齐平然后用一小滴胶水固定。这样既不影响棋子站立又能保证吸力。6. 成本核算与物料清单6.1 主要物料与价格这个项目的成本可高可低取决于你用什么样的零件。我列一下我的配置和大致价格物料型号/规格数量单价元小计元Raspberry Pi4B 4GB1400400ArduinoUno R316060步进电机NEMA17250100步进驱动器A498821530线性导轨MGN12 500mm280160同步带GT2 6mm3m1030同步轮GT2 20齿4832电磁铁12V 5kg14040舵机SG9011515霍尔传感器A314464164移位寄存器74HC595818铝型材2020若干-100电源12V 5A15050棋子标准比赛用1套80803D 打印件边框、支架--50合计1219这个价格是大概估算实际可能会因为采购渠道不同而有浮动。如果你手头有一些零件成本会更低。6.2 省钱与替代方案如果你想控制成本有几个地方可以省用 ESP32 代替 Arduino Pi。ESP32 性能比 Arduino 强很多可以跑一些简单的逻辑甚至可以直接跑 Stockfish 的轻量版。但 ESP32 的实时性不如 Arduino脉冲时序可能不够稳。如果你不追求极致精度ESP32 是一个性价比很高的选择。用 3D 打印代替铝型材。如果你有 3D 打印机框架可以用打印件代替成本能省一半。但打印件的刚性不如铝型材高速运动时可能会有振动。用摄像头代替霍尔传感器。摄像头方案只需要一个摄像头不需要 64 个传感器和移位寄存器成本能省 70 元左右。但图像识别的开发难度更高需要处理光照、透视变形等问题。实操心得我建议第一次做的时候不要省钱用成熟的方案。等做成功了再尝试替代方案。我第一版用摄像头识别调了两个月都没调稳后来换成霍尔传感器一周就搞定了。时间也是成本。7. 最后分享几个调试小技巧调试这个项目我最大的体会是分模块调试不要一次性全接上。先单独测试步进电机确保能正反转再测试 CoreXY 运动学确保走直线然后测试电磁铁确保能吸能放最后测试传感器确保能读能写。每个模块都单独验证过再组合起来问题会少很多。另一个技巧是加日志。Arduino 的串口输出是你的眼睛每一步操作都打印出来比如“移动到 (120, 80)”、“电磁铁通电”、“传感器读取完成”。这样出问题的时候你能快速定位是哪一步卡住了。还有一个是手动模式。在代码里加一个手动模式通过串口发送指令直接控制滑台移动。比如发送JOG X10让滑台向右移动 10mm。这个功能在调试机械结构的时候特别有用你可以精确控制滑台走到任意位置检查是否对准。最后耐心。这个项目涉及机械、电子、软件三个领域任何一个地方出问题都会导致整体失败。我做了三个月才做到稳定运行中间踩了无数坑。但当你看到棋子自己动起来的那一刻所有的折腾都值了。