Development Guide
Workflow
- Branch:
feat/…,fix/…,chore/…— include the issue number (e.g.feat/42-new-planner). - Implement: follow conventions below.
- Test: run all applicable test suites (see checklist).
- PR: fill out the PR template and paste test output.
Every task should have a GitHub issue. Reference it in commits (fixes #42) and in the PR body (Closes #42).
Environment
The recommended environment uses Pixi. All dependencies (CUDA headers, Eigen, nanobind, pytest) are pinned in pixi.toml.
pixi run install # build & install Python bindings
pixi run test # pytest tests/ -v
pixi run tracking # quadrotor lemniscate tracking with plotFor a standalone C++ build:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)Test checklist
Before closing a GitHub issue, run all applicable suites:
Trajectory tracking controllers
pixi run tracking- Automated assert: RMSE < 2.0 m (regression guard).
- Paper baseline: ~0.69 m — report the actual value in the closing comment.
Core kernel / config
./build/tests/mppi_gtest # must report [ PASSED ]
./build/tests/i_mppi_gtest # must report [ PASSED ]
./build/tests/pendulum_test # prints PASSED
./build/tests/bspline_test # prints PASSED
./build/tests/fsmi_unit_test # prints PASSEDFSMI / occupancy grid
./build/tests/fsmi_unit_testPython bindings
pixi run install
pixi run testAdding new dynamics and cost
Every controller is templated on a (Dynamics, Cost) pair — you need both.
1. Dynamics
Create include/mppi/instantiations/my_system.cuh:
struct MySystem {
static constexpr int STATE_DIM = …;
static constexpr int CONTROL_DIM = …;
/* Called once per GPU thread, inside the rollout loop. */
__device__ void step(
const float* x, const float* u, float* x_next, float dt
) const { … }
};2. Cost
In the same header (or a separate one):
struct MyCost {
/* Running cost at each timestep. */
__device__ float compute(const float* x, const float* u, int t) const { … }
/* Terminal cost at the end of the horizon. */
__device__ float terminal(const float* x) const { … }
};3. Test
Add tests/my_system_gtest.cu and register it in tests/CMakeLists.txt:
add_executable(my_system_gtest my_system_gtest.cu)
target_link_libraries(my_system_gtest cuda_mppi_lib GTest::gtest_main)4. Python binding (optional)
In bindings/bindings.cu, instantiate the controller and expose it:
using MyCtrl = MPPIController<MySystem, MyCost>;
nb::class_<MyCtrl>(m, "MySystemMPPI")
.def(nb::init<MPPIConfig, MySystem, MyCost>())
.def("compute", &MyCtrl::compute)
.def("get_action", &MyCtrl::get_action)
.def("shift", &MyCtrl::shift);Coding conventions
- CUDA:
__device__for rollout-path code. Nomalloc/newinside kernels. - Error handling: wrap all
cudaMalloc,cudaMemcpy,curandGenerate*calls withHANDLE_ERROR(...). - Sync: call
cudaDeviceSynchronize()after every kernel launch whose result is read on the host. - Comments: use codedoc-style block comments (
/* … */) for prose;//for short inline notes. - Commits: Conventional Commits —
feat,fix,perf,refactor,test,docs,chore.