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