摄像头应用
1. 概述
1.1 文档定位
本文档面向使用 RK3588 Linux 6.1 平台的开发者,涵盖 MIPI CSI 摄像头(MIPI CSI-2 是摄像头与处理器之间常用的高速串行接口)和 USB 摄像头(UVC,USB Video Class,符合这个标准的 USB 摄像头免驱即插即用)的接入、调试与应用开发。内容基于 V4L2 框架(Video4Linux2,Linux 内核的标准视频采集接口)和 Rkisp 图像处理流水线(ISP 即 Image Signal Processor,负责把传感器输出的原始数据加工成正常图像),适用于 SHIMETAPI-M5-R2A V1.0 底板配合 C-3588LQ V1.0 核心板的硬件环境。
1.2 RK3588 摄像头子系统架构
┌─────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌───────────┐ ┌──────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ GStreamer │ │ FFmpeg │ │ libcamera │ │ 自定义 App │ │
│ └─────┬─────┘ └────┬─────┘ └─────┬──────┘ └──────┬──────┘ │
│────────┼──────────────┼──────────────┼────────────────┼─────────│
│ V4L2 Framework │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ /dev/video* v4l2-ctl media-ctl libv4l2 │ │
│ └──────────┬───────────────────────────────────────────────┘ │
│─────────────┼───────────────────────────────────────────────────│
│ ┌──────────▼──────────┐ ┌──────────────────────────┐ │
│ │ MIPI CSI-2 │ │ USB (UVC) │ │
│ │ csi2@fe480000 │ │ xhci-hcd / ehci-hcd │ │
│ │ ┌──────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ DPHY (2.5Gbps)│ │ │ │ UVC Driver │ │ │
│ │ └──────┬───────┘ │ │ └────────┬─────────┘ │ │
│ └─────────┼───────────┘ └───────────┼──────────────┘ │
│ ┌─────────▼────────────────────────────────▼──────────────┐ │
│ │ Rkisp (ISP) │ │
│ │ ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ 3A │ │ Demosaic │ │ Gamma │ │ Noise │ │ │
│ │ │ (AE/ │ │ │ │ Correct │ │ Reduction │ │ │
│ │ │ AF/ │ │ │ │ │ │ │ │ │
│ │ │ AWB) │ │ │ │ │ │ │ │ │
│ │ └────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └───────────────────────┬─────────────────────────────────┘ │
│ ┌───────────────────────▼─────────────────────────────────┐ │
│ │ V4L2 Video Device (output) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘1.3 支持的摄像头接口
| 接口类型 | 驱动框架 | 分辨率范围 | 典型帧率 | 说明 |
|---|---|---|---|---|
| MIPI CSI-2 (4-lane) | V4L2 + Rkisp | 最高 8192x4320 | 30fps@4K | 通过 CSI-2 DPHY 接收串行数据 |
| MIPI CSI-2 (2-lane) | V4L2 + Rkisp | 最高 3840x2160 | 60fps@1080p | 适用于低带宽传感器 |
| USB 3.0 UVC | V4L2 + UVC | 最高 4K@30 | 取决于传感器 | 即插即用,免驱动安装 |
| USB 2.0 UVC | V4L2 + UVC | 最高 1080p@30 | 30fps | 带宽受限 |
1.4 适用范围
- SoC:RK3588 / RK3588S
- 核心板:C-3588LQ V1.0
- 底板:SHIMETAPI-M5-R2A V1.0
- 内核:Linux 6.1
- 发行版:Debian 12 / Ubuntu 22.04
2. 硬件准备
2.1 MIPI CSI 摄像头连接
2.1.1 CSI-2 接口定义
RK3588 提供两路 MIPI CSI-2 接口,硬件信息来源于 C-3588LQ V1.0 核心板规格书。
| 信号名称 | 引脚功能 | 说明 |
|---|---|---|
| CSI_CLK_P/N | MIPI 时钟差分对 | DPHY 时钟通道 |
| CSI_DATA0_P/N ~ CSI_DATA3_P/N | MIPI 数据差分对 Lane0-3 | 串行数据通道 |
| CSI_SCL / CSI_SDA | I2C 控制 | 传感器 I2C 总线 |
| CSI_RSTGPIO | 复位 GPIO | 传感器硬件复位 |
| CSI_PWRONGPIO | 电源使能 GPIO | 传感器上电控制 |
2.1.2 硬件连接示意
C-3588LQ V1.0 核心板 SHIMETAPI-M5-R2A V1.0 底板
┌─────────────────┐ ┌─────────────────────┐
│ │ FPC 连接器 │ │
│ MIPI CSI-2 ───┼──────────────┼──► CSI 摄像头座 │
│ (4-lane) │ │ (22Pin FPC) │
│ │ │ │
│ I2C5_SCL ─────┼──────────────┼──► CSI_SCL │
│ I2C5_SDA ─────┼──────────────┼──► CSI_SDA │
│ │ │ │
│ GPIO_3_B1 ────┼──────────────┼──► CSI_RSTGPIO │
│ GPIO_3_B0 ────┼──────────────┼──► CSI_PWRONGPIO │
└─────────────────┘ └─────────────────────┘2.2 USB 摄像头连接
将 UVC 兼容的 USB 摄像头插入 SHIMETAPI-M5-R2A V1.0 底板的 USB 3.0 Host 接口即可。系统会在设备插入后自动加载 UVC 驱动。
2.3 已支持的摄像头型号
| 摄像头型号 | 接口 | 分辨率 | 传感器 |
|---|---|---|---|
| SC3338 | MIPI CSI 4-lane | 2304x1296@30 | SC3338 |
| OV13850 | MIPI CSI 2-lane | 2104x1560@30 | OV13850 |
| Logitech C920 | USB 3.0 | 1920x1080@30 | H.264 编码 |
| Logitech C270 | USB 2.0 | 1280x720@30 | MJPEG |
3. 软件准备
3.1 内核配置
确保内核已启用以下配置项:
# 查看当前内核配置
cat /boot/config-$(uname -r) | grep -E "V4L2|VIDEO|MEDIA|ISP|CAM"必需的内核配置:
CONFIG_VIDEO=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_ROCKCHIP=y
CONFIG_VIDEO_ROCKCHIP_ISP=y
CONFIG_VIDEO_ROCKCHIP_CIF=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_VIDEOBUF2_DMA_CONTIG=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y3.2 用户空间工具安装
sudo apt update
sudo apt install -y \
v4l-utils media-ctl \
libv4l-dev libjpeg-dev libusb-1.0-0-dev \
gstreamer1.0-tools gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly gstreamer1.0-plugins-good \
ffmpeg libcamera-dev libcamera-tools3.3 libcamera 环境确认
libcamera 是 Linux 上另一套摄像头框架,先用它确认设备能被识别:
# 列出 libcamera 识别的设备
libcamera-hello --list-cameras
# 预期输出
# Available cameras
# -----------------
# 0 : imx338 3-001a
# Modes: 'SRGGB10_CSI2P' : 2304x1296 [30.00 fps]3.4 ISP 固件检查
接着确认 ISP 固件文件和 media 设备节点都在:
# 确认 ISP 固件文件存在
ls /lib/firmware/rkisp/
# 应包含 rkisp1.bin 等文件
# 确认 Rkisp 设备节点
ls /dev/media*3.5 环境验证脚本
下面的脚本把本节的检查汇总成一遍跑完,输出正常说明环境就绪:
#!/bin/bash
# camera_env_check.sh — 摄像头环境快速检查
echo "=== RK3588 Camera Environment Check ==="
echo "[1] Kernel: $(uname -r)"
echo "[2] V4L2: $(ls /dev/video* 2>/dev/null || echo 'none')"
echo "[3] Media: $(ls /dev/media* 2>/dev/null || echo 'none')"
echo "[4] USB: $(lsusb | grep -iE 'camera|webcam|uvc' || echo 'none')"
echo "[5] libcamera: $(libcamera-hello --list-cameras 2>&1 | head -3)"
echo "[6] ISP firmware: $(ls /lib/firmware/rkisp/ 2>/dev/null || echo 'missing')"
echo "[7] rkisp module: $(lsmod | grep rkisp || echo 'not loaded')"
echo "=== Done ==="4. 操作步骤
4.1 MIPI CSI 摄像头调试流程
4.1.1 检查设备注册
第一步先确认设备注册情况,注意本机的节点编号和老教程不同:
v4l2-ctl --list-devices
# 输出(本机 rkisp_v6 驱动,节点从 video11 起,不是老教程的 video0):
# rkcif-mipi-lvds2 (platform:rkcif-mipi-lvds2):
# /dev/media0
# rkisp_mainpath (platform:rkisp0-vir0):
# /dev/video11 /dev/video12 /dev/video13 /dev/video14
# /dev/video15 /dev/video16 /dev/video17 /dev/media1重要结论(本机未接摄像头模组):设备树声明了
imx415@1a(I2C feab0000、走 csi2-dphy0),但模组未焊接/未接,内核日志可以证实:dmesg | grep -iE "sensor|rkcif" | head -4 # [ 2.328787] : get_remote_sensor: video pad[0] is null # [ 2.328792] : rkcif_update_sensor_info: stream[0] get remote sensor_sd failed!后果:
--stream-mmap采集报VIDIOC_STREAMON returned -1 (Operation not permitted)(root 也一样,原因是 pipeline 没有 sensor,不是权限问题)。本节采集类命令在接上 IMX415 模组前无法产出帧;枚举/格式查询类命令全部可用,接模组后流程相同。
4.1.2 使用 media-ctl 配置 pipeline
media-ctl 用来查看和配置 media pipeline,也就是 sensor、ISP、各输出节点之间的连接关系:
# 查看 media topology(ISP 在 media1,media0 是 rkcif lvds2)
media-ctl -p -d /dev/media1
# 输出(节选):
# - entity 1: rkisp-isp-subdev (4 pads, 9 links)
# - entity 6: rkisp_mainpath ← 对应 /dev/video11
# - entity 12: rkisp_selfpath
# - entity 18: rkisp_fbcpath
# - entity 24: rkisp_iqtool
# - entity 30/36/42: rkisp_rawrd0_m / rawrd2_s / rawrd1_l
# - entity 48: rkisp-statistics
# - entity 54: rkisp-input-params
# 配置 MIPI CSI pipeline(接模组后;sensor 名以 media-ctl -p 实际输出为准)
media-ctl -d /dev/media1 \
-V "'imx415 3-001a':0 [fmt:SRGGB10_1X10/2304x1296]" \
-V "'rkisp-isp-subdev':0 [fmt:SRGGB10_1X10/2304x1296]" \
-V "'rkisp_mainpath':0 [fmt:NV12/1920x1080]"4.1.3 使用 v4l2-ctl 捕获图像
# 查询支持的格式(/dev/video11)
v4l2-ctl -d /dev/video11 --list-formats-ext
# 输出:
# [0]: 'UYVY' (UYVY 4:2:2) Size: Stepwise 32x32 - 800x600 with step 8/8
# [1]: 'NV16' (Y/UV 4:2:2) Size: Stepwise 32x32 - 800x600
# [2]: 'NV61' (Y/VU 4:2:2)
# [3]: 'NV21' (Y/VU 4:2:0)
# [4]: 'NV12' (Y/UV 4:2:0)
# (Stepwise 上限 800x600 是无 sensor 时的默认约束,接模组后按模组能力变化)
# 查看当前生效格式
v4l2-ctl -d /dev/video11 -V
# Format Video Capture Multiplanar:
# Width/Height : 800/600 Pixel Format: 'NV12'
# Plane 0: Bytes per Line 800, Size Image 720000
# 单帧捕获(接模组后可用;本机 STREAMON 报 EPERM,原因见 4.1.1)
v4l2-ctl -d /dev/video11 \
--set-fmt-video=width=1920,height=1080,pixelformat=NV12 \
--stream-mmap --stream-count=1 \
--stream-to=/tmp/test_frame.nv12
# 转换为可查看格式(ffmpeg 出厂未装,需 sudo apt install -y ffmpeg)
ffmpeg -f rawvideo -pixel_format nv12 -video_size 1920x1080 \
-i /tmp/test_frame.nv12 -frames:v 1 /tmp/test_frame.jpg -y4.1.4 持续录像
连续采集用 --stream-count 控制帧数,录完的裸流再用 ffmpeg 封装成 MP4:
# 录像 10 秒(300 帧)
v4l2-ctl -d /dev/video0 \
--set-fmt-video=width=1920,height=1080,pixelformat=NV12 \
--set-parm=30 \
--stream-mmap --stream-count=300 \
--stream-to=/tmp/capture.nv12
# 转换为 MP4
ffmpeg -f rawvideo -pixel_format nv12 -video_size 1920x1080 \
-framerate 30 -i /tmp/capture.nv12 \
-c:v libx264 -pix_fmt yuv420p /tmp/capture.mp4 -y4.2 USB 摄像头调试流程
4.2.1 识别与格式查询
# 检查 USB 设备(本机只有 root hub,未接 USB 摄像头)
lsusb
# Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
# ...(全部是 hub,无摄像头)
# 插入 USB 摄像头后再看分配的设备节点
v4l2-ctl --list-devices
# 查询 USB 摄像头支持的所有格式(节点名以 list-devices 实际输出为准)
v4l2-ctl -d /dev/video<X> --list-formats-ext本机无 USB 摄像头、
/dev/video4不存在。插入 UVC 摄像头后,内核会自动加载 uvcvideo 并分配节点。
4.2.2 USB 摄像头捕获测试
确认格式后按 MJPEG 抓一帧,产物直接是 JPEG 图片:
# MJPEG 格式捕获
v4l2-ctl -d /dev/video4 \
--set-fmt-video=width=1920,height=1080,pixelformat=MJPG \
--stream-mmap --stream-count=1 \
--stream-to=/tmp/usb_frame.jpg4.3 使用 GStreamer 构建视频流水线
4.3.1 MIPI CSI 预览
GStreamer 把采集、格式转换、显示串成一条流水线,一行命令就能预览:
# Wayland 桌面环境
gst-launch-1.0 v4l2src device=/dev/video0 \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! videoconvert ! autovideosink
# 无桌面环境 DRM 输出
gst-launch-1.0 v4l2src device=/dev/video0 \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! videoconvert ! kmssink4.3.2 H.264 硬件编码录像
# RK3588 硬件编码录制 H.264
gst-launch-1.0 v4l2src device=/dev/video0 \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! videoconvert \
! v4l2h264enc extra-controls="encode,video_bitrate=4000000" \
! h264parse ! matroskamux \
! filesink location=/tmp/camera_recording.mkv
# 同时预览 + 录像(tee 分流)
gst-launch-1.0 v4l2src device=/dev/video0 \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! tee name=t \
t. ! queue ! videoconvert ! autovideosink \
t. ! queue ! videoconvert \
! v4l2h264enc extra-controls="encode,video_bitrate=4000000" \
! h264parse ! matroskamux \
! filesink location=/tmp/recording.mkv编码链路可以不接摄像头验证:用 videotestsrc 生成测试视频,走 MPP 硬编码:
gst-launch-1.0 -e videotestsrc num-buffers=300 \
! video/x-raw,width=1280,height=720,format=NV12 \
! mpph264enc ! h264parse ! qtmux ! filesink location=/var/tmp/test720p.mp4收到 EOS 信号 - 终止管道 ...
Execution ended after 0:00:01.031963172
$ ls -la /var/tmp/test720p.mp4
-rw-r--r-- 1 linaro linaro 4220162 ... test720p.mp4结论:300 帧 720p NV12 用 MPP H.264 编码 1.03 秒完成(约 291 FPS),产物 4.2MB MP4 正常。注意 RK 平台的硬编码元件是
mpph264enc(rockchipmpp 插件,出厂已装),不是通用主线的v4l2h264enc(本机无该元件)。MPP 库版本为mpp_info_test报的 520ab553(2025-12-16)。
4.3.3 MJPEG 推流
# USB 摄像头 MJPEG TCP 推流
gst-launch-1.0 v4l2src device=/dev/video4 \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! jpegenc quality=85 \
! multipartmux boundary="--boundary" \
! tcpserversink host=0.0.0.0 port=8080
# 客户端拉流
ffplay http://<server-ip>:80804.4 使用 libcamera 进行摄像头操作
libcamera 自带的命令行工具更省事,预览、拍照、录像各一条命令:
# 列出设备
libcamera-hello --list-cameras
# 预览(3 秒)
libcamera-hello -t 3000
# 拍照
libcamera-still -o /tmp/photo.jpg --width 2304 --height 1296
# 录像(10 秒 H.264)
libcamera-vid -o /tmp/video.h264 \
--width 1920 --height 1080 \
--framerate 30 --timeout 10000 --codec h2644.5 Python + OpenCV 摄像头应用
OpenCV 的 VideoCapture 直接打开 /dev/video<N>,下面是带 FPS 统计和按键抓拍的完整示例:
#!/usr/bin/env python3
"""camera_capture.py — RK3588 摄像头捕获示例"""
import cv2
import sys
import time
def main():
device = int(sys.argv[1]) if len(sys.argv) > 1 else 0
cap = cv2.VideoCapture(device)
if not cap.isOpened():
print(f"Error: Cannot open camera {device}")
sys.exit(1)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
cap.set(cv2.CAP_PROP_FPS, 30)
print(f"Camera {device}: {int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))}x"
f"{int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))} @ "
f"{cap.get(cv2.CAP_PROP_FPS):.0f}fps")
frame_count = 0
start_time = time.time()
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
if time.time() - start_time >= 1.0:
print(f"FPS: {frame_count:.1f}")
frame_count = 0
start_time = time.time()
cv2.imshow("RK3588 Camera", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('s'):
ts = time.strftime("%Y%m%d_%H%M%S")
cv2.imwrite(f"/tmp/snapshot_{ts}.jpg", frame)
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()5. 代码详解
5.1 V4L2 视频采集流程
V4L2 采集有一套固定套路,下面的流程图配合 5.2 的代码对照着看:
┌─────────────────────────────────────────────────────────────┐
│ V4L2 Video Capture Flow │
├─────────────────────────────────────────────────────────────┤
│ 1. open("/dev/video0") │
│ 2. ioctl(VIDIOC_QUERYCAP) -- 查询设备能力 │
│ 3. ioctl(VIDIOC_S_FMT) -- 设置采集格式 │
│ 4. ioctl(VIDIOC_REQBUFS) -- 申请缓冲区 │
│ 5. ioctl(VIDIOC_QUERYBUF) + mmap() -- 映射缓冲区 │
│ 6. ioctl(VIDIOC_QBUF) -- 缓冲区入队 │
│ 7. ioctl(VIDIOC_STREAMON) -- 开始视频流 │
│ 8. loop: │
│ - ioctl(VIDIOC_DQBUF) -- 取出已填充的缓冲区 │
│ - process(frame_data) -- 处理帧数据 │
│ - ioctl(VIDIOC_QBUF) -- 重新入队 │
│ 9. ioctl(VIDIOC_STREAMOFF) -- 停止视频流 │
│ 10. munmap() + close() -- 释放资源 │
└─────────────────────────────────────────────────────────────┘5.2 MMAP 模式采集示例
/*
* v4l2_capture_mmap.c — RK3588 V4L2 MMAP 模式摄像头采集
* 编译: aarch64-linux-gnu-gcc -o v4l2_capture v4l2_capture_mmap.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#define DEVICE_PATH "/dev/video0"
#define BUFFER_COUNT 4
#define IMG_WIDTH 1920
#define IMG_HEIGHT 1080
struct buffer { void *start; size_t length; };
static int xioctl(int fd, int req, void *arg) {
int r;
do { r = ioctl(fd, req, arg); } while (r == -1 && errno == EINTR);
return r;
}
int main(int argc, char *argv[]) {
int fd;
struct v4l2_capability cap;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
struct buffer *buffers;
unsigned int i;
const char *device = (argc > 1) ? argv[1] : DEVICE_PATH;
/* 打开设备 */
fd = open(device, O_RDWR | O_NONBLOCK);
if (fd < 0) { perror("open"); return 1; }
/* 查询能力 */
if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { perror("QUERYCAP"); goto done; }
printf("Device: %s Driver: %s Bus: %s\n", cap.card, cap.driver, cap.bus_info);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "Device does not support capture/streaming\n");
goto done;
}
/* 设置格式 */
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = IMG_WIDTH;
fmt.fmt.pix.height = IMG_HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { perror("S_FMT"); goto done; }
printf("Format: %dx%d bpl=%d size=%d\n",
fmt.fmt.pix.width, fmt.fmt.pix.height,
fmt.fmt.pix.bytesperline, fmt.fmt.pix.sizeimage);
/* 申请缓冲区 */
memset(&req, 0, sizeof(req));
req.count = BUFFER_COUNT;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(fd, VIDIOC_REQBUFS, &req) < 0) { perror("REQBUFS"); goto done; }
/* 映射缓冲区 */
buffers = calloc(req.count, sizeof(*buffers));
for (i = 0; i < req.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { perror("QUERYBUF"); goto done; }
buffers[i].length = buf.length;
buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, buf.m.offset);
if (buffers[i].start == MAP_FAILED) { perror("mmap"); goto done; }
}
/* 入队并启动流 */
for (i = 0; i < req.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(fd, VIDIOC_QBUF, &buf);
}
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMON, &type);
}
printf("Streaming... capturing one frame\n");
/* 取出一帧并保存 */
{
fd_set rfds; struct timeval tv;
FD_ZERO(&rfds); FD_SET(fd, &rfds);
tv.tv_sec = 5; tv.tv_usec = 0;
if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (xioctl(fd, VIDIOC_DQBUF, &buf) == 0) {
FILE *fp = fopen("/tmp/frame.raw", "wb");
if (fp) {
fwrite(buffers[buf.index].start, 1, buf.bytesused, fp);
fclose(fp);
printf("Saved /tmp/frame.raw (%u bytes)\n", buf.bytesused);
}
xioctl(fd, VIDIOC_QBUF, &buf);
}
}
}
/* 停止流并释放 */
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMOFF, &type);
}
for (i = 0; i < req.count; i++)
if (buffers[i].start != MAP_FAILED)
munmap(buffers[i].start, buffers[i].length);
free(buffers);
done:
close(fd);
return 0;
}5.3 Rkisp ISP 图像处理流程
MIPI CSI-2 Input (RAW Bayer)
│
▼
┌───────────────┐
│ Black Level │ ──► Defect Pixel Correction
│ Correction │
└───────┬───────┘
▼
┌───────────────┐
│ Lens Shading │ ──► Demosaic (Bayer → RGB)
│ Correction │
└───────┬───────┘
▼
┌───────────────┐
│ White Balance│ ──► Color Correction Matrix (CCM)
│ (AWB) │
└───────┬───────┘
▼
┌───────────────┐
│ Auto Exposure│ ──► Gamma Correction
│ (AE) │
└───────┬───────┘
▼
┌───────────────┐
│ Noise Reduc. │ ──► Edge Enhancement
│ (NR) │
└───────┬───────┘
▼
V4L2 Video Output (NV12 / YUV420)RK3588 Rkisp 的 3A(AE/AWB/AF,即自动曝光、自动白平衡、自动对焦)算法通过 camera_engine_rkaiq 库实现。ISP tuning 文件位于 /etc/camera/rkisp/,每个传感器有独立的配置文件。
5.4 libcamera 集成示例
#!/usr/bin/env python3
"""libcamera_integration.py — RK3588 libcamera Python 集成示例"""
import subprocess
import os
class RK3588Camera:
"""RK3588 libcamera 摄像头封装"""
def __init__(self, index=0):
self.index = index
result = subprocess.run(
['libcamera-hello', '--list-cameras'],
capture_output=True, text=True
)
if 'No cameras available' in result.stdout:
raise RuntimeError("No cameras detected")
print(f"Camera {index} ready")
def capture_photo(self, path, w=1920, h=1080):
subprocess.run([
'libcamera-still', '-o', path,
'--width', str(w), '--height', str(h),
'--immediate', '-n'
], check=True)
print(f"Saved: {path} ({os.path.getsize(path)} bytes)")
def record_video(self, path, duration=10, w=1920, h=1080, codec='h264'):
subprocess.run([
'libcamera-vid', '-o', path,
'--width', str(w), '--height', str(h),
'--framerate', '30', '--timeout', str(duration * 1000),
'--codec', codec, '-n'
], check=True)
print(f"Saved: {path} ({os.path.getsize(path)} bytes)")
def test_pipeline(self):
r = subprocess.run(['libcamera-hello', '-t', '1000'],
capture_output=True, text=True)
return r.returncode == 0
if __name__ == "__main__":
cam = RK3588Camera(0)
print("Pipeline test:", "OK" if cam.test_pipeline() else "FAIL")
cam.capture_photo('/tmp/photo.jpg')
cam.record_video('/tmp/video.h264', duration=5)6. 常见问题
6.1 MIPI CSI 摄像头无法识别
按下面的顺序排查:
# 检查内核日志
dmesg | grep -iE "csi|isp|sensor|camera"
# 检查 I2C 总线(CSI 摄像头通常在 I2C5)
i2cdetect -y 5
# 检查驱动模块
lsmod | grep -E "rkisp|rkcif|v4l2"
# 检查 DTS 设备树
find /sys/firmware/devicetree/base/ -name "*csi*"常见原因:FPC 排线未正确插入、传感器供电异常、I2C 地址配置错误、DTS 未配置 CSI 控制器、传感器驱动未编译进内核。
6.2 USB 摄像头无法识别
按下面的顺序排查:
lsusb # 确认 USB 设备枚举
dmesg | tail -30 # 查找 UVC 驱动加载信息
lsmod | grep uvcvideo # 检查驱动
sudo modprobe uvcvideo # 手动加载
dmesg | grep -i "over-current" # 检查供电常见原因:USB 端口供电不足、线缆质量差、UVC 驱动未编译进内核。
6.3 采集图像花屏或色彩异常
按下面的顺序排查:
v4l2-ctl -d /dev/video0 --list-formats-ext # 确认像素格式
dmesg | grep -iE "dphy|lane|clock" # 检查 DPHY
# 降低分辨率测试
v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480常见原因:像素格式不匹配、MIPI DPHY 时钟超限、ISP 未正确配置、数据通道数量不匹配。
6.4 帧率不足
按下面的顺序排查:
# 检查实际帧率
v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=300 \
--stream-to=/dev/null 2>&1 | grep "fps"
top -bn1 | head -5 # 系统负载
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq # CPU 频率常见原因:USB 带宽限制、ISP 处理能力不足、CPU 负载过高。
6.5 libcamera 无法找到设备
v4l2-ctl --list-devices # V4L2 是否正常
LIBCAMERA_LOG_LEVELS=* libcamera-hello --list-cameras # 详细日志
ls /etc/libcamera/ipa/rkisp* # IPA 配置常见原因:libcamera IPA 配置缺失、media 设备节点权限不足、Rkisp 驱动未正确注册。
6.6 GStreamer 管道报错
gst-inspect-1.0 v4l2src # 检查插件
gst-inspect-1.0 | grep -i h264 # 可用编码器
gst-launch-1.0 videotestsrc ! autovideosink # 最简测试
echo $WAYLAND_DISPLAY # Wayland 环境7. 相关文档
| 文档名称 | 说明 |
|---|---|
Rockchip_Developer_Guide_Linux_Software_CN.pdf | ISP 和摄像头驱动开发参考 |
C-3588LQ V1.0 核心板规格书 | CSI 接口和引脚定义 |
SHIMETAPI-M5-R2A 底板原理图 | 底板摄像头连接器电路 |
V4L2 API 文档 (linuxtv.org) | V4L2 内核接口规范 |
libcamera 文档 (libcamera.org) | libcamera 用户空间框架 |
| RK3588 TRM | CSI-2 DPHY 和 ISP 寄存器说明 |
