blob: edbd8a06cce8702e6abe5c84f30ce3dc6a1ec712 [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),
21 loop_(new StateFeedbackLoop<2, 1, 1>(MakeWristLoop())),
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
55double WristMotor::LimitVoltage(double absolute_position,
56 double voltage) const {
57 if (state_ == READY) {
58 if (absolute_position >= horizontal_upper_limit_) {
59 voltage = std::min(0.0, voltage);
60 }
61 if (absolute_position <= horizontal_lower_limit_) {
62 voltage = std::max(0.0, voltage);
63 }
64 }
65
66 double limit = (state_ == READY) ? 12.0 : 5.0;
67 // TODO(aschuh): Remove this line when we are done testing.
68 //limit = std::min(0.3, limit);
69 voltage = std::min(limit, voltage);
70 voltage = std::max(-limit, voltage);
71 return voltage;
72}
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 =
108 position->pos + horizontal_hall_effect_start_angle_;
109 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;
128 // Only progress if we are enabled.
129 if (::aos::robot_state->enabled) {
130 if (position->hall_effect) {
131 state_ = MOVING_OFF;
132 } else {
133 state_ = ZEROING;
134 }
135 }
136 }
137 break;
138 case MOVING_OFF:
139 // Move off the hall effect sensor.
140 if (!::aos::robot_state->enabled) {
141 // Start over if disabled.
142 state_ = UNINITIALIZED;
143 } else if (position && !position->hall_effect) {
144 // We are now off the sensor. Time to zero now.
145 state_ = ZEROING;
146 } else {
147 // Slowly creep off the sensor.
148 zeroing_position_ -= horizontal_zeroing_speed_ / 100;
149 loop_->R << zeroing_position_, -horizontal_zeroing_speed_;
150 break;
151 }
152 case ZEROING:
153 if (!::aos::robot_state->enabled) {
154 // Start over if disabled.
155 state_ = UNINITIALIZED;
156 } else if (position && position->hall_effect) {
157 state_ = READY;
158 // Verify that the calibration number is between the last off position
159 // and the current on position. If this is not true, move off and try
160 // again.
161 if (position->calibration <= last_off_position_ ||
162 position->calibration > position->pos) {
163 LOG(ERROR, "Got a bogus calibration number. Trying again.\n");
164 LOG(ERROR,
165 "Last off position was %f, current is %f, calibration is %f\n",
166 last_off_position_, position->pos, position->calibration);
167 state_ = MOVING_OFF;
168 } else {
169 // Save the zero, and then offset the observer to deal with the
170 // phantom step change.
171 const double old_zero_offset = zero_offset_;
172 zero_offset_ = position->calibration;
173 loop_->X_hat(0, 0) += old_zero_offset - zero_offset_;
174 loop_->Y(0, 0) += old_zero_offset - zero_offset_;
175 }
176 } else {
177 // Slowly creep towards the sensor.
178 zeroing_position_ += horizontal_zeroing_speed_ / 100;
179 loop_->R << zeroing_position_, horizontal_zeroing_speed_;
180 }
181 break;
182
183 case READY:
184 {
185 const double limited_goal = ClipGoal(goal->goal);
186 loop_->R << limited_goal, 0.0;
187 break;
188 }
189
190 case ESTOP:
191 LOG(WARNING, "have already given up\n");
192 return;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800193 }
194
Austin Schuhfa033692013-02-24 01:00:55 -0800195 // Update the observer.
196 loop_->Update(position != NULL, output == NULL);
197
198 if (position) {
199 LOG(DEBUG, "pos=%f zero=%f currently %f hall: %s\n",
200 position->pos, zero_offset_, absolute_position,
201 position->hall_effect ? "true" : "false");
202 }
203
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800204 if (output) {
Austin Schuhfa033692013-02-24 01:00:55 -0800205 output->voltage = LimitVoltage(absolute_position, loop_->U(0, 0));
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800206 }
207}
208
209} // namespace control_loops
210} // namespace frc971