# 优化FPS与延迟

以下表格显示了在USB 3.2 Gen 1（5
Gbps）连接下使用OAK相机可预期的性能。测试中禁用了XLink分块（pipeline.setXLinkChunkSize(0)）。示例代码请参见[延迟测量](https://docs.luxonis.com/software-v3/depthai/examples/latency_measurement.md)。

> 要启用10Gbps USB3模式（使用
> **USB 3.2 Gen 2**
> 兼容线缆时），必须在
> [Device](https://docs.luxonis.com/software-v3/depthai/depthai-components/device.md)
> 构造函数中显式设置：
> ```python
> with dai.Device(maxUsbSpeed=dai.UsbSpeed.SUPER_PLUS) as device:
> ```

| 项目 | 分辨率 | FPS | 设定FPS | 延迟 [ms] | 带宽 | 直方图 |
| --- | --- | --- | --- | --- | --- | --- |
| Color (isp) | 1080P | 60 | 60 | 33 | 1.5 Gbps |
[链接](https://user-images.githubusercontent.com/18037362/162675407-77882498-5bf1-4af8-b779-9b9e8321af57.png) |
| Color (isp) | 4K | 28.5 | 30 | 150 | 2.8 Gbps |
[链接](https://user-images.githubusercontent.com/18037362/162675403-f3c5a4c3-1f7d-4acc-a5d5-f5aecff5a66a.png) |
| Color (isp) | 4K | 26 | 26 | 83（标准差：3.6） | 2.6 Gbps | / |
| Mono | 720P/800P | 120 | 120 | 24.5 | 442/482 Mbps |
[链接](https://user-images.githubusercontent.com/18037362/162675400-b19f1e2c-bee0-4482-a861-39481bf2e35e.png) |
| Mono | 400P | 120 | 120 | 7.5 | 246 Mbps |
[链接](https://user-images.githubusercontent.com/18037362/162675393-e3fb08fb-0f17-49d0-85d0-31ae7b5af0f9.png) |

以下是相同的测试，但使用了OAK PoE相机，该相机采用千兆以太网链路。相机直接连接到计算机，中间没有任何交换机或路由器。电源通过M8接口提供。

 * [oak_bandwidth_test.py](https://github.com/luxonis/oak-examples/tree/master/random-scripts#oak-bandwidth-test) 结果：下行797
   Mbps，上行264 Mbps。
 * [oak_latency_test.py](https://github.com/luxonis/oak-examples/tree/master/random-scripts#oak-latency-test) 结果：平均5.2 ms，标准差6.2。

| 项目 | 分辨率 | FPS | 设定FPS | PoE延迟 [ms] | USB延迟 [ms] | 带宽 |
| --- | --- | --- | --- | --- | --- | --- |
| Color (isp) | 1080P | 25 | 25 | 51 | 33（标准差：0.8） | 622 Mbps |
| Color (isp) | 4K | 8 | 8 | 148 | 80（标准差：1.2） | 530 Mbps |
| Color (isp) | 4K | 8.5 | 10 | 530 | 80（标准差：1.3） | 663 Mbps |
| Mono | 400P | 90 | 90 | 12（标准差：5.0） | 8（标准差：0.47） | 184 Mbps |
| Mono | 400P | 110 | 110 | 16（标准差：9.4） | 8（标准差：0.45） | 225 Mbps |

由于带宽限制，PoE测量时我们设定了较低的FPS。例如，4K 8 FPS的延迟为150ms，而4K 10 FPS的延迟为530ms，因为链路已饱和。

 * 延迟 测量的是帧时间戳（imgFrame.getTimestamp()）与主机收到帧时的时间戳（dai.Clock.now()）之间的时间差。
 * 直方图 显示了“帧到达主机时间”在各帧之间的变化。Y轴表示在该时间点出现的帧数量，X轴表示微秒。
 * 带宽 是以指定分辨率和FPS传输帧所需的计算带宽。

## 编码帧

| 项目 | 分辨率 | FPS | 设定FPS | 主机接收时间 [ms] | 直方图 |
| --- | --- | --- | --- | --- | --- |
| Color视频 H.265 | 4K | 28.5 | 30 | 210 |
[链接](https://user-images.githubusercontent.com/18037362/162675386-a59a58d1-1d5c-4c82-89d8-12882926425b.png) |
| Color视频 MJPEG | 4K | 30 | 30 | 71 |
[链接](https://user-images.githubusercontent.com/18037362/162675384-64842bd6-f5e7-4b40-adf4-39b9c25d1c89.png) |
| Color视频 H.265 | 1080P | 60 | 60 | 42 |
[链接](https://user-images.githubusercontent.com/18037362/162675356-c7c86730-63cd-4de8-bad6-4e1484b29ca5.png) |
| Color视频 MJPEG | 1080P | 60 | 60 | 31 |
[链接](https://user-images.githubusercontent.com/18037362/162675371-62aadb93-20f0-4838-9aab-727fb1bfe6f4.png) |
| Mono H.265 | 800P | 60 | 60 | 23.5 |
[链接](https://user-images.githubusercontent.com/18037362/162675318-92a8541b-424c-49b1-8ec4-6d9e0170410a.png) |
| Mono MJPEG | 800P | 60 | 60 | 22.5 |
[链接](https://user-images.githubusercontent.com/18037362/162675328-20afbe8f-4587-4263-8981-919e5a8811c7.png) |
| Mono H.265 | 400P | 120 | 120 | 7.5 |
[链接](https://user-images.githubusercontent.com/18037362/162675345-dfb4d04a-5d3a-4f84-81e9-dfc091bc00b5.png) |
| Mono MJPEG | 400P | 120 | 120 | 7.5 |
[链接](https://user-images.githubusercontent.com/18037362/162675335-2e5a9581-972a-448c-b650-6b6d076a04b8.png) |

您还可以通过使用DepthAI的[零拷贝](https://github.com/luxonis/depthai-core/tree/message_zero_copy)分支来降低帧延迟。该分支将在XLink级别传递指向cv2.Mat的指针，而不是执行内存拷贝（当前的做法），因此性能提升取决于您使用的图像大小。
（注意：API有所不同，message_zero_copy分支上并非所有功能都可用。）

## PoE延迟

在PoE上，延迟可能因多种因素而有很大差异：

 * 网络本身。例如，如果你在一个有很多节点的大型网络中，延迟将比直接连接更高。
 * 带宽存在瓶颈：
   * 也许某些网络链路是10mbps/100mbps而不是完整的1gbps（由于交换机/网卡等原因）。你可以使用
     [PoE测试脚本](https://github.com/luxonis/oak-examples/tree/master/random-scripts#poe-test-script) 进行测试（speed 应为1000）。
   * 网络/计算机被其他流量饱和。你可以使用 [OAK带宽测试](https://github.com/luxonis/oak-examples/tree/master/random-scripts#oak-bandwidth-test)
     脚本测试实际带宽。使用直接连接，我测得下行约800mbps，上行约210mbps。
 * 计算机的网络接口卡设置，[文档在此](https://docs.luxonis.com/hardware/platform/deploy/poe-deployment-guide.md#network-interface-controller-settings)
 * 100%的OAK Leon CSS（CPU）使用率。Leon
   CSS核心处理POE通信（[参见文档](https://docs.luxonis.com/hardware/platform/rvc/rvc2.md#hardware-blocks-and-accelerators)），如果CPU使用率达到100%，它将无法以应有的速度处理通信。解决方法：
   参见 [CPU使用率](https://docs.luxonis.com/software-v3/depthai/tutorials/debugging.md) 文档。
 * 另一种可能的改进PoE延迟的方法是微调网络设置，例如MTU、TCP窗口大小等（详情见[这里](https://docs.luxonis.com/hardware/platform/deploy/poe-deployment-guide.md#advance-network-settings)）

## 带宽

对于大型未编码帧，即使在30FPS下，也可能很快饱和带宽，尤其是在PoE设备（1gbps链路）上：

```bash
4K NV12/YUV420 frames: 3840 * 2160 * 1.5 * 30fps * 8bits = 3 gbps
1080P NV12/YUV420 frames: 1920 * 1080 * 1.5 * 30fps * 8bits = 747 mbps
720P NV12/YUV420 frames: 1280 * 720 * 1.5 * 30fps * 8bits = 331 mbps

1080P RGB frames: 1920 * 1080 * 3 * 30fps * 8bits = 1.5 gbps

800P depth frames: 1280 * 800 * 2 * 30fps * 8bits = 492 mbps
400P depth frames: 640 * 400 * 2 * 30fps * 8bits = 123 mbps

800P mono frames: 1280 * 800 * 1 * 30fps * 8bits = 246 mbps
400P mono frames: 640 * 400 * 1 * 30fps * 8bits = 62 mbps
```

公式中的第三个值是字节/像素，对于NV12/YUV420为1.5，对于RGB为3，对于深度帧为2，对于单色（灰度）帧为1。对于视差帧，它为1（正常模式）或2（子像素模式）。

减少带宽的几个选项：

 * 使用 [VideoEncoder](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/video_encoder.md)
   在设备上对帧进行编码（H.264、H.265、MJPEG）
 * 降低FPS/分辨率/流数量

## 测量操作时间

如果用户将depthai级别设置为trace（参见
[DepthAI调试级别](https://docs.luxonis.com/software-v3/depthai/tutorials/debugging.md)），depthai将记录每个节点/进程的操作时间，如下所示。

```bash
[SpatialDetectionNetwork(1)] [trace] SpatialDetectionNetwork syncing took '70.39142' ms.
[StereoDepth(4)] [trace] Warp node took '2.2945' ms.
[system] [trace] EV:0,S:0,IDS:27,IDD:10,TSS:2,TSN:601935518
[system] [trace] EV:0,S:1,IDS:27,IDD:10,TSS:2,TSN:602001382
[StereoDepth(4)] [trace] Stereo took '12.27392' ms.
[StereoDepth(4)] [trace] 'Median+Disparity to depth' pipeline took '0.86295' ms.
[StereoDepth(4)] [trace] Stereo post processing (total) took '0.931422' ms.
[SpatialDetectionNetwork(1)] [trace] NeuralNetwork inference took '62.274784' ms.
[StereoDepth(4)] [trace] Stereo rectification took '2.686294' ms.
[MonoCamera(3)] [trace] Mono ISP took '1.726888' ms.
[system] [trace] EV:0,S:0,IDS:20,IDD:25,TSS:2,TSN:616446812
[system] [trace] EV:0,S:1,IDS:20,IDD:25,TSS:2,TSN:616489715
[SpatialDetectionNetwork(1)] [trace] DetectionParser took '3.464118' ms.
```

## 运行NN时减少延迟

在上述示例中，我们仅流式传输帧，未在OAK摄像头上进行其他操作。本节将重点讨论在OAK上同时运行NN模型时如何减少延迟。

### 增加NN资源

减少延迟的一种选择是增加NN资源。可以通过更改分配的NCE和SHAVE数量来实现（参见硬件加速器[文档](https://docs.luxonis.com/hardware/platform/rvc/rvc2.md#hardware-blocks-and-accelerators)）。
[编译工具](https://docs.luxonis.com/software/ai-inference/conversion.md#compile-tool) 可以为更多SHAVE核心编译模型。要分配更多NCE，可以使用以下API：

```python
import depthai as dai

pipeline = dai.Pipeline()
# nn = pipeline.create(dai.node.NeuralNetwork)
# nn = pipeline.create(dai.node.MobileNetDetectionNetwork)
nn = pipeline.create(dai.node.YoloDetectionNetwork)
nn.setNumInferenceThreads(1) # 默认使用2个线程
nn.setNumNCEPerInferenceThread(2) # 默认每个线程使用1个NCE
```

Models usually run at max FPS when using 2 threads (1 NCE/Thread), and compiling model for AVAILABLE_SHAVES / 2.

Example of FPS & latency comparison for YoloV7-tiny:

| NN resources | Camera FPS | Latency | NN FPS |
| --- | --- | --- | --- |
| **6 SHAVEs, 2x Threads (1NCE/Thread)** | 15 | 155 ms | 15 |
| 6 SHAVEs, 2x Threads (1NCE/Thread) | 14 | 149 ms | 14 |
| 6 SHAVEs, 2x Threads (1NCE/Thread) | 13 | 146 ms | 13 |
| 6 SHAVEs, 2x Threads (1NCE/Thread) | 10 | 141 ms | 10 |
| **13 SHAVEs, 1x Thread (2NCE/Thread)** | 30 | 145 ms | 11.6 |
| 13 SHAVEs, 1x Thread (2NCE/Thread) | 12 | 128 ms | 12 |
| 13 SHAVEs, 1x Thread (2NCE/Thread) | 10 | 118 ms | 10 |

### 降低相机 FPS 以匹配 NN FPS

降低 FPS 使其不超过 NN 的能力通常能提供最佳的延迟性能，因为 NN 在有新帧可用时能立即开始推理。

例如，在 15 FPS 下，从捕获时间（曝光结束和 MIPI 读取开始）算起，总延迟约为 70 ms。

这段时间包括以下内容：

 * MIPI 读取
 * ISP 处理
 * 预览后处理
 * NN 处理
 * 传输到主机
 * 最后，直到到达应用程序的额外延迟

注意：如果 FPS 略微增加至 19..21 FPS，会出现约 10ms 的额外延迟，我们认为这与固件有关。我们正在积极寻求改进以降低延迟。

### NN 输入队列大小和阻塞行为

如果应用程序设置 detNetwork.input.setBlocking(False)，但队列大小不变，以下调整可能有助于改善延迟性能：

添加 detNetwork.input.setQueueSize(1)，同时将相机 FPS 调回 40，我们得到约 80..105ms 的延迟。 导致不确定性的原因之一是相机的产生速率（帧间隔 25ms）与 NN 完成并可以接受新帧处理的速率不匹配。
