blob: 5b6cee6258c3c569f21a8bcda459b057b43e79c6 [file] [log] [blame]
Milind Upadhyay225156b2022-02-25 22:42:12 -08001#include "y2022/control_loops/superstructure/collision_avoidance.h"
2
3#include <cmath>
4
5#include "absl/functional/bind_front.h"
Austin Schuh99f7c6a2024-06-25 22:07:44 -07006#include "absl/log/check.h"
7#include "absl/log/log.h"
Milind Upadhyay225156b2022-02-25 22:42:12 -08008
Stephan Pleinesf63bde82024-01-13 15:59:33 -08009namespace y2022::control_loops::superstructure {
Milind Upadhyay225156b2022-02-25 22:42:12 -080010
11CollisionAvoidance::CollisionAvoidance() {
12 clear_min_intake_front_goal();
13 clear_max_intake_front_goal();
14 clear_min_intake_back_goal();
15 clear_max_intake_back_goal();
16 clear_min_turret_goal();
17 clear_max_turret_goal();
18}
19
20bool CollisionAvoidance::IsCollided(const CollisionAvoidance::Status &status) {
21 // Checks if intake front is collided.
22 if (TurretCollided(status.intake_front_position, status.turret_position,
23 kMinCollisionZoneFrontTurret,
24 kMaxCollisionZoneFrontTurret)) {
25 return true;
26 }
27
28 // Checks if intake back is collided.
29 if (TurretCollided(status.intake_back_position, status.turret_position,
30 kMinCollisionZoneBackTurret,
31 kMaxCollisionZoneBackTurret)) {
32 return true;
33 }
34
Henry Speiser28288cc2022-03-09 22:59:24 -080035 // If we aren't firing, no need to check the catapult
36 if (!status.shooting) {
37 return false;
38 }
39
40 // Checks if intake front is collided with catapult.
41 if (TurretCollided(
42 status.intake_front_position, status.turret_position + M_PI,
43 kMinCollisionZoneFrontTurret, kMaxCollisionZoneFrontTurret)) {
44 return true;
45 }
46
47 // Checks if intake back is collided with catapult.
48 if (TurretCollided(status.intake_back_position, status.turret_position + M_PI,
49 kMinCollisionZoneBackTurret,
50 kMaxCollisionZoneBackTurret)) {
51 return true;
52 }
53
Milind Upadhyay225156b2022-02-25 22:42:12 -080054 return false;
55}
56
57std::pair<double, int> WrapTurretAngle(double turret_angle) {
58 double wrapped = std::remainder(turret_angle - M_PI, 2 * M_PI) + M_PI;
59 int wraps =
60 static_cast<int>(std::round((turret_angle - wrapped) / (2 * M_PI)));
61 return {wrapped, wraps};
62}
63
64double UnwrapTurretAngle(double wrapped, int wraps) {
65 return wrapped + 2.0 * M_PI * wraps;
66}
67
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080068bool AngleInRange(double theta, double theta_min, double theta_max) {
69 return (
70 (theta >= theta_min && theta <= theta_max) ||
71 (theta_min > theta_max && (theta >= theta_min || theta <= theta_max)));
72}
73
Milind Upadhyay225156b2022-02-25 22:42:12 -080074bool CollisionAvoidance::TurretCollided(double intake_position,
75 double turret_position,
76 double min_turret_collision_position,
77 double max_turret_collision_position) {
78 const auto turret_position_wrapped_pair = WrapTurretAngle(turret_position);
79 const double turret_position_wrapped = turret_position_wrapped_pair.first;
80
81 // Checks if turret is in the collision area.
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080082 if (AngleInRange(turret_position_wrapped, min_turret_collision_position,
83 max_turret_collision_position)) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080084 // Reterns true if the intake is raised.
milind-u3e297d32022-03-05 10:31:12 -080085 if (intake_position > kCollisionZoneIntake) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080086 return true;
87 }
88 } else {
89 return false;
90 }
91 return false;
92}
93
Ravago Jones5da06352022-03-04 20:26:24 -080094void CollisionAvoidance::UpdateGoal(
95 const CollisionAvoidance::Status &status,
96 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
97 *unsafe_turret_goal) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080098 // Start with our constraints being wide open.
99 clear_max_turret_goal();
100 clear_min_turret_goal();
101 clear_max_intake_front_goal();
102 clear_min_intake_front_goal();
103 clear_max_intake_back_goal();
104 clear_min_intake_back_goal();
105
106 const double intake_front_position = status.intake_front_position;
107 const double intake_back_position = status.intake_back_position;
108 const double turret_position = status.turret_position;
109
Ravago Jones5da06352022-03-04 20:26:24 -0800110 const double turret_goal = (unsafe_turret_goal != nullptr
111 ? unsafe_turret_goal->unsafe_goal()
112 : std::numeric_limits<double>::quiet_NaN());
Milind Upadhyay225156b2022-02-25 22:42:12 -0800113
114 // Calculating the avoidance with either intake, and when the turret is
115 // wrapped.
116
Henry Speiser28288cc2022-03-09 22:59:24 -0800117 CalculateAvoidance(true, false, intake_front_position, turret_position,
118 turret_goal, kMinCollisionZoneFrontTurret,
119 kMaxCollisionZoneFrontTurret);
120 CalculateAvoidance(false, false, intake_back_position, turret_position,
121 turret_goal, kMinCollisionZoneBackTurret,
122 kMaxCollisionZoneBackTurret);
123
124 // If we aren't firing, no need to check the catapult
125 if (!status.shooting) {
126 return;
127 }
128
129 CalculateAvoidance(true, true, intake_front_position, turret_position,
130 turret_goal, kMinCollisionZoneFrontTurret,
131 kMaxCollisionZoneFrontTurret);
132 CalculateAvoidance(false, true, intake_back_position, turret_position,
133 turret_goal, kMinCollisionZoneBackTurret,
134 kMaxCollisionZoneBackTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800135}
136
Henry Speiser28288cc2022-03-09 22:59:24 -0800137void CollisionAvoidance::CalculateAvoidance(bool intake_front, bool catapult,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800138 double intake_position,
Henry Speiser28288cc2022-03-09 22:59:24 -0800139 double turret_position,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800140 double turret_goal,
141 double min_turret_collision_goal,
Henry Speiser28288cc2022-03-09 22:59:24 -0800142 double max_turret_collision_goal) {
143 // If we are checking the catapult, offset the turret angle to represent where
144 // the catapult is
145 if (catapult) {
146 turret_position += M_PI;
147 turret_goal += M_PI;
148 }
149
Milind Upadhyay225156b2022-02-25 22:42:12 -0800150 auto [turret_position_wrapped, turret_position_wraps] =
151 WrapTurretAngle(turret_position);
152
153 // If the turret goal is in a collison zone or moving through one, limit
154 // intake.
155 const bool turret_pos_unsafe =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800156 AngleInRange(turret_position_wrapped, min_turret_collision_goal,
157 max_turret_collision_goal);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800158
159 const bool turret_moving_forward = (turret_goal > turret_position);
160
161 // To figure out if we are moving past an intake, find the unwrapped min/max
162 // angles closest to the turret position on the journey.
163 int bounds_wraps = turret_position_wraps;
164 double min_turret_collision_goal_unwrapped =
165 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
166 if (turret_moving_forward &&
167 min_turret_collision_goal_unwrapped < turret_position) {
168 bounds_wraps++;
169 } else if (!turret_moving_forward &&
170 min_turret_collision_goal_unwrapped > turret_position) {
171 bounds_wraps--;
172 }
173 min_turret_collision_goal_unwrapped =
174 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800175 // If we are checking the back intake, the max turret angle is on the wrap
176 // after the min, so add 1 to the number of wraps for it
Milind Upadhyay225156b2022-02-25 22:42:12 -0800177 const double max_turret_collision_goal_unwrapped =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800178 UnwrapTurretAngle(max_turret_collision_goal,
179 intake_front ? bounds_wraps : bounds_wraps + 1);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800180
181 // Check if the closest unwrapped angles are going to be passed
182 const bool turret_moving_past_intake =
183 ((turret_moving_forward &&
184 (turret_position <= max_turret_collision_goal_unwrapped &&
185 turret_goal >= min_turret_collision_goal_unwrapped)) ||
186 (!turret_moving_forward &&
187 (turret_position >= min_turret_collision_goal_unwrapped &&
188 turret_goal <= max_turret_collision_goal_unwrapped)));
189
190 if (turret_pos_unsafe || turret_moving_past_intake) {
191 // If the turret is unsafe, limit the intake
192 if (intake_front) {
milind-u3e297d32022-03-05 10:31:12 -0800193 update_max_intake_front_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800194 } else {
milind-u3e297d32022-03-05 10:31:12 -0800195 update_max_intake_back_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800196 }
197
198 // If the intake is in the way, limit the turret until moved. Otherwise,
199 // let'errip!
milind-u3e297d32022-03-05 10:31:12 -0800200 if (!turret_pos_unsafe && (intake_position > kCollisionZoneIntake)) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800201 // If we were comparing the position of the catapult,
202 // remove that offset of pi to get the turret position
203 const double bounds_offset = (catapult ? -M_PI : 0);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800204 if (turret_position < min_turret_collision_goal_unwrapped) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800205 update_max_turret_goal(min_turret_collision_goal_unwrapped +
206 bounds_offset - kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800207 } else {
208 update_min_turret_goal(max_turret_collision_goal_unwrapped +
Henry Speiser28288cc2022-03-09 22:59:24 -0800209 bounds_offset + kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800210 }
211 }
212 }
213}
214
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800215} // namespace y2022::control_loops::superstructure