USB 摄像头走的是 UVC 标准驱动,插上就出现 /dev/video0——但它和 CSI 相机是两套互不相通的软件栈:rpicam 命令与 picamera2 都看不见它,拍照用 fswebcam、Python 用 OpenCV。
10-1 到 10-5 全部围绕 CSI 栈(libcamera → rpicam 命令 → picamera2),本节换轨到 V4L2 栈(内核 UVC 驱动 → fswebcam/OpenCV)——前面所有相机命令在这里全部失效,这是本章最后一个、也是最容易栽的大坑。7-2 的【几代库并存】思路在这里再演一次:同一台机器上两类相机各用各的库。
一、两套栈对照:本章的总复习兼分水岭
| CSI 官方模块(10-1~10-5) | USB 摄像头(本节) | |
|---|---|---|
| 接口 | CSI 排线直连 SoC | USB 口(UVC 标准) |
| 设备节点 | libcamera 管理(无 /dev/video0) | /dev/video0 |
| 命令行 | rpicam-hello / rpicam-still… | fswebcam / ffmpeg |
| Python | picamera2 | OpenCV(VideoCapture) |
| 画质参数 | 曝光、白平衡、对焦全可控 | 摄像头给什么用什么,控制少 |
一句话记死:rpicam 和 picamera2 只认 CSI 相机;USB 摄像头在它们眼里不存在。反过来,fswebcam/OpenCV 找 USB 摄像头很顺,但摸不到 CSI 模块的 ISP 管线。网上教程混着写时,先看它用的是哪套栈、再看你插的是哪种相机。
二、即插即用四步
# ① USB 口上认到设备了吗
lsusb
# (输出里出现摄像头厂商字样,如 Logitech、Chicony 等)
# ② 设备节点出现了吗
ls -l /dev/video*
# (典型输出:/dev/video0,可能还有配套的 video1 等附属节点)
# ③ 这颗摄像头支持哪些分辨率与格式
v4l2-ctl --list-formats-ext -d /dev/video0
# ④ 拍一张试试
sudo apt install -y fswebcam
fswebcam -r 1280x720 --no-banner photo.jpg
看到什么算成功:lsusb 有设备、/dev/video0 存在、第 ③ 步列出 YUYV/MJPG 等格式清单、photo.jpg 能打开(--no-banner 去掉 fswebcam 自带的文字横幅)。
读结果:第 ③ 步的清单最重要——OpenCV 里设的分辨率必须是清单里有的,随手写 1920x1080 而摄像头不支持,读到的就是默认值还看不出错。
三、Python 走 OpenCV:三行取流
import cv2
cam = cv2.VideoCapture(0) # 0 = /dev/video0(第一个 USB 摄像头)
cam.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG")) # 指定 MJPG 格式(见第四节)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
ret, frame = cam.read() # 取一帧:ret 是成功与否,frame 是 NumPy 数组
print("一帧尺寸:", frame.shape) # (720, 1280, 3) —— 高在前,同 10-4
cam.release() # 用完释放
与 10-4 的 capture_array() 对照:同样是 NumPy 数组,所以学过的逐帧处理思路原样通用——区别只在【取流的来源】(picamera2 走 CSI、OpenCV 走 V4L2)。进阶提示:若默认后端行为怪异,可显式写 cv2.VideoCapture(0, cv2.CAP_V4L2)。
四、为什么必须 MJPG:USB 2.0 的带宽账
未压缩格式 YUYV 每像素 2 字节。算一笔账(1080p、30 帧):
- 每帧:1920 × 1080 × 2 = 4,147,200 字节
- 每秒:× 30 = 124,416,000 字节 ≈ 995 Mbps
- 而 USB 2.0 总共只有 480 Mbps(实际可用约 300 多)——塞不下
所以摄像头在 1080p 下只肯给你 MJPG(帧内压缩)格式;要跑未压缩格式就得降分辨率或降帧率。这解释了 v4l2-ctl 清单里【高分辨率只有 MJPG】那一栏——不是摄像头吝啬,是总线物理上运不动。
五、什么时候选 USB 摄像头
- 板子根本没有 CSI 口:Pi 400/Pi 500(键盘一体机)——只能 USB
- 要拉远距离:排线几十厘米就到头,USB 线可以好几米
- 要多颗摄像头:每颗占一个
/dev/videoN,插几个有几个(注意 2.0 带宽分摊与供电) - 要热插拔、要快换:排线插拔是精细活,USB 拔了重插就是
代价前面说过:画质参数控制少、走 CPU 的处理多、好摄像头不便宜。固定装机、追画质选 CSI;临时扩展、图省事选 USB。
六、动手 Demo:从认设备到取流一条龙
# ① 插上 USB 摄像头后:
lsusb
ls /dev/video*
v4l2-ctl --list-formats-ext -d /dev/video0 | head -15
# ② 拍一张
fswebcam -r 1280x720 --no-banner usb_photo.jpg
# ③ 连拍三张看帧率手感
for i in 1 2 3; do fswebcam -r 1280x720 --no-banner usb_$i.jpg; done
# ④ OpenCV 取流(保存为 usb_cam.py)
import cv2
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG"))
n = 0
while n < 30:
ret, frame = cam.read()
if ret:
n += 1
print("帧", n, "尺寸", frame.shape)
cam.release()
print("共取到", n, "帧")
python3 usb_cam.py
看到什么算成功:脚本打印 30 次【帧 n 尺寸 (720, 1280, 3)】(或你设的分辨率对应值),结尾报告共取到 30 帧,无报错。
读结果:尺寸是(高, 宽, 3)——10-4 的 shape 规矩在 USB 这边一模一样,因为到了 OpenCV 里大家都是 NumPy 数组,栈不同、终点相同。若 ret 一直是 False:查 ① 的设备节点、查是不是被别的程序占用(同一时刻只能有一个主人,7-2 的老规矩)。
七、30 秒实验:自己算一遍带宽账
你的摄像头在 720p(1280 × 720)、30 帧下跑 YUYV 未压缩格式:
- 每秒多少字节、多少 Mbps?
- USB 2.0(480 Mbps)装得下吗?装不下怎么办?
参考答案:1. 1280 × 720 × 2 × 30 = 55,296,000 字节/秒 ≈ 55.3 MB/秒,× 8 = 约 442 Mbps。2. 理论 480 勉强、实际(协议开销后约 300 多 Mbps)装不下——所以清单里 720p30 也会只列 MJPG,或要求降到 15 帧;办法就是第四节那条:用 MJPG 或降规格。
八、常见误区
- 【用 rpicam-hello 找 USB 摄像头】 —— rpicam 全家只认 CSI;USB 摄像头要用
lsusb//dev/video0/fswebcam/OpenCV 这套 V4L2 栈。 - 【OpenCV 里随便设分辨率】 —— 先
v4l2-ctl --list-formats-ext看支持清单,设清单外的值不报错、只是悄悄用默认值。 - 【第二个摄像头一定是 /dev/video1】 —— 节点号按识别顺序分配,拔插顺序变了会漂;多摄像头项目要按设备名或绑定规则定位,别写死数字。
- 【USB 口都一样,随便插】 —— 高分辨率高帧率优先插 USB 3.0 口(蓝色,Pi 4/5 有);2.0 口跑 1080p 很勉强。
- 【摄像头识别不到先怪摄像头】 —— 先查供电(自动对焦马达一启动电流尖峰,弱电源会掉设备,7-1 的供电纪律同样适用于 USB 外设)。
自测三问(点开对答案)
1. picamera2 能控制 USB 摄像头吗? 2. fswebcam 拍出的照片自带文字横幅,怎么去掉? 3. 1080p30 的 YUYV 是多少 Mbps,说明了什么? **参考答案** 1. **不能**。picamera2 建立在 libcamera 上,只覆盖 CSI 相机;USB 摄像头走 OpenCV 的 VideoCapture。 2. 加 `--no-banner` 参数。 3. 1920 × 1080 × 2 字节 × 30 ≈ 995 Mbps,**远超 USB 2.0 的 480 Mbps**——所以高分辨率只能用 MJPG 压缩格式,这是总线的物理限制,不是设置问题。- ☑ 第十章 完结 —— 教材这一章已读完六节:认清 CSI 相机家族与排线门道、按【免启用】的新方式点亮相机、练熟 rpicam 五件套、用 picamera2 把画面变成变量、搭起 MPEG-TS 串流与 MJPEG 浏览器两条直播路,最后给 USB 摄像头这条 V4L2 支线收口
- ☑ 第十一章 AI 实验范例(一):计算机视觉+AI 识别-OpenCV+YOLO —— 已完结:AI 实验范例:OpenCV+YOLO 物体识别(Ultralytics YOLO11 三行出结果,OpenCV 退居画框勤务)
10-1 认硬件(CSI 家族与排线)→ 10-2 点亮它(自动识别,无需启用)→ 10-3 命令行指挥(rpicam 五件套)→ 10-4 交给程序(picamera2 三步骨架)→ 10-5 送上网(编码-封装-传输三层)→ 10-6 换条轨(USB 的 V4L2 世界)。 树莓派至此有了眼睛:静态照片、延时摄影、网络监控三条实战路线全部打通。 回头看 10-1 埋的那句【两套栈互不相通】:rpicam/picamera2 只认 CSI,fswebcam/OpenCV 只认 USB——选对了栈,一切命令才有意义。 下一章按教材顺序,等波仔报标题。
相关笔记
- 认识树莓派的相机模块 —— 本章起点:CSI 与 USB 两条路线的分野
- 使用 Python 操作相机模块 —— picamera2 的数组世界:本节 OpenCV 数组的对照面
- Python 的 GPIO 模块 —— 【同一时刻只能有一个主人】与【几代库并存】的出处
- 建立与管理 Python 虚拟环境 —— 下一章起点:AI 实验的 venv 地基
- AI 实验范例:OpenCV+YOLO 物体识别 —— 下一章收束:OpenCV 取帧喂给 YOLO 的实战位
- 🍓 树莓派 · 索引 —— 全课路线图