blob: ec383ad6ed58e80ab2d586b7068917dc2eddda0c [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#ifndef Y2014_CONTROL_LOOPS_shooter_shooter_H_
2#define Y2014_CONTROL_LOOPS_shooter_shooter_H_
3
4#include <memory>
5
James Kuszmaul61750662021-06-21 21:32:33 -07006#include "frc971/control_loops/control_loop.h"
John Park33858a32018-09-28 23:05:48 -07007#include "aos/time/time.h"
James Kuszmaul61750662021-06-21 21:32:33 -07008#include "frc971/control_loops/state_feedback_loop.h"
Brian Silverman17f503e2015-08-02 18:17:18 -07009#include "y2014/constants.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070010#include "y2014/control_loops/shooter/shooter_goal_generated.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070011#include "y2014/control_loops/shooter/shooter_motor_plant.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070012#include "y2014/control_loops/shooter/shooter_output_generated.h"
13#include "y2014/control_loops/shooter/shooter_position_generated.h"
14#include "y2014/control_loops/shooter/shooter_status_generated.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070015
Austin Schuh24957102015-11-28 16:04:40 -080016namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070017namespace control_loops {
Alex Perrycb7da4b2019-08-28 19:35:56 -070018namespace shooter {
Brian Silverman17f503e2015-08-02 18:17:18 -070019namespace testing {
20class ShooterTest_UnloadWindupPositive_Test;
21class ShooterTest_UnloadWindupNegative_Test;
22class ShooterTest_RezeroWhileUnloading_Test;
James Kuszmaul61750662021-06-21 21:32:33 -070023}; // namespace testing
Brian Silverman17f503e2015-08-02 18:17:18 -070024
Brian Silverman17f503e2015-08-02 18:17:18 -070025// Note: Everything in this file assumes that there is a 1 cycle delay between
26// power being requested and it showing up at the motor. It assumes that
27// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
28// that isn't true.
29
30// This class implements the CapU function correctly given all the extra
31// information that we know about.
32// It does not have any zeroing logic in it, only logic to deal with a delta U
33// controller.
34class ZeroedStateFeedbackLoop : public StateFeedbackLoop<3, 1, 1> {
35 public:
36 ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> &&loop)
37 : StateFeedbackLoop<3, 1, 1>(::std::move(loop)),
38 voltage_(0.0),
39 last_voltage_(0.0),
40 uncapped_voltage_(0.0),
41 offset_(0.0),
42 max_voltage_(12.0),
43 capped_goal_(false) {}
44
45 const static int kZeroingMaxVoltage = 5;
46
Austin Schuhaebfb4f2019-01-27 13:26:38 -080047 void CapU() override;
Brian Silverman17f503e2015-08-02 18:17:18 -070048
49 // Returns the accumulated voltage.
50 double voltage() const { return voltage_; }
51
52 // Returns the uncapped voltage.
53 double uncapped_voltage() const { return uncapped_voltage_; }
54
55 // Zeros the accumulator.
56 void ZeroPower() { voltage_ = 0.0; }
57
58 // Sets the calibration offset given the absolute angle and the corrisponding
59 // encoder value.
60 void SetCalibration(double encoder_val, double known_position);
61
62 double offset() const { return offset_; }
63
64 double absolute_position() const { return X_hat(0, 0) + kPositionOffset; }
65 double absolute_velocity() const { return X_hat(1, 0); }
66
67 void CorrectPosition(double position) {
68 Eigen::Matrix<double, 1, 1> Y;
69 Y << position + offset_ - kPositionOffset;
70 Correct(Y);
71 }
72
73 // Recomputes the power goal for the current controller and position/velocity.
74 void RecalculatePowerGoal();
75
76 double goal_position() const { return R(0, 0) + kPositionOffset; }
77 double goal_velocity() const { return R(1, 0); }
78 void InitializeState(double position) {
79 mutable_X_hat(0, 0) = position - kPositionOffset;
80 mutable_X_hat(1, 0) = 0.0;
81 mutable_X_hat(2, 0) = 0.0;
82 }
83
84 void SetGoalPosition(double desired_position, double desired_velocity) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070085 AOS_LOG(DEBUG, "Goal position: %f Goal velocity: %f\n", desired_position,
86 desired_velocity);
Brian Silverman17f503e2015-08-02 18:17:18 -070087
88 mutable_R() << desired_position - kPositionOffset, desired_velocity,
Austin Schuhc5fceb82017-02-25 16:24:12 -080089 (-plant().A(1, 0) / plant().A(1, 2) *
90 (desired_position - kPositionOffset) -
91 plant().A(1, 1) / plant().A(1, 2) * desired_velocity);
Brian Silverman17f503e2015-08-02 18:17:18 -070092 }
93
94 double position() const { return X_hat(0, 0) - offset_ + kPositionOffset; }
95
96 void set_max_voltage(double max_voltage) { max_voltage_ = max_voltage; }
97 bool capped_goal() const { return capped_goal_; }
98
99 void CapGoal();
100
101 // Friend the test classes for acces to the internal state.
102 friend class testing::ShooterTest_RezeroWhileUnloading_Test;
103
104 private:
105 // The offset between what is '0' (0 rate on the spring) and the 0 (all the
106 // way cocked).
Austin Schuh9d4aca82015-11-08 14:41:31 -0800107 constexpr static double kPositionOffset =
108 ::y2014::control_loops::shooter::kMaxExtension;
Brian Silverman17f503e2015-08-02 18:17:18 -0700109 // The accumulated voltage to apply to the motor.
110 double voltage_;
111 double last_voltage_;
112 double uncapped_voltage_;
113 double offset_;
114 double max_voltage_;
115 bool capped_goal_;
116};
117
Austin Schuh214e9c12016-11-25 17:26:20 -0800118static constexpr ::std::chrono::nanoseconds kUnloadTimeout =
119 ::std::chrono::seconds(10);
120static constexpr ::std::chrono::nanoseconds kLoadTimeout =
121 ::std::chrono::seconds(2);
122static constexpr ::std::chrono::nanoseconds kLoadProblemEndTimeout =
123 ::std::chrono::seconds(1);
124static constexpr ::std::chrono::nanoseconds kShooterBrakeSetTime =
125 ::std::chrono::milliseconds(50);
Brian Silverman17f503e2015-08-02 18:17:18 -0700126// Time to wait after releasing the latch piston before winching back again.
Austin Schuh214e9c12016-11-25 17:26:20 -0800127static constexpr ::std::chrono::nanoseconds kShotEndTimeout =
128 ::std::chrono::milliseconds(200);
129static constexpr ::std::chrono::nanoseconds kPrepareFireEndTime =
130 ::std::chrono::milliseconds(40);
Brian Silverman17f503e2015-08-02 18:17:18 -0700131
132class ShooterMotor
James Kuszmaul61750662021-06-21 21:32:33 -0700133 : public frc971::controls::ControlLoop<Goal, Position, Status, Output> {
Brian Silverman17f503e2015-08-02 18:17:18 -0700134 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700135 explicit ShooterMotor(::aos::EventLoop *event_loop,
136 const ::std::string &name = "/shooter");
Brian Silverman17f503e2015-08-02 18:17:18 -0700137
138 // True if the goal was moved to avoid goal windup.
139 bool capped_goal() const { return shooter_.capped_goal(); }
140
141 double PowerToPosition(double power);
142 double PositionToPower(double position);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700143 void CheckCalibrations(const Position *position);
Brian Silverman17f503e2015-08-02 18:17:18 -0700144
145 typedef enum {
146 STATE_INITIALIZE = 0,
147 STATE_REQUEST_LOAD = 1,
148 STATE_LOAD_BACKTRACK = 2,
149 STATE_LOAD = 3,
150 STATE_LOADING_PROBLEM = 4,
151 STATE_PREPARE_SHOT = 5,
152 STATE_READY = 6,
153 STATE_FIRE = 8,
154 STATE_UNLOAD = 9,
155 STATE_UNLOAD_MOVE = 10,
156 STATE_READY_UNLOAD = 11,
157 STATE_ESTOP = 12
158 } State;
159
160 State state() { return state_; }
161
162 protected:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700163 void RunIteration(const Goal *goal, const Position *position,
164 aos::Sender<Output>::Builder *output,
165 aos::Sender<Status>::Builder *status) override;
Brian Silverman17f503e2015-08-02 18:17:18 -0700166
167 private:
168 // We have to override this to keep the pistons in the correct positions.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700169 flatbuffers::Offset<Output> Zero(
170 aos::Sender<Output>::Builder *output) override;
Brian Silverman17f503e2015-08-02 18:17:18 -0700171
172 // Friend the test classes for acces to the internal state.
173 friend class testing::ShooterTest_UnloadWindupPositive_Test;
174 friend class testing::ShooterTest_UnloadWindupNegative_Test;
175 friend class testing::ShooterTest_RezeroWhileUnloading_Test;
176
177 // Enter state STATE_UNLOAD
Austin Schuh9fe68f72019-08-10 19:32:03 -0700178 void Unload(::aos::monotonic_clock::time_point monotonic_now) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700179 state_ = STATE_UNLOAD;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700180 unload_timeout_ = monotonic_now + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700181 }
182 // Enter state STATE_LOAD
Austin Schuh9fe68f72019-08-10 19:32:03 -0700183 void Load(::aos::monotonic_clock::time_point monotonic_now) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700184 state_ = STATE_LOAD;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700185 load_timeout_ = monotonic_now + kLoadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700186 }
187
Alex Perrycb7da4b2019-08-28 19:35:56 -0700188 bool last_position_latch_ = false;
189 bool last_position_plunger_ = false;
190 double last_position_position_ = 0.0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700191
192 ZeroedStateFeedbackLoop shooter_;
193
194 // state machine state
195 State state_;
196
197 // time to giving up on loading problem
Austin Schuh214e9c12016-11-25 17:26:20 -0800198 ::aos::monotonic_clock::time_point loading_problem_end_time_ =
199 ::aos::monotonic_clock::min_time;
Brian Silverman17f503e2015-08-02 18:17:18 -0700200
201 // The end time when loading for it to timeout.
Austin Schuh214e9c12016-11-25 17:26:20 -0800202 ::aos::monotonic_clock::time_point load_timeout_ =
203 ::aos::monotonic_clock::min_time;
Brian Silverman17f503e2015-08-02 18:17:18 -0700204
205 // wait for brake to set
Austin Schuh214e9c12016-11-25 17:26:20 -0800206 ::aos::monotonic_clock::time_point shooter_brake_set_time_ =
207 ::aos::monotonic_clock::min_time;
Brian Silverman17f503e2015-08-02 18:17:18 -0700208
209 // The timeout for unloading.
Austin Schuh214e9c12016-11-25 17:26:20 -0800210 ::aos::monotonic_clock::time_point unload_timeout_ =
211 ::aos::monotonic_clock::min_time;
Brian Silverman17f503e2015-08-02 18:17:18 -0700212
213 // time that shot must have completed
Austin Schuh214e9c12016-11-25 17:26:20 -0800214 ::aos::monotonic_clock::time_point shot_end_time_ =
215 ::aos::monotonic_clock::min_time;
Brian Silverman17f503e2015-08-02 18:17:18 -0700216
217 // track cycles that we are stuck to detect errors
218 int cycles_not_moved_;
219
220 double firing_starting_position_;
221
222 // True if the latch should be engaged and the brake should be engaged.
223 bool latch_piston_;
224 bool brake_piston_;
225 int32_t last_distal_posedge_count_;
226 int32_t last_proximal_posedge_count_;
227 uint32_t shot_count_;
228 bool zeroed_;
229 int distal_posedge_validation_cycles_left_;
230 int proximal_posedge_validation_cycles_left_;
231 bool last_distal_current_;
232 bool last_proximal_current_;
233
234 DISALLOW_COPY_AND_ASSIGN(ShooterMotor);
235};
236
Alex Perrycb7da4b2019-08-28 19:35:56 -0700237} // namespace shooter
Brian Silverman17f503e2015-08-02 18:17:18 -0700238} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -0800239} // namespace y2014
Brian Silverman17f503e2015-08-02 18:17:18 -0700240
241#endif // Y2014_CONTROL_LOOPS_shooter_shooter_H_