blob: dee0438bddf9f73ce9df486577e9072c48cba177 [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;
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.
150 zeroing_position_ -= horizontal_zeroing_speed_ / 100;
151 loop_->R << zeroing_position_, -horizontal_zeroing_speed_;
152 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.
180 zeroing_position_ += horizontal_zeroing_speed_ / 100;
181 loop_->R << zeroing_position_, horizontal_zeroing_speed_;
182 }
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
200 if (position) {
201 LOG(DEBUG, "pos=%f zero=%f currently %f hall: %s\n",
202 position->pos, zero_offset_, absolute_position,
203 position->hall_effect ? "true" : "false");
204 }
205
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800206 if (output) {
Austin Schuhfa033692013-02-24 01:00:55 -0800207 output->voltage = LimitVoltage(absolute_position, loop_->U(0, 0));
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800208 }
209}
210
211} // namespace control_loops
212} // namespace frc971