blob: 2219c95d44e5bfae66d99afb3d45ef7f1e19d454 [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"
Austin Schuh10c2d112016-02-14 13:42:28 -080011#include "y2016/control_loops/superstructure/superstructure_controls.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070012#include "y2016/control_loops/superstructure/superstructure_goal_generated.h"
13#include "y2016/control_loops/superstructure/superstructure_output_generated.h"
14#include "y2016/control_loops/superstructure/superstructure_position_generated.h"
15#include "y2016/control_loops/superstructure/superstructure_status_generated.h"
16#include "y2016/queues/ball_detector_generated.h"
Comran Morshed25f81a02016-01-23 13:40:10 +000017
18namespace y2016 {
19namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080020namespace superstructure {
21namespace testing {
Philipp Schrader0119eb12016-02-15 18:16:21 +000022class SuperstructureTest_RespectsRange_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080023class SuperstructureTest_DisabledGoalTest_Test;
Diana Vandenberge2843c62016-02-13 17:44:20 -080024class SuperstructureTest_ArmZeroingErrorTest_Test;
25class SuperstructureTest_IntakeZeroingErrorTest_Test;
Philipp Schrader0119eb12016-02-15 18:16:21 +000026class SuperstructureTest_UpperHardstopStartup_Test;
Campbell Crowley152c7cf2016-02-14 21:20:50 -080027class SuperstructureTest_DisabledWhileZeroingHigh_Test;
28class SuperstructureTest_DisabledWhileZeroingLow_Test;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080029} // namespace testing
30
Philipp Schrader0119eb12016-02-15 18:16:21 +000031// Helper class to prevent parts from crashing into each other. The parts in
32// question here are: the frame, the arm (plus shooter), and the intake.
33// Assumptions:
34// - The shoulder, the wrist, and intake are horizontal when at angle 0.
35// - The arm (i.e. shoulder) and shooter (i.e. wrist) are stored when they are
36// both at zero degrees.
37// - The intake at angle 0 is in a position to help get a ball in the robot.
38// - The intake at angle PI is in a "stowed" position. In other words, it is
39// folded over the arm and shooter when they are also in a stowed position.
40// - The shooter must remain horizontal when the arm is folding into the robot.
41// Otherwise, the shooter will collide with the frame.
42// - The arm has priority over the intake. If the arm wants to move in such a
43// way that interferes with the intake's movement then the intake must move
44// out of the way.
45class CollisionAvoidance {
46 public:
47 // Set up collision avoidance for an arm and intake.
48 CollisionAvoidance(Intake *intake, Arm *arm) : intake_(intake), arm_(arm) {}
49
50 // This function accepts goals for the intake and the arm and modifies them
51 // in such a way that collisions between all the different parts of the robot
52 // are avoided. The modified goals are then sent to the arm and intake as
53 // unprofiled goals.
54 void UpdateGoal(double shoulder_angle_goal, double wrist_angle_goal,
55 double intake_angle_goal);
56
Philipp Schrader07147532016-02-16 01:23:07 +000057 // Returns true if any of the limbs and frame are somehow currently
58 // interfering with one another. This is based purely on the angles that the
59 // limbs are reporting.
60 bool collided() const;
61
62 // Detects collision with the specified angles. This is especially useful for
63 // unit testing where we have proper ground truth for all the angles.
64 static bool collided_with_given_angles(double shoulder_angle,
65 double wrist_angle,
66 double intake_angle);
67
Philipp Schrader0119eb12016-02-15 18:16:21 +000068 // The shoulder angle (in radians) below which the shooter must be in a
69 // stowing position. In other words the wrist must be at angle zero if the
70 // shoulder is below this angle.
Austin Schuhb1d682b2016-02-16 13:07:44 -080071 static constexpr double kMinShoulderAngleForHorizontalShooter = 0.6;
Philipp Schrader0119eb12016-02-15 18:16:21 +000072
Campbell Crowley1836a432016-03-05 15:16:51 -080073 // The shoulder angle (in radians) below which the arm and the shooter have
74 // the potential to interfere with the intake.
Austin Schuh6ca0f792016-03-12 14:06:14 -080075 static constexpr double kMinShoulderAngleForIntakeUpInterference = 1.3;
76
77 // The shoulder angle (in radians) below which the shooter should be closer to
78 // level to fix the inverted case.
79 // TODO(austin): Verify
80 static constexpr double kMinShoulderAngleForIntakeInterference = 1.1;
Philipp Schrader0119eb12016-02-15 18:16:21 +000081
82 // The intake angle (in radians) above which the intake can interfere (i.e.
83 // collide) with the arm and/or shooter.
Austin Schuh2c717862016-03-13 15:32:53 -070084 static constexpr double kMaxIntakeAngleBeforeArmInterference = 1.05;
Philipp Schrader0119eb12016-02-15 18:16:21 +000085
86 // The maximum absolute angle (in radians) that the wrist must be below in
87 // order for the shouler to be allowed to move below
88 // kMinShoulderAngleForHorizontalShooter. In other words, only allow the arm
89 // to move down into the belly pan if the shooter is horizontal, ready to
90 // also be placed into the belly pan.
Austin Schuh2f5bfc82016-02-27 14:47:37 -080091 static constexpr double kMaxWristAngleForSafeArmStowing = 0.05;
Philipp Schrader0119eb12016-02-15 18:16:21 +000092
Austin Schuh2c717862016-03-13 15:32:53 -070093 // The maximum angle in radians that the wrist can be from horizontal
Austin Schuh6ca0f792016-03-12 14:06:14 -080094 // while it is near the intake.
Austin Schuh6802a9d2016-03-12 21:34:53 -080095 static constexpr double kMaxWristAngleForMovingByIntake = 0.50;
Austin Schuh2c717862016-03-13 15:32:53 -070096 // The minimum angle in radians that the wrist can be from horizontal
97 // while it is near the intake.
Austin Schuh214164b2016-03-20 16:50:09 -070098 static constexpr double kMinWristAngleForMovingByIntake = -1.50;
Austin Schuh6ca0f792016-03-12 14:06:14 -080099
Philipp Schrader0119eb12016-02-15 18:16:21 +0000100 // The shoulder angle (in radians) below which the intake can safely move
101 // into the collision zone. This is necessary when the robot wants to fold up
102 // completely (i.e. stow the arm, shooter, and intake).
Austin Schuh6ca0f792016-03-12 14:06:14 -0800103 static constexpr double kMaxShoulderAngleUntilSafeIntakeStowing = 0.19;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000104
105 private:
106 Intake *intake_;
107 Arm *arm_;
108};
109
Comran Morshed25f81a02016-01-23 13:40:10 +0000110class Superstructure
Alex Perrycb7da4b2019-08-28 19:35:56 -0700111 : public ::aos::controls::ControlLoop<Goal, Position, Status, Output> {
Comran Morshed25f81a02016-01-23 13:40:10 +0000112 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700113 explicit Superstructure(::aos::EventLoop *event_loop,
114 const ::std::string &name = "/superstructure");
Adam Snaider06779722016-02-14 15:26:22 -0800115
Austin Schuhf132dc52016-03-13 19:43:15 -0700116 static constexpr double kZeroingVoltage = 6.0;
Austin Schuhfef64ac2016-04-24 19:08:01 -0700117 static constexpr double kShooterHangingVoltage = 6.0;
Austin Schuh0c001a82016-05-01 12:30:09 -0700118 static constexpr double kShooterHangingLowVoltage = 2.0;
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000119 static constexpr double kOperatingVoltage = 12.0;
Austin Schuh2c717862016-03-13 15:32:53 -0700120 static constexpr double kLandingShoulderDownVoltage = -1.5;
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000121
Adam Snaider06779722016-02-14 15:26:22 -0800122 // This is the angle above which we will do a HIGH_ARM_ZERO, and below which
123 // we will do a LOW_ARM_ZERO.
124 static constexpr double kShoulderMiddleAngle = M_PI / 4.0;
125 // This is the large scale movement tolerance.
126 static constexpr double kLooseTolerance = 0.05;
127
128 // This is the small scale movement tolerance.
Austin Schuhf132dc52016-03-13 19:43:15 -0700129 static constexpr double kTightTolerance = 0.03;
Adam Snaider06779722016-02-14 15:26:22 -0800130
131 // This is the angle such that the intake will clear the arm when the shooter
132 // is level.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800133 static constexpr double kIntakeUpperClear =
134 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
Adam Snaider06779722016-02-14 15:26:22 -0800135 // This is the angle such that the intake will clear the arm when the shooter
136 // is at almost any position.
Austin Schuh2f5bfc82016-02-27 14:47:37 -0800137 static constexpr double kIntakeLowerClear = 0.4;
Adam Snaider06779722016-02-14 15:26:22 -0800138
139 // This is the angle that the shoulder will go to when doing the
140 // HIGH_ARM_ZERO.
141 static constexpr double kShoulderUpAngle = M_PI / 2.0;
142
143 // This is the angle that the shoulder will go down to when landing in the
144 // bellypan.
145 static constexpr double kShoulderLanded = -0.02;
146
Austin Schuhb1d682b2016-02-16 13:07:44 -0800147 // This is the angle below which the shoulder will slowly profile down and
148 // land.
149 static constexpr double kShoulderTransitionToLanded = 0.10;
150
Adam Snaider06779722016-02-14 15:26:22 -0800151 // This is the angle below which we consider the wrist close enough to level
152 // that we should move it to level before doing anything.
153 static constexpr double kWristAlmostLevel = 0.10;
154
155 // This is the angle that the shoulder will go down to when raising up before
156 // leveling the shooter for calibration.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800157 static constexpr double kShoulderWristClearAngle =
158 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
Adam Snaider06779722016-02-14 15:26:22 -0800159
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800160 enum State {
Adam Snaider06779722016-02-14 15:26:22 -0800161 // Wait for all the filters to be ready before starting the initialization
162 // process.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800163 UNINITIALIZED = 0,
Adam Snaider06779722016-02-14 15:26:22 -0800164
165 // We now are ready to decide how to zero. Decide what to do once we are
166 // enabled.
167 DISABLED_INITIALIZED = 1,
168
169 // Lift the arm up out of the way.
170 HIGH_ARM_ZERO_LIFT_ARM = 2,
171
172 HIGH_ARM_ZERO_LEVEL_SHOOTER = 3,
173
174 HIGH_ARM_ZERO_MOVE_INTAKE_OUT = 4,
175
176 HIGH_ARM_ZERO_LOWER_ARM = 6,
177
178 LOW_ARM_ZERO_LOWER_INTAKE = 7,
179 LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER = 8,
180 LOW_ARM_ZERO_LIFT_SHOULDER = 9,
181 LOW_ARM_ZERO_LEVEL_SHOOTER = 11,
182 // Run, but limit power to zeroing voltages.
183 SLOW_RUNNING = 12,
184 // Run with full power.
185 RUNNING = 13,
Austin Schuhb1d682b2016-02-16 13:07:44 -0800186 // Run, but limit power to zeroing voltages while landing.
187 LANDING_SLOW_RUNNING = 14,
188 // Run with full power while landing.
189 LANDING_RUNNING = 15,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800190 // Internal error caused the superstructure to abort.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800191 ESTOP = 16,
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800192 };
193
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800194 bool IsRunning() const {
195 return (state_ == SLOW_RUNNING || state_ == RUNNING ||
196 state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING);
197 }
198
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800199 State state() const { return state_; }
Comran Morshed25f81a02016-01-23 13:40:10 +0000200
Adam Snaider06779722016-02-14 15:26:22 -0800201 // Returns the value to move the joint to such that it will stay below
202 // reference_angle starting at current_angle, but move at least move_distance
203 static double MoveButKeepBelow(double reference_angle, double current_angle,
204 double move_distance);
205 // Returns the value to move the joint to such that it will stay above
206 // reference_angle starting at current_angle, but move at least move_distance
207 static double MoveButKeepAbove(double reference_angle, double current_angle,
208 double move_distance);
209
Philipp Schrader07147532016-02-16 01:23:07 +0000210 // Returns true if anything is currently considered "collided".
211 bool collided() const { return collision_avoidance_.collided(); }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000212
Comran Morshed25f81a02016-01-23 13:40:10 +0000213 protected:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700214 virtual void RunIteration(const Goal *unsafe_goal, const Position *position,
215 aos::Sender<Output>::Builder *output,
216 aos::Sender<Status>::Builder *status) override;
Comran Morshed25f81a02016-01-23 13:40:10 +0000217
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_