blob: fd00fd1e9e1126dbb1a08210097bcae938f5de2d [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
6#include "aos/common/controls/control_loop.h"
Austin Schuh2fc10fa2016-02-08 00:44:34 -08007#include "aos/common/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"
Comran Morshed25f81a02016-01-23 13:40:10 +000013
14namespace y2016 {
15namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080016namespace superstructure {
17namespace testing {
Philipp Schrader0119eb12016-02-15 18:16:21 +000018class SuperstructureTest_RespectsRange_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080019class SuperstructureTest_DisabledGoalTest_Test;
Diana Vandenberge2843c62016-02-13 17:44:20 -080020class SuperstructureTest_ArmZeroingErrorTest_Test;
21class SuperstructureTest_IntakeZeroingErrorTest_Test;
Philipp Schrader0119eb12016-02-15 18:16:21 +000022class SuperstructureTest_UpperHardstopStartup_Test;
Campbell Crowley152c7cf2016-02-14 21:20:50 -080023class SuperstructureTest_DisabledWhileZeroingHigh_Test;
24class SuperstructureTest_DisabledWhileZeroingLow_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080025} // namespace testing
26
Philipp Schrader0119eb12016-02-15 18:16:21 +000027// Helper class to prevent parts from crashing into each other. The parts in
28// question here are: the frame, the arm (plus shooter), and the intake.
29// Assumptions:
30// - The shoulder, the wrist, and intake are horizontal when at angle 0.
31// - The arm (i.e. shoulder) and shooter (i.e. wrist) are stored when they are
32// both at zero degrees.
33// - The intake at angle 0 is in a position to help get a ball in the robot.
34// - The intake at angle PI is in a "stowed" position. In other words, it is
35// folded over the arm and shooter when they are also in a stowed position.
36// - The shooter must remain horizontal when the arm is folding into the robot.
37// Otherwise, the shooter will collide with the frame.
38// - The arm has priority over the intake. If the arm wants to move in such a
39// way that interferes with the intake's movement then the intake must move
40// out of the way.
41class CollisionAvoidance {
42 public:
43 // Set up collision avoidance for an arm and intake.
44 CollisionAvoidance(Intake *intake, Arm *arm) : intake_(intake), arm_(arm) {}
45
46 // This function accepts goals for the intake and the arm and modifies them
47 // in such a way that collisions between all the different parts of the robot
48 // are avoided. The modified goals are then sent to the arm and intake as
49 // unprofiled goals.
50 void UpdateGoal(double shoulder_angle_goal, double wrist_angle_goal,
51 double intake_angle_goal);
52
Philipp Schrader07147532016-02-16 01:23:07 +000053 // Returns true if any of the limbs and frame are somehow currently
54 // interfering with one another. This is based purely on the angles that the
55 // limbs are reporting.
56 bool collided() const;
57
58 // Detects collision with the specified angles. This is especially useful for
59 // unit testing where we have proper ground truth for all the angles.
60 static bool collided_with_given_angles(double shoulder_angle,
61 double wrist_angle,
62 double intake_angle);
63
Philipp Schrader0119eb12016-02-15 18:16:21 +000064 // The shoulder angle (in radians) below which the shooter must be in a
65 // stowing position. In other words the wrist must be at angle zero if the
66 // shoulder is below this angle.
Austin Schuhb1d682b2016-02-16 13:07:44 -080067 static constexpr double kMinShoulderAngleForHorizontalShooter = 0.6;
Philipp Schrader0119eb12016-02-15 18:16:21 +000068
Campbell Crowley1836a432016-03-05 15:16:51 -080069 // The shoulder angle (in radians) below which the arm and the shooter have
70 // the potential to interfere with the intake.
Austin Schuh6ca0f792016-03-12 14:06:14 -080071 static constexpr double kMinShoulderAngleForIntakeUpInterference = 1.3;
72
73 // The shoulder angle (in radians) below which the shooter should be closer to
74 // level to fix the inverted case.
75 // TODO(austin): Verify
76 static constexpr double kMinShoulderAngleForIntakeInterference = 1.1;
Philipp Schrader0119eb12016-02-15 18:16:21 +000077
78 // The intake angle (in radians) above which the intake can interfere (i.e.
79 // collide) with the arm and/or shooter.
Campbell Crowley1836a432016-03-05 15:16:51 -080080 static constexpr double kMaxIntakeAngleBeforeArmInterference = 1.12;
Philipp Schrader0119eb12016-02-15 18:16:21 +000081
82 // The maximum absolute angle (in radians) that the wrist must be below in
83 // order for the shouler to be allowed to move below
84 // kMinShoulderAngleForHorizontalShooter. In other words, only allow the arm
85 // to move down into the belly pan if the shooter is horizontal, ready to
86 // also be placed into the belly pan.
Austin Schuh2f5bfc82016-02-27 14:47:37 -080087 static constexpr double kMaxWristAngleForSafeArmStowing = 0.05;
Philipp Schrader0119eb12016-02-15 18:16:21 +000088
Austin Schuh6ca0f792016-03-12 14:06:14 -080089 // The maximum absolute angle in radians that the wrist can be from horizontal
90 // while it is near the intake.
Austin Schuh6802a9d2016-03-12 21:34:53 -080091 static constexpr double kMaxWristAngleForMovingByIntake = 0.50;
Austin Schuh6ca0f792016-03-12 14:06:14 -080092
Philipp Schrader0119eb12016-02-15 18:16:21 +000093 // The shoulder angle (in radians) below which the intake can safely move
94 // into the collision zone. This is necessary when the robot wants to fold up
95 // completely (i.e. stow the arm, shooter, and intake).
Austin Schuh6ca0f792016-03-12 14:06:14 -080096 static constexpr double kMaxShoulderAngleUntilSafeIntakeStowing = 0.19;
Philipp Schrader0119eb12016-02-15 18:16:21 +000097
98 private:
99 Intake *intake_;
100 Arm *arm_;
101};
102
Comran Morshed25f81a02016-01-23 13:40:10 +0000103class Superstructure
104 : public ::aos::controls::ControlLoop<control_loops::SuperstructureQueue> {
105 public:
106 explicit Superstructure(
107 control_loops::SuperstructureQueue *my_superstructure =
108 &control_loops::superstructure_queue);
Adam Snaider06779722016-02-14 15:26:22 -0800109
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000110 static constexpr double kZeroingVoltage = 5.0;
111 static constexpr double kOperatingVoltage = 12.0;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800112 static constexpr double kLandingShoulderDownVoltage = -2.0;
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000113
Adam Snaider06779722016-02-14 15:26:22 -0800114 // This is the angle above which we will do a HIGH_ARM_ZERO, and below which
115 // we will do a LOW_ARM_ZERO.
116 static constexpr double kShoulderMiddleAngle = M_PI / 4.0;
117 // This is the large scale movement tolerance.
118 static constexpr double kLooseTolerance = 0.05;
119
120 // This is the small scale movement tolerance.
121 static constexpr double kTightTolerance = 0.01;
122
123 // This is the angle such that the intake will clear the arm when the shooter
124 // is level.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800125 static constexpr double kIntakeUpperClear =
126 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
Adam Snaider06779722016-02-14 15:26:22 -0800127 // This is the angle such that the intake will clear the arm when the shooter
128 // is at almost any position.
Austin Schuh2f5bfc82016-02-27 14:47:37 -0800129 static constexpr double kIntakeLowerClear = 0.4;
Adam Snaider06779722016-02-14 15:26:22 -0800130
131 // This is the angle that the shoulder will go to when doing the
132 // HIGH_ARM_ZERO.
133 static constexpr double kShoulderUpAngle = M_PI / 2.0;
134
135 // This is the angle that the shoulder will go down to when landing in the
136 // bellypan.
137 static constexpr double kShoulderLanded = -0.02;
138
Austin Schuhb1d682b2016-02-16 13:07:44 -0800139 // This is the angle below which the shoulder will slowly profile down and
140 // land.
141 static constexpr double kShoulderTransitionToLanded = 0.10;
142
Adam Snaider06779722016-02-14 15:26:22 -0800143 // This is the angle below which we consider the wrist close enough to level
144 // that we should move it to level before doing anything.
145 static constexpr double kWristAlmostLevel = 0.10;
146
147 // This is the angle that the shoulder will go down to when raising up before
148 // leveling the shooter for calibration.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800149 static constexpr double kShoulderWristClearAngle =
150 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
Adam Snaider06779722016-02-14 15:26:22 -0800151
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800152 enum State {
Adam Snaider06779722016-02-14 15:26:22 -0800153 // Wait for all the filters to be ready before starting the initialization
154 // process.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800155 UNINITIALIZED = 0,
Adam Snaider06779722016-02-14 15:26:22 -0800156
157 // We now are ready to decide how to zero. Decide what to do once we are
158 // enabled.
159 DISABLED_INITIALIZED = 1,
160
161 // Lift the arm up out of the way.
162 HIGH_ARM_ZERO_LIFT_ARM = 2,
163
164 HIGH_ARM_ZERO_LEVEL_SHOOTER = 3,
165
166 HIGH_ARM_ZERO_MOVE_INTAKE_OUT = 4,
167
168 HIGH_ARM_ZERO_LOWER_ARM = 6,
169
170 LOW_ARM_ZERO_LOWER_INTAKE = 7,
171 LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER = 8,
172 LOW_ARM_ZERO_LIFT_SHOULDER = 9,
173 LOW_ARM_ZERO_LEVEL_SHOOTER = 11,
174 // Run, but limit power to zeroing voltages.
175 SLOW_RUNNING = 12,
176 // Run with full power.
177 RUNNING = 13,
Austin Schuhb1d682b2016-02-16 13:07:44 -0800178 // Run, but limit power to zeroing voltages while landing.
179 LANDING_SLOW_RUNNING = 14,
180 // Run with full power while landing.
181 LANDING_RUNNING = 15,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800182 // Internal error caused the superstructure to abort.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800183 ESTOP = 16,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800184 };
185
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800186 bool IsRunning() const {
187 return (state_ == SLOW_RUNNING || state_ == RUNNING ||
188 state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING);
189 }
190
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800191 State state() const { return state_; }
Comran Morshed25f81a02016-01-23 13:40:10 +0000192
Adam Snaider06779722016-02-14 15:26:22 -0800193 // Returns the value to move the joint to such that it will stay below
194 // reference_angle starting at current_angle, but move at least move_distance
195 static double MoveButKeepBelow(double reference_angle, double current_angle,
196 double move_distance);
197 // Returns the value to move the joint to such that it will stay above
198 // reference_angle starting at current_angle, but move at least move_distance
199 static double MoveButKeepAbove(double reference_angle, double current_angle,
200 double move_distance);
201
Philipp Schrader07147532016-02-16 01:23:07 +0000202 // Returns true if anything is currently considered "collided".
203 bool collided() const { return collision_avoidance_.collided(); }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000204
Comran Morshed25f81a02016-01-23 13:40:10 +0000205 protected:
206 virtual void RunIteration(
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800207 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
Comran Morshed25f81a02016-01-23 13:40:10 +0000208 const control_loops::SuperstructureQueue::Position *position,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800209 control_loops::SuperstructureQueue::Output *output,
Comran Morshed25f81a02016-01-23 13:40:10 +0000210 control_loops::SuperstructureQueue::Status *status) override;
211
212 private:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800213 friend class testing::SuperstructureTest_DisabledGoalTest_Test;
Diana Vandenberge2843c62016-02-13 17:44:20 -0800214 friend class testing::SuperstructureTest_ArmZeroingErrorTest_Test;
215 friend class testing::SuperstructureTest_IntakeZeroingErrorTest_Test;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000216 friend class testing::SuperstructureTest_RespectsRange_Test;
217 friend class testing::SuperstructureTest_UpperHardstopStartup_Test;
Campbell Crowley152c7cf2016-02-14 21:20:50 -0800218 friend class testing::SuperstructureTest_DisabledWhileZeroingHigh_Test;
219 friend class testing::SuperstructureTest_DisabledWhileZeroingLow_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800220 Intake intake_;
221 Arm arm_;
222
Philipp Schrader0119eb12016-02-15 18:16:21 +0000223 CollisionAvoidance collision_avoidance_;
224
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800225 State state_ = UNINITIALIZED;
226 State last_state_ = UNINITIALIZED;
227
Austin Schuhbe041152016-02-28 22:01:52 -0800228 float last_shoulder_angle_ = 0.0;
229 float last_wrist_angle_ = 0.0;
230 float last_intake_angle_ = 0.0;
231
Adam Snaider06779722016-02-14 15:26:22 -0800232 // Returns true if the profile has finished, and the joint is within the
233 // specified tolerance.
234 bool IsArmNear(double tolerance);
235 bool IsArmNear(double shoulder_tolerance, double wrist_tolerance);
236 bool IsIntakeNear(double tolerance);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800237
Comran Morshed25f81a02016-01-23 13:40:10 +0000238 DISALLOW_COPY_AND_ASSIGN(Superstructure);
239};
240
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800241} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000242} // namespace control_loops
243} // namespace y2016
244
245#endif // Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_