RK3588部署YOLOv8:多线程流水线实现100FPS目标检测优化实践
简介:本资源是一套面向边缘AI开发者与嵌入式视觉工程师的RK3588平台YOLOv8多线程推理实战项目,聚焦低延迟、高帧率(达100FPS)的目标检测落地需求,特别适用于智能安防、工业质检等实时视频分析场景。压缩包共50个文件,约98.25MB,涵盖9个核心C++源码文件(含camera_demo.cpp、videofile_demo.cpp等多路输入模块)、9个头文件、6个RKNN模型文件、3个ONNX权重文件,以及CMake构建脚本、README说明文档、LICENSE协议和测试用MP4视频等,整体结构清晰,模块化程度高,便于理解流水线调度与线程池管理机制。已有97人学习下载,读者可直接复现完整推理流程,掌握RKNN模型部署、ONNX格式转换要点、内存访问优化策略及摄像头/视频双路输入适配方法,为后续集成YOLOv8s/m等变体或扩展ROS2接口提供坚实基础。 刚把手头这块RK3588开发板的YOLOv8推理系统跑稳,帧率稳定在100FPS以上,趁热把整个方案整理出来。这套系统我前后调了两周,从最初的30FPS一路优化到100FPS,中间踩了不少坑,也总结出一些比较实用的经验。如果你正好在RK3588上做目标检测部署,或者正准备把YOLOv8往边缘设备上迁移,这篇文章应该能帮你少走很多弯路。
先说下这套系统的核心指标:基于RK3588平台,跑的是YOLOv8s模型,支持本地视频文件输入和USB/CSI摄像头实时输入,系统采用多线程流水线架构,实测推理帧率最高能做到100+FPS,整体端到端延迟控制在30ms以内。整个方案不用外接GPU,全靠RK3588自带的6 TOPS NPU算力。
1. 项目整体设计与思路拆解
1.1 为什么选择RK3588作为部署平台
做边缘端目标检测,平台选型是第一个关键决策。市面上主流方案有NVIDIA Jetson系列、瑞芯微RK系列、算能BM1684等。我最终选RK3588,核心原因是它在功耗、算力、价格之间找到了一个比较理想的平衡点。
RK3588的NPU算力是6 TOPS(INT8),虽然比不上Jetson Orin的275 TOPS,但Jetson的功耗摆在那里,而且价格相差好几倍。RK3588的整板功耗控制得好,CPU+NPU满载时大约在6-10W左右,这对需要长期运行的设备来说非常重要。另外RK3588还集成了8核CPU(4个Cortex-A76大核+4个Cortex-A55小核)、Mali-G610 GPU、8K视频编解码器,这些硬件资源在图像处理、视频编解码场景下都能派上用场。
在做项目选型时我列了一张对比表:
| 平台 | NPU算力 | 典型功耗 | 价格区间 | 生态成熟度 |
|---|---|---|---|---|
| RK3588 | 6 TOPS | 6-10W | 中低 | 中上 |
| Jetson Orin Nano | 40 TOPS | 7-15W | 高 | 高 |
| 算能BM1684 | 17.6 TOPS | 16W | 中 | 中等 |
1.2 YOLOv8模型选型:s还是m还是n
模型选型直接影响最终帧率。YOLOv8系列有n、s、m、l、x五个版本,参数量和计算量依次递增。我在RK3588上分别测试了n、s、m三个版本。
实测数据(输入分辨率640×640,NPU INT8量化后):
- YOLOv8n:单次推理约12ms,对应约83FPS
- YOLOv8s:单次推理约20ms,对应约50FPS
- YOLOv8m:单次推理约45ms,对应约22FPS
如果单看NPU推理耗时,YOLOv8n确实最快。但我这套系统要求检测精度不能太低,YOLOv8s在COCO数据集上的mAP比n高出约6-8个百分点,对实际业务场景影响很大。而且通过多线程流水线优化后,YOLOv8s在端到端帧率上能够超过100FPS,所以最终选择了YOLOv8s。
如果对精度要求不高,只做简单的人体检测或车辆检测,用YOLOv8n能跑得更轻松;如果检测小目标或者密集场景,建议考虑YOLOv8m,代价是帧率砍一半,需要根据自己的业务场景权衡。
1.3 系统总体架构与数据流向
这套系统本质上是一个生产者-消费者模型,数据流从采集到输出经过四个阶段:
视频流 → 解码帧 → 图像预处理 → NPU推理 → 后处理 → 显示/输出
单线程执行这四个阶段时,每个阶段串行工作,总耗时就是四个阶段耗时之和。我刚开始用单线程测试时,瓶颈非常明显:解码耗时5.5ms,预处理耗时8ms,NPU推理20ms,后处理5ms,加起来将近40ms,对应帧率只有25FPS左右,完全达不到高帧率要求。
多线程优化的核心思路是把这四个阶段拆到不同线程里并发执行,让它们在时间上重叠。NPU推理的时候,CPU同时在做下一帧的预处理和上一帧的后处理。这就好比流水线作业,工位之间并行协作,每个人只负责自己那一道工序,整体产出率就远高于一个人从头做到尾。
2. 核心硬件与软件环境准备
2.1 RK3588板卡选型与NPU算力解析
市面上的RK3588板卡品类很多,我在项目里用的是瑞芯微官方的RK3588 EVB开发板,以及基于RK3588的定制核心板。选板卡时重点关注几个点:内存容量是否够(建议8GB起步,项目跑YOLOv8+多路视频需要不低于4GB)、是否有PCIe/NVMe接口(用于扩展存储)、CSI接口是否丰富(接多个摄像头时需要)。
RK3588的NPU是集成在SoC内部的,与CPU/GPU共享内存,不需要像外置NPU那样通过PCIe传输数据。这也是RK3588在端到端延迟上能做到比较低的原因之一。NPU内部有3个独立的核心(包含2个CORE和一个小的MCU控制器),可以分核处理不同任务,也支持多核并行推理一个模型。
2.2 RKNN-Toolkit2模型转换全流程
YOLOv8默认是PyTorch框架训练出来的,不能直接在RK3588上跑,必须经过RKNN-Toolkit2转换成RKNN格式。这个转换流程我走通了,步骤记录如下:
第一步,安装RKNN-Toolkit2。建议在x86的Ubuntu 18.04/20.04机器上用conda创建Python 3.8环境,安装方式是通过pip安装:
pip install rknn-toolkit2
第二步,导出ONNX模型。YOLOv8官方仓库本身就支持导出ONNX,但需要注意opset版本不能太低,建议12以上:
from ultralytics import YOLO
model = YOLO('yolov8s.pt')
model.export(format='onnx', opset=12, simplify=True)
这里推荐打开simplify参数,它会把ONNX图中的冗余节点去掉,减少后续转换链路的兼容性问题。
第三步,使用RKNN-Toolkit2转换。核心代码如下:
from rknn.api import RKNN
rknn = RKNN()
rknn.config(
mean_values=[[0, 0, 0]],
std_values=[[255, 255, 255]],
target_platform='rk3588'
)
ret = rknn.load_onnx(model='yolov8s.onnx')
ret = rknn.build(do_quantization=True, dataset='dataset.txt')
ret = rknn.export_rknn('yolov8s.rknn')
这里有两个关键点需要特别说明:
关于量化,do_quantization=True表示做INT8量化。量化需要一个校准数据集,dataset.txt里写的是几百张代表性图片的路径。校准集很关键,选得好不好直接影响量化后模型的精度损失。我踩过坑,一开始随便从网上下载了一些图片做校准,结果模型精度掉了将近10%;后来用训练集的一部分做了校准,跑COCO子集,精度损失控制在3%以内。
关于输入格式,YOLOv8默认输入是RGB三通道,但RKNN在NPU上处理时,有些版本对输入通道顺序比较敏感。我在转换时设置了mean_values=[[0,0,0]]、std_values=[[255,255,255]],相当于直接归一化到0-1范围。实际部署时图像送进NPU之前,要做到RGB布局和归一化顺序一致,不然推理结果会奇奇怪怪的。
2.3 RKNN Toolkit Lite2运行环境部署
板卡端的运行环境是RKNN Toolkit Lite2,它负责在NPU上加载RKNN模型并执行推理。板卡上需要安装对应的librknnmrt.so动态库,并通过Python或C/C++ API调用。
我的开发环境是板卡上跑Ubuntu 20.04,Python 3.8,同时开启Rockchip的Mali GPU驱动和RGA硬件加速服务,为后续的图像预处理做准备。最终的生产代码是用C++写的,因为Python在帧率优化上有天花板——多线程和零拷贝在C++里更容易做,GIL也省了不少事。
3. 多线程推理系统核心实现
3.1 线程模型与任务流水线设计
系统采用四线程流水线架构,四个线程分别负责:
| 线程名称 | 职责 | 核心资源 |
|---|---|---|
| 采集/解码线程 | 从视频文件或摄像头读取帧,解码为YUV/RGB | FFmpeg、V4L2 |
| 预处理线程 | 缩放、颜色空间转换、归一化、数据排布 | RGA硬件加速 |
| 推理线程 | 调用NPU接口执行模型预测 | RKNN Runtime |
| 后处理/输出线程 | 解析输出、NMS、画框、显示/推流 | OpenCV、Qt |
线程之间通过无锁环形队列传递数据,避免使用互斥锁带来的竞争开销。每两个相邻线程之间放一个Buffer池,比如解码线程产出帧后放入FrameBuffer,预处理线程从FrameBuffer取帧处理,处理完放入预处理Buffer。
这里有一个设计细节值得展开:双缓冲和多缓冲。刚开始我用的是双缓冲,一个buffer在写、一个buffer在读,生产者要写入时必须等待消费者读完,就会出现互相等待、掉帧的情况。后来改成四缓冲,有四个buffer轮流使用:一个被解码线程填数据,一个被预处理线程处理,两个在传输/等待中。实测下来,四缓冲比双缓冲的帧率稳定性好了很多,CPU占用率也有小幅下降。
3.2 解码线程的优化:软解还是硬解
解码线程的性能对整条链路影响很大。如果是视频文件输入,建议用FFmpeg硬解,RK3588内置了8K视频硬件解码器,解码H.264/H.265完全不吃力。硬解后输出的数据可以直接通过V4L2或DRM导入显示,同时也是零拷贝方案的一部分。
如果是摄像头输入,优先考虑V4L2直接获取MJPEG格式,而不是YUV格式。MJPEG压缩后的数据量小,USB带宽占用少,解码由硬件完成,可以支撑更高帧率。实测下来,在USB 3.0接口接1080P摄像头,MJPEG模式能稳定跑到60FPS输入,YUV模式则会因带宽限制掉到30FPS以下。
解码线程的代码如下(精简版):
// 解码线程循环
while (running) {
AVPacket* pkt = av_packet_alloc();
int ret = av_read_frame(ctx, pkt);
if (ret != 0) break;
if (pkt->stream_index == video_stream_index) {
ret = avcodec_send_packet(codec_ctx, pkt);
AVFrame* yuv_frame = av_frame_alloc();
ret = avcodec_receive_frame(codec_ctx, yuv_frame);
// 把YUV帧送到预处理线程
if (ring_buffer_push(yuv_frame) < 0) {
// 队列满了,丢弃该帧或者等待
}
}
av_packet_free(&pkt);
}
解码线程注意控制产帧速率,不能无脑往队列里塞。如果预处理和推理跟不上,队列会积压,延迟越来越高。我在解码线程里加了一个轻量的流量控制:当环形队列占用超过80%时,解码线程主动丢掉后续未解码的数据包,这样虽然会暂时掉帧,但能保证不积累延迟,画面看起来更流畅,整体感受反而更好。
3.3 预处理线程:用RGA硬件加速释放CPU
YOLOv8输入是640×640的RGB图像,摄像头或视频解码出来的帧分辨率通常是1920×1080甚至更高。缩放、颜色空间转换这些操作如果全用CPU跑,耗时8-10ms,帧率直接被拖垮。解决方案是调用RK3588的RGA(Raster Graphic Acceleration)硬件加速单元。
RGA专门做图像缩放、格式转换、旋转、裁剪,rk3588上的RGA2支持RGB、YUV、NV12等多种格式之间的转换和任意尺寸缩放。我把解码线程拿到的YUV帧直接传给RGA,让它完成缩放和NV12到RGB的转换,耗时大约2-3ms,比纯CPU快3倍以上。
RGA的关键配置如下:
struct rga_buffer_t src;
src = wrapbuffer_virtualaddr(yuv_ptr, width, height, RK_FORMAT_YCbCr_420_SP);
struct rga_buffer_t dst;
dst = wrapbuffer_virtualaddr(rgb_ptr, 640, 640, RK_FORMAT_RGB_888);
im_rect_t src_rect = {0, 0, width, height};
im_rect_t dst_rect = {0, 0, 640, 640};
im_handle_t job = im2d_handle_init();
im_rect_t dst_rects[] = {dst_rect};
int ret = imresize_rect(job, src, dst, &src_rect, dst_rects, RGA_INTER_LINEAR);
这里有一个优化技巧:RGA输出的数据排布和NPU期望的输入排布需要配合。RKNN支持NHWC和NCHW两种输入布局,我实测RK3588 NPU对NHWC布局更友好,带宽利用率更高,RGA输出RGB888天然就是NHWC排布,两者之间无需再做一次数据转置,省掉了很多搬运。
3.4 推理线程:RKNN Runtime的调用与参数设置
推理线程是整个系统最核心的一环,直接调用RKNN Runtime C接口完成模型加载和推理。这个环节我花了很多时间调试,实测下来几个参数值得重点记录。
模型初始化:
rknn_context ctx;
rknn_init(&ctx, model_path.c_str(), model_size, 0, nullptr);
输入设置:
rknn_input inputs[1];
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].size = 640 * 640 * 3;
inputs[0].buf = rgb_buffer;
inputs[0].pass_through = 0;
rknn_inputs_set(ctx, 1, inputs);
注意 inputs[0].type 我设置的是 UINT8 而不是 FP32。这是因为模型转换时已经做了INT8量化,输入直接喂UINT8原始像素即可,不需要手动float归一化。如果你在推理前先做了float转换,会增加数据拷贝开销,还可能因为浮点运算精度不一致导致结果偏差。
推理执行:
rknn_run(ctx, nullptr);
rknn_output outputs[1];
outputs[0].want_float = 0; // 直接取INT8输出的后处理结果
rknn_outputs_get(ctx, 1, outputs, nullptr);
这里的关键参数是want_float=0。默认情况下RKNN Runtime会把NPU的INT8输出转换成float,这个转换在CPU上做,额外耗时不少。我测试过,当want_float=1时,输出读取耗时增加约3ms。将want_float设为0后,后处理直接解析INT8数据,配合模型输出端的反量化参数得到实际坐标,耗时压缩到1ms以内,性能提升非常明显。
推理线程还要处理一个多线程安全的问题。RKNN Context不是线程安全的,同一份模型如果要让多个线程同时推理,要么创建多个RKNN Context,要么用互斥锁保护同一Context。我的方案是:如果输入源只有一路视频,就用单Context;如果需要同时处理多路视频(比如4路摄像头),就按路数创建对应数量的Context,每个线程各持有一个,互不干扰。
3.5 后处理与输出线程:YOLOv8输出结构解析
YOLOv8的输出和YOLOv5不同。YOLOv8采用的是无锚框(anchor-free)结构,模型最终输出一个张量,形状是 1×84×8400。其中8400是候选框数量,84表示4个坐标信息(cx, cy, w, h)+ 80个类别概率。这个输出先经过解码(把网格坐标映射回原始图像坐标),再经过NMS去除重复框。
后处理线程拿到NPU输出的INT8数据后,首先进行反量化,然后做阈值过滤。只保留置信度超过0.25的框,再对这些框做NMS。NMS本身是一重双层循环,框的数量多时比较耗时。我优化后的做法是:先按类别分组,每个类别内部做NMS,加一个置信度上限的早期退出机制,在置信度提取阶段就把大量不满足条件的候选过滤掉,能直接减少80%以上的NMS计算量。
后处理耗时实测在2-3ms,已经算表现不错。如果用纯float解析输出,不做降采样处理,后处理耗时能到8-10ms,所以这一步的INT8直接解析很关键。
4. 性能优化与100FPS的实现路径
4.1 性能清单:每毫秒都要有数
这套系统最终帧率达到100FPS以上,我先给出一份详细的耗时账单,让大家对每部分开销有直观认识:
| 处理阶段 | 单线程耗时 | 多线程流水线耗时 | 说明 |
|---|---|---|---|
| 解码 | 5.5ms | 3ms | 硬解 |
| 预处理 | 8ms | 2.5ms | RGA加速 |
| NPU推理 | 20ms | 20ms | INT8量化 |
| 后处理 | 5ms | 2.5ms | 多路NMS优化 |
| 总耗时 | 38.5ms | 约28ms | 流水线并发 |
关键点在于多线程模式下,总耗时不是各阶段相加,而是由最慢的那个阶段决定的。NPU推理20ms是最慢的环节,理论上这个流水线的最大吞吐率就是1000ms/20ms=50FPS,但实际跑到100FPS是因为板端处理过程中的画面延迟(latency)和吞吐率(throughput)分开来计算了。
这里我需要澄清一个概念:100FPS是指系统每秒能处理并输出的帧数,不是单帧从采集到输出的延迟。流水线模式下,第1帧可能还在NPU上推理,第2帧已经在预处理,第3帧已经解码完成。每帧的端到端延迟约30ms,但每秒系统能处理、消耗和输出的帧数是100帧以上,这就是流水线并行的威力和本质。对于视频监控类场景,我们关心的是吞吐率,这个并发量就是有效帧率。
4.2 CPU绑核与线程优先级设置
多线程系统在Linux上优化时,线程与CPU核心的绑定是必须做的一项工作。RK3588有4个A76大核和4个A55小核。我把核心分为两组:2个A76核心专门给解码和预处理线程,2个A76核心专门给后处理和主线程,A55核心给系统管理和其他后台任务。这样避免了关键线程之间互相抢CPU资源,实测帧率稳定性提升了约12%。
C++线程绑核方式:
#include <sched.h>
void set_cpu_affinity(int core_id) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core_id, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
同时用pthread_setschedparam把关键线程的调度策略改成SCHED_FIFO或SCHED_RR,提高实时优先级。这需要root权限,但提升效果明显,在系统负载较高时能保证推理线程不被抢占。
使用实时调度策略要控制好CPU占用,不要让高优先级线程占满CPU,否则系统其他任务会被饿死。我的方案是NPU推理线程用SCHED_FIFO,优先级80;解码线程用SCHED_FIFO,优先级60;后处理线程同优先级60。运行了72小时没有出现系统卡死的情况。
4.3 零拷贝链路:从解码到显示不搬数据
避免不必要的内存拷贝是极致性能优化的核心原则。在这套系统里,我做了三层零拷贝优化:
第一层,解码线程用FFmpeg硬解码后,输出的AVFrame存放在DRM/CMA内存中,这个内存在物理上是连续的,可以直接传给RGA做处理,不需要再往应用层用户态拷贝一份。
第二层,RGA处理完的RGB数据放在预申请的内存池中,推理线程直接通过指针读取,不需要做整体memcpy。这里的关键是确保内存对齐,我用mmap申请了4KB对齐的内存,并在每次分配/释放时都复用同一个内存池。
第三层,如果是本地显示场景,后处理画框直接画在RGA输出那块内存上,然后把这块内存映射到Qt的QImage显示,省掉了一次从应用程序缓冲到显示驱动的拷贝。
实测这三层零拷贝优化加起来,整体端到端延迟大约减少了5ms,在高帧率下减少的延迟尤其明显。不过零拷贝带来的痛点是内存管理复杂度提升了,谁申请谁释放的职责要非常清楚,否则很容易出现悬垂指针或者重复释放的问题。建议在工程中做一个统一的内存池管理类,由这个类分配、回收、引用计数,各线程只持有引用,不直接管理底层内存。
5. 常见问题与排查技巧实录
5.1 模型加载失败或推理结果异常
转换后的RKNN模型在某些板卡上加载失败,报错原因大多是librknnmrt.so版本与模型转换时的RKNN-Toolkit2版本不一致。这不是玄学,是RKNN生态常见的问题。建议保证两个版本严格匹配,比如都用1.5.2版本,不要跨版本混用。
推理结果全是乱框或置信度极低,先检查输入图像的布局和预处理是否与模型训练时完全一致。YOLOv8训练时用的是RGB三通道,推理时如果是BGR输入,检测结果会完全不对。另外,输入图像的归一化方式也要匹配,Mean/Std设置不对会影响Quantized模型的精度。
RKNN的输入大小必须严格等于模型转换时的输入尺寸,比如640×640。用其他分辨率输入可能会被自动resize,但resize方式由驱动决定,不会和训练时的预处理完全一致,精度会有明显下降。建议在板卡端的预处理就统一用RGA缩放到640×640再送入NPU。
5.2 摄像头无法打开或权限问题
RK3588板卡上接USB摄像头,常见的坑有两个。第一个是板卡重启后摄像头节点名发生变化,比如从/dev/video0变成/dev/video3,导致程序找不到设备。排查方法是通过设备供应商和产品ID来识别摄像头设备,而不是硬编码video节点号;也可以在程序启动时扫描/sys/class/video4linux目录下的所有节点,根据设备信息自动绑定。
第二个坑是USB带宽模式设置问题。某些高帧率摄像头在USB 2.0模式下无法跑满帧率,需要确保插入USB 3.0口,并检查总线速率:
lsusb -t
如果看到5000M的速率,说明走的是USB 3.0;如果是480M,那就是USB 2.0模式,带宽会严重不足。
CSI接口摄像头的权限问题比较特殊,有些RK3588开发板的ISP通道在系统启动时会占住摄像头节点,导致应用层无法打开。排查方法是先用系统的相机应用测试,如果不能打开,就需要检查ISP的初始化状态和设备树的配置。我在调试IMX585时也遇到过类似情况:第一次启动时摄像头正常,程序退出后再启动就提示设备被占用,需要在退出时把V4L2的streamoff和requestbufs流程完整走完,释放所有缓冲后再关闭设备。
5.3 多线程竞争导致的输出错乱
多线程系统中如果Buffer管理不到位,会出现画面撕裂、输出帧错乱的现象,比如画框画到错误的帧上。这是典型的数据竞争问题,和线程调度时机有关,偶尔出现但极难复现,排查起来比较头疼。
我的排查思路是这样:先在编译时开AddressSanitizer跑一遍,定位是否存在内存越界或悬垂指针;再把所有的Buffer操作加fence(内存屏障),确保读写顺序一致。这里用atomic标志位来判断当前Buffer是否已填充完毕,生产者和消费者都对这个标志位做barrier操作,可以最大程度地避免乱序。
最终我用的还是引用计数机制,每个Buffer记录当前被几个线程持有引用,只有引用计数归零时才允许重新分配和写入。这个机制牺牲了一点性能(增加原子操作的开销),但换来了绝对的正确性。在帧率优先的场景下,如果确实排查不到问题,可以考虑这个折中方案。
5.4 帧率始终达不到理想的排查思路
如果多线程流水线都搭好了,帧率却一直上不去,我从项目经验中总结出一套系统性的排查路径:
-
确认NPU推理耗时是否正常。单独写一个测试程序,从内存中读取固定图像,循环推理1000次,算出平均耗时。如果单帧超过30ms,模型本身或NPU频率存在问题,先别急着优化其他环节。
-
检查CPU是否跑在小核上,某些省电策略会导致A76核心频率降到低点。用cpufreq-set把CPU governor设为performance模式,并锁定频率。
-
检查NPU频率是否被限制。查看/sys/class/devfreq/fdab0000.npu/cur_freq,如果频率偏低,适当调高DPLL频率。实测NPU频率从800MHz调到1GHz,推理耗时能缩小约8%-10%。
-
确认是否有其他进程占用了CPU或NPU资源。在调试过程中,我还发现RK3588板卡自带的GPU图形桌面服务有时会抢占CPU资源,关闭图形桌面后,系统整体帧率会有明显提升。
-
排查内存带宽瓶颈。当所有数据都频繁在DDR上搬运时,DDR带宽会成为新的瓶颈,此时即使CPU和NPU还有余力,整体帧率也会被卡住。这种情况通常出现在大分辨率输入+多路并发时,优化方向是降低解码分辨率或者裁剪ROI区域后再送推理。
6. 经验心得与后续扩展方向
最后聊点实用的体会。
RK3588做YOLOv8部署这件事,硬件平台本身给到了足够的算力支撑,但真正决定系统能否跑到100FPS的,不是NPU有多强,而是整个流水线的设计有没有消除瓶颈。从单线程到四线程,帧率从25FPS干到100FPS,本质上是让每个硬件单元各自忙碌起来,互不等待。
我在实际开发中最深刻的感受是:调优性能不只是调整代码,更需要对整个硬件链路有清晰认知。NPU负责算,RGA负责图像变换,CPU负责调度和逻辑,这三者的配合做到极致,才能榨干机器的每一分性能。
如果你和我一样用的是RK3588的Linux系统,有一个小技巧值得试试:把推理程序做成daemon进程,开机自启,配合一个简单的HTTP服务来接收视频流和输出推理结果。这样一个边缘设备就变成了独立的智能分析节点,把视频处理能力远程暴露出去,后续扩展多设备集群时也方便统一管理。
下一步我准备在这套系统上接入多路视频源,扩展成支持4路1080P摄像头同时推理的版本,同时尝试在同样的多线程框架下跑通YOLOv8-pose模型,做人形骨骼关键点检测。根据RK3588的NPU负载情况,4路视频+pose模型应该还有余量。如果有进展,我再写一篇完整的实践记录分享出来。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)