This is a small Python package that could be helpful for designing an IKFoM. What it does is simple: it leverages CasADi's symbolic framework to model the system and its AD (auto differentiation) engine to calculate the Jacobian matrices required by the IKFoM toolkit.
This package can be installed with pip: clone this repo and run pip install . at the repo root or simply:
pip install git+https://github.com/ErcBunny/IKFoM-Designer.git
ikfmd builds three kinds of kernels:
- one process kernel family through
SysDynamics - one function family for each measurement block
- one function family for each measurement combo
The user implements a child class derived from BaseDesigner, then calls
generate_code().
The child class defines:
- parameters:
_define_parameters() - states:
_define_states() - state perturbations:
_define_states_perturbation() - inputs:
_define_inputs() - process noises:
_define_process_noises() - measurement blocks:
_define_measurement_blocks() - measurement combos:
_define_measurement_combos() - process model:
_dyn(...) - state perturbation rule:
_perturb_states(...)
Measurement blocks are declared with MeasBlockInfo. Each block owns its local
measurement-noise symbols, measurement expression, and boxminus rule.
Measurement combos are declared as:
def _define_measurement_combos(self) -> dict[str, tuple[str, ...]]:
return {
"combo_name_0": ("meas_name_0", "meas_name_1"),
"combo_name_1": ("meas_name_0", "meas_name_2"),
# ...
}A combo does not define new mathematics. It only reuses already compiled measurement blocks in the specified order.
Two reference examples live in tests/:
tests/imu_filter_designer.py: block-only usagetests/meas_combo_demo.py: block + combo usage
The normal workflow is:
from ikfmd import BaseDesigner
class Designer(BaseDesigner):
# implement abstract methods
pass
designer = Designer(
"gen",
dict(cpp=True, with_header=True, main=False, verbose=True, with_mem=False),
True,
)
designer.print_expr()
designer.print_func_io()
designer.generate_code()The generated CasADi function names are:
- process kernels:
fdf_dxdf_dw
- measurement block kernels:
h_block_<block_name>dh_dx_block_<block_name>dh_dv_block_<block_name>
- measurement combo kernels:
h_combo_<combo_name>dh_dx_combo_<combo_name>dh_dv_combo_<combo_name>
Block and combo names are sanitized before they are used in function names.
generate_code() writes the generated source files using the code-generator
name passed to the designer constructor. By default, CasADi writes them into
the current working directory. When with_header=True, CasADi also generates a
header file with the same basename.
For example, if the designer is constructed as:
designer = Designer("gen", ...)and the script is run from the repository root, the generated files are:
gen.cppgen.h
The exported kernels map to IKFoM as follows:
- use
f / df_dx / df_dwfor prediction - use one block family or one combo family for fixed-measurement updates
- use runtime concatenation of block/combo kernels for dynamic-vector updates
- use runtime selection of one combo for dynamic-manifold updates
In generated C++, each CasADi function is called with the standard pattern:
- fill
const casadi_real* arg[...] - fill
casadi_real* res[...] - allocate
casadi_int iw[...]andcasadi_real w[...] - call the exported symbol from
gen.h
The input order is exactly the order printed by designer.print_func_io().
For a hand-written wrapper example around generated CasADi kernels,
please take a look at
https://github.com/ErcBunny/IMU-IKFoM/blob/casadi/include/imu_ikfom/ikfom_formulation.hpp.
Use these signatures:
f:[*x, *u, w (vec), *p]df_dx:[*x, dx (vec), *u, *p]df_dw:[*x, *u, w (vec), *p]h_block_*:[*x, v (vec), *p]dh_dx_block_*:[*x, dx (vec), *p]dh_dv_block_*:[*x, v (vec), *p]h_combo_*:[*x, combo_v (vec), *p]dh_dx_combo_*:[*x, dx (vec), *p]dh_dv_combo_*:[*x, combo_v (vec), *p]
Practical notes:
- a block
hhas one output - a combo
his a multi-output function, one output per block in combo order dh_dx_combo_*is stacked by combo orderdh_dv_combo_*is expressed in combo-local noise coordinates- for dynamic-vector IKFoM updates, flatten and concatenate runtime-selected block/combo outputs in one consistent order
- for dynamic-manifold IKFoM updates, select one combo and map its multi-output
hto the target measurement manifold type in your wrapper
The generated code has no external runtime dependency beyond the generated files themselves.