Welcome, AI Agent! This file contains the architectural context and coding guidelines for the Mobile Robot Programming Toolkit (MRPT) 3.0. When generating, refactoring, or reviewing code in this repository, you must strictly adhere to the following rules.
- MRPT 3.0 is highly modular. It is designed to be built using colcon (similar to ROS 2 packages).
- Each module (e.g.,
mrpt_opengl,mrpt_math) lives in its own directory and functions as an independent CMake project. - Target OS/Compilers: Cross-platform (Linux, Windows, macOS, WebAssembly/Emscripten).
MRPT 3.0 abstracts away standard CMake boilerplate using mrpt_common. Use standard CMake commands but prefer mrpt_common cmake helpers when possible for consistency.
-
Compiling a module: Use
colcon build --packages-up-to mrpt_XXX, then. install/setup.bashthen you can run the executables or unit tests. There is already acolcon_defaults.yamlat the repo root defining symlink install, RelWithDebInfo builds, andCMAKE_EXPORT_COMPILE_COMMANDS. -
Minimum CMake Version:
cmake_minimum_required(VERSION 3.16) -
Always include the MRPT common scripts:
find_package(mrpt_common REQUIRED)- Target Definition: Use mrpt_add_library instead of add_library. This macro automatically handles C++ standard configurations, export targets, and installation steps.
mrpt_add_library(
TARGET ${PROJECT_NAME}
SOURCES ${LIB_SOURCES} ${LIB_PUBLIC_HEADERS}
PUBLIC_LINK_LIBRARIES mrpt::another_module
CMAKE_DEPENDENCIES another_module
)Dependency Management:
- Use find_package(mrpt_<module_name> REQUIRED) to find other MRPT modules.
Link against namespaced targets (e.g., mrpt::mrpt_poses, Eigen3::Eigen).
Standard: Use Modern C++ features where appropriate (C++17/C++20).
Namespaces: All core code must reside within the mrpt:: namespace or its sub-namespaces (e.g., mrpt::opengl::).
Unit tests: test files are in a "tests" subdirectory in each module, and their name must end in _unittest.cpp, then they will be catched automatically for inclusion as unit tests.
License Headers: Every new .cpp, .h, and CMakeLists.txt file must start with the standard MRPT SPDX-License-Identifier header:
/* _
| | Mobile Robot Programming Toolkit (MRPT)
_ __ ___ _ __ _ __ | |_
| '_ ` _ \| '__| '_ \| __| https://www.mrpt.org/
| | | | | | | | |_) | |_
|_| |_| |_|_| | .__/ \__| https://github.com/MRPT/mrpt/
| |
|_|
Copyright (c) 2005-2026, Individual contributors, see AUTHORS file
See: https://www.mrpt.org/Authors - All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
*/Formatting: according to .clang-format and .clang-tidy. In particular: prefer if (x) {\n y;\n } instead of if(x) y;. Use [[nodiscard]] where applicable.
Do not manually configure .so versioning or write manual install() blocks for standard headers/libraries. mrpt_add_library does this.
Do not hardcode compiler flags (like -std=c++17 or -fPIC). The MRPT CMake wrappers handle these natively.
Do not use raw pointers for ownership. Default to std::shared_ptr or std::unique_ptr, and use MRPT's smart pointer macros where applicable.
In mrpt 3.x, most MRPT libraries under ROOT/modules/mrpt_* now have their own pybind11 module, in a modular way so users can import
only the required modules.
modules/mrpt_core/python/mrpt/__init__.pyis the main root python file for all modules, in charge of trying to import the rest, if they exist.- Each module, for example
mrpt_img, has its ownmodules/mrpt_img/python/mrpt/img/__init__.pythat must be updated with new wrapped C++ classes. - Each module, for example
mrpt_img, has its ownmodules/mrpt_img/python_bindings/mrpt_img_py.cppfile with the specific wrapped C++ classes and python adaptors. - Python examples, demonstrating each module wrapped features, live under
mrpt_examples_py. They should be updated/extended or new examples created when appropriate as new classes are wrapped.
To add or extend Python bindings for a module mrpt_foo, three files are needed:
modules/mrpt_foo/python_bindings/mrpt_foo_py.cpp— The C++ pybind11 source. Must definePYBIND11_MODULE(_bindings, m) { ... }. The compiled shared library is always named_bindings.so.modules/mrpt_foo/python/mrpt/foo/__init__.py— Python re-exports:from . import _bindings as _b, thenClassName = _b.ClassNamefor each wrapped class/function, and an__all__list.modules/mrpt_foo/CMakeLists.txt— Must callmrpt_add_python_module(foo python_bindings/${PROJECT_NAME}_py.cpp). If this line is commented out, uncomment it.
The root mrpt/__init__.py (in mrpt_core) auto-imports all known submodules by name. If adding a new module name, add it to the MRPT_MODULES list there.
Includes: Always include <pybind11/pybind11.h> and <pybind11/stl.h>. Add <pybind11/eigen.h> for Eigen/matrix types, <pybind11/numpy.h> for py::array_t<T>, <pybind11/operators.h> for operator overloading, <pybind11/chrono.h> for time types, <pybind11/functional.h> for std::function callbacks.
Class binding with inheritance:
py::class_<Derived, Base1, Base2, std::shared_ptr<Derived>>(m, "Derived")Always use std::shared_ptr<T> holder for classes that are commonly used via smart pointers. Include CSerializable in the base list for serializable classes.
Properties: When C++ uses x() getter and x(double) setter (common in MRPT), use:
.def_property("x",
[](const T& p) { return p.x(); },
[](T& p, double val) { p.x(val); })For public member variables, use .def_readwrite("name", &T::name) or .def_readonly(...).
Overloaded methods: Resolve with py::overload_cast<ArgTypes...>(&Class::method) or static_cast<RetType(Class::*)(ArgTypes...)>(&Class::method).
Output-argument functions → return values: Wrap in lambda:
.def("compute", [](const T& self) {
ResultType result;
bool ok = self.compute(result);
return py::make_tuple(ok, result);
})Operator overloading: Use py::self + py::self, etc. Always add __str__ and __repr__.
NumPy integration:
- For Eigen matrices:
#include <pybind11/eigen.h>enables automatic conversion. For zero-copy, returnEigen::Map<const RowMajorMatrix>(ptr, rows, cols). - For
CImage: Usepy::array_t<uint8_t>with shape/strides and passself_obj(the Python object) as the buffer base for zero-copy. - Add
__array__protocol:.def("__array__", [](const T& self) { return self.as_numpy(); }).
Iterators: py::make_iterator(container.begin(), container.end()) with py::keep_alive<0, 1>().
Enums: py::enum_<T>(m, "Name").value("A", T::A).export_values(); Use py::arithmetic() for bitmask enums.
GIL management: When passing Python callables to C++ threads, acquire the GIL: py::gil_scoped_acquire gil; inside the C++ callback lambda.
Return value policies: Use py::return_value_policy::reference_internal for getters returning references to internal objects (e.g., getCamera() on a Viewport). Use py::return_value_policy::reference for singletons/static data.
Python __init__.py conventions:
- Re-export all bound classes:
ClassName = _b.ClassName - Monkey-patch
__array__for NumPy integration where appropriate - Add Pythonic conveniences (e.g.,
<<operator for scene building, color constants) - Include
__all__listing all public names
For ROS 2 packages that previously depended on mrpt_ros (MRPT 2.x wrappers), see the
porting guide at doc/source/doxygen-docs/port_mrpt3.md (section 19) for:
package.xmldependency renaming (mrpt_lib*→ fine-grainedmrpt_<module>packages)CMakeLists.txtfind_packageand target renaming (mrpt::<X>→mrpt::mrpt_<X>)- Migrating away from the dropped
mrpt_libtclap/mrpt-tclapto CLI11
- Do not use the compound command "cd foo && git ...", instead, use "git -C foo ...".