blob: 0c12d2795b00a0a91b1a3933b39b15836b8daa68 [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) {
55 LOG(INFO, "Changing claw offset from %f to %f.\n",
56 claw_offset_, offset);
57 const double doffset = offset - claw_offset_;
58
59 // Adjust the height. The derivative should not need to be updated since the
60 // speed is not changing.
61 claw_loop_->mutable_X_hat(0, 0) += doffset;
62
63 // Modify claw zeroing goal.
64 claw_goal_ += doffset;
65 // Update the cached offset value to the actual value.
66 claw_offset_ = offset;
67}
68
69double Claw::estimated_claw_position() const {
70 return current_position_.joint.encoder + claw_estimator_.offset();
71}
72
73double Claw::claw_position() const {
74 return current_position_.joint.encoder + claw_offset_;
75}
76
77constexpr double kClawZeroingVelocity = 0.2;
78
79double Claw::claw_zeroing_velocity() {
80 const auto &values = constants::GetValues();
81
82 // Zeroing will work as following. At startup, record the offset of the claw.
83 // Then, start moving the claw towards where the index pulse should be. We
84 // search around it a little, and if we don't find anything, we estop.
85 // Otherwise, we're done.
86
87 const double target_pos = values.claw.zeroing.measured_index_position;
88 // How far away we need to stay from the ends of the range while zeroing.
89 constexpr double zeroing_limit = 0.1375;
90 // Keep the zeroing range within the bounds of the mechanism.
91 const double zeroing_range =
92 ::std::min(target_pos - values.claw.wrist.lower_limit - zeroing_limit,
93 values.claw.zeroing_range);
94
95 if (claw_zeroing_velocity_ == 0) {
96 if (estimated_claw_position() > target_pos) {
97 claw_zeroing_velocity_ = -kClawZeroingVelocity;
98 } else {
99 claw_zeroing_velocity_ = kClawZeroingVelocity;
100 }
101 } else if (claw_zeroing_velocity_ > 0 &&
102 estimated_claw_position() > target_pos + zeroing_range) {
103 claw_zeroing_velocity_ = -kClawZeroingVelocity;
104 } else if (claw_zeroing_velocity_ < 0 &&
105 estimated_claw_position() < target_pos - zeroing_range) {
106 claw_zeroing_velocity_ = kClawZeroingVelocity;
107 }
108
109 return claw_zeroing_velocity_;
110}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800111
112void Claw::RunIteration(
Daniel Petti9cf68c82015-02-14 14:57:17 -0800113 const control_loops::ClawQueue::Goal *unsafe_goal,
114 const control_loops::ClawQueue::Position *position,
115 control_loops::ClawQueue::Output *output,
116 control_loops::ClawQueue::Status *status) {
117 const auto &values = constants::GetValues();
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800118
Daniel Petti9cf68c82015-02-14 14:57:17 -0800119 if (WasReset()) {
120 LOG(ERROR, "WPILib reset! Restarting.\n");
121 claw_estimator_.Reset();
122 state_ = UNINITIALIZED;
123 }
124
125 current_position_ = *position;
126
127 // Bool to track if we should turn the motor on or not.
128 bool disable = output == nullptr;
129 double claw_goal_velocity = 0.0;
130
131 claw_estimator_.UpdateEstimate(position->joint);
132
133 if (state_ != UNINITIALIZED) {
134 Correct();
135 }
136
137 switch (state_) {
138 case UNINITIALIZED:
139 LOG(INFO, "Uninitialized.\n");
140 // Startup. Assume that we are at the origin.
141 claw_offset_ = -position->joint.encoder;
142 claw_loop_->mutable_X_hat().setZero();
143 Correct();
144 state_ = INITIALIZING;
145 disable = true;
146 break;
147
148 case INITIALIZING:
149 LOG(INFO, "Waiting for accurate initial position.\n");
150 disable = true;
151 // Update state_ to accurately represent the state of the zeroing
152 // estimator.
153 UpdateZeroingState();
154
155 if (state_ != INITIALIZING) {
156 // Set the goals to where we are now.
157 claw_goal_ = claw_position();
158 }
159 break;
160
161 case ZEROING:
162 LOG(INFO, "Zeroing.\n");
163
164 // Update state_.
165 UpdateZeroingState();
166 if (claw_estimator_.zeroed()) {
167 LOG(INFO, "Zeroed!\n");
168 SetClawOffset(claw_estimator_.offset());
169 } else if (!disable) {
170 claw_goal_velocity = claw_zeroing_velocity();
171 claw_goal_ += claw_goal_velocity *
172 ::aos::controls::kLoopFrequency.ToSeconds();
173 }
174 break;
175
176 case RUNNING:
177 LOG(DEBUG, "Running!\n");
178
179 // Update state_.
180 UpdateZeroingState();
181 if (unsafe_goal) {
182 claw_goal_ = unsafe_goal->angle;
183 claw_goal_velocity = unsafe_goal->angular_velocity;
184 }
185
186 if (state_ != RUNNING && state_ != ESTOP) {
187 state_ = UNINITIALIZED;
188 }
189 break;
190
191 case ESTOP:
192 LOG(ERROR, "Estop!\n");
193 disable = true;
194 break;
195 }
196
197 // Make sure goal and position do not exceed the hardware limits if we are
198 // RUNNING.
199 if (state_ == RUNNING) {
200 // Limit goal.
201 claw_goal_ = ::std::min(claw_goal_, values.claw.wrist.upper_limit);
202 claw_goal_ = ::std::max(claw_goal_, values.claw.wrist.lower_limit);
203
204 // Check position.
205 if (claw_position() >= values.claw.wrist.upper_hard_limit ||
206 claw_position() <= values.claw.wrist.lower_hard_limit) {
207 LOG(ERROR, "Claw at %f out of bounds [%f, %f].\n", claw_position(),
208 values.claw.wrist.lower_limit, values.claw.wrist.upper_limit);
209 state_ = ESTOP;
210 }
211 }
212
213 // Set the goals.
214 claw_loop_->mutable_R() << claw_goal_, claw_goal_velocity;
215
216 const double max_voltage = (state_ == RUNNING) ? 12.0 : kZeroingVoltage;
217 claw_loop_->set_max_voltage(max_voltage);
218
219 if (state_ == ESTOP) {
220 disable = true;
221 }
222 claw_loop_->Update(disable);
223
224 if (state_ == INITIALIZING || state_ == ZEROING) {
225 if (claw_loop_->U() != claw_loop_->U_uncapped()) {
226 double deltaR = claw_loop_->UnsaturateOutputGoalChange();
227
228 // Move the claw goal by the amount observed.
229 LOG(WARNING, "Moving claw goal by %f to handle saturation.\n",
230 deltaR);
231 claw_goal_ += deltaR;
232 }
233 }
234
235 if (output) {
236 output->voltage = claw_loop_->U(0, 0);
237 if (unsafe_goal) {
238 output->intake_voltage = unsafe_goal->intake;
239 output->rollers_closed = unsafe_goal->rollers_closed;
240 } else {
241 output->intake_voltage = 0.0;
242 output->rollers_closed = false;
243 }
244 if (output->rollers_closed != last_rollers_closed_) {
245 last_piston_edge_ = Time::Now();
246 }
247 }
248
249 status->zeroed = state_ == RUNNING;
250 status->estopped = state_ == ESTOP;
251 status->state = state_;
Daniel Pettiab274232015-02-16 19:15:34 -0800252 zeroing::PopulateEstimatorState(claw_estimator_, &status->zeroing_state);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800253
254 status->angle = claw_loop_->X_hat(0, 0);
255 if (output) {
256 status->intake = output->intake_voltage;
257 } else {
258 status->intake = 0;
259 }
Austin Schuhc3b48df2015-02-22 21:31:37 -0800260 status->goal_angle = claw_goal_;
Daniel Petti9cf68c82015-02-14 14:57:17 -0800261
262 if (output) {
263 status->rollers_open = !output->rollers_closed &&
264 (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
265 status->rollers_closed = output->rollers_closed &&
266 (Time::Now() - last_piston_edge_ >= values.claw.piston_switch_time);
267 } else {
268 status->rollers_open = false;
269 status->rollers_closed = false;
270 }
271
272 if (output) {
273 last_rollers_closed_ = output->rollers_closed;
274 }
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800275}
276
277} // namespace control_loops
278} // namespace frc971