blob: 987bb5e80f27cc341bd8b3a3294f4afbae64fdb2 [file] [log] [blame]
Brian Silvermanb691f5e2015-08-02 11:37:55 -07001#include "y2015/control_loops/claw/claw.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08002
Daniel Petti9cf68c82015-02-14 14:57:17 -08003#include <algorithm>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08004#include <chrono>
Daniel Petti9cf68c82015-02-14 14:57:17 -08005
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08006#include "aos/common/controls/control_loops.q.h"
7#include "aos/common/logging/logging.h"
Austin Schuhf2a50ba2016-12-24 16:16:26 -08008#include "aos/common/time.h"
9#include "aos/common/util/trapezoid_profile.h"
Brian Silvermanb691f5e2015-08-02 11:37:55 -070010#include "y2015/constants.h"
11#include "y2015/control_loops/claw/claw_motor_plant.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080012
Austin Schuh88af0852016-12-04 20:31:32 -080013namespace y2015 {
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080014namespace control_loops {
15
Austin Schuhf2a50ba2016-12-24 16:16:26 -080016using ::aos::monotonic_clock;
Austin Schuh214e9c12016-11-25 17:26:20 -080017namespace chrono = ::std::chrono;
Daniel Petti9cf68c82015-02-14 14:57:17 -080018
Austin Schuhc3b48df2015-02-22 21:31:37 -080019constexpr double kZeroingVoltage = 4.0;
Daniel Petti9cf68c82015-02-14 14:57:17 -080020
21void ClawCappedStateFeedbackLoop::CapU() {
22 mutable_U(0, 0) = ::std::min(mutable_U(0, 0), max_voltage_);
23 mutable_U(0, 0) = ::std::max(mutable_U(0, 0), -max_voltage_);
24}
25
26double ClawCappedStateFeedbackLoop::UnsaturateOutputGoalChange() {
27 // Compute K matrix to compensate for position errors.
28 double Kp = K(0, 0);
29
30 // Compute how much we need to change R in order to achieve the change in U
31 // that was observed.
32 return -(1.0 / Kp) * (U_uncapped() - U())(0, 0);
33}
34
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080035Claw::Claw(control_loops::ClawQueue *claw)
36 : aos::controls::ControlLoop<control_loops::ClawQueue>(claw),
Austin Schuhf2a50ba2016-12-24 16:16:26 -080037 last_piston_edge_(monotonic_clock::min_time),
Adam Snaidere8e09ba2016-03-05 14:40:52 -080038 claw_loop_(new ClawCappedStateFeedbackLoop(
39 ::y2015::control_loops::claw::MakeClawLoop())),
Austin Schuhbd5308a2015-02-28 21:48:44 -080040 claw_estimator_(constants::GetValues().claw.zeroing),
41 profile_(::aos::controls::kLoopFrequency) {}
Daniel Petti9cf68c82015-02-14 14:57:17 -080042
43void Claw::UpdateZeroingState() {
44 if (claw_estimator_.offset_ratio_ready() < 1.0) {
45 state_ = INITIALIZING;
46 } else if (!claw_estimator_.zeroed()) {
47 state_ = ZEROING;
48 } else {
49 state_ = RUNNING;
50 }
51}
52
53void Claw::Correct() {
54 Eigen::Matrix<double, 1, 1> Y;
55 Y << claw_position();
56 claw_loop_->Correct(Y);
57}
58
59void Claw::SetClawOffset(double offset) {
Adam Snaider3cd11c52015-02-16 02:16:09 +000060 LOG(INFO, "Changing claw offset from %f to %f.\n", claw_offset_, offset);
Daniel Petti9cf68c82015-02-14 14:57:17 -080061 const double doffset = offset - claw_offset_;
62
63 // Adjust the height. The derivative should not need to be updated since the
64 // speed is not changing.
65 claw_loop_->mutable_X_hat(0, 0) += doffset;
66
67 // Modify claw zeroing goal.
68 claw_goal_ += doffset;
69 // Update the cached offset value to the actual value.
70 claw_offset_ = offset;
71}
72
73double Claw::estimated_claw_position() const {
74 return current_position_.joint.encoder + claw_estimator_.offset();
75}
76
77double Claw::claw_position() const {
78 return current_position_.joint.encoder + claw_offset_;
79}
80
81constexpr double kClawZeroingVelocity = 0.2;
82
83double Claw::claw_zeroing_velocity() {
84 const auto &values = constants::GetValues();
85
86 // Zeroing will work as following. At startup, record the offset of the claw.
87 // Then, start moving the claw towards where the index pulse should be. We
88 // search around it a little, and if we don't find anything, we estop.
89 // Otherwise, we're done.
90
91 const double target_pos = values.claw.zeroing.measured_index_position;
92 // How far away we need to stay from the ends of the range while zeroing.
93 constexpr double zeroing_limit = 0.1375;
94 // Keep the zeroing range within the bounds of the mechanism.
95 const double zeroing_range =
96 ::std::min(target_pos - values.claw.wrist.lower_limit - zeroing_limit,
97 values.claw.zeroing_range);
98
99 if (claw_zeroing_velocity_ == 0) {
100 if (estimated_claw_position() > target_pos) {
101 claw_zeroing_velocity_ = -kClawZeroingVelocity;
102 } else {
103 claw_zeroing_velocity_ = kClawZeroingVelocity;
104 }
105 } else if (claw_zeroing_velocity_ > 0 &&
106 estimated_claw_position() > target_pos + zeroing_range) {
107 claw_zeroing_velocity_ = -kClawZeroingVelocity;
108 } else if (claw_zeroing_velocity_ < 0 &&
109 estimated_claw_position() < target_pos - zeroing_range) {
110 claw_zeroing_velocity_ = kClawZeroingVelocity;
111 }
112
113 return claw_zeroing_velocity_;
114}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800115
Adam Snaider3cd11c52015-02-16 02:16:09 +0000116void Claw::RunIteration(const control_loops::ClawQueue::Goal *unsafe_goal,
117 const control_loops::ClawQueue::Position *position,
118 control_loops::ClawQueue::Output *output,
119 control_loops::ClawQueue::Status *status) {
Daniel Petti9cf68c82015-02-14 14:57:17 -0800120 const auto &values = constants::GetValues();
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800121
Daniel Petti9cf68c82015-02-14 14:57:17 -0800122 if (WasReset()) {
123 LOG(ERROR, "WPILib reset! Restarting.\n");
124 claw_estimator_.Reset();
125 state_ = UNINITIALIZED;
126 }
127
128 current_position_ = *position;
129
130 // Bool to track if we should turn the motor on or not.
131 bool disable = output == nullptr;
132 double claw_goal_velocity = 0.0;
133
134 claw_estimator_.UpdateEstimate(position->joint);
135
136 if (state_ != UNINITIALIZED) {
137 Correct();
138 }
139
140 switch (state_) {
141 case UNINITIALIZED:
142 LOG(INFO, "Uninitialized.\n");
143 // Startup. Assume that we are at the origin.
144 claw_offset_ = -position->joint.encoder;
145 claw_loop_->mutable_X_hat().setZero();
146 Correct();
147 state_ = INITIALIZING;
148 disable = true;
149 break;
150
151 case INITIALIZING:
152 LOG(INFO, "Waiting for accurate initial position.\n");
153 disable = true;
154 // Update state_ to accurately represent the state of the zeroing
155 // estimator.
156 UpdateZeroingState();
157
158 if (state_ != INITIALIZING) {
159 // Set the goals to where we are now.
160 claw_goal_ = claw_position();
161 }
162 break;
163
164 case ZEROING:
Brian Silverman5fe6c432015-03-21 11:39:53 -0700165 LOG(DEBUG, "Zeroing.\n");
Daniel Petti9cf68c82015-02-14 14:57:17 -0800166
167 // Update state_.
168 UpdateZeroingState();
169 if (claw_estimator_.zeroed()) {
170 LOG(INFO, "Zeroed!\n");
171 SetClawOffset(claw_estimator_.offset());
172 } else if (!disable) {
173 claw_goal_velocity = claw_zeroing_velocity();
Austin Schuh214e9c12016-11-25 17:26:20 -0800174 claw_goal_ += claw_goal_velocity *
175 chrono::duration_cast<chrono::duration<double>>(
176 ::aos::controls::kLoopFrequency).count();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800177 }
Austin Schuhbd5308a2015-02-28 21:48:44 -0800178
179 // Clear the current profile state if we are zeroing.
180 {
181 Eigen::Matrix<double, 2, 1> current;
182 current.setZero();
183 current << claw_goal_, claw_goal_velocity;
184 profile_.MoveCurrentState(current);
185 }
Daniel Petti9cf68c82015-02-14 14:57:17 -0800186 break;
187
188 case RUNNING:
189 LOG(DEBUG, "Running!\n");
190
191 // Update state_.
192 UpdateZeroingState();
193 if (unsafe_goal) {
Austin Schuhbd5308a2015-02-28 21:48:44 -0800194 // Pick a set of sane defaults if none are specified.
195 if (unsafe_goal->max_velocity != 0.0) {
196 profile_.set_maximum_velocity(unsafe_goal->max_velocity);
197 } else {
198 profile_.set_maximum_velocity(2.5);
199 }
200 if (unsafe_goal->max_acceleration != 0.0) {
201 profile_.set_maximum_acceleration(unsafe_goal->max_acceleration);
202 } else {
203 profile_.set_maximum_acceleration(4.0);
204 }
205
206 const double unfiltered_goal = ::std::max(
207 ::std::min(unsafe_goal->angle, values.claw.wrist.upper_limit),
208 values.claw.wrist.lower_limit);
209 ::Eigen::Matrix<double, 2, 1> goal_state =
210 profile_.Update(unfiltered_goal, unsafe_goal->angular_velocity);
211 claw_goal_ = goal_state(0, 0);
212 claw_goal_velocity = goal_state(1, 0);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800213 }
214
215 if (state_ != RUNNING && state_ != ESTOP) {
216 state_ = UNINITIALIZED;
217 }
218 break;
219
220 case ESTOP:
221 LOG(ERROR, "Estop!\n");
222 disable = true;
223 break;
224 }
225
226 // Make sure goal and position do not exceed the hardware limits if we are
227 // RUNNING.
228 if (state_ == RUNNING) {
229 // Limit goal.
230 claw_goal_ = ::std::min(claw_goal_, values.claw.wrist.upper_limit);
231 claw_goal_ = ::std::max(claw_goal_, values.claw.wrist.lower_limit);
232
233 // Check position.
234 if (claw_position() >= values.claw.wrist.upper_hard_limit ||
235 claw_position() <= values.claw.wrist.lower_hard_limit) {
236 LOG(ERROR, "Claw at %f out of bounds [%f, %f].\n", claw_position(),
237 values.claw.wrist.lower_limit, values.claw.wrist.upper_limit);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800238 }
239 }
240
241 // Set the goals.
242 claw_loop_->mutable_R() << claw_goal_, claw_goal_velocity;
243
244 const double max_voltage = (state_ == RUNNING) ? 12.0 : kZeroingVoltage;
245 claw_loop_->set_max_voltage(max_voltage);
246
247 if (state_ == ESTOP) {
248 disable = true;
249 }
250 claw_loop_->Update(disable);
251
252 if (state_ == INITIALIZING || state_ == ZEROING) {
253 if (claw_loop_->U() != claw_loop_->U_uncapped()) {
254 double deltaR = claw_loop_->UnsaturateOutputGoalChange();
255
256 // Move the claw goal by the amount observed.
Adam Snaider3cd11c52015-02-16 02:16:09 +0000257 LOG(WARNING, "Moving claw goal by %f to handle saturation.\n", deltaR);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800258 claw_goal_ += deltaR;
259 }
260 }
261
262 if (output) {
263 output->voltage = claw_loop_->U(0, 0);
Austin Schuh813b9af2015-03-08 18:46:58 -0700264 if (state_ != RUNNING) {
Daniel Petti9cf68c82015-02-14 14:57:17 -0800265 output->intake_voltage = 0.0;
266 output->rollers_closed = false;
Austin Schuh813b9af2015-03-08 18:46:58 -0700267 } else {
268 if (unsafe_goal) {
269 output->intake_voltage = unsafe_goal->intake;
270 output->rollers_closed = unsafe_goal->rollers_closed;
271 } else {
272 output->intake_voltage = 0.0;
273 output->rollers_closed = false;
274 }
Daniel Petti9cf68c82015-02-14 14:57:17 -0800275 }
276 if (output->rollers_closed != last_rollers_closed_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800277 last_piston_edge_ = monotonic_clock::now();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800278 }
279 }
280
281 status->zeroed = state_ == RUNNING;
282 status->estopped = state_ == ESTOP;
283 status->state = state_;
Austin Schuh88af0852016-12-04 20:31:32 -0800284 ::frc971::zeroing::PopulateEstimatorState(claw_estimator_,
285 &status->zeroing_state);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800286
287 status->angle = claw_loop_->X_hat(0, 0);
Austin Schuhbd5308a2015-02-28 21:48:44 -0800288 status->angular_velocity = claw_loop_->X_hat(1, 0);
289
Daniel Petti9cf68c82015-02-14 14:57:17 -0800290 if (output) {
291 status->intake = output->intake_voltage;
292 } else {
293 status->intake = 0;
294 }
Austin Schuhc3b48df2015-02-22 21:31:37 -0800295 status->goal_angle = claw_goal_;
Austin Schuhbd5308a2015-02-28 21:48:44 -0800296 status->goal_velocity = claw_goal_velocity;
Daniel Petti9cf68c82015-02-14 14:57:17 -0800297
298 if (output) {
Brian Silvermanc81a13d2015-12-20 18:25:50 -0500299 status->rollers_open = !output->rollers_closed &&
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800300 (monotonic_clock::now() >=
301 values.claw.piston_switch_time + last_piston_edge_);
302 status->rollers_closed =
303 output->rollers_closed &&
304 (monotonic_clock::now() >=
305 values.claw.piston_switch_time + last_piston_edge_);
Daniel Petti9cf68c82015-02-14 14:57:17 -0800306 } else {
307 status->rollers_open = false;
308 status->rollers_closed = false;
309 }
310
311 if (output) {
312 last_rollers_closed_ = output->rollers_closed;
313 }
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800314}
315
316} // namespace control_loops
Austin Schuh88af0852016-12-04 20:31:32 -0800317} // namespace y2015