blob: 0c1329551dc9395aaa07ca473b92c017457952bc [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
8namespace y2022 {
9namespace control_loops {
10namespace superstructure {
11
12CollisionAvoidance::CollisionAvoidance() {
13 clear_min_intake_front_goal();
14 clear_max_intake_front_goal();
15 clear_min_intake_back_goal();
16 clear_max_intake_back_goal();
17 clear_min_turret_goal();
18 clear_max_turret_goal();
19}
20
21bool CollisionAvoidance::IsCollided(const CollisionAvoidance::Status &status) {
22 // Checks if intake front is collided.
23 if (TurretCollided(status.intake_front_position, status.turret_position,
24 kMinCollisionZoneFrontTurret,
25 kMaxCollisionZoneFrontTurret)) {
26 return true;
27 }
28
29 // Checks if intake back is collided.
30 if (TurretCollided(status.intake_back_position, status.turret_position,
31 kMinCollisionZoneBackTurret,
32 kMaxCollisionZoneBackTurret)) {
33 return true;
34 }
35
Henry Speiser28288cc2022-03-09 22:59:24 -080036 // If we aren't firing, no need to check the catapult
37 if (!status.shooting) {
38 return false;
39 }
40
41 // Checks if intake front is collided with catapult.
42 if (TurretCollided(
43 status.intake_front_position, status.turret_position + M_PI,
44 kMinCollisionZoneFrontTurret, kMaxCollisionZoneFrontTurret)) {
45 return true;
46 }
47
48 // Checks if intake back is collided with catapult.
49 if (TurretCollided(status.intake_back_position, status.turret_position + M_PI,
50 kMinCollisionZoneBackTurret,
51 kMaxCollisionZoneBackTurret)) {
52 return true;
53 }
54
Milind Upadhyay225156b2022-02-25 22:42:12 -080055 return false;
56}
57
58std::pair<double, int> WrapTurretAngle(double turret_angle) {
59 double wrapped = std::remainder(turret_angle - M_PI, 2 * M_PI) + M_PI;
60 int wraps =
61 static_cast<int>(std::round((turret_angle - wrapped) / (2 * M_PI)));
62 return {wrapped, wraps};
63}
64
65double UnwrapTurretAngle(double wrapped, int wraps) {
66 return wrapped + 2.0 * M_PI * wraps;
67}
68
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080069bool AngleInRange(double theta, double theta_min, double theta_max) {
70 return (
71 (theta >= theta_min && theta <= theta_max) ||
72 (theta_min > theta_max && (theta >= theta_min || theta <= theta_max)));
73}
74
Milind Upadhyay225156b2022-02-25 22:42:12 -080075bool CollisionAvoidance::TurretCollided(double intake_position,
76 double turret_position,
77 double min_turret_collision_position,
78 double max_turret_collision_position) {
79 const auto turret_position_wrapped_pair = WrapTurretAngle(turret_position);
80 const double turret_position_wrapped = turret_position_wrapped_pair.first;
81
82 // Checks if turret is in the collision area.
Milind Upadhyay8e2582b2022-03-06 15:14:15 -080083 if (AngleInRange(turret_position_wrapped, min_turret_collision_position,
84 max_turret_collision_position)) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080085 // Reterns true if the intake is raised.
milind-u3e297d32022-03-05 10:31:12 -080086 if (intake_position > kCollisionZoneIntake) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080087 return true;
88 }
89 } else {
90 return false;
91 }
92 return false;
93}
94
Ravago Jones5da06352022-03-04 20:26:24 -080095void CollisionAvoidance::UpdateGoal(
96 const CollisionAvoidance::Status &status,
97 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
98 *unsafe_turret_goal) {
Milind Upadhyay225156b2022-02-25 22:42:12 -080099 // Start with our constraints being wide open.
100 clear_max_turret_goal();
101 clear_min_turret_goal();
102 clear_max_intake_front_goal();
103 clear_min_intake_front_goal();
104 clear_max_intake_back_goal();
105 clear_min_intake_back_goal();
106
107 const double intake_front_position = status.intake_front_position;
108 const double intake_back_position = status.intake_back_position;
109 const double turret_position = status.turret_position;
110
Ravago Jones5da06352022-03-04 20:26:24 -0800111 const double turret_goal = (unsafe_turret_goal != nullptr
112 ? unsafe_turret_goal->unsafe_goal()
113 : std::numeric_limits<double>::quiet_NaN());
Milind Upadhyay225156b2022-02-25 22:42:12 -0800114
115 // Calculating the avoidance with either intake, and when the turret is
116 // wrapped.
117
Henry Speiser28288cc2022-03-09 22:59:24 -0800118 CalculateAvoidance(true, false, intake_front_position, turret_position,
119 turret_goal, kMinCollisionZoneFrontTurret,
120 kMaxCollisionZoneFrontTurret);
121 CalculateAvoidance(false, false, intake_back_position, turret_position,
122 turret_goal, kMinCollisionZoneBackTurret,
123 kMaxCollisionZoneBackTurret);
124
125 // If we aren't firing, no need to check the catapult
126 if (!status.shooting) {
127 return;
128 }
129
130 CalculateAvoidance(true, true, intake_front_position, turret_position,
131 turret_goal, kMinCollisionZoneFrontTurret,
132 kMaxCollisionZoneFrontTurret);
133 CalculateAvoidance(false, true, intake_back_position, turret_position,
134 turret_goal, kMinCollisionZoneBackTurret,
135 kMaxCollisionZoneBackTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800136}
137
Henry Speiser28288cc2022-03-09 22:59:24 -0800138void CollisionAvoidance::CalculateAvoidance(bool intake_front, bool catapult,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800139 double intake_position,
Henry Speiser28288cc2022-03-09 22:59:24 -0800140 double turret_position,
Milind Upadhyay225156b2022-02-25 22:42:12 -0800141 double turret_goal,
142 double min_turret_collision_goal,
Henry Speiser28288cc2022-03-09 22:59:24 -0800143 double max_turret_collision_goal) {
144 // If we are checking the catapult, offset the turret angle to represent where
145 // the catapult is
146 if (catapult) {
147 turret_position += M_PI;
148 turret_goal += M_PI;
149 }
150
Milind Upadhyay225156b2022-02-25 22:42:12 -0800151 auto [turret_position_wrapped, turret_position_wraps] =
152 WrapTurretAngle(turret_position);
153
154 // If the turret goal is in a collison zone or moving through one, limit
155 // intake.
156 const bool turret_pos_unsafe =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800157 AngleInRange(turret_position_wrapped, min_turret_collision_goal,
158 max_turret_collision_goal);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800159
160 const bool turret_moving_forward = (turret_goal > turret_position);
161
162 // To figure out if we are moving past an intake, find the unwrapped min/max
163 // angles closest to the turret position on the journey.
164 int bounds_wraps = turret_position_wraps;
165 double min_turret_collision_goal_unwrapped =
166 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
167 if (turret_moving_forward &&
168 min_turret_collision_goal_unwrapped < turret_position) {
169 bounds_wraps++;
170 } else if (!turret_moving_forward &&
171 min_turret_collision_goal_unwrapped > turret_position) {
172 bounds_wraps--;
173 }
174 min_turret_collision_goal_unwrapped =
175 UnwrapTurretAngle(min_turret_collision_goal, bounds_wraps);
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800176 // If we are checking the back intake, the max turret angle is on the wrap
177 // after the min, so add 1 to the number of wraps for it
Milind Upadhyay225156b2022-02-25 22:42:12 -0800178 const double max_turret_collision_goal_unwrapped =
Milind Upadhyay8e2582b2022-03-06 15:14:15 -0800179 UnwrapTurretAngle(max_turret_collision_goal,
180 intake_front ? bounds_wraps : bounds_wraps + 1);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800181
182 // Check if the closest unwrapped angles are going to be passed
183 const bool turret_moving_past_intake =
184 ((turret_moving_forward &&
185 (turret_position <= max_turret_collision_goal_unwrapped &&
186 turret_goal >= min_turret_collision_goal_unwrapped)) ||
187 (!turret_moving_forward &&
188 (turret_position >= min_turret_collision_goal_unwrapped &&
189 turret_goal <= max_turret_collision_goal_unwrapped)));
190
191 if (turret_pos_unsafe || turret_moving_past_intake) {
192 // If the turret is unsafe, limit the intake
193 if (intake_front) {
milind-u3e297d32022-03-05 10:31:12 -0800194 update_max_intake_front_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800195 } else {
milind-u3e297d32022-03-05 10:31:12 -0800196 update_max_intake_back_goal(kCollisionZoneIntake - kEpsIntake);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800197 }
198
199 // If the intake is in the way, limit the turret until moved. Otherwise,
200 // let'errip!
milind-u3e297d32022-03-05 10:31:12 -0800201 if (!turret_pos_unsafe && (intake_position > kCollisionZoneIntake)) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800202 // If we were comparing the position of the catapult,
203 // remove that offset of pi to get the turret position
204 const double bounds_offset = (catapult ? -M_PI : 0);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800205 if (turret_position < min_turret_collision_goal_unwrapped) {
Henry Speiser28288cc2022-03-09 22:59:24 -0800206 update_max_turret_goal(min_turret_collision_goal_unwrapped +
207 bounds_offset - kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800208 } else {
209 update_min_turret_goal(max_turret_collision_goal_unwrapped +
Henry Speiser28288cc2022-03-09 22:59:24 -0800210 bounds_offset + kEpsTurret);
Milind Upadhyay225156b2022-02-25 22:42:12 -0800211 }
212 }
213 }
214}
215
216} // namespace superstructure
217} // namespace control_loops
218} // namespace y2022