blob: 1ebda6c1a309d6c35d5b804995c234c6f12e51b3 [file] [log] [blame]
Alex Perry731b4602019-02-02 22:13:01 -08001#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
2
3#include "Eigen/Dense"
4
James Kuszmaul75a18c52021-03-10 22:02:07 -08005#include "aos/json_to_flatbuffer.h"
6
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include "aos/realtime.h"
James Kuszmaulc73bb222019-04-07 12:15:35 -07008#include "aos/util/math.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "frc971/control_loops/control_loops_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -080010#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070011#include "frc971/control_loops/drivetrain/drivetrain_goal_generated.h"
12#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
13#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -080014
Alex Perry731b4602019-02-02 22:13:01 -080015namespace frc971 {
16namespace control_loops {
17namespace drivetrain {
18
19SplineDrivetrain::SplineDrivetrain(const DrivetrainConfig<double> &dt_config)
James Kuszmaul75a18c52021-03-10 22:02:07 -080020 : dt_config_(dt_config),
21 current_xva_(Eigen::Vector3d::Zero()),
22 next_xva_(Eigen::Vector3d::Zero()),
23 next_U_(Eigen::Vector2d::Zero()) {}
Alex Perrye32eabc2019-02-08 19:51:19 -080024
25void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -080026 output_was_capped_ =
27 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Alex Perrye32eabc2019-02-08 19:51:19 -080028
Alex Perrycc3ee4c2019-02-09 21:20:41 -080029 if (output_was_capped_) {
Alex Perrye32eabc2019-02-08 19:51:19 -080030 *U *= 12.0 / U->lpNorm<Eigen::Infinity>();
31 }
32}
Alex Perry731b4602019-02-02 22:13:01 -080033
Alex Perrycc3ee4c2019-02-09 21:20:41 -080034void SplineDrivetrain::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -070035 const ::frc971::control_loops::drivetrain::Goal *goal) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080036 if (goal->has_spline_handle()) {
37 commanded_spline_ = goal->spline_handle();
Austin Schuhb574fe42019-12-06 23:51:47 -080038 } else {
James Kuszmaul75a18c52021-03-10 22:02:07 -080039 commanded_spline_.reset();
Alex Perrya71badb2019-02-06 19:40:41 -080040 }
James Kuszmaul75a18c52021-03-10 22:02:07 -080041 UpdateSplineHandles();
42}
43
James Kuszmaul99af8b52021-03-28 10:50:15 -070044bool SplineDrivetrain::IsCurrentTrajectory(
45 const fb::Trajectory *trajectory) const {
46 return (current_trajectory_ != nullptr &&
47 current_trajectory().spline_handle() == trajectory->handle());
48}
49
James Kuszmaul75a18c52021-03-10 22:02:07 -080050bool SplineDrivetrain::HasTrajectory(const fb::Trajectory *trajectory) const {
51 if (trajectory == nullptr) {
52 return false;
53 }
54 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
55 if (trajectories_[ii]->spline_handle() == trajectory->handle()) {
56 return true;
57 }
58 }
59 return false;
60}
61
James Kuszmaul99af8b52021-03-28 10:50:15 -070062void SplineDrivetrain::DeleteTrajectory(const fb::Trajectory *trajectory) {
63 CHECK(trajectory != nullptr);
64
65 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
66 if (trajectories_[ii]->spline_handle() == trajectory->handle()) {
67 trajectories_.erase(trajectories_.begin() + ii);
68 return;
69 }
70 }
71
72 LOG(FATAL) << "Trying to remove unknown trajectory " << trajectory->handle();
73}
74
James Kuszmaul75a18c52021-03-10 22:02:07 -080075void SplineDrivetrain::AddTrajectory(const fb::Trajectory *trajectory) {
76 trajectories_.emplace_back(
77 std::make_unique<FinishedTrajectory>(dt_config_, trajectory));
78 UpdateSplineHandles();
79}
80
81void SplineDrivetrain::DeleteCurrentSpline() {
James Kuszmaul99af8b52021-03-28 10:50:15 -070082 DeleteTrajectory(&CHECK_NOTNULL(current_trajectory_)->trajectory());
James Kuszmaul75a18c52021-03-10 22:02:07 -080083 executing_spline_ = false;
James Kuszmaul99af8b52021-03-28 10:50:15 -070084 current_trajectory_ = nullptr;
James Kuszmaul75a18c52021-03-10 22:02:07 -080085 current_xva_.setZero();
86}
87
88void SplineDrivetrain::UpdateSplineHandles() {
89 // If we are currently executing a spline and have received a change
90 if (executing_spline_) {
91 if (!commanded_spline_) {
92 // We've been told to stop executing a spline; remove it from our queue,
93 // and clean up.
94 DeleteCurrentSpline();
95 return;
96 } else {
97 if (executing_spline_ &&
98 current_trajectory().spline_handle() != *commanded_spline_) {
99 // If we are executing a spline, and the handle has changed, garbage
100 // collect the old spline.
101 DeleteCurrentSpline();
102 }
103 }
104 }
105 // We've now cleaned up the previous state; handle any new commands.
106 if (!commanded_spline_) {
107 return;
108 }
109 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
110 if (trajectories_[ii]->spline_handle() == *commanded_spline_) {
111 executing_spline_ = true;
James Kuszmaul99af8b52021-03-28 10:50:15 -0700112 current_trajectory_ = trajectories_[ii].get();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800113 }
114 }
115 // If we didn't find the commanded spline in the list of available splines,
116 // that's fine; it just means, it hasn't been fully planned yet.
Alex Perrya71badb2019-02-06 19:40:41 -0800117}
118
Alex Perrye32eabc2019-02-08 19:51:19 -0800119// TODO(alex): Hold position when done following the spline.
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700120void SplineDrivetrain::Update(
121 bool enable, const ::Eigen::Matrix<double, 5, 1> &state,
122 const ::Eigen::Matrix<double, 2, 1> &voltage_error) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800123 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800124 enable_ = enable;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800125 if (enable && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800126 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800127 if (!IsAtEnd() && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800128 // TODO(alex): It takes about a cycle for the outputs to propagate to the
129 // motors. Consider delaying the output by a cycle.
James Kuszmaul75a18c52021-03-10 22:02:07 -0800130 U_ff = current_trajectory().FFVoltage(current_xva_(0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800131 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800132
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700133 const double current_distance = current_xva_(0);
Alex Perrye32eabc2019-02-08 19:51:19 -0800134 ::Eigen::Matrix<double, 2, 5> K =
James Kuszmaul75a18c52021-03-10 22:02:07 -0800135 current_trajectory().GainForDistance(current_distance);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800136 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800137 if (current_trajectory().drive_spline_backwards()) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700138 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
139 U_ff = -swapU;
140 goal_state(2, 0) += M_PI;
141 double left_goal = goal_state(3, 0);
142 double right_goal = goal_state(4, 0);
143 goal_state(3, 0) = -right_goal;
144 goal_state(4, 0) = -left_goal;
145 }
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700146 const Eigen::Matrix<double, 5, 1> relative_goal =
James Kuszmaul75a18c52021-03-10 22:02:07 -0800147 current_trajectory().StateToPathRelativeState(current_distance,
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700148 goal_state);
149 const Eigen::Matrix<double, 5, 1> relative_state =
James Kuszmaul75a18c52021-03-10 22:02:07 -0800150 current_trajectory().StateToPathRelativeState(current_distance, state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700151 Eigen::Matrix<double, 5, 1> state_error = relative_goal - relative_state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700152 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800153 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800154
Austin Schuhd749d932020-12-30 21:38:40 -0800155 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2, 1>(0, 0);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800156 next_xva_ = current_trajectory().GetNextXVA(dt_config_.dt, &xv_state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700157 next_U_ = U_ff + U_fb - voltage_error;
Alex Perrye32eabc2019-02-08 19:51:19 -0800158 uncapped_U_ = next_U_;
159 ScaleCapU(&next_U_);
Alex Perry731b4602019-02-02 22:13:01 -0800160 }
161}
162
Alex Perry731b4602019-02-02 22:13:01 -0800163void SplineDrivetrain::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700164 ::frc971::control_loops::drivetrain::OutputT *output) {
Alex Perry731b4602019-02-02 22:13:01 -0800165 if (!output) {
166 return;
167 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800168 if (executing_spline_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800169 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800170 output->left_voltage = next_U_(0);
171 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800172 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800173 }
174 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800175 output->left_voltage = next_U_(0);
176 output->right_voltage = next_U_(1);
Alex Perry731b4602019-02-02 22:13:01 -0800177}
178
Alex Perrye32eabc2019-02-08 19:51:19 -0800179void SplineDrivetrain::PopulateStatus(
Austin Schuhd749d932020-12-30 21:38:40 -0800180 drivetrain::Status::Builder *builder) const {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700181 if (builder && enable_) {
182 builder->add_uncapped_left_voltage(uncapped_U_(0));
183 builder->add_uncapped_right_voltage(uncapped_U_(1));
184 builder->add_robot_speed(current_xva_(1));
185 builder->add_output_was_capped(output_was_capped_);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800186 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700187}
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800188
Alex Perrycb7da4b2019-08-28 19:35:56 -0700189flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
190 flatbuffers::FlatBufferBuilder *builder) const {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800191 int *spline_handles;
192 const flatbuffers::Offset<flatbuffers::Vector<int>> handles_vector =
193 builder->CreateUninitializedVector(trajectories_.size(), &spline_handles);
194
195 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
196 spline_handles[ii] = trajectories_[ii]->spline_handle();
197 }
198
Alex Perrycb7da4b2019-08-28 19:35:56 -0700199 drivetrain::TrajectoryLogging::Builder trajectory_logging_builder(*builder);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800200 if (executing_spline_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700201 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
202 trajectory_logging_builder.add_x(goal_state(0));
203 trajectory_logging_builder.add_y(goal_state(1));
204 trajectory_logging_builder.add_theta(::aos::math::NormalizeAngle(
James Kuszmaul75a18c52021-03-10 22:02:07 -0800205 goal_state(2) +
206 (current_trajectory().drive_spline_backwards() ? M_PI : 0.0)));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700207 trajectory_logging_builder.add_left_velocity(goal_state(3));
208 trajectory_logging_builder.add_right_velocity(goal_state(4));
Alex Perrye32eabc2019-02-08 19:51:19 -0800209 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210 trajectory_logging_builder.add_is_executing(!IsAtEnd() &&
James Kuszmaul75a18c52021-03-10 22:02:07 -0800211 executing_spline_);
212 trajectory_logging_builder.add_is_executed(executing_spline_ && IsAtEnd());
213 if (commanded_spline_) {
214 trajectory_logging_builder.add_goal_spline_handle(*commanded_spline_);
215 if (executing_spline_) {
216 trajectory_logging_builder.add_current_spline_idx(*commanded_spline_);
217 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700218 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800219 trajectory_logging_builder.add_distance_remaining(
220 executing_spline_ ? current_trajectory().length() - current_xva_.x()
221 : 0.0);
222 trajectory_logging_builder.add_available_splines(handles_vector);
223
Alex Perrycb7da4b2019-08-28 19:35:56 -0700224 return trajectory_logging_builder.Finish();
225}
226
227flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
228 aos::Sender<drivetrain::Status>::Builder *builder) const {
229 return MakeTrajectoryLogging(builder->fbb());
Alex Perrye32eabc2019-02-08 19:51:19 -0800230}
231
Alex Perry731b4602019-02-02 22:13:01 -0800232} // namespace drivetrain
233} // namespace control_loops
234} // namespace frc971