
前阵子折腾了一个有点离谱的项目:给大模型(LLM,即大型语言模型)装个鼻子。AI 能读、能写、能看、能听,但偏偏没有嗅觉——你问它冰箱里的牛奶是不是坏了,它只能老实说"闻不出来"。这不科学,毕竟我们每天靠鼻子来判断食物能不能吃。于是我们干脆自己造了一个。

▶ 演示视频 (3:11): youtu.be/n88pRiuib3U
▶ 代码 (MIT): github.com/PGASlab/app
▶ 没有硬件也能体验: live UNO Q simulation
AI 可以读、可以写、可以看、可以听。但你问它冰箱里那盒牛奶是不是坏了,它的诚实回答只能是"闻不出来"。嗅觉是我们每天用来判断食物能不能吃的那一感,AI 偏偏没有。
所以我们自己造了一个。

鼻子的工作原理是不同受体对不同分子做出响应。PGAS 借鉴这个思路,用三个廉价的金属氧化物气体传感器来实现,每个针对不同类别的变质气体:
单独来看,它们就是随周围空气变化阻值的电阻。

接到 Arduino UNO Q 上,三个小鼻子合成为一个。UNO Q 的微控制器每秒采样所有三个传感器,Linux 那侧把读数与洁净空气对比,判断食物是否还能吃,然后行动:板载 LED 矩阵闪烁警告、发出警报、手机显示结论。不上云、不注册账号、不需要互联网,全部在用充电宝供电的板子上完成。
古人用银勺试毒,因为银碰到变质食物产生的硫化物会变黑。PGAS 就是现代版的银勺。我们为夜市摊位和小餐饮店设计的,在这些地方,卖变质食物是健康风险,而扔掉还能吃的食物是浪费。
副业场景。 国内夜市的很多摊位都是副业:白天上班、早上进货、晚上卖到深夜。没有食品检测实验室,只靠鼻子猜今天的牛奶还能不能用、昨天的剩菜还能不能吃。一批货出问题,小摊贩可能就丢了回头客。PGAS 是可以揣进口袋的第二个意见,用跟手机同一个充电宝供电。
这里是一次真实的测试,用一盒变质牛奶,按步骤拍照记录。

PGAS 需要的所有东西:

先用无食物环境跑完整测试,确保 PGAS 不会误报。三个传感器的读数都保持在洁净空气基准(分别上升 +0.03 V、+0.02 V 和 +0.00 V),板子判定 新鲜 耗时 0.6 ms,LED 矩阵显示 ✓。


测试样本是一盒开封后放置变质的鲜奶。第二张照片证明里面确实还有奶。我们把它倒进小容器,方便逐个传感器对准测试。
手机端点击 开始测试,页面会引导你一步步操作。每个传感器各 5 秒:1 秒在洁净空气中、1 秒移动到食物上方、3 秒测量。大圆倒计时和实时读数让你清楚知道进度。

先测 MQ-135。 传感器放在牛奶上方,圆环倒计时。

然后 MQ-3。 传感器测完,页面提示你取出来准备下一个。这里 MQ-135 和 MQ-3 已打勾,轮到 MQ-2。

按键确认 下一个传感器已在洁净空气中再按。PGAS 不会自己跳转,所以永远不会被催。

最后 MQ-2。

PGAS 显示 食物已变质。MQ-3(乙醇)比自身洁净空气读数高出 +0.21 V,超过 0.20 V 阈值,所以板子判定牛奶为 不可销售,耗时仅 0.2 ms。MQ-135 上升 +0.02 V,MQ-2 上升 +0.05 V。乙醇是细菌和酵母发酵牛奶时产生的,所以 MQ-3 是我们预期反应最大的传感器。
弹窗用通俗语言写出结论:一个总判定加每个传感器的独立读数条。还会列出智能体的决策过程:
同一页面往下滚动,每个传感器都有速度表盘,显示当前电压、正常范围和实时图表。超过限值时,卡片变红闪烁。

MQ-3 在洁净空气中:0.37 V,在正常范围内。

MQ-3 放在变质牛奶上方:0.70 V。 几乎翻倍,卡片变红。

移开 30 秒后:0.38 V,恢复到洁净空气值。传感器恢复很快,可以立即测试下一个食物。
| MQ-3 | 洁净空气 | 变质牛奶上方 | 30 秒后 |
|---|---|---|---|
| 模块输出 | 0.37 V | 0.70 V | 0.38 V |
▶ github.com/PGASlab/app(MIT):Arduino sketch、Linux 应用、手机仪表盘、CI 自动测试,以及浏览器模拟的 UNO Q。
| 步骤 | 发生了什么 | 在哪运行 |
|---|---|---|
| 感知 | MQ-135、MQ-3、MQ-2 以 1 Hz 采样,14 位 ADC,16 次平均 | STM32U585 MCU(Arduino sketch) |
| 判断 | 每个传感器与自身几秒前的洁净空气读数比较上升量。ONNX 模型将三个上升量转为变质概率,由安全规则兜底 | QRB2210 Linux(Python,ONNX Runtime) |
| 行动 | LED 矩阵显示 ✓ 或闪烁 ✕,发送警报,手机显示结论 | MCU + Linux |
UNO Q 两部分通过 Arduino Bridge 通信:sketch 每秒调用 Bridge.notify("pgas_sample", …),Linux 用 Bridge.call("draw", …) 回传 LED 矩阵帧。手机页面通过官方 App Lab 的 arduino:web_ui 组件用 Socket.IO 提供服务,手机只需在同一个 Wi-Fi 或热点上。
每个传感器与自身几秒前的洁净空气读数比较,这才是 5 秒测试能工作的原因。MQ 传感器会随温度、湿度和预热漂移,绝对阈值不可靠,但几秒内的上升量是可靠的。
现在的判断来自一个小型机器学习模型,运行在 UNO Q 本地,不上云:
pgas/bag_model.py)。模型文件仅 411 字节。照片 2 和 9 的判定来自规则;我们之后用这些测试数据训练了模型,给出相同答案(洁净空气 7% 变质概率,牛奶 93%)。每增加一个新的带标签测试,模型就会更好:把上升量加到 SAMPLES 里,运行 python -m pgas.bag_model。仓库还有一个用 Qualcomm AI Hub 优化模型的脚本(pgas/aihub.py)。
| 器件 | UNO Q 引脚 | 说明 |
|---|---|---|
| MQ-135 | A0 | AO 通过 10 kΩ / 20 kΩ 分压(5 V → 引脚最大 3.33 V) |
| MQ-3(Waveshare) | A1 | 同上分压 |
| MQ-2 | A2 | 同上分压 |
| 加热器 | 5 V | 所有地线共接 |
| 电源 | USB-C | 5 V 充电宝供电,便携使用 |
浏览器模拟把逻辑调对了,但真机上让我们学到更多:
sketch.yaml 中列出 MsgPack、DebugLog、ArxContainer 和 ArxTypeTraits。我们从板子上的 App Lab 示例复制了精确版本。adb shell 设置了 Android 的 TMPDIR 在 UNO Q 上不存在,导致 arduino-app-cli 报错。我们的脚本会重置它。街边小贩不可能依赖订阅服务、云 API 或手机信号。PGAS 用的全是开源的:Arduino App Lab 及其组件、Arduino sketch、Python、scikit-learn 和 ONNX Runtime 做板载推理,代码用 MIT 许可。任何人都可以按一个板子加三个传感器的成本复现、用自己的食物数据重新训练、换更好的模型,数据永远不会离开摊位。
最佳 Arduino 使用。