# ToF

ToF 节点 用于将 ToF 传感器的原始数据转换为深度图。目前，以下两款产品包含 ToF 传感器：

 * [OAK-D ToF](https://shop71313603.taobao.com/?spm=pc_detail.30350276.shop_block.dshopinfo.27a17dd635FNDA) - 集成 33D ToF
   传感器，搭配立体相机对
 * [OAK-FFC ToF 33D](https://shop71313603.taobao.com/?spm=pc_detail.30350276.shop_block.dshopinfo.27a17dd635FNDA) - 带有 33D ToF
   传感器的独立 FFC 模块

ToF 的 depth 输出可以替代 [StereoDepth](https://docs.luxonis.com/software/depthai-components/nodes/stereo_depth.md) 的输出——因此你可以直接将
ToF.depth 链接到
[MobileNetSpatialDetectionNetwork](https://docs.luxonis.com/software/depthai-components/nodes/mobilenet_spatial_detection_network.md)
/ [YoloSpatialDetectionNetwork](https://docs.luxonis.com/software/depthai-components/nodes/yolo_spatial_detection_network.md) 或
[SpatialLocationCalculator](https://docs.luxonis.com/software/depthai-components/nodes/spatial_location_calculator.md)。

一般来说，ToF 的深度精度比 Stereo 深度更精确。你可以 在 [此处了解 ToF 深度精度](https://docs.luxonis.com/hardware/platform/depth/depth-accuracy.md)。

ToF 点云演示视频，展示深度精度：

## 如何放置

#### Python

```python
pipeline = dai.Pipeline()
tof = pipeline.create(dai.node.ToF)
```

#### C++

```cpp
dai::Pipeline pipeline;
auto tof = pipeline.create<dai::node::ToF>();
```

## 输入和输出

## ToF 设置

在 [ToF depth](https://docs.luxonis.com/software/depthai/examples/tof_depth.md) 示例中，我们让用户可以快速配置 ToF 设置。

以下是最重要的设置：

 * 光学校正：这是一个校正光学效应的过程。启用后，ToF 返回深度图（如下图的绿线所示）而非距离，从而与
   [StereoDepth](https://docs.luxonis.com/software/depthai-components/nodes/stereo_depth.md) 的深度报告匹配。它执行校正和距离到深度的转换（Z 图）。

 * 去畸变：depth 和 amplitude 帧默认会去畸变。

 * 相位解包裹 - 纠正 ToF 传感器相位包裹效应的过程。可设置为 [0..5 为优化值]。数值越大，ToF 量程越长，但也会增加噪声。近似最大距离（精确值见下方 [最大距离](#Max%20distance)）：
   
   * 0 - 禁用，最远约 1.87 米（使用 80MHz 调制频率）
   * 1 - 最远约 3 米
   * 2 - 最远约 4.5 米
   * 3 - 最远约 6 米
   * 4 - 最远约 7.5 米

 * 突发模式：启用后，ToF 节点不会复用帧，如下图所示。这与 ToF 帧的后处理有关，而非实际传感器/投影仪。默认禁用。

 * 相位混洗时间滤波器：平均同一调制频率的混洗和非混洗帧以降低噪声。默认启用。你可以禁用以减少 [ToF 运动模糊](#ToF%20motion%20blur) 和系统负载。

以下时间图展示了基于设置的 ToF 解码过程。

## 相位解包裹

如果光从 ToF 传感器往返的时间超过发射波的周期（1.5米或1.87米），测量结果将“回绕”到较低值。这称为相位包裹。 这类似于时钟在12小时后重置。由于我们的 ToF 传感器有两种不同的调制频率（80MHz 和 100MHz），因此可以进行相位解包裹。

相位解包裹旨在通过允许传感器正确解释更长的距离来纠正此问题。它使用算法跟踪发生的周期数（波的往返次数）， 从而纠正“包裹”的相位。缺点是传感器需要跟踪的周期数越多，测量中引入的噪声就越大。

## ToF 运动模糊

为了减少运动模糊，我们推荐以下设置：

 * 增加相机帧率。最高可达160FPS，使得帧捕获成为最快（帧间隔6.25ms）。这将减少运动模糊，因为ToF组合多个帧来获得深度。注意：160FPS会显著增加系统负载（参见[调试](https://docs.luxonis.com/software/depthai/debugging.md)
   DepthAI流水线）。另请注意：更高的FPS -> 更短的曝光时间，这可能会增加噪声。
 * 禁用相位抖动时域滤波器。这会引入更多噪声。
 * 禁用相位解包裹。这将最大距离减少到1.87米（使用80MHz调制频率），因此大约1立方米的空间可见（非常有限的用例）。
 * 启用突发模式。如果抖动脉冲滤波器和相位解包裹被禁用，此设置无关紧要（见上图）。启用后，ToF节点不会重用帧（较低的FPS）。

在上图中，组合的帧数越少（图的底部），运动模糊越少。组合的帧数越多（图的顶部），滤波越多（更好的精度），但会导致更多的运动模糊。

## 最大距离

最大ToF距离取决于调制频率和相位解包裹级别。如果启用了相位解包裹，最大距离是两个调制频率中较短的一个（因此在100MHz时达到最大距离）。公式如下：

```math
c = 299792458.0 # 光速 m/s

MAX_80MHZ_M = c / (80000000 * 2) = 1.873 m
MAX_100MHZ_M = c / (100000000 * 2) = 1.498 m

MAX_DIST_80MHZ_M = (phaseUnwrappingLevel + 1) * 1.873 + (phaseUnwrapErrorThreshold / 2)
MAX_DIST_100MHZ_M = (phaseUnwrappingLevel + 1) * 1.498 + (phaseUnwrapErrorThreshold / 2)

MAX_DIST_PHASE_UNWRAPPING_M = MAX_DIST_100MHZ_M
```

## ToF 帧率

传感器/发射器最高可达 160 FPS，这将转化为深度输出，要么是 40 FPS（启用突发模式），要么是 80 FPS（禁用突发模式）。这是由于不同的调制频率（80MHz 和 100MHz）以及需要组合抖动/非抖动脉冲帧以减少噪声。

## 用法

#### Python

```python
pipeline = dai.Pipeline()

tof_cam = pipeline.create(dai.node.Camera)
tof_cam.setFps(30)
# 假设 ToF 相机传感器位于端口 CAM_A
tof_cam.setBoardSocket(dai.CameraBoardSocket.CAM_A)

tof = pipeline.create(dai.node.ToF)

# 数字越大 => 处理越快。1 个 shave 核心可以实现 30 FPS。
tof.setNumShaves(1)

# 中值滤波器，核大小 5x5
tof.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_5x5)

tofConfig = tof.initialConfig.get()
# 时域滤波器平均抖动/非抖动脉冲频率
tofConfig.enablePhaseShuffleTemporalFilter = True
# 相位解包裹，用于更远距离。
tofConfig.phaseUnwrappingLevel = 4 # 最高 7.5 米
tofConfig.phaseUnwrapErrorThreshold = 300
tof.initialConfig.set(tofConfig)

# ToF 节点将原始传感器帧转换为深度
tof_cam.raw.link(tof.input)

# 将 ToF 深度输出发送到主机，或者发送到 SLC / 空间检测网络
tof.depth.link(xout.input)
```

#### C++

```cpp
dai::Pipeline pipeline;

auto tofCam = pipeline.create<dai::node::MonoCamera>();
// 假设 ToF 相机传感器位于端口 CAM_A
tofCam->setBoardSocket(dai::CameraBoardSocket::AUTO);

auto tof = pipeline.create<dai::node::ToF>();
// ToF 节点将原始传感器帧转换为深度
tofCam->raw.link(tof->input);

auto xout = pipeline.create<dai::node::XLinkOut>();
xout->setStreamName("depth");
// 将 ToF 深度输出发送到主机
tof->depth.link(xout->input);
```

## 功能示例

 * [ToF depth](https://docs.luxonis.com/software/depthai/examples/tof_depth.md) - 获取 ToF 深度的示例
 * [RGB-ToF Alignment](https://docs.luxonis.com/software/depthai/examples/tof_align.md) - 将 ToF 深度对齐到彩色流

## 参考

### depthai.node.ToF(depthai.Node)

Kind: Class

ToF node

#### setNumFramesPool(self, arg0: typing.SupportsInt) -> ToF: ToF

Kind: Method

Specify number of frames in output pool

Parameter ``numFramesPool``:
Number of frames in output pool

#### setNumShaves(self, arg0: typing.SupportsInt) -> ToF: ToF

Kind: Method

Specify number of shaves reserved for ToF decoding.

#### amplitude

Kind: Property

Outputs ImgFrame message that carries amplitude image.

#### depth

Kind: Property

Outputs ImgFrame message that carries decoded depth image.

#### initialConfig

Kind: Property

Initial config to use for depth calculation.

#### input

Kind: Property

Input raw ToF data. Default queue is blocking with size 8.

#### inputConfig

Kind: Property

Input ToF message with ability to modify parameters in runtime. Default queue is
non-blocking with size 4.

#### intensity

Kind: Property

Outputs ImgFrame message that carries intensity image.

#### phase

Kind: Property

Outputs ImgFrame message that carries phase image, useful for debugging. float32
type.

### 需要帮助？

请前往 [OAKChina 官网](https://www.oakchina.cn/) 获取技术支持或解答您的任何疑问。
