blob: b5acba0b6c57c0661729392a5d96268e9eb18bd7 [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
11constexpr double Hood::kZeroingVoltage;
12constexpr double Hood::kOperatingVoltage;
Austin Schuh6a90cd92017-02-19 20:55:33 -080013// The tracking error to allow before declaring that we are stuck and reversing
14// direction while zeroing.
15constexpr double kStuckZeroingTrackingError = 0.02;
Austin Schuh87c10632017-02-05 19:02:17 -080016
Austin Schuh6a90cd92017-02-19 20:55:33 -080017IndexPulseProfiledSubsystem::IndexPulseProfiledSubsystem()
18 : ::frc971::control_loops::SingleDOFProfiledSubsystem<
19 ::frc971::zeroing::PulseIndexZeroingEstimator>(
Austin Schuh87c10632017-02-05 19:02:17 -080020 ::std::unique_ptr<
21 ::frc971::control_loops::SimpleCappedStateFeedbackLoop<3, 1, 1>>(
22 new ::frc971::control_loops::SimpleCappedStateFeedbackLoop<
23 3, 1, 1>(MakeIntegralHoodLoop())),
24 constants::GetValues().hood.zeroing, constants::Values::kHoodRange,
25 0.5, 10.0) {}
26
Austin Schuh6a90cd92017-02-19 20:55:33 -080027void IndexPulseProfiledSubsystem::CapGoal(const char *name,
28 Eigen::Matrix<double, 3, 1> *goal) {
29 if (zeroed()) {
30 ::frc971::control_loops::SingleDOFProfiledSubsystem<
31 ::frc971::zeroing::PulseIndexZeroingEstimator>::CapGoal(name, goal);
32 } else {
33 const double kMaxRange = range().upper_hard - range().lower_hard;
34 // Limit the goal to min/max allowable positions much less agressively.
35 // We don't know where the limits are, so we have to let the user move far
36 // enough to find them (and the index pulse which might be right next to
37 // one).
38 if ((*goal)(0, 0) > kMaxRange) {
39 LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
40 kMaxRange);
41 (*goal)(0, 0) = kMaxRange;
42 }
43 if ((*goal)(0, 0) < -kMaxRange) {
44 LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
45 kMaxRange);
46 (*goal)(0, 0) = -kMaxRange;
47 }
48 }
49}
50
51Hood::Hood() {}
52
Austin Schuh87c10632017-02-05 19:02:17 -080053void Hood::Reset() {
54 state_ = State::UNINITIALIZED;
55 profiled_subsystem_.Reset();
56}
57
58void Hood::Iterate(const control_loops::HoodGoal *unsafe_goal,
Austin Schuh6a90cd92017-02-19 20:55:33 -080059 const ::frc971::IndexPosition *position, double *output,
60 ::frc971::control_loops::IndexProfiledJointStatus *status) {
Austin Schuh87c10632017-02-05 19:02:17 -080061 bool disable = output == nullptr;
62 profiled_subsystem_.Correct(*position);
63
64 switch (state_) {
65 case State::UNINITIALIZED:
66 // Wait in the uninitialized state until the hood is initialized.
67 LOG(DEBUG, "Uninitialized, waiting for hood\n");
68 if (profiled_subsystem_.initialized()) {
69 state_ = State::DISABLED_INITIALIZED;
70 }
71 disable = true;
72 break;
73
74 case State::DISABLED_INITIALIZED:
75 // Wait here until we are either fully zeroed while disabled, or we become
76 // enabled.
77 if (disable) {
78 if (profiled_subsystem_.zeroed()) {
79 state_ = State::RUNNING;
80 }
81 } else {
82 state_ = State::ZEROING;
83 }
84
85 // Set the goals to where we are now so when we start back up, we don't
86 // jump.
87 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
88 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -080089 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -080090
91 // We are not ready to start doing anything yet.
92 disable = true;
93 break;
94
95 case State::ZEROING:
96 if (profiled_subsystem_.zeroed()) {
97 // Move the goal to the current goal so we stop moving.
98 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
99 state_ = State::RUNNING;
100 } else if (disable) {
101 state_ = State::DISABLED_INITIALIZED;
102 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800103 const double kRange = profiled_subsystem_.range().upper_hard -
104 profiled_subsystem_.range().lower_hard;
105 // Seek +- the range of motion.
106 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800107 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800108 if (profiled_subsystem_.goal(1, 0) > 0 &&
109 ::std::abs(profiled_subsystem_.position() -
110 profiled_subsystem_.goal(0, 0)) <
111 kStuckZeroingTrackingError) {
112 // And moving up and not stuck. Keep going until we've gone the
113 // full range of motion or get stuck.
114 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800115 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800116 // And no longer moving. Go down to the opposite of the range of
117 // motion.
118 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800119 }
120 } else {
121 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800122 if (profiled_subsystem_.goal(1, 0) < 0 &&
123 ::std::abs(profiled_subsystem_.position() -
124 profiled_subsystem_.goal(0, 0)) <
125 kStuckZeroingTrackingError) {
126 // And moving down and not stuck. Keep going until we've gone the
127 // full range of motion or get stuck.
128 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800129 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800130 // And no longer moving. Go up to the opposite of the range of
131 // motion.
132 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800133 }
134 }
135 }
136 break;
137
138 case State::RUNNING: {
139 if (disable) {
140 // Reset the profile to the current position so it starts from here when
141 // we get re-enabled.
142 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
143 }
144
145 // If we have a goal, go to it. Otherwise stay where we are.
146 if (unsafe_goal) {
147 profiled_subsystem_.AdjustProfile(unsafe_goal->profile_params);
148 profiled_subsystem_.set_unprofiled_goal(unsafe_goal->angle);
149 }
150
151 // ESTOP if we hit the hard limits.
152 if (profiled_subsystem_.CheckHardLimits()) {
153 state_ = State::ESTOP;
154 }
155 } break;
156
157 case State::ESTOP:
158 LOG(ERROR, "Estop\n");
159 disable = true;
160 break;
161 }
162
163 // Set the voltage limits.
164 const double max_voltage =
165 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
166
167 profiled_subsystem_.set_max_voltage({{max_voltage}});
168
169 // Calculate the loops for a cycle.
170 profiled_subsystem_.Update(disable);
171
172 // Write out all the voltages.
173 if (output) {
174 *output = profiled_subsystem_.voltage();
175 }
176
177 // Save debug/internal state.
178 // TODO(austin): Save more.
179 status->zeroed = profiled_subsystem_.zeroed();
180
181 profiled_subsystem_.PopulateStatus(status);
182 status->estopped = (state_ == State::ESTOP);
183 status->state = static_cast<int32_t>(state_);
184}
185
186} // namespace hood
187} // namespace superstructure
188} // namespace control_loops
189} // namespace y2017