First Meeting · 初次见面

基于 watcherobot 0.1.9 共享 Runtime 的独立受管理 Application。全部机器人行为通过 ApplicationContext.robot 调用,Application 不直接连接硬件,也不改写 Daemon 路由。 相机和跟随需要配套的 ESP32 / Himax / STM32 固件;本例不是固件安装器。

使用

Windows PowerShell:

./run.ps1
./run.ps1 --test

macOS / Linux Shell(用于运行测试和开发检查;当前真机清单仅声明 Windows):

sh ./run.sh
sh ./run.sh --test

两个入口均委托 launch.py,参数、默认行为与退出码一致。Linux 入口可运行纯 Python 单元测试;当前 schema 3 Application 清单只声明 Windows 真机支持。

启动入口调用 SDK CLI app run,由现有 Daemon 注入 Desktop / Device channel。 不要直接运行 python app.py。 关闭网页不会停止 Application;网页“停止”只停止 交互、释放媒体资源,保留控制台;watcherobot app stop 停止整个 Application。

首次安装依赖可以使用:

python -m pip install watcherobot==0.1.9 httpx numpy pydantic pytest

桌面 Daemon 已运行时,Python 启动器必须位于它配置的 managed-app-root 中。 本次已为应用建立独立开发环境,并将解释器路径写入被 Git 忽略的 artifacts/launcher.json。launch.py 自动选择该解释器。本机路径不属于应用源码, 换电脑时应使用新机器受管理的应用环境重新配置,不能复制旧路径或绕过启动器检查。

控制台仅监听 127.0.0.1,每次启动分配空闲端口并打开网页,地址另存于 artifacts/dashboard.url。设置 FIRST_MEETING_NO_BROWSER=1 可关闭自动打开浏览器。

交互流程

  1. “开始开机流程”:睡觉循环 → 苏醒与抬头 → 左看并回中 → 右看并回中 → 眨眼 → 指定开场白。
  2. 显示“正在聆听”后说“我叫小明”。机器人询问是否可以拍照。
  3. 只有当前正在询问拍照且收到完整的明确同意句,才会调用 SDK 相机一次。 否定、疑问、条件句及含糊回答不会触发相机;不拍照也可继续聊天。
  4. 照片和姓名成功保存后,才播报“好的,我记住你了。接下来你想跟我聊些什么呢?” 如果拍照失败,需要再次明确同意才能重试。
  5. 之后由阿里云百炼 qwen-flash 进行一问一答,火山负责 STT 和 TTS。

网页可开启“端侧人脸跟随”:开场结束后启动设备上的人脸检测与跟随,不上传预览图像。 拍照时先确认跟随停止,再拍照,照片保存后恢复跟随;停止应用时释放跟随。 跟随恢复失败会单独记录,不把已保存的照片误报成失败,也不会声称恢复成功。 该开关默认关闭,需要可用的人脸模型及支持跟随命令的配套固件。 已确认的产品规则是:预览期间不发起拍照;拍照前停止跟随,完成后恢复此前开启的跟随。 本应用采用相机顺序使用,三路同时运行不属于本阶段需求。

“直接对话”跳过开机表演;“只测左右看”仅测试已标定的两段眼神与运动。 文本输入也进入同一状态机,不会绕过拍照同意判断。姓名目前识别“我叫 / 我的名字是 / 叫我”句式。

录音来自机器人麦克风,而非电脑麦克风。应用使用 SDK open_pcm() 接收解码后的 16 kHz 单声道 PCM,以能量阈值和停顿切分句子。录音会先关闭,再识别、合成、播放, 避免麦克风与喇叭争用,也防止把自己的回答当成下一句。暂不支持在机器人说话时打断; 可以随时点击“停止”。停止会取消云请求,等待正在执行的 SDK 操作结束或停止确认。

“应用启动后自动开场”默认关闭,开启后会在该 Application 启动并连接机器人时执行一次。 这是主机 Application 的启动流程;电脑和 Daemon 必须运行,不替换机器人固件的上电流程。

左右看时间轴

方向以机器人自身为准,面对机器人的观察者看到的画面左右相反。 静默 1 基本没有方向变化,静默 2 自带左右扫描,不能分别当成单向左看和右看。 当前使用逐帧确认过的单向资源;更换资源必须重新标定,而非只改动画名称。

源文件位于固件 firmware/s3/assets/gif/,均为 206 × 206、10 fps、每帧 100ms:

资源 帧数 / 总时长 关键帧(零基) 应用运动
standby3 36 / 3600ms 2–4(200–400ms)初次看向画面右侧;13–20(1300–2000ms)继续转眼;29–34(2900–3400ms)回中 90→95° / 200ms;95→108° / 700ms;108→90° / 500ms
standby4 24 / 2400ms 1–5(100–500ms)看向画面左侧;5–16 保持;17–22(1700–2200ms)回中 90→78° / 400ms;78→90° / 500ms

SHA-256:

standby3.gif a80525b1c823e2c5e24106aacb181f95089268190495c4b5fcb66e175a604f97
standby4.gif a7d6b45c69982ea6722290812a8ac1545c72cccb6e6d782dee04d735b0396a40

动作采用单调时钟的绝对偏移,不累加每次网络请求与 Job 完成等待的时间。 每段先发 SDK 动画预取提示,再播放动画;整段结束等待动画 Job 完成。 若动作启动调度落后超过 150ms,会停止,而不是继续播放错位动作。 默认各段按 50 Hz / 500–2500μs 舵机估算约 4–8μs 每有效帧,避免过小步进和大幅跳动。 修改左右角度也会改变这项机械参数,应重新测试。

SDK 0.1.9 没有暴露 LCD 首帧呈现时间戳,因此这里是基于命令接受时刻的应用时序, 不宣称硬件逐帧锁相。日志中的调度偏差是 Python 发起动作相对计划时刻的偏差, 不等同于屏幕实际显示与舵机的物理延迟。源 GIF 均匀帧时长也不能证明真机没有丢帧。 2026-09-04 的该版真机测试调度偏差 3–25ms,用户确认方向与效果良好。

云服务和本机数据

“检测云服务”会合成一句短文本,将其重采样后识别,再测试 LLM。此检查不调用真机相机, 也不播放喇叭。接口说明见 火山 TTS、 火山流式 STT、 阿里云 Chat API。

artifacts/settings.json 保存配置和密钥;artifacts/photos/ 保存授权照片; artifacts/person.json 保存姓名、照片文件名和同意记录;artifacts/meeting.log 是滚动日志。 上述目录均被 Git 忽略。网页配置接口不回传密钥,空白密码框表示保留已有值; 错误响应也不回显原始密钥或云服务响应体。页面 API 使用每次运行的独立令牌并拒绝跨源访问。

姓名与照片保存在本机,没有做人脸识别,也不会把照片发给 LLM。当前仅支持单人会话, 再次启动对话会重新认识用户,历史照片不会被当作新用户的拍照授权。

验证

测试覆盖开机顺序、关键帧时间轴、网络延迟不累计、过期动作停止、同意与否定语义、 照片失败不报成功、重复启动互斥、取消云请求、麦克风释放、ASR 二进制帧、 TTS 截断错误、密钥脱敏、跨源拒绝、离线启动拒绝及发布时排除本机运行数据;另覆盖跟随停止失败不得拍照、 拍照失败恢复跟随、取消时不恢复,以及照片已保存但恢复跟随失败的独立处理。当前共 45 项测试,包含 PowerShell / Shell 入口的参数和退出码一致性。

已完成真机运动(用户确认左右看流畅且方向匹配)、TTS 播放、文本输入到 LLM 再到 喇叭、拒绝拍照继续聊天验证。云端合成→识别回环已通过。真人语音已走通告知姓名、 询问拍照和识别明确同意,录音未丢帧或出现解码错误。

2026-09-04 首次真人拍照超时,随后麦克风返回 no_capacity。当时 ESP32 使用 SSCMA, Himax 却运行 PTL 视频桥接固件。2026-09-05 的独立嵌入式维护已对齐 SSCMA 固件, 保留既有人脸模型槽位 4,并补齐 STM32 对人脸跟随运动来源的支持。 相机初始化失败清理、SPI DMA 缓冲和拍照交付时序也纳入固件回归。 应用仍全部通过受管 Application 环境中的 SDK 0.1.9 交互,未增加串口拍照或 Daemon 业务旁路。

本次真机测试记录及固件版本以 ESP32 仓库的 docs/camera-sdk-e2e-20260905.zh-CN.md 为准。 其中工程拍照与真人姓名/同意流程分开记录,不把自动化测试当作新的真人授权。 首次左右看已由用户确认;本次端侧跟随观感也已获得用户真机体验确认。 后续仍需专门验证复杂光照、遮挡、多目标切换和长时间运行稳定性。

应用不会在启动或重连时自动初始化相机;开启跟随或明确同意拍照后才使用视觉能力。 本次维护固件启用了 Debug CLI,可用项目调试入口打开设备 Python SDK 应用; 关闭调试入口的产品固件仍需通过设备屏幕进入。

语音会话与静默行为修复(2026-09-05)

该语音会话修复最初在 SDK 0.1.8 源码版完成,现已迁移到 SDK 0.1.9 共享 Runtime 分支。配套固件在关闭麦克风时等待录音与音频发送线程释放后才 ACK;SDK 将“音频流结束”与“设备麦克风释放”分开处理,避免安静等待超时后下一轮被旧结束标记误关,以及异常后设备继续显示倾听。

控制台显示当前收音帧数、音量/阈值、讲话检测、识别及错误状态。日志保留会话编号、结束原因和异常调用位置,不记录异常源码行、局部变量或云端原始响应。连续安静时仍会等待新一句话;识别返回空文本会明确记录。

固件的 SDK 应用在播音、录音或拍照结束后恢复清醒静默,显式睡觉流程继续使用睡觉动画。静默候选为 standby1–6、standby_little1–4、blink、blink_fast,只选择设备已经安装的资源,按权重选择且避免连续重复。当前测试设备没有 blink_fast,因此自动跳过。standby1–4 需要对应动作资源;跟随开启时排除这些候选。静默不插入声音,倾听、说话、拍照及显式表情会抢占它。

拍照有独立的结束路径:资源目录中 photo 动画完成后会自动转到 standby_loop。 配套固件为 SDK 系统行为增加单次呈现的结束控制权,阻止该次呈现自动执行目录中的结束跳转, 由 SDK 在任务结束后切回 awake_idle。下一次普通呈现会清除这项控制权,不修改资源目录或全局睡眠语义。

实机已验证连续安静等待超过三个录音周期仍可继续监听;真人姓名、拍照同意和后续问答已走通。 拍照表情补丁烧录后,两次工程测试均保存照片并直接回到 standby_little1,后续播音成功, 未出现 standby_loop 首帧。两次录音各收到 32,000 字节 PCM,丢帧与解码失败均为 0。 跟随恢复在这两次测试中分别为成功、失败;失败时 SSCMA 接收缓冲持续报满并返回连接超时, 一次有界重试也未恢复。该相机重连问题尚未解决,应用保留已保存照片并单独报告跟随恢复失败。

开机与跨应用回归补充(2026-09-05)

本轮实际运行 First Meeting 开机流程,发现设备状态轮询每秒重新创建 HTTP 客户端, 在 Windows 上同步初始化约耗时 260–320 ms,阻塞与动作共用的事件循环,触发左右看 150 ms 的迟到保护。现在由后台线程初始化并复用状态连接;退出时即使查询已取消, 也会等待初始化完成并关闭连接。保持原有动作关键帧和迟到保护,不放宽同步容差。

另一个问题发生在 Test Bench 视频、拍照后切回开机应用:设备有剩余内存,但最大 连续内部块仅 3584 字节,无法创建 4096 字节的录音任务栈。配套 ESP32 修复在 SDK 麦克风启动前回收闲置本地音效任务,再预留语音运行时;复用已有音效生命周期, 没有改动 Daemon 路由或 RTC 全双工音频路径。详见 ESP32 仓库 docs/development/sdk-camera-video-backend-regression.md。

验证:本应用 42 项测试通过。真机顺序为 Test Bench 视频 → 停止视频 → 系统拍照 → 切换 First Meeting → 睡觉/苏醒/抬头/左右看 → 开场白 → 跟随与倾听。 该轮左右看调度偏差 3–13 ms;网页收到录音,丢帧和解码失败为 0,空识别后继续监听。 这是硬件指令、实际音频上传和生命周期回归,不替代真人对屏幕色彩、动作观感的确认; 也不意味着视频高帧率或长时间稳定性已完成验收。