blob: de6258010acc5dd56db51b27646b8ccc09145d12 [file] [log] [blame]
Austin Schuhdc1c84a2013-02-23 16:33:10 -08001#include "frc971/control_loops/wrist.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/wrist_motor_plant.h"
15
16namespace frc971 {
17namespace control_loops {
18
Austin Schuhdc1c84a2013-02-23 16:33:10 -080019WristMotor::WristMotor(control_loops::WristLoop *my_wrist)
20 : aos::control_loops::ControlLoop<control_loops::WristLoop>(my_wrist),
Austin Schuh06ee48e2013-03-02 01:47:54 -080021 loop_(new WristStateFeedbackLoop(MakeWristLoop(), this)),
Austin Schuhfa033692013-02-24 01:00:55 -080022 state_(UNINITIALIZED),
Austin Schuhdc1c84a2013-02-23 16:33:10 -080023 error_count_(0),
Austin Schuhfa033692013-02-24 01:00:55 -080024 zero_offset_(0.0) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080025}
26
Austin Schuhfa033692013-02-24 01:00:55 -080027bool WristMotor::FetchConstants() {
28 if (!constants::horizontal_lower_limit(&horizontal_lower_limit_)) {
29 LOG(ERROR, "Failed to fetch the horizontal lower limit constant.\n");
30 return false;
31 }
32 if (!constants::horizontal_upper_limit(&horizontal_upper_limit_)) {
33 LOG(ERROR, "Failed to fetch the horizontal upper limit constant.\n");
34 return false;
35 }
36 if (!constants::horizontal_hall_effect_start_angle(
37 &horizontal_hall_effect_start_angle_)) {
38 LOG(ERROR, "Failed to fetch the horizontal start angle constant.\n");
39 return false;
40 }
41 if (!constants::horizontal_zeroing_speed(
42 &horizontal_zeroing_speed_)) {
43 LOG(ERROR, "Failed to fetch the horizontal zeroing speed constant.\n");
44 return false;
45 }
46
47 return true;
48}
49
50double WristMotor::ClipGoal(double goal) const {
51 return std::min(horizontal_upper_limit_,
52 std::max(horizontal_lower_limit_, goal));
53}
54
Austin Schuh06ee48e2013-03-02 01:47:54 -080055const double kMaxZeroingVoltage = 5.0;
56
57void WristMotor::WristStateFeedbackLoop::CapU() {
58 if (wrist_motor_->state_ == READY) {
59 if (Y(0, 0) >= wrist_motor_->horizontal_upper_limit_) {
60 U(0, 0) = std::min(0.0, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080061 }
Austin Schuh06ee48e2013-03-02 01:47:54 -080062 if (Y(0, 0) <= wrist_motor_->horizontal_lower_limit_) {
63 U(0, 0) = std::max(0.0, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080064 }
65 }
66
Austin Schuh06ee48e2013-03-02 01:47:54 -080067 double limit = (wrist_motor_->state_ == READY) ? 12.0 : kMaxZeroingVoltage;
68
69 U(0, 0) = std::min(limit, U(0, 0));
70 U(0, 0) = std::max(-limit, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080071}
72
73// Positive angle is up, and positive power is up.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080074void WristMotor::RunIteration(
75 const ::aos::control_loops::Goal *goal,
76 const control_loops::WristLoop::Position *position,
77 ::aos::control_loops::Output *output,
78 ::aos::control_loops::Status * /*status*/) {
79
Austin Schuhfa033692013-02-24 01:00:55 -080080 // Disable the motors now so that all early returns will return with the
81 // motors disabled.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080082 if (output) {
83 output->voltage = 0;
84 }
85
Austin Schuhfa033692013-02-24 01:00:55 -080086 // Cache the constants to avoid error handling down below.
87 if (!FetchConstants()) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080088 return;
89 }
90
Austin Schuhfa033692013-02-24 01:00:55 -080091 // Uninitialize the bot if too many cycles pass without an encoder.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080092 if (position == NULL) {
93 LOG(WARNING, "no new pos given\n");
94 error_count_++;
95 } else {
96 error_count_ = 0;
97 }
Austin Schuhdc1c84a2013-02-23 16:33:10 -080098 if (error_count_ >= 4) {
99 LOG(WARNING, "err_count is %d so forcing a re-zero\n", error_count_);
Austin Schuhfa033692013-02-24 01:00:55 -0800100 state_ = UNINITIALIZED;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800101 }
102
Austin Schuhfa033692013-02-24 01:00:55 -0800103 // Compute the absolute position of the wrist.
104 double absolute_position;
105 if (position) {
106 absolute_position =
107 position->pos + horizontal_hall_effect_start_angle_;
108 if (state_ == READY) {
109 absolute_position -= zero_offset_;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800110 }
Austin Schuhfa033692013-02-24 01:00:55 -0800111 loop_->Y << absolute_position;
112 if (!position->hall_effect) {
113 last_off_position_ = position->pos;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800114 }
115 } else {
Austin Schuhfa033692013-02-24 01:00:55 -0800116 // Dead recon for now.
117 absolute_position = loop_->X_hat(0, 0);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800118 }
119
Austin Schuhfa033692013-02-24 01:00:55 -0800120 switch (state_) {
121 case UNINITIALIZED:
122 if (position) {
123 // Reset the zeroing goal.
124 zeroing_position_ = absolute_position;
125 // Clear the observer state.
126 loop_->X_hat << absolute_position, 0.0;
Austin Schuh464ee1a2013-03-01 22:37:39 -0800127 // Set the goal to here to make it so it doesn't move when disabled.
128 loop_->R = loop_->X_hat;
Austin Schuhfa033692013-02-24 01:00:55 -0800129 // Only progress if we are enabled.
130 if (::aos::robot_state->enabled) {
131 if (position->hall_effect) {
132 state_ = MOVING_OFF;
133 } else {
134 state_ = ZEROING;
135 }
136 }
137 }
138 break;
139 case MOVING_OFF:
140 // Move off the hall effect sensor.
141 if (!::aos::robot_state->enabled) {
142 // Start over if disabled.
143 state_ = UNINITIALIZED;
144 } else if (position && !position->hall_effect) {
145 // We are now off the sensor. Time to zero now.
146 state_ = ZEROING;
147 } else {
148 // Slowly creep off the sensor.
149 zeroing_position_ -= horizontal_zeroing_speed_ / 100;
150 loop_->R << zeroing_position_, -horizontal_zeroing_speed_;
151 break;
152 }
153 case ZEROING:
154 if (!::aos::robot_state->enabled) {
155 // Start over if disabled.
156 state_ = UNINITIALIZED;
157 } else if (position && position->hall_effect) {
158 state_ = READY;
159 // Verify that the calibration number is between the last off position
160 // and the current on position. If this is not true, move off and try
161 // again.
162 if (position->calibration <= last_off_position_ ||
163 position->calibration > position->pos) {
164 LOG(ERROR, "Got a bogus calibration number. Trying again.\n");
165 LOG(ERROR,
166 "Last off position was %f, current is %f, calibration is %f\n",
167 last_off_position_, position->pos, position->calibration);
168 state_ = MOVING_OFF;
169 } else {
170 // Save the zero, and then offset the observer to deal with the
171 // phantom step change.
172 const double old_zero_offset = zero_offset_;
173 zero_offset_ = position->calibration;
174 loop_->X_hat(0, 0) += old_zero_offset - zero_offset_;
175 loop_->Y(0, 0) += old_zero_offset - zero_offset_;
176 }
177 } else {
178 // Slowly creep towards the sensor.
179 zeroing_position_ += horizontal_zeroing_speed_ / 100;
180 loop_->R << zeroing_position_, horizontal_zeroing_speed_;
181 }
182 break;
183
184 case READY:
185 {
186 const double limited_goal = ClipGoal(goal->goal);
187 loop_->R << limited_goal, 0.0;
188 break;
189 }
190
191 case ESTOP:
192 LOG(WARNING, "have already given up\n");
193 return;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800194 }
195
Austin Schuhfa033692013-02-24 01:00:55 -0800196 // Update the observer.
197 loop_->Update(position != NULL, output == NULL);
198
Austin Schuh06ee48e2013-03-02 01:47:54 -0800199 // Verify that the zeroing goal hasn't run away.
200 switch (state_) {
201 case UNINITIALIZED:
202 case READY:
203 case ESTOP:
204 // Not zeroing. No worries.
205 break;
206 case MOVING_OFF:
207 case ZEROING:
208 // Check if we have cliped and adjust the goal.
209 if (loop_->U_uncapped(0, 0) > kMaxZeroingVoltage) {
210 double dx = (loop_->U_uncapped(0, 0) -
211 kMaxZeroingVoltage) / loop_->K(0, 0);
212 zeroing_position_ -= dx;
213 } else if(loop_->U_uncapped(0, 0) < -kMaxZeroingVoltage) {
214 double dx = (loop_->U_uncapped(0, 0) +
215 kMaxZeroingVoltage) / loop_->K(0, 0);
216 zeroing_position_ -= dx;
217 }
218 break;
219 }
220
Austin Schuhfa033692013-02-24 01:00:55 -0800221 if (position) {
222 LOG(DEBUG, "pos=%f zero=%f currently %f hall: %s\n",
223 position->pos, zero_offset_, absolute_position,
224 position->hall_effect ? "true" : "false");
225 }
226
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800227 if (output) {
Austin Schuh06ee48e2013-03-02 01:47:54 -0800228 output->voltage = loop_->U(0, 0);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800229 }
230}
231
232} // namespace control_loops
233} // namespace frc971