blob: 3dcd80656c177e3f0697ffc0242a6f3ae24faf2a [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) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070043 AOS_LOG(WARNING, "Goal %s above limit, %f > %f\n", name, (*goal)(0, 0),
44 kMaxRange);
Austin Schuh6a90cd92017-02-19 20:55:33 -080045 (*goal)(0, 0) = kMaxRange;
46 }
47 if ((*goal)(0, 0) < -kMaxRange) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070048 AOS_LOG(WARNING, "Goal %s below limit, %f < %f\n", name, (*goal)(0, 0),
49 kMaxRange);
Austin Schuh6a90cd92017-02-19 20:55:33 -080050 (*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
Alex Perrycb7da4b2019-08-28 19:35:56 -070064flatbuffers::Offset<frc971::control_loops::IndexProfiledJointStatus>
65Hood::Iterate(const ::aos::monotonic_clock::time_point monotonic_now,
66 const double *unsafe_goal,
67 const frc971::ProfileParameters *unsafe_goal_profile_parameters,
68 const ::frc971::IndexPosition *position, double *output,
69 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh87c10632017-02-05 19:02:17 -080070 bool disable = output == nullptr;
71 profiled_subsystem_.Correct(*position);
72
73 switch (state_) {
74 case State::UNINITIALIZED:
75 // Wait in the uninitialized state until the hood is initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -070076 AOS_LOG(DEBUG, "Uninitialized, waiting for hood\n");
Austin Schuh87c10632017-02-05 19:02:17 -080077 if (profiled_subsystem_.initialized()) {
78 state_ = State::DISABLED_INITIALIZED;
79 }
80 disable = true;
81 break;
82
83 case State::DISABLED_INITIALIZED:
84 // Wait here until we are either fully zeroed while disabled, or we become
85 // enabled.
86 if (disable) {
87 if (profiled_subsystem_.zeroed()) {
88 state_ = State::RUNNING;
89 }
90 } else {
91 state_ = State::ZEROING;
92 }
93
94 // Set the goals to where we are now so when we start back up, we don't
95 // jump.
96 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
97 // Set up the profile to be the zeroing profile.
Austin Schuh6a90cd92017-02-19 20:55:33 -080098 profiled_subsystem_.AdjustProfile(0.30, 1.0);
Austin Schuh87c10632017-02-05 19:02:17 -080099
100 // We are not ready to start doing anything yet.
101 disable = true;
102 break;
103
104 case State::ZEROING:
105 if (profiled_subsystem_.zeroed()) {
106 // Move the goal to the current goal so we stop moving.
107 profiled_subsystem_.set_unprofiled_goal(profiled_subsystem_.goal(0, 0));
108 state_ = State::RUNNING;
109 } else if (disable) {
110 state_ = State::DISABLED_INITIALIZED;
111 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800112 const double kRange = profiled_subsystem_.range().upper_hard -
113 profiled_subsystem_.range().lower_hard;
114 // Seek +- the range of motion.
115 if (profiled_subsystem_.position() > 0) {
Austin Schuh87c10632017-02-05 19:02:17 -0800116 // We are above the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800117 if (profiled_subsystem_.goal(1, 0) > 0 &&
118 ::std::abs(profiled_subsystem_.position() -
119 profiled_subsystem_.goal(0, 0)) <
120 kStuckZeroingTrackingError) {
121 // And moving up and not stuck. Keep going until we've gone the
122 // full range of motion or get stuck.
123 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800124 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800125 // And no longer moving. Go down to the opposite of the range of
126 // motion.
127 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800128 }
129 } else {
130 // We are below the middle.
Austin Schuh6a90cd92017-02-19 20:55:33 -0800131 if (profiled_subsystem_.goal(1, 0) < 0 &&
132 ::std::abs(profiled_subsystem_.position() -
133 profiled_subsystem_.goal(0, 0)) <
134 kStuckZeroingTrackingError) {
135 // And moving down and not stuck. Keep going until we've gone the
136 // full range of motion or get stuck.
137 profiled_subsystem_.set_unprofiled_goal(-kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800138 } else {
Austin Schuh6a90cd92017-02-19 20:55:33 -0800139 // And no longer moving. Go up to the opposite of the range of
140 // motion.
141 profiled_subsystem_.set_unprofiled_goal(kRange);
Austin Schuh87c10632017-02-05 19:02:17 -0800142 }
143 }
144 }
145 break;
146
147 case State::RUNNING: {
148 if (disable) {
149 // Reset the profile to the current position so it starts from here when
150 // we get re-enabled.
151 profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
152 }
153
154 // If we have a goal, go to it. Otherwise stay where we are.
155 if (unsafe_goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700156 profiled_subsystem_.AdjustProfile(unsafe_goal_profile_parameters);
157 profiled_subsystem_.set_unprofiled_goal(*unsafe_goal);
Austin Schuh87c10632017-02-05 19:02:17 -0800158 }
159
160 // ESTOP if we hit the hard limits.
Austin Schuhd93160a2017-03-05 01:00:54 -0800161 if (profiled_subsystem_.CheckHardLimits() ||
162 profiled_subsystem_.error()) {
Austin Schuh87c10632017-02-05 19:02:17 -0800163 state_ = State::ESTOP;
164 }
165 } break;
166
167 case State::ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700168 AOS_LOG(ERROR, "Estop\n");
Austin Schuh87c10632017-02-05 19:02:17 -0800169 disable = true;
170 break;
171 }
172
173 // Set the voltage limits.
174 const double max_voltage =
175 (state_ == State::RUNNING) ? kOperatingVoltage : kZeroingVoltage;
176
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700177 // If we have been in the same position for kNumberCyclesTillNotMoving, make
178 // sure that the kNotMovingVoltage is used instead of kOperatingVoltage.
179 double error_voltage = max_voltage;
180 if (::std::abs(profiled_subsystem_.position() - last_position_) >
181 kErrorOnPositionTillNotMoving) {
182 // Currently moving. Update time of last move.
Austin Schuh92715362019-07-07 20:47:45 -0700183 last_move_time_ = monotonic_now;
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700184 // Save last position.
185 last_position_ = profiled_subsystem_.position();
186 }
Austin Schuh92715362019-07-07 20:47:45 -0700187 if (monotonic_now > kTimeTillNotMoving + last_move_time_) {
Adam Snaider3f5c1e92017-03-24 21:06:03 -0700188 error_voltage = kNotMovingVoltage;
189 }
190
191 profiled_subsystem_.set_max_voltage(
192 {{::std::min(max_voltage, error_voltage)}});
Austin Schuh87c10632017-02-05 19:02:17 -0800193
194 // Calculate the loops for a cycle.
195 profiled_subsystem_.Update(disable);
196
197 // Write out all the voltages.
198 if (output) {
199 *output = profiled_subsystem_.voltage();
200 }
201
202 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700203 frc971::control_loops::IndexProfiledJointStatus::Builder status_builder =
204 profiled_subsystem_.BuildStatus<
205 frc971::control_loops::IndexProfiledJointStatus::Builder>(fbb);
Austin Schuh87c10632017-02-05 19:02:17 -0800206 // TODO(austin): Save more.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700207 status_builder.add_estopped((state_ == State::ESTOP));
208 status_builder.add_state(static_cast<int32_t>(state_));
Austin Schuh87c10632017-02-05 19:02:17 -0800209
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210 return status_builder.Finish();
Austin Schuh87c10632017-02-05 19:02:17 -0800211}
212
213} // namespace hood
214} // namespace superstructure
215} // namespace control_loops
216} // namespace y2017