Files
Oleksandr Bezdieniezhnykh 1ebab29a4f [AZ-332] C1 OKVIS2 Strategy: facade + binding skeleton
Python facade (`Okvis2Strategy`) is production-quality and satisfies
AZ-331's `VioStrategy` protocol; full AC-1..10 coverage with
AC-9 + NFR-perf marked `tier2`. The C++ pybind11 binding compiles
and loads but throws `OkvisFatalException("estimator not yet wired")`
on first `add_frame` — the `okvis::ThreadedKFVio` wiring is a tier2
follow-up the Step-15 Product Completeness Gate is expected to track
as a remediation task.

Resolved contradictions:

* Constructor signature aligned with the AZ-331 factory: `(config, *,
  fdr_client, clock=None)`. Calibration / preintegrator / logger
  built internally from config. No churn on AZ-331.
* IMU substrate: OKVIS2 owns its internal estimator IMU integration;
  the AZ-276 `ImuPreintegrator` is a separate substrate consumed by
  E-C5's fusion graph. Single source of truth lives at the sample
  stream, not the integrator instance.
* FDR API: `FdrClient.enqueue(record)` with new `vio.health` kind
  added to AZ-272 `KNOWN_PAYLOAD_KEYS`.

CI matrix forces `-DBUILD_OKVIS2=OFF` until the tier2 wiring task
brings Ceres / SuiteSparse / OKVIS2 vendored submodules into the
Linux build.

Files: 17 added/modified across `c1_vio/`, `fdr_client/records.py`,
`cpp/okvis2/CMakeLists.txt`, CI workflow, AZ-332 task spec
(implementation-notes section), batch 23 report.

Tests: 17 new (15 tier1 + 2 tier2). Full Tier-1 suite: 1109 pass,
2 skipped (env), 2 deselected (tier2). No regressions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 09:56:45 +03:00

85 lines
3.5 KiB
CMake

# cpp/okvis2/CMakeLists.txt — OKVIS2 wrapper for C1 VIO (AZ-332).
#
# Builds the vendored OKVIS2 upstream (cpp/okvis2/upstream/, git submodule)
# plus a pybind11 binding that exposes the estimator to the Python facade
# at src/gps_denied_onboard/components/c1_vio/okvis2.py.
#
# Gating: BUILD_OKVIS2=ON only on linux production binaries (deployment +
# research matrix kinds in .github/workflows/ci.yml). macOS dev builds
# default BUILD_OKVIS2=OFF; unit tests use a fake pybind11 binding fixture
# installed at sys.modules boundary (tests/unit/c1_vio/conftest.py).
#
# Bundled OKVIS2 deps (DBoW2, brisk, ceres-solver, opengv) are NOT pulled
# into this clone — see ci.yml step that installs them via apt
# (libceres-dev libsuitesparse-dev etc.) and the USE_SYSTEM_* flags below.
if(NOT BUILD_OKVIS2)
return()
endif()
message(STATUS "[okvis2] BUILD_OKVIS2=ON — building OKVIS2 upstream + pybind11 binding")
# Tell OKVIS2 to use system-installed dependencies instead of its bundled
# external/ submodules (which we do not initialise — saves ~hundreds of MB
# and matches the Linux apt-deps approach in ci.yml).
set(USE_SYSTEM_BRISK ON CACHE BOOL "AZ-332: use apt libbrisk-dev" FORCE)
set(USE_SYSTEM_DBOW2 ON CACHE BOOL "AZ-332: use apt libdbow2-dev" FORCE)
set(USE_SYSTEM_CERES ON CACHE BOOL "AZ-332: use apt libceres-dev" FORCE)
# Trim OKVIS2's build surface — we link the estimator libs only.
set(BUILD_APPS OFF CACHE BOOL "AZ-332: skip OKVIS2 demo apps" FORCE)
set(BUILD_TESTS OFF CACHE BOOL "AZ-332: skip OKVIS2 gtests" FORCE)
set(BUILD_ROS2 OFF CACHE BOOL "AZ-332: ROS 2 rejected at Plan time (D-C1-1-SUB-A)" FORCE)
set(HAVE_LIBREALSENSE OFF CACHE BOOL "AZ-332: no realsense pipeline" FORCE)
set(USE_NN OFF CACHE BOOL "AZ-332: drop LibTorch dep (keyframe arch OK per Fact #39)" FORCE)
set(DO_TIMING OFF CACHE BOOL "AZ-332: disable per-frame timing prints" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "AZ-332: link OKVIS as static into the .so" FORCE)
# pybind11 (vendored at cpp/pybind11/upstream/) — guarded so a sibling
# native binding (gtsam_bindings, faiss_index) cannot double-add the
# subdirectory.
if(NOT TARGET pybind11::module)
add_subdirectory(
${CMAKE_SOURCE_DIR}/cpp/pybind11/upstream
${CMAKE_BINARY_DIR}/pybind11_build
)
endif()
# Vendored OKVIS2 upstream — EXCLUDE_FROM_ALL keeps unused targets out of
# the default build graph; we depend on the okvis_* libs we explicitly
# link below.
add_subdirectory(upstream EXCLUDE_FROM_ALL)
# pybind11 binding source — per module-layout.md rule #4 the binding code
# lives next to the Python facade, not under cpp/.
set(OKVIS2_BINDING_SRC
${CMAKE_SOURCE_DIR}/src/gps_denied_onboard/components/c1_vio/_native/okvis2_binding.cpp
)
pybind11_add_module(okvis2_binding ${OKVIS2_BINDING_SRC})
# OKVIS2 export targets — exact list confirmed by walking upstream
# CMakeLists in cpp/okvis2/upstream/okvis_*/. If a target name changes
# upstream, the linker error on first CI run pinpoints which one.
target_link_libraries(okvis2_binding
PRIVATE
okvis_ceres
okvis_frontend
okvis_multisensor_processing
okvis_kinematics
okvis_cv
okvis_common
okvis_time
okvis_util
)
target_compile_features(okvis2_binding PRIVATE cxx_std_17)
# Install the .so next to the Python facade so the lazy import inside
# okvis2.py (`from . import _native; _native.okvis2_binding`) resolves at
# runtime without a sys.path shim.
install(TARGETS okvis2_binding
LIBRARY DESTINATION
${CMAKE_INSTALL_LIBDIR}/gps_denied_onboard/components/c1_vio/_native/
)