news 2026/10/31 19:53:07

RK3588部署YOLOv5s实战:服务层接口与摄像头接入全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RK3588部署YOLOv5s实战:服务层接口与摄像头接入全解析

距离上一篇又过了快三周,RK3588 上的 YOLOv5s 部署总算从模型转换一路走到了“可用”的临界点。前面几篇把 RKNN 转换、NPU 推理、后处理都聊透了,模型跑测试图片和离线视频基本稳定。但干这行的人都清楚,离线跑通和实际能用之间还差着十万八千里:别的系统怎么拿到检测结果?摄像头那一路怎么接进来?今天这篇是整个全链路实践的第七篇,重点就两个——服务层和摄像头,外加一个让我连续折腾两天、差点想砸板的坑。

这条实践记录适合正在 RK3588 上做边缘检测盒子、智能摄像头或巡检设备的朋友。如果你已经能把 YOLOv5s 跑起来,但卡在业务集成这一步,这篇的东西应该能直接接上你手里的活儿。我会把服务接口设计、RTSP / USB / MIPI 三种摄像头接入方式、硬解码细节讲透,最后把我踩过的一个大坑完整复盘一遍,包括现象、排查过程和最终解法。

1. 服务层设计:给 YOLOv5s 套一个能用的壳

1.1 先想清楚服务层到底要管什么

很多人在 RK3588 上做完推理,第一反应是写个死循环读摄像头、打印坐标、画框,就算“跑通了”。坦白说,原型阶段没问题,但真要给业务系统用,这远远不够。服务层存在的意义,是把模型推理能力包装成标准的输入输出接口,让前端页面、手机 App、甚至客户那套老旧的 C# 桌面程序都能稳定调用,而不是直接去 import 你的 Python 函数。

我这次的服务层需要管四件事:接收帧和返回检测结果、摄像头会话的创建与销毁、多路视频流的帧分发、以及跟上层业务的状态反馈。如果只做单路 HTTP 接口,/detect返回 JSON 就够了;但要连摄像头做实时流,还得考虑 WebSocket 推送、断线重连、队列缓冲这些逻辑。换句话说,服务层不是简单套一个 Flask 的路由,而是一个能做资源调度的中间层。

拿我自己的场景举例:客户现场部署了一台海康摄像头,业务平台要能定时拿到当前画面里的目标数量和位置。如果服务层只暴露一个输入图片、输出检测框的接口,摄像头一端完全由业务平台自己拉流,那服务层和摄像头没有任何绑定关系,虽然清晰,但很多客户根本不愿意改他们的业务代码。更实用的方案是检测服务主动去拉摄像头流,把结果主动推给平台。这就决定了服务层必须从“被动接口”升级成“主动采集端”。

1.2 FastAPI、Flask 还是 gRPC:在 RK3588 上怎么选

服务层选型我权衡了很久。Flask 最熟,社区例子多,但它的异步支持基本靠多线程硬撑,而 RK3588 这种边缘设备的 CPU 资源本来就不宽裕,每一个线程都是成本。gRPC 性能确实好,适合内部微服务通信,但定义 proto 加生成客户端对很多业务团队来说是有学习成本的。最终我选了 FastAPI,核心原因是它原生支持 async,可以轻量地处理并发,同时自带 WebSocket,非常契合实时推流这种场景。

不过有个容易被忽略的点:FastAPI 是异步框架,但 OpenCV 拉流和 RKNN NPU 推理都是同步阻塞操作。你会发现自己写着async def,结果函数内部执行cap.read()时把整个事件循环堵死了。所以服务层不能天真地“全异步”,我采取的策略是:把摄像头采集、推理这两个重活各自放进独立的进程,FastAPI 进程只负责从队列中拿结果、做接口交互。这个结构在后续章节中会完整展开。

还有一种场景要考虑:如果客户那边有多个服务实例,需要负载均衡,gRPC 更合适。但对 RK3588 单板来说,单机部署、外部调用量一般不大,FastAPI 在uvicorn指挥下足够稳定。我也特意压过测:单进程连续请求/detect接口,每次等待队列里的推理结果返回,吞吐大约每秒 20 次,平均延迟 60 到 80 毫秒。这个表现对绝大多数业务系统来说是够的。

1.3 接口定义与数据格式

接口设计上,我参考了工业界常见的推理服务规范,没有搞花活。三个核心接口:

  • POST /detect:输入图片(Base64 或二进制),输出检测框列表。
  • POST /camera/start:传入摄像头 RTSP 地址,启动一路拉流会话,返回 sessionId。
  • GET /camera/stream/{sessionId}:通过 WebSocket 推送 MJPEG 视频流。

/detect的响应 JSON 大体是这样:

{ "frame_id": 1024, "timestamp": 1712203456, "detections": [ {"class": "person", "bbox": [102, 201, 356, 489], "confidence": 0.87} ] }

为什么用 Base64 而不用 multipart?第一,很多业务方更习惯传 Base64,省得处理二进制流;第二,RK3588 本地传输,Base64 编解码消耗的 CPU 其实就是几毫秒级,完全可接受。但实时推流不能再用 JSON 一帧一封装,WebSocket 上直接传 JPEG 二进制帧,帧头加四个字节的 frame_id,这样带宽和解析成本都最低。

2. 摄像头接入的三种方式:RTSP、UVC、MIPI 我都试了

2.1 RTSP 网络摄像头:主码流、子码流与 RTSP 地址

碰摄像头之前,我一直以为接海康就是填一个rtsp://admin:密码@ip:554/Streaming/Channels/101的事,实际接起来才发现后面隐藏着解码、缓存、码流切换好多个表格。

先说最常见的主码流和子码流。海康威视的 RTSP 地址里,/101一般是主码流,/102是子码流;大华等品牌也类似。主码流分辨率高,比如 1080P 甚至 4K,画质细节多,适合事后检索取证;子码流通常是 704×576 或 640×480,码率低、带宽占用小。做实时检测时,除非业务要求特别高的检测精度且板端算力充足,否则我强烈建议先用子码流。

原因很简单:YOLOv5s 的输入尺寸是 640×640,你直接把 1080P 画面送进去,最终也会缩放,但带宽、解码压力和内存拷贝开销全被放大了。在 RK3588 上,我用海康子码流(704×576)做检测,NPU 推理速度约 28 帧/秒;换到 1080P 主码流后,解码和缩放拖慢到 18 帧/秒,检测结果并不会有质的提升。

RTSP 还有一种容易被忽略的传输选项:TCP 还是 UDP。默认很多播放器用 UDP,延迟低但容易丢包,尤其在 Wi-Fi 或弱网环境下,花屏、卡顿频繁。在我这边,摄像头走网线直连开发板,网络比较稳,但为了排查问题,我最后还是强制指定了rtsp_transport=tcp,用 TCP 换稳定,代价是延迟会增加几十毫秒,对检测场景可以接受。

2.2 USB UVC 摄像头:多摄像头区分的两个容易踩的坑

如果你手头没有网络摄像头,USB 摄像头是最快的替代方案。接入方式也简单,/dev/video0、/dev/video1依次出现,OpenCV 直接VideoCapture(0)就能读。但真到了接两三个同型号 USB 摄像头时,坑马上就来了。

第一个坑是设备索引不稳定。USB 摄像头每次插拔,内核分配的设备节点顺序可能变化,今天video0是 A 摄像头,明天可能变成 B。解决方法不是在代码里写死 0 和 1,而是通过udev规则按 USB 端口或序列号固定设备名,或者启动时读取/sys/class/video4linux/video0/name等属性来识别。

第二个坑是多个摄像头同时打开时的带宽问题。大多数 USB 摄像头走 USB 2.0,如果三个 1080P 摄像头抢带宽,帧率会彼此拖累。我在做双摄像头测试时,调试工具里看到每个摄像头实际只有 8 到 10 帧/秒。后续改成全部请求 640×480 MJPEG 输出,带宽立刻降下来,帧率回到 25 帧左右。在服务层设计里,对 USB 摄像头我只能主动降分辨率,不能迷信它标称的规格。

另外,有朋友问过“C# DirectShow UVC 回调里怎么区分多个摄像头”。这个本质和 Linux 下按设备节点识别一样,DirectShow 的FriendlyName和DevicePath能区分,但在一个回调里同时处理多路信号,最稳妥的是每个摄像头独立一个采集线程,回调函数里用设备对象标识来分离数据流。别试着把多个源混在一个回调里,后期排查会让你崩溃。

2.3 MIPI 摄像头:绑定设备树之后又是一套玩法

RK3588 开发板上除了 USB 和网络,还有 MIPI CSI 接口,常见的有树莓派摄像头 OV5647 和索尼 IMX415 这类传感器。MIPI 摄像头的优势是延迟低、不占 USB 带宽、画质和帧率都比较可控,但劣势是驱动配置麻烦。

我试过在 RK3588 上接 OV5647,需要根据开发板型号修改设备树,把 sensor 的 I2C 地址、CSI 通道、电源引脚都对上。这一步如果没有官方文档,基本靠摸索。一旦配置好,系统里会出现一个 V4L2 节点(通常是/dev/video0),然后通过media-ctl设置分辨率。

实际上,接 MIPI 摄像头还有一个需要考虑的地方:很多 RK3588 的 MIPI sensor 只输出 RAW Bayer 格式或者 YUYV,OpenCV 直接读取时颜色通道顺序可能不对,需要额外做COLOR_YUV2BGR_YUYV转换。我这次的部署主要用网络摄像头,MIPI 只是顺手验证了一下,就不展开细讲了。如果你对 MIPI 驱动配置不熟,优先选 RTSP 或 USB 方案,能省下一大笔时间。

2.4 哪种摄像头适合检测服务

摄像头选型没有银弹,完全取决于应用场景。我把三种方式的权衡整理成一张表:

类型接入难度稳定性延迟多路扩展适合场景
RTSP 网络摄像头低高(有线)中(100-300ms)容易,只需带宽固定点位、多目巡检
USB UVC低中低受 USB 总线和供电限制桌面设备、临时测试
MIPI CSI高高极低受开发板 CSI 路数限制嵌入式冷启动、低时延场景

做最终选择时,我倾向于“有线网络摄像头 + 子码流 + TCP”这套组合。原因很朴素:RK3588 周边的稳定性高于摄像头本身,网络摄像头断了可以自动重连,而且不用考虑 USB 带宽抢占。如果你只有 USB 摄像头,也别灰心,把分辨率降到 VGA 级别,照样能跑检测。

3. 拉流解码细节:把每一帧稳定送到 NPU

3.1 为什么 OpenCV 的 VideoCapture 在 RK3588 上不够用

刚开始接摄像头,我自然用的是cv2.VideoCapture(rtsp_url)。确实,一行代码就能出画面,RK3588 的 CPU 比较强,软解 1080P H264 也不是不能看。但用了两天就发现问题:长期跑下来,解码线程占掉两个 A76 大核,NPU 推理反而抢不到资源,整体帧率越来越低。最夸张的时候,CPU 占用率飙到 80%,而实际有效检测帧率只有 12 帧/秒。

问题在于 OpenCV 的 VideoCapture 在默认编译下走的是 FFmpeg 软解,没有调用 Rockchip 的 VPU。RK3588 自带 V4L2 M2M 和 Rockchip MPP 硬件编解码单元,可以硬解 H264/H265,把 CPU 从解码里解放出来。可 OpenCV 那层封装偏偏不帮你在出厂状态下启用硬解。

还有一个次要问题是缓冲策略。VideoCapture.read()默认会从内部缓冲区取最新帧还是最旧帧,行为在不同 FFmpeg 版本下不一致。在检测场景里,我们不关心每一帧全都要处理,更希望丢掉冗余帧,只拿最近的实时画面。直接可用的方式是给 OpenCV 设置CAP_PROP_BUFFERSIZE=1,但实测下来有些版本根本不生效,这就很头疼。

3.2 PyAV + RK3588 硬件 VPU 解码实战

后来我转向了 PyAV,也就是 FFmpeg 的 Python 绑定。PyAV 的优势是能直接操作解码器、流参数、帧格式,可以更精细地控制硬解流程。前提是你系统的 FFmpeg 编译时带上了 Rockchip MPP 支持。我这边用的是 RK3588 官方 SDK 里自带的 FFmpeg,检查一下有没有h264_rkmpp或者 GStreamer 里有没有mppvideodec插件。

我最终采用的是 GStreamer pipeline 塞给 OpenCV 的方式,实际效果最稳:

import cv2 def create_capture(rtsp_url): gst_pipeline = ( "rtspsrc location={url} latency=0 ! " "rtph264depay ! h264parse ! mppvideodec ! " "videoconvert ! video/x-raw,format=BGR ! " "appsink drop=true sync=false" ).format(url=rtsp_url) cap = cv2.VideoCapture(gst_pipeline, cv2.CAP_GSTREAMER) if not cap.isOpened(): raise RuntimeError("Failed to open RTSP stream with GStreamer") return cap

重点在这几个参数:latency=0,减少 RTSP 内部缓存带来的延迟;appsink drop=true sync=false,让解码后的帧不被阻塞地丢给应用层。这样读取到的基本就是当前最近的一帧,而不是队列里积压的旧帧。实测下来 CPU 占用从 40% 降到 8%,推理帧率稳定在 25 帧以上。

这里也顺便回应了另一个热词:RK3588 的 VPU 详解。VPU 模块同时包含 VEPU(编码)和 VDPU(解码),支持 H264/H265 硬件编解码。如果你只是用VideoCapture软解,等于把 VPU 当摆设,白白浪费芯片能力。后面如果有机会,我再单独写一篇 RK3588 VPU 的使用笔记。

3.3 图像预处理与队列的节奏控制

硬件解码出来的是 BGR 图像,下一步是喂给 RKNN 推理。YOLOv5s 需要 640×640 的 RGB 输入,并且要经过 letterbox 处理保持比例。由于我们用的是子码流 704×576,比例为 1.22,距离 1:1 有点偏差,letterbox 时上下会填充灰边。这些在前面几篇已经详细讲过,这里不重复。

我更想强调的是帧队列的容量控制。多路摄像头场景下,采集端和推理端的节奏并不一致:采集端能到 30 帧,推理端只能消化 25 帧,多余的帧必须先丢,而不是排队等待。我在每个摄像头对应一个queue.Queue(maxsize=2),生产端每抓一帧就put,如果队列满了直接把旧帧丢弃,再把新帧放进去。这样做能保证推理端拿到的永远是最新的检测画面,而不是延迟了好几秒的旧画面。

有些朋友会担心丢帧影响检测连续性。实际上检测任务绝大多数是流式的,单帧漏检影响很小。但如果业务需要每一帧都检测,那就要提高队列容量和推理吞吐,或者减小输入分辨率。鱼与熊掌的取舍,应当在服务层设计时就考虑到位。

4. 折腾最久的坑:FastAPI 线程池 + VideoCapture 死锁

4.1 现象:服务跑一会就整个卡住

爬起来记录一下这个坑。现象是:服务刚启动时一切正常,摄像头画面能出,检测结果也能在页面上实时刷出来。但跑大约十分钟到半小时后,请求响应变得很慢,最后直接超时。重启服务又恢复正常,过一会儿又卡死。

最让我困惑的是,Python 进程的 CPU 占用并不高,甚至有一个核是空的。从日志看,请求进来了,却没人处理。当时的第一反应是内存泄漏,但看了free数据,内存很稳定。于是我把视线放到线程状态上,用py-spy dump看了一眼线程栈,发现大量线程阻塞在VideoCapture.read()里的GstBufferMap上。原来 FastAPI 每次收到摄像头开始请求,都会在线程池里开一个新的拉流循环,OpenCV 内部的 GStreamer pipeline 在多个线程并发调用时发生了死锁。

底层原因大概是 OpenCV 的VideoCapture对象在同一个进程内创建了多个 GStreamer pipeline,而 appsink 的缓冲返回机制在并发场景下会出现竞争,最终导致所有线程都被卡在等待帧数据的信号量上。这个细节很难查,因为表面上看是网络问题、解码问题,实际上是不应该共享的底层资源被并发触发了。

4.2 排查:从摄像头到服务端一层层排除

这个坑排查过程值得复盘一下。一开始我先怀疑摄像头,因为死锁之前画面偶尔有卡顿。我把摄像头单独接到 VLC 里连续播放了一小时,一点问题没有。接着怀疑 RTSP 传输,把 TCP 换成 UDP,画面卡顿变得严重了,但服务不再死锁那么快,只能说不是网络传输的根本原因。

然后我怀疑 OpenCV 的 GStreamer 管道参数不对。单独运行一个采集脚本,单线程死循环读取摄像头,跑了一个多小时也没有问题。到这里基本确定是并发场景触发的,不是采集代码本身的问题。我又尝试给 FastAPI 的接口加上threading.Lock(),让同一时间只有一个摄像头采集线程,结果是死锁频率下降但不稳定,偶尔还是会卡住。最后干脆把所有摄像头采集逻辑从 FastAPI 进程里全部移出去,才彻底解决。

4.3 最终解法:独立采集进程 + 共享队列

最终的架构变成了这样:每个摄像头对应一个独立进程,进程内部用 GStreamer pipeline 拉流解码,只负责把最新帧放到一个共享队列里。FastAPI 进程不再直接创建任何VideoCapture对象,它从队列消费帧,再调 RKNN 推理。

共享队列我用的是multiprocessing.Manager().Queue()还是直接multiprocessing.Queue?考虑到延时要求,我选择了multiprocessing.Queue(maxsize=2),配合独立进程更直接。每一路摄像头一个队列,FastAPI 的 WebSocket 推送循环从对应队列里取最新帧,有就推,没有就跳过,绝不阻塞。

这样写的好处是,即使某个摄像头的 RTSP 断流导致采集进程内卡住,也不会影响其他摄像头的推理服务。FastAPI 进程只做两件事:接收客户端请求、从队列里拿最近的检测结果。进程间的通信成本在本地很低,实测多一路摄像头只增加 2% 到 3% 的 CPU 开销,完全可接受。

4.4 顺手解决的一个隐藏坑:子码流 SPS/PPS 变化

排查死锁的过程中,我还发现了一个隐藏问题:海康摄像头的子码流在场景变化大或网络波动时,会重新发送 SPS/PPS 等编码参数。如果解码器没有正确响应,画面会出现 1 到 2 帧绿屏或花屏。

这个问题在 VLC 里几乎不可见,因为 VLC 的解码器容错能力强,自动重新初始化。但 OpenCV 的某些旧版本 FFmpeg 不会,它会在遇到新 SPS/PPS 时直接丢帧或者输出错误的解码帧。换成 GStreamer 的h264parse之后,它会自动检测流参数变化并通知下游解码器重新配置,绿屏基本消失了。如果你还在用老式VideoCapture直接拉海康子码流,遇到偶发花屏,很大概率就是这个原因。

5. 完整代码骨架:能直接抄的部署示例

5.1 目录结构与核心模块

下面给出一个能跑的简化版本,只保留核心逻辑。我的项目大致是这个结构:

rk_yolo/ ├── config.yaml ├── camera_proc.py # 摄像头独立进程,拉流并放入队列 ├── inference.py # RKNN 推理封装 ├── server.py # FastAPI 服务层入口 ├── detect_session.py # 会话管理,将队列和推理结果绑定 └── utils.py # 工具函数

每个摄像头进程与 FastAPI 进程通过一个圆形的BaseManager注册队列服务来共享连接。更简单的场景,你完全可以用文件或 Redis 来交换帧,但本地单机场景下multiprocessing.Queue是最直接的。

5.2 摄像头采集进程代码

采集进程的核心逻辑如下:

import cv2 def create_capture(rtsp_url): gst_pipeline = ( "rtspsrc location={url} latency=0 ! " "rtph264depay ! h264parse ! mppvideodec ! " "videoconvert ! video/x-raw,format=BGR ! " "appsink drop=true sync=false" ).format(url=rtsp_url) return cv2.VideoCapture(gst_pipeline, cv2.CAP_GSTREAMER) def camera_worker(rtsp_url, frame_queue, stop_event): cap = create_capture(rtsp_url) while not stop_event.is_set(): ok, frame = cap.read() if not ok: # 等待并重连 cap.release() time.sleep(2) cap = create_capture(rtsp_url) continue if frame_queue.full(): try: frame_queue.get_nowait() # 丢掉旧帧 except Exception: pass frame_queue.put(frame) cap.release()

注意frame_queue.full()时的丢帧逻辑。生产者只保留最新一帧,不能让它无脑累积。采集进程的退出由stop_event控制,服务关闭时需要显式把它置位,否则子进程会一直跑着占资源。

5.3 FastAPI 服务端代码

服务端这边,我用了multiprocessing.Manager来创建队列,并注册到远程。为了简化,这里直接展示同一个进程内的队列通信方式,生产环境建议用封装好的BaseManager:

import cv2 import numpy as np from fastapi import FastAPI, WebSocket from multiprocessing import Queue, Process app = FastAPI() frame_queue = Queue(maxsize=2) detect_session = {} @app.post("/camera/start") def start_camera(rtsp_url: str, session_id: str = None): proc = Process(target=camera_worker, args=(rtsp_url, frame_queue)) proc.daemon = True proc.start() detect_session[session_id] = {"proc": proc, "queue": frame_queue} return {"sessionId": session_id, "status": "started"} @app.websocket("/camera/stream/{session_id}") async def ws_stream(websocket: WebSocket, session_id: str): await websocket.accept() while True: frame = frame_queue.get() boxes = inference(frame) # 这里调用 RKNN 推理 jpeg_bytes = cv2.imencode(".jpg", draw_boxes(frame, boxes))[1].tobytes() await websocket.send_bytes(jpeg_bytes)

这个示例去除了鉴权和错误处理,但结构足够说明问题。inference()看起来是同步阻塞,但在 WebSocket 循环里,它会阻塞住当前任务。实际生产环境,我会将推理放到线程池中执行,避免同一路摄像头调用期间其他请求被阻塞。

5.4 性能实测与参数建议

在自己的 RK3588 板子上,我用上面的代码骨架跑了一台海康 2MP 摄像头,子码流 704×576,H.264,TCP 传输,YOLOv5s RKNN int8 量化,实测数据如下:

指标数值
平均推理耗时28 ms
实际检测帧率26 FPS
HTTP 单帧请求延迟65-80 ms
CPU 占用(含采集、服务)约 18%
内存占用约 620 MB

这里 CPU 占用低,很大功劳是 GStreamer pipeline 启用了 MPP 硬解,解码几乎不占 CPU。如果没有硬解,CPU 会跳到 40% 以上,帧率也会显著下降。如果你的摄像头是 4K 主码流,建议先用ffprobe确认实际编码参数,再决定是否在板端解码;检测场景直接用子码流是最理智的选择。

6. 常见问题速查表与我的避坑清单

6.1 摄像头接入类问题

很多人第一次接入摄像头会碰到各种奇怪现象,我把常见问题整理成一张表,按影响程度排了序:

现象常见原因解决办法
无法打开 RTSP 地址地址中密码含特殊字符未转义用urllib.parse.quote对用户名密码编码
画面卡顿、掉帧使用 UDP 传输或带宽不足改用rtsp_transport=tcp,降低码流分辨率
偶发绿屏/花屏子码流 SPS/PPS 变化,解码器未重置使用 GStreamerh264parse+mppvideodec
多路 USB 摄像头索引错乱设备节点不固定通过 udev 规则或/sys信息固定设备名
摄像头断流服务假死VideoCapture.read()长时间阻塞采集独立进程 + 超时重连机制

我印象最深的是 RTSP 地址密码带@符号的情况。客户给的密码里有@,直接拼进 URL 后 OpenCV 总是打开失败。排查了半天才发现是urlparse分不清账号和主机。这个不算技术难题,但非常折磨人。

6.2 服务与并发类问题

服务层的坑比摄像头更隐蔽。下面几个问题都是我在不同板卡上实际遇到过、且有一定共性的:

  • FastAPI 中用异步函数直接放同步的VideoCapture.read(),会让事件循环卡顿。解决方法是把所有阻塞操作丢给独立线程池,但要注意线程池的并发上限。
  • OpenCV 的CAP_PROP_BUFFERSIZE在 GStreamer 后端下经常不生效。如果你需要严格控制缓存,用appsink drop=true比设置属性可靠得多。
  • multiprocessing.Queue传输大帧(比如 1080P BGR 图像)时序列化开销不小。如果延迟要求苛刻,可以使用SharedMemory或者把帧缩到 640 再传。
  • 多个摄像头采集进程中如果共用了同一个 RTSP 地址,摄像头端会因为连接数过多而拒绝新连接。注意复用连接或者控制连接数量。

这里的核心原则是:服务层的每一个组件都必须能独立退出。采集进程要有 stop_event,WebSocket 循环要处理客户端主动断开,否则摄像头连接和队列资源都会慢慢泄漏,跑上一周后系统必然崩溃。

7. 把服务层和摄像头接到一起之后

这次部署过程中,我最大的体会是:RK3588 上的 YOLOv5s,模型推理只是其中一环,真正让系统稳定工作的是服务层设计和底层解码的选型。OpenCV 的VideoCapture不是不能用,但别把它当成万能钥匙。国产芯片平台的优势很大程度体现在 VPU、MPP 这些硬件加速模块上,如果只是按 PC 的老思维去写代码,性能会大打折扣。

那个折腾最久的坑,说到底还是并发资源竞争问题。如果你也遇到“服务跑一段时间就卡死”的现象,先别急着怀疑摄像头或网络,最好查一下是不是所有线程都阻塞在了同一个VideoCapture上。现在这个独立采集进程的架构,我已在两套板卡上连续运行超过 48 小时,未再出现一次卡死。

后续我打算把 C++ 推理服务重写一遍,去掉 Python 的 GIL 对 RKNN 库调用带来的影响。等到做完那步,再写第八篇。如果你也在 RK3588 上部署类似系统,欢迎一起交流具体踩坑经验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 19:50:03

Windows环境下Redis7安装配置与优化指南

1. Redis7在Windows环境下的安装准备Redis作为高性能的键值存储系统,在Windows平台上的安装与Linux环境有显著差异。由于Redis官方并未提供Windows原生支持,我们需要通过第三方移植版本实现。目前最稳定的Windows版Redis由微软维护,最新版本已…

作者头像 李华
网站建设 2026/10/31 19:49:24

OpenDrive高精地图格式解析与应用实践

1. OpenDrive数据格式解析:高精地图的基石第一次接触OpenDrive格式时,我被它严谨的XML结构震撼到了。这种由德国ASAM组织制定的标准,已经成为自动驾驶领域高精地图的事实标准格式。不同于普通导航地图,OpenDrive精确到厘米级的车道…

作者头像 李华
网站建设 2026/10/31 19:47:33

AutoVLA:端到端自动驾驶模型架构与优化实践

1. AutoVLA模型核心架构解析AutoVLA作为当前端到端自动驾驶领域的前沿模型,其创新性架构设计解决了传统模块化方案的多个痛点。这个模型最显著的特点是将视觉感知、语义理解和动作决策统一在一个自回归生成框架中,实现了从传感器输入到控制输出的真正端到…

作者头像 李华
网站建设 2026/10/31 19:47:31

openrig 配置编排:统一管理 Claude Code 与 Codex 的 AI 编码代理环境

1. openrig 到底想解决什么问题第一次看到openrig这个词,我下意识以为是某个硬件机架项目,毕竟 "rig" 在英文里常指设备支架、测试台架。但结合热搜词里那一串claude code、codex、yaml、node.js来看,这明显是一个围绕 AI 编程助手…

作者头像 李华
网站建设 2026/10/31 19:46:05

AI辅助专利撰写的人机协作边界:发明人资格与确权证据管理

最近我在团队里做 AI 辅助专利撰写的流程标准化,真正卡住我们的不是提示词写得不好,也不是大模型输出不稳定,而是一个更底层的问题:技术成果确权的权利根基到底是人的,还是 AI 的?人机协作之后,…

作者头像 李华
网站建设 2026/10/31 19:44:39

基于ViT与LSTM的机械臂视觉模仿学习系统

1. 项目背景与核心挑战在工业自动化和服务机器人领域,机械臂示教动作模仿一直是个经典难题。传统方法需要工程师手动编程每个动作轨迹,耗时费力且缺乏灵活性。我们团队尝试用ViT(Vision Transformer)神经网络构建端到端的模仿学习…

作者头像 李华