blob: 004ea7a52af8f6bc4aa71e3a1753a297647c627b [file] [log] [blame]
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08001#include "frc971/control_loops/claw/claw.h"
2
Daniel Petti9cf68c82015-02-14 14:57:17 -08003#include <algorithm>
4
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08005#include "aos/common/controls/control_loops.q.h"
6#include "aos/common/logging/logging.h"
7
Daniel Petti9cf68c82015-02-14 14:57:17 -08008#include "frc971/constants.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08009#include "frc971/control_loops/claw/claw_motor_plant.h"
10
11namespace frc971 {
12namespace control_loops {
13
Daniel Petti9cf68c82015-02-14 14:57:17 -080014using ::aos::time::Time;
15
Austin Schuhc3b48df2015-02-22 21:31:37 -080016constexpr double kZeroingVoltage = 4.0;
Daniel Petti9cf68c82015-02-14 14:57:17 -080017
18void ClawCappedStateFeedbackLoop::CapU() {
19 mutable_U(0, 0) = ::std::min(mutable_U(0, 0), max_voltage_);
20 mutable_U(0, 0) = ::std::max(mutable_U(0, 0), -max_voltage_);
21}
22
23double ClawCappedStateFeedbackLoop::UnsaturateOutputGoalChange() {
24 // Compute K matrix to compensate for position errors.
25 double Kp = K(0, 0);
26
27 // Compute how much we need to change R in order to achieve the change in U
28 // that was observed.
29 return -(1.0 / Kp) * (U_uncapped() - U())(0, 0);
30}
31
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080032Claw::Claw(control_loops::ClawQueue *claw)
33 : aos::controls::ControlLoop<control_loops::ClawQueue>(claw),
Daniel Petti9cf68c82015-02-14 14:57:17 -080034 last_piston_edge_(Time::Now()),
35 claw_loop_(new ClawCappedStateFeedbackLoop(MakeClawLoop())),
36 claw_estimator_(constants::GetValues().claw.zeroing) {}
37
38void Claw::UpdateZeroingState() {
39 if (claw_estimator_.offset_ratio_ready() < 1.0) {
40 state_ = INITIALIZING;
41 } else if (!claw_estimator_.zeroed()) {
42 state_ = ZEROING;
43 } else {
44 state_ = RUNNING;
45 }
46}
47
48void Claw::Correct() {
49 Eigen::Matrix<double, 1, 1> Y;
50 Y << claw_position();
51 claw_loop_->Correct(Y);
52}
53
54void Claw::SetClawOffset(double offset) {
Adam Snaider3cd11c52015-02-16 02:16:09 +000055 LOG(INFO, "Changing claw offset from %f to %f.\n", claw_offset_, offset);
Daniel Petti9cf68c82015-02-14 14:57:17 -080056 const double doffset = offset - claw_offset_;
57
58 // Adjust the height. The derivative should not need to be updated since the
59 // speed is not changing.
60 claw_loop_->mutable_X_hat(0, 0) += doffset;
61
62 // Modify claw zeroing goal.
63 claw_goal_ += doffset;
64 // Update the cached offset value to the actual value.
65 claw_offset_ = offset;
66}
67
68double Claw::estimated_claw_position() const {
69 return current_position_.joint.encoder + claw_estimator_.offset();
70}
71
72double Claw::claw_position() const {
73 return current_position_.joint.encoder + claw_offset_;
74}
75
76constexpr double kClawZeroingVelocity = 0.2;
77
78double Claw::claw_zeroing_velocity() {
79 const auto &values = constants::GetValues();
80
81 // Zeroing will work as following. At startup, record the offset of the claw.
82 // Then, start moving the claw towards where the index pulse should be. We
83 // search around it a little, and if we don't find anything, we estop.
84 // Otherwise, we're done.
85
86 const double target_pos = values.claw.zeroing.measured_index_position;
87 // How far away we need to stay from the ends of the range while zeroing.
88 constexpr double zeroing_limit = 0.1375;
89 // Keep the zeroing range within the bounds of the mechanism.
90 const double zeroing_range =
91 ::std::min(target_pos - values.claw.wrist.lower_limit - zeroing_limit,
92 values.claw.zeroing_range);
93
94 if (claw_zeroing_velocity_ == 0) {
95 if (estimated_claw_position() > target_pos) {
96 claw_zeroing_velocity_ = -kClawZeroingVelocity;
97 } else {
98 claw_zeroing_velocity_ = kClawZeroingVelocity;
99 }
100 } else if (claw_zeroing_velocity_ > 0 &&
101 estimated_claw_position() > target_pos + zeroing_range) {
102 claw_zeroing_velocity_ = -kClawZeroingVelocity;
103 } else if (claw_zeroing_velocity_ < 0 &&
104 estimated_claw_position() < target_pos - zeroing_range) {
105 claw_zeroing_velocity_ = kClawZeroingVelocity;
106 }
107
108 return claw_zeroing_velocity_;
109}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800110
Adam Snaider3cd11c52015-02-16 02:16:09 +0000111void Claw::RunIteration(const control_loops::ClawQueue::Goal *unsafe_goal,
112 const control_loops::ClawQueue::Position *position,
113 control_loops::ClawQueue::Output *output,
114 control_loops::ClawQueue::Status *status) {
Daniel Petti9cf68c82015-02-14 14:57:17 -0800115 const auto &values = constants::GetValues();
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800116
Daniel Petti9cf68c82015-02-14 14:57:17 -0800117 if (WasReset()) {
118 LOG(ERROR, "WPILib reset! Restarting.\n");
119 claw_estimator_.Reset();
120 state_ = UNINITIALIZED;
121 }
122
123 current_position_ = *position;
124
125 // Bool to track if we should turn the motor on or not.
126 bool disable = output == nullptr;
127 double claw_goal_velocity = 0.0;
128
129 claw_estimator_.UpdateEstimate(position->joint);
130
131 if (state_ != UNINITIALIZED) {
132 Correct();
133 }
134
135 switch (state_) {
136 case UNINITIALIZED:
137 LOG(INFO, "Uninitialized.\n");
138 // Startup. Assume that we are at the origin.
139 claw_offset_ = -position->joint.encoder;
140 claw_loop_->mutable_X_hat().setZero();
141 Correct();
142 state_ = INITIALIZING;
143 disable = true;
144 break;
145
146 case INITIALIZING:
147 LOG(INFO, "Waiting for accurate initial position.\n");
148 disable = true;
149 // Update state_ to accurately represent the state of the zeroing
150 // estimator.
151 UpdateZeroingState();
152
153 if (state_ != INITIALIZING) {
154 // Set the goals to where we are now.
155 claw_goal_ = claw_position();
156 }
157 break;
158
159 case ZEROING:
160 LOG(INFO, "Zeroing.\n");
161
162 // Update state_.
163 UpdateZeroingState();
164 if (claw_estimator_.zeroed()) {
165 LOG(INFO, "Zeroed!\n");
166 SetClawOffset(claw_estimator_.offset());
167 } else if (!disable) {
168 claw_goal_velocity = claw_zeroing_velocity();
Adam Snaider3cd11c52015-02-16 02:16:09 +0000169 claw_goal_ +=
170 claw_goal_velocity * ::aos::controls::kLoopFrequency.ToSeconds();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800171 }
172 break;
173
174 case RUNNING:
175 LOG(DEBUG, "Running!\n");
176
177 // Update state_.
178 UpdateZeroingState();
179 if (unsafe_goal) {
180 claw_goal_ = unsafe_goal->angle;
181 claw_goal_velocity = unsafe_goal->angular_velocity;
182 }
183
184 if (state_ != RUNNING && state_ != ESTOP) {
185 state_ = UNINITIALIZED;
186 }
187 break;
188
189 case ESTOP:
190 LOG(ERROR, "Estop!\n");
191 disable = true;
192 break;
193 }
194
195 // Make sure goal and position do not exceed the hardware limits if we are
196 // RUNNING.
197 if (state_ == RUNNING) {
198 // Limit goal.
199 claw_goal_ = ::std::min(claw_goal_, values.claw.wrist.upper_limit);
200 claw_goal_ = ::std::max(claw_goal_, values.claw.wrist.lower_limit);
201
202 // Check position.
203 if (claw_position() >= values.claw.wrist.upper_hard_limit ||
204 claw_position() <= values.claw.wrist.lower_hard_limit) {
205 LOG(ERROR, "Claw at %f out of bounds [%f, %f].\n", claw_position(),
206 values.claw.wrist.lower_limit, values.claw.wrist.upper_limit);
207 state_ = ESTOP;
208 }
209 }
210
211 // Set the goals.
212 claw_loop_->mutable_R() << claw_goal_, claw_goal_velocity;
213
214 const double max_voltage = (state_ == RUNNING) ? 12.0 : kZeroingVoltage;
215 claw_loop_->set_max_voltage(max_voltage);
216
217 if (state_ == ESTOP) {
218 disable = true;
219 }
220 claw_loop_->Update(disable);
221
222 if (state_ == INITIALIZING || state_ == ZEROING) {
223 if (claw_loop_->U() != claw_loop_->U_uncapped()) {
224 double deltaR = claw_loop_->UnsaturateOutputGoalChange();
225
226 // Move the claw goal by the amount observed.
Adam Snaider3cd11c52015-02-16 02:16:09 +0000227 LOG(WARNING, "Moving claw goal by %f to handle saturation.\n", deltaR);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800228 claw_goal_ += deltaR;
229 }
230 }
231
232 if (output) {
233 output->voltage = claw_loop_->U(0, 0);
234 if (unsafe_goal) {
235 output->intake_voltage = unsafe_goal->intake;
236 output->rollers_closed = unsafe_goal->rollers_closed;
237 } else {
238 output->intake_voltage = 0.0;
239 output->rollers_closed = false;
240 }
241 if (output->rollers_closed != last_rollers_closed_) {
242 last_piston_edge_ = Time::Now();
243 }
244 }
245
246 status->zeroed = state_ == RUNNING;
247 status->estopped = state_ == ESTOP;
248 status->state = state_;
Daniel Pettiab274232015-02-16 19:15:34 -0800249 zeroing::PopulateEstimatorState(claw_estimator_, &status->zeroing_state);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800250
251 status->angle = claw_loop_->X_hat(0, 0);
252 if (output) {
253 status->intake = output->intake_voltage;
254 } else {
255 status->intake = 0;
256 }
Austin Schuhc3b48df2015-02-22 21:31:37 -0800257 status->goal_angle = claw_goal_;
Daniel Petti9cf68c82015-02-14 14:57:17 -0800258
259 if (output) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000260 status->rollers_open =
261 !output->rollers_closed &&
Daniel Petti9cf68c82015-02-14 14:57:17 -0800262 (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000263 status->rollers_closed =
264 output->rollers_closed &&
Daniel Petti9cf68c82015-02-14 14:57:17 -0800265 (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
266 } else {
267 status->rollers_open = false;
268 status->rollers_closed = false;
269 }
270
271 if (output) {
272 last_rollers_closed_ = output->rollers_closed;
273 }
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800274}
275
276} // namespace control_loops
277} // namespace frc971