blob: c58822a54a7fd53348a44648cdebb6244247fca0 [file] [log] [blame]
Austin Schuh87c10632017-02-05 19:02:17 -08001#include "y2017/control_loops/superstructure/hood/hood.h"
2
3#include "y2017/constants.h"
4#include "y2017/control_loops/superstructure/hood/hood_integral_plant.h"
5
6namespace y2017 {
7namespace control_loops {
8namespace superstructure {
9namespace hood {
10
Adam Snaider3f5c1e92017-03-24 21:06:03 -070011namespace chrono = ::std::chrono;
12
Austin Schuh87c10632017-02-05 19:02:17 -080013constexpr double Hood::kZeroingVoltage;
14constexpr double Hood::kOperatingVoltage;
Austin Schuh6a90cd92017-02-19 20:55:33 -080015// The tracking error to allow before declaring that we are stuck and reversing
16// direction while zeroing.
17constexpr double kStuckZeroingTrackingError = 0.02;
Austin Schuh87c10632017-02-05 19:02:17 -080018
Austin Schuh6a90cd92017-02-19 20:55:33 -080019IndexPulseProfiledSubsystem::IndexPulseProfiledSubsystem()
20 : ::frc971::control_loops::SingleDOFProfiledSubsystem<
21 ::frc971::zeroing::PulseIndexZeroingEstimator>(
Austin Schuh87c10632017-02-05 19:02:17 -080022 ::std::unique_ptr<
23 ::frc971::control_loops::SimpleCappedStateFeedbackLoop<3, 1, 1>>(
24 new ::frc971::control_loops::SimpleCappedStateFeedbackLoop<
25 3, 1, 1>(MakeIntegralHoodLoop())),
26 constants::GetValues().hood.zeroing, constants::Values::kHoodRange,
27 0.5, 10.0) {}
28
Austin Schuh6a90cd92017-02-19 20:55:33 -080029void IndexPulseProfiledSubsystem::CapGoal(const char *name,
30 Eigen::Matrix<double, 3, 1> *goal) {
31 if (zeroed()) {
32 ::frc971::control_loops::SingleDOFProfiledSubsystem<
33 ::frc971::zeroing::PulseIndexZeroingEstimator>::CapGoal(name, goal);
34 } else {
35 const double kMaxRange = range().upper_hard - range().lower_hard;
36 // Limit the goal to min/max allowable positions much less agressively.
37 // We don't know where the limits are, so we have to let the user move far
38 // enough to find them (and the index pulse which might be right next to
39 // one).
40 if ((*goal)(0, 0) > kMaxRange) {
41 LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
42 kMaxRange);
43 (*goal)(0, 0) = kMaxRange;
44 }
45 if ((*goal)(0, 0) < -kMaxRange) {
46 LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
47 kMaxRange);
48 (*goal)(0, 0) = -kMaxRange;
49 }
50 }
51}
52
53Hood::Hood() {}
54
Austin Schuh87c10632017-02-05 19:02:17 -080055void Hood::Reset() {
56 state_ = State::UNINITIALIZED;
57 profiled_subsystem_.Reset();
Adam Snaider3f5c1e92017-03-24 21:06:03 -070058 last_move_time_ = ::aos::monotonic_clock::min_time;
59 last_position_ = 0;
Austin Schuh87c10632017-02-05 19:02:17 -080060}
61
62void Hood::Iterate(const control_loops::HoodGoal *unsafe_goal,
Austin Schuh6a90cd92017-02-19 20:55:33 -080063 const ::frc971::IndexPosition *position, double *output,
64 ::frc971::control_loops::IndexProfiledJointStatus *status) {
Austin Schuh87c10632017-02-05 19:02:17 -080065 bool disable = output == nullptr;
66 profiled_subsystem_.Correct(*position);
67
68 switch (state_) {
69 case State::UNINITIALIZED:
70 // Wait in the uninitialized state until the hood is initialized.
71 LOG(DEBUG, "Uninitialized, waiting for hood\n");
72 if (profiled_subsystem_.initialized()) {
73 state_ = State::DISABLED_INITIALIZED;
74 }
75 disable = true;
76 break;
77
78 case State::DISABLED_INITIALIZED:
79 // Wait here until we are either fully zeroed while disabled, or we become
80 // enabled.
81 if (disable) {
82 if (profiled_subsystem_.zeroed()) {
83 state_ = State::RUNNING;
84 }
85 } else {
86 state_ = State::ZEROING;
87 }
88
89 // Set the goals to where we are now so when we start back up, we don't
90 // jump.
91 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
92 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -080093 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -080094
95 // We are not ready to start doing anything yet.
96 disable = true;
97 break;
98
99 case State::ZEROING:
100 if (profiled_subsystem_.zeroed()) {
101 // Move the goal to the current goal so we stop moving.
102 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
103 state_ = State::RUNNING;
104 } else if (disable) {
105 state_ = State::DISABLED_INITIALIZED;
106 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800107 const double kRange = profiled_subsystem_.range().upper_hard -
108 profiled_subsystem_.range().lower_hard;
109 // Seek +- the range of motion.
110 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800111 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800112 if (profiled_subsystem_.goal(1, 0) > 0 &&
113 ::std::abs(profiled_subsystem_.position() -
114 profiled_subsystem_.goal(0, 0)) <
115 kStuckZeroingTrackingError) {
116 // And moving up and not stuck. Keep going until we've gone the
117 // full range of motion or get stuck.
118 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800119 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800120 // And no longer moving. Go down to the opposite of the range of
121 // motion.
122 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800123 }
124 } else {
125 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800126 if (profiled_subsystem_.goal(1, 0) < 0 &&
127 ::std::abs(profiled_subsystem_.position() -
128 profiled_subsystem_.goal(0, 0)) <
129 kStuckZeroingTrackingError) {
130 // And moving down and not stuck. Keep going until we've gone the
131 // full range of motion or get stuck.
132 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800133 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800134 // And no longer moving. Go up to the opposite of the range of
135 // motion.
136 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800137 }
138 }
139 }
140 break;
141
142 case State::RUNNING: {
143 if (disable) {
144 // Reset the profile to the current position so it starts from here when
145 // we get re-enabled.
146 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
147 }
148
149 // If we have a goal, go to it. Otherwise stay where we are.
150 if (unsafe_goal) {
151 profiled_subsystem_.AdjustProfile(unsafe_goal->profile_params);
152 profiled_subsystem_.set_unprofiled_goal(unsafe_goal->angle);
153 }
154
155 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800156 if (profiled_subsystem_.CheckHardLimits() ||
157 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800158 state_ = State::ESTOP;
159 }
160 } break;
161
162 case State::ESTOP:
163 LOG(ERROR, "Estop\n");
164 disable = true;
165 break;
166 }
167
168 // Set the voltage limits.
169 const double max_voltage =
170 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
171
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700172 // If we have been in the same position for kNumberCyclesTillNotMoving, make
173 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
174 double error_voltage = max_voltage;
175 if (::std::abs(profiled_subsystem_.position() - last_position_) >
176 kErrorOnPositionTillNotMoving) {
177 // Currently moving. Update time of last move.
178 last_move_time_ = ::aos::monotonic_clock::now();
179 // Save last position.
180 last_position_ = profiled_subsystem_.position();
181 }
182 if (::aos::monotonic_clock::now() > kTimeTillNotMoving + last_move_time_) {
183 error_voltage = kNotMovingVoltage;
184 }
185
186 profiled_subsystem_.set_max_voltage(
187 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800188
189 // Calculate the loops for a cycle.
190 profiled_subsystem_.Update(disable);
191
192 // Write out all the voltages.
193 if (output) {
194 *output = profiled_subsystem_.voltage();
195 }
196
197 // Save debug/internal state.
198 // TODO(austin): Save more.
199 status->zeroed = profiled_subsystem_.zeroed();
200
201 profiled_subsystem_.PopulateStatus(status);
202 status->estopped = (state_ == State::ESTOP);
203 status->state = static_cast<int32_t>(state_);
204}
205
206} // namespace hood
207} // namespace superstructure
208} // namespace control_loops
209} // namespace y2017