blob: 610db8e5ac2fe758b00b37fe54819c88a562b118 [file] [log] [blame]
Comran Morshed25f81a02016-01-23 13:40:10 +00001#ifndef Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
2#define Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
3
4#include <memory>
5
John Park33858a32018-09-28 23:05:48 -07006#include "aos/controls/control_loop.h"
7#include "aos/util/trapezoid_profile.h"
Austin Schuh10c2d112016-02-14 13:42:28 -08008#include "frc971/control_loops/state_feedback_loop.h"
Comran Morshed25f81a02016-01-23 13:40:10 +00009
Austin Schuh2fc10fa2016-02-08 00:44:34 -080010#include "frc971/zeroing/zeroing.h"
Comran Morshed25f81a02016-01-23 13:40:10 +000011#include "y2016/control_loops/superstructure/superstructure.q.h"
Austin Schuh10c2d112016-02-14 13:42:28 -080012#include "y2016/control_loops/superstructure/superstructure_controls.h"
Austin Schuh4b652c92019-05-27 13:22:27 -070013#include "y2016/queues/ball_detector.q.h"
Comran Morshed25f81a02016-01-23 13:40:10 +000014
15namespace y2016 {
16namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080017namespace superstructure {
18namespace testing {
Philipp Schrader0119eb12016-02-15 18:16:21 +000019class SuperstructureTest_RespectsRange_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080020class SuperstructureTest_DisabledGoalTest_Test;
Diana Vandenberge2843c62016-02-13 17:44:20 -080021class SuperstructureTest_ArmZeroingErrorTest_Test;
22class SuperstructureTest_IntakeZeroingErrorTest_Test;
Philipp Schrader0119eb12016-02-15 18:16:21 +000023class SuperstructureTest_UpperHardstopStartup_Test;
Campbell Crowley152c7cf2016-02-14 21:20:50 -080024class SuperstructureTest_DisabledWhileZeroingHigh_Test;
25class SuperstructureTest_DisabledWhileZeroingLow_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080026} // namespace testing
27
Philipp Schrader0119eb12016-02-15 18:16:21 +000028// Helper class to prevent parts from crashing into each other. The parts in
29// question here are: the frame, the arm (plus shooter), and the intake.
30// Assumptions:
31// - The shoulder, the wrist, and intake are horizontal when at angle 0.
32// - The arm (i.e. shoulder) and shooter (i.e. wrist) are stored when they are
33// both at zero degrees.
34// - The intake at angle 0 is in a position to help get a ball in the robot.
35// - The intake at angle PI is in a "stowed" position. In other words, it is
36// folded over the arm and shooter when they are also in a stowed position.
37// - The shooter must remain horizontal when the arm is folding into the robot.
38// Otherwise, the shooter will collide with the frame.
39// - The arm has priority over the intake. If the arm wants to move in such a
40// way that interferes with the intake's movement then the intake must move
41// out of the way.
42class CollisionAvoidance {
43 public:
44 // Set up collision avoidance for an arm and intake.
45 CollisionAvoidance(Intake *intake, Arm *arm) : intake_(intake), arm_(arm) {}
46
47 // This function accepts goals for the intake and the arm and modifies them
48 // in such a way that collisions between all the different parts of the robot
49 // are avoided. The modified goals are then sent to the arm and intake as
50 // unprofiled goals.
51 void UpdateGoal(double shoulder_angle_goal, double wrist_angle_goal,
52 double intake_angle_goal);
53
Philipp Schrader07147532016-02-16 01:23:07 +000054 // Returns true if any of the limbs and frame are somehow currently
55 // interfering with one another. This is based purely on the angles that the
56 // limbs are reporting.
57 bool collided() const;
58
59 // Detects collision with the specified angles. This is especially useful for
60 // unit testing where we have proper ground truth for all the angles.
61 static bool collided_with_given_angles(double shoulder_angle,
62 double wrist_angle,
63 double intake_angle);
64
Philipp Schrader0119eb12016-02-15 18:16:21 +000065 // The shoulder angle (in radians) below which the shooter must be in a
66 // stowing position. In other words the wrist must be at angle zero if the
67 // shoulder is below this angle.
Austin Schuhb1d682b2016-02-16 13:07:44 -080068 static constexpr double kMinShoulderAngleForHorizontalShooter = 0.6;
Philipp Schrader0119eb12016-02-15 18:16:21 +000069
Campbell Crowley1836a432016-03-05 15:16:51 -080070 // The shoulder angle (in radians) below which the arm and the shooter have
71 // the potential to interfere with the intake.
Austin Schuh6ca0f792016-03-12 14:06:14 -080072 static constexpr double kMinShoulderAngleForIntakeUpInterference = 1.3;
73
74 // The shoulder angle (in radians) below which the shooter should be closer to
75 // level to fix the inverted case.
76 // TODO(austin): Verify
77 static constexpr double kMinShoulderAngleForIntakeInterference = 1.1;
Philipp Schrader0119eb12016-02-15 18:16:21 +000078
79 // The intake angle (in radians) above which the intake can interfere (i.e.
80 // collide) with the arm and/or shooter.
Austin Schuh2c717862016-03-13 15:32:53 -070081 static constexpr double kMaxIntakeAngleBeforeArmInterference = 1.05;
Philipp Schrader0119eb12016-02-15 18:16:21 +000082
83 // The maximum absolute angle (in radians) that the wrist must be below in
84 // order for the shouler to be allowed to move below
85 // kMinShoulderAngleForHorizontalShooter. In other words, only allow the arm
86 // to move down into the belly pan if the shooter is horizontal, ready to
87 // also be placed into the belly pan.
Austin Schuh2f5bfc82016-02-27 14:47:37 -080088 static constexpr double kMaxWristAngleForSafeArmStowing = 0.05;
Philipp Schrader0119eb12016-02-15 18:16:21 +000089
Austin Schuh2c717862016-03-13 15:32:53 -070090 // The maximum angle in radians that the wrist can be from horizontal
Austin Schuh6ca0f792016-03-12 14:06:14 -080091 // while it is near the intake.
Austin Schuh6802a9d2016-03-12 21:34:53 -080092 static constexpr double kMaxWristAngleForMovingByIntake = 0.50;
Austin Schuh2c717862016-03-13 15:32:53 -070093 // The minimum angle in radians that the wrist can be from horizontal
94 // while it is near the intake.
Austin Schuh214164b2016-03-20 16:50:09 -070095 static constexpr double kMinWristAngleForMovingByIntake = -1.50;
Austin Schuh6ca0f792016-03-12 14:06:14 -080096
Philipp Schrader0119eb12016-02-15 18:16:21 +000097 // The shoulder angle (in radians) below which the intake can safely move
98 // into the collision zone. This is necessary when the robot wants to fold up
99 // completely (i.e. stow the arm, shooter, and intake).
Austin Schuh6ca0f792016-03-12 14:06:14 -0800100 static constexpr double kMaxShoulderAngleUntilSafeIntakeStowing = 0.19;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000101
102 private:
103 Intake *intake_;
104 Arm *arm_;
105};
106
Comran Morshed25f81a02016-01-23 13:40:10 +0000107class Superstructure
108 : public ::aos::controls::ControlLoop<control_loops::SuperstructureQueue> {
109 public:
110 explicit Superstructure(
Austin Schuh55a13dc2019-01-27 22:39:03 -0800111 ::aos::EventLoop *event_loop,
112 const ::std::string &name = ".y2016.control_loops.superstructure_queue");
Adam Snaider06779722016-02-14 15:26:22 -0800113
Austin Schuhf132dc52016-03-13 19:43:15 -0700114 static constexpr double kZeroingVoltage = 6.0;
Austin Schuhfef64ac2016-04-24 19:08:01 -0700115 static constexpr double kShooterHangingVoltage = 6.0;
Austin Schuh0c001a82016-05-01 12:30:09 -0700116 static constexpr double kShooterHangingLowVoltage = 2.0;
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000117 static constexpr double kOperatingVoltage = 12.0;
Austin Schuh2c717862016-03-13 15:32:53 -0700118 static constexpr double kLandingShoulderDownVoltage = -1.5;
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000119
Adam Snaider06779722016-02-14 15:26:22 -0800120 // This is the angle above which we will do a HIGH_ARM_ZERO, and below which
121 // we will do a LOW_ARM_ZERO.
122 static constexpr double kShoulderMiddleAngle = M_PI / 4.0;
123 // This is the large scale movement tolerance.
124 static constexpr double kLooseTolerance = 0.05;
125
126 // This is the small scale movement tolerance.
Austin Schuhf132dc52016-03-13 19:43:15 -0700127 static constexpr double kTightTolerance = 0.03;
Adam Snaider06779722016-02-14 15:26:22 -0800128
129 // This is the angle such that the intake will clear the arm when the shooter
130 // is level.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800131 static constexpr double kIntakeUpperClear =
132 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
Adam Snaider06779722016-02-14 15:26:22 -0800133 // This is the angle such that the intake will clear the arm when the shooter
134 // is at almost any position.
Austin Schuh2f5bfc82016-02-27 14:47:37 -0800135 static constexpr double kIntakeLowerClear = 0.4;
Adam Snaider06779722016-02-14 15:26:22 -0800136
137 // This is the angle that the shoulder will go to when doing the
138 // HIGH_ARM_ZERO.
139 static constexpr double kShoulderUpAngle = M_PI / 2.0;
140
141 // This is the angle that the shoulder will go down to when landing in the
142 // bellypan.
143 static constexpr double kShoulderLanded = -0.02;
144
Austin Schuhb1d682b2016-02-16 13:07:44 -0800145 // This is the angle below which the shoulder will slowly profile down and
146 // land.
147 static constexpr double kShoulderTransitionToLanded = 0.10;
148
Adam Snaider06779722016-02-14 15:26:22 -0800149 // This is the angle below which we consider the wrist close enough to level
150 // that we should move it to level before doing anything.
151 static constexpr double kWristAlmostLevel = 0.10;
152
153 // This is the angle that the shoulder will go down to when raising up before
154 // leveling the shooter for calibration.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800155 static constexpr double kShoulderWristClearAngle =
156 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
Adam Snaider06779722016-02-14 15:26:22 -0800157
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800158 enum State {
Adam Snaider06779722016-02-14 15:26:22 -0800159 // Wait for all the filters to be ready before starting the initialization
160 // process.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800161 UNINITIALIZED = 0,
Adam Snaider06779722016-02-14 15:26:22 -0800162
163 // We now are ready to decide how to zero. Decide what to do once we are
164 // enabled.
165 DISABLED_INITIALIZED = 1,
166
167 // Lift the arm up out of the way.
168 HIGH_ARM_ZERO_LIFT_ARM = 2,
169
170 HIGH_ARM_ZERO_LEVEL_SHOOTER = 3,
171
172 HIGH_ARM_ZERO_MOVE_INTAKE_OUT = 4,
173
174 HIGH_ARM_ZERO_LOWER_ARM = 6,
175
176 LOW_ARM_ZERO_LOWER_INTAKE = 7,
177 LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER = 8,
178 LOW_ARM_ZERO_LIFT_SHOULDER = 9,
179 LOW_ARM_ZERO_LEVEL_SHOOTER = 11,
180 // Run, but limit power to zeroing voltages.
181 SLOW_RUNNING = 12,
182 // Run with full power.
183 RUNNING = 13,
Austin Schuhb1d682b2016-02-16 13:07:44 -0800184 // Run, but limit power to zeroing voltages while landing.
185 LANDING_SLOW_RUNNING = 14,
186 // Run with full power while landing.
187 LANDING_RUNNING = 15,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800188 // Internal error caused the superstructure to abort.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800189 ESTOP = 16,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800190 };
191
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800192 bool IsRunning() const {
193 return (state_ == SLOW_RUNNING || state_ == RUNNING ||
194 state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING);
195 }
196
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800197 State state() const { return state_; }
Comran Morshed25f81a02016-01-23 13:40:10 +0000198
Adam Snaider06779722016-02-14 15:26:22 -0800199 // Returns the value to move the joint to such that it will stay below
200 // reference_angle starting at current_angle, but move at least move_distance
201 static double MoveButKeepBelow(double reference_angle, double current_angle,
202 double move_distance);
203 // Returns the value to move the joint to such that it will stay above
204 // reference_angle starting at current_angle, but move at least move_distance
205 static double MoveButKeepAbove(double reference_angle, double current_angle,
206 double move_distance);
207
Philipp Schrader07147532016-02-16 01:23:07 +0000208 // Returns true if anything is currently considered "collided".
209 bool collided() const { return collision_avoidance_.collided(); }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000210
Comran Morshed25f81a02016-01-23 13:40:10 +0000211 protected:
212 virtual void RunIteration(
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800213 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
Comran Morshed25f81a02016-01-23 13:40:10 +0000214 const control_loops::SuperstructureQueue::Position *position,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800215 control_loops::SuperstructureQueue::Output *output,
Comran Morshed25f81a02016-01-23 13:40:10 +0000216 control_loops::SuperstructureQueue::Status *status) override;
217
218 private:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800219 friend class testing::SuperstructureTest_DisabledGoalTest_Test;
Diana Vandenberge2843c62016-02-13 17:44:20 -0800220 friend class testing::SuperstructureTest_ArmZeroingErrorTest_Test;
221 friend class testing::SuperstructureTest_IntakeZeroingErrorTest_Test;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000222 friend class testing::SuperstructureTest_RespectsRange_Test;
223 friend class testing::SuperstructureTest_UpperHardstopStartup_Test;
Campbell Crowley152c7cf2016-02-14 21:20:50 -0800224 friend class testing::SuperstructureTest_DisabledWhileZeroingHigh_Test;
225 friend class testing::SuperstructureTest_DisabledWhileZeroingLow_Test;
Austin Schuh4b652c92019-05-27 13:22:27 -0700226
227 ::aos::Fetcher<::y2016::sensors::BallDetector> ball_detector_fetcher_;
228
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800229 Intake intake_;
230 Arm arm_;
231
Philipp Schrader0119eb12016-02-15 18:16:21 +0000232 CollisionAvoidance collision_avoidance_;
233
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800234 State state_ = UNINITIALIZED;
235 State last_state_ = UNINITIALIZED;
236
Austin Schuhbe041152016-02-28 22:01:52 -0800237 float last_shoulder_angle_ = 0.0;
238 float last_wrist_angle_ = 0.0;
239 float last_intake_angle_ = 0.0;
240
Comran Morshed71466fe2016-04-21 20:21:14 -0700241 double kill_shoulder_accumulator_ = 0.0;
242 bool kill_shoulder_ = false;
243
Adam Snaider06779722016-02-14 15:26:22 -0800244 // Returns true if the profile has finished, and the joint is within the
245 // specified tolerance.
246 bool IsArmNear(double tolerance);
247 bool IsArmNear(double shoulder_tolerance, double wrist_tolerance);
248 bool IsIntakeNear(double tolerance);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800249
Comran Morshed25f81a02016-01-23 13:40:10 +0000250 DISALLOW_COPY_AND_ASSIGN(Superstructure);
251};
252
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800253} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000254} // namespace control_loops
255} // namespace y2016
256
257#endif // Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_