小智AI全套PCBA中月度成就回顾播报统计逻辑实现

你有没有发现,现在家里的智能音箱越来越“懂”你了?不只是叫它放首歌那么简单——有时候它会突然冒一句:“这个月你叫我最多的一天是15次,真是离不开我呢~” 😄
这种像朋友一样贴心的“月度总结”,背后其实藏着一套精巧的设计逻辑。今天咱们就来扒一扒,小智AI这套基于PCBA(印制电路板组装)系统的 月度成就回顾播报 ,到底是怎么在资源有限的嵌入式设备上跑起来的。

别看它只是说几句话,这背后可是一整套从数据采集、状态追踪到自然语言生成的完整链路,而且全程本地化处理,不联网、不上传,隐私安全拉满 ✅。最关键的是——整个系统运行在只有几百KB内存的MCU上,却能稳定输出有温度的语音反馈 🎯。


从一次清晨唤醒说起

想象一下:每天早上7:30,你习惯性地喊一声“小智,早安”。
第一次你觉得没什么;第十次你觉得挺默契;到了第28次……某天早上它突然回应:

“恭喜你达成『早起之星』成就!本月连续28天准时打卡,作息规律堪比闹钟本钟~继续保持哦!”

是不是瞬间有种被认可的感觉?👏 这就是“成就系统”的魔力:把冷冰冰的交互行为,转化成有情感的记忆点。

而要实现这一切,核心就在于四个字: 统计 + 调度

我们得回答几个关键问题:
- 怎么知道你是“连续”早起?
- 每月什么时候清零重计?
- 数据断电后会不会丢?
- 说了这么多话,会不会卡顿甚至死机?

接下来,我们就一层层拆开来看。


成就不是简单的计数器,而是个聪明的状态机 💡

很多人第一反应是:“哦,那就搞个变量 wake_up_count++ 不就行了?”
但现实远比这复杂。比如:
- 用户中途有一天没喊你,后面又开始喊,算不算“连续”?
- 达成了要不要播报?播过了还能不能再播?
- 如果设备重启了,进度还能不能接上?

所以,我们用了一个更稳健的模型: 有限状态机(FSM)

每个成就都有四种状态:

状态 含义
IDLE 还没开始触发
PROGRESSING 正在积累进度
COMPLETED 目标达成,待播报
ANNOUNCED 已经说过一遍,不再提醒

举个例子,“本月第10次唤醒”这个成就:
- 初始为 IDLE
- 第一次唤醒 → 变成 PROGRESSING ,计数+1
- 第10次唤醒 → 达到目标,进入 COMPLETED
- 播报完成后 → 标记为 ANNOUNCED

这样一来,既避免重复打扰,又能准确跟踪生命周期。

代码层面我们也做了模块化设计,方便扩展👇

typedef struct {
    uint8_t id;
    const char* name;
    uint32_t target_value;
    uint32_t current_value;
    achievement_state_t state;
    void (*update_func)(uint32_t event);
    void (*generate_text)(char* buffer);
} achievement_t;

你看,这里用了函数指针,意味着不同类型的成就可以有自己的更新和文案生成逻辑。新增一个“音乐达人”或者“深夜党”?加个结构体注册进去就行,完全解耦 🧩。


时间不准,一切白搭 ⏰

成就系统是以“月”为单位的,那问题来了:你怎么知道今天是不是 每月1号

很多低端设备靠系统上电读时间,一旦断电久了,时间就乱了。但我们不能让用户每个月手动校准吧?

解决方案很清晰: 硬件RTC + 自动校时

小智AI的PCBA板载了一颗高精度RTC芯片 DS3231,配合ESP32主控自带的低功耗定时器,能做到:
- 断电后靠纽扣电池维持走时
- 日误差小于±1秒/月
- 支持I²C接口读取,资源占用极小

启动时先从RTC拿当前时间,再通过Wi-Fi连接NTP服务器做一次精准校准。后续即使断网也能靠RTC继续运行。

然后每天凌晨0:05,系统会被RTC闹钟中断唤醒(此时CPU处于light-sleep模式,电流<10μA),执行一次检查👇

void monthly_routine_check() {
    rtc_time_t now = rtc_get_time();
    static uint8_t last_month = 0;

    if (last_month != now.month && now.day == 1) {
        backup_last_month_achievements();
        reset_current_month_counters();
        set_announce_flag(true);  // 准备播报
    }
    last_month = now.month;
}

注意这里的防抖设计:用 last_month 缓存上个月份,防止因时间跳变或多核竞争导致重复执行。毕竟谁也不想大半夜听到设备连着播报三遍“欢迎来到新月份”😅。


数据从哪来?事件驱动才是王道 📢

用户的每一次操作——唤醒、播放、调节音量、环境感知……这些都属于“事件”。

我们在系统内部搭建了一个轻量级的 事件总线(Event Bus) ,采用环形缓冲区队列管理,支持异步分发:

[麦克风] → 唤醒检测 → 发布 EVENT_WAKEUP  
[播放器] → 播放结束 → 发布 EVENT_PLAY_STOP(duration=185)  
[传感器] → 检测到移动 → 发布 EVENT_MOTION_DETECTED

成就引擎作为订阅者,监听自己关心的事件。比如“音乐大师”成就只关注 EVENT_PLAY_STOP ,并且要求单次播放超过1分钟才算有效👇

void update_music_duration(uint32_t event, uint32_t duration_sec) {
    static uint32_t total_duration = 0;

    if (event == EVENT_PLAY_STOP && duration_sec > 60) {
        total_duration += duration_sec;
        achievements[MUSIC_MASTER].current_value = total_duration / 60;

        if (total_duration >= 1800) {  // 30小时
            achievements[MUSIC_MASTER].state = ACHIEVE_COMPLETED;
        }
    }
}

同时还有些细节优化:
- 去重机制 :同一动作5分钟内多次触发只记一次(防误触)
- 有效性判断 :短于30秒的播放不计入统计
- 异步处理 :事件入队后由后台任务消费,不影响主流程响应速度

这样哪怕用户一天唤醒几十次,系统也不会卡顿,真正做到“无感统计”。


把数字变成人话:NLG + TTS 的温柔时刻 🗣️

终于到了最打动人的环节:如何把一堆数字,变成一句句温暖的语音?

我们没用复杂的AI大模型,而是采用 模板填充 + 条件拼接 的方式,在本地快速生成口语化文案。

预设一些高情感密度的句式👇

类型 文案模板
唤醒次数 “你这个月叫我 %d 次,比我老妈还勤快!”
最晚入睡 “你最晚熬到 %s,熊猫眼都要出来了🙈”
首次达成 “首次完成『安静夜晚』挑战,为你鼓掌👏”

每个成就都有自己的 generate_text() 函数,根据当前值动态填空。最终整合成一段不超过90秒的语音内容:

void generate_monthly_review(char* output, size_t len) {
    strcpy(output, "亲爱的主人,这是你的本月回顾:\n");

    int announced_count = 0;
    for (int i = 0; i < ACHIEVE_COUNT && announced_count < 3; i++) {
        if (achievements[i].state == ACHIEVE_COMPLETED) {
            char temp[64];
            achievements[i].generate_text(temp);
            strncat(output, temp, len - strlen(output) - 1);
            strcat(output, "\n");
            announced_count++;
        }
    }

    if (announced_count == 0) {
        strcat(output, "这个月你还比较低调,继续加油哦!");
    } else {
        strcat(output, "感谢你的陪伴,我们下个月再见!");
    }
}

几点贴心设计:
- 最多播报3项 :防止信息过载
- 按优先级排序 :重要成就优先说
- 语速适中 :TTS设置为1.1倍速,清晰又不拖沓
- 触发时机讲究 :选在用户高频互动时段(如早8点或晚9点)

甚至还能加入语气词和方言口音包,让声音更有“人味儿”~


整体架构一览:麻雀虽小,五脏俱全 🏗️

整个系统部署在小智AI的主控PCBA上,结构如下:

graph TD
    A[传感器/麦克风] --> B[事件采集层]
    B --> C[事件总线(环形缓冲区)]
    C --> D[成就引擎 FSM]
    D --> E[NLG 文案生成]
    E --> F[TTS 引擎]
    F --> G[DAC + 功放]
    G --> H[扬声器]

    I[RTC DS3231] --> J[时间服务]
    J --> D
    K[NTP] --> J

主要硬件配置:
- 主控:ESP32(双核240MHz,520KB RAM)
- 存储:4MB Flash(存固件、语音库、成就配置表)
- RTC:DS3231 + CR2032电池
- TTS:本地化Kaldi轻量模型 or 中科讯飞SDK

所有数据都在设备端闭环处理,无需联网即可完成统计与播报,真正做到了 低延迟、高隐私、强可靠


实战中的坑,我们都踩过了 🚧

当然,理想很丰满,落地总有波折。开发过程中我们也遇到不少挑战:

问题 解法
Flash容量不够存长期数据 仅保存关键指标,历史数据压缩编码
多成就同时达成太啰嗦 设置优先级队列,最多播3条
用户关闭语音提醒听不到 在APP里同步展示成就徽章
时间不同步导致误判 NTP自动校准 + 手动修正入口

还有一些人性化设计:
- 支持恢复出厂设置时一键清空成就
- OTA升级可动态调整成就规则(比如节日限定活动)
- 内置A/B测试开关,不同用户群体试用不同策略


不止是“表扬信”,更是情感连接的桥梁 🌉

说实话,这种功能看起来像是“锦上添花”,但它带来的用户体验提升却是实实在在的。

当一个机器能记住你的习惯、见证你的改变,并用温柔的声音告诉你:“你进步了”,那种被理解和陪伴的感觉,是任何功能参数都无法衡量的 ❤️。

更重要的是,这套方案完全可以复制到其他场景:
- 儿童陪伴机器人:“宝贝今天自己收拾玩具3次,真棒!”
- 老年看护终端:“阿姨连续5天按时吃药,医生都说好!”
- 车载助手:“本月驾驶平稳率提升20%,老司机稳得很!”

未来我们甚至可以结合简单的行为分析模型,让NLG变得更智能:

“你最近晚上睡得更早了,作息正在悄悄变健康呢。”
“周末听歌时间明显增加,是不是心情变好了?”

这才是真正的“边缘智能”:不在云端炫技,而在身边默默懂你。


技术终将回归人性。
在一个越来越快的世界里,或许最动人的创新,不是多快多准,而是——
有人记得你做过的事,并愿意为你轻轻说一句:我看到了,你很棒。 🌟

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐