blob: 7212fd3891304624293a71c60154e7c7629c6e08 [file] [log] [blame]
Sabina Davis8d20ca82018-02-19 13:17:45 -08001#include "y2018/control_loops/superstructure/intake/intake.h"
2
3#include <chrono>
4
5#include "aos/common/commonmath.h"
6#include "aos/common/controls/control_loops.q.h"
7#include "aos/common/logging/logging.h"
8#include "aos/common/logging/queue_logging.h"
9
10#include "y2018/constants.h"
11#include "y2018/control_loops/superstructure/intake/intake_delayed_plant.h"
12#include "y2018/control_loops/superstructure/intake/intake_plant.h"
13
14namespace y2018 {
15namespace control_loops {
16namespace superstructure {
17namespace intake {
18
19namespace chrono = ::std::chrono;
20using ::aos::monotonic_clock;
21
22constexpr double IntakeController::kDt;
23
24IntakeController::IntakeController()
25 : loop_(new StateFeedbackLoop<5, 1, 2, double, StateFeedbackPlant<5, 1, 2>,
26 StateFeedbackObserver<5, 1, 2>>(
27 superstructure::intake::MakeDelayedIntakeLoop())),
28 intake_range_(::y2018::constants::Values::kIntakeRange()) {
29 Y_.setZero();
30}
31
32void IntakeController::set_position(double spring_angle,
33 double output_position) {
34 // Update position in the model.
35 Y_ << spring_angle, (output_position + offset_);
36}
37
38double IntakeController::voltage() const { return loop_->U(0, 0); }
39
40void IntakeController::Reset() { reset_ = true; }
41
42void IntakeController::UpdateOffset(double offset) {
43 const double doffset = offset - offset_;
44 offset_ = offset;
45
46 loop_->mutable_X_hat(0) += doffset;
47 loop_->mutable_X_hat(2) += doffset;
48}
49
50double IntakeController::goal_angle(const double *unsafe_goal) {
51 if (unsafe_goal == nullptr) {
52 return 0;
53 } else {
54 return ::aos::Clip(*unsafe_goal, intake_range_.lower, intake_range_.upper);
55 }
56}
57
58void IntakeController::Update(bool disabled, const double *unsafe_goal) {
59 if (reset_) {
60 loop_->mutable_X_hat().setZero();
61 loop_->mutable_X_hat(0) = Y_(0) + Y_(1);
62 loop_->mutable_X_hat(2) = Y_(1);
63 reset_ = false;
64 }
65
66 double goal_velocity;
67 loop_->Correct(Y_);
68
69 if (unsafe_goal == nullptr) {
70 disabled = true;
71 goal_velocity = 0.0;
72 } else {
73 goal_velocity = ::aos::Clip(
74 ((goal_angle(unsafe_goal) - loop_->X_hat(0, 0)) * 6.0), -10.0, 10.0);
75 }
76 // Computes the goal.
77 loop_->mutable_R() << 0.0, goal_velocity, 0.0, goal_velocity,
78 (goal_velocity / (kGearRatio * kMotorVelocityConstant));
79
80 loop_->Update(disabled);
81}
82
83void IntakeController::SetStatus(IntakeSideStatus *status,
84 const double *unsafe_goal) {
85 status->goal_position = goal_angle(unsafe_goal);
86 status->goal_velocity = loop_->R(1, 0);
87 status->spring_position = loop_->X_hat(0);
88 status->spring_velocity = loop_->X_hat(1);
89 status->motor_position = loop_->X_hat(2);
90 status->motor_velocity = loop_->X_hat(3);
91 status->delayed_voltage = loop_->X_hat(4);
92}
93
94IntakeSide::IntakeSide(
95 const ::frc971::constants::PotAndAbsoluteEncoderZeroingConstants
96 &zeroing_constants)
97 : zeroing_estimator_(zeroing_constants) {}
98
99void IntakeSide::Reset() { state_ = State::UNINITIALIZED; }
100
101void IntakeSide::Iterate(const double *unsafe_goal,
102 const control_loops::IntakeElasticSensors *position,
103 control_loops::IntakeVoltage *output,
104 control_loops::IntakeSideStatus *status) {
105 double intake_last_position_ = status->estimator_state.position;
106 zeroing_estimator_.UpdateEstimate(position->motor_position);
107
108 switch (state_) {
109 case State::UNINITIALIZED:
110 // Wait in the uninitialized state until the intake is initialized.
111 LOG(DEBUG, "Uninitialized, waiting for intake\n");
112 zeroing_estimator_.Reset();
113 controller_.Reset();
114 state_ = State::ZEROING;
115 break;
116
117 case State::ZEROING:
118 // Zero by not moving.
119 if (zeroing_estimator_.zeroed()) {
120 LOG(INFO, "Now zeroed\n");
121 controller_.UpdateOffset(zeroing_estimator_.offset());
122 state_ = State::RUNNING;
123 }
124 break;
125
126 case State::RUNNING:
127 if (!(zeroing_estimator_.zeroed())) {
128 LOG(ERROR, "Zeroing estimator is no longer zeroed\n");
129 state_ = State::UNINITIALIZED;
130 }
131 if (zeroing_estimator_.error()) {
132 LOG(ERROR, "Zeroing estimator error\n");
133 state_ = State::UNINITIALIZED;
134 }
135 // ESTOP if we hit the hard limits.
136 if ((status->motor_position) > controller_.intake_range_.upper ||
137 (status->motor_position) < controller_.intake_range_.lower) {
138 LOG(ERROR, "Hit hard limits\n");
139 state_ = State::ESTOP;
140 }
141 break;
142
143 case State::ESTOP:
144 LOG(ERROR, "Estop\n");
145 break;
146 }
147
148 const bool disable = (output == nullptr) || state_ != State::RUNNING;
149 controller_.set_position(position->spring_angle,
150 position->motor_position.encoder);
151
152 controller_.Update(disable, unsafe_goal);
153
154 if (output) {
155 output->voltage_elastic = controller_.voltage();
156 }
157
158 // Save debug/internal state.
159 status->estimator_state = zeroing_estimator_.GetEstimatorState();
160 controller_.SetStatus(status, unsafe_goal);
161 status->calculated_velocity =
162 (status->estimator_state.position - intake_last_position_) /
163 controller_.kDt;
164 status->zeroed = zeroing_estimator_.zeroed();
165 status->estopped = (state_ == State::ESTOP);
166 status->state = static_cast<int32_t>(state_);
167}
168
169} // namespace intake
170} // namespace superstructure
171} // namespace control_loops
172} // namespace y2018