Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 1 | #include "frc971/control_loops/drivetrain/splinedrivetrain.h" |
| 2 | |
| 3 | #include "Eigen/Dense" |
| 4 | |
Alex Perry | 1ec3452 | 2019-02-17 22:44:10 -0800 | [diff] [blame] | 5 | #include "aos/init.h" |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 6 | #include "frc971/control_loops/drivetrain/drivetrain.q.h" |
| 7 | #include "frc971/control_loops/drivetrain/drivetrain_config.h" |
| 8 | |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 9 | namespace frc971 { |
| 10 | namespace control_loops { |
| 11 | namespace drivetrain { |
| 12 | |
| 13 | SplineDrivetrain::SplineDrivetrain(const DrivetrainConfig<double> &dt_config) |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 14 | : dt_config_(dt_config), new_goal_(&mutex_) { |
| 15 | worker_thread_ = std::thread(&SplineDrivetrain::ComputeTrajectory, this); |
| 16 | } |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 17 | |
| 18 | void SplineDrivetrain::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) { |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 19 | output_was_capped_ = |
| 20 | ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0; |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 21 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 22 | if (output_was_capped_) { |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 23 | *U *= 12.0 / U->lpNorm<Eigen::Infinity>(); |
| 24 | } |
| 25 | } |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 26 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 27 | void SplineDrivetrain::ComputeTrajectory() { |
Alex Perry | 1ec3452 | 2019-02-17 22:44:10 -0800 | [diff] [blame] | 28 | ::aos::SetCurrentThreadRealtimePriority(1); |
| 29 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 30 | ::aos::MutexLocker locker(&mutex_); |
| 31 | while (run_) { |
| 32 | while (goal_.spline.spline_idx == future_spline_idx_) { |
| 33 | CHECK(!new_goal_.Wait()); |
| 34 | if (!run_) { |
| 35 | return; |
| 36 | } |
| 37 | } |
| 38 | past_distance_spline_.reset(); |
| 39 | past_trajectory_.reset(); |
| 40 | |
| 41 | plan_state_ = PlanState::kBuildingTrajectory; |
| 42 | const ::frc971::MultiSpline &multispline = goal_.spline; |
| 43 | future_spline_idx_ = multispline.spline_idx; |
Austin Schuh | 6bcc230 | 2019-03-23 22:28:06 -0700 | [diff] [blame] | 44 | planning_spline_idx_ = future_spline_idx_; |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 45 | auto x = multispline.spline_x; |
| 46 | auto y = multispline.spline_y; |
| 47 | ::std::vector<Spline> splines = ::std::vector<Spline>(); |
| 48 | for (int i = 0; i < multispline.spline_count; ++i) { |
| 49 | ::Eigen::Matrix<double, 2, 6> points = |
| 50 | ::Eigen::Matrix<double, 2, 6>::Zero(); |
| 51 | for (int j = 0; j < 6; ++j) { |
| 52 | points(0, j) = x[i * 5 + j]; |
| 53 | points(1, j) = y[i * 5 + j]; |
| 54 | } |
| 55 | splines.emplace_back(Spline(points)); |
| 56 | } |
| 57 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 58 | future_distance_spline_ = ::std::unique_ptr<DistanceSpline>( |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 59 | new DistanceSpline(::std::move(splines))); |
| 60 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 61 | future_trajectory_ = ::std::unique_ptr<Trajectory>( |
| 62 | new Trajectory(future_distance_spline_.get(), dt_config_)); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 63 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 64 | for (size_t i = 0; i < multispline.constraints.size(); ++i) { |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 65 | const ::frc971::Constraint &constraint = multispline.constraints[i]; |
| 66 | switch (constraint.constraint_type) { |
| 67 | case 0: |
| 68 | break; |
| 69 | case 1: |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 70 | future_trajectory_->set_longitudal_acceleration(constraint.value); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 71 | break; |
| 72 | case 2: |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 73 | future_trajectory_->set_lateral_acceleration(constraint.value); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 74 | break; |
| 75 | case 3: |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 76 | future_trajectory_->set_voltage_limit(constraint.value); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 77 | break; |
| 78 | case 4: |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 79 | future_trajectory_->LimitVelocity(constraint.start_distance, |
| 80 | constraint.end_distance, |
| 81 | constraint.value); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 82 | break; |
| 83 | } |
| 84 | } |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 85 | plan_state_ = PlanState::kPlanningTrajectory; |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 86 | |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 87 | future_trajectory_->Plan(); |
| 88 | plan_state_ = PlanState::kPlannedTrajectory; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | void SplineDrivetrain::SetGoal( |
| 93 | const ::frc971::control_loops::DrivetrainQueue::Goal &goal) { |
| 94 | current_spline_handle_ = goal.spline_handle; |
| 95 | // If told to stop, set the executing spline to an invalid index. |
Alex Perry | 4b502a9 | 2019-04-06 22:00:38 -0700 | [diff] [blame] | 96 | if (current_spline_handle_ == 0 && has_started_execution_) { |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 97 | current_spline_idx_ = -1; |
| 98 | } |
| 99 | |
| 100 | ::aos::Mutex::State mutex_state = mutex_.TryLock(); |
| 101 | if (mutex_state == ::aos::Mutex::State::kLocked) { |
| 102 | if (goal.spline.spline_idx && future_spline_idx_ != goal.spline.spline_idx) { |
| 103 | goal_ = goal; |
| 104 | new_goal_.Broadcast(); |
| 105 | } |
| 106 | if (future_trajectory_ && |
| 107 | (!current_trajectory_ || |
| 108 | current_trajectory_->is_at_end(current_xva_.block<2, 1>(0, 0)) || |
| 109 | current_spline_idx_ == -1)) { |
| 110 | // Move current data to other variables to be cleared by worker. |
| 111 | past_trajectory_ = std::move(current_trajectory_); |
| 112 | past_distance_spline_ = std::move(current_distance_spline_); |
| 113 | |
| 114 | // Move the computed data to be executed. |
| 115 | current_trajectory_ = std::move(future_trajectory_); |
| 116 | current_distance_spline_ = std::move(future_distance_spline_); |
| 117 | current_spline_idx_ = future_spline_idx_; |
| 118 | |
| 119 | // Reset internal state to a trajectory start position. |
| 120 | current_xva_ = current_trajectory_->FFAcceleration(0); |
| 121 | current_xva_(1) = 0.0; |
Alex Perry | 4b502a9 | 2019-04-06 22:00:38 -0700 | [diff] [blame] | 122 | has_started_execution_ = false; |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 123 | } |
| 124 | mutex_.Unlock(); |
Alex Perry | a71badb | 2019-02-06 19:40:41 -0800 | [diff] [blame] | 125 | } |
| 126 | } |
| 127 | |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 128 | // TODO(alex): Hold position when done following the spline. |
| 129 | // TODO(Austin): Compensate for voltage error. |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 130 | void SplineDrivetrain::Update(bool enable, const ::Eigen::Matrix<double, 5, 1> &state) { |
| 131 | next_U_ = ::Eigen::Matrix<double, 2, 1>::Zero(); |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 132 | enable_ = enable; |
| 133 | if (enable && current_trajectory_) { |
| 134 | ::Eigen::Matrix<double, 2, 1> U_ff = ::Eigen::Matrix<double, 2, 1>::Zero(); |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 135 | if (!IsAtEnd() && |
| 136 | current_spline_handle_ == current_spline_idx_) { |
Alex Perry | 4b502a9 | 2019-04-06 22:00:38 -0700 | [diff] [blame] | 137 | has_started_execution_ = true; |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 138 | // TODO(alex): It takes about a cycle for the outputs to propagate to the |
| 139 | // motors. Consider delaying the output by a cycle. |
| 140 | U_ff = current_trajectory_->FFVoltage(current_xva_(0)); |
| 141 | } |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 142 | |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 143 | ::Eigen::Matrix<double, 2, 5> K = |
| 144 | current_trajectory_->KForState(state, dt_config_.dt, Q, R); |
James Kuszmaul | 1057ce8 | 2019-02-09 17:58:24 -0800 | [diff] [blame] | 145 | ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState(); |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 146 | ::Eigen::Matrix<double, 5, 1> state_error = goal_state - state; |
| 147 | ::Eigen::Matrix<double, 2, 1> U_fb = K * state_error; |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 148 | |
| 149 | ::Eigen::Matrix<double, 2, 1> xv_state = current_xva_.block<2,1>(0,0); |
| 150 | next_xva_ = current_trajectory_->GetNextXVA(dt_config_.dt, &xv_state); |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 151 | next_U_ = U_ff + U_fb; |
| 152 | uncapped_U_ = next_U_; |
| 153 | ScaleCapU(&next_U_); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 154 | } |
| 155 | } |
| 156 | |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 157 | void SplineDrivetrain::SetOutput( |
| 158 | ::frc971::control_loops::DrivetrainQueue::Output *output) { |
| 159 | if (!output) { |
| 160 | return; |
| 161 | } |
| 162 | if (current_spline_handle_ == current_spline_idx_) { |
James Kuszmaul | 1057ce8 | 2019-02-09 17:58:24 -0800 | [diff] [blame] | 163 | if (!IsAtEnd()) { |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 164 | output->left_voltage = next_U_(0); |
| 165 | output->right_voltage = next_U_(1); |
Alex Perry | a71badb | 2019-02-06 19:40:41 -0800 | [diff] [blame] | 166 | current_xva_ = next_xva_; |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 167 | } |
| 168 | } |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 169 | output->left_voltage = next_U_(0); |
| 170 | output->right_voltage = next_U_(1); |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 171 | } |
| 172 | |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 173 | void SplineDrivetrain::PopulateStatus( |
| 174 | ::frc971::control_loops::DrivetrainQueue::Status *status) const { |
| 175 | if (status && enable_) { |
| 176 | status->uncapped_left_voltage = uncapped_U_(0); |
| 177 | status->uncapped_right_voltage = uncapped_U_(1); |
| 178 | status->robot_speed = current_xva_(1); |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 179 | status->output_was_capped = output_was_capped_; |
| 180 | } |
| 181 | |
| 182 | if (status) { |
| 183 | if (current_distance_spline_) { |
| 184 | ::Eigen::Matrix<double, 5, 1> goal_state = CurrentGoalState(); |
| 185 | status->trajectory_logging.x = goal_state(0); |
| 186 | status->trajectory_logging.y = goal_state(1); |
| 187 | status->trajectory_logging.theta = goal_state(2); |
| 188 | status->trajectory_logging.left_velocity = goal_state(3); |
| 189 | status->trajectory_logging.right_velocity = goal_state(4); |
| 190 | } |
| 191 | status->trajectory_logging.planning_state = static_cast<int8_t>(plan_state_.load()); |
Alex Perry | 4b502a9 | 2019-04-06 22:00:38 -0700 | [diff] [blame] | 192 | status->trajectory_logging.is_executing = !IsAtEnd() && has_started_execution_; |
Austin Schuh | 6bcc230 | 2019-03-23 22:28:06 -0700 | [diff] [blame] | 193 | status->trajectory_logging.goal_spline_handle = current_spline_handle_; |
Alex Perry | cc3ee4c | 2019-02-09 21:20:41 -0800 | [diff] [blame] | 194 | status->trajectory_logging.current_spline_idx = current_spline_idx_; |
Austin Schuh | 6bcc230 | 2019-03-23 22:28:06 -0700 | [diff] [blame] | 195 | |
| 196 | int32_t planning_spline_idx = planning_spline_idx_; |
| 197 | if (current_spline_idx_ == planning_spline_idx) { |
| 198 | status->trajectory_logging.planning_spline_idx = 0; |
| 199 | } else { |
| 200 | status->trajectory_logging.planning_spline_idx = planning_spline_idx_; |
| 201 | } |
Alex Perry | e32eabc | 2019-02-08 19:51:19 -0800 | [diff] [blame] | 202 | } |
| 203 | } |
| 204 | |
Alex Perry | 731b460 | 2019-02-02 22:13:01 -0800 | [diff] [blame] | 205 | } // namespace drivetrain |
| 206 | } // namespace control_loops |
| 207 | } // namespace frc971 |