blob: 48a71f44268c8f45c10b9befff84a2c328212f5a [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
Austin Schuhcb091712018-02-21 20:01:55 -08009DEFINE_double(proximal_voltage_error_uncertainty, 8.0,
10 "Proximal joint voltage error uncertainty.");
11DEFINE_double(distal_voltage_error_uncertainty, 2.0,
12 "Distal joint voltage error uncertainty.");
13
Austin Schuh54e5bb52018-02-19 01:09:18 -080014namespace y2018 {
15namespace control_loops {
16namespace superstructure {
17namespace arm {
18
19namespace {
20// TODO(austin): When tuning this, make sure to verify that you are waiting
21// enough cycles to make sure it converges at startup. Otherwise you will have a
22// bad day.
Austin Schuhcb091712018-02-21 20:01:55 -080023::Eigen::Matrix<double, 6, 6> Q_covariance(
Austin Schuh54e5bb52018-02-19 01:09:18 -080024 (::Eigen::DiagonalMatrix<double, 6>().diagonal() << ::std::pow(0.1, 2),
25 ::std::pow(2.0, 2), ::std::pow(0.1, 2), ::std::pow(2.0, 2),
Austin Schuhcb091712018-02-21 20:01:55 -080026 ::std::pow(FLAGS_proximal_voltage_error_uncertainty, 2),
27 ::std::pow(FLAGS_distal_voltage_error_uncertainty, 2))
Austin Schuh54e5bb52018-02-19 01:09:18 -080028 .finished()
29 .asDiagonal());
30} // namespace
31
32EKF::EKF() {
33 X_hat_.setZero();
Austin Schuhcb091712018-02-21 20:01:55 -080034 Q_covariance =
35 ((::Eigen::DiagonalMatrix<double, 6>().diagonal() << ::std::pow(0.1, 2),
36 ::std::pow(2.0, 2), ::std::pow(0.1, 2), ::std::pow(2.0, 2),
37 ::std::pow(FLAGS_proximal_voltage_error_uncertainty, 2),
38 ::std::pow(FLAGS_distal_voltage_error_uncertainty, 2))
39 .finished()
40 .asDiagonal());
Austin Schuh54e5bb52018-02-19 01:09:18 -080041 P_ = Q_covariance;
Austin Schuhcb091712018-02-21 20:01:55 -080042 P_reset_ = P_;
Austin Schuh54e5bb52018-02-19 01:09:18 -080043 //::std::cout << "Reset P: " << P_ << ::std::endl;
44 // TODO(austin): Running the EKF 2000 cycles works, but isn't super clever.
45 // We could just solve the DARE.
46
47 for (int i = 0; i < 1000; ++i) {
48 Predict(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
49 Correct(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
50 }
Austin Schuhcb091712018-02-21 20:01:55 -080051 P_half_converged_ = P_;
Austin Schuh54e5bb52018-02-19 01:09:18 -080052 //::std::cout << "Stabilized P: " << P_ << ::std::endl;
53 for (int i = 0; i < 1000; ++i) {
54 Predict(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
55 Correct(::Eigen::Matrix<double, 2, 1>::Zero(), 0.00505);
56 }
57 //::std::cout << "Really stabilized P: " << P_ << ::std::endl;
Austin Schuhcb091712018-02-21 20:01:55 -080058 P_converged_ = P_;
Austin Schuh54e5bb52018-02-19 01:09:18 -080059
60 Reset(::Eigen::Matrix<double, 4, 1>::Zero());
61}
62
63void EKF::Reset(const ::Eigen::Matrix<double, 4, 1> &X) {
64 X_hat_.setZero();
Austin Schuhcb091712018-02-21 20:01:55 -080065 P_ = P_converged_;
Austin Schuh54e5bb52018-02-19 01:09:18 -080066 X_hat_.block<4, 1>(0, 0) = X;
67}
68
69void EKF::Predict(const ::Eigen::Matrix<double, 2, 1> &U, double dt) {
70 const ::Eigen::Matrix<double, 6, 6> A =
71 ::frc971::control_loops::NumericalJacobianX<6, 2>(
72 Dynamics::UnboundedEKFDiscreteDynamics, X_hat_, U, dt);
73
74 X_hat_ = Dynamics::UnboundedEKFDiscreteDynamics(X_hat_, U, dt);
75 P_ = A * P_ * A.transpose() + Q_covariance;
76}
77
78void EKF::Correct(const ::Eigen::Matrix<double, 2, 1> &Y, double /*dt*/) {
79 const ::Eigen::Matrix<double, 2, 2> R_covariance(
80 (::Eigen::DiagonalMatrix<double, 2>().diagonal() << ::std::pow(0.01, 2),
81 ::std::pow(0.01, 2))
82 .finished()
83 .asDiagonal());
84 // H is the jacobian of the h(x) measurement prediction function
85 const ::Eigen::Matrix<double, 2, 6> H_jacobian =
86 (::Eigen::Matrix<double, 2, 6>() << 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
87 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
88 .finished();
89
90 // Update step Measurement residual error of proximal and distal joint
91 // angles.
92 const ::Eigen::Matrix<double, 2, 1> Y_hat =
93 Y - (::Eigen::Matrix<double, 2, 1>() << X_hat_(0), X_hat_(2)).finished();
94 // Residual covariance
95 const ::Eigen::Matrix<double, 2, 2> S =
96 H_jacobian * P_ * H_jacobian.transpose() + R_covariance;
97
98 // K is the Near-optimal Kalman gain
99 const ::Eigen::Matrix<double, 6, 2> kalman_gain =
100 P_ * H_jacobian.transpose() * S.inverse();
101 // Updated state estimate
102 X_hat_ = X_hat_ + kalman_gain * Y_hat;
103 // Updated covariance estimate
104 P_ = (::Eigen::Matrix<double, 6, 6>::Identity() - kalman_gain * H_jacobian) *
105 P_;
106}
107
108} // namespace arm
109} // namespace superstructure
110} // namespace control_loops
111} // namespace y2018