1. 为什么要在树莓派4B上折腾Yocto加OpenCV这套方案
很多人第一次听到“用树莓派做AI监控”,脑子里浮现的可能是装个官方系统、pip install opencv-python、再跑个现成的检测脚本就完事了。这条路确实能跑通,但只要你真正把它放到需要长期通电、无人值守、还要稳定输出视频流和检测结果的场景里,问题就会一个接一个冒出来:系统盘莫名其妙写满、SD卡跑几个月就挂、开机自启脚本时灵时不灵、Python环境被一次误操作搞崩、升级个库把整个系统带崩。我自己最早那套监控就是拿官方系统搭的,跑了不到三个月,SD卡先坏了一张,换卡重装之后又因为一次apt upgrade把摄像头驱动搞没了,从那之后我就开始认真考虑用Yocto来做一套“固化”的镜像。
Yocto这个工具在嵌入式圈子里名气很大,但很多做应用层开发的朋友对它有点发怵,觉得它门槛高、编译慢、配置复杂。其实换个角度理解就简单了:Yocto不是让你去写一个操作系统,而是让你像写菜谱一样,把“这个设备上到底要装哪些东西、每个东西用什么版本、编译参数是什么”全部用配置文件描述出来,然后它帮你从源码开始构建出一个完全可复现的镜像。这带来的最大好处就是可复现和可裁剪——你今天构建出来的镜像,半年后拿同样的配置再构建一次,结果几乎一模一样;而且镜像里只包含你真正需要的东西,没有多余的桌面环境、没有一堆用不上的服务,系统盘占用可以压到很小,跑在SD卡上寿命也长得多。
那为什么还要配OpenCV?因为AI监控的核心无非是两件事:拿到图像、对图像做处理。OpenCV是图像处理领域最成熟的库,从简单的运动检测、轮廓提取,到配合轻量级模型做目标检测,它都能覆盖。在树莓派4B这个性能级别上,你不可能跑太重的模型,但用OpenCV做帧差法运动检测、背景建模、区域入侵判断这些,完全够用,而且CPU占用可控。把OpenCV编译进Yocto镜像里,意味着你的应用一启动就有完整的图像处理能力,不需要在设备上现场装库、现场编译,省去了大量部署时的麻烦。
这套方案适合谁?我觉得有三类人值得认真看看。第一类是手上已经有树莓派4B、想把它改造成长期运行的监控节点,但被系统稳定性折磨过的朋友;第二类是做嵌入式产品原型、需要把AI视觉能力固化到设备里的开发者;第三类是想学Yocto但一直找不到合适练手项目的人——用树莓派加摄像头做监控,需求明确、硬件便宜、踩坑成本低,是个非常好的入门实战。接下来我会把整个思路、配置、编译、部署、调试的过程拆开讲,尽量把每个“为什么这么做”说清楚,让你不只是抄配置,而是真正理解这套东西怎么运转。
2. 整体方案设计与关键选型背后的考量
2.1 为什么是Yocto而不是官方系统或Ubuntu
先把这个最核心的选型问题说透。树莓派官方系统(Raspberry Pi OS)和Ubuntu for Raspberry Pi都是开箱即用的方案,装完就能跑,开发体验好,这是它们最大的优势。但它们的定位是“通用操作系统”,里面预装了太多你监控场景根本用不到的东西:桌面环境、办公软件、各种后台服务。这些东西不仅占空间,还会在后台消耗CPU和内存,更重要的是,它们会带来大量的写操作,而SD卡的写入寿命是有限的。
我实测过一组数据:官方系统空载运行时,每分钟的磁盘写入量大概在几百KB到1MB之间波动,主要来自日志、系统服务状态记录等;而一个裁剪过的Yocto镜像,空载写入可以压到每分钟几十KB甚至更低。别小看这个差距,SD卡的擦写次数是有限的,写入量降一个数量级,寿命就能延长好几倍。另外Yocto构建出来的镜像是只读挂载根文件系统的理想载体,你可以把根分区设成只读,运行时数据写到单独的可写分区或者tmpfs里,这样即使突然断电,文件系统也不容易损坏——这对无人值守的监控设备来说太重要了。
Ubuntu的优势在于软件包丰富、社区支持好,如果你需要频繁更新应用、用apt装各种依赖,它确实方便。但监控设备一旦部署到位,你并不希望它频繁变动,稳定压倒一切。Yocto的“构建一次、固化部署”模式正好契合这个需求。当然,Yocto的代价是学习曲线和首次构建时间,这个后面会讲怎么优化。
2.2 第三方CSI摄像头的兼容性坑与选型建议
标题里特意写了“第三方CSI摄像头”,这不是随便加的。树莓派官方的Camera Module用的是索尼IMX系列传感器,驱动在官方内核里支持得很好。但第三方CSI摄像头就五花八门了,有OV5647、OV9281、IMX219兼容版、IMX477兼容版等等,价格从几十到几百不等。问题在于,很多第三方摄像头的驱动并没有被主线内核完整支持,或者需要额外的设备树覆盖(device tree overlay)才能正常工作。
我在选型上踩过的坑值得说一下。最早图便宜买了一个标称“兼容树莓派”的CSI摄像头,结果在官方系统上能出图,但换到Yocto构建的镜像里就死活认不到,查了半天发现它的I2C地址和官方模块不一样,需要改设备树。后来换了一个明确标注使用IMX219传感器、并且提供设备树配置文件的第三方模块,就顺利多了。所以选第三方CSI摄像头时,我建议重点确认三件事:传感器型号是否为主流型号(IMX219、IMX477这些)、卖家是否提供Linux下的设备树配置或驱动说明、接口排线是否为标准15pin或22pin(树莓派4B用的是15pin,注意别买错)。
另外树莓派4B有两个CSI接口,一个在网口旁边(CSI0),一个在音频口旁边(CSI1),两个接口在设备树里的配置略有不同,接线的时候要对应好。第三方摄像头如果排线方向插反,是认不到设备的,这个新手很容易犯,插之前一定看清楚排线金属触点朝向。
2.3 OpenCV在Yocto里的编译策略
OpenCV是个大库,完整编译一遍在树莓派上可能要几个小时甚至更久,在Yocto里编译更是如此,因为Yocto是从源码构建的。所以编译策略很关键。我的做法是只编译需要的模块,把用不到的模块全部关掉。比如监控场景里,你大概率不需要opencv_contrib里的那些高级功能(人脸识别、文本检测等),也不需要GUI相关的模块(highgui),因为设备上没有显示器。把WITH_GTK、WITH_QT、WITH_V4L(如果不用V4L2直接采集)这些选项按需配置,能省下大量编译时间。
还有一个关键点是Python绑定。如果你的应用是用Python写的,那必须开启BUILD_opencv_python3,并且确保Python版本和Yocto镜像里的Python一致。我遇到过因为Python版本不匹配导致import cv2报ModuleNotFoundError的情况,排查了很久才发现是编译时链接的Python和运行时用的不是同一个。所以在Yocto的recipe里,Python相关的依赖一定要写清楚。
至于OpenCV的版本,我建议用4.x的稳定版,比如4.5.x或4.8.x。太新的版本可能和Yocto的某些recipe有兼容问题,太老的版本又缺少一些好用的API。具体版本在recipe里通过SRC_URI指定,后面实操部分会给具体配置。
3. 从零开始搭建Yocto构建环境
3.1 宿主机环境准备与依赖安装
Yocto的构建是在一台性能较好的Linux机器上完成的,不建议直接在树莓派上构建(太慢)。我用的是Ubuntu 20.04的x86_64机器,16GB内存、8核CPU、200GB以上空闲磁盘。内存和磁盘这两个指标很关键,Yocto构建过程中会占用大量内存,磁盘占用轻松超过100GB,如果空间不够,构建到一半失败会非常痛苦。
先装依赖包,这是官方文档里列出的,我直接给一条命令:
sudo apt-get install gawk wget git diffstat unzip texinfo gcc build-essential \ chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \ iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \ pylint xterm python3-subunit mesa-common-dev zstd liblz4-tool file locales装完之后配置locale,否则构建时可能报编码相关的错误:
sudo locale-gen en_US.UTF-8这一步很多人会忽略,但一旦报错很难定位,建议一开始就做好。
3.2 获取Yocto层与树莓派BSP层
Yocto本身是一套构建框架,真正让树莓派能跑起来的是BSP层(Board Support Package)。我用的是比较成熟的组合:poky作为基础层,meta-raspberrypi作为树莓派BSP层,再加上meta-openembedded提供一些额外的软件包支持。
mkdir ~/yocto-rpi && cd ~/yocto-rpi git clone -b kirkstone git://git.yoctoproject.org/poky.git git clone -b kirkstone git://git.yoctoproject.org/meta-raspberrypi.git git clone -b kirkstone git://git.openembedded.org/meta-openembedded.git这里我选的是kirkstone这个长期支持版本,它的稳定性和社区支持都比较好。分支选择很重要,不同分支的recipe版本差异很大,混用容易出问题。三个层都下载完之后,进入poky目录初始化构建环境:
cd poky source oe-init-build-env build-rpi执行完这条命令,你会被切换到build-rpi目录,并且环境变量已经配置好了。接下来编辑conf/bblayers.conf,把刚才下载的层加进去:
bitbake-layers add-layer ../../meta-raspberrypi bitbake-layers add-layer ../../meta-openembedded/meta-oe bitbake-layers add-layer ../../meta-openembedded/meta-python bitbake-layers add-layer ../../meta-openembedded/meta-multimediameta-oe和meta-python是OpenCV依赖的一些基础库所需要的,meta-multimedia里有一些图像和视频相关的recipe,加上比较保险。
3.3 配置local.conf适配树莓派4B
conf/local.conf是构建的核心配置文件,里面决定了目标机器、镜像类型、要安装的软件包等。针对树莓派4B,关键配置如下:
MACHINE = "raspberrypi4-64"这里用64位,因为树莓派4B的CPU支持64位,64位下OpenCV的性能会更好一些。如果你有特殊的32位依赖需求,也可以改成raspberrypi4。
IMAGE_FSTYPES = "tar.bz2 ext4 rpi-sdimg"rpi-sdimg是meta-raspberrypi提供的一种镜像格式,可以直接dd到SD卡上启动,非常方便。
LICENSE_FLAGS_ACCEPTED = "commercial"OpenCV的一些组件涉及到商业许可标志,不加这个可能编译不过。
ENABLE_UART = "1"开启串口,方便调试。虽然我们主要用SSH,但串口在系统起不来的时候是救命的。
GPU_MEM = "128"给GPU分配128MB内存,摄像头采集和视频编码会用到。
DISTRO_FEATURES:append = " opengl"OpenCV的一些加速功能需要OpenGL支持。
这些配置写完之后,可以先跑一个最小镜像验证环境是否正常:
bitbake core-image-minimal第一次构建会下载大量源码包,时间比较长,视网络情况可能一两个小时。构建成功后,在tmp/deploy/images/raspberrypi4-64/目录下能找到镜像文件,dd到SD卡上插到树莓派里应该能正常启动。这一步先跑通,再往上加OpenCV,出问题容易定位。
4. 把OpenCV和摄像头支持编译进镜像
4.1 编写OpenCV的Yocto recipe
Yocto里安装软件包有两种方式:一种是直接用现成的recipe,另一种是自己写。OpenCV在meta-openembedded/meta-oe/recipes-support/opencv/里其实有现成的recipe,但默认配置编译出来的东西比较多,而且不一定开启了Python绑定。我的做法是基于现成的recipe做一个.bbappend文件,覆盖掉不需要的配置。
在meta-raspberrypi层里创建一个自己的recipe目录,或者更规范一点,单独建一个自己的层。为了简单,我直接放在meta-raspberrypi/recipes-support/opencv/下面,创建opencv_%.bbappend:
PACKAGECONFIG:remove = "gtk qt" PACKAGECONFIG:append = " python3 v4l"这几行的意思是:去掉GTK和QT的GUI支持(设备上没显示器,不需要),加上Python3绑定和V4L2支持(摄像头采集要用)。PACKAGECONFIG是Yocto里控制软件包编译选项的机制,比直接改CMake参数更规范。
如果你需要指定OpenCV版本,可以在local.conf里加:
PREFERRED_VERSION_opencv = "4.5.5"具体版本号要看你用的层里有哪些版本可用,可以在meta-oe/recipes-support/opencv/目录下看到。
4.2 配置摄像头设备树覆盖
第三方CSI摄像头能不能工作,关键在设备树覆盖。在local.conf里加上:
RPI_EXTRA_CONFIG = "dtoverlay=imx219"这里假设你用的是IMX219兼容的第三方摄像头。如果是OV5647,就改成dtoverlay=ov5647。这个配置会被写进config.txt,启动时加载对应的设备树覆盖,让内核识别摄像头。
如果你不确定自己的摄像头对应哪个overlay,可以在构建好的镜像里查看/boot/overlays/目录,里面列出了所有可用的overlay文件,文件名基本对应传感器型号。另外,有些第三方摄像头需要额外的参数,比如调整I2C地址或者时钟频率,这些可以通过dtoverlay=imx219,i2c_addr=0x10这样的形式传递,具体参数要查摄像头卖家提供的文档。
4.3 定制镜像recipe加入所需软件包
创建一个自定义镜像recipe,比如recipes-core/images/ai-monitor-image.bb:
require recipes-core/images/core-image-base.bb IMAGE_INSTALL:append = " \ opencv \ python3 \ python3-opencv \ python3-numpy \ v4l-utils \ ffmpeg \ openssh \ openssh-sshd \ i2c-tools \ "这里解释一下每个包的作用:opencv是C++库本体,python3-opencv是Python绑定,python3-numpy是OpenCV Python接口依赖的数值库,v4l-utils提供v4l2-ctl等调试工具,ffmpeg用于视频编码和推流,openssh用于远程登录,i2c-tools用于调试摄像头I2C通信。这些包不是每个都必须,但监控场景里基本都会用到。
然后在local.conf里指定构建这个镜像:
IMAGE_INSTALL:append = " ai-monitor-image"或者直接bitbake ai-monitor-image。
4.4 编译过程与时间优化技巧
编译OpenCV是整个流程里最耗时的环节。在8核16GB的机器上,完整编译一次大概需要2到4小时,取决于网络和磁盘速度。有几个优化技巧可以显著缩短时间:
第一,开启并行编译。在local.conf里设置:
BB_NUMBER_THREADS = "8" PARALLEL_MAKE = "-j 8"数字根据你机器的核心数调整,一般设成核心数或核心数加一。
第二,使用ccache缓存编译结果。安装ccache并在local.conf里加:
INHERIT += "ccache"这样重复编译时,没改动的文件会直接命中缓存,速度提升非常明显。
第三,把DL_DIR和SSTATE_DIR指向大容量磁盘,并且不要频繁清理。Yocto的下载目录和共享状态缓存是复用的,第一次构建慢,后续增量构建会快很多。
第四,如果只是调试应用层,可以先用bitbake opencv -c compile单独编译OpenCV,确认没问题再构建完整镜像,避免每次都从头来。
编译完成后,镜像文件在tmp/deploy/images/raspberrypi4-64/下,找到.rpi-sdimg结尾的文件,用dd写入SD卡:
sudo dd if=ai-monitor-image-raspberrypi4-64.rpi-sdimg of=/dev/sdX bs=4M status=progress/dev/sdX要换成你实际的SD卡设备名,写错会覆盖你的硬盘数据,这一步务必确认清楚。
5. 部署、调试与AI监控应用实现
5.1 首次启动与基础环境验证
SD卡插到树莓派4B上,接好摄像头、网线、电源,上电启动。第一次启动会做一些初始化,可能需要一两分钟。通过路由器后台找到树莓派的IP,或者用串口登录查看。SSH登录进去之后,先做几项基础验证:
vcgencmd get_camera这条命令会显示摄像头是否被检测到。如果显示supported=1 detected=1,说明摄像头识别正常;如果detected=0,就要检查排线、设备树覆盖配置。
v4l2-ctl --list-devices列出所有视频设备,正常情况下应该能看到类似bcm2835-codec和unicam的设备节点,对应/dev/video0等。
python3 -c "import cv2; print(cv2.__version__)"验证OpenCV Python绑定是否正常。如果报ModuleNotFoundError,说明Python绑定没编译进去,需要回到recipe检查PACKAGECONFIG里的python3选项。
5.2 用OpenCV读取CSI摄像头画面
树莓派上读取CSI摄像头有几种方式,最简单的是通过V4L2接口,OpenCV的VideoCapture可以直接打开/dev/video0:
import cv2 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("摄像头打开失败") exit() cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) cap.set(cv2.CAP_PROP_FPS, 15) while True: ret, frame = cap.read() if not ret: print("读取帧失败") break # 这里做图像处理 cv2.imwrite("/tmp/test_frame.jpg", frame) break cap.release()这段代码先跑通,确认能拿到画面。分辨率设成640x480、帧率15,是树莓派4B上比较平衡的配置,再高会明显增加CPU负担。如果cap.isOpened()返回False,常见原因是设备节点不对(试试/dev/video1)、权限不够(把用户加到video组)、或者摄像头被其他进程占用。
5.3 运动检测与区域入侵判断的实现
监控场景里最实用的功能是运动检测。用OpenCV做运动检测,最经典的是帧差法和背景减除法。帧差法简单快速,适合树莓派这种算力有限的设备:
import cv2 import numpy as np cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) ret, prev_frame = cap.read() prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY) prev_gray = cv2.GaussianBlur(prev_gray, (21, 21), 0) while True: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (21, 21), 0) delta = cv2.absdiff(prev_gray, gray) thresh = cv2.threshold(delta, 25, 255, cv2.THRESH_BINARY)[1] thresh = cv2.dilate(thresh, None, iterations=2) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: if cv2.contourArea(c) < 500: continue x, y, w, h = cv2.boundingRect(c) cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # 触发报警逻辑 prev_gray = gray # 处理下一帧这里几个参数值得说明:高斯模糊的核大小21是为了抑制噪声,太小了噪声会被当成运动,太大了小物体运动会被抹掉;阈值25是像素差阈值,光照变化大的场景要适当调高;面积阈值500是过滤掉小噪点,根据实际场景调整。区域入侵判断就是在画面上定义一个多边形区域,判断运动轮廓的中心点是否落在区域内,这个用cv2.pointPolygonTest就能实现。
5.4 检测结果存储与远程查看方案
检测到运动之后,需要把结果存下来或者推出去。存储方面,我建议不要把图片直接写到根文件系统,因为根分区可能是只读的,而且频繁写入影响SD卡寿命。更好的做法是写到单独的可写分区,或者通过内存文件系统暂存后定期同步。如果树莓派接了U盘或移动硬盘,写到外置存储上是最理想的。
远程查看有两种思路:一种是推流,用ffmpeg把摄像头画面编码成H.264,通过RTSP或HTTP推出去,客户端用VLC之类的播放器看;另一种是事件触发,只在检测到运动时抓拍图片,通过HTTP上传到服务器或者发邮件通知。前者实时性好但占带宽和CPU,后者省资源但看不到实时画面。实际部署中我通常两者结合:平时低帧率推流,检测到运动时提高帧率并抓拍。
推流的ffmpeg命令大概是这样:
ffmpeg -f v4l2 -input_format h264 -video_size 640x480 -i /dev/video0 \ -c:v copy -f rtsp rtsp://your-server:8554/cam1如果摄像头本身支持H.264输出,用-c:v copy直接复制流,几乎不占CPU;如果不支持,就要用libx264软编码,CPU占用会明显上升,这时候要降低分辨率或帧率。
6. 常见问题排查与实战避坑经验
6.1 摄像头相关问题的排查思路
摄像头问题是这套方案里最高频的故障点。我整理了一个排查顺序,按这个顺序走基本能定位到问题:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
vcgencmd get_camera显示detected=0 | 排线插反或接触不良 | 重新插拔排线,确认金属触点朝向 |
| 设备树overlay未加载 | 检查/boot/config.txt里的dtoverlay配置 | 确认传感器型号匹配 |
/dev/video0不存在 | 驱动未编译进内核 | 检查内核配置里的V4L2和传感器驱动 |
| 能打开设备但读不到帧 | 分辨率或格式不支持 | 用v4l2-ctl --list-formats-ext查看支持的格式 |
| 画面偏色或全绿 | 传感器配置错误 | 换用正确的overlay,检查I2C通信 |
第三方摄像头最容易出问题的是I2C通信。可以用i2cdetect -y 10(树莓派4B的摄像头I2C总线号可能是10)查看传感器是否在总线上。如果检测不到地址,基本就是硬件连接或供电问题。
6.2 OpenCV编译与运行时的典型错误
ModuleNotFoundError: No module named 'opencv'这个错误我见过太多次了。原因通常有三种:一是Python绑定根本没编译进去,检查recipe里的PACKAGECONFIG;二是编译时链接的Python版本和运行时不一致,比如编译用的是Python 3.10,运行时是3.11;三是PYTHONPATH没设置对,OpenCV的.so文件不在Python的搜索路径里。排查方法是在设备上执行:
python3 -c "import sys; print(sys.path)" find / -name "cv2*.so" 2>/dev/null看看cv2的so文件在哪个目录,是否在sys.path里。如果不在,可以通过设置PYTHONPATH或者把so文件软链接到site-packages目录解决。
另一个常见问题是OpenCV运行时报libGL.so.1: cannot open shared object file。这是因为OpenCV链接了OpenGL库,但镜像里没装。解决办法是在镜像里加上libgl1-mesa相关的包,或者在编译时关掉OpenGL支持(PACKAGECONFIG:remove = "opengl")。监控场景其实用不到OpenGL,关掉更省事。
6.3 系统稳定性与SD卡寿命的实战经验
前面提到过根文件系统只读化的思路,这里给具体做法。在Yocto里可以通过IMAGE_FEATURES和read-only-rootfs来实现:
IMAGE_FEATURES:append = " read-only-rootfs"开启之后,根分区以只读方式挂载,系统运行时的临时数据会写到tmpfs(内存文件系统)里。但要注意,有些服务需要写权限,比如SSH的host key、日志等,这些要单独配置到可写分区。我的做法是分三个区:boot分区(FAT32,只读挂载)、根分区(ext4,只读挂载)、数据分区(ext4,可读写,存放日志和抓拍图片)。这样即使数据分区写坏了,重新格式化就行,系统本身不受影响。
另外,日志管理也很关键。默认的syslog会不断写日志,建议用systemd-journald的Storage=volatile配置,把日志放在内存里,或者限制日志大小。监控应用自己的日志也要做轮转,别让一个日志文件无限增长。
6.4 性能调优的几个实用技巧
树莓派4B的CPU性能有限,做AI监控要精打细算。几个我实测有效的优化点:
第一,降低采集分辨率。640x480对于运动检测足够了,1080p会让CPU占用翻好几倍。如果确实需要高分辨率,可以只在检测到运动时才切到高分辨率抓拍。
第二,跳帧处理。不需要每帧都做检测,隔一帧处理一次,CPU占用直接减半,对运动检测的实时性影响很小。
第三,用cv2.setNumThreads()控制OpenCV的线程数。默认OpenCV会开多线程,在树莓派上可能和系统其他任务抢CPU,设成2或4反而更稳定。
第四,把图像处理的关键部分用C++写,Python只做胶水层。Python的循环和逐像素操作很慢,用C++实现核心算法,通过Python绑定调用,性能提升明显。如果不想写C++,至少要用NumPy的向量化操作代替Python循环。
第五,考虑用树莓派的硬件编码器做视频编码。树莓派4B有硬件H.264编码器,通过v4l2的m2m设备可以调用,比软编码省CPU得多。ffmpeg里用h264_v4l2m2m编码器就能用上。
7. 后续扩展方向与个人实践体会
这套方案跑通之后,能扩展的方向其实很多。最直接的是把运动检测升级成目标检测,用轻量级模型比如MobileNet-SSD或者YOLO的tiny版本,在树莓派4B上跑个几帧每秒还是可以的。OpenCV的dnn模块支持加载ONNX和Caffe模型,配合OpenCV编译时开启dnn支持就行。再进一步,可以把检测结果通过MQTT推送到智能家居平台,实现联动控制。
另一个方向是加装红外补光或者用支持夜视的摄像头模块,让监控在低光照下也能工作。第三方CSI摄像头里有带IR-CUT的型号,白天夜晚自动切换,配合红外LED补光,夜间效果不错。不过要注意,夜视模式下画面是黑白的,运动检测的参数要相应调整。
我自己在这套东西上前后折腾了小半年,最大的体会是:Yocto的前期投入是值得的。第一次构建确实麻烦,配置、编译、调试,可能要花好几天。但一旦跑通,后面部署新设备就是复制SD卡的事,十分钟搞定一台,而且每台设备的环境完全一致,不会出现“这台能跑那台不行”的情况。对于需要批量部署或者长期运行的场景,这个优势太明显了。
还有一点是关于第三方硬件的态度。便宜确实有便宜的道理,但监控这种需要长期稳定运行的场景,硬件上省的钱往往会在调试和维护上加倍还回来。我的建议是,摄像头这种核心部件,尽量选有明确文档、社区反馈好的型号,哪怕贵几十块,省下的时间成本远超这个差价。树莓派4B本身倒是很皮实,只要供电稳定、散热做好,连续跑几个月没什么问题。散热方面,加个小风扇或者散热片,CPU温度能降十几度,对稳定性帮助很大。
最后分享一个调试小技巧:在开发阶段,把检测到的每一帧都存成图片,事后回看,比盯着实时画面更容易发现问题。比如误报是因为光照变化还是因为参数太敏感,看几组抓拍图就清楚了。等参数调稳定了,再关掉这个调试输出,减少写入。