云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇

2026-09-242

斯坦福 AI 小镇(Smallville)式的生成式智能体小镇,被我用 DFRobot EasyIoT 平台的三个环境传感器(温度、湿度、光照)“接上了真实现实”——这些数据由行空板 K10(Mind+ 2.0 编程,自带温湿度与环境光传感器)实时采集。两户人家的六口人住进小镇,他们的穿衣、喝水、开灯、作息、对话和记忆,全部由实时环境数据 + 系统时间 + 大模型自主决定。

一、灵感:为什么做一个“AI 小镇”

去年看斯坦福的《生成式智能体》(Generative Agents)论文,里面 25 个 AI 小人住在一个叫 Smallville 的小镇上:他们早上按计划起床、上班、做饭,会约朋友喝咖啡,记得昨天聊过什么,还会在圣诞派对上互相邀约……让我特别着迷的不是“他们像人”,而是 记忆社交 这两套机制让世界真正“活”了起来。

于是我想:能不能把小镇接到真实世界里?DFRobot 的 EasyIoT 平台正好提供了温度、湿度、光照三个环境数据通道,而行空板 K10 一块板子就自带了这三个传感器的采集能力(板载 AHT20 温湿度 + ALS 环境光传感器,详见后文 3.1 节)。我把三者接进小镇——高温了就少穿、干燥了就多喝水、天黑了就开灯。小镇不再只是代码里的一串幻觉,而是和窗外天气同步的生活场景。

项目取名“云天 AI 小镇”:云山县·云天镇·桂花苑小区,两家住户,六位成员,全部由大模型自主决策。

二、系统长什么样

浏览器网页(小镇平面图 + 成员卡片 + 生活日志 + 对话气泡 + 记忆数)
        ▲ WebSocket 实时推送(环境消息 / 状态帧)
        │
本地桥接 server.js(Node.js)
   ├─ MQTT 订阅 ──► DFRobot EasyIoT(iot.dfrobot.com.cn:1883)
   │                温度 z4ksqL6Ig / 湿度 74F2EvvNg / 光照 XZzRM3OHR
   ├─ /api/ai 转发 ──► SiliconFlow(DeepSeek-V3)大模型
   └─ HTTP 静态服务(网页本体 + 历史缓存 + 调试发送)

云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_1.webp

三个 EasyIoT 消息是谁发的都行:本文用行空板 K10(自带温湿度 + 环境光传感器,见 3.1 节)往 Topic 一发,小镇立刻感应到;换成任意 ESP32/Arduino + 传感器也一样。没传感器时,网页还有“演示模式”,拖动三个滑块就能模拟酷暑、严寒、大暴雨、深夜,随时看小镇怎么反应。
云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_2.webp

云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_3.webp

三、数据接入:EasyIoT 的三种身份

做之前先把 EasyIoT 的接口摸了个透:

能力 方式 说明
MQTT 订阅 iot.dfrobot.com.cn:1883 用户名/密码用平台控制台给的 Iot_id / Iot_pwd,Topic 即设备 ID
REST 轮询 POST api.dfrobot.work/easyiot/apiv3/messages/search 官方网页本身就是轮询,需要登录 Token
浏览器直连 不支持 平台没有 MQTT-over-WebSocket 端口,浏览器无法直接订阅

所以网页不能直接收 EasyIoT——这也催生了“本地桥接”这一层:server.js 用 MQTT 订阅三个 Topic,把每条消息通过 WebSocket 实时推给浏览器;页面也可以走 REST 轮询作备用(Token 手动填或自动获取)。

温度主题、湿度主题、光照主题数据层做了噪声过滤:只认数值(或 JSON 里的数值),文本这类消息静默忽略,不刷屏不污染环境状态。
云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_4.webp

3.1 硬件端:行空板 K10 + Mind+ 2.0

数据从哪里来?我用的是 DFRobot 行空板 K10——这块主控板自带了三路环境感知:AHT20 温湿度传感器、板载 ALS 环境光传感器,还有一个可编程 RGB 灯,一颗板子正好把温度/湿度/光照三个数据源集齐了。采用 Mind+ 2.0 图形化编程(代码模式),程序逻辑很直接:

  1. 连 WiFi;
  2. 初始化 EasyIoT(iot.dfrobot.com.cn:1883,用控制台分配的 Iot_id / Iot_pwd);
  3. 每秒读一次三个传感器;
  4. 变化超过阈值才发布(温度 ±0.3℃、湿度 ±10%、光照 ±100)——不掉线的场景下既省流量又不让 IoT 后台刷屏;
  5. 每发一路数据,RGB 灯闪一下对应颜色(温度红/湿度绿/光照蓝),肉眼可见“数据在飞”。
/*!
 * MindPlus 2.0 生成 · DFRobot 行空板 K10
 * 每秒采样三路传感器,变化超过阈值发布到 EasyIoT 三个 Topic
 */
#include <DFRobot_Iot.h>
#include "unihiker_k10.h"

// 动态变量
volatile float mind_n_WenDu, mind_n_GuangZhao, mind_n_HuoQuShuJu, mind_n_ShiDu;

// 静态常量(topic 数组由发布时显式指定,这里留空占位)
const String topics[] = {"", "", "", "", ""};

// 创建对象
DFRobot_Iot myIot;
UNIHIKER_K10 k10;
AHT20 aht20;

void setup() {
  k10.begin();
  myIot.wifiConnect("你的WiFi名称", "你的WiFi密码");   // ← 替换为实际 WiFi
  while (!myIot.wifiStatus()) {}
  myIot.init("iot.dfrobot.com.cn", "你的Iot_id", "你的Iot_pwd", topics, 1883);  // ← 替换为控制台凭据
  myIot.connect();
  while (!myIot.connected()) {}
  mind_n_WenDu = 0;
  mind_n_GuangZhao = 0;
  mind_n_ShiDu = 0;
  Serial.begin(9600);
}

void loop() {
  // --- 温度:变化 ≥ 0.3℃ 才发布 ---
  mind_n_HuoQuShuJu = aht20.getData(AHT20::eAHT20TempC);
  if (abs(mind_n_HuoQuShuJu - mind_n_WenDu) >= 0.3) {
    mind_n_WenDu = mind_n_HuoQuShuJu;
    myIot.publish("z4ksqL6Ig", mind_n_HuoQuShuJu);   // 温度 Topic
    k10.rgb->write(-1, 0xFF0000); delay(500); k10.rgb->write(-1, 0x000000); delay(200);
  }

  // --- 湿度:变化 ≥ 10% 才发布 ---
  mind_n_HuoQuShuJu = aht20.getData(AHT20::eAHT20HumiRH);
  if (abs(mind_n_HuoQuShuJu - mind_n_ShiDu) >= 10) {
    mind_n_ShiDu = mind_n_HuoQuShuJu;
    myIot.publish("74F2EvvNg", mind_n_HuoQuShuJu);   // 湿度 Topic
    k10.rgb->write(-1, 0x00FF00); delay(500); k10.rgb->write(-1, 0x000000); delay(200);
  }

  // --- 光照:变化 ≥ 100 才发布 ---
  mind_n_HuoQuShuJu = k10.readALS();
  if (abs(mind_n_HuoQuShuJu - mind_n_GuangZhao) >= 100) {
    mind_n_GuangZhao = mind_n_HuoQuShuJu;
    myIot.publish("XZzRM3OHR", mind_n_HuoQuShuJu);   // 光照 Topic
    k10.rgb->write(-1, 0x0000FF); delay(500); k10.rgb->write(-1, 0x000000); delay(200);
  }

  delay(1000);
}

云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_5.webp

与小镇联动的三个核对点:

硬件侧 小镇侧
发布的就是浮点数转字符串(如 "25.6") 数据层 parseMessage 识别纯数字
三个 Topic 与 config.json 一致 WS 帧按 topic 映射到温度/湿度/光照
光照夜间读数可能升高(灯光/路灯) 小镇"时间优先":夜晚不显示阳光充足

两个小建议:① 湿度阈值 ±10 偏大,想看到更灵敏的“喝水提醒”可以改成 ±3;② 发布用 myIot.publish(topic, 数值) 的纯数字格式最省事,如果以后想发 JSON(比如含电池电量),小镇解析层也能兼容。

四、小镇居民:两户人家六口人

桂花苑 3 栋 502 · 王家

成员 身份
👨‍💼 王建国 38 岁 县住建局公务员
👩‍🏫 李秀兰 36 岁 县一中高中英语老师
👧 王小朵 9 岁 实验小学三年级(2 班)

桂花苑 5 栋 301 · 陈家

成员 身份
👨‍🚒 陈海峰 40 岁 县消防大队消防员(住得离单位近,5 分钟出警)
👩‍⚕️ 宋雪梅 37 岁 县人民医院护士长
🧒 陈果果 9 岁 实验小学三年级(2 班),和王小朵同班同桌

两家女孩是同班同学——上学结伴、放学同行、课间跳绳,这些“交集”都会出现在小镇里。
云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_6.webp

五、环境决定生活:三个传感器怎么“指挥”家庭

数据 决定 举例
🌡️ 温度 全家穿衣 / 出行安排 ≥35℃ 背心短裤躲空调房;<5℃ 羽绒服加围巾、减少外出
💧 湿度 喝水频次 <30% 干燥:定时提醒“口干,去接杯水”,饮水杯数实时累计;>70% 开窗防潮
💡 光照 昼夜与开灯 时间或光照判黑 → 客厅灯亮;全家入睡 → 灯灭(人睡灯灭,这是合理性检查修出来的规则)

光照还有一个“防呆”:时间优先——晚上哪怕传感器读数飙到 1200,也只会显示“夜晚(光照读数升高为灯光等非阳光)”,绝不会说“阳光充足”。

六、合理性:不是演戏,是过日子

这套系统最花功夫的不是“画得好看”,而是把逻辑做合理。用户提的好几条意见我都改成了规则:

  1. 走街道,不穿楼:小镇是 2D 平面图(县政府、住建局、县一中、实验小学、消防大队、人民医院、菜市场、云山公园、中心广场,两条横路两条竖路四个路口)。出门的人用 Dijkstra 最短路沿街道走,蓝色虚线画出路线。
  2. 行程时间 = 路程 ÷ 交通工具:王爸骑电动车 4 分钟到住建局、两女孩步行 14 分钟到学校(陈家 11 分钟,近一点)、陈爸摩托车 1 分钟到消防大队。时刻表里的“到岗时间”是推算出来的(07:40 出门 → 07:44 到岗),不是写死的。
  3. 同场不重叠:每个场所配“座位点阵”,同一地点的人自动分散站位。
  4. 对话要讲“相遇合理性”:一家人只在同场(家里/公园一起)才聊;爸爸在单位、女儿在学校——不聊;两家人路上相遇、公共场合偶遇、到对方单位办事(比如陈妈受邀去县一中讲急救知识,碰见王妈)——可以聊。
  5. 学生规矩:两个小学生在学校上课期间只能“讨论学习问题”(AI 会被明确提示“课上不许聊闲天”),课间、午休、放学路上才正常聊天。
  6. 称呼前后一致:每个成员都有固定的“称谓表”——妈妈永远叫自己女儿“闺女/小朵”、叫邻居家女孩“果果”,爸爸、陈爸陈妈同理。AI 提示里强制“前后保持一致,不要混用”,杜绝了实测里出现过的“一会叫闺女、一会叫儿子”的硬伤。
  7. 一件事做完不重复:同一时间段内,已经做过的事(量体温、提醒喝水、关窗这类)AI 不会再做第二遍——动作相似度检测会拦截重复记录,提示里也会写明“最近已完成:……”。
  8. 话题连贯:一次对话锁定一个话题聊深聊透(“家里近况”“天气穿衣”“小区趣事”等话题池),聊完自然收尾;下一轮对话自动换一个话题,不重复不东一句西一句。有关联的事(比如从天气聊到穿衣)可以自然接下去。

七、AI 小镇三件套:计划 / 对话 / 记忆

这部分基本照搬了斯坦福 Smallville 的机制,落地成规则 + 大模型:

  • 计划与行动:每人每天自动生成“今日计划”(清晨/白天/晚间三段),显示在卡片上;环境剧变(气温从 25℃ 跳到 38℃)时,日志会提示“今日计划灵活调整:户外改室内”。
  • 对话状态机:一方发出邀请 → 对方接受(70%)或婉拒 → 两人互相走近 → 开始 LLM 交替聊天,最多 8 条消息或 10 分钟 → 道别结束。对话提示里包含:发言者身份、当前计划、对方身份、以及最多 3 条最相关记忆
  • 记忆:聊天结束后,LLM 为每人各自生成第一人称摘要 + 重要性评分(0-9),向量化(2-3 gram 哈希袋,256 维)存入记忆库;下次对话前用复合评分检索 Top 3:
    分数 = 相关性(余弦相似度) × 时近性(每小时 0.99 指数衰减) × 重要性(LLM 评分)
  • 单一操作约束:每人同一时刻只能做一件事——对话中的人不会并行“走回家”,路上的人不搭话,睡觉不打扰。保证了行为逻辑永远清晰。

八、关键实现片段

桥接服务核心(server.js)——MQTT 订阅 → WS 推送:

mq.on('message', (topic, payload) => {
  history.push({ topic, ts: Date.now(), payload: payload.toString() });
  broadcast({ type: 'message', message: history[history.length - 1] });
});

AI 对话的提示构造(对话状态机内):

系统:你是王建国,住建局公务员……说话一口家常话,简短自然,只输出你要说的那句话。
用户:你正和女儿王小朵在家聊天。刚才的对话:…你记得的相关往事:…
     现在轮到你说一句。

记忆检索的复合评分:

score = cosSim(记忆向量, 查询向量) * Math.pow(0.99, 经过的小时数) * 重要性

九、怎么运行

项目全部在本目录(easyiot-dashboard/),零前端依赖,单文件网页。

cd easyiot-dashboard
npm install          # 首次
node server.js       # 或双击 auto.bat

云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_7.webp

浏览器打开 http://localhost:8080

  1. 填好 config.json:MQTT 的 Iot_id / Iot_pwd / 三个传感器 Topic(页面也可直接改);
  2. 网页自动进入“桥接实时”模式,MQTT 消息经 WebSocket 实时上屏;
  3. 没有设备?打开“演示模式”,拖温度/湿度/光照滑块,小镇立刻“变天”;
  4. 想验证连通性?控制台有“调试发送”,一条消息发到 Topic 上,1 秒内小镇就能收到。
    云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_8.webp云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_9.webp云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_10.webp

十、实测效果

  • 三个 Topic 全部订阅成功、实时收到消息:温度主题、湿度主题、光照主题——非数值全部静默过滤,环境面板只认数值。
  • AI 真实决策示例(DeepSeek-V3):深夜降雨场景下,消防员陈海峰自主输出
    {"action":"关窗防止雨水潲入","speech":"天气预报说今晚有大雨,得赶紧把窗户关好","mood":"警惕"} —— 与环境严丝合缝。
  • 全流程自动测试:日程调度、照明合理性、街道寻路、动态到达、对话状态机(8 条上限/10 分钟)、记忆生成与检索、单一操作约束,共 50+ 项断言全部通过。
    云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_11.webp

十一、可以怎么玩下去

  • 接真实硬件:行空板 K10 开箱即用(自带温湿度+环境光,3.1 节代码直接烧录);可增加空气质量传感器、紫外线传感器等定时发数据,小镇就是你自己家的“镜像居民”;
  • 加成员/加小镇:地图坐标、日程表、AI 人设都是数据化的,扩展一个家庭只需要加一份配置;
  • 语音/微信通知:把小镇日志推到手机,出门前看一眼“镇民们穿了几件衣服”;
  • 多 Agent 任务:比如让“邻居”给“你家”送菜、送作业,用现有记忆与对话机制就能编排剧情。

十二、开发花絮:一整座小镇是“对话”出来的

最后聊一件挺有意思的事:这个网页(以及整套小镇引擎)并不是一句一句敲出来的,而是作者用自然语言跟 Kimi Code(AI 编程助手)一段段对话“写”出来的。整个过程很像在“养”小镇:

  • 先提出需求:“做一面网页,能接收 EasyIoT 三个主题的数据”;
  • 再逐步细化:“把小镇画成平面图,两户人家,成员在地图上沿街道走”“行程时间按路程和交通工具算”“加上对话状态机、记忆检索、单一操作约束”“角色在家、上班、上学时位置要显示对”“晚上光照读数高别说阳光充足”“称呼前后一致、一件事不重复做、话题要聊连贯”……
    云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_12.webp

每一轮需求,Kimi Code 都会先检查现状、给出修改方案,我确认后落地代码,再自动跑冒烟测试验证逻辑(光对话质量这一轮的测试就有 11 项断言)。代码量约 1700 行 JavaScript,全部在对话中迭代完成——这也算是生成式智能体项目的一种“自指”:小镇里的居民由 AI 扮演,小镇本身也是由 AI 帮着写出来的。

小镇居民“开口说话”用的大模型是硅基流动(SiliconFlow)平台的 DeepSeek-V3,通过本地桥接的 /api/ai 接口转发调用;API Key 只保存在本地 config.json,网页端不接触密钥。
云天 AI 小镇:用 EasyIoT +行空板K10,驱动一座“会生活”的数字家庭小镇_image_13.webp
注册赠代金券:https://cloud.siliconflow.cn/i/KwyEBX3e


安全保障提醒config.json 里保存了 MQTT 凭据和 AI API Key,切勿上传到公开仓库或论坛附件。API Key 可以在 SiliconFlow 控制台随时吊销重置。

本文基于真实项目整理,代码与完整功能位于本地 easyiot-dashboard/ 目录(node + 无前端依赖)。

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -