James Kuszmaul | 1798c07 | 2022-02-13 15:32:11 -0800 | [diff] [blame] | 1 | #include "y2022/control_loops/drivetrain/localizer.h" |
| 2 | |
| 3 | namespace y2022 { |
| 4 | namespace control_loops { |
| 5 | namespace drivetrain { |
| 6 | |
| 7 | Localizer::Localizer( |
| 8 | aos::EventLoop *event_loop, |
| 9 | const frc971::control_loops::drivetrain::DrivetrainConfig<double> |
| 10 | &dt_config) |
| 11 | : event_loop_(event_loop), |
| 12 | dt_config_(dt_config), |
| 13 | ekf_(dt_config), |
| 14 | localizer_output_fetcher_( |
| 15 | event_loop_->MakeFetcher<frc971::controls::LocalizerOutput>( |
| 16 | "/localizer")), |
| 17 | clock_offset_fetcher_( |
| 18 | event_loop_->MakeFetcher<aos::message_bridge::ServerStatistics>( |
| 19 | "/aos")) { |
| 20 | ekf_.set_ignore_accel(true); |
| 21 | |
| 22 | event_loop->OnRun([this, event_loop]() { |
| 23 | ekf_.ResetInitialState(event_loop->monotonic_now(), |
| 24 | HybridEkf::State::Zero(), ekf_.P()); |
| 25 | }); |
| 26 | |
| 27 | target_selector_.set_has_target(false); |
| 28 | } |
| 29 | |
| 30 | void Localizer::Reset( |
| 31 | aos::monotonic_clock::time_point t, |
| 32 | const frc971::control_loops::drivetrain::HybridEkf<double>::State &state) { |
| 33 | // Go through and clear out all of the fetchers so that we don't get behind. |
| 34 | localizer_output_fetcher_.Fetch(); |
| 35 | ekf_.ResetInitialState(t, state.cast<float>(), ekf_.P()); |
| 36 | } |
| 37 | |
| 38 | void Localizer::Update(const Eigen::Matrix<double, 2, 1> &U, |
| 39 | aos::monotonic_clock::time_point now, |
| 40 | double left_encoder, double right_encoder, |
| 41 | double gyro_rate, const Eigen::Vector3d &accel) { |
| 42 | ekf_.UpdateEncodersAndGyro(left_encoder, right_encoder, gyro_rate, |
| 43 | U.cast<float>(), accel.cast<float>(), now); |
| 44 | if (localizer_output_fetcher_.Fetch()) { |
| 45 | clock_offset_fetcher_.Fetch(); |
| 46 | bool message_bridge_connected = true; |
| 47 | std::chrono::nanoseconds monotonic_offset{0}; |
| 48 | if (clock_offset_fetcher_.get() != nullptr) { |
| 49 | for (const auto connection : *clock_offset_fetcher_->connections()) { |
| 50 | if (connection->has_node() && connection->node()->has_name() && |
| 51 | connection->node()->name()->string_view() == "imu") { |
| 52 | if (connection->has_monotonic_offset()) { |
| 53 | monotonic_offset = |
| 54 | std::chrono::nanoseconds(connection->monotonic_offset()); |
| 55 | } else { |
| 56 | // If we don't have a monotonic offset, that means we aren't |
| 57 | // connected, in which case we should break the loop but shouldn't |
| 58 | // populate the offset. |
| 59 | message_bridge_connected = false; |
| 60 | } |
| 61 | break; |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | if (!message_bridge_connected) { |
| 66 | return; |
| 67 | } |
| 68 | aos::monotonic_clock::time_point capture_time( |
| 69 | std::chrono::nanoseconds( |
| 70 | localizer_output_fetcher_->monotonic_timestamp_ns()) - |
| 71 | monotonic_offset); |
| 72 | // TODO: Finish implementing simple x/y/theta updater with state_at_capture. |
| 73 | // TODO: Implement turret/camera processing logic on pi side. |
| 74 | const std::optional<State> state_at_capture = |
| 75 | ekf_.LastStateBeforeTime(capture_time); |
| 76 | Eigen::Matrix<float, HybridEkf::kNOutputs, HybridEkf::kNStates> H; |
| 77 | H.setZero(); |
| 78 | H(0, StateIdx::kX) = 1; |
| 79 | H(1, StateIdx::kY) = 1; |
| 80 | H(2, StateIdx::kTheta) = 1; |
| 81 | const Eigen::Vector3f Z{ |
| 82 | static_cast<float>(localizer_output_fetcher_->x()), |
| 83 | static_cast<float>(localizer_output_fetcher_->y()), |
| 84 | static_cast<float>(localizer_output_fetcher_->theta())}; |
| 85 | Eigen::Matrix3f R = Eigen::Matrix3f::Zero(); |
| 86 | R.diagonal() << 0.01, 0.01, 1e-4; |
| 87 | const Input U_correct = ekf_.MostRecentInput(); |
| 88 | ekf_.Correct( |
| 89 | Eigen::Vector3f::Zero(), &U_correct, {}, |
| 90 | [H, state_at_capture, Z](const State &, |
| 91 | const Input &) -> Eigen::Vector3f { |
| 92 | Eigen::Vector3f error = H * state_at_capture.value() - Z; |
| 93 | error(2) = aos::math::NormalizeAngle(error(2)); |
| 94 | return error; |
| 95 | }, |
| 96 | [H](const State &) { return H; }, R, now); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | } // namespace drivetrain |
| 101 | } // namespace control_loops |
| 102 | } // namespace y2022 |