在这篇文章中,我将介绍 B.L.O.O.M.(连接本地观察,公开映射),一个基于 Arduino UNO Q 的离线网络甲板,帮助自然学家记录野外笔记,使用 Edge Impulse 识别鸟鸣,并使用 Cloudinary's image generation API 生成复古野外速写。它将所有数据存储在本地,只有在您选择时才会同步到社区站点。
您将学到
- 如何将Arduino UNO Q网络甲板与USB集线器、摄像头、麦克风和HDMI屏幕连接
- 如何使用Arduino App Lab积木运行本地Gemma LLM和音频分类器
- 如何使用平衡数据重新训练Edge Impulse鸟鸣模型
- 如何从Netlify函数将SQLite条目同步到GitHub拉取请求
- 如何使用Cloudinary的image_to_image API以及参考图像生成风格匹配的图像
网络甲板又回来了。80年代科幻梦想中的便携、可黑客的电脑设备又被重新拾起,特别是那些希望技术能离线工作、尊重隐私且看起来很酷的互联网自然爱好者角落。我为这个项目构建的设备放置在一个10美元的复古化妆火车盒中,并生成 Grinnell 风格野外笔记,当您轻手轻脚地穿过郁金香时。
本文介绍了 硬件到云端的管道,特别是对Cloudinary介绍的部分进行了最详细的阐述。完整的构建(布线、外壳、每一步)位于 Hackster。
架构一览
┌──────────── In the field (offline) ────────────┐ ┌────────────── Back online ──────────────┐
│ │ │ │
│ Mic ─► Edge Impulse birdsong model │ │ Netlify Function │
│ Camera ─► photo │ POST │ ├─► Cloudinary image_to_image │
│ Keyboard/mouse ─► sketch + notes ─► SQLite ├───────►│ │ (Nano Banana → field sketch) │
│ Gemma LLM ─► AI note │ │ └─► Octokit: open PR on GitHub │
│ │ │ │ │
│ Arduino UNO Q (App Lab, Chromium kiosk) │ │ merge ─► Astro rebuild ─► live site │
└────────────────────────────────────────────────┘ └──────────────────────────────────────────┘
除非你点击“同步到云端”,否则不会有任何数据离开设备。这是一个硬性要求,因为这个项目同样隶属于我创立的非营利组织 Her AI Studio,我们在那里经常讨论数据治理、安全和隐私,主要在设备上运行本地 AI。
以下是完整的示意图:
云端的核心是向 Cloudinary 发送一次 POST 请求:
{
"prompt": "Create a field sketch illustration in the style of the reference image [1]...",
"reference_images": [{ "source_type": "url", "url": "<your reference sketch>" }],
"model": { "family": "nano-banana", "tier": "premium" },
"target": { "target_type": "managed_asset", "public_id": "field-notes/<slug>-ai" }
}
完整函数如下。
硬件:Arduino UNO Q 零件清单
- Arduino UNO Q (4 GB)
- 5 英寸 HDMI 屏幕 + 6 英寸 HDMI 线缆
- USB 5合1 集线器
- USB 摄像头
- USB 麦克风
- 移动电源 + USB-A 转 Micro-USB 数据线
- 带内置触控板的蓝牙键盘
- 复古火车箱(或您最喜欢的外壳!这是有趣的部分)
The Arduino UNO Q 负责运行一切。4 GB 版本可以完全离线运行小型语言模型和音频分类器,这使得设备在森林小径上无信号时仍然有用……并且可以离线探索硬件上的本地 AI 模型!
由于这是一副网络甲板,它有外设,所有外设都连接到 USB 集线器:屏幕、麦克风、摄像头和电源进入集线器,而集线器的 USB-C 插头连接到 UNO Q。在 Debian 中通过蓝牙配对键盘和鼠标,您就能恢复这些 USB 端口。
At Her AI Studio,学生将获得此 AI 工具包的使用权。
在 UNO Q 上使用 App Lab 运行 Gemma 和鸟鸣检测
该应用程序由 Arduino App Lab 中的三个“砖块”构建,这些砖块是为 Arduino Uno Q 预先构建好的模块化代码。
- 用于摄像头和草图界面的 Web UI
- 运行 Gemma 的 LLM 砖块,已下载到开发板
- 用于鸟鸣的音频分类砖块
Web UI 砖块可帮助您使用少量 JS 和 HTML 构建整洁的界面,该界面通过 Arduino App Lab 在您的本地开发板上运行。这就是您编写野外笔记的方式。LLM 砖块提供一个小空间,让您可以与 LLM 聊天,并了解您使用内置摄像头拍摄的动植物照片的更多信息。
副任务:在 Debian 上使用 kiosk 砖块修复 Chromium 弹出窗口
在 Debian 上,默认的 Web UI 砖块会在其他窗口后面打开 Chromium 弹出窗口。在桌面上运行正常,但在设备上不行。我通过一个小的自定义砖块修复了这个问题,该砖块会等待本地服务器启动,然后以 kiosk 模式启动 Chromium:
def build_kiosk_command(url: str):
if system == "Linux":
return ["chromium-browser", "--kiosk", "--noerrdialogs",
"--disable-infobars", f"--app={url}"]
def launch_kiosk(url: str, ready_timeout: float = 15.0):
if not wait_for_server(url, timeout=ready_timeout):
logger.error(f"Server never became reachable at {url}")
return None
cmd = build_kiosk_command(url)
logger.info(f"Launching: {' '.join(cmd)}")
return subprocess.Popen(cmd)
界面以全屏方式呈现,没有桌面装饰,因此屏幕就是野外笔记本,如下所示:
聆听鸟鸣
我想为我的网络甲板添加一些“耳朵”,于是转向了一个通过 App Lab 向 Arduino 添加本地自定义训练模型效果很好的平台:Edge Impulse。
我从 Edge Impulse 社区画廊中的鸟鸣分类器开始。该模型存在类别不平衡且样本太少,于是我:
- 复制项目并删除不可用的片段
- 从 xeno-canto(一个鸟鸣数据库)中分别获取至少 20 条蓝松鸦、哀鸽和北红雀的录音
- 将所有文件转换为 WAV 格式,添加标签并上传
- 重新平衡训练/测试集的划分并重新训练
- 将新模型放入 App Lab 项目中
当您的 USB 麦克风捕捉到鸟鸣时,点击按钮,模型会对音频进行分类,并将结果保存到野外笔记中。如果您想添加当地物种,该模型是 公开且可克隆 的。
同步:从 SQLite 到拉取请求
每条观察都存储在本地 SQLite 数据库中,但您可能想将野外笔记展示到我们的公开仓库。当您选择共享时,设备会将条目发送到 Netlify 函数。照片和草图经过 Base64 编码,因此整个条目以单一 JSON 有效载荷的形式传输:
def sync_entry(entry_id):
with _db_lock:
conn = sqlite3.connect(DB_PATH)
row = conn.execute(
"SELECT id, timestamp, photo, sketch, ai_note, locality, weather, habitat "
"FROM entries WHERE id = ?",
(entry_id,)
).fetchone()
conn.close()
eid, timestamp, photo, sketch, ai_note, locality, weather, habitat = row
payload = {
"id": eid,
"catalog_no": catalog_number(eid),
"timestamp": timestamp,
"note": ai_note or "",
"locality": locality or "",
"weather": weather or "",
"habitat": habitat or "",
"photo": base64.b64encode(photo).decode("utf-8") if photo else None,
"sketch": base64.b64encode(sketch).decode("utf-8") if sketch else None,
}
response = requests.post(CLOUD_SYNC_URL, json=payload, timeout=CLOUD_SYNC_TIMEOUT)
The function does not write to a database. It opens a pull request against the field-notes repo with Octokit:
const { data: pr } = await octokit.pulls.create({
owner: OWNER,
repo: REPO,
title: `New field note: ${catalog_no}`,
head: branchName,
base: BASE_BRANCH,
body: [
"Submitted automatically from a Bloom device.",
"",
`**Locality:** ${locality || "—"}`,
`**Weather:** ${weather || "—"}`,
`**Habitat:** ${habitat || "—"}`,
aiSketchPath ? "\n_AI-generated sketch included._" : null,
].filter((line) => line !== null).join("\n"),
labels: ["device-submission"],
});
这个流程让社区站点更容易获得人工审核:维护者审查 PR,合并它,Netlify 重新构建 Astro 站点。您可以在 community-field-notes.netlify.app 查看结果。
使用 Cloudinary 的 image_to_image API 生成 AI 场景草图
格里넬传统的自然学家将手写笔记与图画结合。我希望每条同步的笔记都配有一幅这种复古风格的插图,即使撰写笔记的人不会画画。
在 PR 被打开之前,Netlify 函数会使用 image_to_image 端点和 Nano Banana 模型将笔记文本发送到 Cloudinary 的图像生成 API。我还会传递一张参考图像,即已经存储在我的 Cloudinary 账户中的旧式野外速写,以确保每次输出都具有相同的外观:
async function generateAiSketch(noteText, slug) {
const prompt = [
"Create a field sketch illustration in the style of the reference image [1].",
"The sketch should depict:",
noteText || "A natural world observation",
"Use a vintage field journal aesthetic with earthy tones, hand-drawn quality, and scientific illustration style.",
].join(" ");
const auth = Buffer.from(
`${CLOUDINARY_API_KEY}:${CLOUDINARY_API_SECRET}`
).toString("base64");
const response = await fetch(
`https://api.cloudinary.com/v2/generate/${CLOUDINARY_CLOUD_NAME}/image_to_image`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${auth}`,
},
body: JSON.stringify({
prompt,
reference_images: [
{ source_type: "url", url: REFERENCE_IMAGE_URL },
],
model: { family: "nano-banana", tier: "premium" },
target: {
target_type: "managed_asset",
public_id: `field-notes/${slug}-ai`,
},
}),
}
);
// ...handle response, return the asset path for the PR
}
我喜欢这个设置的几点:
- 使用参考图像可以保持风格一致,您可以选择模型,或者将其保持为 'auto' —— 这样模型会根据您的使用情况自动选择。
- 提示词描述要绘制的内容,参考图像决定其外观。更换参考图像后,每个后续笔记都会获得新的视觉身份。 target_type: "managed_asset" 将输出直接保存到我的 Cloudinary 媒体库中,公共 ID 可预测(field-notes/-ai)。我不需要下载并重新上传任何内容,图像已准备好交付给我的社区野外笔记应用。
- 之后它就是一个普通的 Cloudinary 资源,站点可以使用常规转换进行交付:f_auto,q_auto,用于卡片和缩略图的调整大小等等。
- 这只需要一次 fetch 调用。无需 SDK 和额外基础设施,非常适合无服务器函数。
设置技巧
- 将 CLOUDINARY_CLOUD_NAME、CLOUDINARY_API_KEY 和 CLOUDINARY_API_SECRET 保存在 Netlify 环境变量中。切勿将它们放在设备上。
- 首先将您的参考草图上传到 Cloudinary,并使用其交付 URL 作为 REFERENCE_IMAGE_URL。
- 图像生成 API 提供免费额度,足以支持类似此社区项目的使用。如果您尚未有账户,可免费注册。
外壳:无需 3D 打印机
我没有打印外壳,而是重复使用了一个(更适合赛博朋克装置!):一个复古的 Lancôme 化妆火车箱,在 eBay 上大约售价 10 美元。以下是我将所有东西放入箱中的做法:
- 在侧面为相机镜头开一个小孔
- 使用回收海绵和布料制作搁板
- 用扎带固定 USB 集线器
- 为屏幕制作纸板背板,预留线路空间
- 为 UNO Q 预留专用位置,考虑散热(海绵和布料会锁住热量,因此需注意温度或使用如藤条这样的透气编织——我甚至用过寿司卷帘来制作这种搁板)
这个高箱还能放下放大镜、压花、小型水彩套装或叶子指南。

作者享受秋日的野外笔记时光
亲近自然:在野外使用 B.L.O.O.M.
打开电源,走到户外,记录你发现的一切:鸟鸣、照片、草图、对LLM的问题、所在位置、天气和栖息地。当你重新连上Wi‑Fi时,点击同步将笔记发送到GitHub,获取由Cloudinary生成的草图,并将其加入共享的自然历史记录。

发现新生物
资源
📖 Hackster 上的完整构建:B.L.O.O.M.: 野外笔记社区网络设备
💻 设备代码:
B.L.O.O.M — 离线野外日志
连接本地观察,公开映射
B.L.O.O.M 将 Arduino® UNO Q 变成自供电的离线野外日志网络设备:对准植物、动物或徒步途中值得注意的任何事物,拍照、在其上绘制草图,并使用完全本地的 AI 模型讲述你的观察内容。每条条目——照片、草图和笔记——都会保存到开发板本身,除非你主动选择将其同步到配套网站,否则会一直保留。使用它时无需任何连接。
该项目是为 Her AI Studio 的课程而构建,并提交给包括 使用 Arduino UNO Q 和 App Lab 发明未来 竞赛的黑客马拉松,荣获最佳社会影响奖项——其理念是对抗手机和相机文化的一种对策:一款单一用途的设备,让你停下、对准并等待记录,而不是随手拍照然后快速滑走。
…
野外笔记
一本数字化的野外日志——汇集博物学观察、野外调查与考察记录。
本项目基于 Astro 构建,内容通过 Markdown 内容集合进行管理。
项目结构
/
├── public/
│ └── favicon.svg
├── src/
│ ├── content/
│ │ ├── config.ts
│ │ └── notes/
│ │ ├── observation-of-the-red-shouldered-hawk.md
│ │ ├── botanical-survey-of-coastal-dune-ecology.md
│ │ ├── geological-observations-along-the-appalachian-trail.md
│ │ └── nocturnal-insect-survey-june.md
│ ├── layouts/
│ │ └── Layout.astro
│ ├── pages/
│ │ ├── index.astro
│ │ └── notes/
│ │ └── [...slug].astro
│ └── styles/
│ └── global.css
└── package.json
添加新笔记
新建一个 .md 文件,放到 src/content/notes/ 目录下,并在文件开头添加 frontmatter:
---
title: "Your Note Title"
date: 2026-07-21
location: "Location, State"
excerpt: "A brief summary of the observation."
image: "/images/your-image.jpg"
tags: ["tag1", "tag2"]
---该笔记将自动出现在……
如果你像这样构建一个热爱自然的网络甲板,为网站贡献一条笔记,或重新训练模型以识别你附近的鸟类,请在评论中分享。我很想看看其他人的野外笔记长什么样,以及你在你所在地区观察到了什么。🌿
| Cloudinary ❤️ 开发者 |
|---|
| 准备好提升您的媒体工作流程了吗?免费使用 Cloudinary,立即构建更佳的视觉体验。 |
| 👉 创建您的免费账户 |





