☰
RK3588双路视觉实时检测:丢旧帧背压方案详解与实战
2026/10/31 6:27:17 网站建设 项目流程

双路视觉听着不难,不就是把一路摄像头变成两路吗?可真正在香橙派RK3588上跑起来,你很快会发现,麻烦的根本不是“接第二路摄像头”,而是“两路帧几乎同时到来,而你的板子上只有一块NPU”。我这次做的是双路视觉方案二的第二阶段,核心就一个词:丢旧帧背压。简单说,处理不过来的帧直接丢弃,让系统永远在追最新帧跑,而不是在一堆积压的老帧后面排长队。

这个思路适合谁?正在香橙派5/5Pro上做双路yolov5s、或者在RK3588上玩多路视频分析的朋友。它会告诉你为什么双路全量处理会越来越慢,以及怎么用“丢旧帧+背压”把延迟按下来。我尽量把代码和原理都拆开讲,可以照抄。

1. 先把问题讲清楚:双路视觉的瓶颈到底在哪

1.1 一块NPU能扛多少活

香橙派RK3588这颗SoC,NPU算力标称6TOPS,常见说法是能跑到“6TOPS@INT8”。听着不错,但6TOPS不是万能的。拿yolov5s来说,输入640x640、INT8量化,在RK3588上单帧推理普遍在20到40毫秒之间,取决于模型版本和部署细节。实测单路1080p30输入,yolov5s基本能跑得飞起,因为NPU再慢,单帧二三十毫秒也小于33毫秒的帧间隔。

注意,这里算的是“纯NPU推理”时间。真实链路里还有图像采集、格式转换、RGA缩放、内存拷贝、后处理NMS、画框显示。这一整条流水线加起来,单帧开销到达四五十毫秒很正常。这也是很多人在单路时没感觉、一到双路直接崩的原因。

1.2 双路不是两倍工作量,是两路同时抢资源

很多人理解双路是“工作量乘以二”,这个理解不算错,但容易忽略一个关键事实:两路摄像头是独立工作的,它们不会因为你忙完一路再给你送下一路。真实情况是,左边来一帧,右边几乎同时来一帧,而你的NPU一次只能推理一个任务。

这就是排队。一路推理完,另一路还在等;等的人越来越多,延迟就像滚雪球一样涨。生物透气实验里有个经典例子:人的大脑可以同时处理视觉和听觉,但如果是两只眼睛同时看两场电影,注意力切换就会有额外开销。NPU没有“注意力切换”这个概念,它就是单纯地排队执行。

更要命的是,排队会导致“帧龄”越来越大。一帧从摄像头采出来到处理完,可能是50毫秒,也可能是300毫秒。对目标检测这种无状态任务来说,老帧的处理结果几乎没有意义——画面里物体早跑出几个身位了,你在后方画个框,控制决策端拿到的是过期情报。

1.3 全量处理的三个典型症状

如果双路都用“不丢帧、全量推理”的朴素方案,我实测下来通常会暴露三个典型症状:

  • 延迟雪崩:帧积压在队列里没人消费,从外界看就是画面突然卡顿,检测框明显滞后于实际物体位置。延迟不是线性增长,而是排队长度越大、每个新帧等得越久,恶性循环。
  • 内存压力:每路采集线程把帧不断push进来,消费者却来不及清空,队列越长内存峰值越高。Python里如果每帧是一个640x640x3的numpy数组,再加上后处理中间结果,积压上百帧直接吃掉几百MB内存。
  • 推理时间反而变慢:系统负载升高后,CPU调度、内存换页、散热降频一起到来,NPU本来稳定的推理耗时也会被拉长。

这三个症状指向同一个结论:双路视觉做实时,关键不是“每一帧都要处理”,而是“处理的那一帧足够新、足够及时”。丢掉旧帧不是偷懒,是保命。

2. 背压与丢旧帧的设计细节

2.1 背压到底是个什么东西

背压不是嵌入式圈的术语,是从流处理和消息队列里借来的概念。下游消费速度跟不上上游生产速度时,下游不硬扛,而是把压力反馈给上游:你慢点、你少推点、或者你丢点。Kafka消费者lag、Netty的writeBufferHighWaterMark,本质都是在处理这种生产与消费速率不匹配的问题。

在双路视觉里,采集线程始终以传感器帧率生产数据,NPU消费能力是固定的。如果不加背压,上下游之间唯一的结局就是队列无限膨胀。加背压的方式有两种:阻塞式和丢弃式。

阻塞式背压,就是队列满时让采集线程阻塞等待。这在数据不能丢、必须保证完整性的场景是对的,但视觉实时任务恰恰最怕阻塞——你让采集线程等,不只是这一帧晚到,整个帧流时序都被打乱,控制周期也乱了。

丢弃式背压,就是队列满时直接丢弃旧帧,生产者永远可以继续写。这是实时视觉里更实用的策略。丢掉一帧检测结果,影响的只是那一瞬间的感知,但整体延迟曲线稳稳保持在预算以内。我在这个方案里用的就是丢弃式背压。

2.2 丢旧帧的两个策略:丢最旧还是丢最新

同样是丢帧,方向可以完全不同。这是我最开始踩过坑的地方。

  • 丢最旧(丢弃队首):队列满了,先把最老的一帧清掉,来一帧新的,就用新帧占老帧的槽位。这是“实时优先”,始终保留最新信息。
  • 拒绝最新(丢弃队尾):队列满了,新的这一帧直接扔掉,老帧继续排队处理。这是“完整性优先”,保证已经接收的帧都被处理完。

双路视觉做实时目标检测,我只推荐丢最旧。原因很简单:检测任务是单帧无状态的,每一帧的处理结果独立,没有“必须按顺序处理完所有中间帧”的要求。老帧越积越久,就算处理了,结果也没有实时价值。反而是把最旧帧扔掉,让NPU永远在处理尽量新的画面,输出才跟得上现实变化。

有些读者会问:那如果我想让画面输出也是连续的怎么办?这就得看下游是不是接跟踪器,比如ByteTrack、DeepSORT。跟踪器确实依赖连续帧,丢帧可能导致ID断裂。这个问题我在后面专门讲,这里先记住:如果只做纯检测,丢最旧是正确答案。

2.3 缓冲结构设计:从最新槽到环形队列

丢旧帧的落地实现,可以有三种缓冲结构,从简单到复杂依次是:

第一种叫“单帧最新槽”。每路摄像头只保存“当前最新帧”。新帧到达,直接覆盖旧帧。消费者每次取走的就是“此刻最新的一帧”。这个方案实现最简单,延迟也最低,缺点是容错差——如果消费者在处理上一帧时新帧丢了,就真的丢了。

第二种叫“容量为N的环形队列”。每路维护一个深度为N(通常N取2或3)的环形缓冲,消费者每次从里面取最新帧。新帧到来时如果队列已满,先把最旧的槽清掉再写。这是我在双路方案里的主力设计。

第三种叫“有界无锁队列”。用collections.deque(maxlen=N)或者disruptor那套无锁设计,原理类似,只是对不同语言和并发模型有适配。

缓冲深度的选择,其实是在“延迟”和“容错”之间做权衡。深度越大,消费者偶尔卡一下时,新帧还能保住;但深度越大也意味着每一帧的平均等待时间可能更长。实测下来,深度2是延迟和容错比较均衡的点。深度1延迟最漂亮,但消费者只要调度抖动一次,就要丢好几帧。深度大于3,几乎就不算丢帧方案了,等于半路排队。

2.4 双路消费路由:怎么分配才能不饿死

丢帧队列解决的是“每路内部”的堆积问题,但两路同时有帧等处理时,还有一个路由问题:NPU先处理左边还是右边?

最简单的策略是“按帧年龄仲裁”。每路槽位里的帧都记录一个入队时间戳,消费者每次取等待时间更长的那路帧。这个策略有公平性保证,不会出现某一路因为时间戳更晚而总被忽略,也就是不会饿死。其本质是在降“最大延迟”——优先处理最老的那帧,可以控制整体时间曲线。

另一种策略是“按业务优先级”。比如左边是主视角、右边是侧视角,你希望主视角延迟很低,就可以在主视角帧的等待时间上加一个权值。effective_age = real_age + priority_bonus,谁大处理谁。这个适合场景权重明确的控制系统。

还有一种更保守的“时间预算丢帧”。不只是队列满了才丢,而是当某路帧在槽位里等待超过阈值(比如100毫秒)时,消费者干脆不推理它,直接丢弃,改去处理其他路的帧。这种做法把丢帧从“空间不够”扩展到“时间不够”,是我在阶段二里最推荐的双保险机制。

3. RK3588从采集到推理的完整链路

3.1 硬件准备与摄像头配置

我在香橙派5Pro上搭的这套环境:双路MIPI CSI接口,各接一个IMX219 1080p摄像头模组;系统用官方镜像;散热片是必须的,双路推理时NPU长时间满载,CPU也有负载,温度一旦顶到85度以上就会降频,这是延迟曲线变丑的隐形元凶。

如果你是接USB摄像头,也能跑这套逻辑,但要多做一步格式转换。MIPI CSI的好处是走硬件ISP链路,输出可以直接喂给V4L2,格式可控、延迟更低、CPU占用小。RK3588的多路ISP可以同时处理两路信号,这也是我坚持用CSI的原因。接线时要留意FPC排线的方向,我遇到过排线反插导致图像色彩通道错乱的情况,看输出图像一片发绿,排查半天才反应过来是排线问题。

系统层配置主要关注两个东西:一是设备树/Overlay里要把两路摄像头都启用,二是确认两路各自有独立的/dev/video0和/dev/video1节点。检查命令很简单:

v4l2-ctl --list-devices

能看到两个sensor节点、两个ISP主节点,链路就是通的。

3.2 模型转换:yolov5s ONNX转RKNN

这部分是RK3588部署yolov5s的标准流程,我简单过一遍,重点提几个坑。

先在PC端或板端把yolov5s.pt导出成ONNX:

python export.py --weights yolov5s.pt --img-size 640 --include onnx

导出时注意选对opset版本,RKNN工具链对高版本opset的兼容性不一定好,我一般用opset 11到13之间。

然后用rknn-toolkit2转rknn格式。下面是一个最小转换脚本的关键部分:

from rknn.api import RKNN rknn = RKNN() rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588') rknn.load_onnx(model='yolov5s.onnx') rknn.build(do_quantization=True, dataset='dataset.txt') rknn.export_rknn('yolov5s.rknn')

do_quantization=True用的是INT8量化,这一步非常关键。如果不开量化,yolov5s在RK3588上跑FP16,但RKNN工具链对FP16的支持和推理速度往往不如INT8;开了量化,模型体积砍到四分之一,推理速度明显提升。量化需要一个校准数据集,dataset.txt里每一行是一个图片路径,一般准备几十到几百张目标检测相关图片就够了,不需要真实标签。

转完之后可以在板端用rknn-toolkit-lite加载验证:

python test.py --model yolov5s.rknn --input test.jpg

确认输出shape正常,再进下一步。

3.3 采集线程与内存复用

真正的双路实时采集,我不会建议直接cv2.VideoCapture(0)和cv2.VideoCapture(1)这两个默认通道拉流。用OpenCV方便,但问题很多:每个通道拉流会在Python层产生额外拷贝,帧时间戳拿不到硬件时钟,而且OpenCV内部缓冲策略不透明,可能本身就积压旧帧。

更可控的做法是直接用V4L2接口读取,或者用一个库封装V4L2,把每一帧的buffer从内核态导出到用户态。在RK3588上配合RKNN的zero-copy接口,可以做到输入张量直接指向摄像头buffer,跳过memcpy,这能省掉10到20毫秒的拷贝时间。zero-copy配置方式在rknn-toolkit2里有现成的说明,关键是把输入用np.zeros(shape, dtype=np.uint8)在初始化时预分配,推理时复用同一块内存,不要每帧都np.array()。

内存复用还有一个好处,就是减少内存分配带来的抖动。Python里频繁分配大数组,GC一触发,推理线程可能被卡几十毫秒,这就糟了。

3.4 核心代码:丢帧队列加背压消费循环

这里给出一个可以直接用的最小实现。每路一个最新槽(或容量N环形队列),两个采集线程往各自的槽位写,一个NPU消费线程按“等待时间优先”取帧推理。

先写单通道的丢帧缓冲类:

import threading import time class LatestBuffer: """单通道最新帧缓冲:容量为1,新帧直接覆盖旧帧,旧帧即为丢弃帧""" def __init__(self): self._lock = threading.Lock() self._frame = None self._ts = 0.0 def push(self, frame, ts): with self._lock: self._frame = frame self._ts = ts def get(self): with self._lock: frame, ts = self._frame, self._ts self._frame = None return frame, ts def age(self, now): with self._lock: if self._frame is None: return None return now - self._ts

如果想把容量扩展成N,可以用一个带head/tail的环形队列,但核心比较逻辑不变。消费者线程如下:

def nps_consumer(slot_left, slot_right, rknn_model, delay_budget=0.1): while True: now = time.monotonic() age_l = slot_left.age(now) age_r = slot_right.age(now) # 如果某路帧等待超过预算,直接丢弃,不送入NPU if age_l is not None and age_l > delay_budget: slot_left.get() age_l = None if age_r is not None and age_r > delay_budget: slot_right.get() age_r = None # 仲裁:优先处理等待更久的那路 if age_l is None and age_r is None: time.sleep(0.001) continue if age_l is None: frame, ts = slot_right.get() elif age_r is None: frame, ts = slot_left.get() else: if age_l >= age_r: frame, ts = slot_left.get() else: frame, ts = slot_right.get() # NPU推理 outputs = rknn_model.inference(inputs=[frame]) # 后处理部分在这里,比如解码、NMS、画框

这段代码把设计思想体现得很完整:

  • 每路槽位满了直接覆盖,生产者永远不阻塞,这是丢弃式背压的落地。
  • 消费者在取帧前先做“时间预算检查”,超过delay_budget的帧直接扔掉。
  • 双路仲裁用“等待最久优先”,不会饿死任何一路。

实际工程中你可能还要加一个“没取到帧时短暂sleep”的兜底,防止消费线程在空槽位上死循环空转。Python里sleep 1毫秒是够用的,因为帧率一般在30fps左右,1毫秒的空转可以接受。

4. 实测数据与调优记录

4.1 我在香橙派5Pro上的测试环境

我测试时用的配置是:RK3588、双路IMX219 1080p30输入、yolov5s 640x640 INT8 RKNN、后处理在CPU上做、系统为Ubuntu镜像。我在相同环境下对比了三种方案:

  • 方案A:双路全量处理,不装任何丢帧逻辑,采集线程无限往队列里塞。
  • 方案B:双路各自带最新槽,消费者按等待时间取帧,没有时间预算。
  • 方案C:双路最新槽加时间预算丢帧,预算100毫秒。

我测出的典型数据如下(受散热、CPU调度影响,每次会有一两帧波动):

指标方案A方案B方案C
左路平均延迟280ms+68ms66ms
右路平均延迟290ms+72ms70ms
尾部延迟近400ms140ms120ms
队列积压帧数持续增长,最终超过100帧稳定在1~2帧稳定在1帧
内存占用1.5GB+900MB900MB
左右路每路有效检测FPS约8约13约14

方案A的延迟数字我都不想多看,从采集到出框,最后画面上的检测框明显是“两三百毫秒以前的过去”。方案B把延迟压到了70毫秒左右,方案C在此基础上进一步把尾部延迟降下来,而且有效FPS更高一点。

4.2 为什么丢帧不降吞吐

很多人有个直觉误区:丢帧,那检测吞吐不是变低了吗?这要区分“有效检测率”和“推理吞吐”。NPU每秒能推理的帧数是固定的,丢帧并不会让NPU变快,真正提升的是“结果的新鲜度”。

方案A里,NPU确实在满负荷工作,但处理的帧里有一大半是“过期帧”。这些推理请求浪费在积压队列里,最后的有效输出反而低。方案C里,NPU也在满负荷工作,但它处理的每一帧都是实时帧,检测框位置基本跟得上物体运动。

所以丢帧做的事情是“把资源省下来给更新的帧”,而不是“减少工作量”。这是一个取舍:牺牲次要帧的检测机会,换取主要帧的端到端延迟。在实时控制场景里,延迟比帧率值钱得多。

4.3 容量参数和时间预算怎么定

环形缓冲深度和时间预算这两个参数,是最需要根据场景调的两个旋钮。

如果下游是实时控制(比如机器人避障、无人机感知),我建议深度选1到2,时间预算30到80毫秒。控制环路对延迟极敏感,宁愿丢帧也不要让控制器拿到过期数据。

如果下游是人眼看监控或者慢速采集记录,你可以放宽到深度3、时间预算200毫秒。丢帧少一些,画面更连贯,因为人对几十毫秒的延迟并不敏感。

这里有三个调参误区避免踩:

  • 不要把时间预算设成NPU最坏推理时间,要留出后处理和调度抖动的余量。
  • 不要把深度设得很大还挂“实时”的牌子。深度就是延迟的预算,深度越深延迟下限越高。
  • 上路前一定要做“帧迟到测试”。人为把某一路帧率降到10fps,看看另一路会不会受到影响。好的背压方案应该互不干扰。

4.4 双路帧率不同步的情况

我最初踩过一个坑:两路摄像头帧率虽然都是30fps,但实际到达时间不可能精确对齐,可能错开几毫秒甚至几十毫秒。如果我用“两路时间戳必须配对”的思路,结果就是满屏丢帧——因为几乎找不到时间戳完全相同的两帧。

正确做法是让两路独立成两条流水线,各自维护自己的缓冲和时间戳,NPU按仲裁策略去取,不做配对。配对逻辑只在某些特殊需要同步输出(比如双目深度估计)的场景才有意义。yolov5s单路检测不依赖立体视觉,配对属于画蛇添足。

5. 常见问题与排查实录

5.1 画面花屏、色彩错乱、时间戳乱跳

这在我调试双MIPI时遇到最多。第一次接好两路摄像头,右路画面色彩是绿的,左路正常。我开始怀疑是摄像头坏了,换了模组也一样。最后发现是FPC排线插反了。MIPI排线正反插都能插进去,但信号通道就错乱。你把排线金手指方向对准卡扣,重新插一遍就能解决。

花屏多半是排线接触不良或者时钟信号不匹配,可以试试降低分辨率到720p,如果720p稳定而1080p偶尔花屏,大概率是MIPI链路余量不足。还有一种可能是两路分辨率不一致,RK3588的ISP允许不同分辨率,但如果V4L2配置没对上,输出就会乱。每次改完分辨率,都重启一下摄像头应用再确认。

5.2 双路帧有没有必要做硬件时间戳对齐

做丢帧背压时,我们的判定依赖帧的年龄,那就需要靠谱的时间戳来源。我在实际编码里用的是time.monotonic(),它比time.time()可靠——系统时间可能被NTP校准回拨,但单调时钟不会。V4L2读出的v4l2_buffer.timestamp更好,它是内核采帧的硬件时钟。

如果你的摄像头buffer本身有帧序号,把帧序号和时间戳一起保存,比只用时间戳更有利排查问题。我一般会打日志,记录“哪一路、第几帧、何时入队、何时被取走、何时输出”。回放日志时能非常清楚地看到丢帧发生在哪个环节。

5.3 并发推理:千万不要开多个线程同时调NPU

这是RK3588部署时非常常见的翻车现场。RKNNAPI本身不是线程安全的“随便并发也无所谓”,多个线程同时调用inference,要么返回错误,要么性能不升反降。NPU是一个资源,不是多核CPU,你开八个线程也抢不来第二块NPU。

正确做法是:所有推理走单线程,或者用一组串行任务队列把所有推理请求排队。采集线程负责把帧塞进缓冲,消费线程统一调RKNN。即使你后续想加第三路摄像头,也是往同一个推理队列里塞请求,让背压机制决定谁该被处理。

5.4 内存持续上涨,跑几分钟就崩

如果你看到内存曲线稳步上升,别急着怀疑丢帧逻辑,先检查是不是每帧都申请了新数组。Python下常见错误是:

frame = np.frombuffer(buffer, dtype=np.uint8).reshape(...)

这块buffer每次都在变,你如果不深拷贝,后处理时可能数据已被覆盖;如果深拷贝,又是一份新内存。解决办法是预分配一个输入张量,采集线程往预分配内存里拷贝数据,推理线程只读这块内存。RKNN的zero-copy模式能进一步省掉一次拷贝。我在RK3588上实测过,纯Python全拷贝时内存轻松超过1GB,改成预分配后稳定在900MB左右。

5.5 量化后精度崩了,检测不到物体

yolov5s转INT8量化后,置信度普遍会掉一点,但如果掉到检测不到物体,说明校准数据集和实际场景差距太大。我一开始用几张纯风景图做校准,部署到室内后基本什么都检测不到。后来换成了几十张包含人、车、目标的实际场景图,精度就恢复正常了。

这不算模型bug,是量化校准的典型陷阱。校准图片要尽量覆盖目标类别、光照范围、目标尺寸分布。目标太小的话,INT8量化很容易把关键特征丢掉。

5.6 可视化画框导致帧率崩溃

如果你把显示逻辑和推理逻辑放在同一个线程,画面卡顿几乎是必现的。窗口渲染、GPU/FrameBuffer刷新都可能阻塞主流程。处理方法很简单:开一个独立的显示线程,每隔几帧(比如每5帧)从结果队列取一次最新的检测结果来画,画完直接显示。显示线程宁可丢显示帧,也不要阻塞推理。这与丢旧帧背压是同一个哲学:下游慢,就丢掉下游跟不上的一环。

6. 几个容易想歪的点

6.1 丢旧帧不等于降低检测能力

可能有人觉得,丢了那么多帧,很多目标是不是就漏检了?不是一回事。丢帧丢的是“处理不过来”的帧,并不是“检测不出来”。如果NPU每秒只能推理15帧,而两路输入共60帧,那无论如何那45帧的处理机会都没有。丢帧方案只是明确告诉系统:把有限的推理机会给最新的帧,而不是浪费在排队老帧上。

真正提高检测能力的方法,是换轻量模型(yolov5s换成yolov5n)、降低输入分辨率(640x640改320x320)、优化后处理、甚至裁剪图像区域。这些手段能减少每帧推理耗时,从而增加每秒可处理的帧数。丢帧是在这些优化做完之后,最后一道保证延迟稳定性的保险。

6.2 无状态检测器和跟踪器的差异

这套丢帧背压方案直接适用于yolov5s这类无状态单帧检测器。一旦下游接跟踪算法,比如Deepsort、ByteTrack,逻辑就要变。

跟踪器需要连续帧上的同一目标信息来维持ID。你丢掉中间几帧,目标运动轨迹出现断裂,可能直接导致ID切换。如果一定要在双路视觉里接跟踪器,建议把丢帧点放到“进入跟踪器之前”:检测端丢帧,跟踪端尽力匹配;或者降低采集帧率,而不是随机丢帧。更稳妥的办法是把丢帧做成“均匀抽帧”,比如每3帧取1帧,而不是让消费者偶尔抽风丢一个。

我自己在阶段方案里是先做纯检测,后续再往检测结果上套“短时轨迹关联”,目前丢帧策略对轨迹关联的影响还在可控范围内,但确实没有纯检测那么干净。

6.3 背压不意味着系统变慢

背压经常被人误解成“限速”。在阻塞式背压里确实会让生产者等着,但在丢弃式背压里,生产者永远不会等,消费者也永远在满负荷工作。整个系统的瓶颈始终是NPU,只是积压的帧从“无限”变成“接近零”。

这也是我喜欢丢弃式背压的原因:它不降低整个系统的吞吐上限,只是把缓冲空间压缩到最小,让流水线上的每一环都在处理最新鲜的数据。加上时间预算丢帧后,消费者还能“主动跳过”某些等待过久的任务,这正是“背压”想表达的语义:下游处理不过来时,压力被反向施加到上游的积压数据上,数据不再是包袱,而是可弃资产。

7. 最后再讲一点调试心得

我调试这套双路背压方案时,最痛苦的不是写丢帧逻辑,而是没有一个直观的方式判断“丢帧丢得对不对”。后来我用了一个笨办法:给每帧打一个递增序号,把序号、时间戳、丢帧原因(覆盖、超时预算、被仲裁跳过、推理成功)全部写成CSV日志。跑几轮之后直接看日志,就能还原每一帧的完整命运。

这个习惯救了我很多次。有一次帧延时突然波动到200毫秒,我看日志发现是某一路摄像头在偶发情况下暂停输出几帧,下游槽位一直空着,另一路其实没问题。如果没有日志,我八成会误判成“背压策略失效”,然后白白调半天参数。

我的建议是:开局就把帧序号和时间戳当成一等公民,别偷懒。调试完,再把日志关掉或降频写入,不要影响生产性能。

就分享这些。这套“丢旧帧背压”方案,说穿了就一个朴素想法:不吃过期食物,只处理最新画面。但把它在RK3588上真正落地,并且把延迟稳定在70毫秒上下,还是会踩掉前面那一大堆坑。如果你也正在做双路视觉,希望这篇文章能帮你少走几步弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询