Skip to content

Commit cf9b5c0

Browse files
markusgftGiftthaler Markus (CR/PJ-AI-R31)
authored andcommitted
fixed two bugs in state observer and disturbance observer implementations, made minimal example for disturbance observer work.
1 parent 46b58db commit cf9b5c0

23 files changed

+488
-214
lines changed

ct_optcon/examples/KalmanDisturbanceFiltering.cpp

Lines changed: 234 additions & 87 deletions
Large diffs are not rendered by default.

ct_optcon/examples/KalmanFiltering.cpp

Lines changed: 81 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ Licensed under Apache2 license (see LICENSE file in main directory)
1212

1313
#include <ct/optcon/optcon.h>
1414
#include <CustomController.h> // Using the custom controller from ct_core examples.
15+
#include "exampleDir.h"
1516

1617
int main(int argc, char** argv)
1718
{
19+
// file with kalman weights
20+
std::string pathToWeights = ct::optcon::exampleDir + "/kalmanFilterWeights.info";
21+
1822
// a damped oscillator has two states, position and velocity
1923
const size_t STATE_DIM = ct::core::SecondOrderSystem::STATE_DIM; // = 2
2024
const size_t CONTROL_DIM = ct::core::SecondOrderSystem::CONTROL_DIM; // = 1
@@ -46,9 +50,14 @@ int main(int argc, char** argv)
4650
ct::core::ControlVectorArray<CONTROL_DIM> controls;
4751
ct::core::tpl::TimeArray<double> times;
4852

53+
ct::core::StateMatrix<STATE_DIM> process_var;
54+
ct::core::loadMatrix(pathToWeights, "process_noise.process_var", process_var);
55+
56+
ct::core::GaussianNoise position_process_noise(0.0, process_var(0, 0));
57+
ct::core::GaussianNoise velocity_process_noise(0.0, process_var(1, 1));
58+
4959
// simulate 100 steps
5060
double dt = 0.001;
51-
double t0 = 0.0;
5261
size_t nSteps = 100;
5362
states.push_back(x);
5463
for (size_t i = 0; i < nSteps; i++)
@@ -59,6 +68,10 @@ int main(int argc, char** argv)
5968
controls.push_back(u_temp);
6069

6170
integrator.integrate_n_steps(x, i * dt, 1, dt);
71+
72+
position_process_noise.noisify(x(0)); // Position noise.
73+
velocity_process_noise.noisify(x(1)); // Velocity noise.
74+
6275
states.push_back(x);
6376
times.push_back(i * dt);
6477
}
@@ -67,57 +80,105 @@ int main(int argc, char** argv)
6780
ct::core::OutputStateMatrix<OUTPUT_DIM, STATE_DIM> C;
6881
C.setIdentity();
6982

70-
// create Kalman Filter weighting matrices
71-
ct::core::StateMatrix<STATE_DIM, double> Q = ct::core::StateMatrix<STATE_DIM, double>::Identity();
72-
ct::core::OutputMatrix<OUTPUT_DIM, double> R = 10 * ct::core::OutputMatrix<OUTPUT_DIM, double>::Identity();
83+
// load Kalman Filter weighting matrices from file
84+
ct::core::StateMatrix<STATE_DIM> Q, dFdv;
85+
ct::core::OutputMatrix<OUTPUT_DIM> R;
86+
ct::core::loadMatrix(pathToWeights, "kalman_weights.Q", Q);
87+
ct::core::loadMatrix(pathToWeights, "kalman_weights.R", R);
88+
std::cout << "Loaded Kalman R as " << std::endl << R << std::endl;
89+
std::cout << "Loaded Kalman Q as " << std::endl << Q << std::endl;
90+
91+
dFdv.setIdentity(); // todo tune me!
7392

7493
// create a sensitivity approximator to compute A and B matrices
7594
std::shared_ptr<ct::core::SystemLinearizer<STATE_DIM, CONTROL_DIM>> linearizer(
7695
new ct::core::SystemLinearizer<STATE_DIM, CONTROL_DIM>(oscillator));
7796
ct::core::SensitivityApproximation<STATE_DIM, CONTROL_DIM> sensApprox(dt, linearizer);
7897

7998
// set up Extended Kalman Filter
80-
ct::optcon::ExtendedKalmanFilter<STATE_DIM> ekf(states[0]);
99+
ct::optcon::ExtendedKalmanFilter<STATE_DIM> ekf(states[0], R);
81100

82101
// the observer is supplied with a dynamic model identical to the one used above for data generation
83102
std::shared_ptr<ct::core::SecondOrderSystem> oscillator_observer_model(new ct::core::SecondOrderSystem(w_n));
84-
103+
85104
// initialize the observer
86105
ct::optcon::StateObserver<OUTPUT_DIM, STATE_DIM, CONTROL_DIM, ct::optcon::ExtendedKalmanFilter<STATE_DIM>>
87-
stateObserver(oscillator_observer_model, sensApprox, dt, C, ekf, Q, R);
106+
stateObserver(oscillator_observer_model, sensApprox, C, R, ekf, Q, R, dFdv);
107+
88108

89-
ct::core::GaussianNoise position_measurement_noise(0, 0.01);
90-
ct::core::GaussianNoise velocity_measurement_noise(0, 0.1);
109+
ct::core::StateMatrix<STATE_DIM> meas_var;
110+
ct::core::loadMatrix(pathToWeights, "measurement_noise.measurement_var", meas_var);
91111

92-
ct::core::StateVectorArray<STATE_DIM> states_est;
93-
states_est.push_back(states[0]);
94-
ct::core::StateVector<STATE_DIM> xt;
112+
ct::core::GaussianNoise position_measurement_noise(0.0, meas_var(0, 0));
113+
ct::core::GaussianNoise velocity_measurement_noise(0.0, meas_var(1, 1));
114+
115+
ct::core::StateVectorArray<STATE_DIM> states_est(states.size());
116+
ct::core::StateVectorArray<STATE_DIM> states_meas(states.size());
117+
states_est[0] = states[0];
118+
states_meas[0] = states[0];
95119

96120
// run the filter over the simulated data
97121
for (size_t i = 1; i < states.size(); ++i)
98122
{
99123
// compute an observation
100-
xt = states[i];
101-
position_measurement_noise.noisify(xt[0]); // Position noise.
102-
velocity_measurement_noise.noisify(xt[1]); // Velocity noise.
103-
ct::core::OutputVector<OUTPUT_DIM> y = C * xt;
124+
states_meas[i] = states[i];
125+
126+
// todo this is technically not correct, the noise enters not on the state but on the output!!!
127+
position_measurement_noise.noisify(states_meas[i](0)); // Position noise.
128+
velocity_measurement_noise.noisify(states_meas[i](1)); // Velocity noise.
129+
ct::core::OutputVector<OUTPUT_DIM> y = C * states_meas[i];
104130

105131
// Kalman filter prediction step
106-
stateObserver.predict(controls[i], dt * i);
132+
stateObserver.predict(controls[i], dt, dt * i);
107133

108134
// Kalman filter estimation step
109-
ct::core::StateVector<STATE_DIM> x_est = stateObserver.update(y);
135+
ct::core::StateVector<STATE_DIM> x_est = stateObserver.update(y, dt, dt * i);
110136

111137
// and log for printing
112-
states_est.push_back(x_est);
138+
states_est[i] = x_est;
139+
}
140+
141+
142+
// plot if plotting library built.
143+
#ifdef PLOTTING_ENABLED
144+
145+
std::vector<double> time_plot, pos_est_plot, vel_est_plot, pos_plot, vel_plot, pos_meas_plot, vel_meas_plot;
146+
for (size_t i = 0; i < times.size(); i++)
147+
{
148+
time_plot.push_back(times[i]);
149+
pos_est_plot.push_back(states_est[i](0));
150+
vel_est_plot.push_back(states_est[i](1));
151+
pos_plot.push_back(states[i](0));
152+
vel_plot.push_back(states[i](1));
153+
pos_meas_plot.push_back(states_meas[i](0));
154+
vel_meas_plot.push_back(states_meas[i](1));
113155
}
114156

115-
// print results
157+
// plot position
158+
ct::core::plot::subplot(2, 1, 1);
159+
ct::core::plot::labelPlot("pos est", time_plot, pos_est_plot);
160+
ct::core::plot::labelPlot("ground truth", time_plot, pos_plot, "r--");
161+
ct::core::plot::labelPlot("pos meas", time_plot, pos_meas_plot, "k--");
162+
ct::core::plot::legend();
163+
ct::core::plot::ylabel("pos [m]");
164+
165+
// plot velocity
166+
ct::core::plot::subplot(2, 1, 2);
167+
ct::core::plot::labelPlot("vel est", time_plot, vel_est_plot);
168+
ct::core::plot::labelPlot("ground truth", time_plot, vel_plot, "r--");
169+
ct::core::plot::labelPlot("vel meas", time_plot, vel_meas_plot, "k--");
170+
ct::core::plot::ylabel("vel [m/sec]");
171+
ct::core::plot::xlabel("time [sec]");
172+
ct::core::plot::legend();
173+
174+
ct::core::plot::show();
175+
#else // print results to command line
116176
for (size_t i = 0; i < states_est.size(); ++i)
117177
std::cout << "State\t\tState_est\n"
118178
<< std::fixed << std::setprecision(6) << states[i][0] << "\t" << states_est[i][0] << std::endl
119179
<< states[i][1] << "\t" << states_est[i][1] << std::endl
120180
<< std::endl;
181+
#endif
121182

122183
return 0;
123184
}

ct_optcon/include/ct/optcon/filter/CTSystemModel-impl.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,54 @@ namespace optcon {
1111
template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
1212
CTSystemModel<STATE_DIM, CONTROL_DIM, SCALAR>::CTSystemModel(
1313
std::shared_ptr<ct::core::ControlledSystem<STATE_DIM, CONTROL_DIM, SCALAR>> system,
14-
const ct::core::SensitivityApproximation<STATE_DIM, CONTROL_DIM, STATE_DIM / 2, STATE_DIM / 2, SCALAR>& sensApprox,
15-
double dt,
16-
unsigned numSubsteps,
14+
const SensitivityApprox_t& sensApprox,
1715
const state_matrix_t& dFdv,
1816
const ct::core::IntegrationType& intType)
1917
: system_(system->clone()),
2018
constantController_(new ct::core::ConstantController<STATE_DIM, CONTROL_DIM, SCALAR>()),
2119
sensApprox_(sensApprox),
22-
dt_(dt),
2320
dFdv_(dFdv),
24-
integrator_(system_, intType),
25-
numSubsteps_(numSubsteps)
21+
integrator_(system_, intType)
2622
{
2723
if (!system_)
28-
throw std::runtime_error("System not initialized!");
24+
throw std::runtime_error("CTSystemModel: System not initialized!");
2925

3026
// hand over constant controller for dynamics evaluation with known control inputs to the system.
3127
system_->setController(constantController_);
32-
33-
const double dtNormalized = dt_ / (numSubsteps_ + 1);
34-
sensApprox_.setTimeDiscretization(dtNormalized);
3528
}
3629

3730
template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
3831
auto CTSystemModel<STATE_DIM, CONTROL_DIM, SCALAR>::computeDynamics(const state_vector_t& state,
3932
const control_vector_t& u,
33+
const Time_t dt,
4034
Time_t t) -> state_vector_t
4135
{
4236
constantController_->setControl(u);
4337
state_vector_t x = state;
44-
integrator_.integrate_n_steps(x, t, numSubsteps_ + 1, dt_ / (numSubsteps_ + 1));
38+
integrator_.integrate_n_steps(x, t, 1, dt);
4539
return x;
4640
}
4741

4842
template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
4943
auto CTSystemModel<STATE_DIM, CONTROL_DIM, SCALAR>::computeDerivativeState(const state_vector_t& state,
5044
const control_vector_t& u,
45+
const Time_t dt,
5146
Time_t t) -> state_matrix_t
5247
{
53-
sensApprox_.getAandB(state, u, state, int(t / dt_ * (numSubsteps_ + 1) + 0.5), numSubsteps_ + 1, A_, B_);
54-
return A_;
48+
// local vars
49+
state_matrix_t A;
50+
ct::core::StateControlMatrix<STATE_DIM, CONTROL_DIM, SCALAR> Btemp;
51+
52+
sensApprox_.setTimeDiscretization(dt);
53+
54+
sensApprox_.getAandB(state, u, state, int(t / dt), 1, A, Btemp);
55+
return A;
5556
}
5657

5758
template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
5859
auto CTSystemModel<STATE_DIM, CONTROL_DIM, SCALAR>::computeDerivativeNoise(const state_vector_t& state,
5960
const control_vector_t& control,
61+
const Time_t dt,
6062
Time_t t) -> state_matrix_t
6163
{
6264
return dFdv_;

ct_optcon/include/ct/optcon/filter/CTSystemModel.h

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,31 @@ class CTSystemModel final : public SystemModelBase<STATE_DIM, CONTROL_DIM, SCALA
3434
using typename Base::state_vector_t;
3535
using typename Base::Time_t;
3636

37+
using SensitivityApprox_t =
38+
ct::core::SensitivityApproximation<STATE_DIM, CONTROL_DIM, STATE_DIM / 2, STATE_DIM / 2, SCALAR>;
39+
3740
//! Constructor. Takes in the system with defined controller, and sens approximator for computing the derivatives
3841
CTSystemModel(std::shared_ptr<ct::core::ControlledSystem<STATE_DIM, CONTROL_DIM, SCALAR>> system,
39-
const ct::core::SensitivityApproximation<STATE_DIM, CONTROL_DIM, STATE_DIM / 2, STATE_DIM / 2, SCALAR>&
40-
sensApprox,
41-
double dt,
42-
unsigned numSubsteps = 0u,
43-
const state_matrix_t& dFdv = state_matrix_t::Identity(),
42+
const SensitivityApprox_t& sensApprox,
43+
const state_matrix_t& dFdv,
4444
const ct::core::IntegrationType& intType = ct::core::IntegrationType::EULERCT);
4545

4646
//! Propagates the system giving the next state as output. Control input is generated by the system controller.
47-
state_vector_t computeDynamics(const state_vector_t& state, const control_vector_t& u, Time_t t) override;
47+
state_vector_t computeDynamics(const state_vector_t& state,
48+
const control_vector_t& u,
49+
const Time_t dt,
50+
Time_t t) override;
4851

4952
//! Computes the derivative w.r.t state. Control input is generated by the system controller.
50-
state_matrix_t computeDerivativeState(const state_vector_t& state, const control_vector_t& u, Time_t t) override;
53+
state_matrix_t computeDerivativeState(const state_vector_t& state,
54+
const control_vector_t& u,
55+
const Time_t dt,
56+
Time_t t) override;
5157

5258
//! Computes the derivative w.r.t noise. Control input is generated by the system controller.
5359
state_matrix_t computeDerivativeNoise(const state_vector_t& state,
5460
const control_vector_t& control,
61+
const Time_t dt,
5562
Time_t t) override;
5663

5764
protected:
@@ -62,25 +69,13 @@ class CTSystemModel final : public SystemModelBase<STATE_DIM, CONTROL_DIM, SCALA
6269
std::shared_ptr<ct::core::ConstantController<STATE_DIM, CONTROL_DIM, SCALAR>> constantController_;
6370

6471
//! The sensitivity approximator
65-
ct::core::SensitivityApproximation<STATE_DIM, CONTROL_DIM, STATE_DIM / 2, STATE_DIM / 2, SCALAR> sensApprox_;
66-
67-
//! Time step for the sensitivity approximator.
68-
double dt_;
72+
SensitivityApprox_t sensApprox_;
6973

7074
//! Derivative w.r.t. noise.
7175
state_matrix_t dFdv_;
7276

7377
//! Integrator.
7478
ct::core::Integrator<STATE_DIM, SCALAR> integrator_;
75-
76-
//! Integration substeps.
77-
unsigned numSubsteps_;
78-
79-
//! dFdx or the derivative w.r.t. state.
80-
state_matrix_t A_;
81-
82-
//! Matrix for storing control linearization.
83-
ct::core::StateControlMatrix<STATE_DIM, CONTROL_DIM, SCALAR> B_;
8479
};
8580

8681
} // namespace optcon

ct_optcon/include/ct/optcon/filter/DisturbanceObserver-impl.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,19 @@ template <size_t OUTPUT_DIM, size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_D
1212
DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR, SCALAR>::DisturbanceObserver(
1313
std::shared_ptr<DisturbedSystem_t> system,
1414
const SensitivityApproximation_t& sensApprox,
15-
double dt,
1615
const output_estimate_matrix_t& Caug,
1716
const ESTIMATOR& estimator,
1817
const estimate_matrix_t& Qaug,
19-
const output_matrix_t& R)
20-
: Base(system, sensApprox, dt, Caug, estimator, Qaug, R)
18+
const output_matrix_t& R,
19+
const estimate_matrix_t& dFdv)
20+
: Base(system,
21+
sensApprox,
22+
Caug,
23+
output_matrix_t::Zero(), // todo this should actually be D
24+
estimator,
25+
Qaug,
26+
R,
27+
dFdv)
2128
{
2229
}
2330

@@ -27,24 +34,26 @@ DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR, SCA
2734
const SensitivityApproximation_t& sensApprox,
2835
const ESTIMATOR& estimator,
2936
const DisturbanceObserverSettings<OUTPUT_DIM, ESTIMATE_DIM, SCALAR>& do_settings)
30-
: Base(system, sensApprox, do_settings.dt, do_settings.C, estimator, do_settings.Qaug, do_settings.R)
37+
: Base(system, sensApprox, do_settings.dFdv, do_settings.C, estimator, do_settings.Qaug, do_settings.R)
3138
{
3239
}
3340

3441
template <size_t OUTPUT_DIM, size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_DIM, class ESTIMATOR, typename SCALAR>
3542
auto DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR, SCALAR>::predict(
3643
const control_vector_t& u,
44+
const ct::core::Time& dt,
3745
const Time_t& t) -> estimate_vector_t
3846
{
39-
return this->estimator_.template predict<CONTROL_DIM>(this->f_, u, this->Q_, t);
47+
return this->estimator_.template predict<CONTROL_DIM>(this->f_, u, this->Q_, dt, t);
4048
}
4149

4250
template <size_t OUTPUT_DIM, size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_DIM, class ESTIMATOR, typename SCALAR>
4351
auto DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR, SCALAR>::update(
4452
const output_vector_t& y,
45-
const Time_t&) -> estimate_vector_t
53+
const ct::core::Time& dt,
54+
const Time_t& t) -> estimate_vector_t
4655
{
47-
return this->estimator_.template update<OUTPUT_DIM>(y, this->h_, this->R_);
56+
return this->estimator_.template update<OUTPUT_DIM>(y, this->h_, this->R_, dt, t);
4857
}
4958

5059
template <size_t OUTPUT_DIM, size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_DIM, class ESTIMATOR, typename SCALAR>
@@ -61,5 +70,13 @@ auto DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR
6170
return this->estimator_.getEstimate().template tail<DIST_DIM>();
6271
}
6372

73+
template <size_t OUTPUT_DIM, size_t STATE_DIM, size_t DIST_DIM, size_t CONTROL_DIM, class ESTIMATOR, typename SCALAR>
74+
auto DisturbanceObserver<OUTPUT_DIM, STATE_DIM, DIST_DIM, CONTROL_DIM, ESTIMATOR, SCALAR>::getCovarianceMatrix()
75+
-> const estimate_matrix_t&
76+
{
77+
return this->estimator_.getCovarianceMatrix();
78+
}
79+
80+
6481
} // namespace optcon
6582
} // namespace ct

0 commit comments

Comments
 (0)