blob: b49e99cecf6c8a21d7f3421d76f044720b04f33b [file] [log] [blame]
Austin Schuha40624b2013-03-03 14:02:40 -08001#include "frc971/control_loops/wrist/wrist.h"
Austin Schuhdc1c84a2013-02-23 16:33:10 -08002
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"
Austin Schuha40624b2013-03-03 14:02:40 -080014#include "frc971/control_loops/wrist/wrist_motor_plant.h"
Austin Schuhdc1c84a2013-02-23 16:33:10 -080015
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 Schuhd11c3372013-03-09 14:33:31 -080024 zero_offset_(0.0),
25 capped_goal_(false) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080026}
27
Austin Schuhfa033692013-02-24 01:00:55 -080028bool WristMotor::FetchConstants() {
James Kuszmaule06e2512013-03-02 15:04:53 -080029 if (!constants::wrist_lower_limit(&wrist_lower_limit_)) {
30 LOG(ERROR, "Failed to fetch the wrist lower limit constant.\n");
Austin Schuhfa033692013-02-24 01:00:55 -080031 return false;
32 }
James Kuszmaule06e2512013-03-02 15:04:53 -080033 if (!constants::wrist_upper_limit(&wrist_upper_limit_)) {
34 LOG(ERROR, "Failed to fetch the wrist upper limit constant.\n");
Austin Schuhfa033692013-02-24 01:00:55 -080035 return false;
36 }
James Kuszmaule06e2512013-03-02 15:04:53 -080037 if (!constants::wrist_hall_effect_start_angle(
38 &wrist_hall_effect_start_angle_)) {
39 LOG(ERROR, "Failed to fetch the wrist start angle constant.\n");
Austin Schuhfa033692013-02-24 01:00:55 -080040 return false;
41 }
James Kuszmaule06e2512013-03-02 15:04:53 -080042 if (!constants::wrist_zeroing_speed(
43 &wrist_zeroing_speed_)) {
44 LOG(ERROR, "Failed to fetch the wrist zeroing speed constant.\n");
Austin Schuhfa033692013-02-24 01:00:55 -080045 return false;
46 }
47
48 return true;
49}
50
51double WristMotor::ClipGoal(double goal) const {
James Kuszmaule06e2512013-03-02 15:04:53 -080052 return std::min(wrist_upper_limit_,
53 std::max(wrist_lower_limit_, goal));
Austin Schuhfa033692013-02-24 01:00:55 -080054}
55
Austin Schuh06ee48e2013-03-02 01:47:54 -080056const double kMaxZeroingVoltage = 5.0;
57
58void WristMotor::WristStateFeedbackLoop::CapU() {
59 if (wrist_motor_->state_ == READY) {
James Kuszmaule06e2512013-03-02 15:04:53 -080060 if (Y(0, 0) >= wrist_motor_->wrist_upper_limit_) {
Austin Schuh06ee48e2013-03-02 01:47:54 -080061 U(0, 0) = std::min(0.0, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080062 }
James Kuszmaule06e2512013-03-02 15:04:53 -080063 if (Y(0, 0) <= wrist_motor_->wrist_lower_limit_) {
Austin Schuh06ee48e2013-03-02 01:47:54 -080064 U(0, 0) = std::max(0.0, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080065 }
66 }
67
Austin Schuh06ee48e2013-03-02 01:47:54 -080068 double limit = (wrist_motor_->state_ == READY) ? 12.0 : kMaxZeroingVoltage;
69
70 U(0, 0) = std::min(limit, U(0, 0));
71 U(0, 0) = std::max(-limit, U(0, 0));
Austin Schuhfa033692013-02-24 01:00:55 -080072}
73
74// Positive angle is up, and positive power is up.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080075void WristMotor::RunIteration(
76 const ::aos::control_loops::Goal *goal,
77 const control_loops::WristLoop::Position *position,
78 ::aos::control_loops::Output *output,
79 ::aos::control_loops::Status * /*status*/) {
80
Austin Schuhfa033692013-02-24 01:00:55 -080081 // Disable the motors now so that all early returns will return with the
82 // motors disabled.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080083 if (output) {
84 output->voltage = 0;
85 }
86
Austin Schuhfa033692013-02-24 01:00:55 -080087 // Cache the constants to avoid error handling down below.
88 if (!FetchConstants()) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080089 return;
90 }
91
Austin Schuhfa033692013-02-24 01:00:55 -080092 // Uninitialize the bot if too many cycles pass without an encoder.
Austin Schuhdc1c84a2013-02-23 16:33:10 -080093 if (position == NULL) {
94 LOG(WARNING, "no new pos given\n");
95 error_count_++;
96 } else {
97 error_count_ = 0;
98 }
Austin Schuhdc1c84a2013-02-23 16:33:10 -080099 if (error_count_ >= 4) {
100 LOG(WARNING, "err_count is %d so forcing a re-zero\n", error_count_);
Austin Schuhfa033692013-02-24 01:00:55 -0800101 state_ = UNINITIALIZED;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800102 }
103
Austin Schuhfa033692013-02-24 01:00:55 -0800104 // Compute the absolute position of the wrist.
105 double absolute_position;
106 if (position) {
107 absolute_position =
James Kuszmaule06e2512013-03-02 15:04:53 -0800108 position->pos + wrist_hall_effect_start_angle_;
Austin Schuhfa033692013-02-24 01:00:55 -0800109 if (state_ == READY) {
110 absolute_position -= zero_offset_;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800111 }
Austin Schuhfa033692013-02-24 01:00:55 -0800112 loop_->Y << absolute_position;
113 if (!position->hall_effect) {
114 last_off_position_ = position->pos;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800115 }
116 } else {
Austin Schuhfa033692013-02-24 01:00:55 -0800117 // Dead recon for now.
118 absolute_position = loop_->X_hat(0, 0);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800119 }
120
Austin Schuhfa033692013-02-24 01:00:55 -0800121 switch (state_) {
122 case UNINITIALIZED:
123 if (position) {
124 // Reset the zeroing goal.
125 zeroing_position_ = absolute_position;
126 // Clear the observer state.
127 loop_->X_hat << absolute_position, 0.0;
Austin Schuh464ee1a2013-03-01 22:37:39 -0800128 // Set the goal to here to make it so it doesn't move when disabled.
129 loop_->R = loop_->X_hat;
Austin Schuhfa033692013-02-24 01:00:55 -0800130 // Only progress if we are enabled.
131 if (::aos::robot_state->enabled) {
132 if (position->hall_effect) {
133 state_ = MOVING_OFF;
134 } else {
135 state_ = ZEROING;
136 }
137 }
138 }
139 break;
140 case MOVING_OFF:
141 // Move off the hall effect sensor.
142 if (!::aos::robot_state->enabled) {
143 // Start over if disabled.
144 state_ = UNINITIALIZED;
145 } else if (position && !position->hall_effect) {
146 // We are now off the sensor. Time to zero now.
147 state_ = ZEROING;
148 } else {
149 // Slowly creep off the sensor.
James Kuszmaule06e2512013-03-02 15:04:53 -0800150 zeroing_position_ -= wrist_zeroing_speed_ / 100;
151 loop_->R << zeroing_position_, -wrist_zeroing_speed_;
Austin Schuhfa033692013-02-24 01:00:55 -0800152 break;
153 }
154 case ZEROING:
155 if (!::aos::robot_state->enabled) {
156 // Start over if disabled.
157 state_ = UNINITIALIZED;
158 } else if (position && position->hall_effect) {
159 state_ = READY;
160 // Verify that the calibration number is between the last off position
161 // and the current on position. If this is not true, move off and try
162 // again.
163 if (position->calibration <= last_off_position_ ||
164 position->calibration > position->pos) {
165 LOG(ERROR, "Got a bogus calibration number. Trying again.\n");
166 LOG(ERROR,
167 "Last off position was %f, current is %f, calibration is %f\n",
168 last_off_position_, position->pos, position->calibration);
169 state_ = MOVING_OFF;
170 } else {
171 // Save the zero, and then offset the observer to deal with the
172 // phantom step change.
173 const double old_zero_offset = zero_offset_;
174 zero_offset_ = position->calibration;
175 loop_->X_hat(0, 0) += old_zero_offset - zero_offset_;
176 loop_->Y(0, 0) += old_zero_offset - zero_offset_;
177 }
178 } else {
179 // Slowly creep towards the sensor.
James Kuszmaule06e2512013-03-02 15:04:53 -0800180 zeroing_position_ += wrist_zeroing_speed_ / 100;
181 loop_->R << zeroing_position_, wrist_zeroing_speed_;
Austin Schuhfa033692013-02-24 01:00:55 -0800182 }
183 break;
184
185 case READY:
186 {
187 const double limited_goal = ClipGoal(goal->goal);
188 loop_->R << limited_goal, 0.0;
189 break;
190 }
191
192 case ESTOP:
193 LOG(WARNING, "have already given up\n");
194 return;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800195 }
196
Austin Schuhfa033692013-02-24 01:00:55 -0800197 // Update the observer.
198 loop_->Update(position != NULL, output == NULL);
199
Austin Schuhd11c3372013-03-09 14:33:31 -0800200 capped_goal_ = false;
Austin Schuh06ee48e2013-03-02 01:47:54 -0800201 // Verify that the zeroing goal hasn't run away.
202 switch (state_) {
203 case UNINITIALIZED:
204 case READY:
205 case ESTOP:
206 // Not zeroing. No worries.
207 break;
208 case MOVING_OFF:
209 case ZEROING:
210 // Check if we have cliped and adjust the goal.
211 if (loop_->U_uncapped(0, 0) > kMaxZeroingVoltage) {
212 double dx = (loop_->U_uncapped(0, 0) -
213 kMaxZeroingVoltage) / loop_->K(0, 0);
214 zeroing_position_ -= dx;
Austin Schuhd11c3372013-03-09 14:33:31 -0800215 capped_goal_ = true;
Austin Schuh06ee48e2013-03-02 01:47:54 -0800216 } else if(loop_->U_uncapped(0, 0) < -kMaxZeroingVoltage) {
217 double dx = (loop_->U_uncapped(0, 0) +
218 kMaxZeroingVoltage) / loop_->K(0, 0);
219 zeroing_position_ -= dx;
Austin Schuhd11c3372013-03-09 14:33:31 -0800220 capped_goal_ = true;
Austin Schuh06ee48e2013-03-02 01:47:54 -0800221 }
222 break;
223 }
224
Austin Schuhfa033692013-02-24 01:00:55 -0800225 if (position) {
226 LOG(DEBUG, "pos=%f zero=%f currently %f hall: %s\n",
227 position->pos, zero_offset_, absolute_position,
228 position->hall_effect ? "true" : "false");
229 }
230
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800231 if (output) {
Austin Schuh06ee48e2013-03-02 01:47:54 -0800232 output->voltage = loop_->U(0, 0);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800233 }
234}
235
236} // namespace control_loops
237} // namespace frc971