blob: e3e7e6d0d81b235f259c9dc62395dedaf9524b80 [file] [log] [blame]
Austin Schuh3bb9a442014-02-02 16:01:45 -08001#include "frc971/control_loops/claw/claw.h"
2
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
9
10#include "frc971/constants.h"
11#include "frc971/control_loops/claw/top_claw_motor_plant.h"
12#include "frc971/control_loops/claw/bottom_claw_motor_plant.h"
13
14// Zeroing plan.
15// There are 2 types of zeros. Enabled and disabled ones.
16// Disabled ones are only valid during auto mode, and can be used to speed up
17// the enabled zero process. We need to re-zero during teleop in case the auto
18// zero was poor and causes us to miss all our shots.
19//
20// We need to be able to zero manually while disabled by moving the joint over
21// the zeros.
22// Zero on the down edge when disabled (gravity in the direction of motion)
23//
24// When enabled, zero on the up edge (gravity opposing the direction of motion)
25// The enabled sequence needs to work as follows. We can crash the claw if we
26// bring them too close to each other or too far from each other. The only safe
27// thing to do is to move them in unison.
28//
29// Start by moving them both towards the front of the bot to either find either
30// the middle hall effect on either jaw, or the front hall effect on the bottom
31// jaw. Any edge that isn't the desired edge will provide an approximate edge
32// location that can be used for the fine tuning step.
33// Once an edge is found on the front claw, move back the other way with both
34// claws until an edge is found for the other claw.
35// Now that we have an approximate zero, we can robustify the limits to keep
36// both claws safe. Then, we can move both claws to a position that is the
37// correct side of the zero and go zero.
38
39// Valid region plan.
40// Difference between the arms has a range, and the values of each arm has a range.
41// If a claw runs up against a static limit, don't let the goal change outside
42// the limit.
43// If a claw runs up against a movable limit, move both claws outwards to get
44// out of the condition.
45
46namespace frc971 {
47namespace control_loops {
48
Austin Schuh4b7b5d02014-02-10 21:20:34 -080049void ZeroedStateFeedbackLoop::CapU() {
50 const double old_voltage = voltage_;
51 voltage_ += U(0, 0);
52
53 uncapped_voltage_ = voltage_;
54
Austin Schuhf9286cd2014-02-11 00:51:09 -080055 double limit = zeroing_state() != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
Austin Schuh4b7b5d02014-02-10 21:20:34 -080056
57 // Make sure that reality and the observer can't get too far off. There is a
58 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
59 // against last cycle's voltage.
60 if (X_hat(2, 0) > last_voltage_ + 2.0) {
61 voltage_ -= X_hat(2, 0) - (last_voltage_ + 2.0);
62 LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
63 } else if (X_hat(2, 0) < last_voltage_ -2.0) {
64 voltage_ += X_hat(2, 0) - (last_voltage_ - 2.0);
65 LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
66 }
67
68 voltage_ = std::min(limit, voltage_);
69 voltage_ = std::max(-limit, voltage_);
70 U(0, 0) = voltage_ - old_voltage;
Austin Schuhf9286cd2014-02-11 00:51:09 -080071 LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
72 LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
Austin Schuh4b7b5d02014-02-10 21:20:34 -080073
74 last_voltage_ = voltage_;
75}
76
Austin Schuhcc0bf312014-02-09 00:39:29 -080077ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
78 : aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -080079 has_top_claw_goal_(false),
80 top_claw_goal_(0.0),
81 top_claw_(MakeTopClawLoop()),
82 has_bottom_claw_goal_(false),
83 bottom_claw_goal_(0.0),
84 bottom_claw_(MakeBottomClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +000085 was_enabled_(false),
86 doing_calibration_fine_tune_(false) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -080087
Austin Schuh4b7b5d02014-02-10 21:20:34 -080088const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -080089
Austin Schuhf9286cd2014-02-11 00:51:09 -080090bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
91 const constants::Values::Claw &claw_values, double *edge_encoder,
92 double *edge_angle) {
93
94 // TODO(austin): Validate that the hall effect edge makes sense.
95 // We must now be on the side of the edge that we expect to be, and the
96 // encoder must have been on either side of the edge before and after.
97
98 if (front_hall_effect_posedge_count_changed()) {
99 if (encoder() - last_encoder() < 0) {
100 *edge_angle = claw_values.front.upper_angle;
101 } else {
102 *edge_angle = claw_values.front.lower_angle;
103 }
104 *edge_encoder = posedge_value_;
105 return true;
106 }
107 if (front_hall_effect_negedge_count_changed()) {
108 if (encoder() - last_encoder() > 0) {
109 *edge_angle = claw_values.front.upper_angle;
110 } else {
111 *edge_angle = claw_values.front.lower_angle;
112 }
113 *edge_encoder = negedge_value_;
114 return true;
115 }
116 if (calibration_hall_effect_posedge_count_changed()) {
117 if (encoder() - last_encoder() < 0) {
118 *edge_angle = claw_values.calibration.upper_angle;
119 } else {
120 *edge_angle = claw_values.calibration.lower_angle;
121 }
122 *edge_encoder = posedge_value_;
123 return true;
124 }
125 if (calibration_hall_effect_negedge_count_changed()) {
126 if (encoder() - last_encoder() > 0) {
127 *edge_angle = claw_values.calibration.upper_angle;
128 } else {
129 *edge_angle = claw_values.calibration.lower_angle;
130 }
131 *edge_encoder = negedge_value_;
132 return true;
133 }
134 if (back_hall_effect_posedge_count_changed()) {
135 if (encoder() - last_encoder() < 0) {
136 *edge_angle = claw_values.back.upper_angle;
137 } else {
138 *edge_angle = claw_values.back.lower_angle;
139 }
140 *edge_encoder = posedge_value_;
141 return true;
142 }
143 if (back_hall_effect_negedge_count_changed()) {
144 if (encoder() - last_encoder() > 0) {
145 *edge_angle = claw_values.back.upper_angle;
146 } else {
147 *edge_angle = claw_values.back.lower_angle;
148 }
149 *edge_encoder = negedge_value_;
150 return true;
151 }
152 return false;
153}
154
Austin Schuh3bb9a442014-02-02 16:01:45 -0800155// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800156void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
157 const control_loops::ClawGroup::Position *position,
158 control_loops::ClawGroup::Output *output,
Austin Schuh3bb9a442014-02-02 16:01:45 -0800159 ::aos::control_loops::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800160 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800161
162 // Disable the motors now so that all early returns will return with the
163 // motors disabled.
164 if (output) {
165 output->top_claw_voltage = 0;
166 output->bottom_claw_voltage = 0;
167 output->intake_voltage = 0;
168 }
169
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800170 // TODO(austin): Handle the disabled state and the disabled -> enabled
171 // transition in all of these states.
172 // TODO(austin): Handle zeroing while disabled.
173
174 // TODO(austin): Save all the counters so we know when something actually
175 // happens.
176 // TODO(austin): Helpers to find the position of the claw on an edge.
177
178 // TODO(austin): This may not be necesary because of the ControlLoop class.
179 ::aos::robot_state.FetchLatest();
180 if (::aos::robot_state.get() == nullptr) {
181 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800182 }
183
Austin Schuhf9286cd2014-02-11 00:51:09 -0800184 const frc971::constants::Values &values = constants::GetValues();
185
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800186 if (position) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800187 top_claw_.SetPositionValues(position->top);
188 bottom_claw_.SetPositionValues(position->bottom);
189
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800190 if (!has_top_claw_goal_) {
191 has_top_claw_goal_ = true;
192 top_claw_goal_ = position->top.position;
193 }
194 if (!has_bottom_claw_goal_) {
195 has_bottom_claw_goal_ = true;
196 bottom_claw_goal_ = position->bottom.position;
197 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800198 }
199
200 bool autonomous = ::aos::robot_state->autonomous;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800201 bool enabled = ::aos::robot_state->enabled;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800202
203 if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
204 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
205 (autonomous &&
206 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
207 top_claw_.zeroing_state() ==
208 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
209 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
210 bottom_claw_.zeroing_state() ==
211 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
212 // Ready to use the claw.
213 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800214 bottom_claw_goal_ = goal->bottom_angle;
215 top_claw_goal_ = goal->bottom_angle + goal->seperation_angle;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800216 } else if (top_claw_.zeroing_state() !=
217 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
218 bottom_claw_.zeroing_state() !=
219 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
220 // Time to fine tune the zero.
221 // Limit the goals here.
222 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Ben Fredrickson9b388422014-02-13 06:15:31 +0000223 // always get the bottom claw to calibrated first
224 if (!doing_calibration_fine_tune_) {
225 if (position->bottom.position > values.start_fine_tune_pos -
226 values.claw_unimportant_epsilon &&
227 position->bottom.position < values.start_fine_tune_pos +
228 values.claw_unimportant_epsilon) {
229 doing_calibration_fine_tune_ = true;
230 bottom_claw_goal_ += values.claw_zeroing_off_speed * dt;
231 } else {
232 // send bottom to zeroing start
233 bottom_claw_goal_ = values.start_fine_tune_pos;
234 }
235 } else {
236 if (position->bottom.front_hall_effect ||
237 position->bottom.back_hall_effect ||
238 position->top.front_hall_effect ||
239 position->top.back_hall_effect) {
240 // this should not happen, but now we know it won't
241 doing_calibration_fine_tune_ = false;
242 bottom_claw_goal_ = values.start_fine_tune_pos;
243 }
244 if (position->bottom.calibration_hall_effect) {
245 // do calibration
246 bottom_claw_.SetCalibration(
247 position->bottom.posedge_value,
248 values.lower_claw.calibration.lower_angle);
249 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
250 // calinrated so we are done fine tuning bottom
251 doing_calibration_fine_tune_ = false;
252 }
253 }
254 // now set the top claw to track
255 top_claw_goal_ = bottom_claw_goal_ + goal->seperation_angle;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800256 } else {
Ben Fredrickson9b388422014-02-13 06:15:31 +0000257 // bottom claw must be calibrated, start on the top
258 if (!doing_calibration_fine_tune_) {
259 if (position->top.position > values.start_fine_tune_pos -
260 values.claw_unimportant_epsilon &&
261 position->top.position < values.start_fine_tune_pos +
262 values.claw_unimportant_epsilon) {
263 doing_calibration_fine_tune_ = true;
264 top_claw_goal_ += values.claw_zeroing_off_speed * dt;
265 } else {
266 // send top to zeroing start
267 top_claw_goal_ = values.start_fine_tune_pos;
268 }
269 } else {
270 if (position->top.front_hall_effect ||
271 position->top.back_hall_effect ||
272 position->top.front_hall_effect ||
273 position->top.back_hall_effect) {
274 // this should not happen, but now we know it won't
275 doing_calibration_fine_tune_ = false;
276 top_claw_goal_ = values.start_fine_tune_pos;
277 }
278 if (position->top.calibration_hall_effect) {
279 // do calibration
280 top_claw_.SetCalibration(
281 position->top.posedge_value,
282 values.upper_claw.calibration.lower_angle);
283 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
284 // calinrated so we are done fine tuning top
285 doing_calibration_fine_tune_ = false;
286 }
287 }
288 // now set the bottom claw to track
289 bottom_claw_goal_ = top_claw_goal_ - goal->seperation_angle;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800290 }
291 } else {
292 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800293 if (position) {
294 top_claw_goal_ = position->top.position;
295 bottom_claw_goal_ = position->bottom.position;
296 } else {
297 has_top_claw_goal_ = false;
298 has_bottom_claw_goal_ = false;
299 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800300 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800301
302 // TODO(austin): Limit the goals here.
303 // Need to prevent windup, limit voltage, deal with windup on only 1 claw,
304 // ...
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800305 if (top_claw_.zeroing_state() ==
306 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
307 }
308 if (bottom_claw_.zeroing_state() ==
309 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
310 }
311
312 if (bottom_claw_.zeroing_state() !=
313 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800314 if (enabled) {
315 // Time to slowly move back up to find any position to narrow down the
316 // zero.
317 top_claw_goal_ += values.claw_zeroing_off_speed * dt;
318 bottom_claw_goal_ += values.claw_zeroing_off_speed * dt;
319 // TODO(austin): Goal velocity too!
320 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800321 } else {
322 // We don't know where either claw is. Slowly start moving down to find
323 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800324 if (enabled) {
325 top_claw_goal_-= values.claw_zeroing_off_speed * dt;
326 bottom_claw_goal_ -= values.claw_zeroing_off_speed * dt;
327 // TODO(austin): Goal velocity too!
328 }
329 }
330
331 if (enabled) {
332 top_claw_.SetCalibrationOnEdge(
333 values.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
334 bottom_claw_.SetCalibrationOnEdge(
335 values.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
336 } else {
337 top_claw_.SetCalibrationOnEdge(
338 values.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
339 bottom_claw_.SetCalibrationOnEdge(
340 values.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800341 }
342 }
343
344 // TODO(austin): Handle disabled.
Austin Schuh3bb9a442014-02-02 16:01:45 -0800345
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800346 // TODO(austin): ...
Austin Schuhf9286cd2014-02-11 00:51:09 -0800347 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
348 top_claw_.R << top_claw_goal_, 0.0, 0.0;
349 bottom_claw_.R << bottom_claw_goal_, 0.0, 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800350
Austin Schuhf9286cd2014-02-11 00:51:09 -0800351 top_claw_.Update(output == nullptr);
352 bottom_claw_.Update(output == nullptr);
353 } else {
354 top_claw_.ZeroPower();
355 bottom_claw_.ZeroPower();
356 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800357
358 if (position) {
359 //LOG(DEBUG, "pos: %f hall: %s absolute: %f\n", position->top_position,
360 //position->top_calibration_hall_effect ? "true" : "false",
361 //zeroed_joint_.absolute_position());
Austin Schuh3bb9a442014-02-02 16:01:45 -0800362 }
363
364 if (output) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800365 output->top_claw_voltage = top_claw_.voltage();
366 output->bottom_claw_voltage = bottom_claw_.voltage();
Austin Schuh3bb9a442014-02-02 16:01:45 -0800367 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800368 status->done = false;
369 //::std::abs(zeroed_joint_.absolute_position() - goal->bottom_angle -
370 //goal->seperation_angle) < 0.004;
371
372 was_enabled_ = ::aos::robot_state->enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800373}
374
375} // namespace control_loops
376} // namespace frc971