C++/Python 상호 운용

현대 SLAM 연구는 두 세계에 걸쳐 있습니다: 성능이 중요한 추정 코드는 C++로 작성되고, 실험, 딥러닝, 평가는 Python에서 이루어집니다. C++/Python 상호 운용은 그 사이를 잇는 다리입니다 --- C++ 코어를 Python 바인딩으로 감싸서, 동일한 트래커나 옵티마이저를 노트북에서 구동하거나, PyTorch 모델과 결합하거나, Python 도구로 벤치마크할 수 있게 하며, 이를 위해 어떤 것도 다시 작성할 필요가 없습니다.

PyBind11은 이러한 바인딩을 작성하는 사실상의 표준입니다. 헤더 전용 C++ 라이브러리로서, 매우 적은 보일러플레이트로 C++ 클래스와 함수를 Python 모듈로 노출합니다:

#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>   // automatic Eigen <-> NumPy conversion

Eigen::Matrix4d track(const Eigen::Matrix4d& T_prev, const cv::Mat& img);

PYBIND11_MODULE(myslam, m) {
    m.def("track", &track, "Track one frame and return the new pose");
}

pybind11/eigen.h 헤더는 Eigen 행렬과 NumPy 배열 사이를 자동으로 변환하며, 이는 정확히 SLAM 코드가 필요로 하는 것입니다: 자세, 포인트 클라우드, 야코비안이 수동 복사 코드 없이 배열로서 언어 경계를 넘어갑니다. 여러분이 사용할 많은 라이브러리들이 이 방식으로 래핑되어 있습니다 --- GTSAM은 공식 Python 래퍼를 제공하며, COLMAP의 pycolmap 같은 프로젝트들도 동일한 패턴을 따릅니다.

자유 함수 대신 전체 시스템을 바인딩하는 것은 다음과 같습니다 --- GIL 가드를 주목하세요. 이는 Python이 대기하는 동안에도 다중 스레드 SLAM 코어가 계속 실행되도록 유지해줍니다:

namespace py = pybind11;

py::class_<SlamSystem>(m, "SlamSystem")
    .def(py::init<const std::string&>())              // config file path
    .def("track", &SlamSystem::track,
         py::call_guard<py::gil_scoped_release>())    // release GIL during C++ work
    .def_property_readonly("map_points", &SlamSystem::mapPoints);

nanobind는 동일한 저자가 만든 후속작으로, 더 낮은 바인딩 오버헤드, 더 작은 바이너리, 더 빠른 컴파일 시간을 위해 재설계되었습니다. API는 PyBind11과 의도적으로 유사하여 지식이 그대로 전이됩니다; 호출 오버헤드를 신경 쓰는(예를 들어, 특징당 또는 프레임당 호출되는 작은 함수를 바인딩하는) 새 프로젝트들은 점점 더 nanobind를 선택하고 있습니다.

SLAM 코드를 래핑할 때 이해해야 할 것

흔한 함정

SLAM에서의 의미

이 분야는 하이브리드 시스템으로 수렴하고 있습니다: 고전적인 C++ 백엔드가 Python/PyTorch에서 동작하는 학습된 프론트엔드(특징 검출기, 깊이 네트워크, 매처)와 결합됩니다. C++ 옵티마이저를 Python으로 바인딩하거나 --- 또는 내보낸 모델을 통해 C++에서 학습된 매처를 호출하는 --- 능력이 바로 이러한 조합들을 실용적으로 만드는 요소입니다. 이는 또한 여러분 자신의 C++ 코드를 변화시킵니다: 한 번 래핑되면, 스크립트로 다룰 수 있게 되고, pytest로 단위 테스트를 할 수 있게 되며, Python 도구로 데이터셋에 대해 평가하기 쉬워집니다.

실습

관련 문서