blob: 33e4d6e11ab774360de15f506629be06f97463f2 [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"
6#include "glog/logging.h"
7
Stephan Pleinesf63bde82024-01-13 15:59:33 -08008namespace y2022::control_loops::superstructure {
Milind Upadhyay225156b2022-02-25 22:42:12 -08009
10CollisionAvoidance::CollisionAvoidance() {
11 clear_min_intake_front_goal();
12 clear_max_intake_front_goal();
13 clear_min_intake_back_goal();
14 clear_max_intake_back_goal();
15 clear_min_turret_goal();
16 clear_max_turret_goal();
17}
18
19bool CollisionAvoidance::IsCollided(const CollisionAvoidance::Status &status) {
20 // Checks if intake front is collided.
21 if (TurretCollided(status.intake_front_position, status.turret_position,
22 kMinCollisionZoneFrontTurret,
23 kMaxCollisionZoneFrontTurret)) {
24 return true;
25 }
26
27 // Checks if intake back is collided.
28 if (TurretCollided(status.intake_back_position, status.turret_position,
29 kMinCollisionZoneBackTurret,
30 kMaxCollisionZoneBackTurret)) {
31 return true;
32 }
33
Henry Speiser28288cc2022-03-09 22:59:24 -080034 // If we aren't firing, no need to check the catapult
35 if (!status.shooting) {
36 return false;
37 }
38
39 // Checks if intake front is collided with catapult.
40 if (TurretCollided(
41 status.intake_front_position, status.turret_position + M_PI,
42 kMinCollisionZoneFrontTurret, kMaxCollisionZoneFrontTurret)) {
43 return true;
44 }
45
46 // Checks if intake back is collided with catapult.
47 if (TurretCollided(status.intake_back_position, status.turret_position + M_PI,
48 kMinCollisionZoneBackTurret,
49 kMaxCollisionZoneBackTurret)) {
50 return true;
51 }
52
Milind Upadhyay225156b2022-02-25 22:42:12 -080053 return false;
54}
55
56std::pair<double, int> WrapTurretAngle(double turret_angle) {
57 double wrapped = std::remainder(turret_angle - M_PI, 2 * M_PI) + M_PI;
58 int wraps =
59 static_cast<int>(std::round((turret_angle - wrapped) / (2 * M_PI)));
60 return {wrapped, wraps};
61}
62
63double UnwrapTurretAngle(double wrapped, int wraps) {
64 return wrapped + 2.0 * M_PI * wraps;
65}
66
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080067bool AngleInRange(double theta, double theta_min, double theta_max) {
68 return (
69 (theta >= theta_min && theta <= theta_max) ||
70 (theta_min > theta_max && (theta >= theta_min || theta <= theta_max)));
71}
72
Milind Upadhyay225156b2022-02-25 22:42:12 -080073bool CollisionAvoidance::TurretCollided(double intake_position,
74 double turret_position,
75 double min_turret_collision_position,
76 double max_turret_collision_position) {
77 const auto turret_position_wrapped_pair = WrapTurretAngle(turret_position);
78 const double turret_position_wrapped = turret_position_wrapped_pair.first;
79
80 // Checks if turret is in the collision area.
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080081 if (AngleInRange(turret_position_wrapped, min_turret_collision_position,
82 max_turret_collision_position)) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080083 // Reterns true if the intake is raised.
milind-u3e297d32022-03-05 10:31:12 -080084 if (intake_position > kCollisionZoneIntake) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080085 return true;
86 }
87 } else {
88 return false;
89 }
90 return false;
91}
92
Ravago Jones5da06352022-03-04 20:26:24 -080093void CollisionAvoidance::UpdateGoal(
94 const CollisionAvoidance::Status &status,
95 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
96 *unsafe_turret_goal) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080097 // Start with our constraints being wide open.
98 clear_max_turret_goal();
99 clear_min_turret_goal();
100 clear_max_intake_front_goal();
101 clear_min_intake_front_goal();
102 clear_max_intake_back_goal();
103 clear_min_intake_back_goal();
104
105 const double intake_front_position = status.intake_front_position;
106 const double intake_back_position = status.intake_back_position;
107 const double turret_position = status.turret_position;
108
Ravago Jones5da06352022-03-04 20:26:24 -0800109 const double turret_goal = (unsafe_turret_goal != nullptr
110 ? unsafe_turret_goal->unsafe_goal()
111 : std::numeric_limits<double>::quiet_NaN());
Milind Upadhyay225156b2022-02-25 22:42:12 -0800112
113 // Calculating the avoidance with either intake, and when the turret is
114 // wrapped.
115
Henry Speiser28288cc2022-03-09 22:59:24 -0800116 CalculateAvoidance(true, false, intake_front_position, turret_position,
117 turret_goal, kMinCollisionZoneFrontTurret,
118 kMaxCollisionZoneFrontTurret);
119 CalculateAvoidance(false, false, intake_back_position, turret_position,
120 turret_goal, kMinCollisionZoneBackTurret,
121 kMaxCollisionZoneBackTurret);
122
123 // If we aren't firing, no need to check the catapult
124 if (!status.shooting) {
125 return;
126 }
127
128 CalculateAvoidance(true, true, intake_front_position, turret_position,
129 turret_goal, kMinCollisionZoneFrontTurret,
130 kMaxCollisionZoneFrontTurret);
131 CalculateAvoidance(false, true, intake_back_position, turret_position,
132 turret_goal, kMinCollisionZoneBackTurret,
133 kMaxCollisionZoneBackTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800134}
135
Henry Speiser28288cc2022-03-09 22:59:24 -0800136void CollisionAvoidance::CalculateAvoidance(bool intake_front, bool catapult,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800137 double intake_position,
Henry Speiser28288cc2022-03-09 22:59:24 -0800138 double turret_position,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800139 double turret_goal,
140 double min_turret_collision_goal,
Henry Speiser28288cc2022-03-09 22:59:24 -0800141 double max_turret_collision_goal) {
142 // If we are checking the catapult, offset the turret angle to represent where
143 // the catapult is
144 if (catapult) {
145 turret_position += M_PI;
146 turret_goal += M_PI;
147 }
148
Milind Upadhyay225156b2022-02-25 22:42:12 -0800149 auto [turret_position_wrapped, turret_position_wraps] =
150 WrapTurretAngle(turret_position);
151
152 // If the turret goal is in a collison zone or moving through one, limit
153 // intake.
154 const bool turret_pos_unsafe =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800155 AngleInRange(turret_position_wrapped, min_turret_collision_goal,
156 max_turret_collision_goal);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800157
158 const bool turret_moving_forward = (turret_goal > turret_position);
159
160 // To figure out if we are moving past an intake, find the unwrapped min/max
161 // angles closest to the turret position on the journey.
162 int bounds_wraps = turret_position_wraps;
163 double min_turret_collision_goal_unwrapped =
164 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
165 if (turret_moving_forward &&
166 min_turret_collision_goal_unwrapped < turret_position) {
167 bounds_wraps++;
168 } else if (!turret_moving_forward &&
169 min_turret_collision_goal_unwrapped > turret_position) {
170 bounds_wraps--;
171 }
172 min_turret_collision_goal_unwrapped =
173 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800174 // If we are checking the back intake, the max turret angle is on the wrap
175 // after the min, so add 1 to the number of wraps for it
Milind Upadhyay225156b2022-02-25 22:42:12 -0800176 const double max_turret_collision_goal_unwrapped =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800177 UnwrapTurretAngle(max_turret_collision_goal,
178 intake_front ? bounds_wraps : bounds_wraps + 1);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800179
180 // Check if the closest unwrapped angles are going to be passed
181 const bool turret_moving_past_intake =
182 ((turret_moving_forward &&
183 (turret_position <= max_turret_collision_goal_unwrapped &&
184 turret_goal >= min_turret_collision_goal_unwrapped)) ||
185 (!turret_moving_forward &&
186 (turret_position >= min_turret_collision_goal_unwrapped &&
187 turret_goal <= max_turret_collision_goal_unwrapped)));
188
189 if (turret_pos_unsafe || turret_moving_past_intake) {
190 // If the turret is unsafe, limit the intake
191 if (intake_front) {
milind-u3e297d32022-03-05 10:31:12 -0800192 update_max_intake_front_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800193 } else {
milind-u3e297d32022-03-05 10:31:12 -0800194 update_max_intake_back_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800195 }
196
197 // If the intake is in the way, limit the turret until moved. Otherwise,
198 // let'errip!
milind-u3e297d32022-03-05 10:31:12 -0800199 if (!turret_pos_unsafe && (intake_position > kCollisionZoneIntake)) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800200 // If we were comparing the position of the catapult,
201 // remove that offset of pi to get the turret position
202 const double bounds_offset = (catapult ? -M_PI : 0);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800203 if (turret_position < min_turret_collision_goal_unwrapped) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800204 update_max_turret_goal(min_turret_collision_goal_unwrapped +
205 bounds_offset - kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800206 } else {
207 update_min_turret_goal(max_turret_collision_goal_unwrapped +
Henry Speiser28288cc2022-03-09 22:59:24 -0800208 bounds_offset + kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800209 }
210 }
211 }
212}
213
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800214} // namespace y2022::control_loops::superstructure