CogletESP-camera-version/Coglet项目分析与开发指南.md
Rdzleo fc07d3806d Phase 01 调试迭代: OV3660 人脸检测集成 + 23 条踩坑经验汇总
## 代码变更

### main/application.cc
修复 T01 probe 日志 %lld 格式 bug(改用 %lu + unsigned long)

### main/boards/common/esp32_camera.cc
- 修复 DVP V4L2 单 buffer 导致 DMA 饥饿:req.count 从 1 改为 2
- 修复 [T01] Probe 日志 elapsed=ldus 显示问题(同上格式修复)

### main/face_tracker.cc
多轮迭代:
- 新增 frame debug 诊断日志(打印 top-left/center 16B + zero_bytes 统计)
- pix_type 尝试路径:YUYV → RGB565LE → RGB565BE → YUYV → RGB888(手动转换)
- 手动实现 BT.601 公式 YUYV→RGB888 转换,绕过 ImagePreprocessor 黑盒
- face_tracker 任务从 Core 0 切换到 Core 1,避让 RMT/LED 死锁
- 新增 INFO 级限频日志(每秒 1 条 face 检测记录)
- 修复推理时长日志 %lld 格式 bug
- 连续 3 秒无人脸时打印 no face detected

### main/idf_component.yml
esp_video 升级 1.3.1 → ~1.4.1(手动 patch 修 xclk_freq bug)

### partitions/v2/16m.csv
OTA 分区扩容:3.94MB → 5MB,assets 缩到 5.875MB,支持 4.23MB 固件

### docs/phase-01-face-tracking/PROGRESS.md
更新 Phase 01 执行日志,记录实机调试细节

## 文档更新

### Coglet项目分析与开发指南.md 新增第六点五节

完整记录本轮调试的 23 个踩坑,分为:
1. 编译/配置类(5 个):板级重置、依赖冲突、bootloader 缓存、%lld 格式、xclk_freq bug
2. 摄像头数据链路(5 个):sensor driver 启用、V4L2 buffer 数量、分区扩容、镜头保护膜、光照
3. esp-dl 人脸检测(3 个):MSR letterbox 伪影、ESPDET OOD 默认输出、字节序判断
4. 任务调度(3 个):WDT 崩溃、GDMA ISR 崩溃、弱符号链接
5. RP2040 端(4 个):idle 回中、坐标累加撞限位、mpremote 阻塞、两分支代码差异
6. 硬件(3 个):飞线验证、360° 舵机误用、烧录生效验证

附调试方法论 6 条 + 未解决遗留问题 3 条

## 已解决问题

-  ESP-IDF 编译链路(依赖/分区/格式)
-  ESP32 + RP2040 端到端协议(face:x,y UART)
-  WDT 崩溃(face_tracker 切到 Core 1)
-  RP2040 眼球回中机制(idle 时回正)
-  V4L2 双 buffer(DMA 数据更新正常)

## 遗留问题(待解决)

-  face 检测 box 固定伪激活(无论 pix_type / 画面内容 / 模型选择)
-  GDMA ISR 每 ~30s 触发 InstrFetchProhibited 崩溃
- ⚠️ 端到端验收:眼球未真正跟随人脸

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

35 KiB
Raw Blame History

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 状态机

idlespeakinglisteningthinkinghappyneutralcalibration


二、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 代码下载

# 第 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

# 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.pycamera-version 分支有 3 个文件(无 coms.py)。请使用对应分支的文件。

macOS

方式 A — mpremote推荐

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图形界面

brew install --cask thonny
  1. 打开 Thonny → 右下角选择 MicroPython (Raspberry Pi Pico)
  2. 逐个打开 .py 文件 → 另存为 → 选择 Raspberry Pi Pico → 保存同名
  3. 保存完成后 Pico 重启自动运行 main.py

Windows

方式 A — mpremote推荐

# 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μs2500μ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 使用 GP19camera-version 分支 MOU 使用 GP9。请根据实际 PCB 版本确认。

校准模式使用方法

CogNog V1.0 PCB 上有一个 SW11Calibration Switch 拨动开关,连接 RP2040 的 GPIO20。代码中 GPIO20 配置了内部上拉电阻:

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 代码下载

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.pyservoclass.pycoms.pyanimation.py
  • camera-version 分支只有 3 个文件(无 coms.pyUART 通信集成在 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-versionCogletESP
摄像头不工作 确认已完成 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/

现状代码骨架全部完成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.0esp-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 driverCONFIG_CAMERA_OV3660 默认 n sdkconfig 启用对应摄像头:CONFIG_CAMERA_OV3660=y + 具体分辨率/格式
7 V4L2 REQBUFS count 默认为 1DVP 路径DMA 饥饿陈旧数据 esp32_camera.ccreq.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.csvota_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_FACE224×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 个 .pycoms.pycamera-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 M0090MG90S 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 仍固定,整条链路的"眼球跟随真实人脸"未通过


七、参考资源

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