blob: 5a4e5dabd22f314babbfb94941f9b431d86527f2 [file] [log] [blame]
Alex Perry731b4602019-02-02 22:13:01 -08001#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
2
3#include "Eigen/Dense"
James Kuszmaul75a18c52021-03-10 22:02:07 -08004#include "aos/json_to_flatbuffer.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07005#include "aos/realtime.h"
James Kuszmaulc73bb222019-04-07 12:15:35 -07006#include "aos/util/math.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include "frc971/control_loops/control_loops_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -08008#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "frc971/control_loops/drivetrain/drivetrain_goal_generated.h"
10#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
11#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Alex Perry731b4602019-02-02 22:13:01 -080012
Alex Perry731b4602019-02-02 22:13:01 -080013namespace frc971 {
14namespace control_loops {
15namespace drivetrain {
16
17SplineDrivetrain::SplineDrivetrain(const DrivetrainConfig<double> &dt_config)
James Kuszmaul75a18c52021-03-10 22:02:07 -080018 : dt_config_(dt_config),
Austin Schuhf7c65202022-11-04 21:28:20 -070019 velocity_drivetrain_(
20 std::make_shared<StateFeedbackLoop<2, 2, 2, double,
21 StateFeedbackHybridPlant<2, 2, 2>,
22 HybridKalman<2, 2, 2>>>(
23 dt_config_.make_hybrid_drivetrain_velocity_loop())),
James Kuszmaul75a18c52021-03-10 22:02:07 -080024 current_xva_(Eigen::Vector3d::Zero()),
25 next_xva_(Eigen::Vector3d::Zero()),
26 next_U_(Eigen::Vector2d::Zero()) {}
Alex Perrye32eabc2019-02-08 19:51:19 -080027
28void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -080029 output_was_capped_ =
30 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Alex Perrye32eabc2019-02-08 19:51:19 -080031
Alex Perrycc3ee4c2019-02-09 21:20:41 -080032 if (output_was_capped_) {
Alex Perrye32eabc2019-02-08 19:51:19 -080033 *U *= 12.0 / U->lpNorm<Eigen::Infinity>();
34 }
35}
Alex Perry731b4602019-02-02 22:13:01 -080036
Alex Perrycc3ee4c2019-02-09 21:20:41 -080037void SplineDrivetrain::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -070038 const ::frc971::control_loops::drivetrain::Goal *goal) {
James Kuszmaul75a18c52021-03-10 22:02:07 -080039 if (goal->has_spline_handle()) {
40 commanded_spline_ = goal->spline_handle();
Austin Schuhb574fe42019-12-06 23:51:47 -080041 } else {
James Kuszmaul75a18c52021-03-10 22:02:07 -080042 commanded_spline_.reset();
Alex Perrya71badb2019-02-06 19:40:41 -080043 }
James Kuszmaul75a18c52021-03-10 22:02:07 -080044 UpdateSplineHandles();
45}
46
James Kuszmaul99af8b52021-03-28 10:50:15 -070047bool SplineDrivetrain::IsCurrentTrajectory(
48 const fb::Trajectory *trajectory) const {
49 return (current_trajectory_ != nullptr &&
50 current_trajectory().spline_handle() == trajectory->handle());
51}
52
James Kuszmaul75a18c52021-03-10 22:02:07 -080053bool SplineDrivetrain::HasTrajectory(const fb::Trajectory *trajectory) const {
54 if (trajectory == nullptr) {
55 return false;
56 }
57 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
58 if (trajectories_[ii]->spline_handle() == trajectory->handle()) {
59 return true;
60 }
61 }
62 return false;
63}
64
James Kuszmaul99af8b52021-03-28 10:50:15 -070065void SplineDrivetrain::DeleteTrajectory(const fb::Trajectory *trajectory) {
66 CHECK(trajectory != nullptr);
67
68 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
69 if (trajectories_[ii]->spline_handle() == trajectory->handle()) {
70 trajectories_.erase(trajectories_.begin() + ii);
71 return;
72 }
73 }
74
75 LOG(FATAL) << "Trying to remove unknown trajectory " << trajectory->handle();
76}
77
James Kuszmaul75a18c52021-03-10 22:02:07 -080078void SplineDrivetrain::AddTrajectory(const fb::Trajectory *trajectory) {
Austin Schuhf7c65202022-11-04 21:28:20 -070079 trajectories_.emplace_back(std::make_unique<FinishedTrajectory>(
80 dt_config_, trajectory, velocity_drivetrain_));
James Kuszmaul75a18c52021-03-10 22:02:07 -080081 UpdateSplineHandles();
82}
83
84void SplineDrivetrain::DeleteCurrentSpline() {
James Kuszmaul99af8b52021-03-28 10:50:15 -070085 DeleteTrajectory(&CHECK_NOTNULL(current_trajectory_)->trajectory());
James Kuszmaul75a18c52021-03-10 22:02:07 -080086 executing_spline_ = false;
James Kuszmaul99af8b52021-03-28 10:50:15 -070087 current_trajectory_ = nullptr;
James Kuszmaul75a18c52021-03-10 22:02:07 -080088 current_xva_.setZero();
89}
90
91void SplineDrivetrain::UpdateSplineHandles() {
92 // If we are currently executing a spline and have received a change
93 if (executing_spline_) {
94 if (!commanded_spline_) {
95 // We've been told to stop executing a spline; remove it from our queue,
96 // and clean up.
97 DeleteCurrentSpline();
98 return;
99 } else {
100 if (executing_spline_ &&
101 current_trajectory().spline_handle() != *commanded_spline_) {
102 // If we are executing a spline, and the handle has changed, garbage
103 // collect the old spline.
104 DeleteCurrentSpline();
105 }
106 }
107 }
108 // 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) {
113 if (trajectories_[ii]->spline_handle() == *commanded_spline_) {
114 executing_spline_ = true;
James Kuszmaul99af8b52021-03-28 10:50:15 -0700115 current_trajectory_ = trajectories_[ii].get();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800116 }
117 }
118 // If we didn't find the commanded spline in the list of available splines,
119 // that's fine; it just means, it hasn't been fully planned yet.
Alex Perrya71badb2019-02-06 19:40:41 -0800120}
121
Alex Perrye32eabc2019-02-08 19:51:19 -0800122// TODO(alex): Hold position when done following the spline.
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700123void SplineDrivetrain::Update(
124 bool enable, const ::Eigen::Matrix<double, 5, 1> &state,
125 const ::Eigen::Matrix<double, 2, 1> &voltage_error) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800126 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800127 enable_ = enable;
James Kuszmaul75a18c52021-03-10 22:02:07 -0800128 if (enable && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800129 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
James Kuszmaul75a18c52021-03-10 22:02:07 -0800130 if (!IsAtEnd() && executing_spline_) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800131 // TODO(alex): It takes about a cycle for the outputs to propagate to the
132 // motors. Consider delaying the output by a cycle.
James Kuszmaul75a18c52021-03-10 22:02:07 -0800133 U_ff = current_trajectory().FFVoltage(current_xva_(0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800134 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800135
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700136 const double current_distance = current_xva_(0);
Alex Perrye32eabc2019-02-08 19:51:19 -0800137 ::Eigen::Matrix<double, 2, 5> K =
James Kuszmaul75a18c52021-03-10 22:02:07 -0800138 current_trajectory().GainForDistance(current_distance);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800139 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700140 const bool backwards = current_trajectory().drive_spline_backwards();
141 if (backwards) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700142 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
143 U_ff = -swapU;
144 goal_state(2, 0) += M_PI;
145 double left_goal = goal_state(3, 0);
146 double right_goal = goal_state(4, 0);
147 goal_state(3, 0) = -right_goal;
148 goal_state(4, 0) = -left_goal;
149 }
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700150 const Eigen::Matrix<double, 5, 1> relative_goal =
James Kuszmaul75a18c52021-03-10 22:02:07 -0800151 current_trajectory().StateToPathRelativeState(current_distance,
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700152 goal_state, backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700153 const Eigen::Matrix<double, 5, 1> relative_state =
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700154 current_trajectory().StateToPathRelativeState(current_distance, state,
155 backwards);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700156 Eigen::Matrix<double, 5, 1> state_error = relative_goal - relative_state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700157 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800158 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800159
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700160 if (backwards) {
161 Eigen::Matrix<double, 2, 1> swapU(U_fb(1), U_fb(0));
162 U_fb = -swapU;
163 }
164
Austin Schuhd749d932020-12-30 21:38:40 -0800165 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2, 1>(0, 0);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800166 next_xva_ = current_trajectory().GetNextXVA(dt_config_.dt, &xv_state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700167 next_U_ = U_ff + U_fb - voltage_error;
Alex Perrye32eabc2019-02-08 19:51:19 -0800168 uncapped_U_ = next_U_;
169 ScaleCapU(&next_U_);
Alex Perry731b4602019-02-02 22:13:01 -0800170 }
171}
172
Alex Perry731b4602019-02-02 22:13:01 -0800173void SplineDrivetrain::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700174 ::frc971::control_loops::drivetrain::OutputT *output) {
Alex Perry731b4602019-02-02 22:13:01 -0800175 if (!output) {
176 return;
177 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800178 if (executing_spline_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800179 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800180 output->left_voltage = next_U_(0);
181 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800182 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800183 }
184 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800185 output->left_voltage = next_U_(0);
186 output->right_voltage = next_U_(1);
Alex Perry731b4602019-02-02 22:13:01 -0800187}
188
Alex Perrye32eabc2019-02-08 19:51:19 -0800189void SplineDrivetrain::PopulateStatus(
Austin Schuhd749d932020-12-30 21:38:40 -0800190 drivetrain::Status::Builder *builder) const {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700191 if (builder && enable_) {
192 builder->add_uncapped_left_voltage(uncapped_U_(0));
193 builder->add_uncapped_right_voltage(uncapped_U_(1));
194 builder->add_robot_speed(current_xva_(1));
195 builder->add_output_was_capped(output_was_capped_);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800196 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700197}
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800198
Alex Perrycb7da4b2019-08-28 19:35:56 -0700199flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
200 flatbuffers::FlatBufferBuilder *builder) const {
James Kuszmaul75a18c52021-03-10 22:02:07 -0800201 int *spline_handles;
202 const flatbuffers::Offset<flatbuffers::Vector<int>> handles_vector =
203 builder->CreateUninitializedVector(trajectories_.size(), &spline_handles);
204
205 for (size_t ii = 0; ii < trajectories_.size(); ++ii) {
206 spline_handles[ii] = trajectories_[ii]->spline_handle();
207 }
208
Alex Perrycb7da4b2019-08-28 19:35:56 -0700209 drivetrain::TrajectoryLogging::Builder trajectory_logging_builder(*builder);
James Kuszmaul75a18c52021-03-10 22:02:07 -0800210 if (executing_spline_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700211 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
212 trajectory_logging_builder.add_x(goal_state(0));
213 trajectory_logging_builder.add_y(goal_state(1));
James Kuszmaul5e8ce312021-03-27 14:59:17 -0700214 if (current_trajectory().drive_spline_backwards()) {
215 trajectory_logging_builder.add_left_velocity(-goal_state(4));
216 trajectory_logging_builder.add_right_velocity(-goal_state(3));
217 trajectory_logging_builder.add_theta(
218 ::aos::math::NormalizeAngle(goal_state(2) + M_PI));
219 } else {
220 trajectory_logging_builder.add_theta(
221 ::aos::math::NormalizeAngle(goal_state(2)));
222 trajectory_logging_builder.add_left_velocity(goal_state(3));
223 trajectory_logging_builder.add_right_velocity(goal_state(4));
224 }
Alex Perrye32eabc2019-02-08 19:51:19 -0800225 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700226 trajectory_logging_builder.add_is_executing(!IsAtEnd() &&
James Kuszmaul75a18c52021-03-10 22:02:07 -0800227 executing_spline_);
228 trajectory_logging_builder.add_is_executed(executing_spline_ && IsAtEnd());
229 if (commanded_spline_) {
230 trajectory_logging_builder.add_goal_spline_handle(*commanded_spline_);
231 if (executing_spline_) {
232 trajectory_logging_builder.add_current_spline_idx(*commanded_spline_);
233 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700234 }
James Kuszmaul75a18c52021-03-10 22:02:07 -0800235 trajectory_logging_builder.add_distance_remaining(
236 executing_spline_ ? current_trajectory().length() - current_xva_.x()
237 : 0.0);
238 trajectory_logging_builder.add_available_splines(handles_vector);
239
Alex Perrycb7da4b2019-08-28 19:35:56 -0700240 return trajectory_logging_builder.Finish();
241}
242
243flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
244 aos::Sender<drivetrain::Status>::Builder *builder) const {
245 return MakeTrajectoryLogging(builder->fbb());
Alex Perrye32eabc2019-02-08 19:51:19 -0800246}
247
Alex Perry731b4602019-02-02 22:13:01 -0800248} // namespace drivetrain
249} // namespace control_loops
250} // namespace frc971