1. 项目概述与硬件选型
大家好,今天我来分享一个实战项目:在RK3588开发板上部署多摄像头YOLOv5实时检测系统。这个项目特别适合需要多路视频分析的场景,比如智能安防、工业质检或者自动驾驶。我选择RK3588是因为它的NPU性能强悍,六核CPU加上三核NPU的架构,能轻松处理多路视频流。
先说说我的硬件配置:我用的是一块鲁班猫5开发板,搭载RK3588芯片,配备了8GB内存。摄像头方面,我接了三个USB摄像头,用的是罗技C920,支持1080P分辨率。这里有个小坑要注意:RK3588自带的USB接口可能不够用,我额外加了一个PCIe转USB扩展卡,推荐绿联的,兼容性不错。
软件环境方面,我的开发机是Ubuntu 22.04,通过SSH连接到开发板。模型用的是YOLOv5s,兼顾速度和精度。整个项目的目标是在三路摄像头同时工作时,每路都能达到25FPS以上的检测速度。
2. 环境搭建与依赖安装
环境搭建是第一步,也是最容易踩坑的地方。我的经验是尽量使用conda管理Python环境,避免版本冲突。先创建专用环境:
conda create -n rk3588_yolov5 python=3.8 -y
conda activate rk3588_yolov5
接着安装RKNN Toolkit2,这是瑞芯微官方的模型转换和推理工具。注意要安装对应版本的whl文件:
pip install rknn_toolkit2-2.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
开发板端需要安装运行时库。先把librknnrt.so和rknn_server传到开发板:
scp librknnrt.so user@192.168.1.100:~
scp rknn_server user@192.168.1.100:~
然后在开发板上设置权限并启动服务:
sudo mv librknnrt.so /usr/lib/
sudo mv rknn_server /usr/bin/
sudo chmod +x /usr/bin/rknn_server
sudo /usr/bin/rknn_server &
OpenCV是必须的,我建议直接用apt安装,省去编译的麻烦:
sudo apt update
sudo apt install python3-opencv libopencv-dev
验证安装是否成功:
python -c "import cv2; print('OpenCV version:', cv2.__version__)"
python -c "from rknn.api import RKNN; print('RKNN import success')"
3. 模型转换与优化
模型转换是关键步骤。YOLOv5的PyTorch模型需要先转ONNX再转RKNN格式。我用的YOLOv5s版本,带Relu激活函数,对NPU更友好。
先导出ONNX模型:
python export.py --weights yolov5s.pt --include onnx --opset 17
转换时注意输入尺寸要固定为640x640,NPU处理固定尺寸效率更高。接下来用RKNN Toolkit2转换:
from rknn.api import RKNN
rknn = RKNN()
rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]],
target_platform='rk3588', quantize=True)
rknn.load_onnx(model='yolov5s_relu.onnx')
rknn.build(do_quantization=True, dataset='./dataset.txt')
rknn.export_rknn('./yolov5s_relu.rknn')
这里有个重要技巧:量化数据集最好用真实拍摄的图片,我用了500张摄像头采集的图片,这样量化后的精度损失更小。如果直接用COCO数据集,实际效果可能会打折扣。
转换完成后,建议在PC端先模拟运行测试:
rknn.init_runtime()
outputs = rknn.inference(inputs=[image])
确保模型输出正常后再部署到开发板。模型输出是三个特征图,需要做后处理解码,这部分后面会详细讲。
4. 多摄像头数据采集优化
多摄像头采集最容易遇到带宽和帧率问题。我刚开始直接用了OpenCV的VideoCapture,发现三路摄像头只能跑到15FPS,远远不够。后来发现是USB总线带宽分配问题。
解决方案是使用多线程采集,每个摄像头独立线程,并且降低分辨率到720P:
import threading
import cv2
class CameraThread(threading.Thread):
def __init__(self, camera_id):
super().__init__()
self.camera_id = camera_id
self.frame = None
self.running = True
def run(self):
cap = cv2.VideoCapture(self.camera_id)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
while self.running:
ret, frame = cap.read()
if ret:
self.frame = frame
cap.release()
还有一个坑是USB摄像头的电源管理。如果同时启动多个摄像头,可能会因为电流不足导致设备掉线。我的是外接供电的USB Hub,确保每个摄像头都能获得足够功率。
图像预处理也要放在采集线程里,减少主线程负担:
def preprocess(frame):
frame = cv2.resize(frame, (640, 640))
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = frame.astype(np.float32) / 255.0
return frame
这样处理下来,三路摄像头都能稳定在30FPS的采集速度。
5. NPU核心绑定与并行推理
RK3588的NPU有三个核心,默认情况下自动分配任务。但为了提高效率,我手动将三个摄像头绑定到不同的NPU核心上。
先查看NPU核心状态:
cat /sys/kernel/debug/rknpu/load
创建三个RKNN实例,分别绑定到不同核心:
rknn_list = []
for i in range(3):
rknn = RKNN()
rknn.load_rknn('yolov5s_relu.rknn')
rknn.init_runtime(core_mask=1<<i) # 每个实例绑定到不同核心
rknn_list.append(rknn)
推理时采用并行处理,每个摄像头使用独立的RKNN实例:
def inference_thread(cam_id, rknn_instance, input_queue, output_queue):
while True:
if not input_queue.empty():
frame = input_queue.get()
outputs = rknn_instance.inference(inputs=[frame])
output_queue.put((cam_id, outputs))
这样每个NPU核心都能满载运行,实测下来比自动分配效率提升30%以上。
内存使用也要优化。RK3588有8GB内存,但NPU有自己的内存空间。我预先分配了输入输出缓冲区:
input_data = np.zeros((1, 640, 640, 3), dtype=np.float32)
output_data = [np.zeros((1, 255, 80, 80), dtype=np.int8),
np.zeros((1, 255, 40, 40), dtype=np.int8),
np.zeros((1, 255, 20, 20), dtype=np.int8)]
重复使用这些缓冲区,避免频繁申请释放内存。
6. 后处理与结果显示
NPU输出的三个特征图需要解码成检测框。后处理算法在CPU上运行,要注意优化效率:
def decode_outputs(outputs, conf_thresh=0.5):
boxes = []
scores = []
class_ids = []
for i, output in enumerate(outputs):
stride = 8 * (2 ** i) # 8, 16, 32
grid_size = output.shape[2]
output = output.reshape((255, grid_size, grid_size))
output = output.transpose(1, 2, 0)
# 解码逻辑
# ...
return boxes, scores, class_ids
我用了C++编写后处理部分,通过cython集成到Python中,速度比纯Python快5倍。
结果显示也要优化。不要每帧都调用imshow,而是用双缓冲机制:
display_buffer = None
def display_thread():
while True:
if display_buffer:
cv2.imshow('Multi-Cam Detection', display_buffer)
cv2.waitKey(1)
对于多路视频,我采用画中画显示,三路视频缩小显示在一个画面中:
def compose_display(frames, detections):
main_frame = frames[0].copy()
small_h, small_w = 240, 320
for i in range(1, 3):
small_frame = cv2.resize(frames[i], (small_w, small_h))
x = (i-1) * small_w
y = main_frame.shape[0] - small_h
main_frame[y:y+small_h, x:x+small_w] = small_frame
# 画检测框
for box in detections[i]:
# 画框逻辑
pass
return main_frame
7. 性能优化与调参实战
经过以上优化,三路摄像头都能达到25FPS。但还可以进一步优化。
首先是模型量化精度。我发现用I8量化比FP16速度更快,但精度略有下降。解决办法是在量化时使用更多样本:
rknn.build(do_quantization=True,
dataset='./quant_dataset.txt',
batch_size=100) # 使用更多样本量化
其次是内存带宽优化。RK3588的内存带宽是瓶颈,所以要减少数据搬运:
# 使用零拷贝
input_data = rknn.get_input_tensor(0)
input_data.set_data(frame)
outputs = rknn.inference(inputs=[], data_format='native')
电源管理也很重要。高性能模式下NPU频率更高,但功耗更大:
echo performance > /sys/devices/platform/fde40000.npu/devfreq/fde40000.npu/governor
我测试过不同模式下的性能:
| 模式 | 功耗 | FPS | 温度 |
|---|---|---|---|
| 均衡模式 | 5W | 20 | 65°C |
| 性能模式 | 8W | 28 | 75°C |
| 极限模式 | 12W | 30 | 85°C |
建议根据实际需求选择模式,长期运行建议用均衡模式。
最后是温度控制。RK3588发热较大,最好加散热片:
# 监控温度
watch -n 1 cat /sys/class/thermal/thermal_zone*/temp
如果温度超过80°C,可以动态调整频率:
echo 800000000 > /sys/devices/platform/fde40000.npu/devfreq/fde40000.npu/max_freq
8. 实际部署与故障排除
部署到实际环境时,还会遇到各种问题。我这里分享几个常见故障的解决方法。
首先是摄像头掉线问题。查看USB设备状态:
lsusb -t
dmesg | grep usb
如果发现设备频繁重连,可能是供电不足,需要外接供电Hub。
其次是内存泄漏。长时间运行后系统变慢,可以用以下命令监控:
watch -n 1 free -h
RKNN Runtime有时会有内存泄漏,定期重启推理进程可以解决。
网络延迟也要注意。如果通过WiFi连接,视频流可能会卡顿。我建议用有线网络,或者降低视频码率。
还有一个常见问题是模型加载失败:
# 检查驱动版本
cat /proc/version
# 检查库版本
ldd /usr/lib/librknnrt.so
驱动和库版本要匹配,否则会加载失败。
最后是性能调优。如果帧率不达标,可以用perf工具分析瓶颈:
perf top -p $(pidof python)
我遇到过CPU后处理成为瓶颈的情况,后来用C++重写后处理部分,性能提升明显。
日志记录也很重要。我添加了详细的日志,方便排查问题:
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.FileHandler('debug.log'),
logging.StreamHandler()])
9. 扩展应用与进阶优化
这个多摄像头系统还可以进一步扩展。比如添加跟踪功能,实现跨摄像头的目标跟踪:
from sort import Sort
trackers = [Sort() for _ in range(3)]
def track_objects(detections, frame):
tracks = trackers[0].update(detections)
for track in tracks:
# 绘制跟踪轨迹
pass
还可以集成TensorRT,在CPU上运行一些辅助模型,比如行为识别:
# 加载行为识别模型
behavior_rknn = RKNN()
behavior_rknn.load_rknn('behavior.rknn')
behavior_rknn.init_runtime()
对于需要更高精度的场景,可以尝试模型蒸馏,用小模型达到大模型的效果:
# 知识蒸馏训练
teacher_model = YOLOv5l()
student_model = YOLOv5s()
# 蒸馏训练过程
# ...
硬件方面也可以扩展。RK3588支持PCIe 3.0,可以接更多设备。我试过接M.2接口的AI加速卡,进一步提升性能。
电源管理可以做得更智能。根据负载动态调整频率:
def adaptive_power_management(fps):
if fps < 20:
set_npu_frequency('performance')
else:
set_npu_frequency('normal')
最后是部署自动化。我写了一个一键部署脚本:
#!/bin/bash
echo "Installing dependencies..."
apt install -y python3-opencv
echo "Deploying models..."
scp models/* user@192.168.1.100:/app/models/
echo "Starting service..."
ssh user@192.168.1.100 "systemctl restart yolov5.service"
这个项目我从零开始搭建,踩过不少坑,最终实现了三路摄像头实时检测。希望我的经验对你有帮助。如果你在实施过程中遇到问题,欢迎交流讨论。
211



被折叠的 条评论
为什么被折叠?



