blob: d94e34d12e61967511f962b3062e52f2ba87fef1 [file] [log] [blame]
Sabina Davis8d20ca82018-02-19 13:17:45 -08001#include "y2018/control_loops/superstructure/superstructure.h"
2
Neil Balchba9cbba2018-04-06 22:26:38 -07003#include <chrono>
4
John Park33858a32018-09-28 23:05:48 -07005#include "aos/controls/control_loops.q.h"
6#include "aos/logging/logging.h"
Sabina Davis8d20ca82018-02-19 13:17:45 -08007#include "frc971/control_loops/control_loops.q.h"
Austin Schuh8d5fff42018-05-30 20:44:12 -07008#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Sabina Davis8d20ca82018-02-19 13:17:45 -08009#include "y2018/constants.h"
10#include "y2018/control_loops/superstructure/intake/intake.h"
Austin Schuh8d5fff42018-05-30 20:44:12 -070011#include "y2018/status_light.q.h"
12#include "y2018/vision/vision.q.h"
Sabina Davis8d20ca82018-02-19 13:17:45 -080013
14namespace y2018 {
15namespace control_loops {
16namespace superstructure {
17
Neil Balchba9cbba2018-04-06 22:26:38 -070018using ::aos::monotonic_clock;
19
20namespace chrono = ::std::chrono;
21
Sabina Davis8d20ca82018-02-19 13:17:45 -080022namespace {
23// The maximum voltage the intake roller will be allowed to use.
24constexpr double kMaxIntakeRollerVoltage = 12.0;
25} // namespace
26
Austin Schuh55a13dc2019-01-27 22:39:03 -080027Superstructure::Superstructure(::aos::EventLoop *event_loop,
28 const ::std::string &name)
29 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(event_loop,
30 name),
Austin Schuh01a9f2a2019-05-27 13:36:30 -070031 status_light_sender_(
32 event_loop->MakeSender<::y2018::StatusLight>(".y2018.status_light")),
Sabina Davis8d20ca82018-02-19 13:17:45 -080033 intake_left_(constants::GetValues().left_intake.zeroing),
34 intake_right_(constants::GetValues().right_intake.zeroing) {}
35
36void Superstructure::RunIteration(
37 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
38 const control_loops::SuperstructureQueue::Position *position,
39 control_loops::SuperstructureQueue::Output *output,
40 control_loops::SuperstructureQueue::Status *status) {
41 if (WasReset()) {
42 LOG(ERROR, "WPILib reset, restarting\n");
43 intake_left_.Reset();
44 intake_right_.Reset();
Austin Schuhcb091712018-02-21 20:01:55 -080045 arm_.Reset();
Sabina Davis8d20ca82018-02-19 13:17:45 -080046 }
47
Neil Balchba9cbba2018-04-06 22:26:38 -070048 const double clipped_box_distance =
49 ::std::min(1.0, ::std::max(0.0, position->box_distance));
50
51 const double box_velocity =
52 (clipped_box_distance - last_box_distance_) / 0.005;
53
54 constexpr double kFilteredBoxVelocityAlpha = 0.02;
55 filtered_box_velocity_ =
56 box_velocity * kFilteredBoxVelocityAlpha +
57 (1.0 - kFilteredBoxVelocityAlpha) * filtered_box_velocity_;
58 status->filtered_box_velocity = filtered_box_velocity_;
59
60 constexpr double kCenteringAngleGain = 0.0;
61 const double left_intake_goal =
62 ::std::min(
63 arm_.max_intake_override(),
64 (unsafe_goal == nullptr ? 0.0
65 : unsafe_goal->intake.left_intake_angle)) +
66 last_intake_center_error_ * kCenteringAngleGain;
67 const double right_intake_goal =
68 ::std::min(
69 arm_.max_intake_override(),
70 (unsafe_goal == nullptr ? 0.0
71 : unsafe_goal->intake.right_intake_angle)) -
72 last_intake_center_error_ * kCenteringAngleGain;
Austin Schuh83cdd8a2018-03-21 20:49:02 -070073
Austin Schuh96341532018-03-09 21:17:24 -080074 intake_left_.Iterate(unsafe_goal != nullptr ? &(left_intake_goal) : nullptr,
Sabina Daviscfb872f2018-02-25 16:28:20 -080075 &(position->left_intake),
76 output != nullptr ? &(output->left_intake) : nullptr,
Sabina Davis8d20ca82018-02-19 13:17:45 -080077 &(status->left_intake));
78
Austin Schuh96341532018-03-09 21:17:24 -080079 intake_right_.Iterate(unsafe_goal != nullptr ? &(right_intake_goal) : nullptr,
Sabina Daviscfb872f2018-02-25 16:28:20 -080080 &(position->right_intake),
81 output != nullptr ? &(output->right_intake) : nullptr,
Sabina Davis8d20ca82018-02-19 13:17:45 -080082 &(status->right_intake));
83
Neil Balchba9cbba2018-04-06 22:26:38 -070084 const double intake_center_error =
85 intake_right_.output_position() - intake_left_.output_position();
86 last_intake_center_error_ = intake_center_error;
87
Austin Schuh96341532018-03-09 21:17:24 -080088 const bool intake_clear_of_box =
89 intake_left_.clear_of_box() && intake_right_.clear_of_box();
Austin Schuhd76546a2018-07-08 16:05:14 -070090
91 bool open_claw = unsafe_goal != nullptr ? unsafe_goal->open_claw : false;
92 if (unsafe_goal) {
93 if (unsafe_goal->open_threshold != 0.0) {
94 if (arm_.current_node() != unsafe_goal->arm_goal_position ||
95 arm_.path_distance_to_go() > unsafe_goal->open_threshold) {
96 open_claw = false;
97 }
98 }
99 }
Austin Schuhcb091712018-02-21 20:01:55 -0800100 arm_.Iterate(
101 unsafe_goal != nullptr ? &(unsafe_goal->arm_goal_position) : nullptr,
Austin Schuhd76546a2018-07-08 16:05:14 -0700102 unsafe_goal != nullptr ? unsafe_goal->grab_box : false, open_claw,
Neil Balchba9cbba2018-04-06 22:26:38 -0700103 unsafe_goal != nullptr ? unsafe_goal->close_claw : false,
104 &(position->arm), position->claw_beambreak_triggered,
Austin Schuh96341532018-03-09 21:17:24 -0800105 position->box_back_beambreak_triggered, intake_clear_of_box,
Austin Schuhd76546a2018-07-08 16:05:14 -0700106 unsafe_goal != nullptr ? unsafe_goal->voltage_winch > 1.0 : false,
107 unsafe_goal != nullptr ? unsafe_goal->trajectory_override : false,
Austin Schuhcb091712018-02-21 20:01:55 -0800108 output != nullptr ? &(output->voltage_proximal) : nullptr,
109 output != nullptr ? &(output->voltage_distal) : nullptr,
110 output != nullptr ? &(output->release_arm_brake) : nullptr,
Austin Schuhd76546a2018-07-08 16:05:14 -0700111 output != nullptr ? &(output->claw_grabbed) : nullptr, &(status->arm));
Austin Schuh17e484e2018-03-11 01:11:36 -0800112
113 if (output) {
114 if (unsafe_goal) {
115 output->hook_release = unsafe_goal->hook_release;
116 output->voltage_winch = unsafe_goal->voltage_winch;
117 output->forks_release = unsafe_goal->deploy_fork;
118 } else {
119 output->voltage_winch = 0.0;
120 output->hook_release = false;
121 output->forks_release = false;
122 }
123 }
Austin Schuhcb091712018-02-21 20:01:55 -0800124
125 status->estopped = status->left_intake.estopped ||
126 status->right_intake.estopped || status->arm.estopped;
127
128 status->zeroed = status->left_intake.zeroed && status->right_intake.zeroed &&
129 status->arm.zeroed;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800130
131 if (output && unsafe_goal) {
Austin Schuh96341532018-03-09 21:17:24 -0800132 double roller_voltage = ::std::max(
Sabina Davis8d20ca82018-02-19 13:17:45 -0800133 -kMaxIntakeRollerVoltage, ::std::min(unsafe_goal->intake.roller_voltage,
134 kMaxIntakeRollerVoltage));
Neil Balchba9cbba2018-04-06 22:26:38 -0700135 constexpr int kReverseTime = 14;
136 if (unsafe_goal->intake.roller_voltage < 0.0 ||
137 unsafe_goal->disable_box_correct) {
Sabina Daviscfb872f2018-02-25 16:28:20 -0800138 output->left_intake.voltage_rollers = roller_voltage;
139 output->right_intake.voltage_rollers = roller_voltage;
Austin Schuh17dd0892018-03-02 20:06:31 -0800140 rotation_state_ = RotationState::NOT_ROTATING;
141 rotation_count_ = 0;
Neil Balchba9cbba2018-04-06 22:26:38 -0700142 stuck_count_ = 0;
Austin Schuh17dd0892018-03-02 20:06:31 -0800143 } else {
Neil Balchba9cbba2018-04-06 22:26:38 -0700144 monotonic_clock::time_point monotonic_now = monotonic_clock::now();
145 const bool stuck = position->box_distance < 0.20 &&
146 filtered_box_velocity_ > -0.05 &&
147 !position->box_back_beambreak_triggered;
148 // Make sure we don't declare ourselves re-stuck too quickly. We want to
149 // wait 400 ms before triggering the stuck condition again.
150 if (!stuck) {
151 last_unstuck_time_ = monotonic_now;
152 }
153 if (monotonic_now < last_stuck_time_ + chrono::milliseconds(400)) {
154 last_unstuck_time_ = monotonic_now;
155 }
156
Austin Schuh17dd0892018-03-02 20:06:31 -0800157 switch (rotation_state_) {
158 case RotationState::NOT_ROTATING:
Neil Balchba9cbba2018-04-06 22:26:38 -0700159 if (stuck &&
160 monotonic_now > last_stuck_time_ + chrono::milliseconds(400) &&
161 monotonic_now > last_unstuck_time_ + chrono::milliseconds(100)) {
162 rotation_state_ = RotationState::STUCK;
163 ++stuck_count_;
164 last_stuck_time_ = monotonic_now;
165 } else if (position->left_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800166 rotation_state_ = RotationState::ROTATING_RIGHT;
167 rotation_count_ = kReverseTime;
168 break;
Sabina Daviscfb872f2018-02-25 16:28:20 -0800169 } else if (position->right_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800170 rotation_state_ = RotationState::ROTATING_LEFT;
171 rotation_count_ = kReverseTime;
172 break;
173 } else {
174 break;
175 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700176 case RotationState::STUCK: {
177 // Latch being stuck for 80 ms so we kick the box out far enough.
178 if (last_stuck_time_ + chrono::milliseconds(80) < monotonic_now) {
179 rotation_state_ = RotationState::NOT_ROTATING;
180 last_unstuck_time_ = monotonic_now;
181 }
182 } break;
Austin Schuh17dd0892018-03-02 20:06:31 -0800183 case RotationState::ROTATING_LEFT:
Sabina Daviscfb872f2018-02-25 16:28:20 -0800184 if (position->right_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800185 rotation_count_ = kReverseTime;
186 } else {
187 --rotation_count_;
188 }
189 if (rotation_count_ == 0) {
190 rotation_state_ = RotationState::NOT_ROTATING;
191 }
192 break;
193 case RotationState::ROTATING_RIGHT:
Sabina Daviscfb872f2018-02-25 16:28:20 -0800194 if (position->left_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800195 rotation_count_ = kReverseTime;
196 } else {
197 --rotation_count_;
198 }
199 if (rotation_count_ == 0) {
200 rotation_state_ = RotationState::NOT_ROTATING;
201 }
202 break;
203 }
204
Neil Balchba9cbba2018-04-06 22:26:38 -0700205 constexpr double kHoldVoltage = 1.0;
206 constexpr double kStuckVoltage = 10.0;
207
208 if (position->box_back_beambreak_triggered &&
209 roller_voltage > kHoldVoltage) {
210 roller_voltage = kHoldVoltage;
Austin Schuh96341532018-03-09 21:17:24 -0800211 }
Austin Schuh17dd0892018-03-02 20:06:31 -0800212 switch (rotation_state_) {
Neil Balchba9cbba2018-04-06 22:26:38 -0700213 case RotationState::NOT_ROTATING: {
214 double centering_gain = 13.0;
215 if (stuck_count_ > 1) {
216 if ((stuck_count_ - 1) % 2 == 0) {
217 centering_gain = 0.0;
218 }
219 }
220 output->left_intake.voltage_rollers =
221 roller_voltage - intake_center_error * centering_gain;
222 output->right_intake.voltage_rollers =
223 roller_voltage + intake_center_error * centering_gain;
224 } break;
225 case RotationState::STUCK: {
226 if (roller_voltage > kHoldVoltage) {
227 output->left_intake.voltage_rollers = -kStuckVoltage;
228 output->right_intake.voltage_rollers = -kStuckVoltage;
229 }
230 } break;
Austin Schuh17dd0892018-03-02 20:06:31 -0800231 case RotationState::ROTATING_LEFT:
Neil Balchba9cbba2018-04-06 22:26:38 -0700232 if (position->left_intake.beam_break) {
233 output->left_intake.voltage_rollers = -roller_voltage * 0.9;
234 } else {
235 output->left_intake.voltage_rollers = -roller_voltage * 0.6;
236 }
237 output->right_intake.voltage_rollers = roller_voltage;
Austin Schuh17dd0892018-03-02 20:06:31 -0800238 break;
239 case RotationState::ROTATING_RIGHT:
Neil Balchba9cbba2018-04-06 22:26:38 -0700240 output->left_intake.voltage_rollers = roller_voltage;
241 if (position->right_intake.beam_break) {
242 output->right_intake.voltage_rollers = -roller_voltage * 0.9;
243 } else {
244 output->right_intake.voltage_rollers = -roller_voltage * 0.6;
245 }
Austin Schuh17dd0892018-03-02 20:06:31 -0800246 break;
247 }
248 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700249 } else {
250 rotation_state_ = RotationState::NOT_ROTATING;
251 rotation_count_ = 0;
252 stuck_count_ = 0;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800253 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700254 status->rotation_state = static_cast<uint32_t>(rotation_state_);
Austin Schuh8d5fff42018-05-30 20:44:12 -0700255
256 ::frc971::control_loops::drivetrain_queue.output.FetchLatest();
257
258 ::y2018::vision::vision_status.FetchLatest();
259 if (status->estopped) {
260 SendColors(0.5, 0.0, 0.0);
261 } else if (!y2018::vision::vision_status.get() ||
262 y2018::vision::vision_status.Age() > chrono::seconds(1)) {
263 SendColors(0.5, 0.5, 0.0);
264 } else if (rotation_state_ == RotationState::ROTATING_LEFT ||
265 rotation_state_ == RotationState::ROTATING_RIGHT) {
266 SendColors(0.5, 0.20, 0.0);
267 } else if (rotation_state_ == RotationState::STUCK) {
268 SendColors(0.5, 0.0, 0.5);
269 } else if (position->box_back_beambreak_triggered) {
270 SendColors(0.0, 0.0, 0.5);
271 } else if (position->box_distance < 0.2) {
272 SendColors(0.0, 0.5, 0.0);
273 } else if (::frc971::control_loops::drivetrain_queue.output.get() &&
274 ::std::max(::std::abs(::frc971::control_loops::drivetrain_queue
275 .output->left_voltage),
276 ::std::abs(::frc971::control_loops::drivetrain_queue
277 .output->right_voltage)) > 11.5) {
278 SendColors(0.5, 0.0, 0.5);
279 } else {
280 SendColors(0.0, 0.0, 0.0);
281 }
282
Neil Balchba9cbba2018-04-06 22:26:38 -0700283 last_box_distance_ = clipped_box_distance;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800284}
285
Austin Schuh01a9f2a2019-05-27 13:36:30 -0700286void Superstructure::SendColors(float red, float green, float blue) {
287 auto new_status_light = status_light_sender_.MakeMessage();
288 new_status_light->red = red;
289 new_status_light->green = green;
290 new_status_light->blue = blue;
291
292 if (!new_status_light.Send()) {
293 LOG(ERROR, "Failed to send lights.\n");
294 }
295}
296
Sabina Davis8d20ca82018-02-19 13:17:45 -0800297} // namespace superstructure
298} // namespace control_loops
299} // namespace y2018