blob: a2dcaf648d52552e1ce4926b586a516f31a89581 [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")),
Austin Schuh300f2f62019-05-27 13:49:23 -070033 vision_status_fetcher_(
34 event_loop->MakeFetcher<::y2018::vision::VisionStatus>(
35 ".y2018.vision.vision_status")),
Austin Schuhbd0a40f2019-06-30 14:56:31 -070036 drivetrain_output_fetcher_(
37 event_loop
38 ->MakeFetcher<::frc971::control_loops::DrivetrainQueue::Output>(
39 ".frc971.control_loops.drivetrain_queue.output")),
Sabina Davis8d20ca82018-02-19 13:17:45 -080040 intake_left_(constants::GetValues().left_intake.zeroing),
41 intake_right_(constants::GetValues().right_intake.zeroing) {}
42
43void Superstructure::RunIteration(
44 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
45 const control_loops::SuperstructureQueue::Position *position,
46 control_loops::SuperstructureQueue::Output *output,
47 control_loops::SuperstructureQueue::Status *status) {
Austin Schuh20177c92019-07-07 20:48:24 -070048 const monotonic_clock::time_point monotonic_now =
49 event_loop()->monotonic_now();
Sabina Davis8d20ca82018-02-19 13:17:45 -080050 if (WasReset()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070051 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Sabina Davis8d20ca82018-02-19 13:17:45 -080052 intake_left_.Reset();
53 intake_right_.Reset();
Austin Schuhcb091712018-02-21 20:01:55 -080054 arm_.Reset();
Sabina Davis8d20ca82018-02-19 13:17:45 -080055 }
56
Neil Balchba9cbba2018-04-06 22:26:38 -070057 const double clipped_box_distance =
58 ::std::min(1.0, ::std::max(0.0, position->box_distance));
59
60 const double box_velocity =
61 (clipped_box_distance - last_box_distance_) / 0.005;
62
63 constexpr double kFilteredBoxVelocityAlpha = 0.02;
64 filtered_box_velocity_ =
65 box_velocity * kFilteredBoxVelocityAlpha +
66 (1.0 - kFilteredBoxVelocityAlpha) * filtered_box_velocity_;
67 status->filtered_box_velocity = filtered_box_velocity_;
68
69 constexpr double kCenteringAngleGain = 0.0;
70 const double left_intake_goal =
71 ::std::min(
72 arm_.max_intake_override(),
73 (unsafe_goal == nullptr ? 0.0
74 : unsafe_goal->intake.left_intake_angle)) +
75 last_intake_center_error_ * kCenteringAngleGain;
76 const double right_intake_goal =
77 ::std::min(
78 arm_.max_intake_override(),
79 (unsafe_goal == nullptr ? 0.0
80 : unsafe_goal->intake.right_intake_angle)) -
81 last_intake_center_error_ * kCenteringAngleGain;
Austin Schuh83cdd8a2018-03-21 20:49:02 -070082
Austin Schuh96341532018-03-09 21:17:24 -080083 intake_left_.Iterate(unsafe_goal != nullptr ? &(left_intake_goal) : nullptr,
Sabina Daviscfb872f2018-02-25 16:28:20 -080084 &(position->left_intake),
85 output != nullptr ? &(output->left_intake) : nullptr,
Sabina Davis8d20ca82018-02-19 13:17:45 -080086 &(status->left_intake));
87
Austin Schuh96341532018-03-09 21:17:24 -080088 intake_right_.Iterate(unsafe_goal != nullptr ? &(right_intake_goal) : nullptr,
Sabina Daviscfb872f2018-02-25 16:28:20 -080089 &(position->right_intake),
90 output != nullptr ? &(output->right_intake) : nullptr,
Sabina Davis8d20ca82018-02-19 13:17:45 -080091 &(status->right_intake));
92
Neil Balchba9cbba2018-04-06 22:26:38 -070093 const double intake_center_error =
94 intake_right_.output_position() - intake_left_.output_position();
95 last_intake_center_error_ = intake_center_error;
96
Austin Schuh96341532018-03-09 21:17:24 -080097 const bool intake_clear_of_box =
98 intake_left_.clear_of_box() && intake_right_.clear_of_box();
Austin Schuhd76546a2018-07-08 16:05:14 -070099
100 bool open_claw = unsafe_goal != nullptr ? unsafe_goal->open_claw : false;
101 if (unsafe_goal) {
102 if (unsafe_goal->open_threshold != 0.0) {
103 if (arm_.current_node() != unsafe_goal->arm_goal_position ||
104 arm_.path_distance_to_go() > unsafe_goal->open_threshold) {
105 open_claw = false;
106 }
107 }
108 }
Austin Schuhcb091712018-02-21 20:01:55 -0800109 arm_.Iterate(
Austin Schuh20177c92019-07-07 20:48:24 -0700110 monotonic_now,
Austin Schuhcb091712018-02-21 20:01:55 -0800111 unsafe_goal != nullptr ? &(unsafe_goal->arm_goal_position) : nullptr,
Austin Schuhd76546a2018-07-08 16:05:14 -0700112 unsafe_goal != nullptr ? unsafe_goal->grab_box : false, open_claw,
Neil Balchba9cbba2018-04-06 22:26:38 -0700113 unsafe_goal != nullptr ? unsafe_goal->close_claw : false,
114 &(position->arm), position->claw_beambreak_triggered,
Austin Schuh96341532018-03-09 21:17:24 -0800115 position->box_back_beambreak_triggered, intake_clear_of_box,
Austin Schuhd76546a2018-07-08 16:05:14 -0700116 unsafe_goal != nullptr ? unsafe_goal->voltage_winch > 1.0 : false,
117 unsafe_goal != nullptr ? unsafe_goal->trajectory_override : false,
Austin Schuhcb091712018-02-21 20:01:55 -0800118 output != nullptr ? &(output->voltage_proximal) : nullptr,
119 output != nullptr ? &(output->voltage_distal) : nullptr,
120 output != nullptr ? &(output->release_arm_brake) : nullptr,
Austin Schuhd76546a2018-07-08 16:05:14 -0700121 output != nullptr ? &(output->claw_grabbed) : nullptr, &(status->arm));
Austin Schuh17e484e2018-03-11 01:11:36 -0800122
123 if (output) {
124 if (unsafe_goal) {
125 output->hook_release = unsafe_goal->hook_release;
126 output->voltage_winch = unsafe_goal->voltage_winch;
127 output->forks_release = unsafe_goal->deploy_fork;
128 } else {
129 output->voltage_winch = 0.0;
130 output->hook_release = false;
131 output->forks_release = false;
132 }
133 }
Austin Schuhcb091712018-02-21 20:01:55 -0800134
135 status->estopped = status->left_intake.estopped ||
136 status->right_intake.estopped || status->arm.estopped;
137
138 status->zeroed = status->left_intake.zeroed && status->right_intake.zeroed &&
139 status->arm.zeroed;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800140
141 if (output && unsafe_goal) {
Austin Schuh96341532018-03-09 21:17:24 -0800142 double roller_voltage = ::std::max(
Sabina Davis8d20ca82018-02-19 13:17:45 -0800143 -kMaxIntakeRollerVoltage, ::std::min(unsafe_goal->intake.roller_voltage,
144 kMaxIntakeRollerVoltage));
Neil Balchba9cbba2018-04-06 22:26:38 -0700145 constexpr int kReverseTime = 14;
146 if (unsafe_goal->intake.roller_voltage < 0.0 ||
147 unsafe_goal->disable_box_correct) {
Sabina Daviscfb872f2018-02-25 16:28:20 -0800148 output->left_intake.voltage_rollers = roller_voltage;
149 output->right_intake.voltage_rollers = roller_voltage;
Austin Schuh17dd0892018-03-02 20:06:31 -0800150 rotation_state_ = RotationState::NOT_ROTATING;
151 rotation_count_ = 0;
Neil Balchba9cbba2018-04-06 22:26:38 -0700152 stuck_count_ = 0;
Austin Schuh17dd0892018-03-02 20:06:31 -0800153 } else {
Neil Balchba9cbba2018-04-06 22:26:38 -0700154 const bool stuck = position->box_distance < 0.20 &&
155 filtered_box_velocity_ > -0.05 &&
156 !position->box_back_beambreak_triggered;
157 // Make sure we don't declare ourselves re-stuck too quickly. We want to
158 // wait 400 ms before triggering the stuck condition again.
159 if (!stuck) {
160 last_unstuck_time_ = monotonic_now;
161 }
162 if (monotonic_now < last_stuck_time_ + chrono::milliseconds(400)) {
163 last_unstuck_time_ = monotonic_now;
164 }
165
Austin Schuh17dd0892018-03-02 20:06:31 -0800166 switch (rotation_state_) {
167 case RotationState::NOT_ROTATING:
Neil Balchba9cbba2018-04-06 22:26:38 -0700168 if (stuck &&
169 monotonic_now > last_stuck_time_ + chrono::milliseconds(400) &&
170 monotonic_now > last_unstuck_time_ + chrono::milliseconds(100)) {
171 rotation_state_ = RotationState::STUCK;
172 ++stuck_count_;
173 last_stuck_time_ = monotonic_now;
174 } else if (position->left_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800175 rotation_state_ = RotationState::ROTATING_RIGHT;
176 rotation_count_ = kReverseTime;
177 break;
Sabina Daviscfb872f2018-02-25 16:28:20 -0800178 } else if (position->right_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800179 rotation_state_ = RotationState::ROTATING_LEFT;
180 rotation_count_ = kReverseTime;
181 break;
182 } else {
183 break;
184 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700185 case RotationState::STUCK: {
186 // Latch being stuck for 80 ms so we kick the box out far enough.
187 if (last_stuck_time_ + chrono::milliseconds(80) < monotonic_now) {
188 rotation_state_ = RotationState::NOT_ROTATING;
189 last_unstuck_time_ = monotonic_now;
190 }
191 } break;
Austin Schuh17dd0892018-03-02 20:06:31 -0800192 case RotationState::ROTATING_LEFT:
Sabina Daviscfb872f2018-02-25 16:28:20 -0800193 if (position->right_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800194 rotation_count_ = kReverseTime;
195 } else {
196 --rotation_count_;
197 }
198 if (rotation_count_ == 0) {
199 rotation_state_ = RotationState::NOT_ROTATING;
200 }
201 break;
202 case RotationState::ROTATING_RIGHT:
Sabina Daviscfb872f2018-02-25 16:28:20 -0800203 if (position->left_intake.beam_break) {
Austin Schuh17dd0892018-03-02 20:06:31 -0800204 rotation_count_ = kReverseTime;
205 } else {
206 --rotation_count_;
207 }
208 if (rotation_count_ == 0) {
209 rotation_state_ = RotationState::NOT_ROTATING;
210 }
211 break;
212 }
213
Neil Balchba9cbba2018-04-06 22:26:38 -0700214 constexpr double kHoldVoltage = 1.0;
215 constexpr double kStuckVoltage = 10.0;
216
217 if (position->box_back_beambreak_triggered &&
218 roller_voltage > kHoldVoltage) {
219 roller_voltage = kHoldVoltage;
Austin Schuh96341532018-03-09 21:17:24 -0800220 }
Austin Schuh17dd0892018-03-02 20:06:31 -0800221 switch (rotation_state_) {
Neil Balchba9cbba2018-04-06 22:26:38 -0700222 case RotationState::NOT_ROTATING: {
223 double centering_gain = 13.0;
224 if (stuck_count_ > 1) {
225 if ((stuck_count_ - 1) % 2 == 0) {
226 centering_gain = 0.0;
227 }
228 }
229 output->left_intake.voltage_rollers =
230 roller_voltage - intake_center_error * centering_gain;
231 output->right_intake.voltage_rollers =
232 roller_voltage + intake_center_error * centering_gain;
233 } break;
234 case RotationState::STUCK: {
235 if (roller_voltage > kHoldVoltage) {
236 output->left_intake.voltage_rollers = -kStuckVoltage;
237 output->right_intake.voltage_rollers = -kStuckVoltage;
238 }
239 } break;
Austin Schuh17dd0892018-03-02 20:06:31 -0800240 case RotationState::ROTATING_LEFT:
Neil Balchba9cbba2018-04-06 22:26:38 -0700241 if (position->left_intake.beam_break) {
242 output->left_intake.voltage_rollers = -roller_voltage * 0.9;
243 } else {
244 output->left_intake.voltage_rollers = -roller_voltage * 0.6;
245 }
246 output->right_intake.voltage_rollers = roller_voltage;
Austin Schuh17dd0892018-03-02 20:06:31 -0800247 break;
248 case RotationState::ROTATING_RIGHT:
Neil Balchba9cbba2018-04-06 22:26:38 -0700249 output->left_intake.voltage_rollers = roller_voltage;
250 if (position->right_intake.beam_break) {
251 output->right_intake.voltage_rollers = -roller_voltage * 0.9;
252 } else {
253 output->right_intake.voltage_rollers = -roller_voltage * 0.6;
254 }
Austin Schuh17dd0892018-03-02 20:06:31 -0800255 break;
256 }
257 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700258 } else {
259 rotation_state_ = RotationState::NOT_ROTATING;
260 rotation_count_ = 0;
261 stuck_count_ = 0;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800262 }
Neil Balchba9cbba2018-04-06 22:26:38 -0700263 status->rotation_state = static_cast<uint32_t>(rotation_state_);
Austin Schuh8d5fff42018-05-30 20:44:12 -0700264
Austin Schuhbd0a40f2019-06-30 14:56:31 -0700265 drivetrain_output_fetcher_.Fetch();
Austin Schuh8d5fff42018-05-30 20:44:12 -0700266
Austin Schuh300f2f62019-05-27 13:49:23 -0700267 vision_status_fetcher_.Fetch();
Austin Schuh8d5fff42018-05-30 20:44:12 -0700268 if (status->estopped) {
269 SendColors(0.5, 0.0, 0.0);
Austin Schuh300f2f62019-05-27 13:49:23 -0700270 } else if (!vision_status_fetcher_.get() ||
271 monotonic_now >
272 vision_status_fetcher_->sent_time + chrono::seconds(1)) {
Austin Schuh8d5fff42018-05-30 20:44:12 -0700273 SendColors(0.5, 0.5, 0.0);
274 } else if (rotation_state_ == RotationState::ROTATING_LEFT ||
275 rotation_state_ == RotationState::ROTATING_RIGHT) {
276 SendColors(0.5, 0.20, 0.0);
277 } else if (rotation_state_ == RotationState::STUCK) {
278 SendColors(0.5, 0.0, 0.5);
279 } else if (position->box_back_beambreak_triggered) {
280 SendColors(0.0, 0.0, 0.5);
281 } else if (position->box_distance < 0.2) {
282 SendColors(0.0, 0.5, 0.0);
Austin Schuhbd0a40f2019-06-30 14:56:31 -0700283 } else if (drivetrain_output_fetcher_.get() &&
284 ::std::max(::std::abs(drivetrain_output_fetcher_->left_voltage),
285 ::std::abs(drivetrain_output_fetcher_->right_voltage)) >
286 11.5) {
Austin Schuh8d5fff42018-05-30 20:44:12 -0700287 SendColors(0.5, 0.0, 0.5);
288 } else {
289 SendColors(0.0, 0.0, 0.0);
290 }
291
Neil Balchba9cbba2018-04-06 22:26:38 -0700292 last_box_distance_ = clipped_box_distance;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800293}
294
Austin Schuh01a9f2a2019-05-27 13:36:30 -0700295void Superstructure::SendColors(float red, float green, float blue) {
296 auto new_status_light = status_light_sender_.MakeMessage();
297 new_status_light->red = red;
298 new_status_light->green = green;
299 new_status_light->blue = blue;
300
301 if (!new_status_light.Send()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700302 AOS_LOG(ERROR, "Failed to send lights.\n");
Austin Schuh01a9f2a2019-05-27 13:36:30 -0700303 }
304}
305
Sabina Davis8d20ca82018-02-19 13:17:45 -0800306} // namespace superstructure
307} // namespace control_loops
308} // namespace y2018