blob: f90e7e0970e981f28d4eac1d816faa7e1f2ab727 [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() {
James Kuszmauldc534432023-02-05 14:51:11 -080082 DeleteTrajectory(&CHECK_NOTNULL(current_trajectory())->trajectory());
James Kuszmaul75a18c52021-03-10 22:02:07 -080083 executing_spline_ = false;
James Kuszmauldc534432023-02-05 14:51:11 -080084 commanded_spline_.reset();
James Kuszmaul75a18c52021-03-10 22:02:07 -080085 current_xva_.setZero();
86}
87
James Kuszmauldc534432023-02-05 14:51:11 -080088void SplineDrivetrain::UpdateSplineHandles(
89 std::optional<int> commanded_spline) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080090 // If we are currently executing a spline and have received a change
91 if (executing_spline_) {
James Kuszmauldc534432023-02-05 14:51:11 -080092 if (!commanded_spline) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080093 // We've been told to stop executing a spline; remove it from our queue,
94 // and clean up.
95 DeleteCurrentSpline();
96 return;
97 } else {
98 if (executing_spline_ &&
James Kuszmauldc534432023-02-05 14:51:11 -080099 CHECK_NOTNULL(current_trajectory())->spline_handle() !=
100 *commanded_spline) {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800101 // If we are executing a spline, and the handle has changed, garbage
102 // collect the old spline.
103 DeleteCurrentSpline();
104 }
105 }
106 }
James Kuszmauldc534432023-02-05 14:51:11 -0800107 commanded_spline_ = commanded_spline;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800108 // We've now cleaned up the previous state; handle any new commands.
109 if (!commanded_spline_) {
110 return;
111 }
112 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
James Kuszmauldc534432023-02-05 14:51:11 -0800113 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800114 executing_spline_ = true;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800115 }
116 }
117 // If we didn't find the commanded spline in the list of available splines,
118 // that's fine; it just means, it hasn't been fully planned yet.
Alex Perrya71badb2019-02-06 19:40:41 -0800119}
120
James Kuszmauldc534432023-02-05 14:51:11 -0800121FinishedTrajectory *SplineDrivetrain::current_trajectory() {
122 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
123 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
124 return &trajectories_[ii];
125 }
126 }
127 return nullptr;
128}
129
130const FinishedTrajectory *SplineDrivetrain::current_trajectory() const {
131 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
132 if (trajectories_[ii].spline_handle() == *commanded_spline_) {
133 return &trajectories_[ii];
134 }
135 }
136 return nullptr;
137}
138
Alex Perrye32eabc2019-02-08 19:51:19 -0800139// TODO(alex): Hold position when done following the spline.
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700140void SplineDrivetrain::Update(
141 bool enable, const ::Eigen::Matrix<double, 5, 1> &state,
142 const ::Eigen::Matrix<double, 2, 1> &voltage_error) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800143 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800144 enable_ = enable;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800145 if (enable && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800146 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
James Kuszmauldc534432023-02-05 14:51:11 -0800147 const FinishedTrajectory *const trajectory =
148 CHECK_NOTNULL(current_trajectory());
James Kuszmaul75a18c52021-03-10 22:02:07 -0800149 if (!IsAtEnd() && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800150 // TODO(alex): It takes about a cycle for the outputs to propagate to the
151 // motors. Consider delaying the output by a cycle.
James Kuszmauldc534432023-02-05 14:51:11 -0800152 U_ff = trajectory->FFVoltage(current_xva_(0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800153 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800154
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700155 const double current_distance = current_xva_(0);
Alex Perrye32eabc2019-02-08 19:51:19 -0800156 ::Eigen::Matrix<double, 2, 5> K =
James Kuszmauldc534432023-02-05 14:51:11 -0800157 trajectory->GainForDistance(current_distance);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800158 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmauldc534432023-02-05 14:51:11 -0800159 const bool backwards = trajectory->drive_spline_backwards();
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700160 if (backwards) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700161 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
162 U_ff = -swapU;
163 goal_state(2, 0) += M_PI;
164 double left_goal = goal_state(3, 0);
165 double right_goal = goal_state(4, 0);
166 goal_state(3, 0) = -right_goal;
167 goal_state(4, 0) = -left_goal;
168 }
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700169 const Eigen::Matrix<double, 5, 1> relative_goal =
James Kuszmauldc534432023-02-05 14:51:11 -0800170 trajectory->StateToPathRelativeState(current_distance, goal_state,
171 backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700172 const Eigen::Matrix<double, 5, 1> relative_state =
James Kuszmauldc534432023-02-05 14:51:11 -0800173 trajectory->StateToPathRelativeState(current_distance, state,
174 backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700175 Eigen::Matrix<double, 5, 1> state_error = relative_goal - relative_state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700176 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800177 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800178
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700179 if (backwards) {
180 Eigen::Matrix<double, 2, 1> swapU(U_fb(1), U_fb(0));
181 U_fb = -swapU;
182 }
183
Austin Schuhd749d932020-12-30 21:38:40 -0800184 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2, 1>(0, 0);
James Kuszmauldc534432023-02-05 14:51:11 -0800185 next_xva_ = trajectory->GetNextXVA(dt_config_.dt, &xv_state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700186 next_U_ = U_ff + U_fb - voltage_error;
Alex Perrye32eabc2019-02-08 19:51:19 -0800187 uncapped_U_ = next_U_;
188 ScaleCapU(&next_U_);
Alex Perry731b4602019-02-02 22:13:01 -0800189 }
190}
191
Alex Perry731b4602019-02-02 22:13:01 -0800192void SplineDrivetrain::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700193 ::frc971::control_loops::drivetrain::OutputT *output) {
Alex Perry731b4602019-02-02 22:13:01 -0800194 if (!output) {
195 return;
196 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800197 if (executing_spline_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800198 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800199 output->left_voltage = next_U_(0);
200 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800201 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800202 }
203 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800204 output->left_voltage = next_U_(0);
205 output->right_voltage = next_U_(1);
Alex Perry731b4602019-02-02 22:13:01 -0800206}
207
Alex Perrye32eabc2019-02-08 19:51:19 -0800208void SplineDrivetrain::PopulateStatus(
Austin Schuhd749d932020-12-30 21:38:40 -0800209 drivetrain::Status::Builder *builder) const {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210 if (builder && enable_) {
211 builder->add_uncapped_left_voltage(uncapped_U_(0));
212 builder->add_uncapped_right_voltage(uncapped_U_(1));
213 builder->add_robot_speed(current_xva_(1));
214 builder->add_output_was_capped(output_was_capped_);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800215 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700216}
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800217
Alex Perrycb7da4b2019-08-28 19:35:56 -0700218flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
219 flatbuffers::FlatBufferBuilder *builder) const {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800220 int *spline_handles;
221 const flatbuffers::Offset<flatbuffers::Vector<int>> handles_vector =
222 builder->CreateUninitializedVector(trajectories_.size(), &spline_handles);
223
224 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
James Kuszmauldc534432023-02-05 14:51:11 -0800225 spline_handles[ii] = trajectories_[ii].spline_handle();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800226 }
227
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 drivetrain::TrajectoryLogging::Builder trajectory_logging_builder(*builder);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800229 if (executing_spline_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700230 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
231 trajectory_logging_builder.add_x(goal_state(0));
232 trajectory_logging_builder.add_y(goal_state(1));
James Kuszmauldc534432023-02-05 14:51:11 -0800233 if (CHECK_NOTNULL(current_trajectory())->drive_spline_backwards()) {
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700234 trajectory_logging_builder.add_left_velocity(-goal_state(4));
235 trajectory_logging_builder.add_right_velocity(-goal_state(3));
236 trajectory_logging_builder.add_theta(
237 ::aos::math::NormalizeAngle(goal_state(2) + M_PI));
238 } else {
239 trajectory_logging_builder.add_theta(
240 ::aos::math::NormalizeAngle(goal_state(2)));
241 trajectory_logging_builder.add_left_velocity(goal_state(3));
242 trajectory_logging_builder.add_right_velocity(goal_state(4));
243 }
Alex Perrye32eabc2019-02-08 19:51:19 -0800244 }
James Kuszmauldc534432023-02-05 14:51:11 -0800245 trajectory_logging_builder.add_is_executing(!IsAtEnd() && executing_spline_);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800246 trajectory_logging_builder.add_is_executed(executing_spline_ && IsAtEnd());
247 if (commanded_spline_) {
248 trajectory_logging_builder.add_goal_spline_handle(*commanded_spline_);
249 if (executing_spline_) {
250 trajectory_logging_builder.add_current_spline_idx(*commanded_spline_);
251 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700252 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800253 trajectory_logging_builder.add_distance_remaining(
James Kuszmauldc534432023-02-05 14:51:11 -0800254 executing_spline_
255 ? CHECK_NOTNULL(current_trajectory())->length() - current_xva_.x()
256 : 0.0);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800257 trajectory_logging_builder.add_available_splines(handles_vector);
Austin Schuh1a843772023-04-09 22:18:05 -0700258 trajectory_logging_builder.add_distance_traveled(
259 executing_spline_ ? current_xva_.x() : 0.0);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800260
Alex Perrycb7da4b2019-08-28 19:35:56 -0700261 return trajectory_logging_builder.Finish();
262}
263
264flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
265 aos::Sender<drivetrain::Status>::Builder *builder) const {
266 return MakeTrajectoryLogging(builder->fbb());
Alex Perrye32eabc2019-02-08 19:51:19 -0800267}
268
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800269} // namespace frc971::control_loops::drivetrain