blob: 292f291a5a865ff61f8c0338390d401876592faa [file] [log] [blame]
Comran Morshed5323ecb2015-12-26 20:50:55 +00001#include "frc971/control_loops/drivetrain/ssdrivetrain.h"
Austin Schuh64ebab22015-11-26 13:28:30 -08002
John Park33858a32018-09-28 23:05:48 -07003#include "aos/commonmath.h"
4#include "aos/controls/polytope.h"
5#include "aos/logging/matrix_logging.h"
Austin Schuh64ebab22015-11-26 13:28:30 -08006
Austin Schuh64ebab22015-11-26 13:28:30 -08007#include "frc971/control_loops/coerce_goal.h"
Comran Morshed5323ecb2015-12-26 20:50:55 +00008#include "frc971/control_loops/drivetrain/drivetrain.q.h"
9#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Austin Schuh0aae9242018-03-14 19:49:44 -070010#include "frc971/control_loops/state_feedback_loop.h"
Austin Schuh64ebab22015-11-26 13:28:30 -080011
Comran Morshed5323ecb2015-12-26 20:50:55 +000012namespace frc971 {
Austin Schuh64ebab22015-11-26 13:28:30 -080013namespace control_loops {
Austin Schuh6197a182015-11-28 16:04:40 -080014namespace drivetrain {
15
Austin Schuh06b65b82018-12-23 09:21:44 +110016DrivetrainMotorsSS::DrivetrainMotorsSS(
17 const DrivetrainConfig<double> &dt_config, StateFeedbackLoop<7, 2, 4> *kf,
18 double *integrated_kf_heading)
19 : dt_config_(dt_config),
20 kf_(kf),
21 U_poly_(
22 (Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
23 /*[*/ -1, 0 /*]*/,
24 /*[*/ 0, 1 /*]*/,
25 /*[*/ 0, -1 /*]]*/)
26 .finished(),
27 (Eigen::Matrix<double, 4, 1>() << /*[[*/ 1.0 /*]*/,
28 /*[*/ 1.0 /*]*/,
29 /*[*/ 1.0 /*]*/,
30 /*[*/ 1.0 /*]]*/)
31 .finished(),
32 (Eigen::Matrix<double, 2, 4>() << /*[[*/ 1.0, 1.0, -1.0, -1.0 /*]*/,
33 /*[*/ -1.0, 1.0, 1.0, -1.0 /*]*/)
34 .finished()),
35 linear_profile_(::aos::controls::kLoopFrequency),
36 angular_profile_(::aos::controls::kLoopFrequency),
37 integrated_kf_heading_(integrated_kf_heading) {
38 ::aos::controls::HPolytope<0>::Init();
39 T_ << 1, 1, 1, -1;
40 T_inverse_ = T_.inverse();
41 unprofiled_goal_.setZero();
42}
43
Austin Schuh093535c2016-03-05 23:21:00 -080044void DrivetrainMotorsSS::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070045 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
46 ::std::abs((*U)(1, 0)) > max_voltage_;
Austin Schuh093535c2016-03-05 23:21:00 -080047
48 if (output_was_capped_) {
Austin Schuh0aae9242018-03-14 19:49:44 -070049 *U *= max_voltage_ / kf_->U_uncapped().lpNorm<Eigen::Infinity>();
Austin Schuh093535c2016-03-05 23:21:00 -080050 }
Austin Schuh64ebab22015-11-26 13:28:30 -080051}
52
Brian Silverman4e1b0662016-01-31 18:03:19 -050053// This intentionally runs the U-capping code even when it's unnecessary to help
54// make it more deterministic. Only running it when one or both sides want
55// out-of-range voltages could lead to things like running out of CPU under
56// certain situations, which would be bad.
Austin Schuh093535c2016-03-05 23:21:00 -080057void DrivetrainMotorsSS::PolyCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070058 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
59 ::std::abs((*U)(1, 0)) > max_voltage_;
Brian Silverman4e1b0662016-01-31 18:03:19 -050060
Austin Schuh093535c2016-03-05 23:21:00 -080061 const Eigen::Matrix<double, 7, 1> error = kf_->R() - kf_->X_hat();
Austin Schuh64ebab22015-11-26 13:28:30 -080062
Austin Schuh093535c2016-03-05 23:21:00 -080063 LOG_MATRIX(DEBUG, "U_uncapped", *U);
Austin Schuh64ebab22015-11-26 13:28:30 -080064
Brian Silverman4e1b0662016-01-31 18:03:19 -050065 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh32501832017-02-25 18:32:56 -080066 position_K << kf_->controller().K(0, 0), kf_->controller().K(0, 2),
67 kf_->controller().K(1, 0), kf_->controller().K(1, 2);
Brian Silverman4e1b0662016-01-31 18:03:19 -050068 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -080069 velocity_K << kf_->controller().K(0, 1), kf_->controller().K(0, 3),
70 kf_->controller().K(1, 1), kf_->controller().K(1, 3);
Austin Schuh64ebab22015-11-26 13:28:30 -080071
Brian Silverman4e1b0662016-01-31 18:03:19 -050072 Eigen::Matrix<double, 2, 1> position_error;
73 position_error << error(0, 0), error(2, 0);
74 // drive_error = [total_distance_error, left_error - right_error]
75 const auto drive_error = T_inverse_ * position_error;
76 Eigen::Matrix<double, 2, 1> velocity_error;
77 velocity_error << error(1, 0), error(3, 0);
78 LOG_MATRIX(DEBUG, "error", error);
Austin Schuh64ebab22015-11-26 13:28:30 -080079
Austin Schuh093535c2016-03-05 23:21:00 -080080
81 Eigen::Matrix<double, 2, 1> U_integral;
82 U_integral << kf_->X_hat(4, 0), kf_->X_hat(5, 0);
83
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070084 const ::aos::controls::HVPolytope<2, 4, 4> pos_poly_hv(
85 U_poly_.static_H() * position_K * T_,
86 U_poly_.static_H() *
87 (-velocity_K * velocity_error + U_integral - kf_->ff_U()) +
Austin Schuh0aae9242018-03-14 19:49:44 -070088 (U_poly_.static_k() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070089 (position_K * T_).inverse() *
Austin Schuhbcce26a2018-03-26 23:41:24 -070090 ::aos::controls::ShiftPoints<2, 4, double>(
Austin Schuh0aae9242018-03-14 19:49:44 -070091 (U_poly_.StaticVertices() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070092 -velocity_K * velocity_error + U_integral - kf_->ff_U()));
Austin Schuh64ebab22015-11-26 13:28:30 -080093
Brian Silverman4e1b0662016-01-31 18:03:19 -050094 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
95 {
96 const auto &P = drive_error;
Austin Schuh64ebab22015-11-26 13:28:30 -080097
Brian Silverman4e1b0662016-01-31 18:03:19 -050098 Eigen::Matrix<double, 1, 2> L45;
99 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
100 const double w45 = 0;
Austin Schuh64ebab22015-11-26 13:28:30 -0800101
Brian Silverman4e1b0662016-01-31 18:03:19 -0500102 Eigen::Matrix<double, 1, 2> LH;
103 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
104 LH << 0, 1;
105 } else {
106 LH << 1, 0;
107 }
108 const double wh = LH.dot(P);
Austin Schuh64ebab22015-11-26 13:28:30 -0800109
Brian Silverman4e1b0662016-01-31 18:03:19 -0500110 Eigen::Matrix<double, 2, 2> standard;
111 standard << L45, LH;
112 Eigen::Matrix<double, 2, 1> W;
113 W << w45, wh;
114 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
Austin Schuh64ebab22015-11-26 13:28:30 -0800115
Brian Silverman4e1b0662016-01-31 18:03:19 -0500116 bool is_inside_h, is_inside_45;
117 const auto adjusted_pos_error_h =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700118 DoCoerceGoal<double>(pos_poly_hv, LH, wh, drive_error, &is_inside_h);
Brian Silverman4e1b0662016-01-31 18:03:19 -0500119 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700120 DoCoerceGoal<double>(pos_poly_hv, L45, w45, intersection, &is_inside_45);
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700121 if (pos_poly_hv.IsInside(intersection)) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500122 adjusted_pos_error = adjusted_pos_error_h;
123 } else {
124 if (is_inside_h) {
125 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
126 adjusted_pos_error_45.norm() > intersection.norm()) {
127 adjusted_pos_error = adjusted_pos_error_h;
Austin Schuh64ebab22015-11-26 13:28:30 -0800128 } else {
129 adjusted_pos_error = adjusted_pos_error_45;
130 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500131 } else {
132 adjusted_pos_error = adjusted_pos_error_45;
Austin Schuh64ebab22015-11-26 13:28:30 -0800133 }
134 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500135 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800136
Austin Schuh093535c2016-03-05 23:21:00 -0800137 *U = -U_integral + velocity_K *velocity_error +
138 position_K *T_ *adjusted_pos_error + kf_->ff_U();
Brian Silverman4e1b0662016-01-31 18:03:19 -0500139
140 if (!output_was_capped_) {
Austin Schuh093535c2016-03-05 23:21:00 -0800141 if ((*U - kf_->U_uncapped()).norm() > 0.0001) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500142 LOG(FATAL, "U unnecessarily capped\n");
143 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800144 }
145}
146
Austin Schuh093535c2016-03-05 23:21:00 -0800147void DrivetrainMotorsSS::SetGoal(
148 const ::frc971::control_loops::DrivetrainQueue::Goal &goal) {
Alex Perryfc83d3b2019-02-03 15:41:58 -0800149 unprofiled_goal_ << goal.left_goal, 0.0, goal.right_goal, 0.0, 0.0, 0.0, 0.0;
Austin Schuh0aae9242018-03-14 19:49:44 -0700150 if (::std::abs(goal.max_ss_voltage) < 0.01) {
151 max_voltage_ = kMaxVoltage;
152 } else {
153 max_voltage_ = goal.max_ss_voltage;
154 }
Austin Schuh093535c2016-03-05 23:21:00 -0800155
156 use_profile_ =
Austin Schuh32501832017-02-25 18:32:56 -0800157 !kf_->controller().Kff().isZero(0) &&
Austin Schuh093535c2016-03-05 23:21:00 -0800158 (goal.linear.max_velocity != 0.0 && goal.linear.max_acceleration != 0.0 &&
159 goal.angular.max_velocity != 0.0 &&
160 goal.angular.max_acceleration != 0.0);
161 linear_profile_.set_maximum_velocity(goal.linear.max_velocity);
162 linear_profile_.set_maximum_acceleration(goal.linear.max_acceleration);
163 angular_profile_.set_maximum_velocity(goal.angular.max_velocity);
164 angular_profile_.set_maximum_acceleration(goal.angular.max_acceleration);
Austin Schuh64ebab22015-11-26 13:28:30 -0800165}
166
Austin Schuh093535c2016-03-05 23:21:00 -0800167void DrivetrainMotorsSS::Update(bool enable_control_loop) {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700168 Eigen::Matrix<double, 2, 1> wheel_heading =
169 dt_config_.LeftRightToAngular(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700170
171 const double gyro_to_wheel_offset =
172 wheel_heading(0, 0) - *integrated_kf_heading_;
173
Austin Schuh64ebab22015-11-26 13:28:30 -0800174 if (enable_control_loop) {
Austin Schuh093535c2016-03-05 23:21:00 -0800175 // Update profiles.
176 Eigen::Matrix<double, 2, 1> unprofiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700177 dt_config_.LeftRightToLinear(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800178 Eigen::Matrix<double, 2, 1> unprofiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700179 dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800180
181 Eigen::Matrix<double, 2, 1> next_linear;
182 Eigen::Matrix<double, 2, 1> next_angular;
183
184 if (use_profile_) {
185 next_linear = linear_profile_.Update(unprofiled_linear(0, 0),
186 unprofiled_linear(1, 0));
187 next_angular = angular_profile_.Update(unprofiled_angular(0, 0),
188 unprofiled_angular(1, 0));
Austin Schuh093535c2016-03-05 23:21:00 -0800189 } else {
190 next_angular = unprofiled_angular;
191 next_linear = unprofiled_linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800192 }
Austin Schuh093535c2016-03-05 23:21:00 -0800193
Austin Schuhba93d9e2016-03-18 22:38:57 -0700194 const double wheel_compensation_offset =
195 gyro_to_wheel_offset * dt_config_.robot_radius;
196 const double scaled_angle_delta =
197 (gyro_to_wheel_offset - last_gyro_to_wheel_offset_) *
198 dt_config_.robot_radius;
Austin Schuh093535c2016-03-05 23:21:00 -0800199
200 kf_->mutable_next_R().block<4, 1>(0, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700201 dt_config_.AngularLinearToLeftRight(next_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800202
203 kf_->mutable_next_R().block<3, 1>(4, 0) =
204 unprofiled_goal_.block<3, 1>(4, 0);
205
Austin Schuhba93d9e2016-03-18 22:38:57 -0700206 kf_->mutable_next_R(0, 0) -= wheel_compensation_offset;
207 kf_->mutable_next_R(2, 0) += wheel_compensation_offset;
208
Austin Schuh093535c2016-03-05 23:21:00 -0800209 if (!use_profile_) {
210 kf_->mutable_R() = kf_->next_R();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700211 } else {
212 kf_->mutable_R(0, 0) -= scaled_angle_delta;
213 kf_->mutable_R(2, 0) += scaled_angle_delta;
Austin Schuh093535c2016-03-05 23:21:00 -0800214 }
215
216 // Run the controller.
217 Eigen::Matrix<double, 2, 1> U = kf_->ControllerOutput();
218
219 kf_->mutable_U_uncapped() = kf_->mutable_U() = U;
220 ScaleCapU(&kf_->mutable_U());
221
222 // Now update the feed forwards.
223 kf_->UpdateFFReference();
224
225 // Now, move the profile if things didn't go perfectly.
226 if (use_profile_ &&
227 (kf_->U() - kf_->U_uncapped()).lpNorm<Eigen::Infinity>() > 1e-4) {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700228 // kf_->R() is in wheel coordinates, while the profile is in absolute
229 // coordinates. Convert back...
Austin Schuhd91c0d22016-10-15 21:24:28 -0700230 linear_profile_.MoveCurrentState(dt_config_.LeftRightToLinear(kf_->R()));
Austin Schuhba93d9e2016-03-18 22:38:57 -0700231
232 LOG(DEBUG, "Saturated while moving\n");
233
234 Eigen::Matrix<double, 2, 1> absolute_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700235 dt_config_.LeftRightToAngular(kf_->R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700236 absolute_angular(0, 0) -= gyro_to_wheel_offset;
237 angular_profile_.MoveCurrentState(absolute_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800238 }
239 } else {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700240 Eigen::Matrix<double, 2, 1> wheel_linear =
241 dt_config_.LeftRightToLinear(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700242 Eigen::Matrix<double, 2, 1> next_angular = wheel_heading;
243 next_angular(0, 0) = *integrated_kf_heading_;
Austin Schuh093535c2016-03-05 23:21:00 -0800244
Austin Schuhba93d9e2016-03-18 22:38:57 -0700245 unprofiled_goal_.block<4, 1>(0, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700246 dt_config_.AngularLinearToLeftRight(wheel_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800247
Austin Schuhd91c0d22016-10-15 21:24:28 -0700248 auto current_linear = dt_config_.LeftRightToLinear(unprofiled_goal_);
249 auto current_angular = dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700250 linear_profile_.MoveCurrentState(current_linear);
251 angular_profile_.MoveCurrentState(current_angular);
252
253 kf_->mutable_next_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
254 kf_->mutable_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800255 }
Austin Schuhba93d9e2016-03-18 22:38:57 -0700256 last_gyro_to_wheel_offset_ = gyro_to_wheel_offset;
Austin Schuh64ebab22015-11-26 13:28:30 -0800257}
258
Austin Schuh093535c2016-03-05 23:21:00 -0800259void DrivetrainMotorsSS::SetOutput(
Comran Morshed5323ecb2015-12-26 20:50:55 +0000260 ::frc971::control_loops::DrivetrainQueue::Output *output) const {
Austin Schuh64ebab22015-11-26 13:28:30 -0800261 if (output) {
Austin Schuh093535c2016-03-05 23:21:00 -0800262 output->left_voltage = kf_->U(0, 0);
263 output->right_voltage = kf_->U(1, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800264 output->left_high = true;
265 output->right_high = true;
266 }
267}
268
Austin Schuh093535c2016-03-05 23:21:00 -0800269void DrivetrainMotorsSS::PopulateStatus(
270 ::frc971::control_loops::DrivetrainQueue::Status *status) const {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700271 Eigen::Matrix<double, 2, 1> profiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700272 dt_config_.LeftRightToLinear(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700273 Eigen::Matrix<double, 2, 1> profiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700274 dt_config_.LeftRightToAngular(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700275
276 profiled_angular(0, 0) -= last_gyro_to_wheel_offset_;
277
278 Eigen::Matrix<double, 4, 1> profiled_gyro_left_right =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700279 dt_config_.AngularLinearToLeftRight(profiled_linear, profiled_angular);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700280
281 status->profiled_left_position_goal = profiled_gyro_left_right(0, 0);
282 status->profiled_left_velocity_goal = profiled_gyro_left_right(1, 0);
283 status->profiled_right_position_goal = profiled_gyro_left_right(2, 0);
284 status->profiled_right_velocity_goal = profiled_gyro_left_right(3, 0);
Austin Schuh093535c2016-03-05 23:21:00 -0800285}
286
Austin Schuh6197a182015-11-28 16:04:40 -0800287} // namespace drivetrain
Austin Schuh64ebab22015-11-26 13:28:30 -0800288} // namespace control_loops
Comran Morshed5323ecb2015-12-26 20:50:55 +0000289} // namespace frc971