1. 项目缘起当传统机器人遇上AI视觉几年前我还在用一堆舵机、超声波传感器和红外避障模块捣鼓我的第一个“猜拳机器人”。那玩意儿能玩但体验很“机械”——它本质上是一个预设了随机出拳逻辑的自动装置跟“猜”字毫无关系更像一个会动的“石头剪刀布”抽奖机。玩家出拳后它再随机亮出一个结果胜负全靠运气互动感聊胜于无。我一直琢磨着能不能做一个真正能“看见”你出什么、然后“思考”并“回应”的机器人这需要给机器装上“眼睛”和“大脑”。直到我遇到了HUSKYLENS这款AI视觉传感器事情才变得简单起来。它把复杂的图像识别算法和机器学习模型封装进了一个小小的硬件里通过I2C或UART接口就能输出识别结果比如“识别到人脸”、“识别到特定物体”或者对我们这个项目至关重要的——“识别到手势”。这相当于直接把“眼睛”和一部分“大脑”打包好了我们只需要专注于“肢体”机械结构和“策略”游戏逻辑的设计。这个“AI猜拳机器人”项目就是基于HUSKYLENS的手势识别功能结合Arduino和舵机实现一个能实时识别玩家手势、并智能出招对抗的桌面伙伴。它不再随机出拳而是真正具备了“感知-决策-执行”的AI交互闭环。2. 核心部件选型与功能拆解要让机器人“活”起来我们需要几个关键部分协同工作感知输入的“眼睛”处理信息和决策的“大脑”以及执行动作的“手臂”。下面这张表格清晰地展示了我们这个项目的核心部件构成及其扮演的角色部件名称具体型号/类型核心功能在本项目中的角色与选型理由AI视觉模块HUSKYLENS手势识别、人脸跟踪、物体识别等项目的“眼睛”与初级“视觉皮层”。它内置了训练好的手势识别模型能直接输出“石头”、“剪刀”、“布”、“不确定”等结果省去了我们从零搭建和训练CNN模型的巨大工作量。其即插即用的特性是项目快速落地的关键。主控板Arduino Uno R3程序逻辑控制、传感器数据读取、舵机驱动项目的“大脑”与“神经中枢”。负责读取HUSKYLENS的识别结果运行猜拳游戏逻辑决定机器人出什么并生成PWM信号控制舵机动作。Uno板资源足够生态成熟是入门和原型开发的首选。执行机构9g微型舵机 x 3将电信号转换为机械角度项目的“手臂”与“手指”。三个舵机分别模拟机器人的拇指、食指和中指通过不同的角度组合形成“石头”握拳、“剪刀”伸出食指和中指、“布”五指张开三种手势。机械结构3D打印/激光切割件支撑与传动项目的“骨骼”与“关节”。用于固定舵机、主控板并将舵机的旋转运动转化为手指的开合动作。设计上需考虑运动范围、结构强度和美观度。辅助电源5V 2A直流电源为系统供电项目的“能量源”。舵机在动作时电流较大仅靠USB供电可能不足外接电源能保证系统稳定运行避免因电压跌落导致主控板重启或舵机抖动。注意HUSKYLENS本身有几种固件模式人脸识别、物体追踪、颜色识别等我们需要通过其屏幕和按键将其切换到“手势识别”模式。这是项目的第一步也是最容易忽略的一步。如果模式不对后续代码再正确也无法识别手势。2.1 为什么是HUSKYLENS对比传统方案在没有HUSKYLENS这类集成化AI传感器之前实现手势识别通常有两条路一是使用OpenCV Python在电脑上处理摄像头图像再通过串口把结果发给单片机二是使用像Kendryte K210这样的边缘计算芯片自己部署模型。前者需要一台始终连接的电脑系统笨重且延迟高后者开发门槛较高涉及模型转换、SDK调试等。HUSKYLENS的优势在于“集成”和“易用”。它把摄像头、处理芯片疑似ARM Cortex-M7、AI加速单元和算法模型全部做进了一个火柴盒大小的模块里。开发者无需关心图像预处理、特征提取、模型推理这些底层细节只需通过简单的串口指令就能获取识别结果。这极大地降低了AI视觉的应用门槛让我们可以像使用一个普通的超声波传感器一样使用它从而将精力集中在应用逻辑和机械设计上。对于教育、创客和快速原型开发来说这是一个革命性的工具。2.2 机械结构设计思路如何用三个舵机模拟手部人的手部有数十个关节用三个舵机完全复刻是不可能的也是不必要的。我们的目标是进行“特征抽象”用最简单的机械结构表达出“石头”、“剪刀”、“布”这三个手势最核心的视觉特征。我的设计思路是“三指联动”舵机A拇指控制拇指的弯曲。在“石头”和“布”时拇指弯曲贴紧手掌或与其他手指并拢在“剪刀”时拇指弯曲收起为食指和中指让出空间。舵机B食指控制食指的伸出与弯曲。舵机C中指控制中指的伸出与弯曲。通过给这三个舵机设定不同的角度值就能组合出目标形态石头A、B、C三个舵机均转动到“弯曲”角度使三根“手指”向内握拳。剪刀A舵机“弯曲”收起拇指B和C舵机“伸直”伸出食指和中指。布A、B、C三个舵机均转动到“伸直”角度模拟手掌张开。结构上我使用3D打印设计了手掌基座和指节连杆。舵机通过螺丝固定在基座内舵盘连接着指节连杆。连杆的设计需要仔细计算确保在舵机有效转角范围内通常0-180度手指能平滑地从完全弯曲运动到完全伸直且不会发生运动干涉或卡死。这里有个小技巧可以先在三维建模软件里进行运动仿真或者用硬纸板制作简易原型来验证运动轨迹再开始正式打印能节省大量时间和材料。3. 电路连接与系统供电方案硬件连接是项目的基础正确的接线和稳定的供电是项目成功的一半。下图清晰地展示了各模块间的物理连接关系----------------- | 5V 2A电源 | ---------------- | | (提供主电力) --------v-------- | Arduino Uno | | | | 5V---[HUSKYLENS]| | GND---[GND] | | SDA(A4)--[SDA] | | SCL(A5)--[SCL] | | | | 5V---------[舵机1红] | | | [舵机2红] | | ---[舵机3红] | | | | GND--------[舵机1棕] | | [舵机2棕] | ---[舵机3棕] | | | Pin9---[舵机1橙] | Pin10--[舵机2橙] | Pin11--[舵机3橙] -----------------连接详解与注意事项HUSKYLENS与Arduino通信我们使用I2C接口因为它只需要两根线SDA, SCL。将HUSKYLENS背面的拨码开关切换到“I2C”模式。然后用杜邦线将其SDA、SCL、5V、GND分别连接到Arduino Uno的A4(SDA)、A5(SCL)、5V、GND。务必注意HUSKYLENS的I2C地址默认为0x32如果遇到通信失败可以尝试在代码中修改地址或者检查线序是否接反。舵机连接这是供电的关键。每个微型舵机都有三根线红色电源、棕色地GND、橙色信号Signal。信号线分别连接到Arduino的数字引脚9、10、11。这些引脚支持PWM输出可以产生控制舵机角度的脉冲信号。电源线切勿将所有舵机的电源正极红都接到Arduino板载的5V引脚上Arduino Uno的板载稳压芯片最大只能提供约500mA电流。三个舵机同时动作特别是遇到阻力时瞬间电流可能超过1A这会导致Arduino板电压骤降、自动重启或者烧毁稳压芯片。正确的做法是使用一个外部5V 2A以上的直流电源其正极同时接到舵机群的红色线上负极GND接到舵机群的棕色线上并且与Arduino的GND相连共地。这样大电流由外部电源承担Arduino只负责提供控制信号。供电方案推荐使用带DC接口的5V 2A电源适配器通过Arduino的DC接口或VIN引脚为整个系统供电同时通过板载5V引脚给HUSKYLENS供电。如果使用移动电源请确保其输出能力足够。一个简单的测试方法是在舵机空载和带载装上手指结构运行时用万用表测量Arduino的5V引脚电压如果电压跌落超过0.2V说明供电不足需要更强力的电源。4. 软件逻辑从识别到决策的代码实现软件是机器人的“灵魂”它定义了机器人如何看、如何想、如何动。整个程序的逻辑流程图如下它清晰地展示了从启动到完成一轮猜拳的完整过程开始 | v 初始化串口、I2C、舵机、HUSKYLENS | v 等待HUSKYLENS初始化完成 | v [主循环开始] | v HUSKYLENS检测画面中是否有手势 | ------------------ |是 |否 v | 获取手势ID | 显示“请出拳” (石头/剪刀/布) | (屏幕提示或LED) | | v | 玩家手势是否稳定 | (连续N帧识别一致) | | | ------------------ |是 |否 v | 机器人决策出拳 | 返回继续检测 (调用策略函数) | | | v | 控制舵机执行手势 | | | v | 延时展示结果 | | | v | 判断胜负并反馈 | (LED/蜂鸣器) | | | v | 延时准备下一轮----接下来我们分步拆解核心代码。首先需要导入必要的库并定义一些常量和变量。#include Wire.h #include HUSKYLENS.h #include Servo.h // 定义HUSKYLENS手势ID根据实际识别结果调整 #define GESTURE_UNKNOWN 0 #define GESTURE_SCISSOR 1 // 剪刀 #define GESTURE_STONE 2 // 石头 #define GESTURE_PAPER 3 // 布 // 定义三个手指舵机对应的引脚 #define THUMB_PIN 9 #define INDEX_PIN 10 #define MIDDLE_PIN 11 // 定义舵机角度需根据实际机械结构校准 #define ANGLE_OPEN 180 // 手指伸直 #define ANGLE_CLOSE 0 // 手指弯曲 // 实例化对象 HUSKYLENS huskylens; Servo thumbServo; Servo indexServo; Servo middleServo; // 游戏状态变量 int playerGesture GESTURE_UNKNOWN; int robotGesture GESTURE_UNKNOWN; int stableCount 0; // 用于稳定检测的计数器 const int STABLE_THRESHOLD 10; // 连续10帧相同才认为手势稳定在setup()函数中我们需要完成所有硬件的初始化工作。void setup() { Serial.begin(115200); Wire.begin(); // 初始化HUSKYLENS while (!huskylens.begin(Wire)) { Serial.println(F(HUSKYLENS初始化失败请检查连接)); delay(1000); } // 切换到手势识别算法 huskylens.writeAlgorithm(ALGORITHM_GESTURE_RECOGNITION); // 初始化舵机 thumbServo.attach(THUMB_PIN); indexServo.attach(INDEX_PIN); middleServo.attach(MIDDLE_PIN); resetHand(); // 让手回到初始状态例如半握 Serial.println(F(系统初始化完成等待玩家出拳...)); }loop()函数是程序的核心它不断循环实现检测、决策、执行的流程。void loop() { // 1. 尝试从HUSKYLENS读取数据 if (huskylens.request()) { if (huskylens.available()) { HUSKYLENSResult result huskylens.read(); // 只处理手势识别结果 if (result.command COMMAND_RETURN_BLOCK result.ID ! 0) { int detectedGesture result.ID; // 假设ID 1剪刀2石头3布 // 2. 手势稳定检测 if (detectedGesture playerGesture) { stableCount; } else { playerGesture detectedGesture; stableCount 0; } // 3. 如果手势稳定则触发机器人响应 if (stableCount STABLE_THRESHOLD) { Serial.print(F(检测到稳定手势: )); printGestureName(playerGesture); // 4. 机器人决策出拳 robotGesture decideRobotGesture(playerGesture); // 5. 执行机器人手势 performGesture(robotGesture); // 6. 判断胜负并给出反馈 judgeAndFeedback(playerGesture, robotGesture); // 7. 重置状态准备下一轮 delay(3000); // 展示结果3秒 resetHand(); playerGesture GESTURE_UNKNOWN; stableCount 0; Serial.println(F(\n--- 下一轮 ---)); } } } else { // 未检测到任何手势 if (stableCount 0) stableCount--; // 缓慢复位 } } delay(50); // 控制循环频率约20Hz }4.1 核心函数解析决策、执行与反馈上面的主循环调用了几个关键函数它们是机器人智能和互动的体现。决策函数decideRobotGesture这是机器人的“大脑”。最简单的策略是随机出拳但那样就失去了“AI”对抗的趣味。我们可以实现一些简单的策略比如克制策略永远出能打败玩家上一手势的手势如果检测到石头就出布。这会让玩家觉得机器人在“针对”自己但很快会被玩家摸清规律。概率调整策略记录玩家出各种手势的历史频率机器人出拳的概率与之成反比。例如玩家经常出石头机器人就提高出布的概率降低出剪刀的概率。马可夫链策略根据玩家前一次的手势来预测下一次并做出克制预测的出手。这需要记录状态转移概率。这里实现一个简单的“胜率保持”策略作为示例机器人有一定基础概率随机出拳但如果检测到玩家连续两次出相同手势则第三次以高概率出克制它的手势。int decideRobotGesture(int playerGesture) { static int lastPlayerGesture GESTURE_UNKNOWN; static int sameGestureCount 0; // 策略逻辑 int chosenGesture; if (lastPlayerGesture playerGesture) { sameGestureCount; // 如果玩家连续出相同手势超过2次机器人倾向于克制它 if (sameGestureCount 2) { chosenGesture getCounterGesture(playerGesture); // 获取克制手势 Serial.println(F([策略] 检测到玩家重复出拳采取克制策略)); } else { chosenGesture randomGesture(); // 否则随机出 } } else { sameGestureCount 0; chosenGesture randomGesture(); // 玩家换手势了随机出 } lastPlayerGesture playerGesture; return chosenGesture; } // 辅助函数返回克制输入手势的手势 int getCounterGesture(int gesture) { switch (gesture) { case GESTURE_STONE: return GESTURE_PAPER; // 布克石头 case GESTURE_PAPER: return GESTURE_SCISSOR; // 剪刀克布 case GESTURE_SCISSOR: return GESTURE_STONE; // 石头克剪刀 default: return randomGesture(); } } // 辅助函数随机返回一个手势 int randomGesture() { return random(GESTURE_SCISSOR, GESTURE_PAPER 1); // 随机数范围[1,3] }执行函数performGesture这是机器人的“运动神经”。它将决策结果转化为具体的舵机角度。void performGesture(int gesture) { Serial.print(F(机器人出: )); printGestureName(gesture); switch (gesture) { case GESTURE_STONE: // 石头所有手指弯曲 thumbServo.write(ANGLE_CLOSE); indexServo.write(ANGLE_CLOSE); middleServo.write(ANGLE_CLOSE); break; case GESTURE_SCISSOR: // 剪刀拇指弯曲食指中指伸直 thumbServo.write(ANGLE_CLOSE); indexServo.write(ANGLE_OPEN); middleServo.write(ANGLE_OPEN); break; case GESTURE_PAPER: // 布所有手指伸直 thumbServo.write(ANGLE_OPEN); indexServo.write(ANGLE_OPEN); middleServo.write(ANGLE_OPEN); break; default: // 未知手势复位 resetHand(); break; } delay(500); // 给舵机留出运动时间 } void resetHand() { // 复位到初始姿势例如半握 thumbServo.write(90); indexServo.write(90); middleServo.write(90); }反馈函数judgeAndFeedback这是机器人的“表情”和“语音”通过灯光或声音告诉玩家结果。void judgeAndFeedback(int player, int robot) { int result (player - robot 3) % 3; // 巧用取模判断胜负 Serial.print(F(对决结果: )); switch (result) { case 0: Serial.println(F(平局)); // 平局反馈例如LED慢闪黄灯 digitalWrite(LED_BUILTIN, HIGH); delay(300); digitalWrite(LED_BUILTIN, LOW); delay(300); // 可以重复几次 break; case 1: Serial.println(F(玩家赢)); // 玩家赢反馈例如蜂鸣器响一声LED闪绿灯 tone(8, 1000, 200); // 引脚8接有源蜂鸣器 break; case 2: Serial.println(F(机器人赢)); // 机器人赢反馈例如蜂鸣器响两声LED闪红灯 tone(8, 800, 100); delay(150); tone(8, 800, 100); break; } }5. 调试与优化让机器人更“聪明”稳定代码烧录进去硬件组装完毕通电后机器人可能不会立刻完美工作。调试是项目从“能动”到“好用”的关键一步。5.1 HUSKYLENS手势识别校准与优化首先确保HUSKYLENS工作在正确的模式。长按其背面的“功能键”直到屏幕亮起通过短按切换算法直到屏幕显示“手势识别”的图标。接下来是识别优化环境光线HUSKYLENS对光线比较敏感。避免强光直射镜头也避免在过于昏暗的环境下使用。均匀的室内光是最好的。如果环境光复杂可以尝试调整模块上的白平衡在设置菜单中。手势距离与角度将手放在镜头前30-50厘米处手掌正对镜头。手势要清晰、完整地出现在画面中。可以打开HUSKYLENS的“学习”功能分别对“石头”、“剪刀”、“布”手势进行多次、多角度的标定能显著提高识别准确率。实操心得学习时不仅学习静态手势也可以稍微转动手腕让模型学习到不同视角下的特征鲁棒性会更好。减少误识别背景尽量简洁避免出现与肤色、手势形状类似的物体。如果发现容易将握着的鼠标误识别为“石头”可以在学习时特意将鼠标放在旁边作为“负样本”让模块学习忽略。5.2 舵机角度校准与运动平滑处理机械结构安装好后ANGLE_OPEN和ANGLE_CLOSE的数值需要实际校准。不要想当然地认为0度就是握紧180度就是伸直。单舵机校准写一个简单的测试程序让单个舵机从0度缓慢运动到180度观察手指的实际运动。找到手指完全伸直且机械结构不卡顿的角度记为ANGLE_OPEN找到手指完全弯曲握拳的角度记为ANGLE_CLOSE。这两个值很可能不是0和180。运动平滑直接让舵机从当前角度跳到目标角度动作会显得生硬、抖动。可以通过for循环实现渐进运动让动作更拟人。void smoothMove(Servo servo, int fromAngle, int toAngle, int stepDelay 20) { int step (fromAngle toAngle) ? 1 : -1; for (int angle fromAngle; angle ! toAngle; angle step) { servo.write(angle); delay(stepDelay); } servo.write(toAngle); // 确保到达最终位置 }在performGesture函数中调用smoothMove代替直接的servo.write()机器人的动作会立刻变得优雅很多。5.3 游戏逻辑与交互体验优化基本的胜负判断是核心但好的交互体验在于细节。增加“准备-开始”状态不要一上电就立刻开始识别。可以增加一个物理按钮或者通过识别一个特定的“开始手势”比如在镜头前挥手来启动一轮游戏。这给了玩家一个明确的交互起点。引入状态机将程序从简单的loop改为明确的状态机逻辑会更清晰。例如定义STATE_IDLE待机、STATE_DETECTING检测中、STATE_DECIDING决策中、STATE_ACTING执行中、STATE_FEEDBACK反馈中等状态。状态机使得程序更容易扩展例如未来加入倒计时、动画效果等。丰富反馈机制除了LED和蜂鸣器可以增加一个OLED小屏幕显示“请出拳”、“我出的是XX”、“你赢了/我赢了”等文字甚至显示简单的表情符号交互感会大大增强。也可以加入MP3模块播放录制好的语音如“看我的”、“哈哈我赢了”。策略可调可以将机器人的策略难度做成可调的。例如通过一个旋钮电位器读取模拟值映射到不同的策略模式随机模式、简单克制模式、高级学习模式。这样玩家可以挑战不同难度的机器人。6. 项目扩展与进阶玩法一个基础的AI猜拳机器人完成后它的可能性才刚刚开始。这里有几个扩展方向可以让项目从“玩具”升级为更酷的“作品”。方向一多模态交互与情感表达增加语音模块集成SYN6288或XFS5152语音合成模块让机器人不仅能“看”能“动”还能“说”。在出拳前说“我准备好了”赢了之后说“承让承让”输了之后叹气互动沉浸感直接拉满。增加表情屏幕用一个TFT彩屏或点阵LED屏作为机器人的“脸”根据游戏进程显示自信、惊讶、得意、沮丧等表情动画赋予机器人个性。方向二联网与数据智能接入物联网平台通过ESP8266/ESP32替换Arduino Uno让机器人连接Wi-Fi。每次对局的结果玩家手势、机器人手势、胜负都可以上传到云平台如阿里云IoT、ThingsBoard。你可以搭建一个简单的仪表盘统计你和机器人的历史胜率、出拳习惯偏好比如你是不是特别喜欢出石头。实现“云大脑”将决策逻辑放在云端服务器上。机器人只负责采集手势图像或结果并上传服务器运行更复杂的AI模型例如分析玩家出拳的犹豫时间、微表情等来决定出拳策略再将指令下发给机器人。这实现了边缘感知、云端决策的架构。方向三机械结构与竞技性升级增加身体与头部用更多的舵机为机器人增加一个可以左右转动的头部让它的“眼睛”HUSKYLENS可以跟踪移动的手。再增加一个腰部旋转机构让它在获胜时可以扭动“身体”庆祝。设计对战擂台制作两个机器人让它们通过摄像头识别对方的手势进行真正的“AI对AI”猜拳大赛。这需要解决相互识别和同步问题会非常有趣。融合其他游戏将手势识别用于更复杂的游戏比如“猜拳擂台赛”三局两胜、“手势记忆游戏”机器人快速展示一串手势序列让玩家重复等。方向四教学与展示优化代码模块化与封装将硬件驱动、游戏逻辑、AI策略分层封装成库方便其他开发者复用。例如可以创建一个GestureRobot类提供begin(),playRound(),setStrategy()等接口。可视化调试界面利用Arduino的串口绘图功能或者通过串口将识别数据发送到电脑上的Processing、Python程序实时绘制出手势识别框、置信度、决策概率等数据流非常适合用于教学展示和算法调试。这个项目就像一个乐高底座HUSKYLENS提供了最核心的视觉感知能力而围绕它搭建什么样的机械、赋予何种性格、增加哪些互动完全取决于你的创意。从“识别-反应”到“感知-思考-交互-表达”每一步的深化都是对嵌入式系统和AI应用理解的加深。我自己的机器人在增加了语音和联网统计后已经成了工作间里一个颇受欢迎的减压小物件偶尔有朋友来访总会忍不住跟它玩上几把而背后的数据记录又成了我们茶余饭后分析“人性”的谈资。技术的乐趣莫过于此。