blob: 862a32c0ae5b0bb4ce7f7542c2cded2cc402c4a6 [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
Austin Schuh92715362019-07-07 20:47:45 -070064void Hood::Iterate(const ::aos::monotonic_clock::time_point monotonic_now,
65 const control_loops::HoodGoal *unsafe_goal,
Austin Schuh6a90cd92017-02-19 20:55:33 -080066 const ::frc971::IndexPosition *position, double *output,
67 ::frc971::control_loops::IndexProfiledJointStatus *status) {
Austin Schuh87c10632017-02-05 19:02:17 -080068 bool disable = output == nullptr;
69 profiled_subsystem_.Correct(*position);
70
71 switch (state_) {
72 case State::UNINITIALIZED:
73 // Wait in the uninitialized state until the hood is initialized.
74 LOG(DEBUG, "Uninitialized, waiting for hood\n");
75 if (profiled_subsystem_.initialized()) {
76 state_ = State::DISABLED_INITIALIZED;
77 }
78 disable = true;
79 break;
80
81 case State::DISABLED_INITIALIZED:
82 // Wait here until we are either fully zeroed while disabled, or we become
83 // enabled.
84 if (disable) {
85 if (profiled_subsystem_.zeroed()) {
86 state_ = State::RUNNING;
87 }
88 } else {
89 state_ = State::ZEROING;
90 }
91
92 // Set the goals to where we are now so when we start back up, we don't
93 // jump.
94 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
95 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -080096 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -080097
98 // We are not ready to start doing anything yet.
99 disable = true;
100 break;
101
102 case State::ZEROING:
103 if (profiled_subsystem_.zeroed()) {
104 // Move the goal to the current goal so we stop moving.
105 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
106 state_ = State::RUNNING;
107 } else if (disable) {
108 state_ = State::DISABLED_INITIALIZED;
109 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800110 const double kRange = profiled_subsystem_.range().upper_hard -
111 profiled_subsystem_.range().lower_hard;
112 // Seek +- the range of motion.
113 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800114 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800115 if (profiled_subsystem_.goal(1, 0) > 0 &&
116 ::std::abs(profiled_subsystem_.position() -
117 profiled_subsystem_.goal(0, 0)) <
118 kStuckZeroingTrackingError) {
119 // And moving up and not stuck. Keep going until we've gone the
120 // full range of motion or get stuck.
121 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800122 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800123 // And no longer moving. Go down to the opposite of the range of
124 // motion.
125 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800126 }
127 } else {
128 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800129 if (profiled_subsystem_.goal(1, 0) < 0 &&
130 ::std::abs(profiled_subsystem_.position() -
131 profiled_subsystem_.goal(0, 0)) <
132 kStuckZeroingTrackingError) {
133 // And moving down and not stuck. Keep going until we've gone the
134 // full range of motion or get stuck.
135 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800136 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800137 // And no longer moving. Go up to the opposite of the range of
138 // motion.
139 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800140 }
141 }
142 }
143 break;
144
145 case State::RUNNING: {
146 if (disable) {
147 // Reset the profile to the current position so it starts from here when
148 // we get re-enabled.
149 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
150 }
151
152 // If we have a goal, go to it. Otherwise stay where we are.
153 if (unsafe_goal) {
154 profiled_subsystem_.AdjustProfile(unsafe_goal->profile_params);
155 profiled_subsystem_.set_unprofiled_goal(unsafe_goal->angle);
156 }
157
158 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800159 if (profiled_subsystem_.CheckHardLimits() ||
160 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800161 state_ = State::ESTOP;
162 }
163 } break;
164
165 case State::ESTOP:
166 LOG(ERROR, "Estop\n");
167 disable = true;
168 break;
169 }
170
171 // Set the voltage limits.
172 const double max_voltage =
173 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
174
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700175 // If we have been in the same position for kNumberCyclesTillNotMoving, make
176 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
177 double error_voltage = max_voltage;
178 if (::std::abs(profiled_subsystem_.position() - last_position_) >
179 kErrorOnPositionTillNotMoving) {
180 // Currently moving. Update time of last move.
Austin Schuh92715362019-07-07 20:47:45 -0700181 last_move_time_ = monotonic_now;
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700182 // Save last position.
183 last_position_ = profiled_subsystem_.position();
184 }
Austin Schuh92715362019-07-07 20:47:45 -0700185 if (monotonic_now > kTimeTillNotMoving + last_move_time_) {
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700186 error_voltage = kNotMovingVoltage;
187 }
188
189 profiled_subsystem_.set_max_voltage(
190 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800191
192 // Calculate the loops for a cycle.
193 profiled_subsystem_.Update(disable);
194
195 // Write out all the voltages.
196 if (output) {
197 *output = profiled_subsystem_.voltage();
198 }
199
200 // Save debug/internal state.
201 // TODO(austin): Save more.
202 status->zeroed = profiled_subsystem_.zeroed();
203
204 profiled_subsystem_.PopulateStatus(status);
205 status->estopped = (state_ == State::ESTOP);
206 status->state = static_cast<int32_t>(state_);
207}
208
209} // namespace hood
210} // namespace superstructure
211} // namespace control_loops
212} // namespace y2017