blob: 42702c091f2b8cfacfb1c60b4c8874eb4dc480eb [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stddef.h>
Brian Silverman699f0cb2015-02-05 19:45:01 -05002#include <inttypes.h>
brians343bc112013-02-10 01:53:46 +00003
4#include "aos/common/logging/logging.h"
Brian Silverman2ac0fbc2014-03-20 19:45:13 -07005#include "aos/common/messages/robot_state.q.h"
Brian Silvermand6974f42014-02-14 13:39:21 -08006#include "aos/common/logging/queue_logging.h"
Brian3afd6fc2014-04-02 20:41:49 -07007#include "aos/common/util/phased_loop.h"
Austin Schuh3d6e3df2014-02-17 01:51:03 -08008
brians343bc112013-02-10 01:53:46 +00009namespace aos {
Brian Silverman38111502014-04-10 12:36:26 -070010namespace controls {
brians343bc112013-02-10 01:53:46 +000011
12// TODO(aschuh): Tests.
13
Brian Silvermand8f403a2014-12-13 19:12:04 -050014template <class T, bool fail_no_goal>
15constexpr ::aos::time::Time ControlLoop<T, fail_no_goal>::kStaleLogInterval;
Brian Silverman699f0cb2015-02-05 19:45:01 -050016template <class T, bool fail_no_goal>
17constexpr ::aos::time::Time ControlLoop<T, fail_no_goal>::kPwmDisableTime;
Brian Silverman50a9d032014-02-16 17:20:57 -080018
Brian Silvermand8f403a2014-12-13 19:12:04 -050019template <class T, bool fail_no_goal>
Brian Silverman71fbee02014-03-13 17:24:54 -070020void
Brian Silvermand8f403a2014-12-13 19:12:04 -050021ControlLoop<T, fail_no_goal>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000022 aos::ScopedMessagePtr<OutputType> output =
23 control_loop_->output.MakeMessage();
24 Zero(output.get());
25 output.Send();
26}
27
Brian Silvermand8f403a2014-12-13 19:12:04 -050028template <class T, bool fail_no_goal>
29void ControlLoop<T, fail_no_goal>::Iterate() {
Brian Silverman699f0cb2015-02-05 19:45:01 -050030 no_goal_.Print();
Brian Silverman9c9ab422014-02-25 13:42:53 -080031 driver_station_old_.Print();
Brian Silverman699f0cb2015-02-05 19:45:01 -050032 no_sensor_state_.Print();
Brian Silverman9c9ab422014-02-25 13:42:53 -080033 no_driver_station_.Print();
Brian Silverman699f0cb2015-02-05 19:45:01 -050034 motors_off_log_.Print();
Brian Silverman6a1cd212014-02-20 21:04:34 -080035
Brian Silvermand8f403a2014-12-13 19:12:04 -050036 control_loop_->position.FetchAnother();
37 const PositionType *const position = control_loop_->position.get();
38 LOG_STRUCT(DEBUG, "position", *position);
39
40 // Fetch the latest control loop goal. If there is no new
brians343bc112013-02-10 01:53:46 +000041 // goal, we will just reuse the old one.
42 // If there is no goal, we haven't started up fully. It isn't worth
43 // the added complexity for each loop implementation to handle that case.
44 control_loop_->goal.FetchLatest();
45 // TODO(aschuh): Check the age here if we want the loop to stop on old
46 // goals.
47 const GoalType *goal = control_loop_->goal.get();
Brian Silverman699f0cb2015-02-05 19:45:01 -050048 if (goal) {
49 LOG_STRUCT(DEBUG, "goal", *goal);
50 } else {
51 LOG_INTERVAL(no_goal_);
Brian Silverman71fbee02014-03-13 17:24:54 -070052 if (fail_no_goal) {
53 ZeroOutputs();
54 return;
55 }
brians343bc112013-02-10 01:53:46 +000056 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080057
Brian Silverman699f0cb2015-02-05 19:45:01 -050058 ::aos::robot_state.FetchLatest();
59 if (!::aos::robot_state.get()) {
60 LOG_INTERVAL(no_sensor_state_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080061 return;
62 }
Brian Silverman699f0cb2015-02-05 19:45:01 -050063 if (sensor_reader_pid_ != ::aos::robot_state->reader_pid) {
64 LOG(INFO, "new sensor reader PID %" PRId32 ", old was %" PRId32 "\n",
65 ::aos::robot_state->reader_pid, sensor_reader_pid_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080066 reset_ = true;
Brian Silverman699f0cb2015-02-05 19:45:01 -050067 sensor_reader_pid_ = ::aos::robot_state->reader_pid;
Brian Silverman71fbee02014-03-13 17:24:54 -070068 }
brians343bc112013-02-10 01:53:46 +000069
brians343bc112013-02-10 01:53:46 +000070 bool outputs_enabled = false;
71
72 // Check to see if we got a driver station packet recently.
Brian Silverman699f0cb2015-02-05 19:45:01 -050073 if (::aos::joystick_state.FetchLatest()) {
Brian Silverman295f74b2015-02-14 23:00:47 -050074 if (::aos::joystick_state->enabled) outputs_enabled = true;
Brian Silverman699f0cb2015-02-05 19:45:01 -050075 if (::aos::robot_state->outputs_enabled) {
Brian Silverman295f74b2015-02-14 23:00:47 -050076 // If the driver's station reports being disabled, we're probably not
77 // actually going to send motor values regardless of what the FPGA
78 // reports.
79 if (::aos::joystick_state->enabled) {
80 last_pwm_sent_ = ::aos::robot_state->sent_time;
81 } else {
82 LOG(WARNING, "outputs enabled while disabled\n");
83 }
84 } else if (::aos::joystick_state->enabled) {
85 LOG(WARNING, "outputs disabled while enabled\n");
Brian Silverman699f0cb2015-02-05 19:45:01 -050086 }
Brian Silverman295f74b2015-02-14 23:00:47 -050087 } else if (::aos::joystick_state.IsNewerThanMS(kDSPacketTimeoutMs) &&
88 ::aos::joystick_state->enabled) {
brians343bc112013-02-10 01:53:46 +000089 outputs_enabled = true;
90 } else {
Brian Silverman699f0cb2015-02-05 19:45:01 -050091 if (::aos::joystick_state.get()) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080092 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +000093 } else {
Brian Silverman9c9ab422014-02-25 13:42:53 -080094 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +000095 }
96 }
97
Austin Schuh66a3d2f2014-10-21 22:24:00 -070098 // The 100ms is the result of using an oscilliscope to look at the PWM signal
99 // and output of a talon, and timing the delay between the last pulse and the
100 // talon turning off.
Brian Silverman38111502014-04-10 12:36:26 -0700101 const bool motors_off =
Brian Silverman699f0cb2015-02-05 19:45:01 -0500102 (::aos::time::Time::Now() - last_pwm_sent_) >= kPwmDisableTime;
Brian Silverman2704ecf2014-04-09 20:24:03 -0700103 if (motors_off) {
Brian Silverman699f0cb2015-02-05 19:45:01 -0500104 if (::aos::joystick_state.get() && ::aos::joystick_state->enabled) {
Brian Silverman2704ecf2014-04-09 20:24:03 -0700105 LOG_INTERVAL(motors_off_log_);
106 }
107 outputs_enabled = false;
108 }
109
brians343bc112013-02-10 01:53:46 +0000110 aos::ScopedMessagePtr<StatusType> status =
111 control_loop_->status.MakeMessage();
Brian Silverman699f0cb2015-02-05 19:45:01 -0500112 if (status.get() == nullptr) {
brians343bc112013-02-10 01:53:46 +0000113 return;
114 }
115
116 if (outputs_enabled) {
117 aos::ScopedMessagePtr<OutputType> output =
118 control_loop_->output.MakeMessage();
119 RunIteration(goal, position, output.get(), status.get());
120
Brian Silvermand6974f42014-02-14 13:39:21 -0800121 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000122 output.Send();
123 } else {
Brian Silverman699f0cb2015-02-05 19:45:01 -0500124 // The outputs are disabled, so pass nullptr in for the output.
Ben Fredrickson4283bb42014-02-22 08:31:50 +0000125 RunIteration(goal, position, nullptr, status.get());
brians343bc112013-02-10 01:53:46 +0000126 ZeroOutputs();
127 }
128
Brian Silvermand6974f42014-02-14 13:39:21 -0800129 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000130 status.Send();
131}
132
Brian Silvermand8f403a2014-12-13 19:12:04 -0500133template <class T, bool fail_no_goal>
134void ControlLoop<T, fail_no_goal>::Run() {
brians343bc112013-02-10 01:53:46 +0000135 while (true) {
brians343bc112013-02-10 01:53:46 +0000136 Iterate();
137 }
138}
139
Brian Silverman38111502014-04-10 12:36:26 -0700140} // namespace controls
brians343bc112013-02-10 01:53:46 +0000141} // namespace aos