CogletESP-camera-version/Coglet项目分析与开发指南.md
Rdzleo f1c2bfce93 Phase 01 JPEG Dump 诊断 + YVYU 修正 + 矛盾分析汇总
核心变更:
- face_tracker.cc: YUYV→YVYU 序列修正(byte[1]=V, byte[3]=U),
  基于 JPEG Dump 诊断工具验证 OV3660 FORMAT_CTRL00=0x61 实际是 YVYU
- face_tracker.cc: 启动时 base64 打印一帧 JPEG 到串口,用于肉眼验证
- config.h: XCLK 20MHz→10MHz,给飞线信号完整性 2x 裕度
- scripts/auto_capture_jpeg.py: 自动串口抓帧工具(DTR/RTS 复位 + base64 解码)
- scripts/extract_jpeg_from_log.py: 从日志离线提取 JPEG
- Coglet项目分析与开发指南.md: 新增"六点六"章节,汇总 Phase 01
  主要矛盾(画面可辨识≠模型可识别)、YUV→RGB 色偏三层原因、
  esp-dl 模型输入分布敏感性、延迟分析、三方案对比、方案 B 突破口
- docs/: 新增 2 篇 OV3660 相关 CSDN 参考资料

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:01:02 +08:00

827 lines
42 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Coglet 项目分析与开发指南
## 一、项目简介
**Coglet** 是 YouTube 创作者 Will Cogley原 Nilheim Mechatronics开发的 **DIY 桌面 AI 伴侣机器人**,目前在 Kickstarter 众筹中。项目包含 3D 打印外壳、PCB 电路板和双 MCU 软件系统。
### 系统架构
采用**双 MCU 架构**,三个核心模块通过 UART 串联:
```
ESP32-S3 ←── UART 115200 ──→ RP2040 (Pico) ←── UART 921600 ──→ Grove Vision AI V2
(AI/语音/网络) (舵机/动画) (摄像头/人脸检测)
```
| 芯片 | 职责 |
|------|------|
| **ESP32-S3** | AI 对话、语音识别/合成、WiFi 网络、MCP 协议(基于小智 XiaoZhi 固件) |
| **RP2040 (Raspberry Pi Pico)** | 9 个舵机控制、动画表情、人脸追踪MicroPython |
| **Grove Vision AI V2** | 摄像头人脸检测 |
### ESP32 固件核心功能
- 连接方式WiFi / ML307 Cat.1 4G
- 语音交互:流式 ASR + LLMQwen/DeepSeek+ TTS
- 离线唤醒ESP-SR 本地语音唤醒
- 通信协议WebSocket 或 MQTT+UDP
- 音频编码OPUS
- 显示OLED/LCD + 表情动画
- 多语言:中文/英文/日文
- MCP 协议:设备端 MCP控制扬声器、LED、舵机、GPIO+ 云端 MCP智能家居
### RP2040 控制的舵机(共 9 个)
| 舵机代号 | 功能 |
|---------|------|
| EYL / EYR | 左右眼水平转动 |
| PIT | 头部俯仰 |
| YAW | 头部偏航 |
| MOU | 嘴巴开合 |
| LID | 眼睑眨眼 |
| 其他 | 耳朵舵机等 |
### RP2040 状态机
`idle``speaking``listening``thinking``happy``neutral``calibration`
---
## 二、GitHub 仓库总览
### 核心仓库
| 仓库 | 地址 | Stars | 说明 |
|------|------|-------|------|
| **Coglet 主仓库** | https://github.com/will-cogley/Coglet | 135 | 3D 打印件、PCB 原理图、文档 |
| **CogletESP 固件** | https://github.com/will-cogley/CogletESP | 5 | ESP32 固件 + RP2040 代码fork 自小智) |
### 相关参考仓库
| 仓库 | 地址 | 说明 |
|------|------|------|
| 小智上游原版 | https://github.com/78/xiaozhi-esp32 | CogletESP 的上游项目 |
| EyeMech_Epsilon | https://github.com/will-cogley/EyeMech_Epsilon | Coglet 眼球机构的技术前身RP2040 + MicroPython |
### CogletESP 分支列表
| 分支名 | Commits | 说明 |
|--------|---------|------|
| `main` | 789 | 上游同步的主分支 |
| **`CogletESP`** | 758 | **Coglet 专用分支(无 ESP32 摄像头版本,保留 LCD 显示)** |
| **`camera-version`** | 755 | **ESP32 OV3660 摄像头版本(需 PCB 飞线,禁用 LCD** |
| `esp32_camera` | - | ESP32 摄像头分支 |
| `v1` | 669 | v1 稳定版 |
| `bed-operator` | - | 功能分支 |
| `fix_resampler` | - | 重采样器修复 |
| `fix_rndis` | - | RNDIS 修复 |
| `listening_wakeword` | - | 唤醒词监听 |
| `nt26_board` | - | NT26 开发板适配 |
### CogletESP 分支 vs camera-version 分支(关键区别)
> **重要区分**:这里的"摄像头"指 **ESP32-S3 上直连的 OV3660 DVP 摄像头**(小智 AI 视觉功能),**不是** Grove Vision AI V2。Grove Vision AI V2 是独立模块接在 RP2040 上,两个分支都支持它。
两个分支仅 **2 个文件**不同,都在 `main/boards/bread-compact-wifi-s3cam/` 目录下:
| 对比项 | `CogletESP` 分支 | `camera-version` 分支 |
|--------|------------------|----------------------|
| ESP32 摄像头(OV3660) | 默认引脚,与 PSRAM 冲突 | 飞线后的新引脚映射 |
| LCD 显示 | 正常支持 | **禁用**(引脚被摄像头占用,用 DummyDisplay 替代) |
| PCB 改动 | 无需飞线 | **需要 3 根飞线**35→14, 36→41, 37→42 |
| 摄像头画面 | - | 设置 HMirror + VFlip 翻转 |
| JTAG 引脚(GPIO 39-42) | 默认用途 | 释放给摄像头使用 |
| Grove Vision AI V2 | 支持 | 支持 |
#### GPIO 引脚映射差异(部分关键引脚)
| 功能 | `CogletESP` 分支 | `camera-version` 分支 |
|------|------------------|----------------------|
| MIC_WS | GPIO 1 | GPIO 4 |
| MIC_SCK | GPIO 2 | GPIO 5 |
| MIC_DIN | GPIO 42 | GPIO 6 |
| SPK_DOUT | GPIO 39 | GPIO 7 |
| SPK_BCLK | GPIO 40 | GPIO 15 |
| SPK_LRCK | GPIO 41 | GPIO 16 |
| LED | GPIO 48 | GPIO 3 |
| CAMERA D7 | GPIO 16 | GPIO 14飞线 35→14 |
| CAMERA VSYNC | GPIO 6 | GPIO 41飞线 36→41 |
| CAMERA HREF | GPIO 7 | GPIO 42飞线 37→42 |
#### 如何选择分支?
- **带 ESP32 OV3660 摄像头** → 用 `camera-version` 分支 + PCB 做 3 根飞线,但**没有 LCD 屏**
- **不带 ESP32 摄像头 / 想保留 LCD 屏** → 用 `CogletESP` 分支
- **仅使用 Grove Vision AI V2 人脸追踪** → 两个分支都可以,这是 RP2040 端的功能,与 ESP32 分支无关
### Coglet 主仓库目录结构
```
Coglet/
├── 3D Printing Files/
│ └── CogletB34Parts.3mf # 3D 打印零件
├── PCB/
│ ├── CogNogV1_0.pdf # 原理图 PDF
│ └── CogletESP_2026-02-24.epro # EasyEDA 工程文件
├── CogletESP/ # Git 子模块 → CogletESP 仓库
├── Translated Docs for XiaoZhi AI/ # 14 份小智文档英文翻译
│ ├── 固件烧录指南
│ ├── WiFi 配置
│ ├── 面包板接线
│ ├── ESP-IDF 开发环境搭建
│ └── 摄像头接线 ...
└── README.md
```
### 已知 PCB 问题CogNog V1.0
- USB-C 接口过紧
- **摄像头引脚与 PSRAM 冲突**GPIO 35/36/37 被 PSRAM Octal SPI 占用,详见下方飞线说明)
- 体积电容不足
- 摄像头画面水平翻转camera-version 分支已通过 `SetHMirror(true)` 修正)
### 翻译文档索引Translated Docs for XiaoZhi AI
主仓库中 `Translated Docs for XiaoZhi AI/` 包含 13 份小智文档英文翻译,其中与搭建相关的:
| 文件 | 内容 |
|------|------|
| **DIY Breadboard for Xiaozhi AI...** | 完整硬件清单和接线教程最详细18MB |
| **[Latest] Wiring Tutorial...Camera** | **带摄像头的最新接线教程** |
| Setting up ESP IDF 5.5.2... | ESP-IDF 开发环境搭建Windows |
| Flash Tool_Web Terminal... | 网页烧录工具(无需 IDF 环境) |
| Configure Device Wi-Fi... | WiFi 配网和设备添加 |
| Steps for Wake Word Change... | 唤醒词修改 |
---
## 三、项目状态
- **活跃度**:积极开发中(最后更新 2026 年 3 月)
- **成熟度**:早期阶段 / 工作进行中
- Releases 页面为空,**无预编译固件**
- PCB 存在多个已知问题
- 没有专用的 Coglet 板级配置(使用通用 bread-compact 系列)
- **众筹**Kickstarter 进行中
- **社区**Discord + QQ 群
---
## 四、摄像头版本完整搭建流程camera-version
> 你使用的是**带摄像头**的版本,以下是从零到运行的完整操作流程。
### 4.1 为什么需要飞线?
**这是 CogNog V1.0 PCB 的设计缺陷,不是故意的设计。**
ESP32-S3 的 PSRAM 使用 Octal SPI 接口,占用了 GPIO 35/36/37。但 V1.0 PCB 把摄像头的三个信号线D7、VSYNC、HREF也接到了这三个引脚上。当摄像头和 PSRAM 同时工作时,引脚冲突导致摄像头无法使用。
**所有使用 CogNog V1.0 PCB + 摄像头的用户都必须飞线。** 目前只有 V1.0 版本的 PCB。
### 4.2 硬件准备清单
| 部件 | 说明 |
|------|------|
| CogNog V1.0 PCB | 需要飞线修改 |
| ESP32-S3 模组 | 带 PSRAM如 N16R8 |
| OV3660 摄像头模组 | DVP 接口 + 排线 + 转接板 |
| Grove Vision AI V2 | 人脸追踪模块 + 15pin 排线适配器 |
| INMP441 麦克风 | I2S 数字麦克风 |
| 扬声器 | I2S 输出 |
| 9x 舵机 | **必须 180° 标准舵机**(如 KPower M0090 或 MG90S 180° 金属齿轮版),详见下方舵机选型说明 |
| Raspberry Pi Pico (RP2040) | 舵机控制 |
| 6 Pin PicoBlade 连接线 | RP2040 ↔ ESP32 UART |
### 4.3 PCB 飞线操作3 根线)
在 CogNog V1.0 PCB 上,**切断原走线并飞线到新引脚**
| 原始引脚 | 飞线到 | 摄像头信号 | 说明 |
|----------|--------|-----------|------|
| GPIO 35 | **GPIO 14** | CAMERA_PIN_D7 | 数据位 7 |
| GPIO 36 | **GPIO 41** | CAMERA_PIN_VSYNC | 垂直同步 |
| GPIO 37 | **GPIO 42** | CAMERA_PIN_HREF | 行参考信号 |
> **注意**camera-version 分支还释放了 JTAG 引脚GPIO 39-42给摄像头使用代码中通过 `gpio_reset_pin()` 实现。
### 4.4 代码下载
```bash
# 第 1 步:克隆 camera-version 分支ESP32 固件 + RP2040 代码都在里面)
git clone -b camera-version https://github.com/will-cogley/CogletESP.git
# 第 2 步克隆主仓库文档、PCB 原理图、3D 打印件)
git clone https://github.com/will-cogley/Coglet.git
```
克隆后的目录结构:
```
CogletESP/ # camera-version 分支
├── main/
│ └── boards/
│ └── bread-compact-wifi-s3cam/
│ ├── config.h # GPIO 引脚映射(飞线后的版本)
│ └── compact_wifi_board_s3cam.cc # 板级初始化(禁用 LCD启用摄像头
├── components/
├── RP2040/ # RP2040 MicroPython 代码
│ ├── main.py # 主控程序、舵机状态机、UART 通信ESP32 + Grove Vision AI V2
│ ├── servoclass.py # 舵机驱动PWM 50Hz平滑运动
│ └── animation.py # 动画定义(思考、开心、热身等)
├── CMakeLists.txt
└── sdkconfig
```
### 4.5 编译烧录 ESP32 固件macOS
```bash
# 1. 进入项目目录
cd CogletESP
# 2. 激活 IDF 环境
source ~/esp/esp-idf/export.sh
# 3. 设置目标芯片
idf.py set-target esp32s3
# 4. 配置板级选项
idf.py menuconfig
# → 找到板级配置,选择 bread-compact-wifi-s3cam
# 5. 编译
idf.py build
# 6. 查看可用串口
ls /dev/cu.usb*
# 7. 烧录并监控
idf.py -p /dev/cu.usbmodem14101 flash monitor
# 串口名根据实际替换(/dev/cu.usbmodem* 或 /dev/cu.usbserial*
# 8. 退出监视器Ctrl + ]
```
### 4.6 烧录 RP2040MicroPython
**第 1 步:刷入 MicroPython 固件**
下载地址https://micropython.org/download/RPI_PICO/
**版本选择**:下载 **v1.24.x ~ v1.25.x 稳定版** `.uf2` 文件。代码只用基础 APIPin、PWM、UARTv1.20+ 稳定版都兼容。**不要下载 preview/nightly 预览版。**
**硬件说明**CogNog V1.0 PCB 上 RP2040 是直接焊接的(不是独立的 Raspberry Pi Pico 开发板PCB 上有两个相关按键:
- **SW1Boot Switch**:位于 Flash Memory 旁,即 BOOTSEL 功能,用于进入 UF2 烧录模式
- **Run Switch**:位于 RP2040 旁,连接 RUN 引脚,即**复位按钮**。使用场景:
- 上传新的 .py 文件后,按一下重启 RP2040 使新代码生效(等同于 `mpremote reset`
- 舵机行为异常时,按一下重启恢复到初始状态
- 配合 SW1 进入烧录模式(见下方方式 B
操作macOS / Windows 通用):
**方式 A — USB 未连接时:**
1. **按住 SW1Boot Switch** → 通过 RP2040 的 USB-C 口连接电脑 → 松开 SW1
2. 电脑上出现 `RPI-RP2` U 盘macOS 在 FinderWindows 在资源管理器)
3.`.uf2` 文件拖入 U 盘
4. RP2040 自动重启
**方式 B — USB 已连接时(更常用):**
1. **按住 SW1Boot Switch不松手**
2. **按一下 Run Switch复位然后松开**(只按一下,不用保持)
3. **松开 SW1** — 整个过程约 1-2 秒,关键是按 Run Switch 时 SW1 必须处于按下状态
4. 电脑上出现 `RPI-RP2` U 盘
5.`.uf2` 文件拖入 U 盘
6. RP2040 自动重启
> **原理**Run Switch 让 RP2040 复位重启,重启瞬间 SW1 被按着 → RP2040 检测到 BOOTSEL 为低电平 → 进入 UF2 bootloader 模式。
>
> **提示**:如果 RP2040 已经刷过 MicroPython 且只需要更新 .py 文件,不需要重新进入 bootloader 模式,直接用 `mpremote` 上传即可(见第 2 步)。
**第 2 步:上传 .py 文件**
> **注意**`CogletESP` 分支有 4 个文件(含 `coms.py``camera-version` 分支有 3 个文件(无 `coms.py`)。请使用对应分支的文件。
#### macOS
方式 A — mpremote推荐
```bash
pip install mpremote
cd CogletESP/RP2040
mpremote cp main.py :main.py
mpremote cp servoclass.py :servoclass.py
mpremote cp coms.py :coms.py # 仅 CogletESP 分支需要
mpremote cp animation.py :animation.py
mpremote reset # 重启运行
mpremote repl # 查看串口输出(调试用)
```
mpremote 本质就是一个串口文件传输工具,把 .py 文件拷贝到 Pico 的文件系统里。Pico 上电后自动运行 main.py不需要搭建任何 MicroPython 编译环境。
方式 B — Thonny图形界面
```bash
brew install --cask thonny
```
1. 打开 Thonny → 右下角选择 `MicroPython (Raspberry Pi Pico)`
2. 逐个打开 .py 文件 → 另存为 → 选择 `Raspberry Pi Pico` → 保存同名
3. 保存完成后 Pico 重启自动运行 `main.py`
#### Windows
方式 A — mpremote推荐
```powershell
# 1. 安装 mpremote需要 Python 3.x从 python.org 下载安装,勾选 Add to PATH
pip install mpremote
# 2. 进入 RP2040 目录
cd CogletESP\RP2040
# 3. 上传文件Pico 连接 USB 后 Windows 会分配 COM 口mpremote 自动识别)
mpremote cp main.py :main.py
mpremote cp servoclass.py :servoclass.py
mpremote cp coms.py :coms.py # 仅 CogletESP 分支需要
mpremote cp animation.py :animation.py
mpremote reset # 重启运行
mpremote repl # 查看串口输出调试用Ctrl+] 退出)
```
> **Windows 注意事项**
> - 如果 `mpremote` 找不到设备,打开**设备管理器 → 端口(COM 和 LPT)**,确认 Pico 对应的 COM 口(如 COM3然后用 `mpremote connect COM3 cp main.py :main.py` 指定端口
> - 如果设备管理器中看不到 COM 口需要安装驱动MicroPython 固件刷入后 Pico 通常免驱,若未识别可尝试拔插 USB 或换 USB 口
方式 B — Thonny图形界面适合新手
1. 下载安装 Thonnyhttps://thonny.org/ (选择 Windows 版本)
2. 打开 Thonny → 右下角选择 `MicroPython (Raspberry Pi Pico)`
3. 逐个打开 .py 文件 → 文件 → 另存为 → 选择 `Raspberry Pi Pico` → 保存同名
4. 保存完成后 Pico 重启自动运行 `main.py`
### 4.7 配置 Grove Vision AI V2人脸追踪
**不需要手动刷底层固件**,但需要部署一个人脸检测模型。
**操作步骤:**
1. 用 USB-C 线将 Grove Vision AI V2 连接到电脑
2. 用 Chrome 浏览器访问 **SenseCraft AI 平台**https://sensecraft.seeed.cc/
3. 模型下载地址https://sensecraft.seeed.cc/ai/model/deploy?id=60094&uniform_type=36&name=%E4%BA%BA%E8%84%B8%E6%A3%80%E6%B5%8B&adapteds=11&adapteds=12&adapteds=14&task=1
4. 选择一个**人脸检测模型**(推荐 YOLO Face Detection输入尺寸 **224x224**
5. 点击部署,等待上传完成
6. 断开 USB将模块接回 RP2040 的 UART
**为什么是 224x224** 代码中 `pixel_centre = 112` 即 224/2表明模型输入分辨率为 224x224。
**工作原理:**
```
RP2040 发送 AT+INVOKE=1,0,1 (SSCMA AT 协议)
Grove Vision AI V2 运行人脸检测推理
返回 JSON包含 "boxes" 字段 → [x, y, w, h, ...]
RP2040 计算人脸偏移量x_offset = boxes[0] - 112, y_offset = boxes[1] - 112
驱动舵机追踪:
- EYL/EYR眼球左右 → 跟随 x_offset
- PIT头部俯仰 → 跟随 y_offset
- YAW底座旋转 → 眼球偏离中心 >20° 时延迟跟随
deadzone = 20 像素(避免微小抖动触发舵机)
```
### 4.8 硬件连接
```
┌─────────────┐ UART 115200 ┌──────────────┐ UART 921600 ┌───────────────────┐
│ ESP32-S3 │◄──────────────────►│ RP2040 │◄──────────────────►│ Grove Vision AI V2│
│ │ TX→RX(GP5) │ (Pico) │ TX(GP0)→RX │ │
│ AI/语音 │ RX←TX(GP4) │ 舵机控制 │ RX(GP1)←TX │ 人脸检测 │
│ WiFi/网络 │ GND──GND │ 动画状态机 │ │ YOLO 模型 │
└─────────────┘ └──────────────┘ └───────────────────┘
PWM 引脚 x9
┌──────┴──────┐
│ 9 个舵机 │
│ EYL/EYR/PIT │
│ YAW/MOU/LID │
│ + 耳朵等 │
└─────────────┘
```
ESP32 通过 UART 发送状态字符串给 RP2040
`neutral` / `idle` / `listening` / `speaking` / `thinking` / `happy`
→ RP2040 根据状态执行对应舵机动画
### 4.9 舵机选型说明(重要)
> **实测踩坑记录**:使用 MG90S 360° 连续旋
转版舵机后,耳朵舵机转到目标角度后无法停止,持续堵转导致齿轮发出刺耳声音、舵机严重发烫,有烧毁风险。更换为 180° 标准舵机后问题解决。
#### 必须使用 180° 标准舵机的原因
Coglet 的 9 个舵机全部采用**角度控制**模式(代码中 `set_target(角度)` 命令舵机转到指定角度并保持),这只有 180° 标准舵机才能实现。
**180° 标准舵机 vs 360° 连续旋转舵机的核心区别:**
| 对比项 | 180° 标准舵机(如 KPower M0090 | 360° 连续旋转舵机(如 MG90S 360° 改装版) |
|--------|-------------------------------|-------------------------------------|
| **内部结构** | 有电位器(可变电阻),实时反馈当前角度 | 拆除/固定了电位器,失去角度反馈能力 |
| **PWM 信号含义** | 对应**目标角度**1ms=0°, 1.5ms=90°, 2ms=180° | 对应**旋转速度和方向**1.5ms=停止, <1.5ms=反转, >1.5ms=正转) |
| **控制逻辑** | 闭环控制:收到 PWM → 对比目标与当前角度 → 自动转到位停住 | 开环控制:收到 PWM → 按速度持续旋转,永不停止 |
| **能否锁定位置** | 能,到达角度后施力保持 | 不能,停转后无保持力,外力可推动 |
| **旋转范围** | 0°~180° 精确定位 | 360° 无限旋转,无法精确定位 |
#### 为什么 360° 舵机无法通过代码适配?
网上有"定时转动"方案(全速旋转 N 毫秒 → 发停止信号),原理是 `转动时间 = (500ms / 360°) × 目标角度`。但这对 Coglet **完全不可行**
1. **误差累积无法修正**:没有位置反馈,每次转动误差 ±5°~10°。Coglet 的动画是高频反复运动(说话时嘴巴每 250ms 开合一次、耳朵每秒摆动),运行几分钟后累积误差可达几百度,位置完全跑飞
2. **个体差异大**:每台舵机电机特性、齿轮间隙不同,无法统一校准"时间-角度"关系。换一台舵机就要重新标定
3. **无法保持位置**代码中头部俯仰、眼球等需要停在某个角度并持续施力保持。360° 舵机停转后没有保持力,手一碰就偏了
4. **实时性失效**Coglet 需要实时响应 AI 状态变化动态调整角度,定时方案无法做到实时同步
5. **负载和温度影响转速**:电压波动、温度变化都会改变旋转速度,使时间估算更不准确
#### 推荐舵机型号
| 型号 | 说明 | 参考价格 |
|------|------|---------|
| **KPower M0090**(官方推荐) | 9g 模拟金属齿轮 180° 舵机 | ~¥8-12/个 |
| **MG90S 180° 金属齿轮版** | 注意必须是 **180° 版**,不带 "continuous/360" 字样 | ~¥5-8/个 |
> **购买注意**MG90S 有 180° 和 360° 两个版本,外观完全一样。购买时务必确认商品标题或参数中标注 "180°",避免买到 360° 连续旋转版。
#### 9 个舵机角度范围详解
代码中所有舵机的 PWM 映射为 0°~180°脉宽 500μs~2500μs各舵机通过 `min_angle` / `max_angle` 限定实际运动范围(源码:`animation.py` 第 14-23 行):
| 舵机代号 | 功能 | GPIO 引脚 | 角度范围 | 最大跨度 | 运动说明 |
|---------|------|----------|---------|---------|---------|
| **YAW** | 底座旋转 | GP6 | 10°~170° | 160° | 头部左右转动,人脸追踪时由 Grove Vision AI V2 驱动 |
| **ROL** | 颈部侧倾 | GP7 | 30°~120° | 90° | 头部左右歪头happy 状态下 70°↔110° 摆动 |
| **PIT** | 头部俯仰 | GP8 | 1°~80° | 79° | 头部上下点头thinking 状态下 50°↔80° 摆动 |
| **MOU** | 嘴巴开合 | GP19 | 5°~150° | 145° | speaking 时 70°↔130° 每 250ms 切换(张嘴/闭嘴) |
| **EYL** | 左眼球 | GP12 | 30°~150° | 120° | 左右看,人脸追踪时跟随 x 偏移量 |
| **EYR** | 右眼球 | GP13 | 30°~150° | 120° | 与 EYL 同步运动 |
| **LID** | 眼睑 | GP14 | 30°~160° | 130° | 30°=闭眼110°~160°=睁眼,随机眨眼动画 |
| **EAL** | 左耳 | GP15 | 60°~150° | 90° | happy 状态下 100°↔160° 摆动 |
| **EAR** | 右耳 | GP16 | 30°~120° | 90° | happy 状态下 60°↔120° 摆动 |
> **注意**:以上角度范围是代码中的软件限位,基于官方 3D 打印件和 KPower M0090 舵机校准。如果使用其他舵机或自制外壳,可能需要调整 `animation.py` 中的 `min_angle` / `max_angle` 避免堵转。
> **MOU 引脚差异**`CogletESP` 分支 MOU 使用 **GP19**`camera-version` 分支 MOU 使用 **GP9**。请根据实际 PCB 版本确认。
#### 校准模式使用方法
CogNog V1.0 PCB 上有一个 **SW11Calibration Switch** 拨动开关,连接 RP2040 的 GPIO20。代码中 GPIO20 配置了内部上拉电阻:
```python
mode = Pin(20, Pin.IN, Pin.PULL_UP)
is_calibrating = (mode.value() == 1) # 高电平 = 校准模式
```
**SW11 逻辑:**
| SW11 状态 | GPIO20 电平 | 模式 | 表现 |
|----------|-----------|------|------|
| 断开GPIO20 悬空) | 高(内部上拉到 1 | **校准模式** | 所有舵机归 90° 中位LED 闪烁 |
| 接通GPIO20 接 GND | 低 | **正常运行模式** | 响应 ESP32 状态指令,执行动画 |
> **提示**:如果不确定 SW11 哪个位置对应哪种模式,两个位置都试一下,**LED 闪烁的那个就是校准模式**。
#### 校准与组装流程
**前提**RP2040 已刷入 MicroPython 固件并上传 .py 文件。
```
第 1 步:进入校准模式
└→ 将 SW11 拨到校准位置LED 闪烁)
第 2 步:通电
└→ 给 RP2040 和舵机供电
└→ 所有 9 个舵机自动转到 90° 中位staggered startup逐个上电避免电流冲击
第 3 步:安装舵机臂
└→ 在 90° 中位状态下,将舵机臂安装到需要的朝向
└→ 拧紧舵机臂固定螺丝
└→ ⚠️ 关键:必须在通电校准状态下安装,否则角度对不上
第 4 步:组装机械结构
└→ 将舵机装入 3D 打印外壳
└→ 连接所有机械连杆
第 5 步:验证
└→ 将 SW11 拨回正常运行模式
└→ 观察舵机是否能正常运动,无堵转
└→ 如有堵转,调整 animation.py 中对应舵机的 min_angle / max_angle
第 6 步:连接 ESP32
└→ 接上 ESP32 UARTTX→GP5, RX→GP4
└→ ESP32 发送状态指令,舵机开始响应 AI 对话动画
```
> **校准模式不需要断开 ESP32**:校准模式在主循环中每帧都强制覆盖舵机状态(`main.py` 第 153-155 行),即使 ESP32 发送了指令也会被忽略。
### 4.10 camera-version 的代价与限制
| 项目 | 说明 |
|------|------|
| **LCD 屏被禁用** | 摄像头占用了原 LCD 引脚,代码用 DummyDisplay 空实现替代 |
| **音频引脚全部重映射** | MIC 从 GPIO 1/2/42 移到 4/5/6SPK 从 GPIO 39/40/41 移到 7/15/16 |
| **内存压力** | 摄像头 + PSRAM 同时运行可能偶发内存不足(作者提到 "appears to run out of memory sometimes" |
| **摄像头画面翻转** | 已通过代码设置 `SetHMirror(true)` + `SetVFlip(true)` 修正 |
---
## 五、无摄像头版本搭建流程CogletESP 分支)
> 如果不使用 ESP32 OV3660 摄像头,想保留 LCD 显示,使用此方案。
### 5.1 代码下载
```bash
git clone -b CogletESP https://github.com/will-cogley/CogletESP.git
git clone https://github.com/will-cogley/Coglet.git
```
### 5.2 ESP32 编译烧录
与摄像头版本相同(参见 4.5),但 menuconfig 中的板级配置可能不同PCB **无需飞线**
### 5.3 RP2040 烧录
MicroPython 固件刷入方式与摄像头版本相同(参见 4.6),但 **RP2040 代码两个分支不同**
- `CogletESP` 分支有 **4 个文件**`main.py``servoclass.py``coms.py``animation.py`
- `camera-version` 分支只有 **3 个文件**(无 `coms.py`UART 通信集成在 `main.py` 中)
请使用对应分支的 RP2040 目录文件上传。
### 5.4 Grove Vision AI V2
与摄像头版本完全相同(参见 4.7Grove Vision AI V2 的人脸追踪功能由 RP2040 端控制,与 ESP32 分支选择无关。
---
## 六、常见问题
| 问题 | 解决方案 |
|------|---------|
| Python 环境冲突 | `idf.py fullclean` 后重新编译 |
| Bootloader CMake 缓存不匹配 | `rm -rf build/bootloader build/bootloader-prefix` |
| 找不到串口设备 | 安装 CP2102/CH340 驱动,检查 USB 线是否支持数据传输 |
| menuconfig 中找不到板级配置 | 确认在正确的分支(`camera-version``CogletESP` |
| 摄像头不工作 | 确认已完成 3 根飞线35→14, 36→41, 37→42并使用 `camera-version` 分支 |
| 偶发内存不足 | camera-version 已知问题,摄像头 + PSRAM 同时运行时可能出现 |
| Grove Vision AI V2 无响应 | 确认已通过 SenseCraft 部署人脸检测模型UART 波特率 921600 |
| 舵机不动 | 检查 RP2040 是否正确上传 .py 文件CogletESP 分支 4 个 / camera-version 分支 3 个UART 连接是否正常 |
| 舵机堵转、发烫、齿轮刺耳声 | **使用了 360° 连续旋转舵机**,必须更换为 180° 标准舵机(详见 4.9 舵机选型说明) |
| 舵机持续朝一个方向旋转不停 | 同上360° 舵机收到非 90° 信号会持续旋转。180° 舵机会转到目标角度后自动停住 |
---
## 六点五、Phase 01 单摄像头人脸追踪 踩坑经验2026-04-17 ~ 04-20
> **背景**:尝试用 ESP32 上的 OV3660 摄像头做人脸检测,通过 UART 发送人脸坐标给 RP2040驱动眼球/YAW 舵机追踪。详细规划/执行文档见 [docs/phase-01-face-tracking/](docs/phase-01-face-tracking/)。
>
> **现状**代码骨架全部完成15 个任务 T01-T15ESP32 端 + RP2040 端端到端联通但摄像头帧数据解析仍有问题box 固定伪激活),未完全通过验收。
### 6.5.1 编译 / 配置类踩坑
| # | 坑 | 原因 | 解决方案 |
|---|---|-----|---------|
| 1 | 板级配置被重置为 `BREAD_COMPACT_WIFI`(无摄像头版) | `idf.py fullclean` / `set-target` 可能触发 sdkconfig 重置到 defaults | 显式编辑 `sdkconfig` 确认 `CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_CAM=y` |
| 2 | `esp-dl 3.3.0``esp-sr 2.2.x` 版本冲突 | esp-dl 需要 esp-dsp 1.7.0esp-sr 2.2 钉 esp-dsp 1.6.0 | 升级 `esp-sr``~2.3.1`(已切到 esp-dsp 1.7.0 |
| 3 | Bootloader CMake 缓存不匹配,编译报 `source does not match` | ESP-IDF 路径历史变更(如 `esp-idf/v5.4.2/esp-idf/``esp-idf/` | `rm -rf build/bootloader build/bootloader-prefix` 重新编译 |
| 4 | `ESP_LOGI``%lld` 输出变成字符串 `"ldus"` | ESP-IDF nano newlib 不完全支持 `%lld` | 改用 `%lu` + `(unsigned long)` 强转probe 用时 < 71 分钟uint32 足够 |
| 5 | `esp_video 1.3.1/1.4.1` DVP 启动报 `xclk_freq is not set` | esp_video 源码 `dvp_video_device.c` 构造 `esp_cam_ctlr_dvp_config_t` 时缺失 `xclk_freq` 字段IDF 5.4.2 不支持 `external_xtal` | esp_video 1.4.1 **手动 patch** managed_components 源码 `#else` 分支加 `.xclk_freq = 20000000,` |
### 6.5.2 摄像头数据链路踩坑
| # | | 原因 | 解决方案 |
|---|---|-----|---------|
| 6 | `/dev/video2` 打不开errno=2 No such file | sdkconfig 中没有启用任何 sensor driver`CONFIG_CAMERA_OV3660` 默认 `n` | sdkconfig 启用对应摄像头`CONFIG_CAMERA_OV3660=y` + 具体分辨率/格式 |
| 7 | V4L2 REQBUFS count 默认为 1DVP 路径DMA 饥饿陈旧数据 | `esp32_camera.cc` `req.count = strcmp(...) == 0 ? 2 : 1;` DVP 用单 buffer | 改为 `req.count = 2;` DMA 有双缓冲持续更新 |
| 8 | 分区表 OTA 分区 3.94MB 容纳不下 4.23MB 固件 | 加了 esp-dl + human_face_detect 后固件膨胀 | 修改 `partitions/v2/16m.csv`ota_0/ota_1 5MBassets 缩到 5.875MB16MB 模组够用 |
| 9 | 摄像头输出画面全黑Y 值只有 0~8 | **镜头表面出厂保护膜未撕掉** | 用指甲抠边缘撕掉透明保护膜极薄不易察觉 |
| 10 | 画面持续偏暗 | 室内光照不足 / 镜头指纹灰尘 | 白天窗边或明亮台灯下测试 |
### 6.5.3 esp-dl 人脸检测踩坑
| # | | 原因 | 解决方案 |
|---|---|-----|---------|
| 11 | MSR_S8_V1 模型固定在 `box=[0, y, 40, y+40]` 误识别 | 模型输入 160×1204:3摄像头输出 240×2401:1ImagePreprocessor letterbox 填充左右各 20 列灰条模型在灰条上产生假激活 | 改用输入正方形的 `ESPDET_PICO_224_224_FACE`224×224 |
| 12 | ESPDET 模型仍然输出固定 box `[233, 158, 94, 239]`数学上不可能x1 > x2 | 怀疑模型对 out-of-distribution 输入 fallback 到默认 anchor | 尝试中:手动 YUYV→RGB888 转换 + 字节序验证 |
| 13 | 字节序判断反复RGB565LE / BE / YUYV 都试过 | OV3660 FORMAT_CTRL00=0x61 设置是"RGB565 byte-swapped",但实测数据更像 YUYV | 通过 `frame debug` 打印前 16 字节诊断,按多种格式对比解读 |
### 6.5.4 任务调度踩坑Core 亲和 & 崩溃)
| # | 坑 | 原因 | 解决方案 |
|---|---|-----|---------|
| 14 | 唤醒词检测后触发 LED 时崩溃(`Interrupt wdt timeout`| face_tracker 绑 Core 0esp-dl 推理占 150msRMT LED 驱动在同核抢不到 spinlock 超 300ms | face_tracker 改绑 **Core 1**(音频空闲时让出 CPU |
| 15 | GDMA ISR `InstrFetchProhibited` at PC=0x00000000 | esp_video 1.4.1 底层 DMA 回调指针变 NULL疑似 ESP-IDF 5.4.2 兼容性 bug | 未完全修复。降低 FPS 到 5 能推迟崩溃。待升级 ESP-IDF 或改用低级 esp_cam_ctlr API |
| 16 | 弱符号 `uart_send_face` 链接失败 | C++ 名字修饰问题 | `extern "C"` 包裹 weak 前置声明 + strong 定义,确保 C 链接名一致 |
### 6.5.5 RP2040 端踩坑
| # | 坑 | 原因 | 解决方案 |
|---|---|-----|---------|
| 17 | 进入 idle 后眼球卡在最后追踪位置不回中 | 原 `facetrack()` idle 时直接 return没有回中机制 | 新增 `_recenter_tracking_servos()`,在 `grove_active` 从 True→False 或首次进入 idle 时一次性回中到 90° |
| 18 | ESP32 持续发误识别坐标(-95/+40 等固定值) | 眼球持续累加偏移直到撞到机械限位30°/150° | 属 ESP32 侧 bug但 RP2040 侧加 `staticflag` 去重 + 回中机制缓解 |
| 19 | mpremote `could not enter raw repl` | Pico 主循环阻塞或端口被旧 monitor 占用 | 拔插 USB 复位 Pico → 立刻 `mpremote cp` 抢时间窗 |
| 20 | 两个分支 RP2040 代码不同 | `CogletESP` 分支 4 个 .py`coms.py``camera-version` 分支 3 个(无 `coms.py` | 使用时对应分支代码,不要混用 |
### 6.5.6 硬件踩坑
| # | 坑 | 原因 | 解决方案 |
|---|---|-----|---------|
| 21 | 3 根飞线35→14, 36→41, 37→42是否到位 | CogNog V1.0 PCB 设计缺陷PSRAM 和摄像头 D7/VSYNC/HREF 冲突 | 万用表测导通,确保原 GPIO 35/36/37 与摄像头断开,新 GPIO 14/41/42 接通 |
| 22 | 舵机堵转、发烫、齿轮刺耳声 | 误用 **360° 连续旋转舵机**MG90S 360° 版外观和 180° 一样) | 更换为 **KPower M0090****MG90S 180° 金属齿轮版**(详见 4.9 节) |
| 23 | 烧录固件后 `Compile time` 仍是旧版 | 用 IDE 内置烧录工具可能烧到错误路径 / 没完整烧录 | 改用命令行 `idf.py -p /dev/cu.usbmodemXXX flash monitor`,烧完立即 monitor 看 `Compile time` 验证 |
### 6.5.7 调试方法论
| 方法 | 用途 |
|------|-----|
| **frame debug 打印前 16B 字节值** | 按多种格式YUYV / RGB565LE / RGB565BE对比解读判断 sensor 实际输出 |
| **zero_bytes 比例统计** | 判断画面亮度(>30% 大概率镜头遮挡或极暗;<5% 画面正常 |
| **box 数学合法性** | `box[0] > box[2]` `box[2] == width` 这种异常值说明模型崩了OOD 默认输出 |
| **对比不同画面输入下 box 是否变化** | 遮住摄像头 / 白墙 / 人脸 如果 box 完全一致一定是模型输入路径异常 |
| **Compile time 验证烧录生效** | 烧录后 monitor `I (xxx) app_init: Compile time: ...` 必须是最新编译时间 |
| **xtensa-esp32s3-elf-addr2line** | crash backtrace 的地址转成源码文件:行号 |
### 6.5.8 Phase 01 未解决问题(遗留)
1. **box 固定伪激活**无论遮住摄像头白墙真实人脸box 都稳定输出 `[233, 158, 94, 239]` MSR 模型的 `[0, *, 40, *]`)。已验证
- pix_type 不是根因YUYV / RGB565LE / RGB565BE / RGB888 手动转换都不行
- buffer 修复了数据更新问题 box 仍固定
- 模型切换MSR ESPDET改变了 box 坐标但仍固定
- 推断模型对某种 out-of-distribution 输入 fallback 到默认 anchor
2. **GDMA ISR 崩溃** ~30 秒触发一次 `InstrFetchProhibited` at PC=0x00000000 esp_video 1.4.1 / ESP-IDF 5.4.2 底层 bug
3. **端到端未验收**RP2040 Bug 3回中机制已修但因 ESP32 box 仍固定整条链路的"眼球跟随真实人脸"未通过
---
## 六点六、Phase 01 核心矛盾与解决方案分析2026-04-21 ~ 04-22
> 基于 JPEG Dump 诊断工具的大量实验9 次迭代尝试),本节汇总当前主要矛盾、根本原因和方案选择。
### 6.6.1 主要矛盾:画面可辨识 ≠ 模型可识别
**诊断工具** face_tracker.cc 里加 JPEG Dump 代码每次启动 base64 打印一帧 JPEGMac Python 脚本抓取保存为 `.jpg` 文件肉眼验证
**验证结果**
| 观察 | 事实 |
|------|------|
| 手动撕掉镜头保护膜后JPEG 画面可**清晰看到戴眼镜的人脸手部背景** | 摄像头硬件 + 飞线 + DVP 通路完全正常 |
| 画面**整体偏紫绿**RGB565 模式**偏绿**YUV422 模式 | 🟡 软件层 YUVRGB 色彩矩阵偏差不是硬件问题 |
| 同样的摄像头输入esp-dl `HumanFaceDetect` **无论什么 pix_type 都输出固定 box** | 深层集成问题 |
**核心矛盾**
> 人眼能辨识的画面(因为有上下文知识"绿色的这个 = 人脸"),轻量级 CNN 模型无法识别只看像素数值分布。esp-dl 官方模型用**正常色彩的标准人脸数据集**训练,我们的偏色画面在训练集里找不到对应模式 → 模型 fallback 到默认 anchor → box 恒定。
### 6.6.2 YUV→RGB 色偏的三层原因
#### 第 1 层:**YUV→RGB 色彩矩阵公式不完全匹配 BT.601**
- OV3660 输出 YUV **限幅范围**Y [16, 235], U/V [16, 240]中值 128
- 手写转换公式假定 Y [0, 255] **全范围**JFIF 标准
```cpp
int r = Y + 1.402 * (V - 128); // 错:没有黑电平偏移,整体偏暗
```
- 正确应为BT.601
```cpp
int y_scaled = 1.164 * (Y - 16); // 减黑电平、放大到全范围
int r = y_scaled + 1.596 * (V - 128);
```
#### 第 2 层:**OV3660 AWB自动白平衡未启用或响应慢**
- 默认寄存器序列中AWB 可能关闭或慢响应
- 导致 U/V 有**全局偏移**:画面整体偏绿/紫
- Grove Vision AI V2 内置 ISP 硬件自动白平衡,**我们读原始 YUV buffer 没有**
#### 第 3 层:**OV3660 FORMAT_CTRL00 = 0x61 的实际含义**
- `bit[7:4] = 0x6` = RGB565
- `bit[3:0] = 0x1` = byte-swap 序列
- 在 Kconfig RGB565 模式下sensor 实际输出可能是 **YVYU sequence**Y-V-Y-U而非标准 YUYV导致 U/V 解读时互换
- 修正方向:在 Kconfig 改用 YUV422 模式FORMAT_CTRL00=0x30标准 YUYV
### 6.6.3 esp-dl 模型输入分布敏感
即使色彩完全校正正确轻量级模型MSR_S8_V1 仅 60KB、ESPDET_PICO_224_224_FACE 约 500KB对 RGB 分布偏差**极其敏感**。具体要求:
| 要求 | 解释 |
|------|------|
| RGB 通道均值接近训练集 | ImageNet 类数据集 RGB 均值约 (124, 116, 104) |
| 归一化范围精确 | ESPDET 用 `(pixel-0)/255`,要求 pixel ∈ [0, 255] 全范围 |
| 无严重色偏 | 偏绿会让模型前几层卷积产生"异常激活",后续全部 fallback |
| 无边缘伪影 | letterbox 填充不能和画面内容对比度过强 |
> **为什么 Grove Vision AI V2 一定能行**Grove 用 Himax WiseEye HX6538 专用 AI 视觉处理器,内置 ISP + 针对自己 sensor 训练的**专用人脸检测模型**从硬件到模型端到端自闭环。esp-dl 是通用框架,需要用户自己保证数据质量。
### 6.6.4 sensor 硬件 JPEG 模式的局限
OV3660 支持硬件 JPEG 编码(`CAMERA_OV3660_DVP_JPEG_1280X720_12FPS`),但实测失败:
- `Esp32Camera::Capture()` 默认不协商 JPEG pix_fmt报 `no supported pixel format found`
- 启用 `CONFIG_XIAOZHI_CAMERA_ALLOW_JPEG_INPUT=y` 后能协商,但 `bytesused=0` —— DMA 没采到 JPEG 帧
- 推测 sensor 硬件 JPEG 需要特殊的 DVP 帧同步处理xiaozhi 的 V4L2 mmap 路径不兼容
结论:硬件 JPEG 路径此项目未打通,**需走软件 JPEG 编解码**。
### 6.6.5 延迟分析JPEG 中转路径
走 `xiaozhi Capture() JPEG esp-dl sw_decode_jpeg RGB888 HumanFaceDetect` 路径的延迟估算:
| 阶段 | 耗时 | 说明 |
|------|------|------|
| 摄像头采集一帧 | ~40ms | 24 FPS 间隔 |
| xiaozhi Capture() 软件 JPEG 编码 | 50-80ms | 240×240 YUV→RGB→JPEG |
| esp-dl sw_decode_jpeg 解码 | 30-50ms | JPEG → RGB888 |
| HumanFaceDetect 模型推理 | 150ms | ESPDET_PICO_224 |
| UART 发送坐标 | 1ms | 240 bytes @ 115200 |
| **ESP32 端总延迟** | **~270ms** | |
| RP2040 UART RX + parse | 2ms | |
| 舵机 PWM + 物理转动 | 20-80ms | 机械响应时间 |
| **端到端总延迟(脸动→眼球动)** | **~300-350ms** | |
**对比**
- **人眼感知"流畅跟随"阈值**< 500ms
- **Grove Vision AI V2**~100-150ms专用硬件
- **JPEG 中转方案**~300ms ✅ 可接受
- **人眨眼速度**~400ms
### 6.6.6 三个路径选择
| 方案 | 预计工时 | 成功率 | 备注 |
|------|---------|-------|------|
| **A. 继续深挖 esp-dl改色彩矩阵、启 AWB、fork 预处理)** | 8-10 小时 | ⭐⭐20-30%| 涉及 ov3660 寄存器调优 + 模型内部调试 |
| **B. JPEG 中转路径(走 xiaozhi 完整 Capture + esp-dl sw_decode_jpeg** | 2-3 小时 | ⭐⭐⭐⭐70-80%| **推荐**。`take_photo` 已证明 Capture() 色彩正常 |
| **C. 退回 Grove Vision AI V2项目原设计** | 2 小时 + ¥200 | ⭐⭐⭐⭐⭐100%| 官方 turnkey 方案,稳妥 |
### 6.6.7 方案 B 的关键突破口
**关键发现**xiaozhi 的 `self.camera.take_photo` MCP 功能拍的照片**云端 AI 能清晰识别**,说明 `Capture()` 函数内部有正确的色彩处理白平衡、色彩矩阵、JPEG 标准编码)。
**未尝试的真正路径**
```
Esp32Camera::Capture()
↓ 内部完整 pipeline色彩正常的 JPEG
JPEG buffer
↓ esp-dl sw_decode_jpegesp-dl 官方 example 路径)
标准 RGB888 画面(色彩 100% 匹配训练集)
HumanFaceDetect → 真正的 box
```
**之前的失败路径**:我一直绕过 `Capture()` 用 `CaptureForDetection()` 直接拿 V4L2 mmap 的原始 YUV buffer缺少 xiaozhi 的色彩校正。
### 6.6.8 验证方案 B 可行性的最简方法
**不用改代码**
1. 烧录当前 YUV422 模式的固件
2. 通过小智对话说"**帮我拍张照看看**"或"**你看见什么了**"
3. AI 云端返回画面描述
- 如果 AI 说能**清晰看到人脸/房间物体** → 证明 `Capture()` 色彩正常 → **方案 B 可行性 80%+**
- 如果 AI 说看不清或描述错乱 → `Capture()` 也有色偏 → 需考虑方案 C
### 6.6.9 当前代码状态快照2026-04-22
- `main/face_tracker.cc`:手动 YUYV→RGB888 转换 + pix_type=RGB888失败路径保留代码
- `main/face_tracker.cc`JPEG Dump 诊断代码(每次启动拍一张 YUYV JPEG
- `sdkconfig``CAMERA_OV3660_DVP_YUV422_240X240_24FPS=y`(画面偏绿但结构清晰)
- 固件编译通过烧录正常face_tracker 启动正常
- 症状box 恒定 `[233, 158, 94, 239]`眼球卡在极限位置不跟随
---
## 七、参考资源
| 资源 | 地址 |
|------|------|
| Coglet GitHub 主仓库 | https://github.com/will-cogley/Coglet |
| CogletESP 固件仓库 | https://github.com/will-cogley/CogletESP |
| 小智 ESP32 原版 | https://github.com/78/xiaozhi-esp32 |
| 眼球机构参考 | https://github.com/will-cogley/EyeMech_Epsilon |
| Coglet 组装视频 | https://www.youtube.com/watch?v=-7I-jFSNP2E |
| MicroPython 固件下载 | https://micropython.org/download/RPI_PICO/ |
| ESP-IDF 官方文档 | https://docs.espressif.com/projects/esp-idf/zh_CN/v5.4.2/ |
| MicroPython 官方文档 | https://docs.micropython.org/en/latest/ |
| mpremote 工具文档 | https://docs.micropython.org/en/latest/reference/mpremote.html |
| SenseCraft AI 平台Grove Vision AI V2 模型部署 | https://sensecraft.seeed.cc/ |
| Grove Vision AI V2 Wiki | https://wiki.seeedstudio.com/grove_vision_ai_v2/ |