Austin Schuh | 54e5bb5 | 2018-02-19 01:09:18 -0800 | [diff] [blame^] | 1 | #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 | |
| 9 | namespace y2018 { |
| 10 | namespace control_loops { |
| 11 | namespace superstructure { |
| 12 | namespace arm { |
| 13 | |
| 14 | namespace { |
| 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. |
| 18 | const ::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 | |
| 26 | EKF::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 | |
| 48 | void 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 | |
| 54 | void 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 | |
| 63 | void 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 |