blob: 4f1a4eb3b33aa858f25b10f53ecee0615205b017 [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,
Sabina Davis0e484f92020-02-23 17:47:53 -080032 Eigen::Matrix<double, 3, 1> *goal,
33 bool print) {
Austin Schuh6a90cd92017-02-19 20:55:33 -080034 if (zeroed()) {
35 ::frc971::control_loops::SingleDOFProfiledSubsystem<
36 ::frc971::zeroing::PulseIndexZeroingEstimator>::CapGoal(name, goal);
37 } else {
38 const double kMaxRange = range().upper_hard - range().lower_hard;
39 // Limit the goal to min/max allowable positions much less agressively.
40 // We don't know where the limits are, so we have to let the user move far
41 // enough to find them (and the index pulse which might be right next to
42 // one).
43 if ((*goal)(0, 0) > kMaxRange) {
Sabina Davis0e484f92020-02-23 17:47:53 -080044 if (print) {
45 AOS_LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
46 kMaxRange);
47 }
Austin Schuh6a90cd92017-02-19 20:55:33 -080048 (*goal)(0, 0) = kMaxRange;
49 }
50 if ((*goal)(0, 0) < -kMaxRange) {
Sabina Davis0e484f92020-02-23 17:47:53 -080051 if (print) {
52 AOS_LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
53 kMaxRange);
54 }
Austin Schuh6a90cd92017-02-19 20:55:33 -080055 (*goal)(0, 0) = -kMaxRange;
56 }
57 }
58}
59
60Hood::Hood() {}
61
Austin Schuh87c10632017-02-05 19:02:17 -080062void Hood::Reset() {
63 state_ = State::UNINITIALIZED;
64 profiled_subsystem_.Reset();
Adam Snaider3f5c1e92017-03-24 21:06:03 -070065 last_move_time_ = ::aos::monotonic_clock::min_time;
66 last_position_ = 0;
Austin Schuh87c10632017-02-05 19:02:17 -080067}
68
Alex Perrycb7da4b2019-08-28 19:35:56 -070069flatbuffers::Offset<frc971::control_loops::IndexProfiledJointStatus>
70Hood::Iterate(const ::aos::monotonic_clock::time_point monotonic_now,
71 const double *unsafe_goal,
72 const frc971::ProfileParameters *unsafe_goal_profile_parameters,
73 const ::frc971::IndexPosition *position, double *output,
74 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh87c10632017-02-05 19:02:17 -080075 bool disable = output == nullptr;
76 profiled_subsystem_.Correct(*position);
77
78 switch (state_) {
79 case State::UNINITIALIZED:
80 // Wait in the uninitialized state until the hood is initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -070081 AOS_LOG(DEBUG, "Uninitialized, waiting for hood\n");
Austin Schuh87c10632017-02-05 19:02:17 -080082 if (profiled_subsystem_.initialized()) {
83 state_ = State::DISABLED_INITIALIZED;
84 }
85 disable = true;
86 break;
87
88 case State::DISABLED_INITIALIZED:
89 // Wait here until we are either fully zeroed while disabled, or we become
90 // enabled.
91 if (disable) {
92 if (profiled_subsystem_.zeroed()) {
93 state_ = State::RUNNING;
94 }
95 } else {
96 state_ = State::ZEROING;
97 }
98
99 // Set the goals to where we are now so when we start back up, we don't
100 // jump.
101 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
102 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800103 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -0800104
105 // We are not ready to start doing anything yet.
106 disable = true;
107 break;
108
109 case State::ZEROING:
110 if (profiled_subsystem_.zeroed()) {
111 // Move the goal to the current goal so we stop moving.
112 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
113 state_ = State::RUNNING;
114 } else if (disable) {
115 state_ = State::DISABLED_INITIALIZED;
116 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800117 const double kRange = profiled_subsystem_.range().upper_hard -
118 profiled_subsystem_.range().lower_hard;
119 // Seek +- the range of motion.
120 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800121 // We are above 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 up 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 down 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 } else {
135 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800136 if (profiled_subsystem_.goal(1, 0) < 0 &&
137 ::std::abs(profiled_subsystem_.position() -
138 profiled_subsystem_.goal(0, 0)) <
139 kStuckZeroingTrackingError) {
140 // And moving down and not stuck. Keep going until we've gone the
141 // full range of motion or get stuck.
142 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800143 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800144 // And no longer moving. Go up to the opposite of the range of
145 // motion.
146 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800147 }
148 }
149 }
150 break;
151
152 case State::RUNNING: {
153 if (disable) {
154 // Reset the profile to the current position so it starts from here when
155 // we get re-enabled.
156 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
157 }
158
159 // If we have a goal, go to it. Otherwise stay where we are.
160 if (unsafe_goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700161 profiled_subsystem_.AdjustProfile(unsafe_goal_profile_parameters);
162 profiled_subsystem_.set_unprofiled_goal(*unsafe_goal);
Austin Schuh87c10632017-02-05 19:02:17 -0800163 }
164
165 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800166 if (profiled_subsystem_.CheckHardLimits() ||
167 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800168 state_ = State::ESTOP;
169 }
170 } break;
171
172 case State::ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700173 AOS_LOG(ERROR, "Estop\n");
Austin Schuh87c10632017-02-05 19:02:17 -0800174 disable = true;
175 break;
176 }
177
178 // Set the voltage limits.
179 const double max_voltage =
180 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
181
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700182 // If we have been in the same position for kNumberCyclesTillNotMoving, make
183 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
184 double error_voltage = max_voltage;
185 if (::std::abs(profiled_subsystem_.position() - last_position_) >
186 kErrorOnPositionTillNotMoving) {
187 // Currently moving. Update time of last move.
Austin Schuh92715362019-07-07 20:47:45 -0700188 last_move_time_ = monotonic_now;
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700189 // Save last position.
190 last_position_ = profiled_subsystem_.position();
191 }
Austin Schuh92715362019-07-07 20:47:45 -0700192 if (monotonic_now > kTimeTillNotMoving + last_move_time_) {
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700193 error_voltage = kNotMovingVoltage;
194 }
195
196 profiled_subsystem_.set_max_voltage(
197 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800198
199 // Calculate the loops for a cycle.
200 profiled_subsystem_.Update(disable);
201
202 // Write out all the voltages.
203 if (output) {
204 *output = profiled_subsystem_.voltage();
205 }
206
207 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700208 frc971::control_loops::IndexProfiledJointStatus::Builder status_builder =
209 profiled_subsystem_.BuildStatus<
210 frc971::control_loops::IndexProfiledJointStatus::Builder>(fbb);
Austin Schuh87c10632017-02-05 19:02:17 -0800211 // TODO(austin): Save more.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700212 status_builder.add_estopped((state_ == State::ESTOP));
213 status_builder.add_state(static_cast<int32_t>(state_));
Austin Schuh87c10632017-02-05 19:02:17 -0800214
Alex Perrycb7da4b2019-08-28 19:35:56 -0700215 return status_builder.Finish();
Austin Schuh87c10632017-02-05 19:02:17 -0800216}
217
218} // namespace hood
219} // namespace superstructure
220} // namespace control_loops
221} // namespace y2017