blob: 845f1c6325376b63ef334f2ebd0509d85638eeb7 [file] [log] [blame]
Austin Schuhd78ab542013-03-01 22:22:19 -08001#include "frc971/control_loops/index.h"
2
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/aos_core.h"
8
9#include "aos/common/messages/RobotState.q.h"
10#include "aos/common/control_loop/control_loops.q.h"
11#include "aos/common/logging/logging.h"
12
13#include "frc971/constants.h"
14#include "frc971/control_loops/index_motor_plant.h"
15
16using ::aos::time::Time;
17
18namespace frc971 {
19namespace control_loops {
20
21IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
22 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
23 wrist_loop_(new StateFeedbackLoop<2, 1, 1>(MakeIndexLoop())),
24 hopper_disc_count_(0),
25 total_disc_count_(0),
26 loader_up_(false),
27 disc_clamped_(false),
28 disc_ejected_(false),
29 last_bottom_disc_detect_(false) {
30}
31
32const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
33const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
34
35bool IndexMotor::FetchConstants() {
36 if (!constants::horizontal_lower_limit(&horizontal_lower_limit_)) {
37 LOG(ERROR, "Failed to fetch the horizontal lower limit constant.\n");
38 return false;
39 }
40 if (!constants::horizontal_upper_limit(&horizontal_upper_limit_)) {
41 LOG(ERROR, "Failed to fetch the horizontal upper limit constant.\n");
42 return false;
43 }
44 if (!constants::horizontal_hall_effect_start_angle(
45 &horizontal_hall_effect_start_angle_)) {
46 LOG(ERROR, "Failed to fetch the horizontal start angle constant.\n");
47 return false;
48 }
49 if (!constants::horizontal_zeroing_speed(
50 &horizontal_zeroing_speed_)) {
51 LOG(ERROR, "Failed to fetch the horizontal zeroing speed constant.\n");
52 return false;
53 }
54
55 return true;
56}
57
58// Distance to move the indexer when grabbing a disc.
59const double kNextPosition = 10.0;
60
61/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
62 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
63}
64
65/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(const double angle) {
66 return angle * (kDiscRadius + kRollerRadius);
67}
68
69/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
70 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
71}
72
73/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
74 return IndexMotor::ConvertDiscAngleToDiscPosition(
75 ConvertIndexToDiscAngle(angle));
76}
77
78// Positive angle is towards the shooter, and positive power is towards the
79// shooter.
80void IndexMotor::RunIteration(
81 const control_loops::IndexLoop::Goal *goal,
82 const control_loops::IndexLoop::Position *position,
83 control_loops::IndexLoop::Output *output,
84 control_loops::IndexLoop::Status *status) {
85 // Make goal easy to work with.
86 Goal goal_enum = static_cast<Goal>(goal->goal_state);
87
88 // Disable the motors now so that all early returns will return with the
89 // motors disabled.
90 if (output) {
91 output->transfer_voltage = 0.0;
92 output->index_voltage = 0.0;
93 }
94
95 status->ready_to_intake = false;
96
97 // Cache the constants to avoid error handling down below.
98 if (!FetchConstants()) {
99 return;
100 }
101
102 if (position) {
103 wrist_loop_->Y << position->index_position;
104 }
105 const double index_position = wrist_loop_->X_hat(0, 0);
106
107 bool safe_to_change_state_ = true;
108 switch (safe_goal_) {
109 case HOLD:
110 // The goal should already be good, so sit tight with everything the same
111 // as it was.
112 printf("HOLD Not implemented\n");
113 break;
114 case READY_LOWER:
115 printf("READY_LOWER Not implemented\n");
116 break;
117 case INTAKE:
118 {
119 Time now = Time::Now();
120 if (hopper_disc_count_ < 4) {
121 output->transfer_voltage = 12.0;
122 }
123 // Posedge of the disc entering the beam break.
124 if (position) {
125 if (position->bottom_disc_detect && !last_bottom_disc_detect_) {
126 transfer_frisbee_.Reset();
127 transfer_frisbee_.bottom_posedge_time_ = now;
128 printf("Posedge of bottom disc %f\n",
129 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
130 ++hopper_disc_count_;
131 }
132
133 // Disc exited the beam break now.
134 if (!position->bottom_disc_detect && last_bottom_disc_detect_) {
135 transfer_frisbee_.bottom_negedge_time_ = now;
136 printf("Negedge of bottom disc %f\n",
137 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
138 frisbees_.push_front(transfer_frisbee_);
139 }
140
141 if (position->bottom_disc_detect) {
142 output->transfer_voltage = 12.0;
143 // Must wait until the disc gets out before we can change state.
144 safe_to_change_state_ = false;
145
146 // TODO(aschuh): A disc on the way through needs to start moving the
147 // indexer if it isn't already moving. Maybe?
148
149 Time elapsed_posedge_time = now -
150 transfer_frisbee_.bottom_posedge_time_;
151 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
152 // It has been too long. The disc must be jammed.
153 LOG(ERROR, "Been way too long. Jammed disc?\n");
154 printf("Been way too long. Jammed disc?\n");
155 }
156 }
157
158 for (Frisbee &frisbee : frisbees_) {
159 if (!frisbee.has_been_indexed_) {
160 output->transfer_voltage = 12.0;
161 Time elapsed_posedge_time = now -
162 frisbee.bottom_posedge_time_;
163 if (elapsed_posedge_time >= Time::InSeconds(0.07)) {
164 // Should have just engaged.
165 // Save the indexer position, and the time.
166
167 // It has been long enough since the disc entered the indexer.
168 // Treat now as the time at which it contacted the indexer.
169 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
170 printf("Grabbed on the index now at %f\n", index_position);
171 frisbee.has_been_indexed_ = true;
172 frisbee.index_start_position_ = index_position;
173 frisbee.index_start_time_ = now;
174 }
175 }
176 if (!frisbee.has_been_indexed_) {
177 // Discs must all be indexed before it is safe to stop indexing.
178 safe_to_change_state_ = false;
179 }
180 }
181
182 double new_index_position = wrist_loop_->R(0, 0);
183
184 // TODO(aschuh): As we loop through, assess the state of the indexer
185 // and figure if the bottom disc is in a place such that we can
186 // intake without filling the hopper early.
187 // status->ready_to_intake = false;
188
189 for (Frisbee &frisbee : frisbees_) {
190 if (frisbee.has_been_indexed_) {
191 // We want to store it pi from where the disc was grabbed
192 // (for now).
193 new_index_position = ::std::max(
194 new_index_position,
195 (frisbee.index_start_position_ +
196 ConvertDiscAngleToIndex(M_PI)));
197 // TODO(aschuh): We should be able to pick the M_PI knowing if
198 // the next disc is coming in hot or not.
199 }
200 }
201 wrist_loop_->R << new_index_position, 0.0;
202 }
203 printf("INTAKE Not implemented\n");
204 }
205 break;
206 case READY_SHOOTER:
207 printf("READY_SHOOTER Not implemented\n");
208 break;
209 case SHOOT:
210 printf("SHOOT Not implemented\n");
211 break;
212 }
213
214 // Update the observer.
215 wrist_loop_->Update(position != NULL, output == NULL);
216
217 if (position) {
218 LOG(DEBUG, "pos=%f currently %f\n",
219 position->index_position, index_position);
220 last_bottom_disc_detect_ = position->bottom_disc_detect;
221 }
222
223 status->hopper_disc_count = hopper_disc_count_;
224 status->total_disc_count = total_disc_count_;
225
226
227 if (output) {
228 output->index_voltage = wrist_loop_->U(0, 0);
229 }
230
231 if (safe_to_change_state_) {
232 safe_goal_ = goal_enum;
233 }
234}
235
236} // namespace control_loops
237} // namespace frc971