blob: 2095d6ce55efa3dc51bad75ff4aa012f4464a1c3 [file] [log] [blame]
Comran Morshed25f81a02016-01-23 13:40:10 +00001#include "y2016/control_loops/superstructure/superstructure.h"
2
3#include "aos/common/controls/control_loops.q.h"
4#include "aos/common/logging/logging.h"
5
Austin Schuh2fc10fa2016-02-08 00:44:34 -08006#include "y2016/control_loops/superstructure/integral_intake_plant.h"
7#include "y2016/control_loops/superstructure/integral_arm_plant.h"
8
9#include "y2016/constants.h"
10
Comran Morshed25f81a02016-01-23 13:40:10 +000011namespace y2016 {
12namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080013namespace superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000014
Austin Schuh2fc10fa2016-02-08 00:44:34 -080015namespace {
16constexpr double kZeroingVoltage = 4.0;
Comran Morshed25f81a02016-01-23 13:40:10 +000017
Austin Schuh2fc10fa2016-02-08 00:44:34 -080018double UseUnlessZero(double target_value, double default_value) {
19 if (target_value != 0.0) {
20 return target_value;
21 } else {
22 return default_value;
23 }
Comran Morshed25f81a02016-01-23 13:40:10 +000024}
25
Austin Schuh2fc10fa2016-02-08 00:44:34 -080026} // namespace
27
28void SimpleCappedStateFeedbackLoop::CapU() {
29 mutable_U(0, 0) = ::std::min(U(0, 0), max_voltage_);
30 mutable_U(0, 0) = ::std::max(U(0, 0), -max_voltage_);
31}
32
33void DoubleCappedStateFeedbackLoop::CapU() {
34 mutable_U(0, 0) = ::std::min(U(0, 0), shoulder_max_voltage_);
35 mutable_U(0, 0) = ::std::max(U(0, 0), -shoulder_max_voltage_);
36 mutable_U(1, 0) = ::std::min(U(1, 0), wrist_max_voltage_);
37 mutable_U(1, 0) = ::std::max(U(1, 0), -wrist_max_voltage_);
38}
39
40// Intake
41Intake::Intake()
42 : loop_(new SimpleCappedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1>(
43 ::y2016::control_loops::superstructure::MakeIntegralIntakeLoop()))),
44 estimator_(constants::GetValues().intake.zeroing),
45 profile_(::aos::controls::kLoopFrequency) {
46 Y_.setZero();
47 unprofiled_goal_.setZero();
48 offset_.setZero();
49 AdjustProfile(0.0, 0.0);
50}
51
52void Intake::UpdateIntakeOffset(double offset) {
53 const double doffset = offset - offset_(0, 0);
54 LOG(INFO, "Adjusting Intake offset from %f to %f\n", offset_(0, 0), offset);
55
56 loop_->mutable_X_hat()(0, 0) += doffset;
57 Y_(0, 0) += doffset;
58 loop_->mutable_R(0, 0) += doffset;
59
60 profile_.MoveGoal(doffset);
61 offset_(0, 0) = offset;
62
63 CapGoal("R", &loop_->mutable_R());
64}
65
66
67void Intake::Correct(PotAndIndexPosition position) {
68 estimator_.UpdateEstimate(position);
69
70 if (!initialized_) {
71 if (estimator_.offset_ready()) {
72 UpdateIntakeOffset(estimator_.offset());
73 initialized_ = true;
74 }
75 }
76
77 Y_ << position.encoder;
78 Y_ += offset_;
79 loop_->Correct(Y_);
80}
81
82void Intake::CapGoal(const char *name, Eigen::Matrix<double, 3, 1> *goal) {
83 const auto &values = constants::GetValues();
84
85 // Limit the goal to min/max allowable angles.
86 if ((*goal)(0, 0) >= values.intake.limits.upper) {
87 LOG(WARNING, "Intake goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
88 values.intake.limits.upper);
89 (*goal)(0, 0) = values.intake.limits.upper;
90 }
91 if ((*goal)(0, 0) <= values.intake.limits.lower) {
92 LOG(WARNING, "Intake goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
93 values.intake.limits.lower);
94 (*goal)(0, 0) = values.intake.limits.lower;
95 }
96}
97
98void Intake::ForceGoal(double goal) {
99 set_unprofiled_goal(goal);
100 loop_->mutable_R() = unprofiled_goal_;
Austin Schuh39fd6102016-02-13 22:59:48 -0800101 loop_->mutable_next_R() = loop_->R();
102
103 profile_.MoveCurrentState(loop_->R().block<2, 1>(0, 0));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800104}
105
106void Intake::set_unprofiled_goal(double unprofiled_goal) {
107 unprofiled_goal_(0, 0) = unprofiled_goal;
108 unprofiled_goal_(1, 0) = 0.0;
109 unprofiled_goal_(2, 0) = 0.0;
110 CapGoal("unprofiled R", &unprofiled_goal_);
111}
112
113void Intake::Update(bool disable) {
114 if (!disable) {
115 ::Eigen::Matrix<double, 2, 1> goal_state =
116 profile_.Update(unprofiled_goal_(0, 0), unprofiled_goal_(1, 0));
117
118 loop_->mutable_next_R(0, 0) = goal_state(0, 0);
119 loop_->mutable_next_R(1, 0) = goal_state(1, 0);
120 loop_->mutable_next_R(2, 0) = 0.0;
121 CapGoal("next R", &loop_->mutable_next_R());
122 }
123
124 loop_->Update(disable);
125
126 if (!disable && loop_->U(0, 0) != loop_->U_uncapped(0, 0)) {
Austin Schuh39fd6102016-02-13 22:59:48 -0800127 profile_.MoveCurrentState(loop_->R().block<2, 1>(0, 0));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800128 }
129}
130
131bool Intake::CheckHardLimits() {
132 const auto &values = constants::GetValues();
133 // Returns whether hard limits have been exceeded.
134
135 if (angle() >= values.intake.limits.upper_hard ||
136 angle() <= values.intake.limits.lower_hard) {
137 LOG(ERROR, "Intake at %f out of bounds [%f, %f], ESTOPing\n", angle(),
138 values.intake.limits.lower_hard, values.intake.limits.upper_hard);
139 return true;
140 }
141
142 return false;
143}
144
145void Intake::set_max_voltage(double voltage) { loop_->set_max_voltage(voltage); }
146
147void Intake::AdjustProfile(double max_angular_velocity,
148 double max_angular_acceleration) {
149 profile_.set_maximum_velocity(UseUnlessZero(max_angular_velocity, 10.0));
150 profile_.set_maximum_acceleration(UseUnlessZero(max_angular_acceleration, 10.0));
151}
152
153void Intake::Reset() {
154 estimator_.Reset();
155 initialized_ = false;
156 zeroed_ = false;
157}
158
159EstimatorState Intake::IntakeEstimatorState() {
160 EstimatorState estimator_state;
161 ::frc971::zeroing::PopulateEstimatorState(estimator_, &estimator_state);
162
163 return estimator_state;
164}
165
166Arm::Arm()
167 : loop_(new DoubleCappedStateFeedbackLoop(
168 ::y2016::control_loops::superstructure::MakeIntegralArmLoop())),
169 shoulder_profile_(::aos::controls::kLoopFrequency),
170 wrist_profile_(::aos::controls::kLoopFrequency),
171 shoulder_estimator_(constants::GetValues().shoulder.zeroing),
172 wrist_estimator_(constants::GetValues().wrist.zeroing) {
173 Y_.setZero();
174 offset_.setZero();
175 unprofiled_goal_.setZero();
176 AdjustProfile(0.0, 0.0, 0.0, 0.0);
177}
178
179void Arm::UpdateWristOffset(double offset) {
180 const double doffset = offset - offset_(1, 0);
181 LOG(INFO, "Adjusting Wrist offset from %f to %f\n", offset_(1, 0), offset);
182
183 loop_->mutable_X_hat()(2, 0) += doffset;
184 Y_(1, 0) += doffset;
185 loop_->mutable_R(2, 0) += doffset;
Austin Schuh39fd6102016-02-13 22:59:48 -0800186 loop_->mutable_next_R(2, 0) += doffset;
187 unprofiled_goal_(2, 0) += doffset;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800188
189 wrist_profile_.MoveGoal(doffset);
190 offset_(1, 0) = offset;
191
192 CapGoal("R", &loop_->mutable_R());
Austin Schuh39fd6102016-02-13 22:59:48 -0800193 CapGoal("unprofiled R", &loop_->mutable_next_R());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800194}
195
196void Arm::UpdateShoulderOffset(double offset) {
197 const double doffset = offset - offset_(0, 0);
198 LOG(INFO, "Adjusting Shoulder offset from %f to %f\n", offset_(0, 0),
199 offset);
200
201 loop_->mutable_X_hat()(0, 0) += doffset;
202 loop_->mutable_X_hat()(2, 0) += doffset;
203 Y_(0, 0) += doffset;
204 loop_->mutable_R(0, 0) += doffset;
205 loop_->mutable_R(2, 0) += doffset;
Austin Schuh39fd6102016-02-13 22:59:48 -0800206 loop_->mutable_next_R(0, 0) += doffset;
207 loop_->mutable_next_R(2, 0) += doffset;
208 unprofiled_goal_(0, 0) += doffset;
209 unprofiled_goal_(2, 0) += doffset;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800210
211 shoulder_profile_.MoveGoal(doffset);
Austin Schuh39fd6102016-02-13 22:59:48 -0800212 wrist_profile_.MoveGoal(doffset);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800213 offset_(0, 0) = offset;
214
215 CapGoal("R", &loop_->mutable_R());
Austin Schuh39fd6102016-02-13 22:59:48 -0800216 CapGoal("unprofiled R", &loop_->mutable_next_R());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800217}
218
219// TODO(austin): Handle zeroing errors.
220
221void Arm::Correct(PotAndIndexPosition position_shoulder,
222 PotAndIndexPosition position_wrist) {
223 shoulder_estimator_.UpdateEstimate(position_shoulder);
224 wrist_estimator_.UpdateEstimate(position_wrist);
225
226 if (!initialized_) {
227 if (shoulder_estimator_.offset_ready() && wrist_estimator_.offset_ready()) {
228 UpdateShoulderOffset(shoulder_estimator_.offset());
229 UpdateWristOffset(wrist_estimator_.offset());
230 initialized_ = true;
231 }
232 }
233
234 if (!shoulder_zeroed_ && shoulder_estimator_.zeroed()) {
235 UpdateShoulderOffset(shoulder_estimator_.offset());
236 shoulder_zeroed_ = true;
237 }
238 if (!wrist_zeroed_ && wrist_estimator_.zeroed()) {
239 UpdateWristOffset(wrist_estimator_.offset());
240 wrist_zeroed_ = true;
241 }
242
243 {
244 Y_ << position_shoulder.encoder, position_wrist.encoder;
245 Y_ += offset_;
246 loop_->Correct(Y_);
247 }
248}
249
250void Arm::CapGoal(const char *name, Eigen::Matrix<double, 6, 1> *goal) {
251 // Limit the goals to min/max allowable angles.
252 const auto &values = constants::GetValues();
253
254 if ((*goal)(0, 0) >= values.shoulder.limits.upper) {
255 LOG(WARNING, "Shoulder goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
256 values.shoulder.limits.upper);
257 (*goal)(0, 0) = values.shoulder.limits.upper;
258 }
259 if ((*goal)(0, 0) <= values.shoulder.limits.lower) {
260 LOG(WARNING, "Shoulder goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
261 values.shoulder.limits.lower);
262 (*goal)(0, 0) = values.shoulder.limits.lower;
263 }
264
265 const double wrist_goal_angle_ungrounded = (*goal)(2, 0) - (*goal)(0, 0);
266
267 if (wrist_goal_angle_ungrounded >= values.wrist.limits.upper) {
268 LOG(WARNING, "Wrist goal %s above limit, %f > %f\n", name,
269 wrist_goal_angle_ungrounded, values.wrist.limits.upper);
270 (*goal)(2, 0) = values.wrist.limits.upper + (*goal)(0, 0);
271 }
272 if (wrist_goal_angle_ungrounded <= values.wrist.limits.lower) {
273 LOG(WARNING, "Wrist goal %s below limit, %f < %f\n", name,
274 wrist_goal_angle_ungrounded, values.wrist.limits.lower);
275 (*goal)(2, 0) = values.wrist.limits.lower + (*goal)(0, 0);
276 }
277}
278
279void Arm::ForceGoal(double goal_shoulder, double goal_wrist) {
280 set_unprofiled_goal(goal_shoulder, goal_wrist);
281 loop_->mutable_R() = unprofiled_goal_;
Austin Schuh39fd6102016-02-13 22:59:48 -0800282 loop_->mutable_next_R() = loop_->R();
283
284 shoulder_profile_.MoveCurrentState(loop_->R().block<2, 1>(0, 0));
285 wrist_profile_.MoveCurrentState(loop_->R().block<2, 1>(2, 0));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800286}
287
288void Arm::set_unprofiled_goal(double unprofiled_goal_shoulder,
289 double unprofiled_goal_wrist) {
290 unprofiled_goal_ << unprofiled_goal_shoulder, 0.0, unprofiled_goal_wrist, 0.0,
291 0.0, 0.0;
292 CapGoal("unprofiled R", &unprofiled_goal_);
293}
294
295void Arm::AdjustProfile(double max_angular_velocity_shoulder,
296 double max_angular_acceleration_shoulder,
297 double max_angular_velocity_wrist,
298 double max_angular_acceleration_wrist) {
299 shoulder_profile_.set_maximum_velocity(
300 UseUnlessZero(max_angular_velocity_shoulder, 10.0));
301 shoulder_profile_.set_maximum_acceleration(
302 UseUnlessZero(max_angular_acceleration_shoulder, 10.0));
303 wrist_profile_.set_maximum_velocity(
304 UseUnlessZero(max_angular_velocity_wrist, 10.0));
305 wrist_profile_.set_maximum_acceleration(
306 UseUnlessZero(max_angular_acceleration_wrist, 10.0));
307}
308
309bool Arm::CheckHardLimits() {
310 const auto &values = constants::GetValues();
311 if (shoulder_angle() >= values.shoulder.limits.upper_hard ||
312 shoulder_angle() <= values.shoulder.limits.lower_hard) {
313 LOG(ERROR, "Shoulder at %f out of bounds [%f, %f], ESTOPing\n",
314 shoulder_angle(), values.shoulder.limits.lower_hard,
315 values.shoulder.limits.upper_hard);
316 return true;
317 }
318
319 if (wrist_angle() - shoulder_angle() >= values.wrist.limits.upper_hard ||
320 wrist_angle() - shoulder_angle() <= values.wrist.limits.lower_hard) {
321 LOG(ERROR, "Wrist at %f out of bounds [%f, %f], ESTOPing\n",
322 wrist_angle() - shoulder_angle(), values.wrist.limits.lower_hard,
323 values.wrist.limits.upper_hard);
324 return true;
325 }
326
327 return false;
328}
329
330void Arm::Update(bool disable) {
331 if (!disable) {
332 // Compute next goal.
333 ::Eigen::Matrix<double, 2, 1> goal_state_shoulder =
334 shoulder_profile_.Update(unprofiled_goal_(0, 0),
335 unprofiled_goal_(1, 0));
336 loop_->mutable_next_R(0, 0) = goal_state_shoulder(0, 0);
337 loop_->mutable_next_R(1, 0) = goal_state_shoulder(1, 0);
338
339 ::Eigen::Matrix<double, 2, 1> goal_state_wrist =
340 wrist_profile_.Update(unprofiled_goal_(2, 0), unprofiled_goal_(3, 0));
341 loop_->mutable_next_R(2, 0) = goal_state_wrist(0, 0);
342 loop_->mutable_next_R(3, 0) = goal_state_wrist(1, 0);
343
344 loop_->mutable_next_R(4, 0) = unprofiled_goal_(4, 0);
345 loop_->mutable_next_R(5, 0) = unprofiled_goal_(5, 0);
346 CapGoal("next R", &loop_->mutable_next_R());
347 }
348
349 // Move loop
350 loop_->Update(disable);
351
352 // Shoulder saturated
353 if (!disable && loop_->U(0, 0) != loop_->U_uncapped(0, 0)) {
Austin Schuh39fd6102016-02-13 22:59:48 -0800354 shoulder_profile_.MoveCurrentState(loop_->R().block<2, 1>(0, 0));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800355 }
356
357 // Wrist saturated
358 if (!disable && loop_->U(1, 0) != loop_->U_uncapped(1, 0)) {
Austin Schuh39fd6102016-02-13 22:59:48 -0800359 wrist_profile_.MoveCurrentState(loop_->R().block<2, 1>(2, 0));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800360 }
361}
362
363void Arm::set_max_voltage(double shoulder_max_voltage,
364 double wrist_max_voltage) {
365 loop_->set_max_voltage(shoulder_max_voltage, wrist_max_voltage);
366}
367
368void Arm::Reset() {
369 shoulder_estimator_.Reset();
370 wrist_estimator_.Reset();
371 initialized_ = false;
372 shoulder_zeroed_ = false;
373 wrist_zeroed_ = false;
374}
375
376EstimatorState Arm::ShoulderEstimatorState() {
377 EstimatorState estimator_state;
378 ::frc971::zeroing::PopulateEstimatorState(shoulder_estimator_,
379 &estimator_state);
380
381 return estimator_state;
382}
383
384EstimatorState Arm::WristEstimatorState() {
385 EstimatorState estimator_state;
386 ::frc971::zeroing::PopulateEstimatorState(wrist_estimator_, &estimator_state);
387
388 return estimator_state;
389}
390
391// ///// Superstructure /////
392Superstructure::Superstructure(
393 control_loops::SuperstructureQueue *superstructure_queue)
394 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
395 superstructure_queue) {}
396
397void Superstructure::UpdateZeroingState() {
398 // TODO(austin): Explicit state transitions instead of this.
399 // TODO(adam): Change this once we have zeroing written.
400 if (!arm_.initialized() || !intake_.initialized()) {
401 state_ = INITIALIZING;
402 } else if (!intake_.zeroed()) {
403 state_ = ZEROING_INTAKE;
404 } else if (!arm_.zeroed()) {
405 state_ = ZEROING_ARM;
406 } else {
407 state_ = RUNNING;
408 }
409}
410
411void Superstructure::RunIteration(
412 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
413 const control_loops::SuperstructureQueue::Position *position,
414 control_loops::SuperstructureQueue::Output *output,
415 control_loops::SuperstructureQueue::Status *status) {
416 if (WasReset()) {
417 LOG(ERROR, "WPILib reset, restarting\n");
418 arm_.Reset();
419 intake_.Reset();
420 state_ = UNINITIALIZED;
421 }
422
423 // Bool to track if we should turn the motors on or not.
424 bool disable = output == nullptr;
425
426 arm_.Correct(position->shoulder, position->wrist);
427 intake_.Correct(position->intake);
428
429 // Zeroing will work as follows:
430 // Start with the intake. Move it towards the center. Once zeroed, move it
431 // back to the bottom. Rotate the shoulder towards the center. Once zeroed,
432 // move it up enough to rotate the wrist towards the center.
433
434 // We'll then need code to do sanity checking on values.
435
436 switch (state_) {
437 case UNINITIALIZED:
438 LOG(DEBUG, "Uninitialized\n");
439 state_ = INITIALIZING;
440 disable = true;
441 break;
442
443 case INITIALIZING:
444 LOG(DEBUG, "Waiting for accurate initial position.\n");
445 disable = true;
446 // Update state_ to accurately represent the state of the zeroing
447 // estimators.
448 UpdateZeroingState();
449 if (state_ != INITIALIZING) {
450 // Set the goals to where we are now.
451 intake_.ForceGoal(intake_.angle());
452 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
453 }
454 break;
455
456 case ZEROING_INTAKE:
457 case ZEROING_ARM:
458 // TODO(adam): Add your magic here.
459 state_ = RUNNING;
460 break;
461
462 case RUNNING:
463 if (unsafe_goal) {
464 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
465 unsafe_goal->max_angular_acceleration_shoulder,
466 unsafe_goal->max_angular_velocity_wrist,
467 unsafe_goal->max_angular_acceleration_wrist);
468 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_wrist,
469 unsafe_goal->max_angular_acceleration_intake);
470
471 arm_.set_unprofiled_goal(unsafe_goal->angle_shoulder,
472 unsafe_goal->angle_wrist);
473 intake_.set_unprofiled_goal(unsafe_goal->angle_intake);
474 }
475
476 // Update state_ to accurately represent the state of the zeroing
477 // estimators.
478
479 if (state_ != RUNNING && state_ != ESTOP) {
480 state_ = UNINITIALIZED;
481 }
482 break;
483
484 case ESTOP:
485 LOG(ERROR, "Estop\n");
486 disable = true;
487 break;
488 }
489
490 // ESTOP if we hit any of the limits. It is safe(ish) to hit the limits while
491 // zeroing since we use such low power.
492 if (state_ == RUNNING) {
493 // ESTOP if we hit the hard limits.
494 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
495 state_ = ESTOP;
496 }
497 }
498
499 // Set the voltage limits.
500 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
501 arm_.set_max_voltage(max_voltage, max_voltage);
502 intake_.set_max_voltage(max_voltage);
503
504 // Calculate the loops for a cycle.
505 arm_.Update(disable);
506 intake_.Update(disable);
507
508 // Write out all the voltages.
509 if (output) {
510 output->voltage_intake = intake_.intake_voltage();
511 output->voltage_shoulder = arm_.shoulder_voltage();
512 output->voltage_wrist = arm_.wrist_voltage();
513 }
514
515 // Save debug/internal state.
516 // TODO(austin): Save the voltage errors.
517 status->zeroed = state_ == RUNNING;
518
519 status->shoulder.angle = arm_.X_hat(0, 0);
520 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
521 status->shoulder.goal_angle = arm_.goal(0, 0);
522 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800523 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
524 status->shoulder.unprofiled_goal_angular_velocity =
525 arm_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800526 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
527
528 status->wrist.angle = arm_.X_hat(2, 0);
529 status->wrist.angular_velocity = arm_.X_hat(3, 0);
530 status->wrist.goal_angle = arm_.goal(2, 0);
531 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800532 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
533 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800534 status->wrist.estimator_state = arm_.WristEstimatorState();
535
536 status->intake.angle = intake_.X_hat(0, 0);
537 status->intake.angular_velocity = intake_.X_hat(1, 0);
538 status->intake.goal_angle = intake_.goal(0, 0);
539 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800540 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
541 status->intake.unprofiled_goal_angular_velocity =
542 intake_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800543 status->intake.estimator_state = intake_.IntakeEstimatorState();
544
545 status->estopped = (state_ == ESTOP);
546
547 status->state = state_;
548
549 last_state_ = state_;
550}
551
552} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000553} // namespace control_loops
554} // namespace y2016