Hello,
I have been doing some OD using a Kalman filter on data generated using the Generator methods to produce range data with a sigma value of 0.5m. I perform a batch least-squares procedure initially to get a covariance matrix to use with the EKF and I then feed each synthetic measurement into the EKF for processing. I am using the orbit from the generator step as an “exact” solution such that I can determine the error of my OD process in the across-, normal- and along-track directions.
The figure above shows the results that I get where the red crosses indicate an EKF cycle. Everything is great for the first ~50 hours and the error is kept very low (perhaps unrealistically so as I am not using an atmospheric model yet). In any case, at ~50 hours a sample provided to the EKF causes a “large” jump up in error, particularly in the normal-track direction. Over time this error then leads to more error growth in the along-track direction.
I have two questions about this behavior that I was hoping someone could help with.
- Is it expected that a sample in the EKF process can actually cause the estimated state to get worse (I have used a dynamic outlier filter set at 2 sigma) and is there anything that I can do to help it?
- It seems that the EKF is unable to recover after this point - is there a reason for the additional measurements not helping get the error back down to the < 5m range? I suspect that it could be the value of Q that I am using for the process noise covariance matrix but am not sure how to better define this object - any references or examples would be appreciated!
My code is below:
# extract only the satellites we want from the passes list.
estimator_convergence_thres = conf["propagator_params"]["estimator_convergence_thres"]
estimator_max_iterations = conf["propagator_params"]["estimator_max_iterations"]
estimator_max_evaluations = conf["propagator_params"]["estimator_max_evaluations"]
propagator_tolerance = conf["propagator_params"]["prop_position_error"]
# first we need to use BLS to get an initial covariance matrix for the Kalman filter.
optimizer = SequentialGaussNewtonOptimizer()
estimator = SequentialBatchLSEstimator(optimizer, prop_builder)
estimator.setParametersConvergenceThreshold(estimator_convergence_thres)
estimator.setMaxIterations(estimator_max_iterations)
estimator.setMaxEvaluations(estimator_max_evaluations)
initial_passes=6
for pass_indiv in pass_list[:initial_passes]:
[estimator.addMeasurement(r) for r in pass_indiv['range']]
# Perform BatchLSE to obtain the orbital parameters.
estimated_propagator = estimator.estimate()[0]
rms = estimator.getOptimum().getRMS()
initial_cov = estimator.getPhysicalCovariances(1e-10) # Get covariance at end of initial OD process.
print(f"\tRMS value of measured-estimated residuals is {rms:.2f} m.")
# now we set up for using a kalman filter.
initial_guess = estimated_propagator.propagate(estimated_propagator.getInitialState().getDate()).getOrbit() # initial guess at time
prop_kalman, prop_builder_kalman, prop_kalman_lvlh = create_orbital_propagator(conf, initial_guess)
# configure an outliers filter for the Kalman filtering process.
steps_initial_without_filter = 100
max_sigma = 2.0
outlier_filter = DynamicOutlierFilter(steps_initial_without_filter, max_sigma)
Q = MatrixUtils.createRealDiagonalMatrix([1e-7, 1e-7, 1e-7, 1e-10, 1e-10, 1e-10])
# Build a Kalman filter.
kalman = KalmanEstimatorBuilder().addPropagationConfiguration(prop_builder_kalman,
ConstantProcessNoise(initial_cov, Q)).build()
Thanks so much!
/Paul