blob: 9fa0077a87f2bd67c0fc8020e7226dc97dc663c1 [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
19static const double MIN_POS = -0.80;
20static const double MAX_POS = 1.77;
21static const double positive_deadband_power = 0.15 * 12.0;
22static const double negative_deadband_power = 0.09 * 12.0;
23// Final units is radians/iteration.
24static const double MAX_SPEED = 19300.0/*free RPM*/ * 12.0/*pinions*/ / 60.0 * 14.0 / 50.0 * 14.0 / 48.0 / 60.0/*60 sec/min*/ / 100.0/*100 cycles/sec*/ * (2.0 * M_PI)/*rotations to radians*/;
25// The minimum magnitude for X_hat[1] to be sure of the direction it's moving so
26// that the code can get a zero. X_hat[1] is the angular velocity of the wrist
27// predicted by the observer, and it can be wrong when the wrist is just
28// switching direction and/or it is moving very slowly in/around the victor
29// deadband. Using this threshold instead of just 0 helps avoid falsely zeroing
30// on the back edge of the magnet instead of the front one.
31static const double kX_hatThreshold = 0.2;
32// The maximum amount that the calibration value can be off for it to be
33// accepted.
34// MAX_SPEED / 10 is the amount it would be off if it was read 1ms earlier than
35// the actual encoder position, which is a lot more than it should ever be.
36static const double kMaxCalibrationError = MAX_SPEED / 10;
37
38WristMotor::WristMotor(control_loops::WristLoop *my_wrist)
39 : aos::control_loops::ControlLoop<control_loops::WristLoop>(my_wrist),
40 loop_(new StateFeedbackLoop<2, 1, 1>(MakeWristLoop())),
41 stop_(false),
42 error_count_(0),
43 zeroed_(false),
44 zero_offset_(0.0) {
45}
46
47// down is positive
48void WristMotor::RunIteration(
49 const ::aos::control_loops::Goal *goal,
50 const control_loops::WristLoop::Position *position,
51 ::aos::control_loops::Output *output,
52 ::aos::control_loops::Status * /*status*/) {
53
54 if (output) {
55 output->voltage = 0;
56 }
57
58 if (stop_) {
59 LOG(WARNING, "have already given up\n");
60 return;
61 }
62
63 if (position == NULL) {
64 LOG(WARNING, "no new pos given\n");
65 error_count_++;
66 } else {
67 error_count_ = 0;
68 }
69
70 double horizontal;
71 if (!constants::horizontal_offset(&horizontal)) {
72 LOG(ERROR, "Failed to fetch the horizontal offset constant.\n");
73 return;
74 }
75
76 static bool good_zeroed = false;
77 static bool first = true;
78 if (error_count_ >= 4) {
79 LOG(WARNING, "err_count is %d so forcing a re-zero\n", error_count_);
80 first = true;
81 zeroed_ = false;
82 good_zeroed = false;
83 }
84
85 const double limited_goal = std::min(MAX_POS, std::max(MIN_POS, goal->goal));
86
87 static double zero_goal;
88 static bool old_cal = false;
89 static double old_pos = -5000000;
90 bool bad_encoder = false;
91 if (!first) {
92 if (old_cal != position->hall_effect) {
93 LOG(INFO, "old=%s X_hat=%f\n", old_cal ? "true" : "false", loop_->X_hat[1]);
94 // Don't want to miss the first one if it starts really close to the edge
95 // of the magnet so isn't moving very fast when it passes.
96 const double X_hatThreshold = zeroed_ ? kX_hatThreshold : 0;
97 if ((old_cal && loop_->X_hat[1] < -X_hatThreshold) ||
98 (!old_cal && loop_->X_hat[1] > X_hatThreshold)) {
99 LOG(INFO, "zeroing at %f (pos=%f, old_pos=%f) (used to be %f)\n",
100 position->calibration, position->pos, old_pos, zero_offset_);
101 // If ((old_pos - 0.02) <= position->calibration <= position->pos) or
102 // the other way around. Checks if position->calibration is between
103 // old_pos and position->pos if the wrist is moving either way, with a
104 // slight tweak to
105 // old_pos in case it was triggered while the sensor packet was being
106 // sent or something.
107 if (!((old_pos - kMaxCalibrationError <= position->calibration &&
108 position->calibration <= position->pos) ||
109 (old_pos + kMaxCalibrationError >= position->calibration &&
110 position->calibration >= position->pos))) {
111 if (!good_zeroed) {
112 LOG(WARNING, "using encoder pos because "
113 "calibration sensor pos doesn't make sense\n");
114 zero_offset_ = position->pos;
115 } else {
116 LOG(INFO, "already had a good zero, "
117 "so not using inaccurate zero value\n");
118 }
119 } else {
120 good_zeroed = true;
121 zero_offset_ = position->calibration;
122 }
123 zeroed_ = true;
124 } else {
125 LOG(INFO, "hit back edge at %f\n", position->calibration);
126 }
127 old_cal = position->hall_effect;
128 }
129 if (std::abs(position->pos - old_pos) > MAX_SPEED * 1.5) {
130 bad_encoder = true;
131 LOG(WARNING, "encoder value changed by %f which is more than MAX_SPEED(%f)\n",
132 std::abs(position->pos - old_pos), MAX_SPEED);
133 }
134 } // !first
135
136 old_pos = position->pos;
137 const double absolute_position = position->pos - zero_offset_ - horizontal;
138 if (first) {
139 first = false;
140 old_cal = position->hall_effect;
141 zero_goal = absolute_position;
142 }
143
144 loop_->Y << absolute_position;
145 if (!zeroed_) {
146 loop_->R << zero_goal, 0.0;
147 if (aos::robot_state->enabled) {
148 if (!position->hall_effect) {
149 zero_goal += 0.010;
150 } else {
151 zero_goal -= 0.010;
152 }
153 }
154 } else {
155 loop_->R << limited_goal, 0.0;
156 }
157 loop_->Update(!bad_encoder, bad_encoder || output == NULL);
158 double output_voltage = loop_->U[0];
159 //LOG(DEBUG, "fancy math gave %f\n", status->pwm);
160
161 if (output_voltage > 0) {
162 output_voltage += positive_deadband_power;
163 }
164 if (output_voltage < 0) {
165 output_voltage -= negative_deadband_power;
166 }
167
168 if (zeroed_) {
169 if (absolute_position >= MAX_POS) {
170 output_voltage = std::min(0.0, output_voltage);
171 }
172 if (absolute_position <= MIN_POS) {
173 output_voltage = std::max(0.0, output_voltage);
174 }
175 }
176
177 double limit = zeroed_ ? 1.0 : 0.5;
178 //limit = std::min(0.3, limit);
179 output_voltage = std::min(limit, output_voltage);
180 output_voltage = std::max(-limit, output_voltage);
181 LOG(DEBUG, "pos=%f zero=%f horizontal=%f currently %f hall: %s\n",
182 position->pos, zero_offset_, horizontal, absolute_position,
183 position->hall_effect ? "true" : "false");
184 if (output) {
185 output->voltage = output_voltage;
186 }
187}
188
189} // namespace control_loops
190} // namespace frc971