鸟类访客
摘要
一个在阳台上安装USB麦克风捕捉鸟鸣的项目,使用BirdNET-Pi进行物种识别,并在网站上叠加花鸟画拼贴展示。
暂无内容
查看缓存全文
缓存时间: 2026/05/31 07:32
# 鸟类访客
来源: https://theodore.net/projects/AvianVisitors/
- 01首页 (https://teddywarner.com/) - 02项目 (https://theodore.net/projects) - 03文章 (https://theodore.net/writings)
---
一开始我并没打算把这个当成一个“真正的”个人项目。我当然喜欢一份好的项目总结 (https://theodore.net/projects/),但坦白说,我觉得这不过是一个下午就能搞定的快速项目,撑死发条推文就够了,不值得写更多文档。但推特上的反应却出乎意料……
> 我在公寓阳台上装了一个小麦克风,用来听路过的鸟鸣,然后建了一个网站,每当听到鸟叫就把它们的图像拼贴出来
> pic.twitter.com/85KrLRL5tu (https://t.co/85KrLRL5tu)
> — Teddy (@WarnerTeddy) 2026年5月28日 (https://x.com/WarnerTeddy/status/2060018688645115964?ref_src=twsrc%5Etfw)
……所以我就匆匆写了这篇短文,分享给那些也想在自己住处监测鸟类访客的人。现在内容简洁明了,为的是尽快发布,不过这项工作是我一系列与鸟类相关的项目中的一环,后续我会写更详细的总结!
---
### 公寓里的鸟儿 (https://theodore.net/projects/AvianVisitors/#apartment-birds)
Avian Visitors 是 BirdNET-Pi (https://github.com/Nachtzuster/BirdNET-Pi) 的一个分支,在上面叠加了一层花鸟拼贴画(kachō-e collage)界面。BirdNET-Pi 负责音频采集和物种识别,它利用康奈尔大学的 BirdNET (https://birdnet.cornell.edu/) 声学分类器分析 Pi 上 USB 麦克风捕捉到的声音。你可以访问 bird.onethreenine.net (https://bird.onethreenine.net/) 查看运行效果:
#### 物料清单 (https://theodore.net/projects/AvianVisitors/#bom)
自己搭建一个鸟类追踪站其实很简单。完整的项目仓库在 github.com/Twarner491/AvianVisitors (https://github.com/Twarner491/AvianVisitors)。你需要准备以下物品:
| 数量 | 描述 | 价格 | 购买链接 |
|------|------|------|----------|
| 1 | 树莓派 (4B / 5 / Zero 2W) | ~$35-80 | Raspberry Pi (https://www.raspberrypi.com/products/) |
| 1 | Micro SD 卡 (≥32 GB) | ~$10 | Amazon (https://www.amazon.com/s?k=32gb+micro+sd+card&i=electronics&crid=1RCJAD1J0EPDX&sprefix=32gb+micro+sd+card%2Celectronics%2C226&ref=nb_sb_noss_1) |
| 1 | USB 领夹麦克风 | $16.95 | Amazon (https://www.amazon.com/dp/B0176NRE1G) |
| 1 | 树莓派电源 | ~$10 | - |
| **总计** | | **~$80** | |
显示全部 5 项
收起
顺便提一下,你还需要获取一个 Gemini API 密钥 (https://aistudio.google.com/apikey) 来重绘插画(免费版即可),以及一个 eBird API 密钥 (https://ebird.org/api/keygen) 来按地区筛选物种。
### Birdnet.local (https://theodore.net/projects/AvianVisitors/#birdnet-dot-local)
使用 Raspberry Pi Imager (https://www.raspberrypi.com/software/) 烧录 SD 卡。选择 Raspberry Pi OS Lite(64 位)。在自定义设置中配置:
- 用户名
- WiFi SSID 和密码
- 主机名:`birdnet`
- 启用 SSH 并允许密码认证
将 USB 麦克风插入 Pi,放在窗户旁或固定在室外。我把它贴在朝向阳台的小窗户纱网上,Pi 则留在室内,免受风雨影响。然后开机!
Pi 连上网络后,通过 SSH 登录并运行安装脚本:
```
ssh <用户名>@birdnet.local
curl -s https://raw.githubusercontent.com/Twarner491/AvianVisitors/avian-visitors/newinstaller.sh | bash
```
安装脚本假定已启用免密码 sudo(Raspberry Pi OS Lite 默认如此——如果你强化过安全设置,请先运行 `sudo raspi-config` -> *系统选项* -> 恢复默认设置)。脚本会克隆分支,运行 BirdNET-Pi 的安装程序(音频采集、模型、Web UI 等),将 AvianVisitors 的叠加层软链接到 Caddy Web 根目录,然后重启系统。整个过程根据 Pi 型号和 Wi-Fi 速度需要 20-40 分钟。Pi 重启后,拼贴页面位于 `http://birdnet.local/`,而原版 BirdNET-Pi 界面仍可通过 `http://birdnet.local/index.php` 访问。右上角的菜单抽屉提供了一个管理面板,包含原生设置、系统监控、日志和工具面板,它们通过 Pi 上的轻量 JSON 接口工作,因此无需离开拼贴页面即可调整分析器、查看服务状态和跟踪日志。
#### 从局域网向外转发(可选)
默认安装将一切保留在局域网内,但 `avian/forwarding/` (https://github.com/Twarner491/AvianVisitors/tree/avian-visitors/avian/forwarding) 目录提供了三种可选方案:
*Cloudflare Tunnel*
这能提供一个公网 HTTPS 地址,无需端口转发,也不暴露家庭 IP,我正是用它来访问 bird.onethreenine.net (https://bird.onethreenine.net/) 的。需要一个免费 Cloudflare 账户,设置大约需要 5 分钟。首先在 Pi 上安装 `cloudflared`:
```
sudo apt install -y lsb-release
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
```
然后进行认证并创建隧道,将其指向你拥有的域名下的一个主机名:
```
cloudflared tunnel login
cloudflared tunnel create birds
cloudflared tunnel route dns birds birds.your-domain.com
```
将附带的配置文件放置到位,将 `tunnel:` 字段指向 `cloudflared tunnel create` 输出的 UUID,然后安装并启动服务:
```
sudo cp ~/BirdNET-Pi/avian/forwarding/cloudflared.yml /etc/cloudflared/config.yml
sudo nano /etc/cloudflared/config.yml
sudo cloudflared service install
sudo systemctl restart cloudflared
```
若想为公网地址添加密码保护,可设置 Cloudflare Access(免费版最多支持 50 个用户),并在主机名上添加策略。如果你更倾向于通过 Caddy 自身实现 HTTP Basic 认证,`caddy-auth.caddy` (https://github.com/Twarner491/AvianVisitors/blob/avian-visitors/avian/forwarding/caddy-auth.caddy) 片段中提供了可运行的示例。
*Home Assistant REST 传感器*
这将最新的检测结果以 `sensor.latest_bird` 形式暴露给 Home Assistant,你可以用它创建自动化(比如听到稀有物种时闪烁灯光、推送通知等)。在 `configuration.yaml` 中添加:
```
rest:
- resource: http://birdnet.local/avian/api/birdnet-api.php?action=recent&hours=1
scan_interval: 60
sensor:
- name: "Latest Bird"
value_template: "{{ value_json.species[0].com if value_json.species else 'none' }}"
json_attributes_path: "$.species[0]"
json_attributes:
- sci
- n
- last_seen
- best_conf
```
`recent` 端点返回的物种已按数量降序排列,因此 `species[0]` 就是过去一小时内出现次数最多的鸟。如果你希望按 `last_seen` 排序,请相应调整 `value_template`。
*MQTT 桥接*
MQTT 桥接每分钟轮询一次最近检测端点,并将新物种以 JSON 格式发布到 `birdnet/` 主题下,这对于希望将检测数据通过现有 MQTT 代理流入其他服务的情况非常有用。安装 paho-mqtt,复制桥接脚本和服务文件,然后启用:
```
sudo pip3 install paho-mqtt --break-system-packages
cp ~/BirdNET-Pi/avian/forwarding/mqtt-bridge.py ~/avian-mqtt.py
nano ~/avian-mqtt.py # 设置代理主机、主题前缀、凭据
sudo cp ~/BirdNET-Pi/avian/forwarding/avian-mqtt.service /etc/systemd/system/
sudo nano /etc/systemd/system/avian-mqtt.service # 如果不是 'birdnet' 用户,请设置 User=
sudo systemctl daemon-reload
sudo systemctl enable --now avian-mqtt
```
去重仅存储在内存中,因此每次服务重启时,桥接会重新发布过去一小时的检测数据。下游消费者应具备幂等性。
#### 插画与拼贴 (https://theodore.net/projects/AvianVisitors/#illustrations-collage)
拼贴页面内置了 450 张北美常见物种的插画,这些插画通过 Gemini 的 `gemini-2.5-flash-image` (https://ai.google.dev/gemini-api/docs/image-generation) 模型生成。每个物种有两种姿态:栖息的 (perched) 和飞行的 (in-flight)。提示词模板位于 `avian/scripts/prompt.template.md` (https://github.com/Twarner491/AvianVisitors/blob/avian-visitors/avian/scripts/prompt.template.md):
```
以江户时代日本花鸟木版画风格生成一张 {pose} 的 {com_name} ({sci_name}) 图像。
自信的墨绘线条搭配柔和的水彩晕染。
朴素克制的调色板:焦赭色、赭石色、靛蓝、朱红、柔和的绿色。
羽毛细节用短促的方向性笔触表现;眼睛、喙和脚用清晰墨线勾勒。
只有鸟这一主体。无背景,除非姿态需要(栖息时可带一根稀疏的细枝),无边框或画框,无文字或签名。
解剖结构必须符合所指定物种的生物特性:
- 确切的两只翅膀。两条腿。一个头。一个喙。一条尾巴。
- 姿态、颜色、斑纹和身体比例需与野外观鸟指南中 {com_name} 的描述一致。
- 栖息姿态:一只翅膀收于体侧,另一只藏在身后。飞行姿态:双翅以自然的拍打姿势展开。
以高分辨率渲染在完全透明的背景上。干净地裁切出鸟的轮廓。无阴影,无纸纹,无标题。
```
每个请求会替换三个模板变量:学名、通用名、姿态。若要重绘整组图像,只需编辑此文件并重新运行预生成脚本(加上 `--force` 参数):
```
export GEMINI_API_KEY='your-key'
# 重新渲染 BirdNET-Pi 模型中的所有物种:
python3 ~/BirdNET-Pi/avian/scripts/pregen.py \
--labels ~/BirdNET-Pi/model/labels.txt --force
# 或仅限定到 eBird 地区中观察到的物种:
export EBIRD_API_KEY='your-key'
python3 ~/BirdNET-Pi/avian/scripts/pregen.py \
--labels ~/BirdNET-Pi/model/labels.txt \
--ebird-region US-CA
```
当传递 `--ebird-region` 时,预生成脚本会将 BirdNET 的全部物种列表与 eBird 报告在该地区观察到的物种进行交集运算。eBird 地区代码使用 `-` 分隔(例如 `US-CA` 表示州级筛选,或 `US-CA-085` 表示圣克拉拉县,进行更精确的筛选)。这会将渲染数量从全球约 3000 种减少到实际在你周围飞过的物种。
需要指出的是,Gemini 会相当频繁地出现解剖结构幻觉,因此仓库附带了经过后审核的图像集,其中已移除多余的翅膀、脱离身体的脚以及训练图像水印。生成当前捆绑集时的审核过程发现,栖息姿态的解剖缺陷约为 3%,飞行姿态约为 5%。飞行姿态更难处理,因为 Gemini 对“翅膀展开”有很强的先验,会将身体附近的任何羽毛团块都解读为可能的翅膀,因此同一只山雀可能需要尝试五六次才能生成干净的输出。
每个物种都附带一个二进制 alpha 蒙版(离线生成):将插画下采样到约 93 像素宽,对 alpha 通道进行阈值处理,并将结果打包成 base64 编码的位数组。完整的蒙版注册表位于 `avian/frontend/masks.json` (https://github.com/Twarner491/AvianVisitors/blob/avian-visitors/avian/frontend/masks.json),249 个物种约 280KB。该蒙版编码了鸟的轮廓。前端利用这些蒙版实现两件事:瓦片打包(只要轮廓不重叠,边界框可以重叠)和悬停命中测试(当鼠标悬停在两个瓦片边界框重叠的区域时,高亮显示正确的鸟)。
打包算法本身是一个中心向外螺旋:瓦片按面积降序排序,最大的放在中心位置,后续每个瓦片从中心向外螺旋,直到找到一个其蒙版不与任何已放置蒙版相交的位置。成本函数偏向水平方向,以产生更宽、更适合横屏显示的簇:
\[
\text{cost}(x, y) = \sqrt{\left(\frac{\Delta x}{b}\right)^2 + \Delta y^2}
\]
其中 \(b = 2.1\) 是椭圆纵横比偏差。
瓦片尺寸的计算是另一个需要仔细处理的问题。简单的方法是设置每个瓦片的面积为检测次数的幂,并限制每个瓦片的最大值:
\[
A_i = \min(A_{\text{max}}, \, A_{\text{base}} \cdot n_i^{1.2})
\]
但一旦某个物种超过限制阈值,这种方法就会失效,因为所有高于阈值的物种都会以相同的最大尺寸渲染,而不管实际计数如何,这破坏了通过频率调整瓦片尺寸所带来的视觉层次感。解决办法是改用视口面积预算进行归一化:每个瓦片获得一个计数加权分数,所有分数按比例缩放,使它们的总和等于视口的一部分,然后根据缩放后的面积推导瓦片尺寸:
\[
s_i = n_i^{0.65} \quad,\quad A_i = \max\left(A_{\text{min}}, \, \frac{B}{\sum_j s_j} \cdot s_i\right) \quad,\quad w_i = \sqrt{A_i \cdot \text{ar}_i}
\]
其中 \(B\) 是视口面积预算(根据物种数量为视口的 28% 到 46%),\(\text{ar}_i\) 是物种的纵横比。0.65 指数提供了一个可见的层次结构(检测 400 次的物种渲染面积大约是检测 30 次的 5 倍),而不会出现上限导致的扁平化;而且由于所有值都是根据视口面积归一化的,相同的逻辑在任何屏幕尺寸下都能产生合理的布局。
初始打包后,如果有任何瓦片超出屏幕,所有瓦片会缩小 7%,然后重新打包整个布局,循环最多 10 次(此时线性缩放约为原始尺寸的 50%)。这保证了每个物种都能适应从 390px 移动设备宽度到 2560px 工作室显示器的任何视口,对于一个拼贴画本身就是页面的网站来说,这一点比你想象的要重要得多。
#### ~ 实时更新 (https://theodore.net/projects/AvianVisitors/#real-time)
前端每 30 秒轮询一次最近检测端点,当有新物种进入当前时间窗口时,它会在下次刷新时加入布局,簇会稍稍移动以腾出空间。前端会进行完全重新打包,而不是增量插入。在 Pi 4 客户端上,V8 引擎中重新打包约 10 个物种(当前网格步长为 4px)所需时间不到 20ms。
时间窗口选择器(`1H / 12H / 24H / 7D / ALL`)会以对应的 `?hours=N` 参数重新获取数据并就地重新渲染,整个过程非常安静,我曾连续几个小时开着页面都没有注意到这些变化。
点击拼贴中的任何瓦片(或图集视图中的任何卡片)会打开一个详情弹窗,该弹窗会调用 Wikipedia 摘要端点获取物种描述,并提供一个切换按钮显示栖息和飞行两种姿态。录音列表会获取该物种最近的 BirdNET-Pi 存档 MP3(按通用名匹配),来源为 `$HOME/BirdSongs/Extracted/By_Date/...`,每个音频文件与其频谱图一同渲染,底部还有维基百科和 eBird 的外部参考标签。
---
好了,以上就是关于如何追踪路过的各位小鸟的一个相当简单的搭建方法 ;) 输入您的邮箱,偶尔接收更新。
相似文章
看见鸟鸣
Seeing Birdsong 是一个将鸟类鸣叫声转化为几何形态和 3D 可视化的框架,架起了艺术与声学科学之间的桥梁。它利用频谱描述符来构建数据丰富的结构,以用于研究、教育和艺术表演。
Kiwibit的AI喂鸟器成为我的新后院伙伴
这是一篇关于Kiwibit AI喂鸟器的评测,它内置摄像头和鸟类识别算法,可追踪识别超过10,000种鸟类,提供有趣且连接自然的后院体验。
搜索鸟类
一个交互式数据故事,探索北美鸟类的Google搜索趋势,揭示哪些物种吸引人类注意,并以中央公园的一只Snowy Owl为例进行案例研究。
@0xCortexl:东京某人将鸟语映射为真实数据模式,可视化结果宛如神经网络在梦境中
一套AI系统将鸟类鸣叫转化为三维可视化,实时将频率和调制数据转换为彩色点簇,在工业和医疗异常检测中具有潜在应用。
大家都在谈论并实际在购买的智能鸟类喂食器(2026)
一篇Wired对智能鸟类喂食器的评测,测试了Netvue Birdfy Lite和Birdbuddy Pro等热门型号的耐用性、AI识别和订阅功能。