Kalman smoother implementation

I’ve just pushed a (draft) pair of merge requests to both Hipparchus (request 364) and orekit (request 713) to implement a Kalman smoother. It’s an implementation of a backwards RTS (Rauch-Tung-Striebel) recursion that’s constructed so that it can be used for linear, extendend or unscented filters.

I’d like a bit of feedback on how I’ve approached the implementation, mostly in orekit, but also in Hipparchus. What I’ve done is create a smoother class that implements all of the methods a sequential estimator needs. On instantiation, the smoother takes a forward filter as a parameter, then delegates all of the estimator calls (e.g. add observer, process measurement) to the underlying filter, collecting information the smoother needs as it proceeds. Once all of the measurements have been processed the smoother method can be called to produce smoothed estimates (time/mean/covariance).

For example

        // Build the Kalman filter
        final KalmanEstimator kalmanEstimator = new KalmanEstimatorBuilder().
                addPropagationConfiguration(propagatorBuilder, new ConstantProcessNoise(initialP, Q)).
                build();
        final KalmanSmoother kalmanSmoother = new KalmanSmoother(kalmanEstimator);

        // Add the measurements to the Kalman filter
        Propagator[] estimated = kalmanSmoother.processMeasurements(measurements);

        // Test backwards smoothing
        List<PhysicalEstimatedState> smoothedStates = kalmanSmoother.backwardsSmooth();

I have a few questions:

  • Does wrapping a filter seem like a reasonable way to present a smoother to a user?
  • At the moment I have the smoother returning a list of estimated states, but an Ephemeris would be better. How does an ephemeris handle measurement parameters?
  • I’ve added another layer to an already messy arrangement of interfaces and abstract classes in the sequential estimation package. Is that OK?

Hi Mark,

First of all thanks a lot for tackling this.

Here are my few cents from reading your post alone:
The observer is indeed the way to go to collect all needed info. However I would create a dedicated “RTS Smoothing Observer” class that does nothing else. The backward smoother itself would take it as an argument (either constructor or method). And I would leave the smoothing away from the filter interfaces. For me it’s purely post process, and it’s fine if users have a little work to do by passing stuff along.

I’m not sure to understand tho, I thought a smoother was very much tight to the way the filtering was done, so basically you should use unscented smoothing after the UKF, and not the EKF. But you’re saying it’s the same code for everyone here? Or it’s the same generic logic with specific implementations for the update part?

Cheers,
Romain

An observer is a good idea. I’ll try it and see how it goes. I think we’ll need to stitch in a Kalman filter observer on the Hipparchus side as well.

You can think of the smoother in a similar way to the KF/EKF/UKF update step. Once you have calculated the predicted quantities and the gain, the state and covariance update equations are identical. Similarly, we calculate the smoothing gain in a slightly different way for each filter (you can see the getStateCrossCovariance method for the KalmanFilter in Hipparchus), but once those are collected the smoothing recursion is the same for all the filter types.

The observer technique worked nicely, @Serrof! I’ve added an observer mechanism to the Hipparchus filter classes, so that I can collect up the required data in a structured way. The smoother is also implemented as an observer on the orekit side. It’s instantiation looks a bit odd (I need some info from the estimator inside the smoother observer), but it seems to work nicely.

An example is:

        // Build the Kalman filter
        final KalmanEstimator kalmanEstimator = new KalmanEstimatorBuilder().
                addPropagationConfiguration(propagatorBuilder, new ConstantProcessNoise(initialP, Q)).
                build();

        // Add smoother observer to filter
        final RtsSmoother rtsSmoother = new RtsSmoother(kalmanEstimator);
        kalmanEstimator.setObserver(rtsSmoother);

        // Add the measurements to the Kalman filter
        Propagator[] estimated = kalmanEstimator.processMeasurements(measurements);

        // Test backwards smoothing
        List<PhysicalEstimatedState> smoothedStates = rtsSmoother.backwardsSmooth();

I guess the smoother could add itself as an observer, but that might knock out an existing observer?

Hi Mark,

If one must, they can wrap several observers in a custom observer.

If we’re happy with this, I can merge on Hipparchus and then Orekit.

Cheers,
Romain.

1 Like

I’m happy with that.
The Hipparchus side is ready to merge. On the orekit side I’m still looking at the semi-analytic filters. It should be fairly straightforward to include the smoother for those, but I need a bit of time to confirm and add a few tests.

But the semi analytical could be part of another MR right? If we merge on Hipparchus we need to merge the changes on Orekit otherwise the develop branch will be broken due to API.