blob: f19bbf727703e1591c7c14000e37b7a35afc75c3 [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;
Philipp Schrader8e3ac0f2017-04-09 23:36:17 +000015constexpr ::aos::monotonic_clock::duration Hood::kTimeTillNotMoving;
16
Austin Schuh6a90cd92017-02-19 20:55:33 -080017// The tracking error to allow before declaring that we are stuck and reversing
18// direction while zeroing.
19constexpr double kStuckZeroingTrackingError = 0.02;
Austin Schuh87c10632017-02-05 19:02:17 -080020
Austin Schuh6a90cd92017-02-19 20:55:33 -080021IndexPulseProfiledSubsystem::IndexPulseProfiledSubsystem()
22 : ::frc971::control_loops::SingleDOFProfiledSubsystem<
23 ::frc971::zeroing::PulseIndexZeroingEstimator>(
Austin Schuh87c10632017-02-05 19:02:17 -080024 ::std::unique_ptr<
25 ::frc971::control_loops::SimpleCappedStateFeedbackLoop<3, 1, 1>>(
26 new ::frc971::control_loops::SimpleCappedStateFeedbackLoop<
27 3, 1, 1>(MakeIntegralHoodLoop())),
28 constants::GetValues().hood.zeroing, constants::Values::kHoodRange,
29 0.5, 10.0) {}
30
Austin Schuh6a90cd92017-02-19 20:55:33 -080031void IndexPulseProfiledSubsystem::CapGoal(const char *name,
32 Eigen::Matrix<double, 3, 1> *goal) {
33 if (zeroed()) {
34 ::frc971::control_loops::SingleDOFProfiledSubsystem<
35 ::frc971::zeroing::PulseIndexZeroingEstimator>::CapGoal(name, goal);
36 } else {
37 const double kMaxRange = range().upper_hard - range().lower_hard;
38 // Limit the goal to min/max allowable positions much less agressively.
39 // We don't know where the limits are, so we have to let the user move far
40 // enough to find them (and the index pulse which might be right next to
41 // one).
42 if ((*goal)(0, 0) > kMaxRange) {
43 LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
44 kMaxRange);
45 (*goal)(0, 0) = kMaxRange;
46 }
47 if ((*goal)(0, 0) < -kMaxRange) {
48 LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
49 kMaxRange);
50 (*goal)(0, 0) = -kMaxRange;
51 }
52 }
53}
54
55Hood::Hood() {}
56
Austin Schuh87c10632017-02-05 19:02:17 -080057void Hood::Reset() {
58 state_ = State::UNINITIALIZED;
59 profiled_subsystem_.Reset();
Adam Snaider3f5c1e92017-03-24 21:06:03 -070060 last_move_time_ = ::aos::monotonic_clock::min_time;
61 last_position_ = 0;
Austin Schuh87c10632017-02-05 19:02:17 -080062}
63
64void Hood::Iterate(const control_loops::HoodGoal *unsafe_goal,
Austin Schuh6a90cd92017-02-19 20:55:33 -080065 const ::frc971::IndexPosition *position, double *output,
66 ::frc971::control_loops::IndexProfiledJointStatus *status) {
Austin Schuh87c10632017-02-05 19:02:17 -080067 bool disable = output == nullptr;
68 profiled_subsystem_.Correct(*position);
69
70 switch (state_) {
71 case State::UNINITIALIZED:
72 // Wait in the uninitialized state until the hood is initialized.
73 LOG(DEBUG, "Uninitialized, waiting for hood\n");
74 if (profiled_subsystem_.initialized()) {
75 state_ = State::DISABLED_INITIALIZED;
76 }
77 disable = true;
78 break;
79
80 case State::DISABLED_INITIALIZED:
81 // Wait here until we are either fully zeroed while disabled, or we become
82 // enabled.
83 if (disable) {
84 if (profiled_subsystem_.zeroed()) {
85 state_ = State::RUNNING;
86 }
87 } else {
88 state_ = State::ZEROING;
89 }
90
91 // Set the goals to where we are now so when we start back up, we don't
92 // jump.
93 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
94 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -080095 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -080096
97 // We are not ready to start doing anything yet.
98 disable = true;
99 break;
100
101 case State::ZEROING:
102 if (profiled_subsystem_.zeroed()) {
103 // Move the goal to the current goal so we stop moving.
104 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
105 state_ = State::RUNNING;
106 } else if (disable) {
107 state_ = State::DISABLED_INITIALIZED;
108 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800109 const double kRange = profiled_subsystem_.range().upper_hard -
110 profiled_subsystem_.range().lower_hard;
111 // Seek +- the range of motion.
112 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800113 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800114 if (profiled_subsystem_.goal(1, 0) > 0 &&
115 ::std::abs(profiled_subsystem_.position() -
116 profiled_subsystem_.goal(0, 0)) <
117 kStuckZeroingTrackingError) {
118 // And moving up and not stuck. Keep going until we've gone the
119 // full range of motion or get stuck.
120 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800121 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800122 // And no longer moving. Go down to the opposite of the range of
123 // motion.
124 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800125 }
126 } else {
127 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800128 if (profiled_subsystem_.goal(1, 0) < 0 &&
129 ::std::abs(profiled_subsystem_.position() -
130 profiled_subsystem_.goal(0, 0)) <
131 kStuckZeroingTrackingError) {
132 // And moving down and not stuck. Keep going until we've gone the
133 // full range of motion or get stuck.
134 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800135 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800136 // And no longer moving. Go up to the opposite of the range of
137 // motion.
138 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800139 }
140 }
141 }
142 break;
143
144 case State::RUNNING: {
145 if (disable) {
146 // Reset the profile to the current position so it starts from here when
147 // we get re-enabled.
148 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
149 }
150
151 // If we have a goal, go to it. Otherwise stay where we are.
152 if (unsafe_goal) {
153 profiled_subsystem_.AdjustProfile(unsafe_goal->profile_params);
154 profiled_subsystem_.set_unprofiled_goal(unsafe_goal->angle);
155 }
156
157 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800158 if (profiled_subsystem_.CheckHardLimits() ||
159 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800160 state_ = State::ESTOP;
161 }
162 } break;
163
164 case State::ESTOP:
165 LOG(ERROR, "Estop\n");
166 disable = true;
167 break;
168 }
169
170 // Set the voltage limits.
171 const double max_voltage =
172 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
173
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700174 // If we have been in the same position for kNumberCyclesTillNotMoving, make
175 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
176 double error_voltage = max_voltage;
177 if (::std::abs(profiled_subsystem_.position() - last_position_) >
178 kErrorOnPositionTillNotMoving) {
179 // Currently moving. Update time of last move.
180 last_move_time_ = ::aos::monotonic_clock::now();
181 // Save last position.
182 last_position_ = profiled_subsystem_.position();
183 }
184 if (::aos::monotonic_clock::now() > kTimeTillNotMoving + last_move_time_) {
185 error_voltage = kNotMovingVoltage;
186 }
187
188 profiled_subsystem_.set_max_voltage(
189 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800190
191 // Calculate the loops for a cycle.
192 profiled_subsystem_.Update(disable);
193
194 // Write out all the voltages.
195 if (output) {
196 *output = profiled_subsystem_.voltage();
197 }
198
199 // Save debug/internal state.
200 // TODO(austin): Save more.
201 status->zeroed = profiled_subsystem_.zeroed();
202
203 profiled_subsystem_.PopulateStatus(status);
204 status->estopped = (state_ == State::ESTOP);
205 status->state = static_cast<int32_t>(state_);
206}
207
208} // namespace hood
209} // namespace superstructure
210} // namespace control_loops
211} // namespace y2017