blob: 6707356ebb66e6916c4e53b6a619f806cb2a16bb [file] [log] [blame]
Sabina Davis8d20ca82018-02-19 13:17:45 -08001#include "y2018/control_loops/superstructure/intake/intake.h"
2
3#include <chrono>
4
John Park33858a32018-09-28 23:05:48 -07005#include "aos/commonmath.h"
6#include "aos/controls/control_loops.q.h"
7#include "aos/logging/logging.h"
8#include "aos/logging/queue_logging.h"
Sabina Davis8d20ca82018-02-19 13:17:45 -08009
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(
Austin Schuh17dd0892018-03-02 20:06:31 -080074 ((goal_angle(unsafe_goal) - loop_->X_hat(0, 0)) * 12.0), -16.0, 16.0);
Sabina Davis8d20ca82018-02-19 13:17:45 -080075 }
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);
Austin Schuh17dd0892018-03-02 20:06:31 -080087 status->spring_position = loop_->X_hat(0) - loop_->X_hat(2);
88 status->spring_velocity = loop_->X_hat(1) - loop_->X_hat(3);
Sabina Davis8d20ca82018-02-19 13:17:45 -080089 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) {
Sabina Davis8d20ca82018-02-19 13:17:45 -0800105 zeroing_estimator_.UpdateEstimate(position->motor_position);
106
107 switch (state_) {
108 case State::UNINITIALIZED:
109 // Wait in the uninitialized state until the intake is initialized.
110 LOG(DEBUG, "Uninitialized, waiting for intake\n");
111 zeroing_estimator_.Reset();
112 controller_.Reset();
113 state_ = State::ZEROING;
114 break;
115
116 case State::ZEROING:
117 // Zero by not moving.
118 if (zeroing_estimator_.zeroed()) {
119 LOG(INFO, "Now zeroed\n");
120 controller_.UpdateOffset(zeroing_estimator_.offset());
121 state_ = State::RUNNING;
122 }
123 break;
124
125 case State::RUNNING:
126 if (!(zeroing_estimator_.zeroed())) {
127 LOG(ERROR, "Zeroing estimator is no longer zeroed\n");
128 state_ = State::UNINITIALIZED;
129 }
130 if (zeroing_estimator_.error()) {
131 LOG(ERROR, "Zeroing estimator error\n");
132 state_ = State::UNINITIALIZED;
133 }
134 // ESTOP if we hit the hard limits.
135 if ((status->motor_position) > controller_.intake_range_.upper ||
136 (status->motor_position) < controller_.intake_range_.lower) {
137 LOG(ERROR, "Hit hard limits\n");
138 state_ = State::ESTOP;
139 }
140 break;
141
142 case State::ESTOP:
143 LOG(ERROR, "Estop\n");
144 break;
145 }
146
147 const bool disable = (output == nullptr) || state_ != State::RUNNING;
148 controller_.set_position(position->spring_angle,
149 position->motor_position.encoder);
150
151 controller_.Update(disable, unsafe_goal);
152
153 if (output) {
154 output->voltage_elastic = controller_.voltage();
155 }
156
157 // Save debug/internal state.
158 status->estimator_state = zeroing_estimator_.GetEstimatorState();
159 controller_.SetStatus(status, unsafe_goal);
160 status->calculated_velocity =
161 (status->estimator_state.position - intake_last_position_) /
162 controller_.kDt;
163 status->zeroed = zeroing_estimator_.zeroed();
164 status->estopped = (state_ == State::ESTOP);
165 status->state = static_cast<int32_t>(state_);
Austin Schuh17dd0892018-03-02 20:06:31 -0800166 intake_last_position_ = status->estimator_state.position;
Sabina Davis8d20ca82018-02-19 13:17:45 -0800167}
168
169} // namespace intake
170} // namespace superstructure
171} // namespace control_loops
172} // namespace y2018