Skip to content

Dynamic calibration - multi sensor calibration#1839

Merged
MaticTonin merged 20 commits into
developfrom
feature/dynamicCalibration_multiSensorCalibration
Jul 2, 2026
Merged

Dynamic calibration - multi sensor calibration#1839
MaticTonin merged 20 commits into
developfrom
feature/dynamicCalibration_multiSensorCalibration

Conversation

@JakubFara

@JakubFara JakubFara commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR expands DynamicCalibration from a fixed stereo-oriented flow into a more flexible multi-input calibration path with improved host/device APIs,
broader calibration-frame support, and updated Python bindings/examples.

What Changed

DynamicCalibration node

  • Generalized DynamicCalibration to work with an arbitrary number of synced inputs instead of assuming only two fixed sensors.

  • Added support for an additional rgb input and exposed the internal sync/inputs interface for more flexible pipeline wiring, including Python.

  • Reworked initialization and calibration flow to track connected sensors explicitly, preserve socket ordering from calibration data, and support multi-
    sensor baseline handling.

  • Added keepCameraCenters support to calibration commands so callers can choose whether sensor centers remain fixed during recalibration.

  • Improved runtime behavior when the sync node has no input by exiting cleanly instead of throwing.

Calibration handling

  • Added CalibrationHandler::overwriteCameraExtrinsics(...) for updating existing extrinsics in-place.

  • Extended housing-origin APIs:

    • getHousingToHousingOrigin(...) now supports explicit output units.
    • Added setHousingToHousingOriginExtrinsics(...).
  • Updated dynamic calibration internals to work with either camera-based or housing-based calibration frames.

Results and API updates

  • Added per-socket coverage output:

    • CoverageData::coveragePerCell keyed by CameraBoardSocket
  • Marked older stereo-only result fields as deprecated:

    • coveragePerCellA
    • coveragePerCellB
    • deprecated legacy quality-difference fields in CalibrationQuality::Data
  • Extended DynamicCalibrationControl commands:

    • calibrate(force, keepCameraCenters)
    • startCalibration(loadImagePeriod, calibrationPeriod, keepCameraCenters)

Python bindings and examples

  • Updated Python bindings to expose:
    • keepCameraCenters
    • CoverageData.coveragePerCell
    • DynamicCalibration.inputs
    • DynamicCalibration.sync

Summary by CodeRabbit

  • New Features
    • Added keepCameraCenters (default true) to dynamic calibration commands and execution flow.
    • Added socket-keyed CoverageData.coveragePerCell and CalibrationQuality’s pairwiseRotationDifference.
    • Exposed read-only DynamicCalibration properties: sync and inputs.
    • Added SE(3) matrix utilities (create/extract/invert).
  • Deprecations
    • coveragePerCellA / coveragePerCellB deprecated in favor of coveragePerCell.
    • CalibrationQuality.rotationChange marked deprecated.
  • Bug Fixes
    • Made depth-error-difference reporting defensive when metrics are missing.
    • Improved Sync behavior by returning early instead of throwing on empty inputs.
  • Documentation
    • Updated Dynamic Calibration READMEs to clarify coveragePerCell terminology.

@JakubFara JakubFara requested a review from MaticTonin June 8, 2026 13:41
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Extends dynamic calibration from a fixed stereo (A/B) model to a flexible multi-sensor list. Adds a keepCameraCenters boolean to Calibrate and StartCalibration command payloads, factory methods, and bindings. Replaces per-socket-pair coverage fields with a socket-keyed coveragePerCell map, adds pairwiseRotationDifference to quality data, and introduces SE(3) matrix utility functions.

Changes

Dynamic Calibration Multi-Sensor Refactor

Layer / File(s) Summary
Calibration command keepCameraCenters parameter
include/depthai/pipeline/datatype/DynamicCalibrationControl.hpp, bindings/python/src/pipeline/datatype/DynamicCalibrationControlBindings.cpp, bindings/python/tests/dynamic_calibration_test.py, tests/src/onhost_tests/dynamic_calibration_test.cpp
keepCameraCenters: bool = true added to Calibrate and StartCalibration command structs, serialization, C++ factory methods, Python bindings, and validated in host and Python tests.
CoverageData and CalibrationQuality socket-keyed fields
include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp, bindings/python/src/pipeline/datatype/DynamicCalibrationResultsBindings.cpp, bindings/python/tests/dynamic_calibration_test.py, examples/cpp/DynamicCalibration/README.md, examples/python/DynamicCalibration/README.md
Adds coveragePerCell unordered map keyed by CameraBoardSocket (deprecates coveragePerCellA/B), adds pairwiseRotationDifference and dataConfidence fields, updates serialization, Python bindings, tests, and documentation.
SE(3) matrix transformation utilities
include/depthai/utility/matrixOps.hpp, src/utility/matrixOps.cpp
Declares and implements createTransformationMatrix, extractRotationMatrix, extractTranslationVector, and invertSe3 overloads operating on std::vector-based matrix representations.
DynamicCalibrationNode header multi-sensor support
include/depthai/pipeline/node/DynamicCalibrationNode.hpp
Introduces ConnectedSensor struct, rgb input stream, daiSocketBase variant, socket-to-extrinsics container, startCalibrationCommand optional, postBuildStage() override, findConnectedSensor helper, and keepCameraCenters parameter on runCalibration.
DclUtils API refactoring and Sync fix
src/pipeline/node/DynamicCalibrationUtils.hpp, src/pipeline/node/Sync.cpp
Replaces dual-socket convertDaiCalibrationToDcl overload with single-handle overload taking a boardSocketBase variant, adds setHousingToDai helper, updates convertDclCalibrationToDai signature, and changes Sync::run empty-inputs path from exception to debug log with early return.
DynamicCalibrationNode implementation refactor
src/pipeline/node/DynamicCalibrationNode.cpp
Rewrites initializePipeline to discover connected sensors from MessageGroup frame names, refactors runLoadImage for variable sensor count, updates coverage mapping to coveragePerCell per socket, expands runCalibration for keepCameraCenters and pairwise quality computation, refactors command dispatch around startCalibrationCommand, and adds postBuildStage.
Python bindings for node properties
bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp
Exposes DynamicCalibration.sync and DynamicCalibration.inputs as read-only pybind properties with reference_internal return policy.
Example scripts and documentation
examples/python/DynamicCalibration/calibration_dynamic.py, examples/cpp/DynamicCalibration/README.md, examples/python/DynamicCalibration/README.md
Updates performance-mode command to enum-qualified PerformanceMode.OPTIMIZE_PERFORMANCE, adds defensive length guard before printing depth error differences, and updates coveragePerCell documentation.
On-device calibration tests
tests/src/ondevice_tests/dynamic_calibration_test.cpp
Refactors getHandler to support optional housing-base configuration, adds housing-base recalibration and metrics tests, captures pre/post translation vectors, tightens rotation tolerances, and comments out CalibrationQuality request blocks.
Dynamic calibration library version bump
cmake/Depthai/DepthaiDynamicCalibrationConfig.cmake
Updates DEPTHAI_DYNAMIC_CALIBRATION_VERSION to a new upstream commit hash.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • luxonis/depthai-core#1783: Overlaps in Dynamic Calibration DCL integration—modifies DclUtils conversion paths and calibration-quality computation in src/pipeline/node/DynamicCalibrationNode.cpp that this PR significantly refactors.
  • luxonis/depthai-core#1795: Both PRs modify DclUtils::convertDaiCalibrationToDcl(...) conversion pipeline in DynamicCalibrationNode.cpp to refactor calibration-handle creation with intrinsics and distortion overrides.

Suggested labels

testable

Suggested reviewers

  • pheec
  • MaticTonin
  • moratom

Poem

🐇 Hop, hop, through sensors galore,
No more just A and B—we want more!
keepCameraCenters holds the line,
SE(3) math helps everything align.
Each socket mapped, each coverage told,
This bunny's calibration story: bold! 📐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.97% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Dynamic calibration - multi sensor calibration' directly reflects the main objective: expanding the DynamicCalibration node to support multi-sensor calibration instead of fixed stereo-only flow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dynamicCalibration_multiSensorCalibration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MaticTonin MaticTonin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a good PR, I like it and most of API stays almost the same it seems to me.

Comment thread bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp Outdated
Comment thread include/depthai/device/CalibrationHandler.hpp Outdated
Comment thread include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
Comment thread src/device/CalibrationHandler.cpp Outdated
Comment thread src/device/CalibrationHandler.cpp
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp Outdated
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp
Comment thread src/pipeline/node/Sync.cpp Outdated
Comment thread tests/src/ondevice_tests/dynamic_calibration_test.cpp Outdated
Comment thread tests/src/ondevice_tests/dynamic_calibration_test.cpp Outdated
@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch 2 times, most recently from eba0159 to 6ab1f10 Compare June 9, 2026 09:28
@JakubFara JakubFara marked this pull request as ready for review June 9, 2026 09:29
@JakubFara JakubFara marked this pull request as draft June 9, 2026 09:32
@JakubFara

Copy link
Copy Markdown
Collaborator Author

Later I will separate this draft into 2 PRs:

  • CalibrationHandler update
  • Multisensor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
include/depthai/pipeline/node/DynamicCalibrationNode.hpp (1)

21-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Typo in documentation comment.

The comment starts with /*u instead of /** for Doxygen documentation.

-/*u
+/**
  * `@brief` Dynamic calibration node. Performs calibration check and dynamically calibrates the device
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/depthai/pipeline/node/DynamicCalibrationNode.hpp` around lines 21 -
23, Update the Doxygen comment opener in DynamicCalibrationNode.hpp so it starts
with the proper /** sequence (not "/*u"); locate the block comment above the
DynamicCalibrationNode declaration (the comment that begins with "/*u" and reads
"`@brief` Dynamic calibration node...") and change it to a standard Doxygen doc
comment /** to ensure the documentation is picked up correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp`:
- Around line 43-50: Update the deprecation comments for the legacy fields
coveragePerCellA and coveragePerCellB to use the Doxygen-style format consistent
with rotationChange (e.g., /** `@deprecated` Use coveragePerCell keyed by
CameraBoardSocket instead. */) so documentation generators pick them up
uniformly; locate the declarations of coveragePerCellA and coveragePerCellB in
DynamicCalibrationResults.hpp and replace the // `@deprecated` lines with the /**
`@deprecated` ... */ style while keeping the same message.

In `@src/pipeline/node/DynamicCalibrationNode.cpp`:
- Around line 646-650: The handler for DC::Commands::CalibrationQuality in
DynamicCalibrationNode.cpp currently only logs and returns
ErrorCode::INVALID_COMMAND; implement proper handling in the function that
processes cmd so it computes or retrieves the calibration quality and publishes
a DatatypeEnum::CalibrationQuality message to the node's qualityOutput (or, if
there is existing helper logic, call the node's computeCalibrationQuality /
evaluateCalibrationQuality routine). Use the existing command variant
(std::get<DC::Commands::CalibrationQuality>) named c, preserve any fields such
as c.force when deciding to recompute, and replace the
ErrorCode::INVALID_COMMAND return with appropriate success/failure ErrorCode
after publishing; alternatively, if this command is no longer valid,
remove/deprecate the DC::Commands::CalibrationQuality handling and the exposed
qualityOutput port to keep behavior consistent.

In `@tests/src/ondevice_tests/dynamic_calibration_test.cpp`:
- Line 485: Calls to stereoImageToMessageGroup are missing the required
CalibrationHandler parameter; update every call (including the one that
constructs 'group' using makeFilename("data/LeftCam_", 0, helper) /
makeFilename("data/RightCam_", 0, helper)) to pass the existing calibration
object (the variable named calibration) as the third argument so
stereoImageToMessageGroup(leftPath, rightPath, calibration) can use
setFrameTransformation() to set intrinsics/distortion/transform; alternatively,
if omission was intentional, add an overload or default parameter to
stereoImageToMessageGroup that accepts no handler and applies the desired
default behavior.

---

Outside diff comments:
In `@include/depthai/pipeline/node/DynamicCalibrationNode.hpp`:
- Around line 21-23: Update the Doxygen comment opener in
DynamicCalibrationNode.hpp so it starts with the proper /** sequence (not
"/*u"); locate the block comment above the DynamicCalibrationNode declaration
(the comment that begins with "/*u" and reads "`@brief` Dynamic calibration
node...") and change it to a standard Doxygen doc comment /** to ensure the
documentation is picked up correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 39dc4bd4-a2b4-4235-9db5-6dabba8ed233

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0fa35 and 6ab1f10.

📒 Files selected for processing (19)
  • bindings/python/src/pipeline/datatype/DynamicCalibrationControlBindings.cpp
  • bindings/python/src/pipeline/datatype/DynamicCalibrationResultsBindings.cpp
  • bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp
  • bindings/python/tests/dynamic_calibration_test.py
  • examples/cpp/DynamicCalibration/README.md
  • examples/python/DynamicCalibration/README.md
  • examples/python/DynamicCalibration/calibration_dynamic.py
  • include/depthai/device/CalibrationHandler.hpp
  • include/depthai/pipeline/datatype/DynamicCalibrationControl.hpp
  • include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
  • include/depthai/pipeline/node/DynamicCalibrationNode.hpp
  • include/depthai/utility/matrixOps.hpp
  • src/device/CalibrationHandler.cpp
  • src/pipeline/node/DynamicCalibrationNode.cpp
  • src/pipeline/node/DynamicCalibrationUtils.hpp
  • src/pipeline/node/Sync.cpp
  • src/utility/matrixOps.cpp
  • tests/src/ondevice_tests/dynamic_calibration_test.cpp
  • tests/src/onhost_tests/dynamic_calibration_test.cpp
📜 Review details
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-24T22:39:04.364Z
Learnt from: MaticTonin
Repo: luxonis/depthai-core PR: 1732
File: src/pipeline/Pipeline.cpp:705-705
Timestamp: 2026-03-24T22:39:04.364Z
Learning: Do not flag the `!= ""` part of the auto-calibration condition as redundant when it appears in `PipelineImpl::build()` (or closely related pipeline build logic). If the code uses `utility::getEnvAs<std::string>(..., default)` with a default such as `"ON_START"`, the explicit empty-string guard may still be intentional to treat an explicitly empty env var as “OFF/disabled” (or to avoid special-casing elsewhere). Only consider removing `!= ""` if the codebase has an explicit, enforceable guarantee that `DEPTHAI_AUTOCALIBRATION` can never be set to an empty string (e.g., via validated parsing/CI checks); otherwise, keep the guard.

Applied to files:

  • src/pipeline/node/Sync.cpp
  • src/pipeline/node/DynamicCalibrationNode.cpp
📚 Learning: 2026-05-28T13:36:26.383Z
Learnt from: moratom
Repo: luxonis/depthai-core PR: 1812
File: examples/cpp/ImageManip/image_manip_remap.cpp:51-52
Timestamp: 2026-05-28T13:36:26.383Z
Learning: In depthai-core example code, do not set `ImageManip::Backend::GPU` unconditionally. The GPU backend is only available on RVC4 (not RVC2). Prefer leaving the backend as the default, or comment out the GPU backend selection and add a clear note explaining it is RVC4-only support (so the example won’t fail or mislead on RVC2).

Applied to files:

  • examples/python/DynamicCalibration/calibration_dynamic.py
🪛 Cppcheck (2.20.0)
tests/src/onhost_tests/dynamic_calibration_test.cpp

[error] 18-18: There is an unknown macro here somewhere. Configuration is required. If DEPTHAI_NLOHMANN_DEFINE_TYPE_INTRUSIVE is a macro then please configure it.

(unknownMacro)

src/pipeline/node/DynamicCalibrationNode.cpp

[error] 78-78: There is an unknown macro here somewhere. Configuration is required. If DEPTHAI_NLOHMANN_DEFINE_TYPE_INTRUSIVE is a macro then please configure it.

(unknownMacro)

🔇 Additional comments (60)
include/depthai/pipeline/datatype/DynamicCalibrationControl.hpp (4)

54-59: LGTM!


86-95: LGTM!


171-173: LGTM!


206-210: LGTM!

bindings/python/src/pipeline/datatype/DynamicCalibrationControlBindings.cpp (4)

40-43: LGTM!


53-60: LGTM!


90-95: LGTM!


106-112: LGTM!

bindings/python/tests/dynamic_calibration_test.py (5)

43-56: LGTM!


73-89: LGTM!


143-149: LGTM!


159-165: LGTM!


212-235: LGTM!

tests/src/onhost_tests/dynamic_calibration_test.cpp (2)

8-19: LGTM!


28-39: LGTM!

examples/python/DynamicCalibration/calibration_dynamic.py (2)

52-56: LGTM!


112-119: LGTM!

include/depthai/utility/matrixOps.hpp (1)

9-9: LGTM!

Also applies to: 39-39, 51-52, 57-58

src/utility/matrixOps.cpp (4)

269-274: LGTM!


436-450: LGTM!


452-464: LGTM!


466-490: LGTM!

include/depthai/device/CalibrationHandler.hpp (3)

30-32: LGTM!

Also applies to: 778-778


653-656: LGTM!


768-773: LGTM!

src/device/CalibrationHandler.cpp (3)

612-686: LGTM!


1122-1145: LGTM!


1183-1200: LGTM!

tests/src/ondevice_tests/dynamic_calibration_test.cpp (5)

554-554: Same handler omission pattern as non-housing test.

Lines 554 and 560 also call stereoImageToMessageGroup without the calibration handler. If intentional for testing purposes, consider adding a brief comment explaining why transformation data is omitted for these frames.

Also applies to: 560-560


160-203: LGTM!


519-531: LGTM!


581-584: 💤 Low value

Very tight rotation threshold may cause flaky tests.

The housing-base test uses thresholdRotation = 1e-7f (line 581), which is 10,000× tighter than the non-housing test's 1e-3f (line 514). While synthetic data may achieve this precision, numerical differences across platforms or library versions could cause intermittent failures. Consider whether this strictness is intentional or if a slightly relaxed threshold (e.g., 1e-5f) would be more robust while still validating correctness.


601-637: LGTM!

include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp (2)

86-104: LGTM!


68-68: Ensure std::hash<dai::CameraBoardSocket> (or a custom hasher) exists for unordered_map keys

CameraBoardSocket is a scoped enum class (include/depthai/common/CameraBoardSocket.hpp), and std::unordered_map<CameraBoardSocket, ...> (e.g., coveragePerCell in DynamicCalibrationResults.hpp) relies on std::hash<CameraBoardSocket> by default. Make sure the project provides a std::hash<dai::CameraBoardSocket> specialization or uses an unordered_map hasher explicitly for portability across standard libraries.

bindings/python/src/pipeline/datatype/DynamicCalibrationResultsBindings.cpp (1)

24-24: LGTM!

include/depthai/pipeline/node/DynamicCalibrationNode.hpp (3)

137-149: LGTM!


105-120: LGTM!


131-131: LGTM!

Also applies to: 173-173, 187-188, 205-213

src/pipeline/node/DynamicCalibrationUtils.hpp (1)

13-27: LGTM!

examples/cpp/DynamicCalibration/README.md (1)

22-23: LGTM!

examples/python/DynamicCalibration/README.md (1)

22-23: LGTM!

src/pipeline/node/Sync.cpp (1)

52-53: LGTM!

src/pipeline/node/DynamicCalibrationNode.cpp (16)

24-100: LGTM!


115-123: LGTM!


142-154: LGTM!


156-175: LGTM!


292-311: LGTM!


313-334: LGTM!


395-411: LGTM!


413-429: LGTM!


432-476: LGTM!


478-517: LGTM!


519-596: LGTM!


599-626: LGTM!


639-644: LGTM!

Also applies to: 652-657, 670-682, 686-692, 699-704


711-754: LGTM!


756-780: LGTM!


348-376: Recheck keepCameraCenters vs keptBaselineEdges semantics (possible inversion)

DynamicCalibrationControl.hpp documents keepCameraCenters as “Keep the position of the cameras”, but DynamicCalibrationNode.cpp builds keptBaselineEdges only when keepCameraCenters == false (otherwise it passes std::nullopt to dynCalibImpl.findNewCalibration). This appears counterintuitive unless the DCL API defines keptBaselineEdges in the opposite sense.

Please confirm in the DCL findNewCalibration(..., keptBaselineEdges) contract whether:

  • passing std::nullopt keeps camera centers (translations) fixed, and passing a vector instead allows shifting while only constraining baseline lengths
  • or, if not, align the condition/parameter naming (and/or add a comment) so keepCameraCenters matches actual behavior.
bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp (1)

34-37: LGTM!

Comment thread include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp Outdated
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp
Comment thread tests/src/ondevice_tests/dynamic_calibration_test.cpp Outdated
@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch 4 times, most recently from eb5c75c to 3355008 Compare June 10, 2026 11:52
@MaticTonin

Copy link
Copy Markdown
Collaborator

Testing observations:

  • in running python3 examples/python/DynamicCalibration/calibration_dynamic.py you get
Successfully calibrated
Rotation difference: || r_current - r_new || = 0.00 deg
Mean Sampson error achievable = 0.259 px
Mean Sampson error current    = 0.000 px

so empty Mean Sampson error current and empty Rotation Difference.
But overall works, nice, I woudl add the
rgbd.py example to the repo mybe.

@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch 8 times, most recently from 436ca31 to 73b3759 Compare June 19, 2026 12:31
@JakubFara JakubFara marked this pull request as ready for review June 19, 2026 12:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmake/Depthai/DepthaiDynamicCalibrationConfig.cmake`:
- Line 2: Before merging this change to DEPTHAI_DYNAMIC_CALIBRATION_VERSION with
the new hash 848ba66de1b880ce6cbcdf54232323f20743cc1a, verify that the
corresponding artifact has been published and is accessible. Confirm the
artifact is available on the artifact server for all required platform
architectures (x86_64-ubuntu-20.04, x86_64-macos, armv7l, etc.) and that builds
with DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT enabled can successfully download it.
If the artifact is not yet available publicly, ensure that CI/deployment uses
the DEPTHAI_DYNAMIC_CALIBRATION_PATH override with a local archive, or wait
until the artifact is published before merging this version update.

In `@tests/src/ondevice_tests/dynamic_calibration_test.cpp`:
- Around line 333-350: Add TODO comments or issue tracking references before the
commented-out test blocks for CalibrationQuality commands (both with force=true
and force=false variations). Place these tracking comments above the
commented-out code sections to clearly document why they are disabled and
indicate that they should be re-enabled once the corresponding command handler
implementation is complete. This will help future developers understand the
intent and when these tests should be activated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6ea8ec88-9e10-45a7-bdfc-1d7edaa6247e

📥 Commits

Reviewing files that changed from the base of the PR and between 6ab1f10 and 73b3759.

📒 Files selected for processing (18)
  • bindings/python/src/pipeline/datatype/DynamicCalibrationControlBindings.cpp
  • bindings/python/src/pipeline/datatype/DynamicCalibrationResultsBindings.cpp
  • bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp
  • bindings/python/tests/dynamic_calibration_test.py
  • cmake/Depthai/DepthaiDynamicCalibrationConfig.cmake
  • examples/cpp/DynamicCalibration/README.md
  • examples/python/DynamicCalibration/README.md
  • examples/python/DynamicCalibration/calibration_dynamic.py
  • include/depthai/pipeline/datatype/DynamicCalibrationControl.hpp
  • include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
  • include/depthai/pipeline/node/DynamicCalibrationNode.hpp
  • include/depthai/utility/matrixOps.hpp
  • src/pipeline/node/DynamicCalibrationNode.cpp
  • src/pipeline/node/DynamicCalibrationUtils.hpp
  • src/pipeline/node/Sync.cpp
  • src/utility/matrixOps.cpp
  • tests/src/ondevice_tests/dynamic_calibration_test.cpp
  • tests/src/onhost_tests/dynamic_calibration_test.cpp
📜 Review details
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-24T22:39:04.364Z
Learnt from: MaticTonin
Repo: luxonis/depthai-core PR: 1732
File: src/pipeline/Pipeline.cpp:705-705
Timestamp: 2026-03-24T22:39:04.364Z
Learning: Do not flag the `!= ""` part of the auto-calibration condition as redundant when it appears in `PipelineImpl::build()` (or closely related pipeline build logic). If the code uses `utility::getEnvAs<std::string>(..., default)` with a default such as `"ON_START"`, the explicit empty-string guard may still be intentional to treat an explicitly empty env var as “OFF/disabled” (or to avoid special-casing elsewhere). Only consider removing `!= ""` if the codebase has an explicit, enforceable guarantee that `DEPTHAI_AUTOCALIBRATION` can never be set to an empty string (e.g., via validated parsing/CI checks); otherwise, keep the guard.

Applied to files:

  • src/pipeline/node/Sync.cpp
  • src/pipeline/node/DynamicCalibrationNode.cpp
📚 Learning: 2026-05-28T13:36:26.383Z
Learnt from: moratom
Repo: luxonis/depthai-core PR: 1812
File: examples/cpp/ImageManip/image_manip_remap.cpp:51-52
Timestamp: 2026-05-28T13:36:26.383Z
Learning: In depthai-core example code, do not set `ImageManip::Backend::GPU` unconditionally. The GPU backend is only available on RVC4 (not RVC2). Prefer leaving the backend as the default, or comment out the GPU backend selection and add a clear note explaining it is RVC4-only support (so the example won’t fail or mislead on RVC2).

Applied to files:

  • examples/python/DynamicCalibration/calibration_dynamic.py
🪛 Cppcheck (2.21.0)
tests/src/onhost_tests/dynamic_calibration_test.cpp

[error] 18-18: There is an unknown macro here somewhere. Configuration is required. If DEPTHAI_NLOHMANN_DEFINE_TYPE_INTRUSIVE is a macro then please configure it.

(unknownMacro)

src/pipeline/node/DynamicCalibrationNode.cpp

[error] 78-78: There is an unknown macro here somewhere. Configuration is required. If DEPTHAI_NLOHMANN_DEFINE_TYPE_INTRUSIVE is a macro then please configure it.

(unknownMacro)

🔇 Additional comments (28)
include/depthai/utility/matrixOps.hpp (1)

9-9: LGTM!

Also applies to: 39-39, 51-59

src/utility/matrixOps.cpp (1)

138-147: LGTM!

Also applies to: 280-285, 447-475, 477-501

bindings/python/src/pipeline/node/DynamicCalibrationBindings.cpp (1)

34-37: LGTM!

examples/python/DynamicCalibration/calibration_dynamic.py (1)

54-54: LGTM!

Also applies to: 112-119

include/depthai/pipeline/datatype/DynamicCalibrationControl.hpp (1)

55-59: LGTM!

Also applies to: 87-95, 171-173, 206-210

bindings/python/src/pipeline/datatype/DynamicCalibrationControlBindings.cpp (1)

41-43: LGTM!

Also applies to: 54-60, 94-95, 111-112

bindings/python/tests/dynamic_calibration_test.py (1)

45-55: LGTM!

Also applies to: 75-88, 146-147, 162-163, 218-231, 245-253, 304-312

tests/src/onhost_tests/dynamic_calibration_test.cpp (1)

9-19: LGTM!

Also applies to: 34-39

examples/cpp/DynamicCalibration/README.md (1)

22-23: LGTM!

examples/python/DynamicCalibration/README.md (1)

22-23: LGTM!

src/pipeline/node/DynamicCalibrationNode.cpp (8)

662-665: CalibrationQuality command handler remains unimplemented.

This handler logs and returns INVALID_COMMAND while the node exposes a qualityOutput port. This was flagged in a previous review and appears to still be pending implementation.


23-98: LGTM!


100-168: LGTM!


285-446: LGTM!


448-534: LGTM!


536-642: LGTM!


644-661: LGTM!

Also applies to: 666-725


727-796: LGTM!

include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp (2)

46-71: LGTM!


90-110: LGTM!

bindings/python/src/pipeline/datatype/DynamicCalibrationResultsBindings.cpp (1)

24-24: LGTM!

Also applies to: 35-36, 56-57

include/depthai/pipeline/node/DynamicCalibrationNode.hpp (1)

105-120: LGTM!

Also applies to: 131-149, 173-173, 204-212

src/pipeline/node/DynamicCalibrationUtils.hpp (1)

13-27: LGTM!

src/pipeline/node/Sync.cpp (1)

51-54: LGTM!

tests/src/ondevice_tests/dynamic_calibration_test.cpp (4)

160-203: LGTM!


480-537: LGTM!


539-601: LGTM!


603-641: LGTM!

Comment thread cmake/Depthai/DepthaiDynamicCalibrationConfig.cmake Outdated
Comment thread tests/src/ondevice_tests/dynamic_calibration_test.cpp Outdated
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp Outdated
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp
Comment thread src/pipeline/node/DynamicCalibrationNode.cpp
Comment thread include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
Comment thread include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
Comment thread include/depthai/pipeline/datatype/DynamicCalibrationResults.hpp
Comment thread examples/python/DynamicCalibration/calibration_dynamic.py
Comment thread examples/python/DynamicCalibration/calibration_dynamic.py
Comment thread cmake/Depthai/DepthaiDynamicCalibrationConfig.cmake Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
include/depthai/pipeline/node/DynamicCalibrationNode.hpp (1)

21-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Typo in doxygen comment opening.

The comment starts with /*u instead of /**, which breaks doxygen documentation parsing.

📝 Proposed fix
-/*u
+/**
  * `@brief` Dynamic calibration node. Performs calibration check and dynamically calibrates the device
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/depthai/pipeline/node/DynamicCalibrationNode.hpp` around lines 21 -
23, The Doxygen comment opening for the DynamicCalibrationNode class contains a
typo with `/*u` instead of the correct `/**` syntax required for Doxygen
documentation parsing. Fix this by changing the opening from `/*u` to `/**` to
ensure the documentation comment is properly recognized and parsed by Doxygen.
src/pipeline/node/DynamicCalibrationNode.cpp (2)

508-510: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistent error handling: exception thrown instead of returning error code.

Other error conditions in this class (e.g., runLoadImage, runCalibration, initializePipeline) return ErrorCode values, allowing the caller to handle failures gracefully. Throwing an exception here could crash the node instead of allowing recovery.

🛠️ Proposed fix
     if(!resultCoverage.passed()) {
-        throw std::runtime_error("Coverage check failed!");
+        logger->error("Coverage check failed: {}", resultCoverage.errorMessage());
+        return DynamicCalibration::ErrorCode::CALIBRATION_FAILED;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pipeline/node/DynamicCalibrationNode.cpp` around lines 508 - 510, The
error handling in the coverage check block (where resultCoverage.passed() is
checked) is inconsistent with other error handling in the DynamicCalibrationNode
class. Instead of throwing a std::runtime_error exception, return an appropriate
ErrorCode value like the runLoadImage, runCalibration, and initializePipeline
methods do. Replace the throw statement with a return of an error code that
indicates the coverage check failure, allowing callers to handle the error
gracefully rather than experiencing an exception-based crash.

741-768: ⚠️ Potential issue | 🟡 Minor

calibrationPeriod is only used for initial delay, not for ongoing calibration frequency.

The StartCalibration command includes both loadImagePeriod and calibrationPeriod, documented as "Seconds between calibration cycles." However, calibrationPeriod is used only at initialization (line 782) to set the first time point: previousLoadingTimeFloat = std::chrono::steady_clock::now() + std::chrono::duration<float>(calibrationPeriod). After that initial delay, loadImagePeriod (line 748) controls both image loading and calibration frequency, not calibrationPeriod.

This means calibrationPeriod does not achieve its documented purpose of controlling how often calibration cycles run independently. If separate control of calibration frequency was intended, the code should check calibrationPeriod in the loop (similar to line 748), not just use loadImagePeriod.

Additionally, after the first successful calibration (lines 765–766), startCalibrationCommand is reset, stopping the entire process. The parameter's intended purpose of periodic calibration is not realized by the current implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pipeline/node/DynamicCalibrationNode.cpp` around lines 741 - 768, The
calibrationPeriod parameter is only used at initialization for the initial delay
but is not applied during the periodic calibration loop, where loadImagePeriod
controls both image loading and calibration frequency. To fix this, introduce a
separate timing variable for calibration (similar to
previousLoadingAndCalibrationTime) and add an independent elapsed time check
against calibrationPeriod before calling runCalibration. Additionally, the
startCalibrationCommand.reset() call after successful calibration stops the
periodic process prematurely; modify the logic to not reset the command so that
calibration continues to run periodically based on calibrationPeriod until
explicitly stopped by external request.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@include/depthai/pipeline/node/DynamicCalibrationNode.hpp`:
- Around line 21-23: The Doxygen comment opening for the DynamicCalibrationNode
class contains a typo with `/*u` instead of the correct `/**` syntax required
for Doxygen documentation parsing. Fix this by changing the opening from `/*u`
to `/**` to ensure the documentation comment is properly recognized and parsed
by Doxygen.

In `@src/pipeline/node/DynamicCalibrationNode.cpp`:
- Around line 508-510: The error handling in the coverage check block (where
resultCoverage.passed() is checked) is inconsistent with other error handling in
the DynamicCalibrationNode class. Instead of throwing a std::runtime_error
exception, return an appropriate ErrorCode value like the runLoadImage,
runCalibration, and initializePipeline methods do. Replace the throw statement
with a return of an error code that indicates the coverage check failure,
allowing callers to handle the error gracefully rather than experiencing an
exception-based crash.
- Around line 741-768: The calibrationPeriod parameter is only used at
initialization for the initial delay but is not applied during the periodic
calibration loop, where loadImagePeriod controls both image loading and
calibration frequency. To fix this, introduce a separate timing variable for
calibration (similar to previousLoadingAndCalibrationTime) and add an
independent elapsed time check against calibrationPeriod before calling
runCalibration. Additionally, the startCalibrationCommand.reset() call after
successful calibration stops the periodic process prematurely; modify the logic
to not reset the command so that calibration continues to run periodically based
on calibrationPeriod until explicitly stopped by external request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e2a6a88c-4ad8-420e-8deb-142598f7efaa

📥 Commits

Reviewing files that changed from the base of the PR and between 73b3759 and c41350f.

📒 Files selected for processing (2)
  • include/depthai/pipeline/node/DynamicCalibrationNode.hpp
  • src/pipeline/node/DynamicCalibrationNode.cpp
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-24T22:39:04.364Z
Learnt from: MaticTonin
Repo: luxonis/depthai-core PR: 1732
File: src/pipeline/Pipeline.cpp:705-705
Timestamp: 2026-03-24T22:39:04.364Z
Learning: Do not flag the `!= ""` part of the auto-calibration condition as redundant when it appears in `PipelineImpl::build()` (or closely related pipeline build logic). If the code uses `utility::getEnvAs<std::string>(..., default)` with a default such as `"ON_START"`, the explicit empty-string guard may still be intentional to treat an explicitly empty env var as “OFF/disabled” (or to avoid special-casing elsewhere). Only consider removing `!= ""` if the codebase has an explicit, enforceable guarantee that `DEPTHAI_AUTOCALIBRATION` can never be set to an empty string (e.g., via validated parsing/CI checks); otherwise, keep the guard.

Applied to files:

  • src/pipeline/node/DynamicCalibrationNode.cpp
🔇 Additional comments (15)
include/depthai/pipeline/node/DynamicCalibrationNode.hpp (5)

105-106: LGTM!

Also applies to: 117-120


131-150: LGTM!


163-163: LGTM!


172-174: LGTM!


205-213: LGTM!

src/pipeline/node/DynamicCalibrationNode.cpp (10)

23-98: LGTM!


113-168: LGTM!


285-327: LGTM!


329-446: LGTM!


449-493: LGTM!


536-642: LGTM!


644-673: LGTM!


697-709: LGTM!


711-726: LGTM!


773-797: LGTM!

@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch from c41350f to 3d3cc57 Compare June 22, 2026 08:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/pipeline/node/DynamicCalibrationNode.cpp (2)

508-510: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exception thrown in function that returns ErrorCode will crash the node thread.

The function signature returns ErrorCode, but this path throws an exception instead of returning an error code. This inconsistency will crash the node thread when coverage computation fails, rather than allowing graceful error handling.

🐛 Proposed fix to return error code consistently
     if(!resultCoverage.passed()) {
-        throw std::runtime_error("Coverage check failed!");
+        logger->error("Coverage check failed: {}", resultCoverage.errorMessage());
+        return DynamicCalibration::ErrorCode::CALIBRATION_FAILED;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pipeline/node/DynamicCalibrationNode.cpp` around lines 508 - 510, The
coverage check validation in DynamicCalibrationNode.cpp throws an exception when
resultCoverage.passed() returns false, but the function returns ErrorCode and
should handle errors consistently through return values instead of exceptions.
Replace the throw std::runtime_error statement with a return statement that
returns an appropriate ErrorCode value to allow graceful error handling by the
caller instead of crashing the node thread.

680-682: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Return value of computeCoverage() is ignored.

Once the throw in computeCoverage() is replaced with an error return, the error will be silently dropped here. Consider propagating the error or at minimum logging if coverage computation fails after a successful image load.

♻️ Suggested fix to propagate error
         auto error = runLoadImage(true);
-        computeCoverage();
-        return error;
+        if(error != ErrorCode::OK) {
+            return error;
+        }
+        return computeCoverage();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pipeline/node/DynamicCalibrationNode.cpp` around lines 680 - 682, The
return value from the computeCoverage() function call is being ignored after a
successful runLoadImage(true) operation. Capture the error returned by
computeCoverage() and either propagate it by returning it (potentially combined
with the error from runLoadImage if needed) or log it to ensure that coverage
computation failures are not silently dropped. This will ensure error handling
consistency throughout the function flow in DynamicCalibrationNode.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pipeline/node/DynamicCalibrationNode.cpp`:
- Around line 508-510: The coverage check validation in
DynamicCalibrationNode.cpp throws an exception when resultCoverage.passed()
returns false, but the function returns ErrorCode and should handle errors
consistently through return values instead of exceptions. Replace the throw
std::runtime_error statement with a return statement that returns an appropriate
ErrorCode value to allow graceful error handling by the caller instead of
crashing the node thread.
- Around line 680-682: The return value from the computeCoverage() function call
is being ignored after a successful runLoadImage(true) operation. Capture the
error returned by computeCoverage() and either propagate it by returning it
(potentially combined with the error from runLoadImage if needed) or log it to
ensure that coverage computation failures are not silently dropped. This will
ensure error handling consistency throughout the function flow in
DynamicCalibrationNode.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5d32a37-c3f7-46dd-aec8-2ece7db079c7

📥 Commits

Reviewing files that changed from the base of the PR and between c41350f and 3d3cc57.

📒 Files selected for processing (1)
  • src/pipeline/node/DynamicCalibrationNode.cpp
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-24T22:39:04.364Z
Learnt from: MaticTonin
Repo: luxonis/depthai-core PR: 1732
File: src/pipeline/Pipeline.cpp:705-705
Timestamp: 2026-03-24T22:39:04.364Z
Learning: Do not flag the `!= ""` part of the auto-calibration condition as redundant when it appears in `PipelineImpl::build()` (or closely related pipeline build logic). If the code uses `utility::getEnvAs<std::string>(..., default)` with a default such as `"ON_START"`, the explicit empty-string guard may still be intentional to treat an explicitly empty env var as “OFF/disabled” (or to avoid special-casing elsewhere). Only consider removing `!= ""` if the codebase has an explicit, enforceable guarantee that `DEPTHAI_AUTOCALIBRATION` can never be set to an empty string (e.g., via validated parsing/CI checks); otherwise, keep the guard.

Applied to files:

  • src/pipeline/node/DynamicCalibrationNode.cpp
🔇 Additional comments (9)
src/pipeline/node/DynamicCalibrationNode.cpp (9)

23-98: LGTM!


100-147: LGTM!


149-247: LGTM!


285-327: LGTM!


329-446: LGTM!


449-493: LGTM!


512-534: LGTM!


536-642: LGTM!


665-667: LGTM!

Also applies to: 774-798

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pipeline/node/DynamicCalibrationNode.cpp (1)

739-766: ⚠️ Potential issue | 🟠 Major

calibrationPeriod is unused for rate limiting; calibration runs only once per command.

The StartCalibration command accepts calibrationPeriod as a parameter (documented as controlling "Seconds between calibration cycles"), but the implementation only uses loadImagePeriod for rate limiting. In doWork() at line 47, the elapsed time check compares only against loadImagePeriod, not calibrationPeriod. Additionally, startCalibrationCommand is cleared after the first successful calibration (line 65), which causes subsequent doWork() calls to return early (lines 40–41) rather than continuing to run calibration cycles at calibrationPeriod intervals.

The calibrationPeriod value is referenced only once during node initialization (line 80), to add an initial delay before the first calibration attempt, but it plays no role in the ongoing periodic loop. This behavior contradicts the API contract implied by the parameter documentation and struct design.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pipeline/node/DynamicCalibrationNode.cpp` around lines 739 - 766, The
`calibrationPeriod` parameter from the `startCalibrationCommand` is not being
used for rate limiting calibration cycles. Fix this by separating the rate
limiting logic for image loading and calibration: add a new timing variable to
track calibration intervals independently (similar to how
`previousLoadingAndCalibrationTime` is used), and modify the condition that
checks `loadingAndCalibrationRequired` to separately check if
`calibrationPeriod` has elapsed for the calibration step. Additionally, remove
the `startCalibrationCommand.reset()` call after successful calibration so that
the command persists and allows `runCalibration()` to execute repeatedly at
intervals defined by `calibrationPeriod` until the command is explicitly stopped
by external logic, rather than clearing it after the first run.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pipeline/node/DynamicCalibrationNode.cpp`:
- Around line 739-766: The `calibrationPeriod` parameter from the
`startCalibrationCommand` is not being used for rate limiting calibration
cycles. Fix this by separating the rate limiting logic for image loading and
calibration: add a new timing variable to track calibration intervals
independently (similar to how `previousLoadingAndCalibrationTime` is used), and
modify the condition that checks `loadingAndCalibrationRequired` to separately
check if `calibrationPeriod` has elapsed for the calibration step. Additionally,
remove the `startCalibrationCommand.reset()` call after successful calibration
so that the command persists and allows `runCalibration()` to execute repeatedly
at intervals defined by `calibrationPeriod` until the command is explicitly
stopped by external logic, rather than clearing it after the first run.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2ebd9c0e-01e7-41bc-8d21-614aedb20540

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3cc57 and 7383707.

📒 Files selected for processing (2)
  • src/pipeline/node/DynamicCalibrationNode.cpp
  • src/pipeline/node/DynamicCalibrationUtils.hpp
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-24T22:39:04.364Z
Learnt from: MaticTonin
Repo: luxonis/depthai-core PR: 1732
File: src/pipeline/Pipeline.cpp:705-705
Timestamp: 2026-03-24T22:39:04.364Z
Learning: Do not flag the `!= ""` part of the auto-calibration condition as redundant when it appears in `PipelineImpl::build()` (or closely related pipeline build logic). If the code uses `utility::getEnvAs<std::string>(..., default)` with a default such as `"ON_START"`, the explicit empty-string guard may still be intentional to treat an explicitly empty env var as “OFF/disabled” (or to avoid special-casing elsewhere). Only consider removing `!= ""` if the codebase has an explicit, enforceable guarantee that `DEPTHAI_AUTOCALIBRATION` can never be set to an empty string (e.g., via validated parsing/CI checks); otherwise, keep the guard.

Applied to files:

  • src/pipeline/node/DynamicCalibrationNode.cpp
🔇 Additional comments (12)
src/pipeline/node/DynamicCalibrationUtils.hpp (1)

13-42: LGTM!

src/pipeline/node/DynamicCalibrationNode.cpp (11)

23-95: LGTM!


97-144: LGTM!


146-165: LGTM!


247-270: LGTM!


282-324: LGTM!


446-490: LGTM!


492-531: LGTM!


533-639: LGTM!


641-724: LGTM!


771-795: LGTM!


391-396: Unit conversion is correct.

The translation vector is in meters (as shown by LengthUnit::METER usage elsewhere in the codebase), and updateCameraExtrinsics correctly expects centimeters per its API documentation. The 100x multiplier at lines 392-394 properly converts meters to centimeters.

@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch from 6e17291 to cfca204 Compare June 22, 2026 12:53
@JakubFara JakubFara force-pushed the feature/dynamicCalibration_multiSensorCalibration branch from a36fb0f to 328ecbd Compare June 30, 2026 16:15
@aljazkonec1 aljazkonec1 added testable PR is ready to be tested - run vanilla tests and removed testable PR is ready to be tested - run vanilla tests labels Jul 1, 2026
@moratom moratom mentioned this pull request Jul 1, 2026
@MaticTonin MaticTonin added testable PR is ready to be tested - run vanilla tests and removed testable PR is ready to be tested - run vanilla tests labels Jul 2, 2026
@MaticTonin MaticTonin force-pushed the feature/dynamicCalibration_multiSensorCalibration branch from 894ff29 to 8363fff Compare July 2, 2026 20:38
@MaticTonin MaticTonin merged commit 196a676 into develop Jul 2, 2026
1 check passed
@MaticTonin MaticTonin deleted the feature/dynamicCalibration_multiSensorCalibration branch July 2, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testable PR is ready to be tested - run vanilla tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants