blob: d3d288b1531d0761a6e43d6ceacd2cd7eebb1720 [file] [log] [blame]
Adam Snaider18f44172016-10-22 15:30:21 -07001#include "y2016_bot3/control_loops/intake/intake.h"
2#include "y2016_bot3/control_loops/intake/intake_controls.h"
3
4#include "aos/common/commonmath.h"
5#include "aos/common/controls/control_loops.q.h"
6#include "aos/common/logging/logging.h"
7
8#include "y2016_bot3/control_loops/intake/integral_intake_plant.h"
9#include "y2016_bot3/queues/ball_detector.q.h"
10
11namespace y2016_bot3 {
12namespace control_loops {
13namespace intake {
14
15namespace {
16// The maximum voltage the intake roller will be allowed to use.
17constexpr float kMaxIntakeTopVoltage = 12.0;
18constexpr float kMaxIntakeBottomVoltage = 12.0;
19
20}
21// namespace
22
23void LimitChecker::UpdateGoal(double intake_angle_goal) {
24 intake_->set_unprofiled_goal(intake_angle_goal);
25}
26
27Intake::Intake(control_loops::IntakeQueue *intake_queue)
28 : aos::controls::ControlLoop<control_loops::IntakeQueue>(intake_queue),
29 limit_checker_(&intake_) {}
30bool Intake::IsIntakeNear(double tolerance) {
31 return ((intake_.unprofiled_goal() - intake_.X_hat())
32 .block<2, 1>(0, 0)
33 .lpNorm<Eigen::Infinity>() < tolerance);
34}
35
Adam Snaider18f44172016-10-22 15:30:21 -070036void Intake::RunIteration(const control_loops::IntakeQueue::Goal *unsafe_goal,
37 const control_loops::IntakeQueue::Position *position,
38 control_loops::IntakeQueue::Output *output,
39 control_loops::IntakeQueue::Status *status) {
40 const State state_before_switch = state_;
41 if (WasReset()) {
42 LOG(ERROR, "WPILib reset, restarting\n");
43 intake_.Reset();
44 state_ = UNINITIALIZED;
45 }
46
47 // Bool to track if we should turn the motors on or not.
48 bool disable = output == nullptr;
49
50 intake_.Correct(position->intake);
51
52 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
53 //
54 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
55 // moving the shooter to be horizontal, moving the intake out, and then moving
56 // the arm back down.
57 //
58 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
59 // leveling the shooter, and then moving back down.
60
61 if (intake_.error()) {
62 state_ = ESTOP;
63 }
64
65 switch (state_) {
66 case UNINITIALIZED:
67 // Wait in the uninitialized state until intake is initialized.
68 LOG(DEBUG, "Uninitialized, waiting for intake\n");
69 if (intake_.initialized()) {
70 state_ = DISABLED_INITIALIZED;
71 }
72 disable = true;
73 break;
74
75 case DISABLED_INITIALIZED:
76 // Wait here until we are either fully zeroed while disabled, or we become
77 // enabled.
78 if (disable) {
79 if (intake_.zeroed()) {
80 state_ = SLOW_RUNNING;
81 }
82 } else {
83 if (intake_.angle() <= kIntakeMiddleAngle) {
84 state_ = ZERO_LIFT_INTAKE;
85 } else {
86 state_ = ZERO_LOWER_INTAKE;
87 }
88 }
89
90 // Set the goals to where we are now so when we start back up, we don't
91 // jump.
92 intake_.ForceGoal(intake_.angle());
93 // Set up the profile to be the zeroing profile.
94 intake_.AdjustProfile(0.5, 10);
95
96 // We are not ready to start doing anything yet.
97 disable = true;
98 break;
99
100 case ZERO_LOWER_INTAKE:
101 if (disable) {
102 state_ = DISABLED_INITIALIZED;
103 } else {
104 intake_.set_unprofiled_goal(kIntakeDownAngle);
105
106 if (IsIntakeNear(kLooseTolerance)) {
107 // Close enough, start the next move.
108 state_ = RUNNING;
109 }
110 }
111 break;
112
113 case ZERO_LIFT_INTAKE:
114 if (disable) {
115 state_ = DISABLED_INITIALIZED;
116 } else {
117 intake_.set_unprofiled_goal(kIntakeUpAngle);
118
119 if (IsIntakeNear(kLooseTolerance)) {
120 // Close enough, start the next move.
121 state_ = RUNNING;
122 }
123 }
124 break;
125
126 // These 4 cases are very similar.
127 case SLOW_RUNNING:
128 case RUNNING: {
129 if (disable) {
130 // If we are disabled, go to slow running if we are collided.
131 // Reset the profile to the current position so it moves well from here.
132 intake_.ForceGoal(intake_.angle());
133 }
134
135 double requested_intake = M_PI / 2.0;
136
137 if (unsafe_goal) {
138 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
139 unsafe_goal->max_angular_acceleration_intake);
140
141 requested_intake = unsafe_goal->angle_intake;
142 }
143 //Push the request out to the hardware.
144 limit_checker_.UpdateGoal(requested_intake);
145
146 // ESTOP if we hit the hard limits.
147 if (intake_.CheckHardLimits() && output) {
148 state_ = ESTOP;
149 }
150 } break;
151
152 case ESTOP:
153 LOG(ERROR, "Estop\n");
154 disable = true;
155 break;
156 }
157
158 // Set the voltage limits.
159 const double max_voltage =
160 (state_ == RUNNING) ? kOperatingVoltage : kZeroingVoltage;
161
162 intake_.set_max_voltage(max_voltage);
163
164 // Calculate the loops for a cycle.
165 {
166 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
167 status->intake.position_power = intake_.controller().K(0, 0) * error(0, 0);
168 status->intake.velocity_power = intake_.controller().K(0, 1) * error(1, 0);
169 }
170
171 intake_.Update(disable);
172
173 // Write out all the voltages.
174 if (output) {
175 output->voltage_intake = intake_.intake_voltage();
176
177 output->voltage_top_rollers = 0.0;
178 output->voltage_bottom_rollers = 0.0;
179
180 if (unsafe_goal) {
181 // Ball detector lights.
182 ::y2016_bot3::sensors::ball_detector.FetchLatest();
183 bool ball_detected = false;
184 if (::y2016_bot3::sensors::ball_detector.get()) {
185 ball_detected = ::y2016_bot3::sensors::ball_detector->voltage > 2.5;
186 }
187
188 // Intake.
189 if (unsafe_goal->force_intake || !ball_detected) {
190 output->voltage_top_rollers = ::std::max(
191 -kMaxIntakeTopVoltage,
192 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
193 output->voltage_bottom_rollers =
194 ::std::max(-kMaxIntakeBottomVoltage,
195 ::std::min(unsafe_goal->voltage_bottom_rollers,
196 kMaxIntakeBottomVoltage));
197 } else {
198 output->voltage_top_rollers = 0.0;
199 output->voltage_bottom_rollers = 0.0;
200 }
201
202 // Traverse.
203 output->traverse_unlatched = unsafe_goal->traverse_unlatched;
204 output->traverse_down = unsafe_goal->traverse_down;
205 }
206 }
207
208 // Save debug/internal state.
209 status->zeroed = intake_.zeroed();
210
211 status->intake.angle = intake_.X_hat(0, 0);
212 status->intake.angular_velocity = intake_.X_hat(1, 0);
213 status->intake.goal_angle = intake_.goal(0, 0);
214 status->intake.goal_angular_velocity = intake_.goal(1, 0);
215 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
216 status->intake.unprofiled_goal_angular_velocity =
217 intake_.unprofiled_goal(1, 0);
218 status->intake.calculated_velocity =
219 (intake_.angle() - last_intake_angle_) / 0.005;
220 status->intake.voltage_error = intake_.X_hat(2, 0);
221 status->intake.estimator_state = intake_.IntakeEstimatorState();
222 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
223
224 last_intake_angle_ = intake_.angle();
225
226 status->estopped = (state_ == ESTOP);
227
228 status->state = state_;
229
230 last_state_ = state_before_switch;
231}
232
233constexpr double Intake::kZeroingVoltage;
234constexpr double Intake::kOperatingVoltage;
235constexpr double Intake::kLooseTolerance;
236constexpr double Intake::kTightTolerance;
237constexpr double Intake::kIntakeUpAngle;
238constexpr double Intake::kIntakeMiddleAngle;
239constexpr double Intake::kIntakeDownAngle;
240
241} // namespace intake
242} // namespace control_loops
243} // namespace y2016_bot3