本页目录

  • 自动校准选项
  • 动态校准库(DCL)的使用
  • 示例
  • 场景指南
  • PerformanceMode 调优
  • 限制与说明
  • 故障排除
  • 另请参阅

DynamicCalibration - 技术实现

Supported on:RVC2RVC4
DynamicCalibration 是内置于 DepthAI 3.0 中的自校准工作流程,可在温度变化、物理冲击或长期漂移降低出厂校准时恢复并保持立体精度。
在本页中,我们列出了在您项目中集成 DynamicCalibration 的技术实施步骤。如果您想了解 DynamicCalibration 的更多一般信息,请访问此页面

主要功能

  • 恢复深度性能——将视差图恢复至最佳视觉质量。
  • 无需目标标靶——在自然场景中操作;只需移动相机以捕捉不同视角。
  • 快速执行——通常可在数秒内完成。
  • 健康监控——随时运行诊断,无需刷写新校准。

自动校准选项

有两种方法可以在 DynamicCalibration 之上运行自动校准:
  • 使用AutoCalibration 主机节点在管道内进行显式的节点级控制。
  • 使用 DEPTHAI_AUTOCALIBRATION 在部署时启用,无需更改管道代码。
  • 使用 Pipeline.setAutoCalibrationMode() 的直接设置器来启用自动校准,无需使用 AutoCalibration 主机节点。

动态校准库(DCL)的使用

本节是对如何在 DepthAI 中使用 DynamicCalibration 节点进行动态校准工作流程的简单高层次表示。
动态校准以包含命令的DynamicCalibrationControl消息作为输入(例如 ApplyCalibrationCalibrate……有关所有可用命令,请参阅消息定义)。节点根据发送的输入命令,在以下三个输出队列之一返回输出:取决于发送了哪个输入命令。在接下来的段落中,我们将探讨如何将 DCL 实际集成到您的代码中。

初始化 DynamicCalibration 节点

DynamicCalibration 节点需要来自同一设备的两个同步相机流。设置方法如下:
Python
1import depthai as dai
2
3# 初始化管道
4pipeline = dai.Pipeline()
5
6# 创建相机节点
7cam_left = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B)
8cam_right = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C)
9
10# 请求全分辨率 NV12 输出
11left_out = cam_left.requestFullResolutionOutput()
12right_out = cam_right.requestFullResolutionOutput()
13
14# 初始化 DynamicCalibration 节点
15dyn_calib = pipeline.create(dai.node.DynamicCalibration)
16
17# 将相机链接到 DynamicCalibration
18left_out.link(dyn_calib.left)
19right_out.link(dyn_calib.right)
20
21device = pipeline.getDefaultDevice()
22calibration = device.readCalibration()
23device.setCalibration(calibration)
24
25pipeline.start()
26while pipeline.isRunning():
27    ...

向节点发送命令

DepthAI中的节点通过输入/输出消息队列进行通信。 DynamicCalibration节点有多个队列,但控制最重要的队列是inputControl队列。
Python
1# 初始化命令输入队列
2command_input = dyn_calib.inputControl.createInputQueue()
3# 示例命令:发送开始校准
4command_input.send(dai.DynamicCalibrationControl.startCalibration())
可用命令
  • StartCalibration() - 开始校准过程。
  • StopCalibration() - 停止校准过程。
  • Calibrate(force=False) - 基于已加载的数据计算新的校准。
    • force - 不对已加载数据施加限制
  • CalibrationQuality(force=False) - 评估当前校准的质量。
    • force - 不对已加载数据施加限制
  • LoadImage() - 从设备加载一张图像。
  • ComputeCalibrationMetrics(calibration) - 计算校准指标,如数据质量和校准置信度。
  • ApplyCalibration(calibration) - 将校准应用到设备。
  • SetPerformanceMode(performanceMode) - 发送将要使用的性能模式。
  • ResetData() - 移除所有之前加载的数据。

从节点接收数据

该节点提供多个输出队列:
Python
1# 用于接收新校准的队列
2calibration_output = dyn_calib.calibrationOutput.createOutputQueue()
3# 用于接收覆盖数据的队列
4coverage_output = dyn_calib.coverageOutput.createOutputQueue()
5# 用于检查校准质量的队列
6quality_output = dyn_calib.qualityOutput.createOutputQueue()
7# 用于检查校准指标的队列
8metrics_output = dyn_calib.metricsOutput.createOutputQueue()
请查阅 DynamicCalibrationResultCoverageDataCalibrationQualityCalibrationMetrics 的参考文档,了解输出的具体数据结构。

读取覆盖数据

覆盖数据通过 coverageOutput 接收,触发时机包括手动加载图像(使用 LoadImage 命令)或连续校准过程中(运行 StartCalibration 命令后)。手动图像加载
Python
1# 加载单张图像
2command_input.send(dai.DynamicCalibrationControl.loadImage())
3
4# 加载后获取覆盖数据
5coverage = coverage_output.get()
6print(f"Coverage = {coverage.meanCoverage}")
校准期间持续采集
Python
1command_input.send(dai.DynamicCalibrationControl.startCalibration())
2
3while pipeline.isRunning():
4    # 阻塞读取
5    coverage = coverage_output.get()
6    print(f"Coverage = {coverage.meanCoverage}")
7
8    # 非阻塞读取
9    coverage = coverage_output.tryGet()
10    if coverage:
11        print(f"Coverage = {coverage.meanCoverage}")

读取校准数据

校准结果可通过以下方式获取:
  • dai.DynamicCalibrationControl.startCalibration() — 开始数据收集并尝试校准。
  • dai.DynamicCalibrationControl.calibrate(force=False) — 使用已有的已加载数据进行校准(如以下示例所示,需先用 LoadImage 命令加载图像)。
校准数据将以 DynamicCalibrationResult 消息类型返回。手动图像加载
Python
1# 加载单张图像
2command_input.send(dai.DynamicCalibrationControl.loadImage())
3
4# 发送校准命令
5command_input.send(dai.DynamicCalibrationControl.calibrate(force=False))
6
7# 加载后获取校准结果
8calibration = calibration_output.get()
9print(f"Calibration = {calibration.info}")
持续采集
Python
1# 开始数据收集并尝试校准
2command_input.send(dai.DynamicCalibrationControl.startCalibration())
3
4while pipeline.isRunning():
5    # 阻塞读取
6    calibration = calibration_output.get()
7    print(f"Calibration = {calibration.info}")
8
9    # 非阻塞读取
10    calibration = calibration_output.tryGet()
11    if calibration:
12        print(f"Calibration = {calibration.info}")

性能模式

设置性能模式如下:
Python
1# 设置性能模式
2dynCalibInputControl.send(dai.DynamicCalibrationControl.setPerformanceMode(dai.node.DynamicCalibration.OPTIMIZE_PERFORMANCE))
性能模式设置校准所需的数据量。
Python
1dai.node.DynamicCalibration.PerformanceMode.OPTIMIZE_PERFORMANCE  # 最严格的模式
2dai.node.DynamicCalibration.PerformanceMode.DEFAULT               # 较宽松但通常足够
3dai.node.DynamicCalibration.PerformanceMode.OPTIMIZE_SPEED        # 优化速度而非性能
4dai.node.DynamicCalibration.PerformanceMode.STATIC_SCENERY        # 不严格
5dai.node.DynamicCalibration.PerformanceMode.SKIP_CHECKS           # 跳过所有内部检查

示例

Dynamic Calibration Interactive Visualizer

通过以下命令,您可以克隆并运行标定集成。
Command Line
1git clone https://github.com/luxonis/depthai-core.git
2cd depthai-core/
3python3 -m venv venv
4source venv/bin/activate
5python3 examples/python/install_requirements.py
6python3 examples/python/DynamicCalibration/calibration_integration.py

Calibration Quality Check

Python

按照 Github 上的 README 运行此示例。
Python
GitHub
1import depthai as dai
2import numpy as np
3import time
4import cv2
5
6
7def print_metrics(metrics: dai.CalibrationQualityData) -> None:
8    for socket_pair, rotation in metrics.pairwiseRotationDifference.items():
9        print(f"Pairwise rotation difference {socket_pair} = {rotation} deg")
10    print(f"Mean Sampson error achievable = {metrics.sampsonErrorNew:.3f} px")
11    print(f"Mean Sampson error current    = {metrics.sampsonErrorCurrent:.3f} px")
12
13# ---------- Pipeline definition ----------
14with dai.Pipeline() as pipeline:
15    # Create camera nodes
16    monoLeft  = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B)
17    monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C)
18
19    # Request full resolution NV12 outputs
20    monoLeftOut  = monoLeft.requestFullResolutionOutput()
21    monoRightOut = monoRight.requestFullResolutionOutput()
22
23    # Initialize the DynamicCalibration node
24    dynCalib = pipeline.create(dai.node.DynamicCalibration)
25
26    # Link the cameras to the DynamicCalibration
27    monoLeftOut.link(dynCalib.left)
28    monoRightOut.link(dynCalib.right)
29
30    stereo = pipeline.create(dai.node.StereoDepth)
31    monoLeftOut.link(stereo.left)
32    monoRightOut.link(stereo.right)
33
34    # Queues
35    syncedLeftQueue  = stereo.syncedLeft.createOutputQueue()
36    syncedRightQueue = stereo.syncedRight.createOutputQueue()
37    depthQueue = stereo.depth.createOutputQueue()
38
39    # Initialize the command output queues for coverage and calibration output
40    dynCalibCoverageQueue = dynCalib.coverageOutput.createOutputQueue()
41    dynCalibCalibrationQueue = dynCalib.calibrationOutput.createOutputQueue()
42
43    # Initialize the command input queue
44    dynCalibInputControl = dynCalib.inputControl.createInputQueue()
45
46    device = pipeline.getDefaultDevice()
47    device.setCalibration(device.getCalibration())
48
49    pipeline.start()
50    time.sleep(1) # wait for auto exposure to settle
51
52    while pipeline.isRunning():
53        leftSynced  = syncedLeftQueue.get()
54        rightSynced = syncedRightQueue.get()
55        depth = depthQueue.get()
56
57        cv2.imshow("left", leftSynced.getCvFrame())
58        cv2.imshow("right", rightSynced.getCvFrame())
59
60        colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame()
61        cv2.imshow("depth", colorizedDepth)
62
63        # --- Load one frame into calibration & read coverage
64        dynCalibInputControl.send(dai.DynamicCalibrationControl.loadImage())
65        coverage = dynCalibCoverageQueue.get()
66        if coverage is not None:
67            print(f"2D Spatial Coverage = {coverage.meanCoverage} / 100 [%]")
68            print(f"Data Acquired       = {coverage.dataAcquired} / 100 [%]")
69
70        # --- Run one-shot calibration & read metrics from calibrationOutput
71        dynCalibInputControl.send(dai.DynamicCalibrationControl.calibrate(False))
72        dynCalibrationResult = dynCalibCalibrationQueue.get()
73        if dynCalibrationResult is not None:
74            print(f"Dynamic calibration status: {dynCalibrationResult.info}")
75            if dynCalibrationResult.calibrationData:
76                print("Successfully evaluated metrics from calibration output")
77                print_metrics(dynCalibrationResult.calibrationData.calibrationDifference)
78                dynCalibInputControl.send(dai.DynamicCalibrationControl.resetData())
79
80        key = cv2.waitKey(1)
81        if key == ord('q'):
82            pipeline.stop()
83            break

C++

来自我们的 Github 的示例:
1#include <chrono>
2#include <iostream>
3#include <opencv2/opencv.hpp>
4#include <thread>
5
6#include "depthai/depthai.hpp"
7
8namespace {
9void printMetrics(const dai::CalibrationQuality::Data& q) {
10    for(const auto& [socketPair, rotation] : q.pairwiseRotationDifference) {
11        std::cout << "Pairwise rotation difference " << static_cast<int>(socketPair.first) << " -> " << static_cast<int>(socketPair.second) << " = [";
12        for(std::size_t i = 0; i < rotation.size(); ++i) {
13            if(i > 0) std::cout << ", ";
14            std::cout << rotation[i];
15        }
16        std::cout << "] deg" << std::endl;
17    }
18    std::cout << "Mean Sampson error achievable = " << q.sampsonErrorNew << " px" << std::endl;
19    std::cout << "Mean Sampson error current    = " << q.sampsonErrorCurrent << " px" << std::endl;
20}
21}  // namespace
22
23int main() {
24    auto device = std::make_shared<dai::Device>();
25
26    // ---------- Pipeline definition ----------
27    dai::Pipeline pipeline(device);
28
29    auto monoLeft = pipeline.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_B);
30    auto monoRight = pipeline.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_C);
31
32    auto* leftOut = monoLeft->requestFullResolutionOutput();
33    auto* rightOut = monoRight->requestFullResolutionOutput();
34
35    // Dynamic-calibration node
36    auto dynCalib = pipeline.create<dai::node::DynamicCalibration>();
37    leftOut->link(dynCalib->left);
38    rightOut->link(dynCalib->right);
39
40    auto stereo = pipeline.create<dai::node::StereoDepth>();
41    leftOut->link(stereo->left);
42    rightOut->link(stereo->right);
43
44    // In-pipeline host queues
45    auto leftSyncedQueue = stereo->syncedLeft.createOutputQueue();
46    auto rightSyncedQueue = stereo->syncedRight.createOutputQueue();
47    auto disparityQueue = stereo->disparity.createOutputQueue();
48
49    auto dynCoverageOutQ = dynCalib->coverageOutput.createOutputQueue();
50    auto dynCalibOutQ = dynCalib->calibrationOutput.createOutputQueue();
51    auto dynCalibInputControl = dynCalib->inputControl.createInputQueue();
52
53    device->setCalibration(device->getCalibration());
54
55    pipeline.start();
56    std::this_thread::sleep_for(std::chrono::seconds(1));  // wait for autoexposure to settle
57
58    using DCC = dai::DynamicCalibrationControl;
59
60    while(pipeline.isRunning()) {
61        auto leftSynced = leftSyncedQueue->get<dai::ImgFrame>();
62        auto rightSynced = rightSyncedQueue->get<dai::ImgFrame>();
63        auto disparity = disparityQueue->get<dai::ImgFrame>();
64
65        cv::imshow("left", leftSynced->getCvFrame());
66        cv::imshow("right", rightSynced->getCvFrame());
67
68        // --- Load one frame pair into the calibration pipeline
69        dynCalibInputControl->send(DCC::loadImage());
70
71        // Wait for coverage info
72        auto coverageMsg = dynCoverageOutQ->get<dai::CoverageData>();
73        if(coverageMsg) {
74            std::cout << "2D Spatial Coverage = " << coverageMsg->meanCoverage << " / 100 [%]" << std::endl;
75            std::cout << "Data Acquired       = " << coverageMsg->dataAcquired << " / 100 [%]" << std::endl;
76        }
77
78        // Run one-shot calibration and inspect the returned metrics.
79        dynCalibInputControl->send(DCC::calibrate(false));
80
81        // Wait for calibration result
82        auto dynCalibrationResult = dynCalibOutQ->get<dai::DynamicCalibrationResult>();
83        if(dynCalibrationResult) {
84            std::cout << "Dynamic calibration status: " << dynCalibrationResult->info << std::endl;
85
86            if(dynCalibrationResult->calibrationData) {
87                std::cout << "Successfully evaluated metrics from calibration output." << std::endl;
88                printMetrics(dynCalibrationResult->calibrationData->calibrationDifference);
89                dynCalibInputControl->send(DCC::resetData());
90            }
91        } else {
92            std::cout << "Dynamic calibration: no result received." << std::endl;
93        }
94
95        int key = cv::waitKey(1);
96        if(key == 'q') break;
97    }
98
99    return 0;
100}

Dynamic Calibration

Python

按照 Github 上的 README 运行此示例。
Python
GitHub
1import depthai as dai
2import numpy as np
3import time
4import cv2
5
6# ---------- Pipeline definition ----------
7with dai.Pipeline() as pipeline:
8    # Cameras
9    monoLeft  = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B)
10    monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C)
11
12    # Full-res NV12 outputs
13    monoLeftOut  = monoLeft.requestFullResolutionOutput()
14    monoRightOut = monoRight.requestFullResolutionOutput()
15
16    # Initialize the DynamicCalibration node
17    dynCalib = pipeline.create(dai.node.DynamicCalibration)
18
19    # Link the cameras to the DynamicCalibration
20    monoLeftOut.link(dynCalib.left)
21    monoRightOut.link(dynCalib.right)
22
23    # Stereo (for depth + synced previews)
24    stereo = pipeline.create(dai.node.StereoDepth)
25    monoLeftOut.link(stereo.left)
26    monoRightOut.link(stereo.right)
27
28    # Output queues
29    syncedLeftQueue  = stereo.syncedLeft.createOutputQueue()
30    syncedRightQueue = stereo.syncedRight.createOutputQueue()
31    depthQueue       = stereo.depth.createOutputQueue()
32
33    # Initialize the command output queues for calibration and coverage
34    dynCalibCalibrationQueue = dynCalib.calibrationOutput.createOutputQueue()
35    dynCalibCoverageQueue    = dynCalib.coverageOutput.createOutputQueue()
36
37    # Initialize the command input queue
38    dynCalibInputControl = dynCalib.inputControl.createInputQueue()
39
40    device = pipeline.getDefaultDevice()
41    device.setCalibration(device.getCalibration())
42
43    pipeline.start()
44    time.sleep(1) # wait for auto exposure to settle
45
46    # Set performance mode
47    dynCalibInputControl.send(
48        dai.DynamicCalibrationControl.setPerformanceMode(
49            dai.DynamicCalibrationControl.PerformanceMode.OPTIMIZE_PERFORMANCE
50        )
51    )
52
53    # Start periodic calibration
54    dynCalibInputControl.send(
55        dai.DynamicCalibrationControl.startCalibration()
56    )
57
58    while pipeline.isRunning():
59        leftSynced  = syncedLeftQueue.get()
60        rightSynced = syncedRightQueue.get()
61        depth = depthQueue.get()
62
63        cv2.imshow("left", leftSynced.getCvFrame())
64        cv2.imshow("right", rightSynced.getCvFrame())
65
66        colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame()
67        cv2.imshow("depth", colorizedDepth)
68
69        # --- Coverage (non-blocking) ---
70        coverage = dynCalibCoverageQueue.tryGet()
71        if coverage is not None:
72            print(f"2D Spatial Coverage = {coverage.meanCoverage} / 100 [%]")
73            print(f"Data Acquired       = {coverage.dataAcquired} / 100 [%]")
74
75        # --- Calibration result (non-blocking) ---
76        dynCalibrationResult = dynCalibCalibrationQueue.tryGet()
77        calibrationData = dynCalibrationResult.calibrationData if dynCalibrationResult is not None else None
78
79        if dynCalibrationResult is not None:
80            print(f"Dynamic calibration status: {dynCalibrationResult.info}")
81
82        # --- Apply calibration if available, print quality deltas, then reset+continue ---
83        if calibrationData:
84            print("Successfully calibrated")
85            # Apply to device
86            dynCalibInputControl.send(
87                dai.DynamicCalibrationControl.applyCalibration(calibrationData.newCalibration)
88            )
89
90            q = calibrationData.calibrationDifference
91            if q.pairwiseRotationDifference:
92                pairwiseRotation = next(iter(q.pairwiseRotationDifference.values()))
93                rotDiff = float(np.sqrt(sum(axis * axis for axis in pairwiseRotation)))
94                print(f"Rotation difference: || r_current - r_new || = {rotDiff:.2f} deg")
95            print(f"Mean Sampson error achievable = {q.sampsonErrorNew:.3f} px")
96            print(f"Mean Sampson error current    = {q.sampsonErrorCurrent:.3f} px")
97
98            # Reset accumulators and continue periodic calibration
99            dynCalibInputControl.send(
100                dai.DynamicCalibrationControl.resetData()
101            )
102            dynCalibInputControl.send(
103                dai.DynamicCalibrationControl.startCalibration()
104            )
105
106        key = cv2.waitKey(1)
107        if key == ord('q'):
108            pipeline.stop()
109            break

C++

来自我们的 Github 的示例:
1// examples/cpp/DynamicCalibration/calibrate.cpp
2#include <chrono>
3#include <cmath>
4#include <iomanip>
5#include <iostream>
6#include <opencv2/opencv.hpp>
7#include <thread>
8
9#include "depthai/depthai.hpp"
10
11int main() {
12    auto device = std::make_shared<dai::Device>();
13
14    // ---------- Pipeline definition ----------
15    dai::Pipeline pipeline(device);
16
17    auto monoLeft = pipeline.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_B);
18    auto monoRight = pipeline.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_C);
19
20    auto* leftOut = monoLeft->requestFullResolutionOutput();
21    auto* rightOut = monoRight->requestFullResolutionOutput();
22
23    // Dynamic-calibration node
24    auto dynCalib = pipeline.create<dai::node::DynamicCalibration>();
25    leftOut->link(dynCalib->left);
26    rightOut->link(dynCalib->right);
27
28    auto stereo = pipeline.create<dai::node::StereoDepth>();
29    leftOut->link(stereo->left);
30    rightOut->link(stereo->right);
31
32    // In-pipeline host queues
33    auto leftSyncedQueue = stereo->syncedLeft.createOutputQueue();
34    auto rightSyncedQueue = stereo->syncedRight.createOutputQueue();
35    auto depthQueue = stereo->depth.createOutputQueue();
36
37    auto dynCalibOutQ = dynCalib->calibrationOutput.createOutputQueue();
38    auto dynCoverageOutQ = dynCalib->coverageOutput.createOutputQueue();
39
40    auto dynCalibInputControl = dynCalib->inputControl.createInputQueue();
41
42    device->setCalibration(device->getCalibration());
43
44    pipeline.start();
45    std::this_thread::sleep_for(std::chrono::seconds(1));  // wait for autoexposure to settle
46
47    using DCC = dai::DynamicCalibrationControl;
48    // Optionally set performance mode:
49    dynCalibInputControl->send(DCC::setPerformanceMode(DCC::PerformanceMode::OPTIMIZE_PERFORMANCE));
50
51    // Start calibration (optimize performance)
52    dynCalibInputControl->send(DCC::startCalibration());
53
54    while(pipeline.isRunning()) {
55        auto leftSynced = leftSyncedQueue->get<dai::ImgFrame>();
56        auto rightSynced = rightSyncedQueue->get<dai::ImgFrame>();
57        auto depth = depthQueue->get<dai::ImgFrame>();
58
59        cv::imshow("left", leftSynced->getCvFrame());
60        cv::imshow("right", rightSynced->getCvFrame());
61
62        cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth).getCvFrame());
63
64        // Coverage (non-blocking)
65        if(auto coverageMsg = dynCoverageOutQ->tryGet<dai::CoverageData>()) {
66            std::cout << "2D Spatial Coverage = " << coverageMsg->meanCoverage << "  / 100 [%]\n";
67            std::cout << "Data Acquired       = " << coverageMsg->dataAcquired << "  / 100 [%]\n";
68        }
69
70        // Calibration result (non-blocking)
71        if(auto dynCalibrationResult = dynCalibOutQ->tryGet<dai::DynamicCalibrationResult>()) {
72            std::cout << "Dynamic calibration status: " << dynCalibrationResult->info << std::endl;
73
74            if(dynCalibrationResult->calibrationData) {
75                std::cout << "Successfully calibrated." << std::endl;
76
77                // Apply the produced calibration
78                const auto& newCalib = dynCalibrationResult->calibrationData->newCalibration;
79                dynCalibInputControl->send(DCC::applyCalibration(newCalib));
80
81                // Print quality deltas
82                const auto& q = dynCalibrationResult->calibrationData->calibrationDifference;
83
84                if(!q.pairwiseRotationDifference.empty()) {
85                    const auto& pairwiseRotation = q.pairwiseRotationDifference.begin()->second;
86                    float rotDiff = 0.0f;
87                    for(const auto axis : pairwiseRotation) rotDiff += axis * axis;
88                    rotDiff = std::sqrt(rotDiff);
89                    std::cout << "Rotation difference: " << rotDiff << " deg\n";
90                }
91                std::cout << "Mean Sampson error achievable = " << q.sampsonErrorNew << " px\n";
92                std::cout << "Mean Sampson error current    = " << q.sampsonErrorCurrent << " px\n";
93
94                // Reset and start a new round if desired
95                dynCalibInputControl->send(DCC::startCalibration());
96            }
97        }
98
99        int key = cv::waitKey(1);
100        if(key == 'q') break;
101    }
102
103    return 0;
104}
更多信息请参阅示例的 README。

场景指南

好的校准场景能让算法更容易检测、匹配和跟踪特征。建议的特征如下:
  • 包含多个深度范围内的纹理物体。
  • 避免使用空白墙壁或毫无特征的表面。
  • 缓慢移动摄像头以覆盖整个视场;避免突然的运动
建议原始图像 VS. 特征覆盖(绿色区域)
确保丰富的纹理和视觉细节 - 纹理丰富、边缘清晰且在视场中均匀分布的物体,能创造理想的校准条件。
🚫避免平坦或毫无特征的表面 - 缺乏纹理表面或视觉上可区分的物体,能提供的可用特征很少。
🚫避免反光和透明表面 - 反光和透明表面会产生错误的3D特征。
🚫避免黑暗场景 - 低对比度、阴影以及照明不足的场景,能检测到的特征很少。
🚫避免重复图案 - 许多有图案的区域看起来过于相似,难以区分。

PerformanceMode 调优

模式使用场景
DEFAULT平衡精度与速度。
STATIC_SCENERY相机固定,场景稳定。
OPTIMIZE_SPEED最快标定,精度降低。
OPTIMIZE_PERFORMANCE在特征丰富的场景中实现最大精度。
SKIP_CHECKS自动化流水线,忽略确保场景质量的内部检查。
通过将合适的场景与正确的 PerformanceMode 相结合,用户可以显著提高标定可靠性和深度估计质量。

限制与说明

  • 支持的设备 — 动态标定适用于:
    • 所有立体OAK Series 2相机(不包括FFC)
    • 所有立体OAK Series 4相机
  • DepthAI版本 — 需要 DepthAI 3.0 或更高版本。
  • 重新标定的参数 — 仅更新外参;内参保持不变。
  • 操作系统支持 — 支持 Linux、macOS 和 Windows。
  • 绝对深度规格 — DCL 改善相对深度感知;绝对精度可能仍与原始工厂规格略有差异。

故障排除

症状可能原因修复方法
高重投影误差板载配置中模型名称或HFOV不正确验证板JSON和相机规格
“成功”DCL后深度仍不正确左/右相机互换交换插座或更新板配置并重新标定
nullopt 质量报告场景覆盖不足移动相机以捕获更丰富的纹理
运行时错误:"The calibration on the device is too old to perform DynamicCalibration, full re-calibration required!"设备标定过于陈旧,动态重新标定无法提供任何好处。需要更新的设备

另请参阅