blob: a8c1aa6cb4668cae9f3457b7ef37a3c67a9f88c7 [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
Stephan Pleinesf63bde82024-01-13 15:59:33 -08006namespace y2017::control_loops::superstructure::hood {
Austin Schuh87c10632017-02-05 19:02:17 -08007
Adam Snaider3f5c1e92017-03-24 21:06:03 -07008namespace chrono = ::std::chrono;
9
Austin Schuh87c10632017-02-05 19:02:17 -080010constexpr double Hood::kZeroingVoltage;
11constexpr double Hood::kOperatingVoltage;
Philipp Schrader8e3ac0f2017-04-09 23:36:17 +000012constexpr ::aos::monotonic_clock::duration Hood::kTimeTillNotMoving;
13
Austin Schuh6a90cd92017-02-19 20:55:33 -080014// The tracking error to allow before declaring that we are stuck and reversing
15// direction while zeroing.
16constexpr double kStuckZeroingTrackingError = 0.02;
Austin Schuh87c10632017-02-05 19:02:17 -080017
Austin Schuh6a90cd92017-02-19 20:55:33 -080018IndexPulseProfiledSubsystem::IndexPulseProfiledSubsystem()
19 : ::frc971::control_loops::SingleDOFProfiledSubsystem<
20 ::frc971::zeroing::PulseIndexZeroingEstimator>(
Austin Schuh87c10632017-02-05 19:02:17 -080021 ::std::unique_ptr<
22 ::frc971::control_loops::SimpleCappedStateFeedbackLoop<3, 1, 1>>(
23 new ::frc971::control_loops::SimpleCappedStateFeedbackLoop<
24 3, 1, 1>(MakeIntegralHoodLoop())),
25 constants::GetValues().hood.zeroing, constants::Values::kHoodRange,
26 0.5, 10.0) {}
27
Austin Schuh6a90cd92017-02-19 20:55:33 -080028void IndexPulseProfiledSubsystem::CapGoal(const char *name,
Sabina Davis0e484f92020-02-23 17:47:53 -080029 Eigen::Matrix<double, 3, 1> *goal,
30 bool print) {
Austin Schuh6a90cd92017-02-19 20:55:33 -080031 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) {
Sabina Davis0e484f92020-02-23 17:47:53 -080041 if (print) {
42 AOS_LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
43 kMaxRange);
44 }
Austin Schuh6a90cd92017-02-19 20:55:33 -080045 (*goal)(0, 0) = kMaxRange;
46 }
47 if ((*goal)(0, 0) < -kMaxRange) {
Sabina Davis0e484f92020-02-23 17:47:53 -080048 if (print) {
49 AOS_LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
50 kMaxRange);
51 }
Austin Schuh6a90cd92017-02-19 20:55:33 -080052 (*goal)(0, 0) = -kMaxRange;
53 }
54 }
55}
56
57Hood::Hood() {}
58
Austin Schuh87c10632017-02-05 19:02:17 -080059void Hood::Reset() {
60 state_ = State::UNINITIALIZED;
61 profiled_subsystem_.Reset();
Adam Snaider3f5c1e92017-03-24 21:06:03 -070062 last_move_time_ = ::aos::monotonic_clock::min_time;
63 last_position_ = 0;
Austin Schuh87c10632017-02-05 19:02:17 -080064}
65
Alex Perrycb7da4b2019-08-28 19:35:56 -070066flatbuffers::Offset<frc971::control_loops::IndexProfiledJointStatus>
67Hood::Iterate(const ::aos::monotonic_clock::time_point monotonic_now,
68 const double *unsafe_goal,
69 const frc971::ProfileParameters *unsafe_goal_profile_parameters,
70 const ::frc971::IndexPosition *position, double *output,
71 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh87c10632017-02-05 19:02:17 -080072 bool disable = output == nullptr;
73 profiled_subsystem_.Correct(*position);
74
75 switch (state_) {
76 case State::UNINITIALIZED:
77 // Wait in the uninitialized state until the hood is initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -070078 AOS_LOG(DEBUG, "Uninitialized, waiting for hood\n");
Austin Schuh87c10632017-02-05 19:02:17 -080079 if (profiled_subsystem_.initialized()) {
80 state_ = State::DISABLED_INITIALIZED;
81 }
82 disable = true;
83 break;
84
85 case State::DISABLED_INITIALIZED:
86 // Wait here until we are either fully zeroed while disabled, or we become
87 // enabled.
88 if (disable) {
89 if (profiled_subsystem_.zeroed()) {
90 state_ = State::RUNNING;
91 }
92 } else {
93 state_ = State::ZEROING;
94 }
95
96 // Set the goals to where we are now so when we start back up, we don't
97 // jump.
98 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
99 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800100 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -0800101
102 // We are not ready to start doing anything yet.
103 disable = true;
104 break;
105
106 case State::ZEROING:
107 if (profiled_subsystem_.zeroed()) {
108 // Move the goal to the current goal so we stop moving.
109 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
110 state_ = State::RUNNING;
111 } else if (disable) {
112 state_ = State::DISABLED_INITIALIZED;
113 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800114 const double kRange = profiled_subsystem_.range().upper_hard -
115 profiled_subsystem_.range().lower_hard;
116 // Seek +- the range of motion.
117 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800118 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800119 if (profiled_subsystem_.goal(1, 0) > 0 &&
120 ::std::abs(profiled_subsystem_.position() -
121 profiled_subsystem_.goal(0, 0)) <
122 kStuckZeroingTrackingError) {
123 // And moving up and not stuck. Keep going until we've gone the
124 // full range of motion or get stuck.
125 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800126 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800127 // And no longer moving. Go down to the opposite of the range of
128 // motion.
129 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800130 }
131 } else {
132 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800133 if (profiled_subsystem_.goal(1, 0) < 0 &&
134 ::std::abs(profiled_subsystem_.position() -
135 profiled_subsystem_.goal(0, 0)) <
136 kStuckZeroingTrackingError) {
137 // And moving down and not stuck. Keep going until we've gone the
138 // full range of motion or get stuck.
139 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800140 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800141 // And no longer moving. Go up to the opposite of the range of
142 // motion.
143 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800144 }
145 }
146 }
147 break;
148
149 case State::RUNNING: {
150 if (disable) {
151 // Reset the profile to the current position so it starts from here when
152 // we get re-enabled.
153 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
154 }
155
156 // If we have a goal, go to it. Otherwise stay where we are.
157 if (unsafe_goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700158 profiled_subsystem_.AdjustProfile(unsafe_goal_profile_parameters);
159 profiled_subsystem_.set_unprofiled_goal(*unsafe_goal);
Austin Schuh87c10632017-02-05 19:02:17 -0800160 }
161
162 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800163 if (profiled_subsystem_.CheckHardLimits() ||
164 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800165 state_ = State::ESTOP;
166 }
167 } break;
168
169 case State::ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700170 AOS_LOG(ERROR, "Estop\n");
Austin Schuh87c10632017-02-05 19:02:17 -0800171 disable = true;
172 break;
173 }
174
175 // Set the voltage limits.
176 const double max_voltage =
177 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
178
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700179 // If we have been in the same position for kNumberCyclesTillNotMoving, make
180 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
181 double error_voltage = max_voltage;
182 if (::std::abs(profiled_subsystem_.position() - last_position_) >
183 kErrorOnPositionTillNotMoving) {
184 // Currently moving. Update time of last move.
Austin Schuh92715362019-07-07 20:47:45 -0700185 last_move_time_ = monotonic_now;
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700186 // Save last position.
187 last_position_ = profiled_subsystem_.position();
188 }
Austin Schuh92715362019-07-07 20:47:45 -0700189 if (monotonic_now > kTimeTillNotMoving + last_move_time_) {
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700190 error_voltage = kNotMovingVoltage;
191 }
192
193 profiled_subsystem_.set_max_voltage(
194 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800195
196 // Calculate the loops for a cycle.
197 profiled_subsystem_.Update(disable);
198
199 // Write out all the voltages.
200 if (output) {
201 *output = profiled_subsystem_.voltage();
202 }
203
204 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700205 frc971::control_loops::IndexProfiledJointStatus::Builder status_builder =
206 profiled_subsystem_.BuildStatus<
207 frc971::control_loops::IndexProfiledJointStatus::Builder>(fbb);
Austin Schuh87c10632017-02-05 19:02:17 -0800208 // TODO(austin): Save more.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700209 status_builder.add_estopped((state_ == State::ESTOP));
210 status_builder.add_state(static_cast<int32_t>(state_));
Austin Schuh87c10632017-02-05 19:02:17 -0800211
Alex Perrycb7da4b2019-08-28 19:35:56 -0700212 return status_builder.Finish();
Austin Schuh87c10632017-02-05 19:02:17 -0800213}
214
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800215} // namespace y2017::control_loops::superstructure::hood