blob: 8c3a239270455d857ab8290e88c831721aa31949 [file] [log] [blame]
James Kuszmaul9a1733a2023-02-19 16:51:47 -08001#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_LOCALIZATION_UTILS_H_
2#define FRC971_CONTROL_LOOPS_DRIVETRAIN_LOCALIZATION_UTILS_H_
3#include <Eigen/Dense>
4
5#include "aos/events/event_loop.h"
6#include "aos/network/message_bridge_server_generated.h"
7#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
James Kuszmaul19612ab2024-02-17 20:45:58 -08008#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
9#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
James Kuszmaulf6aa0382024-03-01 19:46:05 -080010#include "frc971/control_loops/drivetrain/rio_localizer_inputs_generated.h"
James Kuszmaul19612ab2024-02-17 20:45:58 -080011#include "frc971/control_loops/pose.h"
James Kuszmaul9a1733a2023-02-19 16:51:47 -080012#include "frc971/input/joystick_state_generated.h"
13#include "frc971/vision/calibration_generated.h"
14
15namespace frc971::control_loops::drivetrain {
16// This class provides a variety of checks that have generally proved useful for
17// the localizer but which have no clear place to live otherwise.
18// Specifically, it tracks:
19// * Drivetrain voltages, including checks for whether the Output message
20// has timed out.
21// * Offsets between monotonic clocks on different devices.
22// * Whether we are in autonomous mode.
James Kuszmaul19612ab2024-02-17 20:45:58 -080023// * Drivetrain encoder voltages, as reported by the roborio.
James Kuszmaul9a1733a2023-02-19 16:51:47 -080024class LocalizationUtils {
25 public:
26 LocalizationUtils(aos::EventLoop *event_loop);
27
28 // Returns the latest drivetrain output voltage, or zero if no output is
29 // available (which happens when the robot is disabled; when the robot is
30 // disabled, the voltage is functionally zero). Return value will be
31 // [left_voltage, right_voltage]
32 Eigen::Vector2d VoltageOrZero(aos::monotonic_clock::time_point now);
33
James Kuszmaul2700e0f2024-03-16 16:45:48 -070034 // Returns the latest drivetrain encoder values (in meters), or nullopt if
35 // there has been no new encoder reading since the last call. Returns encoders
36 // as [left_position, right_position]
James Kuszmaul19612ab2024-02-17 20:45:58 -080037 std::optional<Eigen::Vector2d> Encoders(aos::monotonic_clock::time_point now);
38
James Kuszmaul9a1733a2023-02-19 16:51:47 -080039 // Returns true if either there is no JoystickState message available or if
40 // we are currently in autonomous mode.
41 bool MaybeInAutonomous();
James Kuszmaul4fe845a2023-03-26 12:57:30 -070042 aos::Alliance Alliance();
James Kuszmaul9a1733a2023-02-19 16:51:47 -080043
44 // Returns the offset between our node and the specified node (or nullopt if
45 // no offset is available). The sign of this will be such that the time on
46 // the remote node = time on our node + ClockOffset().
47 std::optional<aos::monotonic_clock::duration> ClockOffset(
48 std::string_view node);
49
50 private:
James Kuszmaul19da3232024-03-01 21:28:43 -080051 aos::EventLoop *const event_loop_;
James Kuszmaul9a1733a2023-02-19 16:51:47 -080052 aos::Fetcher<frc971::control_loops::drivetrain::Output> output_fetcher_;
James Kuszmaul19612ab2024-02-17 20:45:58 -080053 aos::Fetcher<frc971::control_loops::drivetrain::Position> position_fetcher_;
James Kuszmaulf6aa0382024-03-01 19:46:05 -080054 aos::Fetcher<frc971::control_loops::drivetrain::RioLocalizerInputs>
55 combined_fetcher_;
James Kuszmaul9a1733a2023-02-19 16:51:47 -080056 aos::Fetcher<aos::message_bridge::ServerStatistics> clock_offset_fetcher_;
57 aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
58};
59
60// Converts a flatbuffer TransformationMatrix to an Eigen matrix.
61Eigen::Matrix<double, 4, 4> FlatbufferToTransformationMatrix(
62 const frc971::vision::calibration::TransformationMatrix &flatbuffer);
63
James Kuszmaul19612ab2024-02-17 20:45:58 -080064// This approximates the Jacobian of a vector of [heading, distance, skew]
65// of a target with respect to the full state of a drivetrain EKF.
66// Note that the only nonzero values in the returned matrix will be in the
67// columns corresponding to the X, Y, and Theta components of the state.
68// This is suitable for use as the H matrix in the kalman updates of the EKF,
69// although due to the approximation it should not be used to actually
70// calculate the expected measurement.
71// target_pose is the global pose of the target that we have identified.
72// camera_pose is the current estimate of the global pose of
73// the camera that can see the target.
74template <typename Scalar>
75Eigen::Matrix<double, 3, HybridEkf<Scalar>::kNStates>
76HMatrixForCameraHeadingDistanceSkew(const TypedPose<Scalar> &target_pose,
77 const TypedPose<Scalar> &camera_pose) {
78 // For all of the below calculations, we will assume to a first
79 // approximation that:
80 //
81 // dcamera_theta / dtheta ~= 1
82 // dcamera_x / dx ~= 1
83 // dcamera_y / dy ~= 1
84 //
85 // For cameras sufficiently far from the robot's origin, or if the robot were
86 // spinning extremely rapidly, this would not hold.
87
88 // To calculate dheading/d{x,y,theta}:
89 // heading = arctan2(target_pos - camera_pos) - camera_theta
90 Eigen::Matrix<Scalar, 3, 1> target_pos = target_pose.abs_pos();
91 Eigen::Matrix<Scalar, 3, 1> camera_pos = camera_pose.abs_pos();
92 Scalar diffx = target_pos.x() - camera_pos.x();
93 Scalar diffy = target_pos.y() - camera_pos.y();
94 Scalar norm2 = diffx * diffx + diffy * diffy;
95 Scalar dheadingdx = diffy / norm2;
96 Scalar dheadingdy = -diffx / norm2;
97 Scalar dheadingdtheta = -1.0;
98
99 // To calculate ddistance/d{x,y}:
100 // distance = sqrt(diffx^2 + diffy^2)
101 Scalar distance = ::std::sqrt(norm2);
102 Scalar ddistdx = -diffx / distance;
103 Scalar ddistdy = -diffy / distance;
104
105 // Skew = target.theta - camera.theta - heading
106 // = target.theta - arctan2(target_pos - camera_pos)
107 Scalar dskewdx = -dheadingdx;
108 Scalar dskewdy = -dheadingdy;
109 Eigen::Matrix<Scalar, 3, HybridEkf<Scalar>::kNStates> H;
110 H.setZero();
111 H(0, HybridEkf<Scalar>::kX) = dheadingdx;
112 H(0, HybridEkf<Scalar>::kY) = dheadingdy;
113 H(0, HybridEkf<Scalar>::kTheta) = dheadingdtheta;
114 H(1, HybridEkf<Scalar>::kX) = ddistdx;
115 H(1, HybridEkf<Scalar>::kY) = ddistdy;
116 H(2, HybridEkf<Scalar>::kX) = dskewdx;
117 H(2, HybridEkf<Scalar>::kY) = dskewdy;
118 return H;
119}
120
James Kuszmaul9a1733a2023-02-19 16:51:47 -0800121} // namespace frc971::control_loops::drivetrain
122
123#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_LOCALIZATION_UTILS_H_