
院子里那台监控摄像头以前最常用的功能是回看“谁又进来翻垃圾桶”。直到有一次整理素材时才发现在某一整段录像里有一种叫不上名字的鸟在门前的树枝上叫了很久。当时只觉得声音好听却根本不知道是什么鸟。后来查了一圈资料发现完全可以让摄像头自己“听懂”鸟叫把监控摄像头自带的音频流接入一个叫 BirdNet-Go 的开源服务让模型实时判断这段声音里有没有鸟叫是哪一种鸟并把结果自动记录下来。这篇文章就围绕“监控摄像头 BirdNet-Go 自动鸟类识别系统”的搭建过程展开。内容包括整个系统的识别原理、RTSP 音频流验证、BirdNet-Go 的安装与配置、识别日志解读、常见报错排查以及后期可以继续扩展的方向。如果你手里恰好有一台支持 RTSP 的监控摄像头又有 Linux 小主机或者树莓派那么这套方案非常值得试一试。1. 为什么给监控摄像头加一个“声纹耳朵”1.1 传统监控回放的痛点很多家用摄像头或者庭院监控主要能力都是“视频录制 事件回放”。从技术角度看它已经有了一块完整的“记忆”但缺少“理解”。举个很实际的场景鸟并不是每时每刻都在叫更多时候只是某一小段时间在窗台附近活动。如果不用全天录像回放很容易错过。但如果真的把一整天 24 小时的视频都搬到播放器里一帧一帧去听又非常消耗精力。更麻烦的是视频文件中存在大量静默段、风声、雨声、汽车声这些都会干扰人工判断。人不可能长时间高强度集中注意力去监听。所以如果能把“先录音再判断”这个过程自动化就能把人力解放出来。1.2 BirdNET 与 BirdNet-Go 分别是什么BirdNET 是康奈尔鸟类学实验室开源的一个深度学习模型。它专门用于从音频中识别鸟类核心逻辑是输入一段鸟鸣或环境声音模型输出几种最可能的鸟类物种以及对应置信度。BirdNET 最常见的用法是拿着录音文件去跑一次离线分析。但在“监控摄像头持续采集”的场景里我们需要的是一个能 7x24 小时常驻、自动读取音频流、循环推理的服务程序。BirdNet-Go 就是基于 BirdNET 模型的服务化封装用 Go 语言实现。它相比直接用 Python 脚本批量跑音频更适合部署在低功耗小主机上。概括一下我理解中 BirdNet-Go 的核心优点常驻后台不需要每次分析都手动启动一次。可以直接把 RTSP 流或本机音频设备作为输入源。识别结果支持写日志、生成事件记录也能通过 Web 界面查看。部署简单通常单个二进制文件加配置文件就能运行。CPU 占用率比我在纯 Python 上的尝试低很多适合树莓派、旧 mini 主机这类设备。1.3 这套系统到底帮你完成了什么回到最初的问题我们希望摄像头从“录像机”变成“观察助手”这需要三个能力第一持续监听环境声音而不是等鸟叫完了再去翻录像。第二对声音做物种识别判断出声音大概率来自什么鸟。第三自动生成事件记录比如在哪个时间点发现乌鸫置信度多少持续多久。“监控摄像头 RTSP 音频 BirdNet-Go”就是满足这三个条件的低成本组合。摄像头负责收集声音BirdNet-Go 负责识别日志和通知负责保存结果。2. 系统原理与架构设计2.1 整体数据流先给出一个整体数据流图方便后面理解每一步在做什么[监控摄像头自带拾音器] | | RTSP 音视频流 v [BirdNet-Go 常驻服务] —— 解码音频 —— 静音检测/分片 | | 有效音频片段 v [BirdNET 深度学习模型推理] | | 返回物种 置信度 v [置信度大于阈值] | 是 | 否 v v [记录检测事件] [丢弃或忽略] | v [Web 界面 / 日志 / 自定义通知]核心思想并不复杂。监控摄像头通常内置麦克风并且把音频打包进 RTSP 视频流一起传输。BirdNet-Go 通过 RTSP 协议拉流后不是直接对整段长音频做判断而是把音频切成一段段短窗口逐步送入 BirdNET 模型。2.2 一次“自动识别”的背后过程当系统正常启动后识别过程大致是这样的服务从 RTSP 流中拉取音频数据。如果音频编码不是模型期望的格式会先做解码、重采样。系统按固定的时间窗口切分音频。每个片段进入 BirdNET 模型推理输出常见的候选物种。如果候选物种置信度超过设定阈值则把这一条记录为一次有效检测。检测结果会写入日志同时推送到自带的 Web 界面。这里有一个概念需要区分短视频平台的“识鸟”大多是识别画面里的鸟而 BirdNET 识别的是声音。所以摄像头并不需要把鸟拍得多么清楚只要音频通道正常鸟在离摄像头一定范围内叫系统就有机会识别到。2.3 系统边界与适用场景这套系统更适合庭院、花园、阳台、农田、自然保护区外围、半开放观察点。它不适合完全封闭的房间内部因为鸟类活动较少。如果你的摄像头装在公共区域记得遵守当地法律法规与隐私要求不要将摄像头正对邻居门窗、路人行走区等敏感范围。技术可以用来做生态观察但不应该以牺牲他人隐私为代价。3. 环境准备与前置验证3.1 硬件需求在搭建前建议先确认硬件条件。识别节点建议选择一台常开的 Linux 主机不一定需要高性能。以 BirdNet-Go 的运行方式来说一台 2GB 及以上内存的树莓派 4B、群晖 NAS 虚拟机、或者闲置的 x86 小主机都可以承担。摄像头方面核心要求是支持 RTSP 协议并且开启音频采集。部分摄像头默认关闭了音频需要在 App 或 Web 管理后台中检查。另外有一点很容易被忽略很多摄像头有“主码流”和“子码流”两路 RTSP音频可能只存在于主码流或者单独的一路通道中。后面我会专门讲怎么验证音频流。3.2 软件环境本文示例以 Linux 为例尽量采用通用命令。版本方面不需要完全固定文章会在关键位置提示你根据实际环境调整。最基础的软件环境是Linux 操作系统Debian/Ubuntu/Raspberry Pi OS 均可curl、wget 等基础命令ffmpeg/ffprobe 工具用于验证 RTSP 音频流BirdNet-Go从 GitHub Release 页面获取systemd用于把 BirdNet-Go 配置成开机自启服务如果你使用的是其他发行版命令可以自行等价替换。3.3 验证摄像头 RTSP 地址和音频通道安装 BirdNet-Go 之前强烈建议先手工确认一下摄像头 RTSP 流是否真的包含音频。否则后面配置半天最后发现摄像头本身就没声音排查会很痛苦。不同厂商摄像头的 RTSP 地址规则不一样常见的大致有下面样式rtsp://用户名:密码摄像头IP:554/Streaming/Channels/101 rtsp://用户名:密码摄像头IP:554/live rtsp://用户名:密码摄像头IP:554/stream1你可以登录摄像头后台查看官方文档找到对应的 RTSP 地址。拿到地址后使用 ffprobe 检查流信息# 使用 TCP 传输避免 UDP 丢包导致音频卡顿 ffprobe -rtsp_transport tcp -i rtsp://admin:password192.168.1.64:554/Streaming/Channels/102 -show_streams -show_format正常情况下输出里会同时出现codec_typevideo和codec_typeaudio。例如音频编码常见的是aac、pcm_alaw、pcm_mulaw。如果把102换成101后画面有声音但102没有音频流则说明音频只存在于某一道码流中那么 BirdNet-Go 配置里就应该填入带音频的那一路地址。如果 ffprobe 不存存sudo apt update sudo apt install ffmpeg安装后再执行上面的命令验证。4. 安装与配置 BirdNet-Go4.1 下载安装包BirdNet-Go 一般以预编译二进制形式发布。你需要访问它的 GitHub Release 页面找到适合你硬件架构的包。以 x86_64 Linux 主机为例下载并解压的思路如下sudo mkdir -p /opt/birdnet cd /opt/birdnet # 注意请把你从 Release 页面复制的实际下载地址替换到下面 sudo wget -O birdnet-go.tar.gz https://github.com/tphakala/birdnet-go/releases/download/请替换为版本号/请替换为安装包名.tar.gz sudo tar -xzf birdnet-go.tar.gz sudo ls -lh如果主机是树莓派架构通常是arm64或armv7需要下载对应平台的包。不要盲目把 x86_64 的包复制到 ARM 设备上运行。解压后目录中一般会包含可执行文件、示例配置和模型文件。不同版本的文件名会有所不同所以不要死记文件名以 Release 页面和实际解压结果为准。4.2 初始化配置文件BirdNet-Go 项目本身一直在迭代不同版本对字段的命名会有差异。因此这里不让你直接复制一份所谓的“万能配置”而是建议按下面的思路去处理首先运行一次birdnet-go。有些版本首次启动会自动生成默认配置。或者在解压目录中找到config.example.yaml或类似文件复制为实际配置文件。根据注释修改音频源、地理坐标、判定阈值等参数。一份配置文件的关键结构通常长这样# 示意结构字段名以你当前版本自动生成的配置为准 log: level: info audio: # 如果服务支持 rtsp 类型的输入源就在这里配置 source: rtsp rtsp_url: rtsp://admin:password192.168.1.64:554/Streaming/Channels/102 rtsp_transport: tcp analysis: # 每次推理的音频窗口大小常见是 3 秒 window_seconds: 3 overlap: 0.5 # 检测置信度阈值 conf_threshold: 0.75 # 地理位置部分版本会利用经纬度过滤不可能的物种 lat: 31.23 lon: 121.47 # Web 管理端口如果版本默认不是 8080按日志输出为准 web: port: 8080上面不是一份可以从任意版本直接复制的配置但它展示了关键配置项的作用。如果你在配置时遇到“参数名对不上”第一反应不应该是怀疑而应该打开当前版本的默认配置注释逐项对照。有一点特别想提醒lat和lon一定要尽量填写准确。BirdNET 模型在推理时可以利用地理位置缩小候选物种范围。如果你设备在上海却填了美国某个位置的经纬度系统很可能输出一堆原产地根本不在附近的鸟类误报率会明显增加。4.3 使用 systemd 常驻运行一旦手动运行 BirdNet-Go 能正常工作就可以把它注册成 systemd 服务让它在系统启动后自动运行即使进程崩溃也能自动重启。先创建一个专门运行服务的管理用户避免直接使用 rootsudo useradd -r -s /usr/sbin/nologin birdnet sudo mkdir -p /var/lib/birdnet sudo chown -R birdnet:birdnet /var/lib/birdnet然后创建服务文件sudo nano /etc/systemd/system/birdnet.service在文件中写入[Unit] DescriptionBirdNet-Go Bird Sound Recognition Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Userbirdnet Groupbirdnet WorkingDirectory/opt/birdnet ExecStart/opt/birdnet/birdnet-go Restarton-failure RestartSec10 EnvironmentHOME/var/lib/birdnet [Install] WantedBymulti-user.target接着重新加载 systemd 并启动sudo systemctl daemon-reload sudo systemctl enable --now birdnet.service查看是否正常运行sudo systemctl status birdnet.service如果服务状态是active (running)并且日志中没有报错说明 BirdNet-Go 已经进入常驻分析状态。4.4 快速自测为了确认识别链路是否已经打通可以考虑做一个最小验证。方法一打开摄像头所在区域的实时音频监听在摄像头附近播放规范的鸟鸣录音。比如在手机或音箱上播放白头鹎的鸣叫声然后观察 BirdNet-Go 日志是否出现一条类似“检测到白头鹎”的记录。方法二如果手上有一段可信度很高的鸟类录音可以直接用录音播放后再回头收集日志对比系统检测结果和实际声音内容。只要能在 10 到 40 分钟内捕捉到至少一条有效检测就说明“摄像头拾音 → RTSP 拉流 → 模型推理 → 日志输出”的整条链路已打通。5. 识别日志与结果解读5.1 如何看当日检测结果使用 journalctl 可以实时查看服务日志sudo journalctl -fu birdnet.service如果系统检测到了鸟类可能会看到类似下面的日志内容time2025-05-18T06:32:0708:00 levelinfo msgdetection species乌鸫 commonCommon Blackbird score0.92 time2025-05-18T06:38:4408:00 levelinfo msgdetection species白头鹎 commonLight-vented Bulbul score0.84注意不同版本输出的日志格式可能不同。以实际输出为准。这里重要的是三个信息species物种名称。time检测发生时间。score模型置信度。通常 score 越接近 1说明模型对该片段声音属于某物种的判断越自信。5.2 置信度与准确率的关系BirdNet-Go 并没有“完全确定”这回事。模型输出的是概率分布选出的物种只是概率最高的候选。假设阈值设置为 0.5那么当周围有风吹树叶、汽车鸣笛、装修电钻等声音时也容易把一个低置信度结果提升到阈值以上从而产生误报。如果把阈值调高到 0.95误报会减少但一些本来声音不清晰、远处鸟叫的弱信号也会被过滤掉。因此阈值不是越高越好。建议前期把阈值设得稍低比如 0.7 左右先积累一周数据。根据误报比例再逐步往 0.8、0.85 调整。5.3 建立一份人工抽检记录当系统开始运行后你可以拿“人工回听”作为参照标准。操作很简单每当系统输出一条检测记录就把对应时间附近的监控录像找出来回听 1 到 2 分钟确认录像里到底是不是这种鸟在叫。连续记录几天后你就能知道自己这套系统在当前环境下的误检率。比如系统报了 20 次乌鸫你人工确认其中 17 次确实有乌鸫声那在你这套设备环境下置信度阈值和麦克风增益就算基本合理。这个抽检步骤虽然繁琐却是整个系统里最值得投入的部分。因为不同摄像头拾音性能差异很大四周环境底噪也不一样脱离实际场地去调参数是不科学的。6. 增加自动通知识别结果推送到手机6.1 通知的基本思路如果摄像头安装在办公室、老家或者院子里你不可能每天打开后台看一次日志。更稳妥的做法是把识别结果发送到钉钉、飞书、Server酱或者 Telegram 机器人。实现方式不需要每次都改 BirdNet-Go 源码很多版本已经支持配置 Webhook 输出。如果你的 BirdNet-Go 版本支持 Webhook那么在配置里添加类似下面的内容webhooks: enabled: true url: https://你的通知服务地址如果版本暂时没有直接配 Webhook也可以让日志作为中转。大致思路是把 BirdNet-Go 日志输出到指定日志文件。写一个小脚本实时 tail 日志内容。当脚本发现满足条件比如 score 大于 0.9 的记录时提取物种名称。使用 curl 将消息发送到通知服务。6.2 日志触发脚本示例下面以一个通用的 JSON Webhook 为例演示脚本思路#!/usr/bin/env bash LOG_FILE/var/lib/birdnet/birdnet.log WEBHOOK_URLhttps://example.com/webhook tail -n 0 -F $LOG_FILE | while read -r line; do # 只处理包含 detection 标识的记录避免把普通日志也发出去 if echo $line | grep -q detection; then # 不同日志格式解析方式不同这里用 sed/jq 按实际格式提取 species$(echo $line | sed -n s/.*species\([^]*\).*/\1/p) score$(echo $line | sed -n s/.*score\([0-9.]*\).*/\1/p) if [ -n $species ]; then # 只推送置信度较高的结果 if awk -v s$score BEGIN {exit !(s 0.9)}; then curl -s -X POST $WEBHOOK_URL \ -H Content-Type: application/json \ -d {\species\:\$species\,\score\:\$score\,\time\:\$(date %Y-%m-%d %H:%M:%S)\} fi fi fi done这个脚本有两个地方需要根据实际调整日志文件路径。sed提取字段的正则表达式。第二个点比较容易踩坑。因为不同版本 BirdNet-Go 的日志字段格式可能不同。如果你发现脚本没有输出结果先用sudo journalctl -u birdnet.service --since 1 hour ago | grep detection打印一条真实日志再根据真实格式修改正则。6.3 通知推送注意事项推送通知可能带来新的问题。如果鸟类活动频繁而阈值设置过低手机一天收到几百条消息很容易变成“通知轰炸”。解决思路有两个一是提高推送阈值把通知阈值和记录阈值分开。比如识别记录阈值是 0.75但只有 confidence 超过 0.9 的检测事件才推送到手机。二是增加“相同物种冷却时间”。比如同一种鸟在 5 分钟内只推送一次。这一点经常有人忽略而它恰恰是让整套通知系统可用的关键。7. 常见问题与排查思路7.1 有画面但没有识别记录如果系统运行正常但始终没有任何鸟检测记录先按顺序排查。先确认摄像头是否开启了音频通道。去摄像头管理后台看音频设置部分摄像头默认只录制视频需要手动开启音频。再用 ffprobe 验证ffprobe -rtsp_transport tcp -i rtsp://你的RTSP地址 -show_streams如果输出里没有codec_typeaudio说明这一路 RTSP 流根本没有音频需要换通道或者调整摄像头声音设置。如果音频流存在继续检查 BirdNet-Go 日志中是否正确解码、是否在持续接收音频包。有些编码格式需要额外配置解码器或者需要打开ffmpeg预处理选项。7.2 频繁误报误报来源往往不是模型“太笨”而是环境声音太杂。排查顺序建议如下第一把阈值从 0.7 提升到 0.8 以上观察误报是否明显下降。 第二检查经纬度配置是否正确。如果在欧洲地区用非洲位置的经纬度显然会产生大量地域外物种。 第三留意风扇声、空调外机声、蟋蟀声、蛙声。某些频率段与鸟鸣接近模型可能把昆虫声误判为鸟鸣。一个比较实际的思路是设置“识别时间段限制”。早晨 5 点到上午 10 点、下午 3 点到傍晚 6 点通常鸟类活动更集中。如果摄像头旁边有空调外机持续运转还可以通过“物种黑名单”进一步过滤明显是误报的物种。7.3 内存占用持续增长、服务卡死某些长期运行的流媒体分析程序在持续拉取不稳定的 RTSP 流时可能出现内存增长。建议一升级到最新版本并定期关注项目 Release 和 Issue 中是否有类似问题反馈。建议二在 service 配置里增加一定程度的自动重启策略。比如检测到服务假死时由 systemd 拉起Restarton-failure RestartSec10建议三给系统增加简单的内存监控超过阈值就主动重启服务避免摄像头音频流中断导致解码器异常# 这是一个监控思路示例具体路径按实际环境调整 if [ $(ps -C birdnet-go -o rss) -gt 1048576 ]; then sudo systemctl restart birdnet.service fi7.4 常见问题速查表问题现象常见原因解决思路服务启动失败版本包架构不对检查是否下载了 x86_64/arm64 对应包无任何识别记录摄像头 RTSP 流没有音频用 ffprobe 检查切换到带音频的通道识别结果全是常见鸟但明显不对经纬度未设置或错误填写准确经纬度重建候选列表白天正常傍晚误报激增昆虫、蛙类等环境声音干扰提高阈值增加时间段过滤日志有报错但不知道在哪默认查看方式不对用journalctl -u birdnet.service -e查看尾部错误Web 界面无法打开端口被占用或配置错误检查服务日志输出端口再检查防火墙7.5 日志排查清单当问题出现时不要盲目重启建议按下面清单收集信息服务当前状态是 active 还是 failed。最近 10 分钟日志中是否反复出现同一报错。ffprobe 能否正常读取音频流。摄像头与主机之间网络是否丢包。RTSP 地址里是否包含特殊字符导致解析失败。配置文件中音频源是否被其他进程占用。8. 最佳实践与工程化建议8.1 不要把 RTSP 地址暴露到公网RTSP 地址经常自带用户名和密码例如rtsp://admin:password192.168.1.64:554/...。如果为了在外网查看识别结果将 RTSP 端口直接映射到公网很容易成为扫描器的攻击目标。摄像头固件本身的安全性参差不齐这类设备被渗透的例子并不少见。更安全的做法是摄像头和 BirdNet-Go 主机放在同一内网。外网访问时只开放 Web 管理界面端口并启用高强度密码、二次认证。RTSP 相关端口不对公网开放。8.2 定期保留“证据片段”识别日志只是一串文本。如果后续想把数据用于真正的生态观察最好在检测到鸟时把对应时间前后的监控音频片段保存下来。思路很简单鸟事件发生后调用 ffmpeg 从录像回放中截取片段或者直接对 RTSP 流做短时录制。之所以强调这一点是因为如果只保存物种名称和置信度几个月后想重新核对某条“稀有记录”时会缺少原始素材导致无法验证真假。鸟类识别不是纯娱乐任何观察记录都需要尽量做到可追溯。8.3 用最小权限和独立用户运行服务不要为了让服务能访问文件就直接用 root 启动 BirdNet-Go。建议单独创建系统用户只给必要的目录权限。这样即使服务本身因为解析异常输入出现漏洞攻击者能获得的影响范围也会被限制。在 systemd 章节中我们已经创建了birdnet用户这一步不是多余的而是比较重要的安全基础。8.4 阈值不要一调了之置信度阈值影响非常大。系统刚开始运行时不要追求极低的误报率而是先保持较低的阈值收集真实数据。不建议今天觉得误报多就调 0.9明天觉得漏报多又调回 0.5。更好的做法是运行一周记录阈值。对比人工抽检结果。按周为单位调整。每次调整都记录日期和原因。这样做的好处是一个月之后你能清楚知道当前阈值到底是在“减少误报”还是“增加漏报”。8.5 及时更新模型和版本BirdNet-Go 和 BirdNET 模型都在持续迭代。新版本通常会修复一些识别错误增加更多物种或者改善低置信度处理逻辑。建议时不时关注项目官方 Release 页面升级前先备份配置在测试环境验证后再切换。不要在生产主机上直接覆盖升级而不测试。8.6 隐私与合规问题最后这点必须单独强调。监控摄像头属于敏感设备使用时应严格遵守当地法律法规和平台规范。不要将摄像头对准邻居家门、公共道路、他人住宅窗户等可能侵犯隐私的区域。即便你是为了观察院子里的鸟类也应该明确告知家庭成员和相关人员摄像头的作用范围。技术项目可以做得很酷但前提是安全、合规、尊重他人隐私。9. 后续可以继续扩展的方向当“识别鸟叫”这一条链路稳定运行后后续可以做的事情其实很多。比如把识别结果记录到数据库中并按小时、按天统计鸟类出现频率生成一张“庭院鸟类活动热力图”。整个冬天下来你就能看到哪些鸟是常客哪些鸟可能只是路过哪些鸟在特定天气条件下更活跃。如果主机性能允许还可以把视频通道和音频通道结合当 BirdNet-Go 识别到某只鸟时立刻调用摄像头 PTZ 功能转动到声音来源方向拍一段该鸟的画面。这样不仅记录了声音还可能得到一张比较清晰的鸟类照片。如果你有多台摄像头分布在不同的位置还可以把 BirdNet-Go 部署成中心节点统一采集多个点的检测结果形成多点对比。这种方式对农田鸟害调查、小型公园鸟类分布观察都有帮助。回到最开始的问题一支监控摄像头加一个开源模型就能把一个“只能事后回放”的设备升级成“会自动记录、会主动通知”的观察工具。这个项目真正动手起来最大的门槛并不是代码而是验证音频流、调整阈值、积累抽检数据这些看起来不起眼的琐碎步骤。如果你正在准备搭建一套类似的 BirdNet-Go 自动鸟类识别系统建议先从“确认摄像头 RTSP 里真的有音频”开始。把那一步验证好后面的大部分流程基本就能顺利推进。