Coder Social home page Coder Social logo

decargroup / navlie Goto Github PK

View Code? Open in Web Editor NEW
146.0 4.0 11.0 8.96 MB

A state estimation package for Lie groups!

Home Page: https://decargroup.github.io/navlie/

License: MIT License

Python 100.00%
kalman-filter lie-groups navigation python state-estimation robotics

navlie's Introduction

navlie

Tests

Docs

Demo Figures

An on-manifold state estimation library for robotics.

The core idea behind this project is to abstract-away the state definition such that a single estimator implementation can operate on a variety of state manifolds, such as the usual vector space, and any Lie group. At the moment, algorithms and features of this package include:

  • Extended Kalman Filter
  • Iterated Extended Kalman Filter
  • Sigmapoint Kalman Filters (Unscented, Spherical Cubature, Gauss-Hermite)
  • Interacting Multiple Model Filter
  • Batch MAP Estimation
  • A large collection of common process and measurement models
  • Out-of-the-box on-manifold numerical jacobian using finite differencing
  • Various utils for plotting, error, and consistency evaluation
  • Monte Carlo experiment executor with result aggregation
  • A preintegration module for linear, wheel odometry, and IMU process models

By implementing a few classes, the user can model almost any problem. Documentation can be found at https://decargroup.github.io/navlie

Disclaimer

While navlie is starting to get some maturity, its still definitely not perfect. The code is probably still simple enough that you can check out the source directly to get a better understanding. If (when) you find a bug, please feel free to open an Github issue. Contributions/thoughts are welcome, and if anything regarding documentation is still grossly unclear, just let us know :).

Setup

Installation

Clone this repo, change to its directory, and execute

pip install -e .

This command should automatically install all dependencies, including our package pymlg (found at https://github.com/decargroup/pymlg) for back-end Lie group mathematical operations.

Examples

Some starting examples running EKFs can be found in the examples/ folder. Simply run these as python3 scripts

Tests

To run integration tests,

pytest tests

or, to run tests on a specific test file,

pytest -ra tests/integration/filename.py

The Core Concept - Defining a State Implementation

The algorithms and models in this repo are centered around the abstract State class. An instance of State is an object containing, at a minimum, the following attributes:

  • `value`: a value of some sort;
  • `dof`: the degrees of freedom associated with the state.

It will also contain the following mandatory methods that must be implemented by the user.

  • `plus(): A generic "addition" operation given a `dx vector with as many elements as the dof of this state.
  • `minus()`: A generic "subtraction" operation given another State object of the same type, which returns a numpy array of error values.
  • `copy()`: A method that returns a new object of the same type, and with the same attibute values.

Optionally, it is often useful to assign a timestamp (stamp) and a label (state_id) to differentiate state instances from others. The snippet below shows how to define a simple vector-space state:

from navlie.types import State 
import numpy as np

class VectorState(State):
    """
    A standard vector-based state, with value represented by a 1D numpy array.
    """

    def __init__(self, value: np.ndarray, stamp: float = None, state_id=None):
        super(VectorState, self).__init__(
            value=value,
            dof=value.size,
            stamp=stamp,
            state_id=state_id,
        )

    def plus(self, dx: np.ndarray):
        new = self.copy()
        new.value = new.value + dx
        return new

    def minus(self, x: "VectorState") -> np.ndarray:
        return self.value - x.value

    def copy(self) -> "VectorState":
        return VectorState(self.value.copy(), self.stamp, self.state_id)

As another more complicated example, a state object belonging to the SE(3) Lie group can be implemented as

from navlie.types import State 
from pymlg import SE3 
import numpy as np 

class SE3State(State):
    def __init__(self, value: np.ndarray, stamp: float = None, state_id=None):
        super(SE3State, self).__init__(
            value=value,
            dof=6,
            stamp=stamp,
            state_id=state_id,
        )

    def plus(self, dx: np.ndarray):
        new = self.copy()
        new.value = new.value @ SE3.Exp(dx)
        return new

    def minus(self, x: "SE3State") -> np.ndarray:
        return SE3.Log(SE3.inverse(x.value) @ self.value)

    def copy(self) -> "SE3State":
        return SE3State(self.value.copy(), self.stamp, self.state_id)

Process and Measurement Models

System Diagram

There are a few more core types in this package. The main ones are the ProcessModel and MeasurementModel classes. Both of these are abstract classes requiring the user to implement

  • an evaluate() method,
  • a covariance() method,
  • and optionally a jacobian() method.

For example, a simple "single integrator" (velocity input) model can be implemented as follows:

class SingleIntegrator(ProcessModel):
    """
    The single-integrator process model is a process model of the form

        x_k = x_{k-1} + dt * u_{k-1}
    """

    def __init__(self, Q: np.ndarray):
        self._Q = Q

    def evaluate(self, x: VectorState, u: VectorInput, dt: float) -> np.ndarray:
        """
        Returns a state with an updated value according to a process model.
        """
        x.value = x.value + dt * u.value
        return x

    def jacobian(self, x: VectorState, u: VectorInput, dt: float) -> np.ndarray:
        """
        (optional) Jacobian of the process model with respect to the state.
        """
        return np.identity(x.dof)

    def covariance(self, x: VectorState, u: VectorInput, dt: float) -> np.ndarray:
        """
        Returns the covariance of the process model errors.
        """
        return dt**2 * self._Q

Similarly, a single distance-to-landmark measurement model can be implemented as

class RangePointToAnchor(MeasurementModel):
    """
    Range measurement from a point state to an anchor (which is also another
    point).
    """

    def __init__(self, anchor_position: List[float], R: float):
        self._r_cw_a = np.array(anchor_position)
        self._R = np.array(R)

    def evaluate(self, x: VectorState) -> np.ndarray:
        r_zw_a = x.value
        y = np.linalg.norm(self._r_cw_a - r_zw_a)
        return y

    def jacobian(self, x: VectorState) -> np.ndarray:
        r_zw_a = x.value
        r_zc_a = r_zw_a - self._r_cw_a
        y = np.linalg.norm(r_zc_a)
        return r_zc_a.reshape((1, -1)) / y

    def covariance(self, x: VectorState) -> np.ndarray:
        return self._R

In fact, for both ProcessModel and MeasurementModel, subclasses will inherit a finite-difference numerical differentiation method jacobian_fd(), which also serves as the default implementation if jacobian is not overriden. Nevertheless, it allows for an easy way to check your jacobian() implementation! (evaluate() method must be implemented for this to work, see some of the files in tests/ for an example of this.)

Built-in Library

Many state, process, and measurement models are already written and part of the built-in library and, as an example, can be accessed with

from navlie.lib.states import VectorState, SE3State
from navlie.lib.models import RangePoseToAnchor, Altitude

The following state types are currently part of the lib:

  • VectorState
  • SO2State
  • SO3State
  • SE2State
  • SE3State
  • SE23State
  • 'SL3State'
  • IMUState (contains IMU biases as part of the state)
  • CompositeState (for holding many sub-states as a single state)

The following process models are currently part of the lib:

  • SingleIntegrator
  • BodyFrameVelocity
  • RelativeBodyFrameVelocity
  • CompositeProcessModel
  • IMUKinematics

The following measurement models are currently part of the lib:

  • RangePointToAnchor
  • RangePoseToAnchor
  • RangePoseToPose
  • RangeRelativePose
  • GlobalPosition
  • Altitude
  • Gravitometer
  • and many more

Finally, this repo has the following state estimation algorithms implemented:

  • ExtendedKalmanFilter
  • IteratedKalmanFilter
  • UnscentedKalmanFilter
  • InteractingModelFilter
  • and more

Contributing

If you wish to make some changes, fork this repo, make your changes, and then make a pull request. Here are some conventions that should be followed:

  • Code style should follow the PEP8 style guide. https://peps.python.org/pep-0008. We recommend using black --line-length 80 . to format the code.
  • Everything should be type hinted as much as possible. Essentially, in the VS Code dark theme, you should not have any white text.

The goal of this project is to write general algorithms that work for any implementation of the abstract State, ProcessModel and MeasurementModel. As such, please give thought to how this could be done to any algorithm you implement. As a rule of thumb, code outside of the navlie/lib folder should not depend on any of the classes in navlie/lib, although sometimes this rule is broken.

If you want to discuss anything regarding this repo, feel free to email [email protected].

Contributing to the Documentation

You must first install the dependencies for the documentation. This can be done by running

pip install -r docs/requirements.txt

After this is done, change to the ./docs/ directory and run

make html

after which the documentation will be updated, and viewable by opening the docs/index.html file in your browser. In terms of actually writing documentation, we use the numpy format, which can be seen in some of the existing docstrings in the code, and used as a template.

Alternatively and prefereably, install the autoDocstring extension for VSCode. <https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring> and change the docstring format in the settings to numpy.

navlie's People

Contributors

arturodcb avatar charlescossette avatar mitchellcohen3 avatar pdeco avatar shalabyma avatar vkorotkine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

navlie's Issues

Add a composition operation to Lie group states

Consider the problem of compounding two pose transformations, T_ab and T_bc
to get T_ac. This corresponds to the \circle sign in the micro lie paper.
The way to do this in pynav currently is to do matrix multiplication ad-hoc in the script. However, this becomes tedious e.g. for quaternions, or more complicated groups with their own defined composition (e.g. The Geometry of Navigation Problems by Barrau and Bonnabel).

This issue is to add a "compose" method to the state class, which could work similarly to the minus operator. For the pose transformation example we would
do T_ac = T_ab.compose(T_bc)

Tests not running with import errors

After installing navlie with pip install -e . , running
pytest tests
results in the following error. Running on Linux Mint 20.2 Cinnamon 64-bit. Using a pyenv version 3.10.1, and the examples run fine. But pytest looks for python 2.7.

(navlie_test_env) vassili@vassili-HP-ProBook-430-G5:~/projects/navlie$ pytest tests
============================= test session starts ==============================
platform linux2 -- Python 2.7.18, pytest-4.6.9, py-1.8.1, pluggy-0.13.0
rootdir: /home/vassili/projects/navlie
collected 15 items / 23 errors                                                 

==================================== ERRORS ====================================
__________ ERROR collecting tests/integration/test_batch_noiseless.py __________
ImportError while importing test module '/home/vassili/projects/navlie/tests/integration/test_batch_noiseless.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/integration/test_batch_noiseless.py:1: in <module>
    from concurrent.futures import process
E   ImportError: No module named concurrent.futures
_________ ERROR collecting tests/integration/test_filter_ekf_basic.py __________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/integration/test_filter_ekf_basic.py", line 58
E       def ekf_trial(trial_number: int) -> List[GaussianResult]:
E                                 ^
E   SyntaxError: invalid syntax
_______ ERROR collecting tests/integration/test_filter_ekf_noiseless.py ________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/integration/test_filter_ekf_noiseless.py", line 59
E       def ekf_trial(trial_number: int) -> List[GaussianResult]:
E                                 ^
E   SyntaxError: invalid syntax
____ ERROR collecting tests/integration/test_interacting_multiple_models.py ____
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/integration/test_interacting_multiple_models.py", line 68
E       def imm_trial(trial_number: int) -> List[GaussianResult]:
E                                 ^
E   SyntaxError: invalid syntax
___________ ERROR collecting tests/integration/test_invariant_so3.py ___________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/integration/test_invariant_so3.py:1: in <module>
    from navlie.lib.states import SO3State
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
___________ ERROR collecting tests/integration/test_iterated_ekf.py ____________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/integration/test_iterated_ekf.py", line 80
E       trial_number: int,
E                   ^
E   SyntaxError: invalid syntax
_____________ ERROR collecting tests/unit/test_associate_stamps.py _____________
ImportError while importing test module '/home/vassili/projects/navlie/tests/unit/test_associate_stamps.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/unit/test_associate_stamps.py:1: in <module>
    import numpy as np
E   ImportError: No module named numpy
_____________ ERROR collecting tests/unit/test_batch_residuals.py ______________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_batch_residuals.py:3: in <module>
    from navlie.types import Measurement
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
______________ ERROR collecting tests/unit/test_body_velocity.py _______________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_body_velocity.py", line 107
E       T_12 = SE3.inverse(T_a1) @ T_a2
E                                ^
E   SyntaxError: invalid syntax
__________________ ERROR collecting tests/unit/test_camera.py __________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_camera.py:1: in <module>
    from navlie.lib.camera import PinholeCamera, PoseMatrix
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
________________ ERROR collecting tests/unit/test_composite.py _________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_composite.py:1: in <module>
    from navlie.lib import (
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
_________________ ERROR collecting tests/unit/test_datagen.py __________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_datagen.py:1: in <module>
    from navlie.lib.models import SingleIntegrator
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
__________________ ERROR collecting tests/unit/test_filter.py __________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_filter.py:1: in <module>
    import navlie as nav
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
___________________ ERROR collecting tests/unit/test_imu.py ____________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_imu.py", line 42
E       assert np.allclose(U @ U_inv, np.eye(5))
E                            ^
E   SyntaxError: invalid syntax
______________ ERROR collecting tests/unit/test_interpolation.py _______________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_interpolation.py:1: in <module>
    from navlie.utils import state_interp
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
_______________ ERROR collecting tests/unit/test_meas_models.py ________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_meas_models.py", line 31
E       x: MatrixLieGroupState, model: MeasurementModel, atol=1e-6, rtol=1e-4
E        ^
E   SyntaxError: invalid syntax
________________ ERROR collecting tests/unit/test_plot_poses.py ________________
ImportError while importing test module '/home/vassili/projects/navlie/tests/unit/test_plot_poses.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/unit/test_plot_poses.py:1: in <module>
    import numpy as np
E   ImportError: No module named numpy
______________ ERROR collecting tests/unit/test_preintegration.py ______________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_preintegration.py", line 206
E       Qd_fd = L @ rmi.covariance @ L.T
E                 ^
E   SyntaxError: invalid syntax
______________ ERROR collecting tests/unit/test_process_models.py ______________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_process_models.py", line 27
E       cov_test = L @ np.identity(3) @ L.T
E                    ^
E   SyntaxError: invalid syntax
___________________ ERROR collecting tests/unit/test_spkf.py ___________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
/usr/lib/python2.7/dist-packages/_pytest/assertion/rewrite.py:304: in load_module
    exec(co, mod.__dict__)
tests/unit/test_spkf.py:1: in <module>
    from navlie.lib.states import MatrixLieGroupState, VectorState, SE3State
navlie/__init__.py:1: in <module>
    from .types import (
E     File "/home/vassili/projects/navlie/navlie/types.py", line 18
E       dof: int,
E          ^
E   SyntaxError: invalid syntax
__________________ ERROR collecting tests/unit/test_states.py __________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_states.py", line 20
E       sample_states: Dict[str, State] = {
E                    ^
E   SyntaxError: invalid syntax
__________________ ERROR collecting tests/unit/test_utils.py ___________________
/usr/lib/python2.7/dist-packages/_pytest/python.py:507: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
/usr/lib/python2.7/dist-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
E     File "/home/vassili/projects/navlie/tests/unit/test_utils.py", line 55
E       e.reshape((1, -1)) @ np.linalg.inv(c) @ e.reshape((-1, 1))
E                          ^
E   SyntaxError: invalid syntax
_________________ ERROR collecting tests/unit/test_van_loan.py _________________
ImportError while importing test module '/home/vassili/projects/navlie/tests/unit/test_van_loan.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/unit/test_van_loan.py:1: in <module>
    import numpy as np
E   ImportError: No module named numpy
!!!!!!!!!!!!!!!!!!! Interrupted: 23 errors during collection !!!!!!!!!!!!!!!!!!!

Add sigma point filters

I suggest this be done in the following way.

First, create a standalone function that generates unit sigma points sigma_points = get_sigma_points(N, "unscented"), where "unscented" can be replaced by either "spherical" or "gausshermite" or whatever other sigma-point scheme. This is used internally by the filter, but also a public function useful for other things

It is then possible to create a general SigmaPointKalmanFilter(process_model, method = "unscented")

However, just for ease-of-access, we can make quick convenience classes in the form:

class UnscentedKalmanFilter(SigmaPointKalmanFilter):
    def __init__(self, process_model):
        super().__init__(self, process_model,  method = "unscented")

For sigma-point generation in the prediction step, the noise statistics of the input, are needed. This can be provided as an optional argument in the prediction step:

ukf.predict(x, u, dt, input_covariance = Q)

and then since noise is basically always additive to the input, u.plus(sigma_point) and feed to the process model. If Q is not provided, the input object can be checked for a u.covariance property.

For sigma-point generation in the correction step, we currently always assume the noise is additive to the state. We will have to modify things elsewhere in pynav to add a more complicated option, but I have an idea. Nevertheless, we can currently pass state sigma points through the nonlinear process models

Add "linear" state interpolation

Leveraging .plus() and .minus(), it is straightforward to implement an on-manifold "linear" interpolation scheme.

Let x1: State and x2: State be two points at times t1, t2 respectively where t1 < t2

alpha = (t_interp - t1)/(t2 - t1)
dx = x2.minus(x1)
x_interp = x1.plus(alpha*dx)

Add batch

Adding general-purpose batch estimation that can work with out state definitions, process and measurement models

Automatically build documentation

Have the documentation automatically update when there are new pushes to the main branch. This can be done with github actions. This could lead to a very bloated git history however as we need to push all the build artifices to some dedicated documentation branch. An alternative is to use ReadTheDocs

Add `from_list()` constructor to `GaussianResultList`

The goal is to simplify this common snippet:

estimate_list = run_filter(ekf, x0, P0, input_list, meas_list)

# Postprocess the results and plot
results = GaussianResultList(
    [
        GaussianResult(estimate_list[i], states_true[i])
        for i in range(len(estimate_list))
    ]
)

to something like

estimate_list = run_filter(ekf, x0, P0, input_list, meas_list)

# Postprocess the results and plot
results = GaussianResultList(estimate_list, states_true, interp="nearest")
resullts = GaussianResultList.from_list(estimate_list, states_true, interp="nearest") # Alternative interface

UKF example broken

Traceback (most recent call last):
  File "/home/charles/pynav/examples/ex_ukf_se2.py", line 65, in <module>
    x = ukf.correct(x, y, u)
  File "/home/charles/pynav/pynav/filters.py", line 697, in correct
    x = self.predict(x, u, dt)
  File "/home/charles/pynav/pynav/filters.py", line 591, in predict
    raise ValueError(
ValueError: Input covariance information must be provided.

Add a `evaluate_with_jacobian` default method to ProcessModel and MeasurementModel

Add an optionally-overridable method to process/meas models that allows the user to return both the evaluation and jacobian with a single function call. This could speed things up a lot in cases where these models have a lot of common calculations between evaluate/jacobian. The default implementation will simple call evaluate followed by jacobian.

Also, the algorithms all need to be switched to actually call this new method now.

This wont be a breaking change.

Investigate the use of Jax

Jax could be an interesting framework to use with its autodiff and just-in-time compiling capabilities. I personally thought there was value in having a purely numpy-based package to begin with, in terms of having lower barrier to entry (many people in our lab group were familiar with python/numpy/lie groups but not with Jax).

There's potentially a solution that mixes jax with the current code, where jax is used for JIT-able state/process/measurement model evaluations along with autodiff Jacobians, but the output of the models is converted back to regular numpy arrays for use by the same filter implementations as there is now. While this would not produce end-to-end differentiable/compilable code, it would speed up evaluation/jacobian calculations compared to the current finite differencing.

To start exploring this, I recommend we define some new jax-based abstract classes for state/process/measurement models. Say JaxState, JaxProcessModel and JaxMeasurementModel. These classes could have the default jacobian implementations use jax's autodiff, and the user is responsible for writing jax-compatible code in the evaluate method.

If you'd like to tackle this. Please reach out :)

Add SLAM example

Add a SLAM-type problem where the state to be estimated is both robot states and static landmarks

Integration tests

I think we need some integration tests for changes that we make.
Right now I just make sure to run a few of the previous examples to check nothing is broken. But running a single script will be easier when adding changes. My proposal is to run a test, and check the resulting time-avaraged NEES value to make sure it's "reasonable" even if its out of bounds.

Add general-purpose van loans

Should just be a simple function of the form

A_d, B_d, Q_d = van_loans(A_c, B_c, Q_c)

where all arguments/returns are numpy arrays. Please add to utils.py

Reports of IMM not working

From Phil:
"imm se3 throw an error: ValueError: x and y must have same first dimension, but have shapes (98,) and (2,)
and the vector case is sensitive to initialization, sometimes the estimation is not good"

Add on-manifold B-splines

It would be great to have interpolation capability that isnt just "linear", but that still complies with the abstraction provided by State.plus() and State.minus(). This might be kind of a tricky problem to figure out and require some derivation from scratch if it doesnt already exist out there.

This paper defines a B-spline for SE(3):
https://projet.liris.cnrs.fr/imagine/pub/proceedings/BMVC-2013/Papers/paper0094/paper0094.pdf
We could generalize from there: replace uses of exp and log with oplus and ominus appropriately and then that could be directly implementable in our framework. An input to their algorithm though is the "control points" which now become "control poses". If we want to use splines for interpolation, we will need to find a way to compute the control points that result in exact interpolation of the desired states.

Measurement models for a subset of the state

Our measurement models right now all assume a specific form for the state. E.g. RangePointToAnchor assumes the whole state is the position. But if state consists of state+velocity, one has to roll a different measurement model (like I did for this PR). I think a wrapper class that returns a measurement model which takes this into account would be helpful at some point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.