造物集

一台为孩子设计的家庭早教设备

从孩子的日常需求出发,独立完成低成本硬件、多媒体显示、英语学习与语音交互的完整实现。

01 · 为什么要做这台设备

小孩正在读幼儿园中班,日常需求很明确:看绘本、听故事、看适龄动画,以及接触简单英语。

固定内容的玩具故事机难以更新,故事质量需要家长筛选;订阅型产品的长期成本较高。由家长选择、下载和整理内容,可以花一些精力换取更合适的阅读品质,也减少持续订阅的支出。

动画也是如此。英语版《小猪佩奇》等内容已经由家长挑选好,但把手机或平板交给孩子后,孩子往往会自行操作,离开原本的播放内容。因此,我希望把“家长维护内容”和“孩子使用设备”分成两个入口:家长负责筛选和维护内容,孩子主要通过语音完成日常操作,英语学习中的翻页、录音等交互则配合实体按键。网页提供另一个管理入口。

产品目标是一个用途明确、内容可更新、交互简单的家庭终端。它的主要的4种模式分别对应视频、音频、相册和英语学习。

有了这些目标,先看看开发完成后的实机效果。

02 · 实机演示:沿着视频看产品

这段实机视频,展示了默认表情、相册横竖屏切换、视频与动画播放,以及英语绘本学习模式。先从完整演示了解设备的使用形态,再展开讲解硬件与软件实现。

接下来,从器件选择与组装开始,详细说明整套方案。

03 · 硬件选择:把已有器件组织成产品

部件选择与来源选择原因
主控Orange Pi Zero 2W,H618,1 GB 内存;此前约 90 元购入已有器件、成本低,具备 ARM Linux 与多媒体扩展基础
屏幕VRGATE 头显拆机屏,6 英寸、2K,约 99 元HDMI 输入、USB 供电,便于直接连接开发板
扩展板为项目购入,32 元扩展接口与实体热键,方便配网和日常管理
结构件铝型材与固定件,33 元便于搭建、调整和维护原型结构
拾音器二手 USB 拾音器,18 元提供可被 Linux 音频系统接入的采集设备
本地存储手头已有的 32 GB 闪迪 SD 卡承载系统与程序,主要媒体内容由家庭 NAS 提供
声音输出旧电脑小音箱复用已有器件,降低新增投入
屏幕转向存量舵机,使用开发板 PWM 控制适应长条屏幕的横竖屏场景,并利用 40-pin PWM 引脚简化控制

TB.jpg
总成本约合计约 300 元。但很多其实是利旧产物。

存储取舍:本地 SD 卡运行系统,家庭 NAS 管理内容

开发板采用 SD 卡存储,手头可用的是一张 32 GB 闪迪卡。在实际使用中,这块开发板对SD卡的兼容性较挑剔,部分低端卡出现识别问题。若为了存放大量动画和故事而另购更大容量、兼容性可靠的卡,本地扩容的性价比并不理想。

因此,存储方案采用本地 SD 卡与家庭 NAS 配合:SD 卡承载系统和程序,动画视频、故事音频、照片及英语学习内容集中放在 NAS,通过网络共享挂载供设备使用。家长可以在电脑或其他终端整理、增删文件,后期新增一套动画、一批故事或新的学习内容时,主要操作就是将文件整理到 NAS 的对应目录中,再由程序扫描或更新媒体库。日常维护不必拔出 SD 卡,也不必为每次内容扩充单独操作板端存储。

这一选择同时考虑了器件兼容性、扩容成本和内容维护流程。NAS 中的媒体需要家庭局域网与共享服务可用。

为什么选择舵机驱动屏幕

这块拆机屏外形类似手机屏幕,原生为竖屏。照片、动画和学习页面的内容方向不同,需要在横屏与竖屏之间切换,因此将屏幕做成可旋转结构,让显示姿态适应使用场景。

在执行器选择上,手头已有的舵机可以直接控制目标角度,而硬件接入也简单:开发板的 40-pin 接口提供 PWM 功能引脚,可以利用板端硬件 PWM 输出控制信号,无需为这项动作另加 MCU。

软件将目标屏幕方向映射为已配置的舵机角度,再通过 PWM 控制转向。

选好器件后,开始搭建支架、转屏机构和整机连接。

04 · 硬件组装

先搭建支撑结构与转屏机构

铝型材底座与竖向支撑组成设备骨架,透明安装板用于承载屏幕。舵机固定在竖向支撑附近,通过舵盘与安装板连接,使屏幕具备横竖方向切换的机械基础。

JBJG.jpg

再完成屏幕、主控与音频连接

安装屏幕后,将 HDMI 显示连接、屏幕供电、开发板与扩展接口、USB 拾音和 3.5 mm 音箱组合起来。背部保留开放结构,方便检查接线、调试和替换器件。

ZPCG1.jpg
ZPCG2.jpg

硬件组装与软件开发同步推进。

05 · 软件架构:按职责拆分,按场景协作

系统使用 Armbian 命令行环境,减少桌面常驻开销。

服务主要职责关键接口与边界
VisualVoiceRobot / Rust模式、播放队列、媒体索引、显示控制HTTP 控制 API;串行控制器组织播放操作
VisualVoiceManager / Rust配网、挂载、蓝牙输出和 LRADC 热键网页管理入口;Unix socket 协调显示
VoiceCtrlAssistant / Rust音频输入、离线唤醒、在线识别与意图控制调用 Robot API,按执行结果组织反馈
英语学习服务 / Go图书、章节、录音、页面状态与遥控HTTPS API、WebSocket、SQLite、内嵌前端
显示子进程Slint 相册/默认界面与 Cog 网页独立生命周期,按模式交接显示资源

业务链路可以概括为:语音或实体输入 → 意图 / 控制命令 → 主程序或学习服务 → 媒体与显示执行 → 状态反馈。 语音承担日常内容选择与控制,家长管理页提供网络、内容来源和播放状态的管理入口。

独立服务让网络配置、语音交互、媒体播放与学习页面分别迭代。接口形成了清晰的协作契约,也使故障定位可以沿着输入、控制、执行与反馈逐层展开。

06 · 多媒体技术路线:打通硬件加速与显示

从硬件规格到可用的软件链路

H618 集成 Mali-G31 GPU 和视频处理能力,但之前的方法固件文档是说当时的 Linux 版本都不支持。

oldlinux.jpg

自从有了GPT,一切皆可能。 GPT告知Armbian适配的线索,刷了一个尝试了下,确认内核驱动、Mesa/Panfrost、GStreamer 解码器都能正常工作,但其实整个过程中还是存在不少坑,甚至有些是系统内核的肯定,并且在开发的这一个月时间内,Armbian也在不断更新,已经填了不少坑。

视频链路

视频的解码渲染路径为:

GStreamer 解封装 / Cedrus VPU 解码
    ↓ NV12 DMA-BUF / appsink
Rust GBM + EGL + OpenGL ES 合成,输出 FBO
    ↓ native fence
DRM/KMS atomic commit
    ↓ HDMI
1440 × 2560 @ 59.97 Hz 原生竖屏显示

这条路线把媒体解码、GPU 合成和显示提交连接起来。DMA-BUF 用于跨模块传递图像缓冲,避免显式 CPU 转色回退;native fence 参与图形与显示同步。这里不把整条链路笼统宣称为绝对零拷贝,因为仍包含 GPU 合成到 FBO 的过程。

项目实测了 sun4i-drm 的原生 HDMI 模式,并区分显示节点与 Panfrost 渲染节点。判断 VPU 是否实际生效,并检查 active_decoder、hardware_decode 和 GStreamer 的实际 decoder 日志,硬件解码和渲染都已经实现。

显示模式如何切换

视频合成器、Slint 查看器和 Cog 网页是互斥显示会话。主程序在切换前回收旧会话资源,释放并重新取得 DRM master,完成显示所有权交接。独立音频可以与相册共存。

相册查看器使用 Slint 的 linuxkms / femtovg 后端。程序比较照片纵横比与屏幕方向,并通过配置的 PWM 舵机控制调整物理朝向。

网页视频中的工程难点

Cog / WPE WebKit 通过 DRM/GLES 显示页面,网页视频由 GStreamer 与 Cedrus 解码,再交给 WebKit 页面合成器。项目随主程序部署媒体兼容 shim,处理了几个跨组件问题:

问题实现方式工程意义
解码器要求的缓冲元数据缺失为 allocation query 补充 GstVideoMeta连接 WebKit 与 stateless decoder 的协商契约
异常音频时钟影响视频速度固定单调系统时钟,音频 sink 跟随系统时钟把媒体同步问题与解码性能问题区分开
GPU 采样未完成,解码缓冲提前复用有界保留最近 3 个 DMA-BUF 帧管理跨模块缓冲生命周期,避免提前覆写
无 GL context 的清理线程首次解析资源释放入口预解析 GL 入口,并处理 EGL provider 转发处理多视频页面退出与资源清理的稳定性

这些细节体现了对驱动、媒体框架、GPU 同步和资源生命周期的理解。项目的技术深度来自将这些边界逐一打通。

07 · 英语学习:从网页内容到实体互动

后端采用 Go + Gin + SQLite,前端采用 Vue 3 + TypeScript + Vant。通过 go:embed 将构建后的前端嵌入 Go 程序,在同一 HTTPS 端口提供页面与 API,减少部署文件与跨域配置。

章节视频通过 HTTP Range 提供流式访问;页面使用 MediaRecorder 录音,服务端按用户、图书和章节保存记录。HTTPS 同时满足浏览器麦克风所需的安全上下文,证书与权限需要按板端环境配置。

媒体库支持分级或扁平图书目录,启动扫描后写入 SQLite,并通过 fsnotify、防抖重扫处理文件变化。章节稳定 ID 与历史记录保留策略,让内容更新不会简单破坏已有录音关系。

实体输入经 Go 服务映射,再由 WebSocket 驱动页面;断线重连与服务端页面状态恢复使学习过程能够继续。这里的设计把输入设备与页面布局解耦,后续可以调整遥控器或页面而保持控制协议。

按键方案的演进:先验证交互,再完善专用硬件

英语学习需要翻页、选择图书、录音和回放等少量操作。最初考虑通过开发板 40-pin 接口上的 UART 接入矩阵键盘,但矩阵键盘本身不能直接输出 UART 命令:要形成完善的输入模块,还需要 MCU 扫描行列按键、处理消抖,再通过串口发送事件。

既然需要增加 MCU,便进一步考虑做一个机械小键盘,为孩子提供更明确的实体按键。为体验 PCB 制作流程,已经使用嘉立创的免费打板服务完成一版小键盘 PCB 打样,免费打板的体验也不错。不过,当时以体验为主,方案选择较随意,后续组装、固件和调试仍有工作量,目前尚未开始组装与实现。

JPDLB.jpg

当前测试直接通过 USB 连接电脑键盘,利用现有 Linux 输入事件链路验证网页操作。软件先把按键转换为翻页、确认、返回等控制命令,再通过 WebSocket 驱动页面;因此后续更换专用小键盘时,可以复用已有的业务控制协议。

阶段输入方案当前状态与取舍
最初设想矩阵键盘 + MCU 扫描 + UART需要额外完成扫描、消抖和串口协议,未作为当前测试输入
硬件探索自制机械小键盘已通过嘉立创免费服务打样 PCB,尚未组装与实现
当前验证USB 电脑键盘用于测试学习页面的选择、翻页与录音等操作

这项取舍让网页互动与专用输入硬件分别推进:先把使用流程跑通,再根据实际操作需求完善按键布局和硬件实现。

08 · 语音控制与音频共享

语音交互采用本地 sherpa-onnx 唤醒与在线 ASR、LLM、TTS 的组合。语音程序独立运行,通过主程序接口控制设备。

USB 麦克风 / ALSA 采集
 → 音频前处理 → 离线 KWS / VAD
 → 在线 ASR → LLM 结构化意图
 → 本地校验 → 媒体检索 / Robot API
 → 按真实执行结果生成反馈 → 在线 TTS → 扬声器

语音设计将实时音频采集与网络等待分离:采集线程通过预分配、有界队列交付音频,业务运行时处理请求、超时和取消。新唤醒通过会话代次与取消机制隔离旧任务,避免已取消的云端回复继续触发操作。

LLM 输出先转为结构化意图,再由本地校验与路由执行。回复依据真实执行回执生成,让“已经播放”这样的反馈对应实际执行结果。语音设计覆盖媒体检索、播放控制、相册和英语学习入口。

音频共享:一套拾音与外放设备,服务多个程序

设备只有一个 USB 拾音器和一套接在板端 3.5 mm 输出上的音箱,但使用音频的程序不止一个。语音助手需要持续监听唤醒词,英语学习网页需要录音;语音回应与故事、动画播放也需要使用同一套音箱。

这里有两个不同的问题:输入端需要把同一份采集音频提供给多个消费者,输出端需要将多个播放来源送往同一个硬件出口。仅在各程序中填写相同的声卡地址,不能保证多进程共享成功。

可选路线适用特点本项目的取舍
按场景独占设备、停止旧程序后再交接实现较直接,但采集交接期间语音监听会中断不适合作为持续唤醒与网页录音的主要共享方式
PulseAudio / PipeWire 音频服务集中管理路由、混音和设备,便于复杂音频策略功能丰富,但增加常驻服务与集成配置;是否采用应由功能需求决定
ALSA 插件共享在 PCM 层提供采集共享、播放混音及格式转换输入采用 dsnoop,输出采用 dmix,复用 ALSA 插件完成共享
自建统一音频代理统一采集、混音、回声参考与优先级控制能力强,但需自行维护跨进程传输、时序和恢复机制

USB 拾音器:采用 dsnoop 共享采集

主程序 README 明确记录了 ALSA dsnoop 方案,并提供命名采集端 vvr_mic_shared。语音助手和英语学习录音都接入共享 PCM,由 dsnoop 将同一硬件采集流提供给不同消费者。

USB 拾音器 / ALSA 硬件采集
    ↓ dsnoop 共享采集端(vvr_mic_shared)
    ├─ 语音助手:持续唤醒检测与语音识别输入
    └─ WPE WebKit:英语学习录音

共享采集端需要固定硬件支持的采样格式、采样率与通道数;不同消费者若有不同要求,可在共享端外通过 plug 或应用侧转换。plughw 本身负责格式转换,不能代替 dsnoop 的多客户端采集共享。

浏览器接入还涉及设备发现:项目部署了 WebKit 设备枚举插件,让网页录音能够选择共享 PCM,而不是绕过它直接打开 USB 硬件。安装流程还会迁移已知的语音助手直连配置,并检查重启后的采集就绪状态。

这一选择让英语学习录音和语音唤醒可以接入同一拾音硬件,减少模式切换时的设备释放与重开。dsnoop 只解决音频流共享;是否在跟读期间处理唤醒事件,以及如何避免课程声音干扰识别,仍由业务与回声处理策略决定。

3.5 mm 音箱:统一播放入口与混音方案

现有 README 确认媒体播放通过 GStreamer alsasink 使用命名输出 vca_output,默认连接板端 3.5 mm 音箱。语音回应也接入同一个共享输出入口,避免两个程序分别独占硬件播放设备。

输出端采用的方案是 ALSA dmix 软件混音:多个程序提交各自的 PCM 音频,由共享混音端合并后输出到板端声卡。不同来源的采样率或通道格式,可以通过外层 plug 适配。语音回应与媒体播放因此能够共享同一套音箱

语音助手 TTS ─────────────┐
                         ├─ 统一共享 PCM → dmix → 板端声卡 → 3.5 mm 音箱
GStreamer 故事 / 动画音频 ─┘

该方案要求各进程进入同一个混音实例,并统一底层设备、格式与共享参数;仅给两个输出配置相似的名称,不能保证它们实际共享。网页使用 WebKit 的 autoaudiosink,还需要核对它最终选择的输出路径,确保它没有绕过共享入口。

能同时播放,还需要能听清楚

采集共享和播放混音解决的是设备访问问题。语音回应时是否降低媒体音量、暂停故事、回应结束后恢复播放,则属于业务策略,不能由 dmix 自动完成。媒体音量与 TTS 音量应分别控制,避免修改总输出音量时一并压低语音回应。

同样,音箱外放会重新进入麦克风。dsnoop 与 dmix 都不提供声学回声消除;AEC 需要与实际外放相对应的参考音频。如果语音助手只取得自己的 TTS 参考,就不能宣称同时消除了其他进程播放的故事或动画回声。完整的外放唤醒和打断效果,需要结合最终混音路径与实机测试验证。

这部分设计体现了对音频设备、格式协商、多进程共享与业务交互边界的区分:底层解决“多个程序能否接入”,上层解决“孩子在这个场景中该听到什么”。

09 · 技术选型背后的判断

选择对应的约束带来的价值
Armbian minimal1 GB 内存、板端驱动需求减少桌面开销,为应用保留资源
Rust 主程序与管理服务原生库接入、模式状态与资源交接用明确的生命周期组织底层能力
GStreamer音视频管线与硬件 decoder 接入复用成熟媒体框架,关注平台适配
GBM / EGL / GLES / DRM直接控制嵌入式显示链路将解码、合成与输出统一组织
Slint相册、默认表情与原生显示独立界面子进程,清晰管理模式生命周期
Cog / WPE WebKit网页学习及网页视频复用 Web 内容生态与板端媒体能力
Go + 内嵌 Vue 前端内容服务、录音与部署单程序承载服务与页面,便于 ARM64 部署
WebSocket实体输入到网页的实时控制输入协议与页面布局解耦
SQLite / FTS5本地索引、检索、学习状态保持数据服务轻量,减少外部依赖
离线唤醒 + 在线识别低资源设备与自然语言交互本地持续监听,在线处理复杂语言任务
ALSA dsnoop 共享采集语音常驻监听与网页录音共用 USB 拾音器在 PCM 层共享输入,配合 WebKit 枚举接入
命名播放 PCM / dmix 混音TTS 与媒体共用 3.5 mm 音箱统一输出并支持混音,业务层独立协调音量与优先级

10 · 第一次使用:扫码配网与日常操作

第一次使用先完成配网。设备没有可用网络,或用户通过扩展板热键触发时,进入配网模式:开发板切换为 Wi-Fi 热点,屏幕显示二维码。手机扫码并连接设备热点后,进入 Wi-Fi 配置页面,填写家庭网络信息并提交,设备再退出热点模式、切换为客户端连接家庭网络。

使用顺序:开机 → 进入配网 → 扫码连接设备热点 → 填写并提交家庭 Wi-Fi → 设备切换连接模式 → 回到日常界面。

SZWL.jpg

为什么采用热点与客户端分时切换

Orange Pi Zero 2W 的官方用户手册将无线器件列为 20U5622,支持双频 802.11 a/b/g/n/ac 与蓝牙 5.0。这里按官方手册使用该型号标识;模块名称、内部芯片名称与 Linux 驱动名称不应混为一谈。Orange Pi Zero 2W 用户手册,硬件特性

在本项目的板端驱动环境与使用验证中,Wi-Fi 不能同时承担热点(AP)和客户端(STA)两种角色。因此,配网需要按阶段交接同一无线接口。这个限制按项目环境描述,不能仅凭型号断言芯片在所有系统中都不支持 AP + STA 并发;后续可附板端 iw list 的有效接口组合记录。

另一条路线是通过蓝牙连接手机,把 Wi-Fi 信息发送给设备。但在本项目考虑的实现方式下,还需要配套 App,增加开发、安装和维护成本。最终选择热点加本地网页,让家长使用手机已有的扫码与浏览器能力完成配置。

这一取舍降低了首次使用门槛,也让配网入口在尚未接入家庭网络时仍可用。AP 切换为 STA 时,手机与设备热点的连接会断开,属于切换过程的正常现象。扫码后的页面弹出方式可能因手机系统而异;连接热点后也可访问 http://192.168.4.1/。

配网如何与播放系统协作

网络管理由独立 Rust 服务 VisualVoiceManager 承担,通过 Unix socket IPC 请求 VisualVoiceRobot 暂停视频或图片、全屏显示二维码,并在配网成功后恢复播放。

这让网络管理与媒体渲染有清晰的职责边界。当前 README 还明确了浏览器模式的差异:Cog 占用显示时,需要先停止网页,才能显示配网二维码。

配网后的日常入口:以语音操作,网页辅助管理

配网后,孩子主要通过语音选择故事、动画、相册或英语学习模式。语音程序解析意图,检索家长准备的内容并调用主程序执行。许多日常操作可以直接通过语音完成,无需先打开网页。网页则为家长提供另一种明确的管理与控制方式。

配网完成后,设备默认界面显示眨眼表情和当前 IP。家长在同一局域网通过 http://设备IP:8088/ 打开管理页,配置内容目录、选择音频输出,或直接选择媒体播放。

家长配置 NAS,并持续扩充内容

视频、音频和照片统一由家庭 NAS 管理,通过 SMB/NFS 等网络共享挂载到设备。家长在 NAS 目录中整理和更新内容,设备读取对应目录,无需逐次向 SD 卡复制媒体文件。英语学习目录支持读写,用于学习内容与录音数据;视频、音频和图片目录采用只读挂载。

gzml.png

主程序启动后扫描音视频目录,并按配置定时同步到 SQLite。媒体检索采用 jieba-rs 分词与 FTS5 召回,为网页选择和语音找内容提供统一的媒体库基础。

检索、播放与队列管理

家长可以查看文件列表、输入关键词检索、立即播放或加入队列。当前管理页截图展示了一组包含 108 条文件的音频库;这是该次演示的数据量,不代表设备容量上限。

playlist.png

视频和音频支持暂停、恢复、下一项和队列播放;视频还支持时间跳转。播放结束后自动取同类队列下一项,队列耗尽后回到默认界面。相册与独立音频可以共存,因此照片展示和听故事可以组合使用。

英语学习中的翻页、录音与回放

进入英语学习模式后,设备运行英语绘本网页。孩子可以通过板端输入选择等级和图书、翻页、开始或停止录音,并回放自己的录音。

Go 服务读取 Linux 键盘事件,将其映射为控制命令,再通过 WebSocket 推送给页面。浏览器负责界面响应,实体输入不必模拟鼠标点击。页面状态保存在 SQLite,重连时恢复等级、图书和章节位置。

目前使用 USB 电脑键盘测试,具体按键映射以板端配置为准。评分页面当前使用 Mock 分数,不分析真实发音;真实语音评测通过 Provider 接口预留。

从默认界面进入不同场景

默认画面显示眨眼表情与当前 IP。听故事时可以保留默认画面或配合相册;看动画时进入视频模式;查看照片时根据内容方向转屏;英语学习则进入绘本网页,通过按键完成章节切换和录音操作。媒体队列耗尽或停止播放后,设备恢复默认显示。

SWXG.jpg

11 · 总结

项目的广度体现在跨领域能力形成了完整的使用流程:从结构和电气连接,到操作系统、驱动、多媒体、网页、实时通信和语音服务,每一层都服务于同一组家庭需求。

软件设计功底体现在职责边界、状态和资源管理:模式切换交接显示所有权,媒体队列维护播放过程,实时音频与网络任务分离,麦克风通过共享采集协调,网页状态在重连后恢复。

独立完成路线的能力,则体现在把“器件可以连接”推进到“服务能够部署、内容能够管理、使用流程能够展示”。照片与视频支持原型的实物及显示效果,README 提供接口和实现说明;二者结合让工程叙述可追溯。

从低成本器件到实际可操作的设备,这个项目将需求、硬件、系统软件和交互组织成了一套完整方案。其价值体现在选择合适的技术、处理组件之间的边界,并独立把整个使用流程连接起来。

12 · 后续补充

下一步是完成英语学习专用小键盘。PCB 已通过嘉立创免费打板服务完成打样,后续需要推进器件组装、固件实现、按键映射及整机联调。目前继续使用 USB 电脑键盘验证学习页面的交互。

已有 WebSocket 控制协议将按键输入与页面操作分开,专用键盘完成后可以复用翻页、确认、录音和回放等业务指令。