Implementing New State Spaces
This guide explains how to add a new State and StateSpace to OxMPL and expose them to the bindings.
Do You Need a New State Type?
Before implementing a custom State and StateSpace, consider if your requirements can be met using CompoundState and CompoundStateSpace.
Using CompoundStateSpace
Most robotic systems can be represented as a collection of simpler state spaces. A CompoundStateSpace allows you to combine existing spaces (like RealVectorStateSpace, SO2StateSpace, SO3StateSpace) via a Cartesian product.
When to use CompoundStateSpace:
- Your system is composed of multiple independent parts (e.g., a mobile base ($SE(2)$) + a robotic arm ($R^n$)).
- You need to assign different weights to different components during distance calculation.
When to implement a New State:
- Unique Topology: You are working with a manifold that is not easily represented by existing spaces (e.g., a specific non-Euclidean surface).
- Custom Metric: You require a specialized distance function that cannot be decomposed into component-wise distances (e.g., Dubins path distance for non-holonomic vehicles).
- Custom Interpolation: The path between two states follows specific rules (e.g., geodesic on a sphere).
Rust Implementation (oxmpl)
- Define State: Create a struct implementing
oxmpl::base::state::State(andClone,Debug,serde::Serialize,serde::Deserialize). - Define State Space: Create a struct implementing
oxmpl::base::space::StateSpace.sample_uniform: Generate a random state.distance: Compute distance between two states.interpolate: Interpolate between two states.satisfies_bounds/enforce_bounds: Check/enforce constraints.
- Export: Add them to
oxmpl/src/base/states/andoxmpl/src/base/spaces/.
Python Bindings (oxmpl-py)
- Wrap the State:
- Create
oxmpl-py/src/base/<my_state>.rs. - Define
PyMyState(pub Arc<OxmplMyState>)with#[pyclass]. - Implement
__init__, getters, and__repr__. - Implement
oxmpl_py::base::py_state_convert::PyStateConvertforOxmplMyStateto handle conversion between Rust and Python wrappers.
- Create
- Wrap the State Space:
- Create
oxmpl-py/src/base/<my_state_space>.rs. - Define
PyMyStateSpace(pub Arc<Mutex<OxmplMyStateSpace>>)with#[pyclass]. - Implement
distance,sample, etc.
- Create
- Register: Add the classes to
oxmpl-py/src/base/mod.rsincreate_module. - Update Planners: You may need to update the
PlannerVariantenums inoxmpl-py/src/geometric/*.rsto include a variant for your new state type if it’s not covered by existing generic logic.
JavaScript Bindings (oxmpl-js)
- Wrap the State:
- Create
oxmpl-js/src/base/<my_state>.rs. - Define
JsMyStatestruct wrappingArc<OxmplMyState>with#[wasm_bindgen]. - Implement constructor and getters.
- Implement
oxmpl_js::base::js_state_convert::JsStateConvertforOxmplMyState.
- Create
- Wrap the State Space:
- Create
oxmpl-js/src/base/<my_state_space>.rs. - Define
JsMyStateSpacestruct wrappingArc<Mutex<OxmplMyStateSpace>>. - Implement
sample,distance, etc.
- Create
- Register: Export them in
oxmpl-js/src/base/mod.rs. - Update Planners: Similar to Python, update
oxmpl-js/src/geometric/*.rsto include the new state variant in the planner enums.
Note on JsStateConvert:
When implementing from_js_value for your state, you often need to inspect the JS object properties to ensure it matches your state type (e.g., check for x, y for a 2D point).
Integration Testing
Ensure your new state space is robust by adding tests across the stack.
Rust
- Unit Tests: Add
#[test]functions in your implementation file (or a separate test module) to verify:- Distance calculation properties (symmetry, non-negativity).
- Interpolation accuracy.
- Bounds enforcement.
- Serialization/Deserialization (if applicable).
- Integration Tests: Add a test file in
oxmpl/tests/to check interaction with planners.
Python (oxmpl-py/tests/)
Add tests in oxmpl-py/tests/ to ensure the bindings are correct.
- Verify that
__init__works with valid arguments and raises errors for invalid ones. - Check that methods like
distance()andsample()return expected types and values. - Ensure that states can be passed to and retrieved from planners correctly.
JavaScript (oxmpl-js/tests/)
Add tests in oxmpl-js/tests/ to ensure the WASM bindings work as expected.
- Verify object creation and property access.
- Test that the
JsStateConverttrait correctly handles conversion between JS objects and Rust structs. - Ensure usage within a
ProblemDefinitionworks without panic.