# 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° 舵机会转到目标角度后自动停住 |

---

## 七、参考资源

| 资源 | 地址 |
|------|------|
| 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/ |
