RIT VEXU Core API
Loading...
Searching...
No Matches
odometry_nwheel.h
1#pragma once
2
3#include <Eigen/Dense>
4
5#include "core/subsystems/custom_encoder.h"
6#include "core/subsystems/odometry/odometry_base.h"
7#include "core/math/util.h"
8
40
45typedef struct {
46 double x;
47 double y;
48 double theta_rad;
49 double radius;
51
52template <int WHEELS> class OdometryNWheel : public OdometryBase {
53public:
62 OdometryNWheel(
63 const std::array<CustomEncoder, WHEELS> &encoders, const std::array<tracking_wheel_cfg_t, WHEELS> wheel_configs,
64 vex::inertial *imu, bool is_async
65 )
66 : OdometryBase(is_async), imu(imu), encoders(encoders) {
67 Eigen::Matrix<double, WHEELS, 3> transfer_matrix;
68 for (int i = 0; i < WHEELS; i++) {
69 double x = wheel_configs[i].x;
70 double y = wheel_configs[i].y;
71 double theta_rad = wheel_configs[i].theta_rad;
72 double radius = wheel_configs[i].radius;
73 // Eigen::Vector of the radii of the tracking wheels
74 // defined at the bottom of private:
75 wheel_radii(i) = radius;
76
77 double x_factor = cos(theta_rad);
78 double y_factor = -sin(theta_rad);
79 double theta_factor = -(x * sin(theta_rad)) - (y * cos(theta_rad));
80
81 // prevent numerical error due to float precision
82 if (abs(y_factor) < 1e-9) {
83 y_factor = 0;
84 }
85 if (abs(x_factor) < 1e-9) {
86 x_factor = 0;
87 }
88
89 transfer_matrix.row(i) << x_factor, y_factor, theta_factor;
90 }
91
92 transfer_matrix_pseudoinverse = transfer_matrix.completeOrthogonalDecomposition().pseudoInverse();
93 angle_offset = 0;
94 old_wheel_angles.fill(0);
95 }
96
103 Pose2d update() override {
104 Eigen::Vector<double, WHEELS> radian_deltas;
105
106 for (int i = 0; i < WHEELS; i++) {
107 double angle_rad = encoders[i].position(vex::rev) * std::numbers::pi * 2;
108 radian_deltas(i) = angle_rad - old_wheel_angles[i];
109
110 old_wheel_angles[i] = angle_rad;
111 }
112
113 Pose2d updated_pos = calculate_new_pos(radian_deltas, current_pos);
114
115 // if we do not pass in an IMU we use the wheels for rotation
116 if (imu != nullptr) {
117 angle = 0;
118 // Translate "0 forward and clockwise positive" to "CCW positive and radians"
119 angle = -imu->rotation(vex::rotationUnits::rev) * 2 * std::numbers::pi;
120 // Offset the angle, if we've done a set_position
121 angle += angle_offset;
122 }
123
124 static Pose2d last_pos = updated_pos;
125 static double last_speed = 0;
126 static double last_ang_speed = 0;
127 static vex::timer tmr;
128
129 double speed_local = 0;
130 double accel_local = 0;
131 double ang_speed_local = 0;
132 double ang_accel_local = 0;
133 bool update_vel_accel = tmr.time(vex::sec) > 0.1;
134
135 // This loop runs too fast. Only check at LEAST every 1/10th sec
136 if (update_vel_accel) {
137 // Calculate robot velocity
138 speed_local = updated_pos.translation().distance(last_pos.translation()).to(units::in) / tmr.time(vex::sec);
139
140 // Calculate robot acceleration
141 accel_local = (speed_local - last_speed) / tmr.time(vex::sec);
142
143 // Calculate robot angular velocity (deg/sec)
144 ang_speed_local = smallest_angle(updated_pos.rotation().degrees(), last_pos.rotation().degrees()) / tmr.time(vex::sec);
145
146 // Calculate robot angular acceleration (deg/sec^2)
147 ang_accel_local = (ang_speed_local - last_ang_speed) / tmr.time(vex::sec);
148
149 tmr.reset();
150 last_pos = updated_pos;
151 last_speed = speed_local;
152 last_ang_speed = ang_speed_local;
153 }
154
155 this->current_pos = updated_pos;
156 if (update_vel_accel) {
157 this->speed = speed_local;
158 this->accel = accel_local;
159 this->ang_speed_deg = ang_speed_local;
160 this->ang_accel_deg = ang_accel_local;
161 }
162
163 if (imu != nullptr) {
164 old_angle = angle;
165 }
166
167 return current_pos;
168 }
169
171 void set_position(const Pose2d &newpos) override {
172 mut.lock();
173 angle_offset = newpos.rotation().radians() - (current_pos.rotation().radians() - angle_offset);
174 mut.unlock();
175
177 }
178
183 Pose2d get_position(void) {
184 Pose2d without_wrapped_angle = OdometryBase::get_position();
185 Pose2d with_wrapped_angle(without_wrapped_angle.translation(), without_wrapped_angle.rotation().wrapped_radians_360());
186 return with_wrapped_angle;
187 }
188
189private:
198 Pose2d calculate_new_pos(Eigen::Vector<double, WHEELS> radian_deltas, Pose2d old_pose) {
199 // Mr T = E -> Mr^{+} E = T
200 // We take the diagonal of radian_deltas to do a coefficient wise multiplication rather than a dot product
201 Eigen::Vector3d pose_delta = transfer_matrix_pseudoinverse * (radian_deltas.asDiagonal() * wheel_radii);
202 Eigen::Vector3d old_pose_vector{old_pose.x(units::in), old_pose.y(units::in), old_pose.rotation().radians()};
203 // we achieve better performance by using the imu for rotation directly when possible
204 // If an imu is not passed in when constructing, simply use the wheels for rotation
205 if (imu != nullptr) {
206 pose_delta(2) = angle - old_angle;
207 }
208 Pose2d pose_delta2d(pose_delta, units::in);
209 Pose2d old_pose_Pose2d(old_pose_vector, units::in);
210
211 Pose2d new_pose = old_pose_Pose2d.exp(Twist2d(pose_delta, units::in));
212
213 // simply replaces the calculated angle with the imu angle directly
214 if (imu != nullptr) {
215 new_pose.set_rotation(Rotation2d(angle));
216 }
217 return new_pose;
218 }
219
220 // values used for imu integration
221 double angle;
222 double old_angle;
223 double angle_offset;
224
225 vex::inertial *imu;
226
227 Eigen::Matrix<double, 3, WHEELS> transfer_matrix_pseudoinverse;
228
229 std::array<CustomEncoder, WHEELS> encoders;
230 Eigen::Vector<double, WHEELS> wheel_radii;
231 // from the last timestep, used for finding deltas
232 Eigen::Vector<double, WHEELS> old_wheel_angles;
233};
constexpr Q distance(LinearVector2d other) const
Gets the distance between two vectors.
Definition translation2d.h:108
Definition odometry_base.h:27
double ang_accel_deg
Definition odometry_base.h:134
double ang_speed_deg
Definition odometry_base.h:133
Pose2d current_pos
Current position of the robot in terms of x,y,rotation.
Definition odometry_base.h:129
double speed
Definition odometry_base.h:131
OdometryBase(bool is_async)
Definition odometry_base.cpp:12
static double smallest_angle(double start_deg, double end_deg)
Definition odometry_base.cpp:78
vex::mutex mut
Mutex to control multithreading.
Definition odometry_base.h:126
double accel
Definition odometry_base.h:132
virtual Pose2d get_position(void)
Gets the current position and rotation.
Definition odometry_base.cpp:46
virtual void set_position(const Pose2d &newpos=zero_pos)
Sets the current position of the robot.
Definition odometry_base.cpp:58
constexpr Rotation2d rotation() const
Gets the rotation.
Definition pose2d.h:102
constexpr void set_rotation(Rotation2d val)
Sets the rotation.
Definition pose2d.h:105
constexpr units::Length y() const
Gets the y value of the translation.
Definition pose2d.h:87
constexpr Translation2d translation() const
Gets the translation.
Definition pose2d.h:96
constexpr units::Length x() const
Gets the x value of the translation.
Definition pose2d.h:78
constexpr double radians() const
Gets the rotation in radians.
Definition rotation2d.h:72
constexpr double degrees() const
Gets the rotation in degrees.
Definition rotation2d.h:75
constexpr Rotation2d()=default
Default Constructor for Rotation2d.
constexpr double wrapped_radians_360() const
Gets the value of this rotation in radians from [0, 2pi).
Definition rotation2d.h:114
constexpr Twist2d()
Default Constructor for Twist2d.
Definition twist2d.h:28
Definition odometry_nwheel.h:45
double x
Definition odometry_nwheel.h:46
double radius
Definition odometry_nwheel.h:49
double theta_rad
Definition odometry_nwheel.h:48
double y
Definition odometry_nwheel.h:47