blob: 93c88d4bb5e69650cbd9b89ee5a17526a42b81f9 [file] [log] [blame]
Austin Schuh54e5bb52018-02-19 01:09:18 -08001#include "y2018/control_loops/superstructure/arm/ekf.h"
2
3#include "Eigen/Dense"
4#include <iostream>
5
6#include "frc971/control_loops/jacobian.h"
7#include "y2018/control_loops/superstructure/arm/dynamics.h"
8
9namespace y2018 {
10namespace control_loops {
11namespace superstructure {
12namespace arm {
13
14namespace {
15// TODO(austin): When tuning this, make sure to verify that you are waiting
16// enough cycles to make sure it converges at startup. Otherwise you will have a
17// bad day.
18const ::Eigen::Matrix<double, 6, 6> Q_covariance(
19 (::Eigen::DiagonalMatrix<double, 6>().diagonal() << ::std::pow(0.1, 2),
20 ::std::pow(2.0, 2), ::std::pow(0.1, 2), ::std::pow(2.0, 2),
21 ::std::pow(0.80, 2), ::std::pow(0.70, 2))
22 .finished()
23 .asDiagonal());
24} // namespace
25
26EKF::EKF() {
27 X_hat_.setZero();
28 P_ = Q_covariance;
29 //::std::cout << "Reset P: " << P_ << ::std::endl;
30 // TODO(austin): Running the EKF 2000 cycles works, but isn't super clever.
31 // We could just solve the DARE.
32
33 for (int i = 0; i < 1000; ++i) {
34 Predict(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
35 Correct(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
36 }
37 //::std::cout << "Stabilized P: " << P_ << ::std::endl;
38 for (int i = 0; i < 1000; ++i) {
39 Predict(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
40 Correct(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
41 }
42 //::std::cout << "Really stabilized P: " << P_ << ::std::endl;
43 P_reset_ = P_;
44
45 Reset(::Eigen::Matrix<double, 4, 1>::Zero());
46}
47
48void EKF::Reset(const ::Eigen::Matrix<double, 4, 1> &X) {
49 X_hat_.setZero();
50 P_ = P_reset_;
51 X_hat_.block<4, 1>(0, 0) = X;
52}
53
54void EKF::Predict(const ::Eigen::Matrix<double, 2, 1> &U, double dt) {
55 const ::Eigen::Matrix<double, 6, 6> A =
56 ::frc971::control_loops::NumericalJacobianX<6, 2>(
57 Dynamics::UnboundedEKFDiscreteDynamics, X_hat_, U, dt);
58
59 X_hat_ = Dynamics::UnboundedEKFDiscreteDynamics(X_hat_, U, dt);
60 P_ = A * P_ * A.transpose() + Q_covariance;
61}
62
63void EKF::Correct(const ::Eigen::Matrix<double, 2, 1> &Y, double /*dt*/) {
64 const ::Eigen::Matrix<double, 2, 2> R_covariance(
65 (::Eigen::DiagonalMatrix<double, 2>().diagonal() << ::std::pow(0.01, 2),
66 ::std::pow(0.01, 2))
67 .finished()
68 .asDiagonal());
69 // H is the jacobian of the h(x) measurement prediction function
70 const ::Eigen::Matrix<double, 2, 6> H_jacobian =
71 (::Eigen::Matrix<double, 2, 6>() << 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
72 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
73 .finished();
74
75 // Update step Measurement residual error of proximal and distal joint
76 // angles.
77 const ::Eigen::Matrix<double, 2, 1> Y_hat =
78 Y - (::Eigen::Matrix<double, 2, 1>() << X_hat_(0), X_hat_(2)).finished();
79 // Residual covariance
80 const ::Eigen::Matrix<double, 2, 2> S =
81 H_jacobian * P_ * H_jacobian.transpose() + R_covariance;
82
83 // K is the Near-optimal Kalman gain
84 const ::Eigen::Matrix<double, 6, 2> kalman_gain =
85 P_ * H_jacobian.transpose() * S.inverse();
86 // Updated state estimate
87 X_hat_ = X_hat_ + kalman_gain * Y_hat;
88 // Updated covariance estimate
89 P_ = (::Eigen::Matrix<double, 6, 6>::Identity() - kalman_gain * H_jacobian) *
90 P_;
91}
92
93} // namespace arm
94} // namespace superstructure
95} // namespace control_loops
96} // namespace y2018