blob: cd29e394f278e845353844e6d06211b58adca268 [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"
Austin Schuh64ebab22015-11-26 13:28:30 -08005
Austin Schuh64ebab22015-11-26 13:28:30 -08006#include "frc971/control_loops/coerce_goal.h"
Comran Morshed5323ecb2015-12-26 20:50:55 +00007#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include "frc971/control_loops/drivetrain/drivetrain_goal_generated.h"
9#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
10#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Austin Schuh0aae9242018-03-14 19:49:44 -070011#include "frc971/control_loops/state_feedback_loop.h"
Austin Schuh64ebab22015-11-26 13:28:30 -080012
Comran Morshed5323ecb2015-12-26 20:50:55 +000013namespace frc971 {
Austin Schuh64ebab22015-11-26 13:28:30 -080014namespace control_loops {
Austin Schuh6197a182015-11-28 16:04:40 -080015namespace drivetrain {
16
Austin Schuh06b65b82018-12-23 09:21:44 +110017DrivetrainMotorsSS::DrivetrainMotorsSS(
18 const DrivetrainConfig<double> &dt_config, StateFeedbackLoop<7, 2, 4> *kf,
James Kuszmaul3431d622019-02-17 17:07:44 -080019 LocalizerInterface *localizer)
Austin Schuh06b65b82018-12-23 09:21:44 +110020 : dt_config_(dt_config),
21 kf_(kf),
22 U_poly_(
23 (Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
24 /*[*/ -1, 0 /*]*/,
25 /*[*/ 0, 1 /*]*/,
26 /*[*/ 0, -1 /*]]*/)
27 .finished(),
28 (Eigen::Matrix<double, 4, 1>() << /*[[*/ 1.0 /*]*/,
29 /*[*/ 1.0 /*]*/,
30 /*[*/ 1.0 /*]*/,
31 /*[*/ 1.0 /*]]*/)
32 .finished(),
33 (Eigen::Matrix<double, 2, 4>() << /*[[*/ 1.0, 1.0, -1.0, -1.0 /*]*/,
34 /*[*/ -1.0, 1.0, 1.0, -1.0 /*]*/)
35 .finished()),
36 linear_profile_(::aos::controls::kLoopFrequency),
37 angular_profile_(::aos::controls::kLoopFrequency),
James Kuszmaul3431d622019-02-17 17:07:44 -080038 localizer_(localizer) {
Austin Schuh06b65b82018-12-23 09:21:44 +110039 ::aos::controls::HPolytope<0>::Init();
40 T_ << 1, 1, 1, -1;
41 T_inverse_ = T_.inverse();
42 unprofiled_goal_.setZero();
43}
44
Austin Schuh093535c2016-03-05 23:21:00 -080045void DrivetrainMotorsSS::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070046 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
47 ::std::abs((*U)(1, 0)) > max_voltage_;
Austin Schuh093535c2016-03-05 23:21:00 -080048
49 if (output_was_capped_) {
Austin Schuh0aae9242018-03-14 19:49:44 -070050 *U *= max_voltage_ / kf_->U_uncapped().lpNorm<Eigen::Infinity>();
Austin Schuh093535c2016-03-05 23:21:00 -080051 }
Austin Schuh64ebab22015-11-26 13:28:30 -080052}
53
Brian Silverman4e1b0662016-01-31 18:03:19 -050054// This intentionally runs the U-capping code even when it's unnecessary to help
55// make it more deterministic. Only running it when one or both sides want
56// out-of-range voltages could lead to things like running out of CPU under
57// certain situations, which would be bad.
Austin Schuh093535c2016-03-05 23:21:00 -080058void DrivetrainMotorsSS::PolyCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070059 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
60 ::std::abs((*U)(1, 0)) > max_voltage_;
Brian Silverman4e1b0662016-01-31 18:03:19 -050061
Austin Schuh093535c2016-03-05 23:21:00 -080062 const Eigen::Matrix<double, 7, 1> error = kf_->R() - kf_->X_hat();
Austin Schuh64ebab22015-11-26 13:28:30 -080063
Brian Silverman4e1b0662016-01-31 18:03:19 -050064 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh32501832017-02-25 18:32:56 -080065 position_K << kf_->controller().K(0, 0), kf_->controller().K(0, 2),
66 kf_->controller().K(1, 0), kf_->controller().K(1, 2);
Brian Silverman4e1b0662016-01-31 18:03:19 -050067 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -080068 velocity_K << kf_->controller().K(0, 1), kf_->controller().K(0, 3),
69 kf_->controller().K(1, 1), kf_->controller().K(1, 3);
Austin Schuh64ebab22015-11-26 13:28:30 -080070
Brian Silverman4e1b0662016-01-31 18:03:19 -050071 Eigen::Matrix<double, 2, 1> position_error;
72 position_error << error(0, 0), error(2, 0);
73 // drive_error = [total_distance_error, left_error - right_error]
74 const auto drive_error = T_inverse_ * position_error;
75 Eigen::Matrix<double, 2, 1> velocity_error;
76 velocity_error << error(1, 0), error(3, 0);
Austin Schuh093535c2016-03-05 23:21:00 -080077
78 Eigen::Matrix<double, 2, 1> U_integral;
79 U_integral << kf_->X_hat(4, 0), kf_->X_hat(5, 0);
80
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070081 const ::aos::controls::HVPolytope<2, 4, 4> pos_poly_hv(
82 U_poly_.static_H() * position_K * T_,
83 U_poly_.static_H() *
84 (-velocity_K * velocity_error + U_integral - kf_->ff_U()) +
Austin Schuh0aae9242018-03-14 19:49:44 -070085 (U_poly_.static_k() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070086 (position_K * T_).inverse() *
Austin Schuhbcce26a2018-03-26 23:41:24 -070087 ::aos::controls::ShiftPoints<2, 4, double>(
Austin Schuh0aae9242018-03-14 19:49:44 -070088 (U_poly_.StaticVertices() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070089 -velocity_K * velocity_error + U_integral - kf_->ff_U()));
Austin Schuh64ebab22015-11-26 13:28:30 -080090
Brian Silverman4e1b0662016-01-31 18:03:19 -050091 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
92 {
93 const auto &P = drive_error;
Austin Schuh64ebab22015-11-26 13:28:30 -080094
Brian Silverman4e1b0662016-01-31 18:03:19 -050095 Eigen::Matrix<double, 1, 2> L45;
96 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
97 const double w45 = 0;
Austin Schuh64ebab22015-11-26 13:28:30 -080098
Brian Silverman4e1b0662016-01-31 18:03:19 -050099 Eigen::Matrix<double, 1, 2> LH;
100 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
101 LH << 0, 1;
102 } else {
103 LH << 1, 0;
104 }
105 const double wh = LH.dot(P);
Austin Schuh64ebab22015-11-26 13:28:30 -0800106
Brian Silverman4e1b0662016-01-31 18:03:19 -0500107 Eigen::Matrix<double, 2, 2> standard;
108 standard << L45, LH;
109 Eigen::Matrix<double, 2, 1> W;
110 W << w45, wh;
111 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
Austin Schuh64ebab22015-11-26 13:28:30 -0800112
Brian Silverman4e1b0662016-01-31 18:03:19 -0500113 bool is_inside_h, is_inside_45;
114 const auto adjusted_pos_error_h =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700115 DoCoerceGoal<double>(pos_poly_hv, LH, wh, drive_error, &is_inside_h);
Brian Silverman4e1b0662016-01-31 18:03:19 -0500116 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700117 DoCoerceGoal<double>(pos_poly_hv, L45, w45, intersection, &is_inside_45);
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700118 if (pos_poly_hv.IsInside(intersection)) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500119 adjusted_pos_error = adjusted_pos_error_h;
120 } else {
121 if (is_inside_h) {
122 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
123 adjusted_pos_error_45.norm() > intersection.norm()) {
124 adjusted_pos_error = adjusted_pos_error_h;
Austin Schuh64ebab22015-11-26 13:28:30 -0800125 } else {
126 adjusted_pos_error = adjusted_pos_error_45;
127 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500128 } else {
129 adjusted_pos_error = adjusted_pos_error_45;
Austin Schuh64ebab22015-11-26 13:28:30 -0800130 }
131 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500132 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800133
Austin Schuh093535c2016-03-05 23:21:00 -0800134 *U = -U_integral + velocity_K *velocity_error +
135 position_K *T_ *adjusted_pos_error + kf_->ff_U();
Brian Silverman4e1b0662016-01-31 18:03:19 -0500136
137 if (!output_was_capped_) {
Austin Schuh093535c2016-03-05 23:21:00 -0800138 if ((*U - kf_->U_uncapped()).norm() > 0.0001) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700139 AOS_LOG(FATAL, "U unnecessarily capped\n");
Brian Silverman4e1b0662016-01-31 18:03:19 -0500140 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800141 }
142}
143
Austin Schuh093535c2016-03-05 23:21:00 -0800144void DrivetrainMotorsSS::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700145 const ::frc971::control_loops::drivetrain::Goal *goal) {
146 unprofiled_goal_ << goal->left_goal(), 0.0, goal->right_goal(), 0.0, 0.0, 0.0,
147 0.0;
148 if (!goal->has_max_ss_voltage()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700149 max_voltage_ = kMaxVoltage;
150 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700151 max_voltage_ = goal->has_max_ss_voltage();
Austin Schuh0aae9242018-03-14 19:49:44 -0700152 }
Austin Schuh093535c2016-03-05 23:21:00 -0800153
Alex Perrycb7da4b2019-08-28 19:35:56 -0700154 use_profile_ = !kf_->controller().Kff().isZero(0) &&
155 (goal->has_linear() && goal->has_angular() &&
156 goal->linear()->has_max_velocity() &&
157 goal->linear()->has_max_acceleration() &&
158 goal->angular()->has_max_velocity() &&
159 goal->angular()->has_max_acceleration());
160 if (goal->has_linear()) {
161 linear_profile_.set_maximum_velocity(goal->linear()->max_velocity());
162 linear_profile_.set_maximum_acceleration(
163 goal->linear()->max_acceleration());
164 }
165 if (goal->has_angular()) {
166 angular_profile_.set_maximum_velocity(goal->angular()->max_velocity());
167 angular_profile_.set_maximum_acceleration(
168 goal->angular()->max_acceleration());
169 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800170}
171
Austin Schuh093535c2016-03-05 23:21:00 -0800172void DrivetrainMotorsSS::Update(bool enable_control_loop) {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700173 Eigen::Matrix<double, 2, 1> wheel_heading =
174 dt_config_.LeftRightToAngular(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700175
James Kuszmaul3431d622019-02-17 17:07:44 -0800176 const double gyro_to_wheel_offset = wheel_heading(0, 0) - localizer_->theta();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700177
Austin Schuh64ebab22015-11-26 13:28:30 -0800178 if (enable_control_loop) {
Austin Schuh093535c2016-03-05 23:21:00 -0800179 // Update profiles.
180 Eigen::Matrix<double, 2, 1> unprofiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700181 dt_config_.LeftRightToLinear(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800182 Eigen::Matrix<double, 2, 1> unprofiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700183 dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800184
185 Eigen::Matrix<double, 2, 1> next_linear;
186 Eigen::Matrix<double, 2, 1> next_angular;
187
188 if (use_profile_) {
189 next_linear = linear_profile_.Update(unprofiled_linear(0, 0),
190 unprofiled_linear(1, 0));
191 next_angular = angular_profile_.Update(unprofiled_angular(0, 0),
192 unprofiled_angular(1, 0));
Austin Schuh093535c2016-03-05 23:21:00 -0800193 } else {
194 next_angular = unprofiled_angular;
195 next_linear = unprofiled_linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800196 }
Austin Schuh093535c2016-03-05 23:21:00 -0800197
Austin Schuhba93d9e2016-03-18 22:38:57 -0700198 const double wheel_compensation_offset =
199 gyro_to_wheel_offset * dt_config_.robot_radius;
200 const double scaled_angle_delta =
201 (gyro_to_wheel_offset - last_gyro_to_wheel_offset_) *
202 dt_config_.robot_radius;
Austin Schuh093535c2016-03-05 23:21:00 -0800203
204 kf_->mutable_next_R().block<4, 1>(0, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700205 dt_config_.AngularLinearToLeftRight(next_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800206
207 kf_->mutable_next_R().block<3, 1>(4, 0) =
208 unprofiled_goal_.block<3, 1>(4, 0);
209
Austin Schuhba93d9e2016-03-18 22:38:57 -0700210 kf_->mutable_next_R(0, 0) -= wheel_compensation_offset;
211 kf_->mutable_next_R(2, 0) += wheel_compensation_offset;
212
Austin Schuh093535c2016-03-05 23:21:00 -0800213 if (!use_profile_) {
214 kf_->mutable_R() = kf_->next_R();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700215 } else {
216 kf_->mutable_R(0, 0) -= scaled_angle_delta;
217 kf_->mutable_R(2, 0) += scaled_angle_delta;
Austin Schuh093535c2016-03-05 23:21:00 -0800218 }
219
220 // Run the controller.
221 Eigen::Matrix<double, 2, 1> U = kf_->ControllerOutput();
222
223 kf_->mutable_U_uncapped() = kf_->mutable_U() = U;
224 ScaleCapU(&kf_->mutable_U());
225
226 // Now update the feed forwards.
227 kf_->UpdateFFReference();
228
229 // Now, move the profile if things didn't go perfectly.
230 if (use_profile_ &&
231 (kf_->U() - kf_->U_uncapped()).lpNorm<Eigen::Infinity>() > 1e-4) {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700232 // kf_->R() is in wheel coordinates, while the profile is in absolute
233 // coordinates. Convert back...
Austin Schuhd91c0d22016-10-15 21:24:28 -0700234 linear_profile_.MoveCurrentState(dt_config_.LeftRightToLinear(kf_->R()));
Austin Schuhba93d9e2016-03-18 22:38:57 -0700235
Austin Schuhf257f3c2019-10-27 21:00:43 -0700236 AOS_LOG(DEBUG, "Saturated while moving\n");
Austin Schuhba93d9e2016-03-18 22:38:57 -0700237
238 Eigen::Matrix<double, 2, 1> absolute_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700239 dt_config_.LeftRightToAngular(kf_->R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700240 absolute_angular(0, 0) -= gyro_to_wheel_offset;
241 angular_profile_.MoveCurrentState(absolute_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800242 }
243 } else {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700244 Eigen::Matrix<double, 2, 1> wheel_linear =
245 dt_config_.LeftRightToLinear(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700246 Eigen::Matrix<double, 2, 1> next_angular = wheel_heading;
James Kuszmaul3431d622019-02-17 17:07:44 -0800247 next_angular(0, 0) = localizer_->theta();
Austin Schuh093535c2016-03-05 23:21:00 -0800248
Austin Schuhba93d9e2016-03-18 22:38:57 -0700249 unprofiled_goal_.block<4, 1>(0, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700250 dt_config_.AngularLinearToLeftRight(wheel_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800251
Austin Schuhd91c0d22016-10-15 21:24:28 -0700252 auto current_linear = dt_config_.LeftRightToLinear(unprofiled_goal_);
253 auto current_angular = dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700254 linear_profile_.MoveCurrentState(current_linear);
255 angular_profile_.MoveCurrentState(current_angular);
256
257 kf_->mutable_next_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
258 kf_->mutable_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800259 }
Austin Schuhba93d9e2016-03-18 22:38:57 -0700260 last_gyro_to_wheel_offset_ = gyro_to_wheel_offset;
Austin Schuh64ebab22015-11-26 13:28:30 -0800261}
262
Austin Schuh093535c2016-03-05 23:21:00 -0800263void DrivetrainMotorsSS::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700264 ::frc971::control_loops::drivetrain::OutputT *output) const {
Austin Schuh64ebab22015-11-26 13:28:30 -0800265 if (output) {
Austin Schuh093535c2016-03-05 23:21:00 -0800266 output->left_voltage = kf_->U(0, 0);
267 output->right_voltage = kf_->U(1, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800268 output->left_high = true;
269 output->right_high = true;
270 }
271}
272
Austin Schuh093535c2016-03-05 23:21:00 -0800273void DrivetrainMotorsSS::PopulateStatus(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700274 ::frc971::control_loops::drivetrain::StatusBuilder *builder) const {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700275 Eigen::Matrix<double, 2, 1> profiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700276 dt_config_.LeftRightToLinear(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700277 Eigen::Matrix<double, 2, 1> profiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700278 dt_config_.LeftRightToAngular(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700279
280 profiled_angular(0, 0) -= last_gyro_to_wheel_offset_;
281
282 Eigen::Matrix<double, 4, 1> profiled_gyro_left_right =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700283 dt_config_.AngularLinearToLeftRight(profiled_linear, profiled_angular);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700284
Alex Perrycb7da4b2019-08-28 19:35:56 -0700285 builder->add_profiled_left_position_goal(profiled_gyro_left_right(0, 0));
286 builder->add_profiled_left_velocity_goal(profiled_gyro_left_right(1, 0));
287 builder->add_profiled_right_position_goal(profiled_gyro_left_right(2, 0));
288 builder->add_profiled_right_velocity_goal(profiled_gyro_left_right(3, 0));
Austin Schuh093535c2016-03-05 23:21:00 -0800289}
290
Austin Schuh6197a182015-11-28 16:04:40 -0800291} // namespace drivetrain
Austin Schuh64ebab22015-11-26 13:28:30 -0800292} // namespace control_loops
Comran Morshed5323ecb2015-12-26 20:50:55 +0000293} // namespace frc971