Skip to content

Commit 4f1afd4

Browse files
markusgftGiftthaler Markus (CR/PJ-AI-R31)
authored andcommitted
Fixing major bug in unscented Kalman Filter, moved system and measurement model pointers to Estimator base, deleted now redundant observer classes.
1 parent cf9b5c0 commit 4f1afd4

18 files changed

+342
-676
lines changed

ct_optcon/examples/KalmanDisturbanceFiltering.cpp

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,26 @@ Licensed under Apache2 license (see LICENSE file in main directory)
1919
#include "exampleDir.h"
2020

2121
// a damped oscillator has two states, position and velocity
22-
const size_t STATE_DIM = ct::core::SecondOrderSystem::STATE_DIM; // = 2
23-
const size_t CONTROL_DIM = ct::core::SecondOrderSystem::CONTROL_DIM; // = 1
24-
const size_t OUTPUT_DIM = STATE_DIM; // we measure the full state
25-
const size_t DIST_DIM = CONTROL_DIM; // we consider an input disturbance
22+
const size_t state_dim = ct::core::SecondOrderSystem::STATE_DIM; // = 2
23+
const size_t control_dim = ct::core::SecondOrderSystem::CONTROL_DIM; // = 1
24+
const size_t output_dim = state_dim; // we measure the full state
25+
const size_t dist_dim = control_dim; // we consider an input disturbance
2626

2727

2828
/*!
2929
* \brief An example controller, in which we artifically add a time-varying disturbance.
3030
* @note this is only required for this simulated example. Not required for proper simulator/hardware setups.
3131
*/
32-
class CustomController : public ct::core::Controller<STATE_DIM, CONTROL_DIM>
32+
class CustomController : public ct::core::Controller<state_dim, control_dim>
3333
{
3434
public:
3535
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
3636

37-
static const size_t STATE_DIM = 2;
38-
static const size_t CONTROL_DIM = 1;
37+
static const size_t state_dim = 2;
38+
static const size_t control_dim = 1;
3939

4040
//! default constructor
41-
CustomController(const ct::core::ControlVector<CONTROL_DIM>& uff_max, // feedforward control amplitude
41+
CustomController(const ct::core::ControlVector<control_dim>& uff_max, // feedforward control amplitude
4242
const double& uff_frequency, // frequency of the feedforward control osciallation
4343
const double& kp, // P gain
4444
const double& kd, // D gain
@@ -58,25 +58,25 @@ class CustomController : public ct::core::Controller<STATE_DIM, CONTROL_DIM>
5858
CustomController* clone() const override { return new CustomController(*this); }
5959

6060
//! override the compute control method with a custom control law which includes a disturbance
61-
void computeControl(const ct::core::StateVector<STATE_DIM>& state,
61+
void computeControl(const ct::core::StateVector<state_dim>& state,
6262
const double& t,
63-
ct::core::ControlVector<CONTROL_DIM>& controlAction) override
63+
ct::core::ControlVector<control_dim>& controlAction) override
6464
{
6565
controlAction = uff_max_ * std::sin(t * uff_frequency_); // apply feedforward control
6666
controlAction += getSimulatedDisturbance(t); // apply simulated disturbance
6767
controlAction(0) += -kp_ * state(0) - kd_ * state(1); // add feedback control
6868
}
6969

7070
//! simulate the disturbance allow to reconstruct it from outside
71-
ct::core::ControlVector<CONTROL_DIM> getSimulatedDisturbance(const double& t)
71+
ct::core::ControlVector<control_dim> getSimulatedDisturbance(const double& t)
7272
{
73-
ct::core::ControlVector<CONTROL_DIM> dist_in;
73+
ct::core::ControlVector<control_dim> dist_in;
7474
dist_in(0) = d_ * std::cos(t * t * d_freq_);
7575
return dist_in;
7676
}
7777

7878
private:
79-
ct::core::ControlVector<CONTROL_DIM> uff_max_; //! feedforward control action
79+
ct::core::ControlVector<control_dim> uff_max_; //! feedforward control action
8080
double uff_frequency_; // frequency of feedforward oscillation
8181
double kp_; //! P gain
8282
double kd_; //! D gain
@@ -90,21 +90,21 @@ class CustomController : public ct::core::Controller<STATE_DIM, CONTROL_DIM>
9090
* for dynamics prediction and computing derivatives.
9191
* @note this system is not used for simulation, but for filtering.
9292
*/
93-
template <size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_DIM, typename SCALAR = double>
94-
class CustomDisturbedSystem : public ct::optcon::DisturbedSystem<STATE_DIM, DIST_DIM, CONTROL_DIM, SCALAR>
93+
template <size_t state_dim, size_t dist_dim, size_t control_dim, typename SCALAR = double>
94+
class CustomDisturbedSystem : public ct::optcon::DisturbedSystem<state_dim, dist_dim, control_dim, SCALAR>
9595
{
9696
public:
9797
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
9898

9999
//! constructor
100-
CustomDisturbedSystem(std::shared_ptr<ct::core::ControlledSystem<STATE_DIM, CONTROL_DIM, SCALAR>> sys)
101-
: ct::optcon::DisturbedSystem<STATE_DIM, DIST_DIM, CONTROL_DIM, SCALAR>(sys->getController()), system_(sys)
100+
CustomDisturbedSystem(std::shared_ptr<ct::core::ControlledSystem<state_dim, control_dim, SCALAR>> sys)
101+
: ct::optcon::DisturbedSystem<state_dim, dist_dim, control_dim, SCALAR>(sys->getController()), system_(sys)
102102
{
103103
}
104104

105105
//! copy constructor
106106
CustomDisturbedSystem(const CustomDisturbedSystem& other)
107-
: ct::optcon::DisturbedSystem<STATE_DIM, DIST_DIM, CONTROL_DIM, SCALAR>(*this), system_(other.system_->clone())
107+
: ct::optcon::DisturbedSystem<state_dim, dist_dim, control_dim, SCALAR>(*this), system_(other.system_->clone())
108108
{
109109
}
110110

@@ -114,27 +114,27 @@ class CustomDisturbedSystem : public ct::optcon::DisturbedSystem<STATE_DIM, DIST
114114
/*!
115115
* Override the computeControlledDynamics() with a custom update rule.
116116
*/
117-
void computeControlledDynamics(const ct::core::StateVector<STATE_DIM + DIST_DIM, SCALAR>& state,
117+
void computeControlledDynamics(const ct::core::StateVector<state_dim + dist_dim, SCALAR>& state,
118118
const SCALAR& t,
119-
const ct::core::ControlVector<CONTROL_DIM, SCALAR>& control,
120-
ct::core::StateVector<STATE_DIM + DIST_DIM, SCALAR>& derivative) override
119+
const ct::core::ControlVector<control_dim, SCALAR>& control,
120+
ct::core::StateVector<state_dim + dist_dim, SCALAR>& derivative) override
121121
{
122122
derivative.setZero();
123-
ct::core::StateVector<STATE_DIM, SCALAR> tempDerivative;
123+
ct::core::StateVector<state_dim, SCALAR> tempDerivative;
124124

125125
// the control consists of actual commanded control plus the estimated input disturbance,
126126
// which is the augmented part of the state vector
127-
ct::core::ControlVector<CONTROL_DIM, SCALAR> disturbed_control = control + state.template tail<DIST_DIM>();
127+
ct::core::ControlVector<control_dim, SCALAR> disturbed_control = control + state.template tail<dist_dim>();
128128

129129
// the dynamics of the augmented system
130-
system_->computeControlledDynamics(state.head(STATE_DIM), t, disturbed_control, tempDerivative);
131-
derivative.template head<STATE_DIM>() = tempDerivative;
130+
system_->computeControlledDynamics(state.head(state_dim), t, disturbed_control, tempDerivative);
131+
derivative.template head<state_dim>() = tempDerivative;
132132
}
133133

134134

135135
private:
136136
// the nominal system (the one we are trying to control)
137-
std::shared_ptr<ct::core::ControlledSystem<STATE_DIM, CONTROL_DIM, SCALAR>> system_;
137+
std::shared_ptr<ct::core::ControlledSystem<state_dim, control_dim, SCALAR>> system_;
138138
};
139139

140140

@@ -149,7 +149,7 @@ int main(int argc, char** argv)
149149
ct::core::loadScalar(configFile, "experiment_settings.nSteps", nSteps);
150150

151151
// state vector (initial state)
152-
ct::core::StateVector<STATE_DIM> x;
152+
ct::core::StateVector<state_dim> x;
153153
ct::core::loadMatrix(configFile, "x0", x);
154154

155155
// create an oscillator with resonance-frequency w_n
@@ -160,7 +160,7 @@ int main(int argc, char** argv)
160160
// create our controller: a PD controller which also "simulates" a time-varying disturbance
161161
double kp = 10.0;
162162
double kd = 1.0;
163-
ct::core::ControlVector<CONTROL_DIM> uff_magnitude;
163+
ct::core::ControlVector<control_dim> uff_magnitude;
164164
double uff_frequency, disturbance_max, disturbance_freq;
165165
ct::core::loadMatrix(configFile, "experiment_settings.uff_magnitude", uff_magnitude);
166166
ct::core::loadScalar(configFile, "experiment_settings.uff_frequency", uff_frequency);
@@ -174,24 +174,23 @@ int main(int argc, char** argv)
174174
oscillator->setController(disturbed_controller);
175175

176176
// create an integrator for "simulating" the measured data
177-
ct::core::Integrator<STATE_DIM> integrator(oscillator, ct::core::IntegrationType::EULERCT);
177+
ct::core::Integrator<state_dim> integrator(oscillator, ct::core::IntegrationType::EULERCT);
178178

179-
ct::core::StateVectorArray<STATE_DIM> states; // recorded sim states
180-
ct::core::ControlVectorArray<DIST_DIM> disturbance; // recorded disturbance
179+
ct::core::StateVectorArray<state_dim> states; // recorded sim states
180+
ct::core::ControlVectorArray<dist_dim> disturbance; // recorded disturbance
181181
ct::core::tpl::TimeArray<double> times; // recorded times
182182

183183
// read process noise parameters from file
184-
ct::core::StateMatrix<STATE_DIM> process_var;
184+
ct::core::StateMatrix<state_dim> process_var;
185185
ct::core::loadMatrix(configFile, "process_noise.process_var", process_var);
186186

187187
// process noise (scaled with dt)
188188
ct::core::GaussianNoise position_process_noise(0.0, dt * process_var(0, 0));
189189
ct::core::GaussianNoise velocity_process_noise(0.0, dt * process_var(1, 1));
190190

191191
// simulate the disturbed system to generate data
192-
ct::core::Time t0 = 0.0;
193192
states.push_back(x);
194-
for (size_t i = 0; i < nSteps; i++)
193+
for (int i = 0; i < nSteps; i++)
195194
{
196195
// integrate system
197196
integrator.integrate_n_steps(x, i * dt, 1, dt);
@@ -216,85 +215,87 @@ int main(int argc, char** argv)
216215
std::shared_ptr<CustomController> controller_nominal(
217216
new CustomController(uff_magnitude, uff_frequency, kp, kd, 0.0, 0.0));
218217

219-
std::shared_ptr<CustomDisturbedSystem<STATE_DIM, DIST_DIM, CONTROL_DIM>> customdisturbedSystem(
220-
new CustomDisturbedSystem<STATE_DIM, DIST_DIM, CONTROL_DIM>(oscillator_obs));
218+
std::shared_ptr<CustomDisturbedSystem<state_dim, dist_dim, control_dim>> customdisturbedSystem(
219+
new CustomDisturbedSystem<state_dim, dist_dim, control_dim>(oscillator_obs));
221220

222221
// Observation matrix for the state
223-
ct::core::OutputStateMatrix<OUTPUT_DIM, STATE_DIM> C;
222+
ct::core::OutputStateMatrix<output_dim, state_dim> C;
224223
C.setIdentity();
225224

226225
// Observation matrix for the disturbance (assuming the disturbance does not enter the output equation)
227-
ct::core::OutputStateMatrix<OUTPUT_DIM, DIST_DIM> Cd;
226+
ct::core::OutputStateMatrix<output_dim, dist_dim> Cd;
228227
Cd.setZero();
229228

230229
// Total observation matrix, state and disturbance combined
231-
ct::core::OutputStateMatrix<OUTPUT_DIM, STATE_DIM + DIST_DIM, double> Caug;
230+
ct::core::OutputStateMatrix<output_dim, state_dim + dist_dim, double> Caug;
232231
Caug << C, Cd;
233232

234233
// Kalman filter weighting matrices Q, dFdv and R
235-
ct::core::StateMatrix<STATE_DIM + DIST_DIM> Qaug, dFdv;
236-
ct::core::OutputMatrix<OUTPUT_DIM> R;
234+
ct::core::StateMatrix<state_dim + dist_dim> Qaug, dFdv;
235+
ct::core::OutputMatrix<output_dim> R;
237236
ct::core::loadMatrix(configFile, "kalman_weights.Qaug", Qaug);
238237
ct::core::loadMatrix(configFile, "kalman_weights.R", R);
239238

240239
dFdv.setIdentity(); // todo tune me
241240

242241
// create a sensitivity approximator to obtain discrete-time dynamics matrices
243-
std::shared_ptr<ct::core::SystemLinearizer<STATE_DIM + DIST_DIM, CONTROL_DIM>> linearizer(
244-
new ct::core::SystemLinearizer<STATE_DIM + DIST_DIM, CONTROL_DIM>(customdisturbedSystem));
245-
ct::core::SensitivityApproximation<STATE_DIM + DIST_DIM, CONTROL_DIM> sensApprox(dt, linearizer);
242+
std::shared_ptr<ct::core::SystemLinearizer<state_dim + dist_dim, control_dim>> linearizer(
243+
new ct::core::SystemLinearizer<state_dim + dist_dim, control_dim>(customdisturbedSystem));
244+
ct::core::SensitivityApproximation<state_dim + dist_dim, control_dim> sensApprox(dt, linearizer);
245+
246+
// set up the system model
247+
std::shared_ptr<ct::optcon::CTSystemModel<state_dim + dist_dim, control_dim>> sysModel(
248+
new ct::optcon::CTSystemModel<state_dim + dist_dim, control_dim>(customdisturbedSystem, sensApprox, dFdv));
249+
250+
// set up the measurement model
251+
std::shared_ptr<ct::optcon::LinearMeasurementModel<output_dim, state_dim + dist_dim>> measModel(
252+
new ct::optcon::LTIMeasurementModel<output_dim, state_dim + dist_dim>(Caug));
246253

247254
// load measurement noise data from file
248-
ct::core::StateMatrix<STATE_DIM> meas_var;
255+
ct::core::StateMatrix<state_dim> meas_var;
249256
ct::core::loadMatrix(configFile, "measurement_noise.measurement_var", meas_var);
250257
ct::core::GaussianNoise position_measurement_noise(0.0, meas_var(0, 0));
251258
ct::core::GaussianNoise velocity_measurement_noise(0.0, meas_var(1, 1));
252259

253260
// generate initial state for the estimator (with noise, disturbance assumed to be zero at beginning)
254-
ct::core::StateVector<STATE_DIM + DIST_DIM> x0aug;
261+
ct::core::StateVector<state_dim + dist_dim> x0aug;
255262
x0aug << states[0], 0.0;
256263
position_measurement_noise.noisify(x0aug(0));
257264
velocity_measurement_noise.noisify(x0aug(1));
258265

259-
// create instance of the extended Kalman Filter
260-
// initialize it estimated starting covariance equal to Qaug
261-
ct::optcon::ExtendedKalmanFilter<STATE_DIM + DIST_DIM> ekf(x0aug, Qaug);
262-
263-
// create instance of the disturbance observer
264-
ct::optcon::DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM,
265-
ct::optcon::ExtendedKalmanFilter<STATE_DIM + DIST_DIM>>
266-
disturbanceObserver(customdisturbedSystem, sensApprox, Caug, ekf, Qaug, R, dFdv);
267-
268-
269266
// data containers for logging data while estimating
270-
ct::core::StateVectorArray<STATE_DIM + DIST_DIM> states_est(states.size());
271-
ct::core::OutputVectorArray<OUTPUT_DIM> output_meas(states.size());
272-
ct::core::StateMatrixArray<STATE_DIM + DIST_DIM> cov_est(states.size());
267+
ct::core::StateVectorArray<state_dim + dist_dim> states_est(states.size());
268+
ct::core::OutputVectorArray<output_dim> output_meas(states.size());
269+
ct::core::StateMatrixArray<state_dim + dist_dim> cov_est(states.size());
273270
states_est[0] = x0aug;
274271
output_meas[0] = states[0];
275-
cov_est[0] = disturbanceObserver.getCovarianceMatrix();
272+
cov_est[0] = Qaug;
273+
274+
275+
// set up Extended Kalman Filter
276+
ct::optcon::ExtendedKalmanFilter<state_dim + dist_dim, control_dim, output_dim> ekf(sysModel, measModel, Qaug, R, x0aug, Qaug);
276277

277278

278279
// run the filter over the simulated data
279280
for (size_t i = 1; i < states.size(); ++i)
280281
{
281282
// compute an observation
282-
ct::core::OutputVector<OUTPUT_DIM> y = C * states[i] + Cd * disturbance[i - 1];
283+
ct::core::OutputVector<output_dim> y = C * states[i] + Cd * disturbance[i - 1];
283284
position_measurement_noise.noisify(y(0)); // Position noise.
284285
velocity_measurement_noise.noisify(y(1)); // Velocity noise.
285286
output_meas[i] = y;
286287

287288
// this is the control input that we would have "measured"
288-
ct::core::ControlVector<CONTROL_DIM> nominal_control;
289-
controller_nominal->computeControl(states_est[i - 1].template head<STATE_DIM>(), dt * (i - 1), nominal_control);
289+
ct::core::ControlVector<control_dim> nominal_control;
290+
controller_nominal->computeControl(states_est[i - 1].template head<state_dim>(), dt * (i - 1), nominal_control);
290291

291292
// Kalman filter prediction step
292-
disturbanceObserver.predict(nominal_control, dt, dt * i);
293+
ekf.predict(nominal_control, dt, dt * i);
293294

294295
// Kalman filter estimation step (state + disturbance)
295-
states_est[i] = disturbanceObserver.update(y, dt, dt * i);
296+
states_est[i] = ekf.update(y, dt, dt * i);
296297

297-
cov_est[i] = disturbanceObserver.getCovarianceMatrix();
298+
cov_est[i] = ekf.getCovarianceMatrix();
298299
}
299300

300301

0 commit comments

Comments
 (0)