前前后后折腾了快一个月,我终于把桌上这台小音箱从“光会聊天”变成了“能看会抓”的状态:喊一声“小智小智,帮我把左边那个红色方块拿过来”,它会回一句“好的,我看看”,然后转动摄像头确认目标,驱动三自由度机械臂平移过去,夹爪合拢把方块拎到你面前。整个闭环跑在一块 ESP32-S3 上——语音识别、视觉检测、机械臂控制三个任务,靠同一颗双核芯片协调完成。
这篇文章适合两类人:一是已经玩过 ESP32-S3 语音助手、想让这些“会说话的音箱”长出执行能力的 DIY 玩家;二是对机器人端到端链路感兴趣,想从硬件选型、引脚规划、代码结构到联调踩坑完整过一遍的嵌入式爱好者。我会把方案取舍、引脚规划、关键代码和调试经验都摊开讲,尽量让没接触过机械臂和视觉的朋友也能按图索骥。
1. 整体思路拆解:语音助手从“会说话”到“会动手”
1.1 小智这类项目的本质与扩展空间
先说说“小智”是什么。它本质上是跑在 ESP32-S3 上的一套开源语音助手框架,把唤醒词检测、麦克风采集、云端语音识别(ASR)、大模型对话、语音合成(TTS)串成一条完整链路。板子一上电,你叫一声唤醒词,它就能跟你多轮聊天。这个项目最有价值的地方在于把音频前后处理、网络连接、对话状态管理都封装好了,普通玩家不需要从零写音频驱动。
但它的短板也很明显:输出只有“说话”。对话内容再聪明,最后也只是从喇叭里传出来。所以要给它“装手臂和眼睛”,本质上是做两件事:第一,把聊天框架的输出从纯文本扩展成带动作指令的结构化数据;第二,在原本只用麦克风和喇叭的板子上,接入摄像头和舵机这些新外设,让 AI 的“决策”能落到物理世界。
这两件事单独拎出来都不算难,难的是它们挤在同一块 ESP32-S3 上。这颗芯片不是树莓派,内存有限、引脚有限、总线有限,语音和摄像头都要吃 DMA、都要占用大量内存,稍不注意就互相打架。这篇文章的核心其实就是讲:怎么在资源受限的情况下,把这三个子系统优雅地塞进同一个固件里。
1.2 端到端链路:听、想、看、做怎么协作
先明确整个系统的行为链路,我把它拆成六个环节:
- 唤醒阶段:本地检测到唤醒词,系统从低功耗空闲态进入工作态。
- 收音阶段:麦克风持续采集音频,流式上传到 ASR 服务,转成文字。
- 决策阶段:文字交给大模型,系统提示词里预先定义了机器人可执行的动作集合,模型输出结构化 JSON。
- 感知阶段:根据 JSON 里的指令,触发摄像头拍照,对画面做目标识别或颜色检测,得到目标在图像中的位置。
- 执行阶段:把目标位置换算成机械臂各关节角度,驱动舵机完成抓取动作。
- 反馈阶段:TTS 播报执行结果,系统回到空闲态。
这里要注意一个顺序问题:到底是“先看后动”还是“边看边动”。对小臂展的三自由度桌面机械臂来说,我推荐先看后动——先拍照定位,再开环执行抓取,抓完可以再拍一张做验证。边看边动需要实时的视觉伺服,那意味着摄像头要持续输出画面、算法要实时计算偏差、舵机要高频修正,对 ESP32-S3 来说负担太重,而且三自由度机械臂本身精度有限,闭环改了也未必稳。先看后动足够完成大多数“拿个方块”“推一下杯子”这类任务。
1.3 为什么选 ESP32-S3,而不是树莓派或普通 ESP32
我最初也纠结过要不要直接上树莓派,毕竟视觉和语音在 Linux 生态里成熟得多。但最终选了 ESP32-S3,原因有三个。
第一是成本与体积。一块带 8MB PSRAM 的 ESP32-S3 开发板只要几十块钱,树莓派加摄像头加麦克风的组合要贵出好几倍,体积也大不少。桌面小机器人图的就是紧凑,一块板子搞定所有事情。
第二是外设齐全。ESP32-S3 有 DVP 摄像头接口、I2S 音频接口、多路 LEDC PWM 定时器,Wi-Fi 和蓝牙也是标配。这意味着摄像头、麦克风、喇叭、舵机都可以直接挂在同一颗芯片上,不需要额外加 MCU 做中转。
第三是生态。小智这类语音框架本来就是为 ESP32-S3 写的,语音链路可以原样复用;esp32-camera 库和 Arduino 生态让摄像头和舵机驱动也有大量现成代码可以参考。
对比普通 ESP32(比如经典的 ESP32-WROOM-32),ESP32-S3 的优势更明显:普通 ESP32 虽然有摄像头接口,但常常要跟 Flash 引脚冲突,处理 JPEG 编码时内存也紧张;S3 的双核性能更强,带 PSRAM 的型号可以灵活分配帧缓冲,跑完语音再跑视觉也不至于直接 OOM。我这里用的是带 8MB PSRAM 的版本,强烈建议不要省这个钱,后面会解释为什么。
2. 硬件准备与电路设计:手臂、眼睛、嘴巴都要照顾好
2.1 核心硬件清单与选型逻辑
这个项目的硬件清单看起来长,但每样都有明确用途。我按子系统分组列出:
| 子系统 | 硬件 | 说明 |
|---|---|---|
| 主控 | ESP32-S3 开发板(带 PSRAM) | 建议选引出引脚较多的板型,方便同时接摄像头和舵机 |
| 语音输入 | INMP441 I2S 麦克风模块 | 数字麦克风,抗干扰好,接线简单 |
| 语音输出 | 小型 I2S 功放 + 喇叭 | 小智框架常用 ES8311 编解码方案,也可用 PAM8403 功放 |
| 视觉 | OV2640 摄像头模块 | 200 万像素,DVP 并口,esp32-camera 库原生支持 |
| 机械臂 | 3 个 MG996R 舵机 + 1 个 SG90 舵机(夹爪) | 底座、大臂、小臂各一个 MG996R,夹爪用 SG90 足够 |
| 电源 | 5V/2A 电源适配器 + 锂电池或 DC-DC 降压模块 | 舵机必须独立供电,不能直接吃开发板的 USB 电源 |
| 辅助 | 大容量电解电容、逻辑电平转换模块、舵机支架/3D 打印件 | 电容用于吸收舵机启动瞬间的大电流 |
开发板选型是第一个坑。市面上 ESP32-S3 开发板五花八门,有的把引出引脚画得很紧凑,接了摄像头就没地方接舵机了。我一开始用了一块迷你的 S3 板,结果发现能用的 GPIO 只剩六七个,只好换回引脚全引出的 DevKitC 风格板子。如果你用的是 ESP32-S3-BOX 系列或者专门的语音助手板,更要先确认摄像头排线接口和舵机引脚是否冲突。
机械臂部分,如果不想自己画结构件,直接买现成的三自由度桌面机械臂套件最省事,但要注意它的舵机型号和供电需求。我自己是 3D 打印的支架,舵机用 MG996R,虽然精度一般,但胜在扭矩足够、皮实耐操。夹爪部分很多套件用的是 SG90 加齿轮减速,抓轻质方块完全够用。
2.2 机械臂驱动方式:PWM 舵机与总线舵机的取舍
机械臂的关节驱动,市面方案基本分两类:传统 PWM 舵机和串口总线舵机。
传统 PWM 舵机(SG90、MG996R 这类)靠 50Hz 的 PWM 信号控制角度,脉冲宽度 500 到 2500 微秒对应 0 到 180 度。优点是非常便宜、资料多、Arduino 直接就能驱动;缺点是要每个舵机占一个 PWM 通道,而且角度控制是开环的,你没法知道舵机实际转没转到位,堵转时还会持续发热。
串口总线舵机(比如 Feetech 的 SCS/LX 系列、以及各种 6115 类总线舵机)走的是半双工 UART,可以级联多台,还能回传角度、温度、电压等状态。对机器人的“可靠执行”来说,回传状态太重要了——夹爪有没有夹紧、关节有没有堵转,PWM 舵机完全不知道,总线舵机一问便知。缺点也很直接:贵,单只价格通常是 MG996R 的好几倍,而且协议各家不一样,调试要先摸清手册。
我的建议是:预算允许并且想认真玩,至少大小臂用总线舵机;只是想把原型跑通、验证语音视觉联动的逻辑,用 MG996R 完全没问题。我目前这套是先拿 PWM 舵机做原型,等机械结构稳定后再升级总线舵机,因为前期最需要调的是软件逻辑,而不是舵机本身。
还有一个容易被忽略的点:舵机的扭矩选型。三自由度桌面机械臂每个关节要承受的扭矩,跟臂长和负载有关。粗略估算公式是“扭矩 ≈ 负载重量 × 重心到关节的距离”,再加上 1.5 到 2 倍的裕量。比如你的臂长 20 厘米,末端负载加上小臂自重等效 300 克,那肩关节需要的扭矩大约是 0.3kg × 20cm = 6kg·cm,MG996R 标称 9.4kg·cm(4.8V 下),实际打点折扣也够用。如果臂再长一点,就要考虑换成更大扭矩的舵机或加配重。
2.3 供电、引脚与信号完整性:最容易翻车的环节
供电是整个项目里我踩得最惨的坑。舵机启动瞬间电流很大,三四个舵机同时动作时,瞬时电流可能到 2A 甚至更高。如果舵机跟主控共用同一路 USB 5V 供电,电压一掉,ESP32-S3 直接重启,表现就是语音助手刚说完“好的,我看看”就黑屏了。
正确的做法是把电源彻底分开:一路 5V/2A 的电源给舵机,另一路干净的 5V 或者直接用 USB 给开发板供电,两路电源的地线要共地。舵机电源入口并联一个 470uF 以上的电解电容,最好再加一个 0.1uF 瓷片电容滤高频。如果是电池供电,DC-DC 降压模块的输出纹波也要留意。
引脚规划方面,我整理了一份参考分配表。注意这只是示例,不同开发板的默认引脚不同,一定要先查自己板子的原理图:
| 功能 | 引脚(示例) | 说明 |
|---|---|---|
| I2S 麦克风(INMP441) | GPIO 15(SCK)、16(WS)、17(SD) | 与 TTS 功放共用 I2S 总线时注意分配 |
| OV2640 数据线 D0-D7 | GPIO 4、5、6、7、8、9、10、11 | DVP 并口数据线,顺序不能错 |
| OV2640 控制信号 | GPIO 12(SCL)、13(SDA)、14(XCLK)、3(VSYNC)、2(HREF)、1(PCLK) | XCLK 必须接在支持时钟输出的引脚 |
| 舵机 1/2/3 | GPIO 40、41、42 | 走 LEDC 通道 |
| 夹爪舵机 | GPIO 43 | 单独一个通道 |
| 状态 LED | GPIO 48 | 指示系统状态 |
这里有三个关键点。第一,OV2640 的引脚一旦确定,基本就占死了十个左右的 GPIO,后续加外设前要先把摄像头的引脚需求列出来。第二,舵机信号线虽然只需要一根 GPIO,但强烈建议用带屏蔽的杜邦线或者双绞线,长度尽量短,否则舵机转动产生的电磁干扰会串到音频线路上,导致麦克风拾到“滋滋”声。第三,I2S 麦克风和功放如果共用总线,要靠 I2S 的左右声道区分,具体配置要跟小智框架的音频底层对齐,别自己另起炉灶。
3. 核心软件实现:语音、视觉、机械臂的完整闭环
3.1 语音链路改造:从“聊天”到“指令”
语音链路我直接复用了小智框架的现有能力:唤醒词在本地检测,唤醒后开始上传音频流到 ASR,识别出文字后交给大模型。我要改的核心是“大模型输出”这一环。
默认状态下,大模型的输出是一段自然语言,比如“好的,我帮你拿红色方块”。但机械臂听不懂这句话,我需要的是结构化指令。解决办法是在系统提示词里写清楚动作格式,要求模型输出 JSON:
你是一个桌面机器人助手。你可以用以下动作: - look_left / look_right / look_center:控制摄像头方向 - move_to(x, y):让机械臂移动到指定坐标 - grab():合拢夹爪 - release():张开夹爪 - speak(text):向用户播报文本 请根据用户指令输出 JSON,格式如下: {"action": "grab", "params": {"target": "red_block"}} 除了 JSON,不要输出任何其他内容。这样模型返回的就是干净的 JSON,在固件里用 cJSON 库解析,再映射到对应的执行函数。实际测试下来,大模型对这种结构化输出的理解相当稳定,只要提示词里把动作集合和参数范围写清楚,它一般不会乱来。
要注意的是,ASR 识别结果本身可能有错别字,比如“红色方块”识别成“红色方款”,所以大模型在解析时要有一定的容错能力。我的做法是让模型在 JSON 里额外返回一个“description”字段,描述它对目标的理解,方便我在调试时看它到底听懂了什么。
3.2 视觉链路:OV2640 图像采集与目标识别
视觉部分我用的是 esp32-camera 库。首先要正确初始化摄像头,关键参数包括像素格式、分辨率、JPEG 质量和帧缓冲数量。推荐用 JPEG 格式 + QVGA(320x240)+ 2 个帧缓冲,这样既节省内存又能保证传输速度。初始化代码大致如下:
#include "esp_camera.h" static camera_config_t camera_config = { .pin_pwdn = -1, .pin_reset = -1, .pin_xclk = 14, .pin_sccb_sda = 13, .pin_sccb_scl = 12, .pin_d7 = 11, .pin_d6 = 10, .pin_d5 = 9, .pin_d4 = 8, .pin_d3 = 7, .pin_d2 = 6, .pin_d1 = 5, .pin_d0 = 4, .pin_vsync = 3, .pin_href = 2, .pin_pclk = 1, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_QVGA, .jpeg_quality = 12, .fb_count = 2, .grab_mode = CAMERA_GRAB_LATEST, }; esp_err_t err = esp_camera_init(&camera_config); if (err != ESP_OK) { // 打印具体错误码,排查引脚配置 }拍照和取帧就更直接了:
camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { // 获取帧失败,多半是帧缓冲不足 return; } // fb->buf 是 JPEG 数据,fb->len 是数据长度 handleFrame(fb->buf, fb->len); esp_camera_fb_return(fb);识别目标我用的是“先本地颜色检测,必要时再上云端大模型”的两级方案。本地颜色检测适合识别纯色物体,比如红色、蓝色方块。思路是:把 JPEG 帧解码成 RGB 或直接用一个低分辨率的 RGB565 帧缓冲,然后扫描像素,用 HSV 颜色空间判断每个像素是否落在目标颜色区间内,最后计算所有命中像素的质心,就得到目标在画面中的大致坐标。这个方法在光照稳定的室内非常可靠,而且完全离线,不花流量。
如果目标不是纯色,或者需要理解场景语义(比如“找到那个圆形的金属杯子”),本地颜色检测就不够了。这时我把 JPEG 帧 base64 编码后,连同用户指令一起发给支持视觉的大模型,让它返回目标在画面中的归一化坐标。这个方案灵活但延迟高,一次请求可能多花两三秒,所以我只在本地检测失败时才触发。
3.3 机械臂控制:PWM 舵机与串口总线舵机的驱动细节
PWM 舵机在 ESP32-S3 上用 LEDC 驱动非常方便。舵机是 50Hz 的方波信号,周期 20 毫秒,脉宽 500 到 2500 微秒对应 0 到 180 度。ESP32 的 LEDC 可以用 16 位分辨率,把脉宽换算成占空比:
// 舵机 PWM 驱动(Arduino + ESP32-S3) #define SERVO_PIN_1 40 #define SERVO_PIN_2 41 #define SERVO_PIN_3 42 #define PWM_FREQ 50 // 舵机标准 50Hz #define PWM_RES 16 // 16 位分辨率 void servoBegin() { ledcSetup(0, PWM_FREQ, PWM_RES); ledcSetup(1, PWM_FREQ, PWM_RES); ledcSetup(2, PWM_FREQ, PWM_RES); ledcAttachPin(SERVO_PIN_1, 0); ledcAttachPin(SERVO_PIN_2, 1); ledcAttachPin(SERVO_PIN_3, 2); } void setServoPulse(uint8_t ch, uint16_t pulseUs) { // pulseUs 范围 500~2500,对应 0~180 度 uint32_t duty = (uint32_t)((pulseUs * 65535UL) / 20000UL); ledcWrite(ch, duty); } void setServoAngle(uint8_t ch, float angle) { uint16_t pulseUs = (uint16_t)(500.0f + (angle / 180.0f) * 2000.0f); setServoPulse(ch, pulseUs); }这段代码本身不难,难的是角度标定。每个舵机的 0 度未必对应脉宽 500 微秒,机械臂装配也会带来误差。我的经验是先写一个标定模式,逐个舵机发送不同脉冲宽度,记录实际角度,再建立一张角度到脉宽的映射表。这个映射表在后续运动学换算时非常重要,否则你让大臂转 45 度,实际可能只转了 40 度,累计下来夹爪根本对不准目标。
如果你升级到串口总线舵机,控制方式就变成通过 UART 发送指令帧。以市面上常见的总线舵机协议为例:
帧结构: [0x55][0x55][ID][Length][Cmd][Param...][Checksum] 其中 Checksum 的算法因厂商而异,常见的有: - 累加取低 8 位(本例中 ID+Length+Cmd+Param 累加 = 0x36) - 累加取反(0xFF - 0x36 = 0xC9)具体命令字、参数定义一定要以你手上舵机的官方协议表为准。总线舵机的优势在这里体现得淋漓尽致:你可以读取每个关节的角度和负载,判断夹爪有没有真的夹到东西。比如夹爪闭合后读取电流或位置反馈,发现位置没到位,就知道抓空了,可以重新尝试或提示用户。这个能力 PWM 舵机给不了。
3.4 把三个模块串起来:状态机与任务调度
三个子系统各自跑通了,最关键的步骤是把它们串成一个协调的整体。我的做法是用一个简单的状态机来管理整个任务流程,每个状态对应一组动作:
- 空闲态:麦克风待机,摄像头不工作,舵机保持在安全位姿。
- 唤醒态:检测到唤醒词,等待用户指令。
- 收音态:采集音频并上传 ASR。
- 决策态:大模型返回 JSON,解析指令。
- 感知态:触发摄像头拍照,做目标识别。
- 执行态:调用舵机驱动执行动作。
- 反馈态:TTS 播报结果,回到空闲态。
为什么用状态机?因为语音、视觉、机械臂这三个子系统分别跑在不同的 FreeRTOS 任务里,如果没有一个统一的状态管理,很容易出现“语音任务还在录音,视觉任务已经把摄像头打开了”这类资源竞争。状态机保证同一时刻只有一个子系统在占用关键资源。
具体到代码结构,我开了三个任务:音频任务负责麦克风和 TTS;视觉任务负责摄像头采集和识别;控制任务负责舵机驱动。三个任务之间通过事件标志组和队列通信。比如音频任务解析出 JSON 后,把动作指令塞进队列,控制任务收到后置位一个“需要视觉”的事件,唤醒视觉任务去拍照,视觉任务把识别结果通过队列回传给控制任务,控制任务再驱动舵机。
这里有个时序细节:拍照和舵机移动最好不要同时进行。舵机转动时的振动会导致画面模糊,所以我的流程是“先拍照,后移动”,拍照时舵机完全静止,移动时摄像头已停止采集。这样虽然牺牲了一点实时性,但识别成功率显著提高。
4. 联调实录与问题排查:那些文档里不会写的坑
4.1 延迟都去哪儿了:端到端耗时拆解
整个端到端流程跑通后,第一个感受是“慢”。从喊出指令到机械臂开始动,普遍要 4 到 6 秒。我专门给每个阶段打了时间戳,拆解结果大致是:唤醒检测约 0.3 秒,音频上传和 ASR 约 1 到 1.5 秒,大模型决策约 1 到 2 秒,拍照识别约 0.5 到 1 秒,舵机执行约 1 到 2 秒。
这里面最可控的优化点是 ASR 和大模型。ASR 可以选择更快的语音识别服务,或者使用流式识别,边说边返回文字,而不是等整句话说完。大模型的选型影响最大,同一个请求,不同模型的耗时能差一倍。我的建议是在系统提示词里限定输出长度、要求“简洁”,并且关闭不必要的思维链输出,让模型尽快返回 JSON。
另外,数据库缓存也值得做:高频指令(比如“拿起红色方块”)对应的 JSON 结果可以做本地缓存,下次遇到同样的指令直接跳过 ASR 和大模型,秒级响应。我用了一个简单的字符串匹配表,把常见指令模式直接映射到动作,只有遇到陌生指令才走完整链路。
4.2 舵机抖动、重启与供电问题
我遇到得最多的故障就是舵机一动作,ESP32-S3 就重启。最初怀疑是代码问题,后来用示波器量了电源,发现舵机瞬间把电压拉到 4V 以下。解决办法前面说了,一是舵机独立供电,二是电源入口并联大电容。但还有一个容易被忽略的细节:舵机信号线与电源线要分开走线,别绑在一起。
如果舵机出现抖动,先检查信号线上有没有干扰,再看 PWM 频率是否准确。有些便宜的舵机对脉冲宽度很敏感,脉宽差 10 微秒就会抖。我的经验是不要直接复制网上的“map(0,180,500,2500)”写死,而是针对每只舵机单独标定脉宽范围,然后把标定结果存到 NVS 里,每次开机自动加载。
还有一个很隐蔽的问题:舵机在机械臂碰到限位时堵转,电流飙升,不仅发热还可能烧坏舵机齿轮。所以代码里一定要加限位保护,在标定阶段记录机械结构的物理限位角,执行时把目标角度 clamp 在安全范围内。
4.3 摄像头吃资源导致的语音异常
摄像头对资源的占用比我预想的更狠。最典型的表现是:摄像头初始化之后,唤醒词检测的灵敏度明显下降,偶尔还会出现音频断流。
原因有两方面。第一,摄像头帧缓冲会占用大量 PSRAM,而语音处理也要用内存做音频缓冲,两者抢内存导致分配失败。第二,摄像头采集时 DMA 传输会占用总线带宽,对 I2S 音频的实时性造成影响。
解决思路有三个:一是尽量把摄像头帧率降到最低可用值,比如 5fps,只在需要识别时抓帧;二是在不需要视觉时彻底停掉摄像头,比如进入“等待唤醒”状态时就释放摄像头资源,等到指令解析出需要视觉再重新初始化;三是给音频任务更高的任务优先级,确保音频不被饿死。实践下来,第三点的影响最大,优先级设低了,音频断流几乎是必然的。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 舵机一动,板子重启 | 舵机供电不足,电压跌落 | 舵机独立供电,端口并联 470uF 电容,两路电源共地 |
| 舵机抖动不停 | 信号干扰或 PWM 脉宽标定不准 | 信号线远离电源线,逐舵机标定脉宽范围 |
| 摄像头初始化失败 | 引脚配置错误或 XCLK 引脚不对 | 对照原理图核对 pin 映射,换用开发板默认示例配置 |
| 唤醒词长时间无响应 | 摄像头抢内存导致语音任务异常 | 空闲时释放摄像头资源,调高音频任务优先级 |
| 识别成功率低 | 光照变化或目标颜色阈值太窄 | 在 HSV 空间留足阈值余量,必要时叠加云端视觉模型 |
| 机械臂抓不准目标 | 舵机角度误差累积,或标定映射表不对 | 逐关节标定角度-脉宽映射,目标坐标换算时加上补偿值 |
| TTS 声音卡顿 | 拍照和 TTS 同时占用 I2S/内存 | 时序上错开,拍照时暂停 TTS,或降低 JPEG 质量 |
排查这类问题,我的习惯是先在代码里埋好日志,每个状态切换、每次关键调用都打一行带时间戳的日志。联调时不要急着改代码,先把日志拉出来看故障发生的时刻系统在干什么,往往问题一下就清楚了。比如舵机重启那次,日志显示最后一条是“servo move to 120”,说明是舵机动作瞬间出的问题,供电方向排查就对了。
另外强烈建议准备一个万用表和一台便宜的逻辑分析仪。万用表量电压,逻辑分析仪看舵机信号和串口总线数据,很多模棱两可的“玄学问题”一测就原形毕露。我调试总线舵机时,就是靠逻辑分析仪发现发送帧的校验字节算错了,读出来的数据全是乱的。
结尾:一点个人体会
整套系统做完,我最深的感触是:端到端机器人的难点从来不在某一个子模块,而在资源协调和时间调度。语音、视觉、机械臂任意一个单独跑都没问题,一旦同时上电,内存、总线、电源、任务优先级全都变成制约因素。所以我的建议是务必分阶段推进:先跑通语音加舵机,再加摄像头,最后才做三者的完整闭环。每一步都确认稳定了再走下一步,否则出了问题你根本不知道是哪个环节引起的。
最后再分享两个小技巧。第一,给机械臂的所有关节设置软件限位,并在舵机启动前强制回中,能避免很多机械损坏和人身安全的隐患。第二,把视觉识别结果和舵机目标角度都通过日志打印出来,调试时肉眼观察“它看到了什么、打算怎么动”,比任何仿真都好用。这个项目后续我打算把 PWM 舵机换成总线舵机,再给摄像头加一个二自由度云台,让它能主动转头寻找目标,有兴趣的朋友可以一起交流。