blob: ebe0f647b6d853689565d42ca49f488ee40c36bb [file] [log] [blame]
Alex Perry731b4602019-02-02 22:13:01 -08001#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
2
3#include "Eigen/Dense"
Philipp Schrader790cb542023-07-05 21:06:52 -07004
James Kuszmaul75a18c52021-03-10 22:02:07 -08005#include "aos/json_to_flatbuffer.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07006#include "aos/realtime.h"
James Kuszmaulc73bb222019-04-07 12:15:35 -07007#include "aos/util/math.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include "frc971/control_loops/control_loops_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -08009#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070010#include "frc971/control_loops/drivetrain/drivetrain_goal_generated.h"
11#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
12#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -080013
Stephan Pleinesf63bde82024-01-13 15:59:33 -080014namespace frc971::control_loops::drivetrain {
Alex Perry731b4602019-02-02 22:13:01 -080015
16SplineDrivetrain::SplineDrivetrain(const DrivetrainConfig<double> &dt_config)
James Kuszmaul75a18c52021-03-10 22:02:07 -080017 : dt_config_(dt_config),
Austin Schuhf7c65202022-11-04 21:28:20 -070018 velocity_drivetrain_(
19 std::make_shared<StateFeedbackLoop<2, 2, 2, double,
20 StateFeedbackHybridPlant<2, 2, 2>,
21 HybridKalman<2, 2, 2>>>(
22 dt_config_.make_hybrid_drivetrain_velocity_loop())),
James Kuszmaul75a18c52021-03-10 22:02:07 -080023 current_xva_(Eigen::Vector3d::Zero()),
24 next_xva_(Eigen::Vector3d::Zero()),
25 next_U_(Eigen::Vector2d::Zero()) {}
Alex Perrye32eabc2019-02-08 19:51:19 -080026
27void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -080028 output_was_capped_ =
29 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Alex Perrye32eabc2019-02-08 19:51:19 -080030
Alex Perrycc3ee4c2019-02-09 21:20:41 -080031 if (output_was_capped_) {
Alex Perrye32eabc2019-02-08 19:51:19 -080032 *U *= 12.0 / U->lpNorm<Eigen::Infinity>();
33 }
34}
Alex Perry731b4602019-02-02 22:13:01 -080035
Alex Perrycc3ee4c2019-02-09 21:20:41 -080036void SplineDrivetrain::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -070037 const ::frc971::control_loops::drivetrain::Goal *goal) {
James Kuszmauldc534432023-02-05 14:51:11 -080038 UpdateSplineHandles(goal->has_spline_handle()
39 ? std::make_optional<int>(goal->spline_handle())
40 : std::nullopt);
James Kuszmaul75a18c52021-03-10 22:02:07 -080041}
42
James Kuszmaul99af8b52021-03-28 10:50:15 -070043bool SplineDrivetrain::IsCurrentTrajectory(
44 const fb::Trajectory *trajectory) const {
James Kuszmauldc534432023-02-05 14:51:11 -080045 const FinishedTrajectory *current = current_trajectory();
46 return (current != nullptr &&
47 current->spline_handle() == trajectory->handle());
James Kuszmaul99af8b52021-03-28 10:50:15 -070048}
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) {
James Kuszmauldc534432023-02-05 14:51:11 -080055 if (trajectories_[ii].spline_handle() == trajectory->handle()) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080056 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) {
James Kuszmauldc534432023-02-05 14:51:11 -080066 if (trajectories_[ii].spline_handle() == trajectory->handle()) {
James Kuszmaul99af8b52021-03-28 10:50:15 -070067 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) {
James Kuszmauldc534432023-02-05 14:51:11 -080076 CHECK_LT(trajectories_.size(), trajectories_.capacity());
James Kuszmaul5c4ccf62024-03-03 17:29:49 -080077 trajectories_.emplace_back(&dt_config_, trajectory, velocity_drivetrain_);
James Kuszmauldc534432023-02-05 14:51:11 -080078 UpdateSplineHandles(commanded_spline_);
James Kuszmaul75a18c52021-03-10 22:02:07 -080079}
80
81void SplineDrivetrain::DeleteCurrentSpline() {
Austin Schuh6bdcc372024-06-27 14:49:11 -070082 const FinishedTrajectory *const trajectory = current_trajectory();
83 CHECK(trajectory != nullptr);
84 DeleteTrajectory(&trajectory->trajectory());
James Kuszmaul75a18c52021-03-10 22:02:07 -080085 executing_spline_ = false;
James Kuszmauldc534432023-02-05 14:51:11 -080086 commanded_spline_.reset();
James Kuszmaul75a18c52021-03-10 22:02:07 -080087 current_xva_.setZero();
88}
89
James Kuszmauldc534432023-02-05 14:51:11 -080090void SplineDrivetrain::UpdateSplineHandles(
91 std::optional<int> commanded_spline) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080092 // If we are currently executing a spline and have received a change
93 if (executing_spline_) {
James Kuszmauldc534432023-02-05 14:51:11 -080094 if (!commanded_spline) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080095 // We've been told to stop executing a spline; remove it from our queue,
96 // and clean up.
97 DeleteCurrentSpline();
98 return;
99 } else {
Austin Schuh6bdcc372024-06-27 14:49:11 -0700100 if (executing_spline_) {
101 const FinishedTrajectory *const trajectory = current_trajectory();
102 CHECK(trajectory != nullptr);
103
104 if (trajectory->spline_handle() != *commanded_spline) {
105 // If we are executing a spline, and the handle has changed, garbage
106 // collect the old spline.
107 DeleteCurrentSpline();
108 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800109 }
110 }
111 }
James Kuszmauldc534432023-02-05 14:51:11 -0800112 commanded_spline_ = commanded_spline;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800113 // We've now cleaned up the previous state; handle any new commands.
114 if (!commanded_spline_) {
115 return;
116 }
117 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
James Kuszmauldc534432023-02-05 14:51:11 -0800118 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800119 executing_spline_ = true;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800120 }
121 }
122 // If we didn't find the commanded spline in the list of available splines,
123 // that's fine; it just means, it hasn't been fully planned yet.
Alex Perrya71badb2019-02-06 19:40:41 -0800124}
125
James Kuszmauldc534432023-02-05 14:51:11 -0800126FinishedTrajectory *SplineDrivetrain::current_trajectory() {
127 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
128 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
129 return &trajectories_[ii];
130 }
131 }
132 return nullptr;
133}
134
135const FinishedTrajectory *SplineDrivetrain::current_trajectory() const {
136 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
137 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
138 return &trajectories_[ii];
139 }
140 }
141 return nullptr;
142}
143
Alex Perrye32eabc2019-02-08 19:51:19 -0800144// TODO(alex): Hold position when done following the spline.
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700145void SplineDrivetrain::Update(
146 bool enable, const ::Eigen::Matrix<double, 5, 1> &state,
147 const ::Eigen::Matrix<double, 2, 1> &voltage_error) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800148 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800149 enable_ = enable;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800150 if (enable && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800151 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
Austin Schuh6bdcc372024-06-27 14:49:11 -0700152 const FinishedTrajectory *const trajectory = current_trajectory();
153 CHECK(trajectory != nullptr);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800154 if (!IsAtEnd() && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800155 // TODO(alex): It takes about a cycle for the outputs to propagate to the
156 // motors. Consider delaying the output by a cycle.
James Kuszmauldc534432023-02-05 14:51:11 -0800157 U_ff = trajectory->FFVoltage(current_xva_(0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800158 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800159
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700160 const double current_distance = current_xva_(0);
Alex Perrye32eabc2019-02-08 19:51:19 -0800161 ::Eigen::Matrix<double, 2, 5> K =
James Kuszmauldc534432023-02-05 14:51:11 -0800162 trajectory->GainForDistance(current_distance);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800163 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmauldc534432023-02-05 14:51:11 -0800164 const bool backwards = trajectory->drive_spline_backwards();
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700165 if (backwards) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700166 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
167 U_ff = -swapU;
168 goal_state(2, 0) += M_PI;
169 double left_goal = goal_state(3, 0);
170 double right_goal = goal_state(4, 0);
171 goal_state(3, 0) = -right_goal;
172 goal_state(4, 0) = -left_goal;
173 }
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700174 const Eigen::Matrix<double, 5, 1> relative_goal =
James Kuszmauldc534432023-02-05 14:51:11 -0800175 trajectory->StateToPathRelativeState(current_distance, goal_state,
176 backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700177 const Eigen::Matrix<double, 5, 1> relative_state =
James Kuszmauldc534432023-02-05 14:51:11 -0800178 trajectory->StateToPathRelativeState(current_distance, state,
179 backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700180 Eigen::Matrix<double, 5, 1> state_error = relative_goal - relative_state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700181 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800182 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
James Kuszmaula5a418f2024-03-17 17:16:06 -0700183 last_U_components_ = K * Eigen::DiagonalMatrix<double, 5>(state_error);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800184
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700185 if (backwards) {
186 Eigen::Matrix<double, 2, 1> swapU(U_fb(1), U_fb(0));
187 U_fb = -swapU;
188 }
189
Austin Schuhd749d932020-12-30 21:38:40 -0800190 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2, 1>(0, 0);
James Kuszmauldc534432023-02-05 14:51:11 -0800191 next_xva_ = trajectory->GetNextXVA(dt_config_.dt, &xv_state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700192 next_U_ = U_ff + U_fb - voltage_error;
Alex Perrye32eabc2019-02-08 19:51:19 -0800193 uncapped_U_ = next_U_;
194 ScaleCapU(&next_U_);
James Kuszmaula5a418f2024-03-17 17:16:06 -0700195 last_state_error_ = state_error;
196 last_U_ff_ = U_ff;
Alex Perry731b4602019-02-02 22:13:01 -0800197 }
198}
199
Alex Perry731b4602019-02-02 22:13:01 -0800200void SplineDrivetrain::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700201 ::frc971::control_loops::drivetrain::OutputT *output) {
Alex Perry731b4602019-02-02 22:13:01 -0800202 if (!output) {
203 return;
204 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800205 if (executing_spline_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800206 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800207 output->left_voltage = next_U_(0);
208 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800209 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800210 }
211 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800212 output->left_voltage = next_U_(0);
213 output->right_voltage = next_U_(1);
James Kuszmaulad394ac2024-04-05 17:31:44 -0700214 if (IsAtEnd()) {
215 output->left_voltage = 0.0;
216 output->right_voltage = 0.0;
217 }
Alex Perry731b4602019-02-02 22:13:01 -0800218}
219
Alex Perrye32eabc2019-02-08 19:51:19 -0800220void SplineDrivetrain::PopulateStatus(
Austin Schuhd749d932020-12-30 21:38:40 -0800221 drivetrain::Status::Builder *builder) const {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700222 if (builder && enable_) {
223 builder->add_uncapped_left_voltage(uncapped_U_(0));
224 builder->add_uncapped_right_voltage(uncapped_U_(1));
225 builder->add_robot_speed(current_xva_(1));
226 builder->add_output_was_capped(output_was_capped_);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800227 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228}
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800229
James Kuszmaula5a418f2024-03-17 17:16:06 -0700230namespace {
231template <typename Matrix>
232flatbuffers::Offset<SplineState> MakeSplineState(
233 const Matrix &state, flatbuffers::FlatBufferBuilder *fbb) {
234 SplineState::Builder builder(*fbb);
235 builder.add_x(state(0));
236 builder.add_y(state(1));
237 builder.add_theta(::aos::math::NormalizeAngle(state(2)));
238 builder.add_left_velocity(state(3));
239 builder.add_right_velocity(state(4));
240 return builder.Finish();
241}
242} // namespace
243
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
245 flatbuffers::FlatBufferBuilder *builder) const {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800246 int *spline_handles;
247 const flatbuffers::Offset<flatbuffers::Vector<int>> handles_vector =
248 builder->CreateUninitializedVector(trajectories_.size(), &spline_handles);
249
250 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
James Kuszmauldc534432023-02-05 14:51:11 -0800251 spline_handles[ii] = trajectories_[ii].spline_handle();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800252 }
253
James Kuszmaula5a418f2024-03-17 17:16:06 -0700254 const flatbuffers::Offset<SplineState> state_error_offset =
255 MakeSplineState(last_state_error_, builder);
256 const flatbuffers::Offset<SplineState> left_voltage_components_offset =
257 MakeSplineState(last_U_components_.row(0), builder);
258 const flatbuffers::Offset<SplineState> right_voltage_components_offset =
259 MakeSplineState(last_U_components_.row(1), builder);
260
Alex Perrycb7da4b2019-08-28 19:35:56 -0700261 drivetrain::TrajectoryLogging::Builder trajectory_logging_builder(*builder);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800262 if (executing_spline_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700263 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
264 trajectory_logging_builder.add_x(goal_state(0));
265 trajectory_logging_builder.add_y(goal_state(1));
Austin Schuh6bdcc372024-06-27 14:49:11 -0700266 const FinishedTrajectory *const trajectory = current_trajectory();
267 CHECK(trajectory != nullptr);
268 if (trajectory->drive_spline_backwards()) {
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700269 trajectory_logging_builder.add_left_velocity(-goal_state(4));
270 trajectory_logging_builder.add_right_velocity(-goal_state(3));
271 trajectory_logging_builder.add_theta(
272 ::aos::math::NormalizeAngle(goal_state(2) + M_PI));
273 } else {
274 trajectory_logging_builder.add_theta(
275 ::aos::math::NormalizeAngle(goal_state(2)));
276 trajectory_logging_builder.add_left_velocity(goal_state(3));
277 trajectory_logging_builder.add_right_velocity(goal_state(4));
278 }
James Kuszmaula5a418f2024-03-17 17:16:06 -0700279 trajectory_logging_builder.add_state_error(state_error_offset);
280 trajectory_logging_builder.add_left_voltage_components(
281 left_voltage_components_offset);
282 trajectory_logging_builder.add_right_voltage_components(
283 right_voltage_components_offset);
284 trajectory_logging_builder.add_left_ff_voltage(last_U_ff_(0));
285 trajectory_logging_builder.add_right_ff_voltage(last_U_ff_(1));
Alex Perrye32eabc2019-02-08 19:51:19 -0800286 }
James Kuszmauldc534432023-02-05 14:51:11 -0800287 trajectory_logging_builder.add_is_executing(!IsAtEnd() && executing_spline_);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800288 trajectory_logging_builder.add_is_executed(executing_spline_ && IsAtEnd());
289 if (commanded_spline_) {
290 trajectory_logging_builder.add_goal_spline_handle(*commanded_spline_);
291 if (executing_spline_) {
292 trajectory_logging_builder.add_current_spline_idx(*commanded_spline_);
293 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700294 }
Austin Schuh6bdcc372024-06-27 14:49:11 -0700295 if (executing_spline_) {
296 const FinishedTrajectory *const trajectory = current_trajectory();
297 CHECK(trajectory != nullptr);
298 trajectory_logging_builder.add_distance_remaining(trajectory->length() -
299 current_xva_.x());
300 } else {
301 trajectory_logging_builder.add_distance_remaining(0.0);
302 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800303 trajectory_logging_builder.add_available_splines(handles_vector);
Austin Schuh1a843772023-04-09 22:18:05 -0700304 trajectory_logging_builder.add_distance_traveled(
305 executing_spline_ ? current_xva_.x() : 0.0);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800306
Alex Perrycb7da4b2019-08-28 19:35:56 -0700307 return trajectory_logging_builder.Finish();
308}
309
310flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
311 aos::Sender<drivetrain::Status>::Builder *builder) const {
312 return MakeTrajectoryLogging(builder->fbb());
Alex Perrye32eabc2019-02-08 19:51:19 -0800313}
314
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800315} // namespace frc971::control_loops::drivetrain