blob: e9892a3c25d5f08bface520d59ce5f7c7b8e5cb7 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#ifndef AOS_CONTROL_LOOP_CONTROL_LOOP_H_
2#define AOS_CONTROL_LOOP_CONTROL_LOOP_H_
3
Austin Schuhb58ceb62017-02-05 14:21:57 -08004#include <atomic>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07005#include <cstring>
brians343bc112013-02-10 01:53:46 +00006
Stephan Pleines743f83a2024-02-02 18:32:09 -08007#include "aos/actions/actor.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include "aos/events/event_loop.h"
Nikolai Sohmers2df6f302024-05-30 13:12:29 -07009#include "aos/flatbuffers/static_table.h"
John Park33858a32018-09-28 23:05:48 -070010#include "aos/time/time.h"
John Park33858a32018-09-28 23:05:48 -070011#include "aos/util/log_interval.h"
James Kuszmaul7077d342021-06-09 20:23:58 -070012#include "frc971/input/joystick_state_generated.h"
13#include "frc971/input/robot_state_generated.h"
brians343bc112013-02-10 01:53:46 +000014
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080015namespace frc971::controls {
brians343bc112013-02-10 01:53:46 +000016
Brian Silverman3204dd82013-03-12 18:42:01 -070017// Control loops run this often, "starting" at time 0.
Austin Schuh214e9c12016-11-25 17:26:20 -080018constexpr ::std::chrono::nanoseconds kLoopFrequency =
Stephan Pleines743f83a2024-02-02 18:32:09 -080019 aos::common::actions::kLoopFrequency;
Brian Silverman3204dd82013-03-12 18:42:01 -070020
Nikolai Sohmers2df6f302024-05-30 13:12:29 -070021// In order to support using both "raw" and the static flatbuffer APIs with the
22// outputs of the ControlLoop class (i.e., the Output and Status messages), we
23// need to provide different compile-time code for each option. In order to do
24// this, we set up two different specializations of the BuilderType class, and
25// will select the appropriate one depending on the type of the flatbuffer. The
26// methods and types within BuilderType are then used within the ControlLoop
27// class whenever we need to call methods associated with the flatbuffers or
28// senders themselves.
29template <typename T, class Enable = void>
30struct BuilderType;
31
32template <class GoalType, class PositionType, class StatusType,
33 class OutputType>
34class ControlLoop;
35
36template <typename T>
37struct BuilderType<T, typename std::enable_if_t<
38 std::is_base_of<flatbuffers::Table, T>::value>> {
39 typedef aos::Sender<T>::Builder Builder;
40 typedef T FlatbufferType;
41 static Builder MakeBuilder(aos::Sender<T> *sender) {
42 return sender->MakeBuilder();
43 }
44 template <class GoalType, class PositionType, class StatusType,
45 class OutputType>
46 static void SendZeroFlatbuffer(
47 ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
48 Builder *builder);
49 static void CheckSent(Builder *builder) { builder->CheckSent(); }
50};
51
52template <typename T>
53struct BuilderType<
54 T, typename std::enable_if_t<std::is_base_of<aos::fbs::Table, T>::value>> {
55 typedef aos::Sender<T>::StaticBuilder Builder;
56 typedef T::Flatbuffer FlatbufferType;
57 static Builder MakeBuilder(aos::Sender<T> *sender) {
58 return sender->MakeStaticBuilder();
59 }
60 template <class GoalType, class PositionType, class StatusType,
61 class OutputType>
62 static void SendZeroFlatbuffer(
63 ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
64 Builder *builder);
65 static void CheckSent(Builder *builder) {
66 // TODO (niko) create a CheckSent() function for static flatbuffers
67 (void)builder;
68 }
69};
70
brians343bc112013-02-10 01:53:46 +000071// Provides helper methods to assist in writing control loops.
brians343bc112013-02-10 01:53:46 +000072// It will then call the RunIteration method every cycle that it has enough
73// valid data for the control loop to run.
Alex Perrycb7da4b2019-08-28 19:35:56 -070074template <class GoalType, class PositionType, class StatusType,
75 class OutputType>
Austin Schuh9fe68f72019-08-10 19:32:03 -070076class ControlLoop {
brians343bc112013-02-10 01:53:46 +000077 public:
Nikolai Sohmers2df6f302024-05-30 13:12:29 -070078 using StatusBuilder = typename BuilderType<StatusType>::Builder;
79 using OutputBuilder = typename BuilderType<OutputType>::Builder;
80 using OutputFlatbufferType = typename BuilderType<OutputType>::FlatbufferType;
81
James Kuszmaul61750662021-06-21 21:32:33 -070082 ControlLoop(aos::EventLoop *event_loop, const ::std::string &name)
Austin Schuhd3cd6992019-01-27 22:44:07 -080083 : event_loop_(event_loop), name_(name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070084 output_sender_ = event_loop_->MakeSender<OutputType>(name_);
85 status_sender_ = event_loop_->MakeSender<StatusType>(name_);
86 goal_fetcher_ = event_loop_->MakeFetcher<GoalType>(name_);
87 robot_state_fetcher_ = event_loop_->MakeFetcher<::aos::RobotState>("/aos");
Austin Schuheeec74a2019-01-27 20:58:59 -080088 joystick_state_fetcher_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -070089 event_loop_->MakeFetcher<::aos::JoystickState>("/aos");
Austin Schuh9fe68f72019-08-10 19:32:03 -070090
Alex Perrycb7da4b2019-08-28 19:35:56 -070091 event_loop_->MakeWatcher(name_, [this](const PositionType &position) {
92 this->IteratePosition(position);
93 });
Austin Schuheeec74a2019-01-27 20:58:59 -080094 }
95
96 const ::aos::RobotState &robot_state() const { return *robot_state_fetcher_; }
97 bool has_joystick_state() const { return joystick_state_fetcher_.get(); }
98 const ::aos::JoystickState &joystick_state() const {
99 return *joystick_state_fetcher_;
Austin Schuha1654ed2019-01-27 17:24:54 -0800100 }
Brian Silverman699f0cb2015-02-05 19:45:01 -0500101
102 // Returns true if all the counters etc in the sensor data have been reset.
103 // This will return true only a single time per reset.
104 bool WasReset() {
105 if (reset_) {
106 reset_ = false;
107 return true;
108 } else {
109 return false;
110 }
111 }
112
Brian Silvermand1e65b92014-03-08 17:07:14 -0800113 // Constructs and sends a message on the output queue which sets everything to
Austin Schuhaebfb4f2019-01-27 13:26:38 -0800114 // a safe state. Default is to set everything to zero. Override Zero below
115 // to change that behavior.
116 void ZeroOutputs();
brians343bc112013-02-10 01:53:46 +0000117
118 // Sets the output to zero.
Austin Schuhaebfb4f2019-01-27 13:26:38 -0800119 // Override this if a value of zero (or false) is not "off" for this
120 // subsystem.
Nikolai Sohmers2df6f302024-05-30 13:12:29 -0700121 virtual flatbuffers::Offset<OutputFlatbufferType> Zero(
122 typename ::aos::Sender<OutputFlatbufferType>::Builder *builder) {
123 return builder->template MakeBuilder<OutputFlatbufferType>().Finish();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700124 }
brians343bc112013-02-10 01:53:46 +0000125
brians343bc112013-02-10 01:53:46 +0000126 protected:
Austin Schuh9fe68f72019-08-10 19:32:03 -0700127 // Runs one cycle of the loop.
Austin Schuha1654ed2019-01-27 17:24:54 -0800128 void IteratePosition(const PositionType &position);
129
James Kuszmaul61750662021-06-21 21:32:33 -0700130 aos::EventLoop *event_loop() { return event_loop_; }
Theo Bafrali3274a182019-02-17 20:01:38 -0800131
Alex Perrycb7da4b2019-08-28 19:35:56 -0700132 // Returns the position context. This is only valid inside the RunIteration
133 // method.
134 const aos::Context &position_context() { return event_loop_->context(); }
135
brians343bc112013-02-10 01:53:46 +0000136 // Runs an iteration of the control loop.
Brian Silverman089f5812015-02-15 01:58:19 -0500137 // goal is the last goal that was sent. It might be any number of cycles old
138 // or nullptr if we haven't ever received a goal.
139 // position is the current position, or nullptr if we didn't get a position
140 // this cycle.
141 // output is the values to be sent to the motors. This is nullptr if the
142 // output is going to be ignored and set to 0.
brians343bc112013-02-10 01:53:46 +0000143 // status is the status of the control loop.
144 // Both output and status should be filled in by the implementation.
Nikolai Sohmers2df6f302024-05-30 13:12:29 -0700145 virtual void RunIteration(const GoalType *goal, const PositionType *position,
146 OutputBuilder *output, StatusBuilder *status) = 0;
brians343bc112013-02-10 01:53:46 +0000147
148 private:
Austin Schuh61bdc602016-12-04 19:10:10 -0800149 static constexpr ::std::chrono::milliseconds kStaleLogInterval =
150 ::std::chrono::milliseconds(100);
Brian Silverman699f0cb2015-02-05 19:45:01 -0500151 // The amount of time after the last PWM pulse we consider motors enabled for.
152 // 100ms is the result of using an oscilliscope to look at the input and
153 // output of a Talon. The Info Sheet also lists 100ms for Talon SR, Talon SRX,
154 // and Victor SP.
Austin Schuh61bdc602016-12-04 19:10:10 -0800155 static constexpr ::std::chrono::milliseconds kPwmDisableTime =
156 ::std::chrono::milliseconds(100);
Brian Silverman699f0cb2015-02-05 19:45:01 -0500157
brians343bc112013-02-10 01:53:46 +0000158 // Pointer to the queue group
James Kuszmaul61750662021-06-21 21:32:33 -0700159 aos::EventLoop *event_loop_;
Austin Schuheeec74a2019-01-27 20:58:59 -0800160 ::std::string name_;
Brian Silverman50a9d032014-02-16 17:20:57 -0800161
Austin Schuha1654ed2019-01-27 17:24:54 -0800162 ::aos::Sender<OutputType> output_sender_;
163 ::aos::Sender<StatusType> status_sender_;
164 ::aos::Fetcher<GoalType> goal_fetcher_;
Austin Schuheeec74a2019-01-27 20:58:59 -0800165 ::aos::Fetcher<::aos::RobotState> robot_state_fetcher_;
166 ::aos::Fetcher<::aos::JoystickState> joystick_state_fetcher_;
167
168 // Fetcher only to be used for the Iterate method. If Iterate is called, Run
169 // can't be called.
170 bool has_iterate_fetcher_ = false;
171 ::aos::Fetcher<PositionType> iterate_position_fetcher_;
Austin Schuha1654ed2019-01-27 17:24:54 -0800172
Brian Silverman699f0cb2015-02-05 19:45:01 -0500173 bool reset_ = false;
174 int32_t sensor_reader_pid_ = 0;
175
Austin Schuh61bdc602016-12-04 19:10:10 -0800176 ::aos::monotonic_clock::time_point last_pwm_sent_ =
177 ::aos::monotonic_clock::min_time;
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800178
Brian Silverman50a9d032014-02-16 17:20:57 -0800179 typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
Brian Silverman699f0cb2015-02-05 19:45:01 -0500180 SimpleLogInterval no_sensor_state_ =
181 SimpleLogInterval(kStaleLogInterval, ERROR, "no sensor state");
Brian Silverman2704ecf2014-04-09 20:24:03 -0700182 SimpleLogInterval motors_off_log_ =
183 SimpleLogInterval(kStaleLogInterval, WARNING, "motors disabled");
Brian Silverman699f0cb2015-02-05 19:45:01 -0500184 SimpleLogInterval no_goal_ =
185 SimpleLogInterval(kStaleLogInterval, ERROR, "no goal");
brians343bc112013-02-10 01:53:46 +0000186};
187
Nikolai Sohmers2df6f302024-05-30 13:12:29 -0700188template <typename T>
189template <class GoalType, class PositionType, class StatusType,
190 class OutputType>
191void BuilderType<T, typename std::enable_if_t<
192 std::is_base_of<flatbuffers::Table, T>::value>>::
193 SendZeroFlatbuffer(
194 ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
195 Builder *builder) {
196 builder->CheckOk(builder->Send(loop->Zero(builder)));
197}
198
199template <typename T>
200template <class GoalType, class PositionType, class StatusType,
201 class OutputType>
202void BuilderType<
203 T, typename std::enable_if_t<std::is_base_of<aos::fbs::Table, T>::value>>::
204 SendZeroFlatbuffer(ControlLoop<GoalType, PositionType, StatusType,
205 OutputType> * /* loop */,
206 Builder *builder) {
207 builder->CheckOk(builder->Send());
208}
209
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800210} // namespace frc971::controls
brians343bc112013-02-10 01:53:46 +0000211
James Kuszmaul61750662021-06-21 21:32:33 -0700212#include "frc971/control_loops/control_loop-tmpl.h" // IWYU pragma: export
brians343bc112013-02-10 01:53:46 +0000213
214#endif