让六台安防摄像头学会观鸟
自托管 BirdNET-Go 接上六台 UniFi 摄像头的麦克风后,夜里也能听见刺猬和狐狸;但作者 ffprobe 后发现摄像头音频只有 16 kHz,唱得高的鹪鹩根本没被录下来。
中文
复制
我家周围装了六个 UniFi 摄像头,每个都带麦克风,而我一次都没用过。与此同时,花园里全是我叫不上名字的鸟,从被压平的草坪来看,夜里还有刺猬和狐狸排队经过。所以当我偶然发现 BirdNET-Go——一个自托管分析器,能把 BirdNET 模型跑在实时音频上——计划就自己成形了:把它接到摄像头麦克风上,让机器替我观鸟。
社区里有现成的 Proxmox 脚本,我本以为安装是最无聊的部分。结果并不是。
早饭前挂掉的两个容器
第一次部署静默死在一个交互式存储选择提示上。原来新版社区脚本是从单个应用的变量文件里读存储池,而不是我三十多个容器一直依赖的全局默认值。第二次部署起来后没有默认网关,因为在 appdefaults 模式下,全局网关设置同样被忽略了。接着安装程序卡在 80% CPU 上空转,去探测它根本解析不了的 Debian 镜像源。第三次,把六个键都写进应用文件,三分钟就装好了。
音频接入倒是意外地顺利。UniFi Protect 的集成 API 直接认了我现有的控制器密钥,每个摄像头一次调用就返回现成的 RTSPS 地址:
GET /proxy/protect/integration/v1/cameras//rtsps-stream
六路流,六个 ffmpeg 进程剥掉视频,不到一分钟仪表盘上就出现了一只鹪鹩。
不可能的鸟出现的那天早上
第一个黎明合唱产生了上千条识别结果,物种列表一眼就能看出问题。河乌——一种河边生活的鸟。九月初六十一只红翼鸫,比任何红翼鸫抵达的时间都早了六周。还有翠鸟、赤膀鸭、文须雀。这其实是路面噪音。轮胎碾过柏油路的嘶嘶声是宽频的,对一个在河边数据上训练出来的模型来说,听起来就像河乌。
真正的放大器更隐蔽。平均置信度是 0.5,而阈值是 0.7,按理说不该有结果——直到你了解动态阈值:一次高置信度的误报会把那个物种的门槛降到 0.2,并维持一整天,于是所有相似的声音都能溜进来。有二十六个物种的门槛被噪音拉低了。
修复用的是项目自带的调参工具链,只不过这次用对了。深度检测要求同一物种在一个时间窗内多次命中、相互印证。更严格的音域过滤,光这一项就把候选名单从 6,522 个物种压到 139 个,河乌直接被踢了出去。对仍在音域内的三个噪声重灾区,按物种单独设 0.9 阈值。再加一个 250 Hz 高通滤掉引擎低频轰鸣。我还清空了数据库,所以这之后的一切都是干净的基准。
然后我调过头了。误报过滤的第 3 级要求重复命中五次,我眼看着 29 个候选接连被丢弃,一只林岩鹨已经命中四次、差一次就被扔掉。隐私过滤设成凡是可能为人声的一律静音,结果它以 0.06 的置信度对车流声也触发了。把级别调回 2、隐私阈值设为 0.3,鸟就回来了。
崩溃过、后来又没事的 GPU
这台机器有一块 Intel 核显,BirdNET-Go 可以通过 OpenVINO 在它上面跑推理。启用之后服务陷入崩溃循环:free(): invalid next size,glibc 堆损坏中止,发生在驱动的设备枚举过程中。我把它固定到 CPU 上,然后去睡了。
第二天早上我试着真正解决它。一个做同样枚举的最小 C 程序跑得很干净。用 valgrind 检查:零错误。接着真正的二进制文件也不再崩了。这两天之间我改过容器的核心数、线程设置,还预热了驱动的着色器缓存,到现在我也不知道是哪一个起了作用。现在两个模型都跑在核显上,CPU 占用从 30% 降到 10% 以下。
我能修的是故障模式。探测过程中驱动中止,不该把整个分析器拖垮,于是我写了个补丁:把设备枚举放进一个短命子进程里执行,子进程挂掉就回退到 ONNX Runtime。我往枚举的兼容层里注入了一个 abort() 做故障测试,看着子进程以和原来完全一样的特征崩溃,而服务毫不在意,继续运行。这现在是上游的一个 issue和一个 pull request,两个审查机器人从我第一版里找出了三个真实问题,既让人汗颜,也确实有用。
刺猬,以及那个坑
夜班时段,我加了 Google 的 Perch v2 作为第二个模型,它的标签集里有 Red Fox、European Hedgehog 和 Badger。夜行类群被加进白昼过滤器的名单,这样白天识别出的“fox”就会被当作噪声丢掉。
然后就撞上了那个坑——起因是我在查为什么站在门口就能听见的鸟,得分只有可怜的 0.55。我用 ffprobe 看了摄像头的流。所有画质档位带的音频都一样:AAC、单声道、16 kHz。这意味着信号上限被卡在 8 kHz,而鸣禽相当大一部分能量都在这个频率之上。模型听到的花园是被闷住、削掉高频的版本,再怎么调参也找不回从未被采到的频率。
所以摄像头就继续留给鸦科、椋鸟和狐狸吧,它们都稳稳地在 8 kHz 以下。至于鹪鹩和戴菊,下一步是在花园里放一块小板子,接一个正经的 48 kHz 麦克风,作为第七路流回传。刺猬嘛,据说今晚会来。