☰
用树莓派DIY可视门铃:远程监控、移动侦测与自动录像完整方案
2026/10/11 8:11:06 网站建设 项目流程

朋友家那台带屏幕的可视门铃用了不到一年就罢工了,开机卡死在LOGO画面,售后说要寄修,来回折腾了一个多星期。我劝他先别急,正好桌上一块吃灰的树莓派4B,还有一块当年拆下来没舍得扔的OV5647摄像头模块,周末两天就拼出一个功能完全不缩水的“可视门铃”:远程查看摄像头、移动侦测、自动保存视频,一样没少,花费不到五百块,而且后续想加什么功能完全自己说了算。

这篇文章我会把方案选型、硬件接线、系统搭建、远程访问、移动侦测、自动录制的完整过程都记录下来,包括中间踩过的坑。手头有树莓派、对摄像头监控和智能家居感兴趣的朋友,基本可以照着抄作业;完全没有树莓派基础的小白也能看懂每一步在干什么、为什么这么干。

1. 项目概述与整体方案设计

1.1 这个项目究竟解决了什么问题

市售可视门铃的真实痛点其实挺多的:广告吹得天花乱坠,到手发现云存储要年费、推送延迟十几秒、夜视效果发绿发糊、电池版隔几个月就得充电,更别说出故障后完全依赖售后。更重要的是,这类产品往往只能做它出厂定义好的那几件事,想加个“识别快递员”“有人停留太久提醒我”之类的功能,基本没门。

树莓派方案的本质就一句话:把门铃拆成“画面采集、事件检测、存储通知”三个模块,每个模块都能独立选型和编程。画面来自CSI摄像头,事件检测可以靠PIR传感器硬件触发,也可以靠OpenCV算法判断画面变化,检测结果触发录制和消息推送。整个系统的核心控制权完全在自己手里,不需要依赖任何第三方云平台,视频默认存在本地SD卡里,数据和隐私都更可控。

这个项目适合谁?想入门树莓派和摄像头开发的人,想给老房子低成本改造智能门禁的人,还有已经玩过一些树莓派基础操作、想往OpenCV方向进阶的人。它比单纯的“点亮LED”“读取温湿度”项目复杂一个台阶,但又没有复杂到像自动驾驶小车那样劝退新人,是一个很理想的进阶练手项目。

1.2 两条技术路线:一条快、一条深

做树莓派摄像头监控,网上一搜能找到两条主流路线:

路线代表方案优点缺点
开箱即用MotionEyeOS / Motion装完就是完整监控系统,带网页界面、移动侦测、录像管理,半小时跑通定制逻辑要改配置文件,加推送、加AI识别很别扭
自研编程Python + OpenCV + picamera2逻辑完全可控,想怎么改就怎么改,能扩展人脸识别、物体检测需要写代码,初期工作量稍大

我的建议很直接:想快速看效果,可以先装Motion跑一晚上,感受一下完整的监控系统长什么样。但如果你真的想做一个“好用的门铃”,而不是一个“能看画面的玩具”,还是老老实实走Python自研路线。

原因很现实:门铃场景里有大量细节需求是Motion这类通用软件照顾不到的。比如白天和晚上移动侦测灵敏度要自动切换,比如有人按门铃时要给你手机推一条包含照片的通知,比如只对门口一米范围的运动感兴趣、远处马路上的车经过不要误触。这些需求靠改OpenCV代码来实现,比在Motion的配置墙里硬钻轻松得多。而且自研路线学到的OpenCV知识,之后做树莓派小车避障、药品检测、火灾火焰识别这些项目都能直接迁移复用,投资回报率完全不同。

1.3 硬件清单与选型理由

我的完整清单如下,大部分东西网上都能买到:

部件型号/规格用途参考价格
主控板树莓派4B 2GB版核心计算,跑Python和OpenCV约300元
摄像头OV5647 CSI摄像头模块门口画面采集,500万像素约50元
存储32GB MicroSD卡 A1级别系统盘+录像存储约30元
门铃传感器HC-SR501人体红外模块检测门口是否有人靠近约5元
门铃按键轻触按键开关访客主动呼叫忽略不计
电源5V 3A Type-C充电器稳定供电约20元
散热30mm主动散热风扇+铝散热片防止高负载过热降频约15元
接线杜邦线若干、面包板连接PIR和按键约10元

有几个选型细节值得展开讲讲。树莓派4B的2GB版跑这个项目完全够用,但为什么我不推荐3B+?因为4B在OpenCV图像处理上性能强出一截,移动侦测的实时性体验差别很明显。OV5647这块摄像头是树莓派官方生态里最便宜的模块,500万像素,虽然画质比不过同价位USB摄像头,但胜在走CSI接口、延迟低、不占USB口,适合长期挂机。HC-SR501是市面上最常用的红外人体感应模块,它的作用不是替代移动侦测算法,而是作为“有人来了”的硬触发信号,和算法判断互为校验,能大幅减少误报。

2. 硬件准备与系统搭建

2.1 系统刷写与基础配置

我的做法是直接烧一个Lite版系统,不带桌面环境,省内存、省存储、稳定优先。用Raspberry Pi Imager工具烧录时,有一个容易被忽略的入口:右上角齿轮图标可以预置SSH、WiFi和用户密码。把这个配置填好,树莓派通电后就能直接走SSH连接,不用先接显示器键盘鼠标。

系统建议选Raspberry Pi OS Lite(64位),刷完后开机,在路由器后台找到树莓派的IP,用SSH连进去,第一件事换国内软件源。不同版本系统和不同Debian版本的源地址不一样,直接搜“树莓派修改源”能查到对应的条目,核心思路就是把/etc/apt/sources.list里deb.debian.org替换成国内镜像地址,然后把软件列表更新一遍。这个操作能节省后面安装OpenCV、Python依赖包的大量等待时间,属于必做项。

基础配置完成后,顺便把系统里的包全升级一遍:

sudo apt update && sudo apt full-upgrade -y

2.2 摄像头模块接线(OV5647 CSI)

CSI摄像头最开始的接线环节就能吓退一部分人。树莓派4B的CSI接口在网口和HDMI接口之间,是一排带卡扣的排线座。接的时候先轻轻向外拨开卡扣,把排线插到底,注意排线上的金属触点朝向,不同型号树莓派方向不同,4B上蓝色标签那一面朝网口方向,插到位后把卡扣按回去。这里踩过一次坑:排线没插到底,系统里怎么都搜不到摄像头设备,重新拔插后立刻就好了。

新版本的Raspberry Pi OS默认启用了camera_auto_detect=1,理论上插上排线就能用。输入libcamera-hello,如果屏幕窗口出现画面,说明硬件链路没问题:

libcamera-hello -t 5

再拍一张测试照片验证清晰度:

libcamera-still -o test.jpg

如果提示找不到设备,先把排线重新插一遍,再看/boot/config.txt是否被别的配置覆盖。

2.3 门铃按键与PIR传感器的接线

这里会用到树莓派4B的GPIO引脚图。引脚编号有两种体系,实际编程时我们用的是BCM编号,不是物理引脚编号,很多新手在这里栽跟头。

接线方案我整理成了下面的对应关系:

模块模块引脚树莓派BCM引脚说明
HC-SR501VCC5V供电(物理引脚2)给模块供电
HC-SR501GNDGND(物理引脚6)公共地
HC-SR501OUTGPIO17(物理引脚11)人体感应输出信号
按键一端任意脚GPIO26(物理引脚37)按键检测
按键另一端任意脚GND(物理引脚39)拉低电平

树莓派GPIO内部默认可以打开上拉电阻,所以按键接线只需要两根线,一端接GPIO26,一端接GND,按下时GPIO26读到的电平从高变低,就能识别到门铃被按下。HC-SR501模块上有两个橙色电位器旋钮,一个是灵敏度调节,一个是信号维持时间调节,实测时把维持时间调到最小那一侧更合适,触发后几秒内就能复位,避免长时间占着IO口。

2.4 散热与供电要点

树莓派4B发热其实不算小,跑OpenCV连续处理视频流时,CPU占用能到一半以上,如果没散热,温度会迅速冲上80度,然后触发降频保护,主频从1.5GHz降到600MHz,本来流畅的画面直接开始掉帧。所以散热风扇不是可选项,而是必选项。主动散热风扇直接接5V和GND就能转,有些三针风扇带测速线,还能把转速信号读出来监控温度曲线。

供电这块更是暗藏玄机。树莓派4B官方要求5V 3A输入,很多用户图便宜用手头杂牌安卓充电器,结果电压纹波大、供电不稳,最典型的现象就是USB摄像头间歇性掉线、WiFi信号突然消失,严重时系统直接重启。我项目初期用的是旧手机充电器,摄像头每半小时断连一次,排查半天才意识到是供电问题。建议直接买品牌的PD电源配USB-C转接线,或者官方树莓派电源,这块钱不能省。

3. 远程查看摄像头的实现

3.1 局域网内快速查看方案

先解决最基础的需求:人在家里,手机上随时能看到门口画面。

最简单的办法是装Motion,一条命令搞定:

sudo apt install motion

然后修改/etc/motion/motion.conf,把stream_port 8081、webcontrol_port 8080和stream_localhost off都写进去,在浏览器访问http://树莓派IP:8081就能看到实时画面流。Motion自带的网页界面做得很直白,内置录像管理和事件列表,作为第一轮验证方案非常顺手。

不过Motion的画质和延迟一般,想追求更干净的自研体验,可以用Flask + OpenCV搭一个不到20行的MJPEG流服务:

from flask import Flask, Response import cv2 app = Flask(__name__) camera = cv2.VideoCapture(0) def generate_frames(): while True: success, frame = camera.read() if not success: break ret, buffer = cv2.imencode('.jpg', frame) if not ret: continue frame_bytes = buffer.tobytes() yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n') @app.route('/video') def video_feed(): return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') app.run(host='0.0.0.0', port=8080, threaded=True)

MJPEG的本质就是连续不断的JPEG帧串,浏览器直接访问/video就能播放,手机电脑通用,不需要装任何播放器。但注意分辨率别设太高,实测在树莓派上640x480、15帧左右是一个甜点值,画面流畅且CPU占用可控;强行推720p 30帧,编码开销会把树莓派压得喘不上气。

3.2 外网远程访问:frp内网穿透是最稳的选择

人在公司或者外地,要随时看门口画面,就涉及从公网访问树莓派的问题。最直白的方案是在路由器上做端口映射,直接把树莓派的8080端口暴露到公网,但这有安全隐患:端口扫描、暴力破解都冲着你的设备来,而且很多家庭宽带有公网IP但80/8080这类常用端口被封。

更推荐的方案是内网穿透,我用的frp。原理很简单:你有一台带公网IP的云服务器当作跳板,树莓派主动和服务器建立一条长连接,外部用户访问服务器某个端口时,流量会被这条连接转发到内网的树莓派。

frp服务端在云服务器上运行,配置示例:

serverAddr = "你的云服务器IP" serverPort = 7000 auth.token = "一段足够长的随机字符串"

frp客户端在树莓派上运行,配置示例:

serverAddr = "你的云服务器IP" serverPort = 7000 auth.token = "上面同一段随机字符串" [[proxies]] name = "webui" type = "tcp" localIP = "127.0.0.1" localPort = 8080 remotePort = 8080

配置对应起来后,外部访问http://云服务器IP:8080就能看到树莓派上的视频流。frp不同大版本的配置格式差异很大,v0.52之后用TOML格式,老版本用INI格式,照网上教程填写时先看一眼版本,别直接复制。

部署完成后还要记得在云服务器的安全组入方向规则里放行7000和8080端口,这个设置遗漏了,服务端怎么都连不上,是内网穿透最常见的翻车点。

3.3 手机端与电脑端实测体验

局域网环境下,用浏览器直接访问MJPEG流延迟很低,从按下手机到画面出现在屏幕上大约0.5秒,基本能接受。公网访问就取决于网络状况了,实测家宽上行带宽30Mbps时,外网看640x480分辨率的画面很稳,720p偶尔起缓冲,1080p几乎不可用。所以远程查看场景下,我的建议是宁愿降低分辨率也要保证流畅性,毕竟门口来人时你不希望画面卡在半空中。

电脑端除了浏览器,还可以用VLC打开流地址。VLC支持的协议很全,MJPEG、RTSP、HTTP都能直接播放。如果想同时实现录像回放,可以在树莓派上把每段事件视频单独保存成文件,Web页面做一个按日期罗列文件链接的简单列表,点击就能播放或下载,体验不比市售门铃自带的云存储App差。

4. 移动侦测:原理与代码实现

4.1 移动侦测到底在干什么

移动侦测的本质不是“看懂画面里有没有人”,而是“发现画面发生了异常变化”。最轻量的算法是帧差法:把当前帧和上一帧对应像素做减法,计算出变化幅度,超过阈值的像素点就是疑似移动区域。这个算法的好处是计算量极小、对硬件要求低,缺点是对光线突变很敏感,阴天过云层、灯突然打开都可能导致大面积像素变化。

更稳一点的是MOG2背景建模算法。它会对每个像素建立高斯混合模型,持续学习“哪些亮度值属于正常背景”,画面中持续停留的背景会被记住,只有前景物体移动时才产生响应。MOG2在室外复杂场景下的误报率明显低于帧差法,但计算量也更大。树莓派4B跑640x480的MOG2勉强能到实时,但CPU占用会高一大截。

两种算法我都在树莓派上实测过,最终选择了帧差法加PIR传感器双重校验的路线。理由很简单:帧差法算得快、省CPU、长期挂机更稳,配合PIR硬件传感器过滤误报,能达到“又简单又够用”的效果。很多商业监控产品在低端机上也用类似策略,不是算法本身有多深奥,而是性能和效果平衡得好。

4.2 OpenCV代码实现

移动侦测核心逻辑的Python实现如下,这段代码可以直接跑:

import cv2 import numpy as np cap = cv2.VideoCapture(0) if not cap.isOpened(): print("摄像头打开失败") exit(1) # 预热第一帧 ret, frame = cap.read() if not ret: exit(1) # 统一缩放到320x240再计算,大幅降低CPU占用 prev_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) prev_gray = cv2.resize(prev_gray, (320, 240)) while True: ret, frame = cap.read() if not ret: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.resize(gray, (320, 240)) # 帧差法 diff = cv2.absdiff(gray, prev_gray) _, thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY) thresh = cv2.dilate(thresh, None, iterations=2) # 找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) motion_detected = False for cnt in contours: area = cv2.contourArea(cnt) if area >= 800: motion_detected = True x, y, w, h = cv2.boundingRect(cnt) # 因为前面缩放了4倍,画框时坐标乘回比例 cv2.rectangle(frame, (x * 4, y * 4), ((x + w) * 4, (y + h) * 4), (0, 255, 0), 2) if motion_detected: print("检测到移动") cv2.imshow("Motion", frame) # 用灰度帧作为下一轮的参考帧 prev_gray = gray if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

这段代码里有两个细节非常关键。一是cv2.resize缩放到320x240再处理,因为逐像素比较在640x480下计算量是320x240的四倍,树莓派上影响帧率很明显;二是画框的坐标要乘回4倍比例,否则检测框的位置会偏到画面左上角,这个坑我调试时浪费了快半小时。使用CSI摄像头模块的版本,过了libcamera-hello测试后还需要额外加载V4L2驱动或者直接用picamera2接帧,后面单独讲;如果是USB摄像头,以上代码开箱即用。

4.3 参数调优与误报抑制

移动侦测调参是有套路可循的,核心参数就这么几个,理解透了就能应对大部分场景:

参数我用的值调大效果调小效果
threshold 像素差异阈值25误报减少,弱运动漏报灵敏度提高,更易误报
dilate 膨胀迭代次数2区域更连续,抗碎点区域容易断裂
contourArea 最小面积800过滤小飞虫,漏掉小物体连猫尾巴摆动都能触发

晚上现场测试会发现,单纯靠帧差法很容易被路过的车辆远光灯、树叶影子闪动触发。解决思路不是拼命调阈值,而是分层过滤:先画一个ROI区域遮罩,只处理门口和门前2米范围内的像素,区域外全部忽略,再用PIR传感器做第二道确认,只有算法的运动检测和PIR检测都成立时才算有效事件。

ROI遮罩实现起来也不复杂:

import numpy as np mask = np.zeros((240, 320), dtype=np.uint8) # 只关心画面下半部分中间区域 polygon = np.array([[80, 240], [80, 100], [240, 100], [240, 240]], np.int32) cv2.fillPoly(mask, [polygon], 255) # 在阈值化之后应用掩码 thresh = cv2.bitwise_and(thresh, thresh, mask=mask)

5. 自动保存视频与事件通知

5.1 触发式录制与编码选择

移动侦测到有人来时,系统自动开始录像,这个逻辑用OpenCV实现很顺手。检测到运动的同时启动一个VideoWriter,持续写入画面,当连续一段时间没有运动就停止保存:

import time import cv2 fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = None recording = False last_motion_time = time.time() motion_timeout = 3 # 运动结束后最多再录3秒 while True: # ... 上面移动侦测的部分,得到 motion_detected 变量 ... if motion_detected: if not recording: timestamp = time.strftime("%Y%m%d_%H%M%S") out = cv2.VideoWriter(f"event_{timestamp}.mp4", fourcc, 20.0, (640, 480)) recording = True print("开始录制") last_motion_time = time.time() if recording: out.write(frame) if time.time() - last_motion_time > motion_timeout: out.release() recording = False print("停止录制") if cv2.waitKey(1) & 0xFF == ord('q'): break if recording and out: out.release() cv2.destroyAllWindows()

编码器的选择上有个讲究。上面用mp4v编码兼容性最好,大部分播放器都能打开,缺点是文件体积偏大,一分钟720p视频约20MB。如果视频保存量大,建议把录制核心外包给libcamera-vid,树莓派硬件编码器直接输出H.264格式的mp4,同样画质体积能降到三分之一,而且编码过程几乎不占CPU。但H.264视频在浏览器里的播放兼容性稍差,需要VLC或支持H.264的播放器,看回放时注意选择。

5.2 存储策略与磁盘循环清理

门铃挂在门口,一天可能触发几十段录像,SD卡再大也会被填满,所以存储策略必须从第一天就设计好。我的目录组织方式是按日期分目录:

/home/pi/events/ ├── 20240812/ │ ├── event_20240812_083012.mp4 │ └── event_20240812_145001.mp4 └── 20240813/ └── event_20240813_073015.mp4

保留策略用crontab定时任务最省心,每天凌晨清理7天前的旧文件:

0 3 * * * find /home/pi/events -type f -name "*.mp4" -mtime +7 -delete

这里有一个很多人吃过亏的教训:树莓派SD卡的写入寿命是有限制的,长期高频写视频会加速坏卡。32GB普通卡在每天几十次短录像的场景下,一年时间就开始出现坏块。对策有两个,一是在系统层面把/home/pi/events目录挂载到外接U盘或移动SSD上,二是购买带A1标识的MicroSD卡,A1是专门针对随机读写性能制定的标准,长期高负载下稳定性明显好于普通卡。有条件的话上外置存储是最稳妥的。

5.3 事件通知:有人按门铃能推到你手机

远程查看是主动行为,但门铃的核心价值是被动通知——你必须知道“此刻门口有人”。我接入了Server酱(微信推送)和Bark(iOS推送)两套方案,原理都是HTTP请求。用Python的requests库发一条POST请求就能推送消息:

import requests def send_notify(title: str, content: str): # Server酱示例 url = "https://sctapi.ftqq.com/<你的SendKey>.send" params = { "title": title, "desp": content, } requests.post(url, data=params, timeout=5) # 示例 send_notify("门口有人", "检测到移动,已保存视频片段,时间:2024-08-12 14:50")

推送的触发条件我做了分层设计。普通移动侦测触发只保存视频,不往手机发推送,避免一天几十条通知把人烦死;只有PIR检测到人体信号时才发推送;如果是门铃按键按下,则发优先级更高的通知,类似“有人按门铃”的专属提示。这套逻辑放回代码里就是在触发录制的同时调用send_notify,非常简单,但用户体验完全不同。

6. 常见问题与排查实录

6.1 系统与TF卡相关问题

树莓派跑起来之后,一类问题集中在系统盘和启动阶段。很多新手刷完系统,把TF卡插进Windows电脑,发现只能看到一个几十MB的boot分区,就以为系统没烧进去。实际上这是正常的,树莓派系统的根分区用的是Linux文件系统,Windows默认读不了,只有macOS和Linux能看到完整分区。想重新写卡或者清空SD卡,用SD Card Formatter工具,它会把整张卡恢复到出厂容量,“TF卡刷了树莓派后怎么删除”这个问题,其实就是用这个工具格式化一遍。

SSH连不上的场景我也遇到过,排查顺序固定三步:先确认树莓派通电后活动灯在闪,再确认路由器后台能看到树莓派的IP,最后确认系统里预置SSH配置的方式正确。新版本树莓派OS在烧录工具的高级设置里勾选“启用SSH”即可,但注意WiFi上网关和网络环境匹配,公司网络下的访客隔离会把树莓派和其他设备隔开,SSH自然连不上。

6.2 摄像头与画质问题

用OV5647 CSI摄像头时,最头疼的故障就是libcamera-hello报no camera available。排插线顺序是先断电再操作,检查排线两端是否完全插入卡座底部,再看卡扣是否压紧,90%的情况都是这两个细节没做好。剩下10%里,有的老版本系统需要手动在/boot/config.txt写入camera_auto_detect=1。

画质问题集中在夜间。OV5647摄像头没有主动红外灯,晚上纯黑环境下画面就是一片噪点。我的做法是在门口天花板上加一个850nm红外补光灯板,成本二十来块,能在完全无可见光的环境下给摄像头提供照明,画面立刻从“什么都看不见”变成“清晰的黑白图像”。注意别选940nm波段的灯,波长太长,很多摄像头的CMOS感光度不够,实测效果很拉胯。

6.3 移动侦测误报排查

移动侦测的误报是这类项目最劝退人的环节,树叶一动就推一条通知到手机,确实会让人气得想拆机。排查误报有个固定思路:不要猜,用画面说话。先在调试窗口把ROI区域画出来、把触发时的当前帧保存成图片,看一眼到底是什么在触发。风大的白天,树枝影子扫过地面就会触发帧差,这时候最小面积阈值直接翻倍,效果立竿见影。

第二个有效的策略是连续帧确认。不要看到一帧有运动就触发,而是连续3帧都检测到运动才判定为有效事件。这个方法对飞虫飞过、车灯闪烁这类瞬时干扰效果极好。最后加PIR传感器确认,人经过才触发,从源头上把背景变化类误报拦在门外。三层过滤下来,误报率能压到一周一两次以内。

7. 写在最后:扩展方向与一点体会

项目跑稳定之后,你会发现这套系统已经是一块很好的积木,扩展空间非常大。接入Home Assistant智能家居平台后,门铃事件可以和灯光联动:夜晚有人靠近时自动打开门口灯;可以和智能锁联动:门开时不及时关门就推送提醒。把YOLO模型部署到树莓派,能进一步区分“人”和“其他物体”,精准识别快递员、同时过滤猫狗。再进一步,把树莓派视频流通过RTSP协议输出,海康和大华的录像机都能当普通网络摄像头直接添加,和现有监控系统无缝整合。

我个人在实际操作中最大的体会是,做这类项目不要一上来追求花哨功能,把最基本的链路跑通才是第一优先级。能看到画面,能检测到人,能自动存视频,能把通知推到手机,这四件事形成了一个完整闭环之后,再慢慢加ROI遮罩、加夜间模式、加AI识别,每一步都清晰可控。这种渐进式做法的好处是,任何一个环节出问题,你都知道问题出在哪一层,而不是面对一个黑盒无从下手。自建门铃省下的那几百块钱倒是小事,真正值钱的是你对自己家里设备完全说了算的那种踏实感。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询