blob: 423465aa13215b902a5fa858373414b300b3acea [file] [log] [blame]
Alex Perry731b4602019-02-02 22:13:01 -08001#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
2
3#include "Eigen/Dense"
4
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)
Alex Perrycc3ee4c2019-02-09 21:20:41 -080018 : dt_config_(dt_config), new_goal_(&mutex_) {
19 worker_thread_ = std::thread(&SplineDrivetrain::ComputeTrajectory, this);
20}
Alex Perrye32eabc2019-02-08 19:51:19 -080021
22void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -080023 output_was_capped_ =
24 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Alex Perrye32eabc2019-02-08 19:51:19 -080025
Alex Perrycc3ee4c2019-02-09 21:20:41 -080026 if (output_was_capped_) {
Alex Perrye32eabc2019-02-08 19:51:19 -080027 *U *= 12.0 / U->lpNorm<Eigen::Infinity>();
28 }
29}
Alex Perry731b4602019-02-02 22:13:01 -080030
Alex Perrycc3ee4c2019-02-09 21:20:41 -080031void SplineDrivetrain::ComputeTrajectory() {
Alex Perry1ec34522019-02-17 22:44:10 -080032 ::aos::SetCurrentThreadRealtimePriority(1);
33
Alex Perrycc3ee4c2019-02-09 21:20:41 -080034 ::aos::MutexLocker locker(&mutex_);
35 while (run_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070036 while (goal_.spline_idx == future_spline_idx_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070037 AOS_CHECK(!new_goal_.Wait());
Alex Perrycc3ee4c2019-02-09 21:20:41 -080038 if (!run_) {
39 return;
40 }
41 }
42 past_distance_spline_.reset();
43 past_trajectory_.reset();
44
Austin Schuh872723c2019-12-25 14:38:09 -080045 plan_state_ = PlanningState::BUILDING_TRAJECTORY;
Alex Perrycb7da4b2019-08-28 19:35:56 -070046 future_spline_idx_ = goal_.spline_idx;
Austin Schuh6bcc2302019-03-23 22:28:06 -070047 planning_spline_idx_ = future_spline_idx_;
Alex Perrycb7da4b2019-08-28 19:35:56 -070048 const MultiSpline &multispline = goal_.spline;
Alex Perry731b4602019-02-02 22:13:01 -080049 auto x = multispline.spline_x;
50 auto y = multispline.spline_y;
51 ::std::vector<Spline> splines = ::std::vector<Spline>();
52 for (int i = 0; i < multispline.spline_count; ++i) {
53 ::Eigen::Matrix<double, 2, 6> points =
54 ::Eigen::Matrix<double, 2, 6>::Zero();
55 for (int j = 0; j < 6; ++j) {
56 points(0, j) = x[i * 5 + j];
57 points(1, j) = y[i * 5 + j];
58 }
59 splines.emplace_back(Spline(points));
60 }
61
Alex Perrycb7da4b2019-08-28 19:35:56 -070062 future_drive_spline_backwards_ = goal_.drive_spline_backwards;
James Kuszmaul29e417d2019-04-13 10:03:35 -070063
Alex Perrycc3ee4c2019-02-09 21:20:41 -080064 future_distance_spline_ = ::std::unique_ptr<DistanceSpline>(
Alex Perry731b4602019-02-02 22:13:01 -080065 new DistanceSpline(::std::move(splines)));
66
Alex Perrycc3ee4c2019-02-09 21:20:41 -080067 future_trajectory_ = ::std::unique_ptr<Trajectory>(
68 new Trajectory(future_distance_spline_.get(), dt_config_));
Alex Perry731b4602019-02-02 22:13:01 -080069
Alex Perrycc3ee4c2019-02-09 21:20:41 -080070 for (size_t i = 0; i < multispline.constraints.size(); ++i) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070071 const ::frc971::ConstraintT &constraint = multispline.constraints[i];
Alex Perry731b4602019-02-02 22:13:01 -080072 switch (constraint.constraint_type) {
Austin Schuh872723c2019-12-25 14:38:09 -080073 case frc971::ConstraintType::CONSTRAINT_TYPE_UNDEFINED:
Alex Perry731b4602019-02-02 22:13:01 -080074 break;
Austin Schuh872723c2019-12-25 14:38:09 -080075 case frc971::ConstraintType::LONGITUDINAL_ACCELERATION:
James Kuszmaulea314d92019-02-18 19:45:06 -080076 future_trajectory_->set_longitudinal_acceleration(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080077 break;
Austin Schuh872723c2019-12-25 14:38:09 -080078 case frc971::ConstraintType::LATERAL_ACCELERATION:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080079 future_trajectory_->set_lateral_acceleration(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080080 break;
Austin Schuh872723c2019-12-25 14:38:09 -080081 case frc971::ConstraintType::VOLTAGE:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080082 future_trajectory_->set_voltage_limit(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080083 break;
Austin Schuh872723c2019-12-25 14:38:09 -080084 case frc971::ConstraintType::VELOCITY:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080085 future_trajectory_->LimitVelocity(constraint.start_distance,
86 constraint.end_distance,
87 constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080088 break;
89 }
90 }
Austin Schuh872723c2019-12-25 14:38:09 -080091 plan_state_ = PlanningState::PLANNING_TRAJECTORY;
Alex Perry731b4602019-02-02 22:13:01 -080092
Alex Perrycc3ee4c2019-02-09 21:20:41 -080093 future_trajectory_->Plan();
Austin Schuh872723c2019-12-25 14:38:09 -080094 plan_state_ = PlanningState::PLANNED;
Alex Perrycc3ee4c2019-02-09 21:20:41 -080095 }
96}
97
98void SplineDrivetrain::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -070099 const ::frc971::control_loops::drivetrain::Goal *goal) {
100 current_spline_handle_ = goal->spline_handle();
101
James Kuszmaul29e417d2019-04-13 10:03:35 -0700102 // If told to stop, set the executing spline to an invalid index and clear out
103 // its plan:
104 if (current_spline_handle_ == 0 &&
Alex Perrycb7da4b2019-08-28 19:35:56 -0700105 (goal->spline() == nullptr ||
106 current_spline_idx_ != CHECK_NOTNULL(goal->spline())->spline_idx())) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800107 current_spline_idx_ = -1;
108 }
109
110 ::aos::Mutex::State mutex_state = mutex_.TryLock();
111 if (mutex_state == ::aos::Mutex::State::kLocked) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700112 if (goal->spline() != nullptr && goal->spline()->spline_idx() &&
113 future_spline_idx_ != goal->spline()->spline_idx()) {
114 CHECK_NOTNULL(goal->spline());
115 goal_.spline_idx = goal->spline()->spline_idx();
116 goal_.drive_spline_backwards = goal->spline()->drive_spline_backwards();
117
118 const frc971::MultiSpline *multispline = goal->spline()->spline();
119 CHECK_NOTNULL(multispline);
120
121 goal_.spline.spline_count = multispline->spline_count();
122
123 CHECK_EQ(multispline->spline_x()->size(),
124 static_cast<size_t>(goal_.spline.spline_count * 5 + 1));
125 CHECK_EQ(multispline->spline_y()->size(),
126 static_cast<size_t>(goal_.spline.spline_count * 5 + 1));
127
128 std::copy(multispline->spline_x()->begin(),
129 multispline->spline_x()->end(), goal_.spline.spline_x.begin());
130 std::copy(multispline->spline_y()->begin(),
131 multispline->spline_y()->end(), goal_.spline.spline_y.begin());
132
133 for (size_t i = 0; i < 6; ++i) {
134 if (multispline->constraints() != nullptr &&
135 i < multispline->constraints()->size()) {
136 multispline->constraints()->Get(i)->UnPackTo(
137 &goal_.spline.constraints[i]);
138 } else {
139 goal_.spline.constraints[i].constraint_type =
Austin Schuh872723c2019-12-25 14:38:09 -0800140 ConstraintType::CONSTRAINT_TYPE_UNDEFINED;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700141 }
142 }
143
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800144 new_goal_.Broadcast();
James Kuszmaul29e417d2019-04-13 10:03:35 -0700145 if (current_spline_handle_ != current_spline_idx_) {
146 // If we aren't going to actively execute the current spline, evict it's
147 // plan.
148 past_trajectory_ = std::move(current_trajectory_);
149 past_distance_spline_ = std::move(current_distance_spline_);
150 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800151 }
James Kuszmaul29e417d2019-04-13 10:03:35 -0700152 // If you never started executing the previous spline, you're screwed.
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800153 if (future_trajectory_ &&
154 (!current_trajectory_ ||
155 current_trajectory_->is_at_end(current_xva_.block<2, 1>(0, 0)) ||
156 current_spline_idx_ == -1)) {
157 // Move current data to other variables to be cleared by worker.
158 past_trajectory_ = std::move(current_trajectory_);
159 past_distance_spline_ = std::move(current_distance_spline_);
160
161 // Move the computed data to be executed.
162 current_trajectory_ = std::move(future_trajectory_);
163 current_distance_spline_ = std::move(future_distance_spline_);
James Kuszmaul29e417d2019-04-13 10:03:35 -0700164 current_drive_spline_backwards_ = future_drive_spline_backwards_;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800165 current_spline_idx_ = future_spline_idx_;
166
167 // Reset internal state to a trajectory start position.
168 current_xva_ = current_trajectory_->FFAcceleration(0);
169 current_xva_(1) = 0.0;
Alex Perry4b502a92019-04-06 22:00:38 -0700170 has_started_execution_ = false;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800171 }
172 mutex_.Unlock();
Austin Schuhb574fe42019-12-06 23:51:47 -0800173 } else {
174 VLOG(1) << "Failed to acquire trajectory lock.";
Alex Perrya71badb2019-02-06 19:40:41 -0800175 }
176}
177
Alex Perrye32eabc2019-02-08 19:51:19 -0800178// TODO(alex): Hold position when done following the spline.
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700179void SplineDrivetrain::Update(
180 bool enable, const ::Eigen::Matrix<double, 5, 1> &state,
181 const ::Eigen::Matrix<double, 2, 1> &voltage_error) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800182 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800183 enable_ = enable;
184 if (enable && current_trajectory_) {
185 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
Austin Schuhd749d932020-12-30 21:38:40 -0800186 if (!IsAtEnd() && current_spline_handle_ == current_spline_idx_) {
Alex Perry4b502a92019-04-06 22:00:38 -0700187 has_started_execution_ = true;
Alex Perrye32eabc2019-02-08 19:51:19 -0800188 // TODO(alex): It takes about a cycle for the outputs to propagate to the
189 // motors. Consider delaying the output by a cycle.
190 U_ff = current_trajectory_->FFVoltage(current_xva_(0));
191 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800192
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700193 const double current_distance = current_xva_(0);
Alex Perrye32eabc2019-02-08 19:51:19 -0800194 ::Eigen::Matrix<double, 2, 5> K =
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700195 current_trajectory_->GainForDistance(current_distance);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800196 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmaul29e417d2019-04-13 10:03:35 -0700197 if (current_drive_spline_backwards_) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700198 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
199 U_ff = -swapU;
200 goal_state(2, 0) += M_PI;
201 double left_goal = goal_state(3, 0);
202 double right_goal = goal_state(4, 0);
203 goal_state(3, 0) = -right_goal;
204 goal_state(4, 0) = -left_goal;
205 }
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700206 const Eigen::Matrix<double, 5, 1> relative_goal =
207 current_trajectory_->StateToPathRelativeState(current_distance,
208 goal_state);
209 const Eigen::Matrix<double, 5, 1> relative_state =
210 current_trajectory_->StateToPathRelativeState(current_distance, state);
211 Eigen::Matrix<double, 5, 1> state_error = relative_goal - relative_state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700212 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800213 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800214
Austin Schuhd749d932020-12-30 21:38:40 -0800215 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2, 1>(0, 0);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800216 next_xva_ = current_trajectory_->GetNextXVA(dt_config_.dt, &xv_state);
James Kuszmaulaa2499d2020-06-02 21:31:19 -0700217 next_U_ = U_ff + U_fb - voltage_error;
Alex Perrye32eabc2019-02-08 19:51:19 -0800218 uncapped_U_ = next_U_;
219 ScaleCapU(&next_U_);
Alex Perry731b4602019-02-02 22:13:01 -0800220 }
221}
222
Alex Perry731b4602019-02-02 22:13:01 -0800223void SplineDrivetrain::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700224 ::frc971::control_loops::drivetrain::OutputT *output) {
Alex Perry731b4602019-02-02 22:13:01 -0800225 if (!output) {
226 return;
227 }
228 if (current_spline_handle_ == current_spline_idx_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800229 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800230 output->left_voltage = next_U_(0);
231 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800232 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800233 }
234 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800235 output->left_voltage = next_U_(0);
236 output->right_voltage = next_U_(1);
Alex Perry731b4602019-02-02 22:13:01 -0800237}
238
Alex Perrye32eabc2019-02-08 19:51:19 -0800239void SplineDrivetrain::PopulateStatus(
Austin Schuhd749d932020-12-30 21:38:40 -0800240 drivetrain::Status::Builder *builder) const {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700241 if (builder && enable_) {
242 builder->add_uncapped_left_voltage(uncapped_U_(0));
243 builder->add_uncapped_right_voltage(uncapped_U_(1));
244 builder->add_robot_speed(current_xva_(1));
245 builder->add_output_was_capped(output_was_capped_);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800246 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700247}
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800248
Alex Perrycb7da4b2019-08-28 19:35:56 -0700249flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
250 flatbuffers::FlatBufferBuilder *builder) const {
251 drivetrain::TrajectoryLogging::Builder trajectory_logging_builder(*builder);
252 if (current_distance_spline_) {
253 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
254 trajectory_logging_builder.add_x(goal_state(0));
255 trajectory_logging_builder.add_y(goal_state(1));
256 trajectory_logging_builder.add_theta(::aos::math::NormalizeAngle(
257 goal_state(2) + (current_drive_spline_backwards_ ? M_PI : 0.0)));
258 trajectory_logging_builder.add_left_velocity(goal_state(3));
259 trajectory_logging_builder.add_right_velocity(goal_state(4));
Alex Perrye32eabc2019-02-08 19:51:19 -0800260 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700261 trajectory_logging_builder.add_planning_state(plan_state_.load());
262 trajectory_logging_builder.add_is_executing(!IsAtEnd() &&
263 has_started_execution_);
264 trajectory_logging_builder.add_is_executed((current_spline_idx_ != -1) &&
265 IsAtEnd());
266 trajectory_logging_builder.add_goal_spline_handle(current_spline_handle_);
267 trajectory_logging_builder.add_current_spline_idx(current_spline_idx_);
268 trajectory_logging_builder.add_distance_remaining(
269 current_trajectory_ ? current_trajectory_->length() - current_xva_.x()
270 : 0.0);
271
272 int32_t planning_spline_idx = planning_spline_idx_;
273 if (current_spline_idx_ == planning_spline_idx) {
274 trajectory_logging_builder.add_planning_spline_idx(0);
275 } else {
276 trajectory_logging_builder.add_planning_spline_idx(planning_spline_idx_);
277 }
278 return trajectory_logging_builder.Finish();
279}
280
281flatbuffers::Offset<TrajectoryLogging> SplineDrivetrain::MakeTrajectoryLogging(
282 aos::Sender<drivetrain::Status>::Builder *builder) const {
283 return MakeTrajectoryLogging(builder->fbb());
Alex Perrye32eabc2019-02-08 19:51:19 -0800284}
285
Alex Perry731b4602019-02-02 22:13:01 -0800286} // namespace drivetrain
287} // namespace control_loops
288} // namespace frc971