# Phase 01 执行进度追踪 > 由于本仓库非 git 仓库,用本文件替代 commit 作为原子进度追踪。 > 每完成一个任务追加一行;遇到偏差记录 `[!]` 条目。 ## 任务状态表 - [~] T01 摄像头硬件 V4L2 probe —— 代码完成,硬件验证待用户 - [x] T02 Kconfig 开关 + FPS choice - [x] T03 esp-dl + human_face_detect 依赖 - [x] T04 Esp32Camera CaptureForDetection + 双超时 mutex - [x] T05 face_tracker.{h,cc} 骨架 + CMake 条件编译 - [x] T06 集成 HumanFaceDetect 推理 + 坐标归一化(代码部分;实测待 T12) - [x] T07 uart_send_face + uart mutex - [ ] T08 RP2040 parse_face + static 去重 - [ ] T09 RP2040 main.py incoming_commands 识别 face: - [ ] T10 RP2040 facetrack() 改造(D-07 idle return) - [ ] T11 application.cc 接入 face_tracker_start - [ ] T12 端到端联调 - [ ] T13 性能调优 - [ ] T14 关开关回归测试 - [ ] T15 最终验收 ## 执行日志 - [x] T01 代码部分完成:2026-04-17 - 新增 `ProbeFrameCapture()` 到 `main/boards/common/esp32_camera.{h,cc}` - 在 `main/application.cc` 的 `Start()` 末尾插入 probe 调用(`#ifndef CONFIG_IDF_TARGET_ESP32` 守卫) - 硬件验证部分待用户接 USB 后在 T02/T03 通过后烧录验证 - [x] T02 完成:2026-04-17 - 在 `main/Kconfig.projbuild` 的 Camera Configuration menu 末尾新增 `XIAOZHI_ENABLE_FACE_TRACKING` + FPS choice(5/10/15) - 采用 PLAN_CHECK NOTE-1 方案 B:`depends on IDF_TARGET_ESP32S3` 只支持 S3,与 CMake 排除逻辑对齐 - [!] T03 偏差:2026-04-17 — 依赖版本冲突 阻塞批次 1 - **第一轮偏差**:PLAN 原定 `esp-dl==3.2.0` + `human_face_detect==0.4.1` 不兼容 (registry 数据显示 human_face_detect 0.4.1 实际依赖 `esp-dl ~3.3.0`) - 自动修正为 `esp-dl ~3.3.0` - **第二轮偏差(blocking)**:`esp-dl 3.3.0` 要求 `esp-dsp ==1.7.0`, 但项目已有 `esp-sr ~2.2.0` 要求 `esp-dsp ==1.6.0`,互斥 - 此为真正的版本冲突,已停下汇报 orchestrator - [x] T03 偏差已解决:2026-04-17 —— 用户决策方案 A:升级 esp-sr - 将 `idf_component.yml` 中 `esp-sr` 从 `~2.2.0` 升级为 `~2.3.1` - esp-sr 2.3.x 已切换到 esp-dsp 1.7.0,与 esp-dl 3.3.0 兼容 - `idf.py reconfigure` 通过:esp-dl 3.3.x / esp-dsp 1.7.0 / esp-sr 2.3.1 / human_face_detect 0.4.1 全部就绪 - 编译遇到 bootloader CMake 缓存不匹配(与 IDF 路径历史变更有关),已清理 `build/bootloader*` 目录后重新编译 - [x] T04 完成:2026-04-17 - 修改文件: main/boards/common/esp32_camera.{h,cc} - `esp32_camera.h`: 新增公开结构体 `FrameRef`(data/len/width/height/format/buf_index) + `CaptureForDetection(FrameRef*)` / `ReleaseDetectionFrame(const FrameRef&)` 声明 + 私有成员 `SemaphoreHandle_t capture_mutex_` - `esp32_camera.cc`: 构造函数末尾 `xSemaphoreCreateMutex()`,析构函数 `vSemaphoreDelete` 实现 `CaptureForDetection`(10ms timeout 拿不到锁即返回 false 跳帧,成功后不解锁) 实现 `ReleaseDetectionFrame`(VIDIOC_QBUF 归还 + 释放 mutex) `Capture()` 头部用栈上 RAII `CaptureLockGuard` 以 portMAX_DELAY 加锁,确保任何 return 路径都解锁 - `idf.py build` 通过,固件 2.47MB / 剩余 1.47MB (37% free) - [x] T05 完成:2026-04-17 - 新增: main/face_tracker.{h,cc};修改: main/CMakeLists.txt - `face_tracker.h`: `extern "C"` 导出 3 个接口:`face_tracker_start/stop/get_fps` - `face_tracker.cc`: 三重保护 1) Kconfig 层面(批次 1 已加 depends on IDF_TARGET_ESP32S3) 2) 代码层面 `#if defined(CONFIG_XIAOZHI_ENABLE_FACE_TRACKING) && defined(CONFIG_IDF_TARGET_ESP32S3)` 守卫 3) 构建层面:CMakeLists.txt `if(NOT CONFIG_IDF_TARGET_ESP32S3) list(REMOVE_ITEM SOURCES "face_tracker.cc")` 骨架任务 pin Core 0 / 优先级 2 / 栈 8KB,每秒打印 `hello from core 0` - `idf.py build` 通过,固件 2.47MB / 剩余 1.47MB (face_tracker.cc.obj 已被编译链接) - [!] T06 偏差:2026-04-17 - PLAN 中 T06 依赖 T07 的 uart_send_face 符号,但批次 2 未做 T07 - 采取方案:face_tracker.cc 中用 `__attribute__((weak))` 前向声明 `uart_send_face` T07 完成后,uart_component.cc 提供的 strong symbol 自动覆盖弱符号 调用处加 `if (uart_send_face != nullptr)` 判空(弱符号未定义时为 NULL) - 此偏差属于"修复 T06 的前置依赖缺失",无需架构层面变更,已内联解决 - [x] T06 完成(代码部分):2026-04-17 - 修改: main/face_tracker.cc - 包含 `human_face_detect.hpp` / `dl_image_define.hpp` / `dl_detect_define.hpp` - 构造 `HumanFaceDetect()`(默认 model_type 由 CONFIG_DEFAULT_HUMAN_FACE_DETECT_MODEL 决定) - 任务主循环:`vTaskDelayUntil(period)` 按 Kconfig FPS → CaptureForDetection → 组装 img_t (YUYV) → detector->run(img) → ReleaseDetectionFrame → 坐标归一化 - 坐标公式严格遵守 RESEARCH Pitfall 7:`cx * 224 / width - 112`(匹配 RP2040 deadzone=20) - PLAN 未定义多人脸排序,补充健壮性:遍历 list 挑 score 最高的 result(避免多脸摇摆) - 启动时打印 `PSRAM after detector init` 供 R2 OOM 风险追踪 - 每 10 秒打印 `face stats: hit/miss/fps` - `idf.py build` 通过,固件 2.50MB / 剩余 1.46MB (36% free) — 相比 T05 +30KB (esp-dl 推理库 + human_face_detect 模型注册表代码被链接) - **实测部分待 T12**:需烧录后将人脸对准摄像头验证 score / infer 时长 / FPS 若 score < 0.5 则进入决策点 D-B(改为 DL_IMAGE_PIX_TYPE_RGB565LE) - [x] T07 完成:2026-04-17 - 修改: main/uart_component.{h,cc} - `uart_component.h`: 新增 `uart_send_face(int,int)` 声明,用 `extern "C"` 包裹 以保证 C 链接名(匹配 face_tracker.cc 的 `extern "C" __attribute__((weak))` 前置声明) 其他函数保持原 C++ 修饰名不变,不影响 main.cc/display.cc 现有调用 - `uart_component.cc`: * 新增 `static SemaphoreHandle_t s_uart_tx_mutex`,在 `uart_init_component()` 末尾创建 * `uart_send_string()` 整体加 mutex 保护(防止与 uart_send_face 并发撕包) * `uart_signal_start/stop` 经由 uart_send_string 间接加锁,无需重复保护 * 新增 `extern "C" void uart_send_face(int,int)`:snprintf 到 24 字节栈缓冲, 加锁后 `uart_write_bytes(buf,n)` + `uart_write_bytes("\r\n",2)`,与现有格式一致 - [!] 小偏差(Rule 2):PLAN 示例中 header 未用 extern "C",但 face_tracker.cc 的弱符号 前置声明是 C 链接,strong 实现必须也是 C 链接才能覆盖 weak;加 extern "C" 包裹解决 - `idf.py build` 通过,固件 0x280760 = 2.51MB / 剩余 36% (1.46MB),相比 T06 几乎持平 (仅 +数百字节,符合 PLAN T07 "< 1KB" 预期) - **nm 验证**:`libmain.a` 中 `uart_send_face` 为 T(strong 定义),`face_tracker.cc.obj` 中为 w(weak 引用)。弱符号覆盖链生效。最终 ELF 暂时没这些符号是因为 T11 未做, application 未调用 face_tracker_start,触发链接器 DCE 把整个 face_tracker 子图剔除。 T11 接入后会自动拉入 uart_send_face 的 strong 实现。 - 未添加 test hook(PLAN DoD 中提到的 `uart_send_face(42,-30)` 临时调用), 留给 T12 端到端联调时用真实 face_tracker 数据验证