# 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 + LLM（Qwen/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 烧录 RP2040（MicroPython）

**第 1 步：刷入 MicroPython 固件**

下载地址：https://micropython.org/download/RPI_PICO/

**版本选择**：下载 **v1.24.x ~ v1.25.x 稳定版** `.uf2` 文件。代码只用基础 API（Pin、PWM、UART），v1.20+ 稳定版都兼容。**不要下载 preview/nightly 预览版。**

**硬件说明**：CogNog V1.0 PCB 上 RP2040 是直接焊接的（不是独立的 Raspberry Pi Pico 开发板），PCB 上有两个相关按键：
- **SW1（Boot Switch）**：位于 Flash Memory 旁，即 BOOTSEL 功能，用于进入 UF2 烧录模式
- **Run Switch**：位于 RP2040 旁，连接 RUN 引脚，即**复位按钮**。使用场景：
  - 上传新的 .py 文件后，按一下重启 RP2040 使新代码生效（等同于 `mpremote reset`）
  - 舵机行为异常时，按一下重启恢复到初始状态
  - 配合 SW1 进入烧录模式（见下方方式 B）

操作（macOS / Windows 通用）：

**方式 A — USB 未连接时：**
1. **按住 SW1（Boot Switch）** → 通过 RP2040 的 USB-C 口连接电脑 → 松开 SW1
2. 电脑上出现 `RPI-RP2` U 盘（macOS 在 Finder，Windows 在资源管理器）
3. 将 `.uf2` 文件拖入 U 盘
4. RP2040 自动重启

**方式 B — USB 已连接时（更常用）：**
1. **按住 SW1（Boot 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. 下载安装 Thonny：https://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 上有一个 **SW11（Calibration 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 UART（TX→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/6，SPK 从 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.7），Grove 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-T15），ESP32 端 + 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.0，esp-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 默认为 1（DVP 路径），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 各 5MB，assets 缩到 5.875MB（16MB 模组够用） |
| 9 | 摄像头输出画面全黑，Y 值只有 0~8 | **镜头表面出厂保护膜未撕掉** | 用指甲抠边缘撕掉透明保护膜（极薄不易察觉） |
| 10 | 画面持续偏暗 | 室内光照不足 / 镜头指纹灰尘 | 白天窗边或明亮台灯下测试 |

### 6.5.3 esp-dl 人脸检测踩坑

| # | 坑 | 原因 | 解决方案 |
|---|---|-----|---------|
| 11 | MSR_S8_V1 模型固定在 `box=[0, y, 40, y+40]` 误识别 | 模型输入 160×120（4:3），摄像头输出 240×240（1:1），ImagePreprocessor 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 0，esp-dl 推理占 150ms，RMT 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 打印一帧 JPEG，Mac 端 Python 脚本抓取保存为 `.jpg` 文件肉眼验证。

**验证结果**：

| 观察 | 事实 |
|------|------|
| 手动撕掉镜头保护膜后，JPEG 画面可**清晰看到戴眼镜的人脸、手部、背景** | ✅ 摄像头硬件 + 飞线 + DVP 通路完全正常 |
| 画面**整体偏紫绿**（RGB565 模式）或**偏绿**（YUV422 模式） | 🟡 软件层 YUV→RGB 色彩矩阵偏差，不是硬件问题 |
| 同样的摄像头输入，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_jpeg（esp-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/ |
