blob: de593558720fce31ef20564278d76f65ddcdea73 [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 Perry1ec34522019-02-17 22:44:10 -08005#include "aos/init.h"
James Kuszmaulc73bb222019-04-07 12:15:35 -07006#include "aos/util/math.h"
Alex Perry731b4602019-02-02 22:13:01 -08007#include "frc971/control_loops/drivetrain/drivetrain.q.h"
8#include "frc971/control_loops/drivetrain/drivetrain_config.h"
9
Alex Perry731b4602019-02-02 22:13:01 -080010namespace frc971 {
11namespace control_loops {
12namespace drivetrain {
13
14SplineDrivetrain::SplineDrivetrain(const DrivetrainConfig<double> &dt_config)
Alex Perrycc3ee4c2019-02-09 21:20:41 -080015 : dt_config_(dt_config), new_goal_(&mutex_) {
16 worker_thread_ = std::thread(&SplineDrivetrain::ComputeTrajectory, this);
17}
Alex Perrye32eabc2019-02-08 19:51:19 -080018
19void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -080020 output_was_capped_ =
21 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Alex Perrye32eabc2019-02-08 19:51:19 -080022
Alex Perrycc3ee4c2019-02-09 21:20:41 -080023 if (output_was_capped_) {
Alex Perrye32eabc2019-02-08 19:51:19 -080024 *U *= 12.0 / U->lpNorm<Eigen::Infinity>();
25 }
26}
Alex Perry731b4602019-02-02 22:13:01 -080027
Alex Perrycc3ee4c2019-02-09 21:20:41 -080028void SplineDrivetrain::ComputeTrajectory() {
Alex Perry1ec34522019-02-17 22:44:10 -080029 ::aos::SetCurrentThreadRealtimePriority(1);
30
Alex Perrycc3ee4c2019-02-09 21:20:41 -080031 ::aos::MutexLocker locker(&mutex_);
32 while (run_) {
33 while (goal_.spline.spline_idx == future_spline_idx_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070034 AOS_CHECK(!new_goal_.Wait());
Alex Perrycc3ee4c2019-02-09 21:20:41 -080035 if (!run_) {
36 return;
37 }
38 }
39 past_distance_spline_.reset();
40 past_trajectory_.reset();
41
42 plan_state_ = PlanState::kBuildingTrajectory;
43 const ::frc971::MultiSpline &multispline = goal_.spline;
44 future_spline_idx_ = multispline.spline_idx;
Austin Schuh6bcc2302019-03-23 22:28:06 -070045 planning_spline_idx_ = future_spline_idx_;
Alex Perry731b4602019-02-02 22:13:01 -080046 auto x = multispline.spline_x;
47 auto y = multispline.spline_y;
48 ::std::vector<Spline> splines = ::std::vector<Spline>();
49 for (int i = 0; i < multispline.spline_count; ++i) {
50 ::Eigen::Matrix<double, 2, 6> points =
51 ::Eigen::Matrix<double, 2, 6>::Zero();
52 for (int j = 0; j < 6; ++j) {
53 points(0, j) = x[i * 5 + j];
54 points(1, j) = y[i * 5 + j];
55 }
56 splines.emplace_back(Spline(points));
57 }
58
James Kuszmaul29e417d2019-04-13 10:03:35 -070059 future_drive_spline_backwards_ = goal_.spline.drive_spline_backwards;
60
Alex Perrycc3ee4c2019-02-09 21:20:41 -080061 future_distance_spline_ = ::std::unique_ptr<DistanceSpline>(
Alex Perry731b4602019-02-02 22:13:01 -080062 new DistanceSpline(::std::move(splines)));
63
Alex Perrycc3ee4c2019-02-09 21:20:41 -080064 future_trajectory_ = ::std::unique_ptr<Trajectory>(
65 new Trajectory(future_distance_spline_.get(), dt_config_));
Alex Perry731b4602019-02-02 22:13:01 -080066
Alex Perrycc3ee4c2019-02-09 21:20:41 -080067 for (size_t i = 0; i < multispline.constraints.size(); ++i) {
Alex Perry731b4602019-02-02 22:13:01 -080068 const ::frc971::Constraint &constraint = multispline.constraints[i];
69 switch (constraint.constraint_type) {
70 case 0:
71 break;
72 case 1:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080073 future_trajectory_->set_longitudal_acceleration(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080074 break;
75 case 2:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080076 future_trajectory_->set_lateral_acceleration(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080077 break;
78 case 3:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080079 future_trajectory_->set_voltage_limit(constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080080 break;
81 case 4:
Alex Perrycc3ee4c2019-02-09 21:20:41 -080082 future_trajectory_->LimitVelocity(constraint.start_distance,
83 constraint.end_distance,
84 constraint.value);
Alex Perry731b4602019-02-02 22:13:01 -080085 break;
86 }
87 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -080088 plan_state_ = PlanState::kPlanningTrajectory;
Alex Perry731b4602019-02-02 22:13:01 -080089
Alex Perrycc3ee4c2019-02-09 21:20:41 -080090 future_trajectory_->Plan();
91 plan_state_ = PlanState::kPlannedTrajectory;
92 }
93}
94
95void SplineDrivetrain::SetGoal(
96 const ::frc971::control_loops::DrivetrainQueue::Goal &goal) {
97 current_spline_handle_ = goal.spline_handle;
James Kuszmaul29e417d2019-04-13 10:03:35 -070098 // If told to stop, set the executing spline to an invalid index and clear out
99 // its plan:
100 if (current_spline_handle_ == 0 &&
101 current_spline_idx_ != goal.spline.spline_idx) {
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800102 current_spline_idx_ = -1;
103 }
104
105 ::aos::Mutex::State mutex_state = mutex_.TryLock();
106 if (mutex_state == ::aos::Mutex::State::kLocked) {
107 if (goal.spline.spline_idx && future_spline_idx_ != goal.spline.spline_idx) {
108 goal_ = goal;
109 new_goal_.Broadcast();
James Kuszmaul29e417d2019-04-13 10:03:35 -0700110 if (current_spline_handle_ != current_spline_idx_) {
111 // If we aren't going to actively execute the current spline, evict it's
112 // plan.
113 past_trajectory_ = std::move(current_trajectory_);
114 past_distance_spline_ = std::move(current_distance_spline_);
115 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800116 }
James Kuszmaul29e417d2019-04-13 10:03:35 -0700117 // If you never started executing the previous spline, you're screwed.
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800118 if (future_trajectory_ &&
119 (!current_trajectory_ ||
120 current_trajectory_->is_at_end(current_xva_.block<2, 1>(0, 0)) ||
121 current_spline_idx_ == -1)) {
122 // Move current data to other variables to be cleared by worker.
123 past_trajectory_ = std::move(current_trajectory_);
124 past_distance_spline_ = std::move(current_distance_spline_);
125
126 // Move the computed data to be executed.
127 current_trajectory_ = std::move(future_trajectory_);
128 current_distance_spline_ = std::move(future_distance_spline_);
James Kuszmaul29e417d2019-04-13 10:03:35 -0700129 current_drive_spline_backwards_ = future_drive_spline_backwards_;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800130 current_spline_idx_ = future_spline_idx_;
131
132 // Reset internal state to a trajectory start position.
133 current_xva_ = current_trajectory_->FFAcceleration(0);
134 current_xva_(1) = 0.0;
Alex Perry4b502a92019-04-06 22:00:38 -0700135 has_started_execution_ = false;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800136 }
137 mutex_.Unlock();
Alex Perrya71badb2019-02-06 19:40:41 -0800138 }
139}
140
Alex Perrye32eabc2019-02-08 19:51:19 -0800141// TODO(alex): Hold position when done following the spline.
142// TODO(Austin): Compensate for voltage error.
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800143void SplineDrivetrain::Update(bool enable, const ::Eigen::Matrix<double, 5, 1> &state) {
144 next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrye32eabc2019-02-08 19:51:19 -0800145 enable_ = enable;
146 if (enable && current_trajectory_) {
147 ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero();
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800148 if (!IsAtEnd() &&
149 current_spline_handle_ == current_spline_idx_) {
Alex Perry4b502a92019-04-06 22:00:38 -0700150 has_started_execution_ = true;
Alex Perrye32eabc2019-02-08 19:51:19 -0800151 // TODO(alex): It takes about a cycle for the outputs to propagate to the
152 // motors. Consider delaying the output by a cycle.
153 U_ff = current_trajectory_->FFVoltage(current_xva_(0));
154 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800155
Alex Perrye32eabc2019-02-08 19:51:19 -0800156 ::Eigen::Matrix<double, 2, 5> K =
157 current_trajectory_->KForState(state, dt_config_.dt, Q, R);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800158 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
James Kuszmaul29e417d2019-04-13 10:03:35 -0700159 if (current_drive_spline_backwards_) {
James Kuszmaulc73bb222019-04-07 12:15:35 -0700160 ::Eigen::Matrix<double, 2, 1> swapU(U_ff(1, 0), U_ff(0, 0));
161 U_ff = -swapU;
162 goal_state(2, 0) += M_PI;
163 double left_goal = goal_state(3, 0);
164 double right_goal = goal_state(4, 0);
165 goal_state(3, 0) = -right_goal;
166 goal_state(4, 0) = -left_goal;
167 }
Alex Perrye32eabc2019-02-08 19:51:19 -0800168 ::Eigen::Matrix<double, 5, 1> state_error = goal_state - state;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700169 state_error(2, 0) = ::aos::math::NormalizeAngle(state_error(2, 0));
Alex Perrye32eabc2019-02-08 19:51:19 -0800170 ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800171
172 ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2,1>(0,0);
173 next_xva_ = current_trajectory_->GetNextXVA(dt_config_.dt, &xv_state);
Alex Perrye32eabc2019-02-08 19:51:19 -0800174 next_U_ = U_ff + U_fb;
175 uncapped_U_ = next_U_;
176 ScaleCapU(&next_U_);
Alex Perry731b4602019-02-02 22:13:01 -0800177 }
178}
179
Alex Perry731b4602019-02-02 22:13:01 -0800180void SplineDrivetrain::SetOutput(
181 ::frc971::control_loops::DrivetrainQueue::Output *output) {
182 if (!output) {
183 return;
184 }
185 if (current_spline_handle_ == current_spline_idx_) {
James Kuszmaul1057ce82019-02-09 17:58:24 -0800186 if (!IsAtEnd()) {
Alex Perrye32eabc2019-02-08 19:51:19 -0800187 output->left_voltage = next_U_(0);
188 output->right_voltage = next_U_(1);
Alex Perrya71badb2019-02-06 19:40:41 -0800189 current_xva_ = next_xva_;
Alex Perry731b4602019-02-02 22:13:01 -0800190 }
191 }
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800192 output->left_voltage = next_U_(0);
193 output->right_voltage = next_U_(1);
Alex Perry731b4602019-02-02 22:13:01 -0800194}
195
Alex Perrye32eabc2019-02-08 19:51:19 -0800196void SplineDrivetrain::PopulateStatus(
197 ::frc971::control_loops::DrivetrainQueue::Status *status) const {
198 if (status && enable_) {
199 status->uncapped_left_voltage = uncapped_U_(0);
200 status->uncapped_right_voltage = uncapped_U_(1);
201 status->robot_speed = current_xva_(1);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800202 status->output_was_capped = output_was_capped_;
203 }
204
205 if (status) {
206 if (current_distance_spline_) {
207 ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState();
208 status->trajectory_logging.x = goal_state(0);
209 status->trajectory_logging.y = goal_state(1);
James Kuszmaul29e417d2019-04-13 10:03:35 -0700210 status->trajectory_logging.theta = ::aos::math::NormalizeAngle(
211 goal_state(2) + (current_drive_spline_backwards_ ? M_PI : 0.0));
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800212 status->trajectory_logging.left_velocity = goal_state(3);
213 status->trajectory_logging.right_velocity = goal_state(4);
214 }
215 status->trajectory_logging.planning_state = static_cast<int8_t>(plan_state_.load());
Alex Perry4b502a92019-04-06 22:00:38 -0700216 status->trajectory_logging.is_executing = !IsAtEnd() && has_started_execution_;
James Kuszmaul29e417d2019-04-13 10:03:35 -0700217 status->trajectory_logging.is_executed =
218 (current_spline_idx_ != -1) && IsAtEnd();
Austin Schuh6bcc2302019-03-23 22:28:06 -0700219 status->trajectory_logging.goal_spline_handle = current_spline_handle_;
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800220 status->trajectory_logging.current_spline_idx = current_spline_idx_;
James Kuszmaul5a543412019-04-13 15:49:42 -0700221 status->trajectory_logging.distance_remaining =
222 current_trajectory_ ? current_trajectory_->length() - current_xva_.x()
223 : 0.0;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700224
225 int32_t planning_spline_idx = planning_spline_idx_;
226 if (current_spline_idx_ == planning_spline_idx) {
227 status->trajectory_logging.planning_spline_idx = 0;
228 } else {
229 status->trajectory_logging.planning_spline_idx = planning_spline_idx_;
230 }
Alex Perrye32eabc2019-02-08 19:51:19 -0800231 }
232}
233
Alex Perry731b4602019-02-02 22:13:01 -0800234} // namespace drivetrain
235} // namespace control_loops
236} // namespace frc971