Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 1 | #include "y2022/control_loops/superstructure/collision_avoidance.h" |
| 2 | |
| 3 | #include <cmath> |
| 4 | |
| 5 | #include "absl/functional/bind_front.h" |
Austin Schuh | 99f7c6a | 2024-06-25 22:07:44 -0700 | [diff] [blame] | 6 | #include "absl/log/check.h" |
| 7 | #include "absl/log/log.h" |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 8 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 9 | namespace y2022::control_loops::superstructure { |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 10 | |
| 11 | CollisionAvoidance::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 | |
| 20 | bool 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 Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 35 | // 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 54 | return false; |
| 55 | } |
| 56 | |
| 57 | std::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 | |
| 64 | double UnwrapTurretAngle(double wrapped, int wraps) { |
| 65 | return wrapped + 2.0 * M_PI * wraps; |
| 66 | } |
| 67 | |
Milind Upadhyay | 8e2582b | 2022-03-06 15:14:15 -0800 | [diff] [blame] | 68 | bool 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 74 | bool 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 Upadhyay | 8e2582b | 2022-03-06 15:14:15 -0800 | [diff] [blame] | 82 | if (AngleInRange(turret_position_wrapped, min_turret_collision_position, |
| 83 | max_turret_collision_position)) { |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 84 | // Reterns true if the intake is raised. |
milind-u | 3e297d3 | 2022-03-05 10:31:12 -0800 | [diff] [blame] | 85 | if (intake_position > kCollisionZoneIntake) { |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 86 | return true; |
| 87 | } |
| 88 | } else { |
| 89 | return false; |
| 90 | } |
| 91 | return false; |
| 92 | } |
| 93 | |
Ravago Jones | 5da0635 | 2022-03-04 20:26:24 -0800 | [diff] [blame] | 94 | void CollisionAvoidance::UpdateGoal( |
| 95 | const CollisionAvoidance::Status &status, |
| 96 | const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal |
| 97 | *unsafe_turret_goal) { |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 98 | // 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 Jones | 5da0635 | 2022-03-04 20:26:24 -0800 | [diff] [blame] | 110 | const double turret_goal = (unsafe_turret_goal != nullptr |
| 111 | ? unsafe_turret_goal->unsafe_goal() |
| 112 | : std::numeric_limits<double>::quiet_NaN()); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 113 | |
| 114 | // Calculating the avoidance with either intake, and when the turret is |
| 115 | // wrapped. |
| 116 | |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 117 | 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 135 | } |
| 136 | |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 137 | void CollisionAvoidance::CalculateAvoidance(bool intake_front, bool catapult, |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 138 | double intake_position, |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 139 | double turret_position, |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 140 | double turret_goal, |
| 141 | double min_turret_collision_goal, |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 142 | 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 150 | 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 Upadhyay | 8e2582b | 2022-03-06 15:14:15 -0800 | [diff] [blame] | 156 | AngleInRange(turret_position_wrapped, min_turret_collision_goal, |
| 157 | max_turret_collision_goal); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 158 | |
| 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 Upadhyay | 8e2582b | 2022-03-06 15:14:15 -0800 | [diff] [blame] | 175 | // 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 177 | const double max_turret_collision_goal_unwrapped = |
Milind Upadhyay | 8e2582b | 2022-03-06 15:14:15 -0800 | [diff] [blame] | 178 | UnwrapTurretAngle(max_turret_collision_goal, |
| 179 | intake_front ? bounds_wraps : bounds_wraps + 1); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 180 | |
| 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-u | 3e297d3 | 2022-03-05 10:31:12 -0800 | [diff] [blame] | 193 | update_max_intake_front_goal(kCollisionZoneIntake - kEpsIntake); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 194 | } else { |
milind-u | 3e297d3 | 2022-03-05 10:31:12 -0800 | [diff] [blame] | 195 | update_max_intake_back_goal(kCollisionZoneIntake - kEpsIntake); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | // If the intake is in the way, limit the turret until moved. Otherwise, |
| 199 | // let'errip! |
milind-u | 3e297d3 | 2022-03-05 10:31:12 -0800 | [diff] [blame] | 200 | if (!turret_pos_unsafe && (intake_position > kCollisionZoneIntake)) { |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 201 | // 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 Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 204 | if (turret_position < min_turret_collision_goal_unwrapped) { |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 205 | update_max_turret_goal(min_turret_collision_goal_unwrapped + |
| 206 | bounds_offset - kEpsTurret); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 207 | } else { |
| 208 | update_min_turret_goal(max_turret_collision_goal_unwrapped + |
Henry Speiser | 28288cc | 2022-03-09 22:59:24 -0800 | [diff] [blame] | 209 | bounds_offset + kEpsTurret); |
Milind Upadhyay | 225156b | 2022-02-25 22:42:12 -0800 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 215 | } // namespace y2022::control_loops::superstructure |