Resurgence (PY2022)
Codebase for the Husky Robotics 2021-2022 rover Resurgence
Loading...
Searching...
No Matches
MultiSensorEKF.h
1#pragma once
2
3#include "../navtypes.h"
4#include "../utils/math.h"
5#include "KalmanFilterBase.h"
6#include "StateSpaceUtil.h"
7
8#include <array>
9#include <functional>
10#include <loguru.hpp>
11
12#include <Eigen/Core>
13#include <Eigen/LU>
14
15namespace filters {
16
20class Output {
21public:
22 // (state, outputnoise) -> output
23 using outputfunc_t =
24 std::function<Eigen::VectorXd(const Eigen::VectorXd&, const Eigen::VectorXd&)>;
25
26 const int stateDim;
27 const int outputDim;
28 const int outputNoiseDim;
29 const outputfunc_t outputFunc;
30 const statespace::NoiseCovMatX covMat;
31
44 Output(int stateDim, int outputDim, int outputNoiseDim, const outputfunc_t& outputFunc,
45 const statespace::NoiseCovMatX& covMat)
46 : stateDim(stateDim), outputDim(outputDim), outputNoiseDim(outputNoiseDim),
47 outputFunc(outputFunc), covMat(covMat) {
48 CHECK_F(covMat.stateDim == stateDim && covMat.size == outputNoiseDim &&
49 covMat.paramDim == outputDim);
50 }
51
61 Eigen::MatrixXd outputFuncJacobianX(const Eigen::VectorXd& state) {
62 CHECK_EQ_F(state.size(), stateDim);
63 Eigen::VectorXd outputNoise = Eigen::VectorXd::Zero(outputNoiseDim);
64 if (outFuncJacobianX) {
65 return this->outFuncJacobianX(state, outputNoise);
66 } else {
67 auto func = [&](const Eigen::VectorXd& s) { return outputFunc(s, outputNoise); };
68 return util::numericalJacobian(func, state, outputDim);
69 }
70 }
71
81 Eigen::MatrixXd outputFuncJacobianV(const Eigen::VectorXd& state) {
82 CHECK_EQ_F(state.size(), stateDim);
83 if (outFuncJacobianV) {
84 Eigen::VectorXd outputNoise = Eigen::VectorXd::Zero(outputNoiseDim);
85 return this->outFuncJacobianV(state, outputNoise);
86 } else {
87 auto func = [&](const Eigen::VectorXd& outputNoise) {
88 return outputFunc(state, outputNoise);
89 };
90 Eigen::VectorXd v = Eigen::VectorXd::Zero(outputNoiseDim);
91 return util::numericalJacobian(func, v, outputDim);
92 }
93 }
94
101 Eigen::VectorXd getOutput(const Eigen::VectorXd& state) {
102 CHECK_EQ_F(state.size(), stateDim);
103 Eigen::VectorXd outputNoise = Eigen::VectorXd::Zero(outputNoiseDim);
104 return outputFunc(state, outputNoise);
105 }
106
121 const std::function<Eigen::MatrixXd(const Eigen::VectorXd&, const Eigen::VectorXd&)>&
122 jacobianX) {
123 outFuncJacobianX = jacobianX;
124 }
125
140 const std::function<Eigen::MatrixXd(const Eigen::VectorXd&, const Eigen::VectorXd&)>&
141 jacobianV) {
142 outFuncJacobianV = jacobianV;
143 }
144
145private:
146 std::function<Eigen::MatrixXd(const Eigen::VectorXd&, const Eigen::VectorXd&)>
147 outFuncJacobianX;
148
149 std::function<Eigen::MatrixXd(const Eigen::VectorXd&, const Eigen::VectorXd&)>
150 outFuncJacobianV;
151};
152
153template <int stateDim, int inputDim, int processNoiseDim, int numOutputs>
154class MultiSensorEKF : public KalmanFilterBase<stateDim, inputDim> {
155public:
156 using state_t = navtypes::Vectord<stateDim>;
157 using input_t = navtypes::Vectord<inputDim>;
158 using processnoise_t = navtypes::Vectord<processNoiseDim>;
159
160 using statefunc_t =
161 std::function<state_t(const state_t&, const input_t&, const processnoise_t&)>;
162
163 MultiSensorEKF(
164 const statefunc_t& stateFunc,
166 double dt, const std::array<Output, numOutputs>& outputs)
167 : stateFunc(stateFunc), Q(processNoise), dt(dt), outputs(outputs) {}
168
169 void predict(const input_t& input) override {
170 navtypes::Matrixd<stateDim, stateDim> F = stateFuncJacobianX(this->xHat, input);
171 navtypes::Matrixd<processNoiseDim, processNoiseDim> processNoise =
172 Q.get(this->xHat, input);
173
174 navtypes::Matrixd<stateDim, processNoiseDim> L = stateFuncJacobianW(this->xHat, input);
175
176 this->xHat = stateFunc(this->xHat, input, processnoise_t::Zero());
177 this->P = F * this->P * F.transpose() + L * processNoise * L.transpose();
178 }
179
180 void correct(int outputIdx, const Eigen::VectorXd& measurement) {
181 Output& output = outputs[outputIdx];
182
183 Eigen::MatrixXd H = output.outputFuncJacobianX(this->xHat);
184 Eigen::MatrixXd outputNoise = output.covMat.get(this->xHat, measurement);
185 Eigen::MatrixXd M = output.outputFuncJacobianV(this->xHat);
186 Eigen::MatrixXd S = H * this->P * H.transpose() +
187 M * outputNoise * M.transpose(); // residual covariance
188 // near-optimal kalman gain
190 S.transpose().colPivHouseholderQr().solve(H * this->P.transpose()).transpose();
191 Eigen::VectorXd y =
192 measurement -
193 output.outputFunc(this->xHat, Eigen::VectorXd::Zero(
194 output.outputNoiseDim)); // measurement residual
195
196 this->xHat = this->xHat + K * y;
197 this->P = (Eigen::Matrix<double, stateDim, stateDim>::Identity() - K * H) * this->P;
198 }
199
200 template <int outputIdx>
201 void correct(const Eigen::VectorXd& measurement) {
202 static_assert(0 <= outputIdx && outputIdx < numOutputs);
203 correct(outputIdx, measurement);
204 }
205
206 void setStateFuncJacobianX(
207 const std::function<navtypes::Matrixd<stateDim, stateDim>(
208 const state_t&, const input_t&, const processnoise_t&)>& stateFuncJacobianX) {
209 this->stateFuncJacobianXSol = stateFuncJacobianX;
210 }
211
212 void setStateFuncJacobianW(
213 const std::function<navtypes::Matrixd<stateDim, processNoiseDim>(
214 const state_t&, const input_t&, const processnoise_t&)>& stateFuncJacobianW) {
215 this->stateFuncJacobianWSol = stateFuncJacobianW;
216 }
217
218 navtypes::Matrixd<stateDim, stateDim> stateFuncJacobianX(const state_t& x,
219 const input_t& u) {
220 processnoise_t w = processnoise_t::Zero();
221 if (stateFuncJacobianXSol) {
222 return stateFuncJacobianXSol(x, u, w);
223 } else {
224 auto func = [&](const state_t& state) { return stateFunc(state, u, w); };
225 return util::numericalJacobian(func, x, stateDim);
226 }
227 }
228
229 navtypes::Matrixd<stateDim, processNoiseDim> stateFuncJacobianW(const state_t& x,
230 const input_t& u) {
231 processnoise_t w = processnoise_t::Zero();
232 if (stateFuncJacobianWSol) {
233 return stateFuncJacobianWSol(x, u, w);
234 } else {
235 auto func = [&](const processnoise_t& noise) { return stateFunc(x, u, noise); };
236 return util::numericalJacobian(func, w, stateDim);
237 }
238 }
239
240private:
241 statefunc_t stateFunc;
243 double dt;
245
246 std::function<navtypes::Matrixd<stateDim, stateDim>(const state_t&, const input_t&,
247 const processnoise_t&)>
248 stateFuncJacobianXSol;
249
250 std::function<navtypes::Matrixd<stateDim, processNoiseDim>(const state_t&, const input_t&,
251 const processnoise_t&)>
252 stateFuncJacobianWSol;
253};
254
255} // namespace filters
TransposeReturnType transpose()
static const IdentityReturnType Identity()
Represent an output for a system.
Definition MultiSensorEKF.h:20
Output(int stateDim, int outputDim, int outputNoiseDim, const outputfunc_t &outputFunc, const statespace::NoiseCovMatX &covMat)
Create an output for a model.
Definition MultiSensorEKF.h:44
Eigen::MatrixXd outputFuncJacobianX(const Eigen::VectorXd &state)
Calculate the output function jacobian at the given state wrt state.
Definition MultiSensorEKF.h:61
void setOutputFuncJacobianV(const std::function< Eigen::MatrixXd(const Eigen::VectorXd &, const Eigen::VectorXd &)> &jacobianV)
Set an analytic solution to dh/dv.
Definition MultiSensorEKF.h:139
void setOutputFuncJacobianX(const std::function< Eigen::MatrixXd(const Eigen::VectorXd &, const Eigen::VectorXd &)> &jacobianX)
Set an analytic solution to dh/dx.
Definition MultiSensorEKF.h:120
Eigen::VectorXd getOutput(const Eigen::VectorXd &state)
Evaluate the output function at the given state.
Definition MultiSensorEKF.h:101
Eigen::MatrixXd outputFuncJacobianV(const Eigen::VectorXd &state)
Calculate the output function jacobian at the given state wrt the output noise.
Definition MultiSensorEKF.h:81
Represents a square noise covariance matrix.
Definition StateSpaceUtil.h:249
Eigen::Matrix< double, size, size > get(const state_t &x, const param_t &param)
Gets the noise covariance matrix, given the current state and additonal parameter.
Definition StateSpaceUtil.h:298
uint32_t v
Eigen::MatrixXd numericalJacobian(const std::function< Eigen::VectorXd(const Eigen::VectorXd &)> &func, const Eigen::VectorXd &x, int outputDim)
Estimate the jacobian of a multivariate function using finite differences.
Definition math.cpp:6