blob: ff7ed6ea242782e243bb621bda9dec55057323a8 [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
3#include "aos/common/controls/polytope.h"
4#include "aos/common/commonmath.h"
5#include "aos/common/logging/matrix_logging.h"
6
7#include "frc971/control_loops/state_feedback_loop.h"
8#include "frc971/control_loops/coerce_goal.h"
Comran Morshed5323ecb2015-12-26 20:50:55 +00009#include "frc971/control_loops/drivetrain/drivetrain.q.h"
10#include "frc971/control_loops/drivetrain/drivetrain_config.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 Schuh093535c2016-03-05 23:21:00 -080016void DrivetrainMotorsSS::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
17 output_was_capped_ =
18 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
19
20 if (output_was_capped_) {
21 *U *= 12.0 / kf_->U_uncapped().lpNorm<Eigen::Infinity>();
22 }
Austin Schuh64ebab22015-11-26 13:28:30 -080023}
24
Brian Silverman4e1b0662016-01-31 18:03:19 -050025// This intentionally runs the U-capping code even when it's unnecessary to help
26// make it more deterministic. Only running it when one or both sides want
27// out-of-range voltages could lead to things like running out of CPU under
28// certain situations, which would be bad.
Austin Schuh093535c2016-03-05 23:21:00 -080029void DrivetrainMotorsSS::PolyCapU(Eigen::Matrix<double, 2, 1> *U) {
30 output_was_capped_ =
31 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Brian Silverman4e1b0662016-01-31 18:03:19 -050032
Austin Schuh093535c2016-03-05 23:21:00 -080033 const Eigen::Matrix<double, 7, 1> error = kf_->R() - kf_->X_hat();
Austin Schuh64ebab22015-11-26 13:28:30 -080034
Austin Schuh093535c2016-03-05 23:21:00 -080035 LOG_MATRIX(DEBUG, "U_uncapped", *U);
Austin Schuh64ebab22015-11-26 13:28:30 -080036
Brian Silverman4e1b0662016-01-31 18:03:19 -050037 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh093535c2016-03-05 23:21:00 -080038 position_K << kf_->K(0, 0), kf_->K(0, 2), kf_->K(1, 0), kf_->K(1, 2);
Brian Silverman4e1b0662016-01-31 18:03:19 -050039 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh093535c2016-03-05 23:21:00 -080040 velocity_K << kf_->K(0, 1), kf_->K(0, 3), kf_->K(1, 1), kf_->K(1, 3);
Austin Schuh64ebab22015-11-26 13:28:30 -080041
Brian Silverman4e1b0662016-01-31 18:03:19 -050042 Eigen::Matrix<double, 2, 1> position_error;
43 position_error << error(0, 0), error(2, 0);
44 // drive_error = [total_distance_error, left_error - right_error]
45 const auto drive_error = T_inverse_ * position_error;
46 Eigen::Matrix<double, 2, 1> velocity_error;
47 velocity_error << error(1, 0), error(3, 0);
48 LOG_MATRIX(DEBUG, "error", error);
Austin Schuh64ebab22015-11-26 13:28:30 -080049
Austin Schuh093535c2016-03-05 23:21:00 -080050
51 Eigen::Matrix<double, 2, 1> U_integral;
52 U_integral << kf_->X_hat(4, 0), kf_->X_hat(5, 0);
53
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070054 const ::aos::controls::HVPolytope<2, 4, 4> pos_poly_hv(
55 U_poly_.static_H() * position_K * T_,
56 U_poly_.static_H() *
57 (-velocity_K * velocity_error + U_integral - kf_->ff_U()) +
58 U_poly_.static_k(),
59 (position_K * T_).inverse() *
60 ::aos::controls::ShiftPoints<2, 4>(
61 U_poly_.StaticVertices(),
62 -velocity_K * velocity_error + U_integral - kf_->ff_U()));
Austin Schuh64ebab22015-11-26 13:28:30 -080063
Brian Silverman4e1b0662016-01-31 18:03:19 -050064 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
65 {
66 const auto &P = drive_error;
Austin Schuh64ebab22015-11-26 13:28:30 -080067
Brian Silverman4e1b0662016-01-31 18:03:19 -050068 Eigen::Matrix<double, 1, 2> L45;
69 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
70 const double w45 = 0;
Austin Schuh64ebab22015-11-26 13:28:30 -080071
Brian Silverman4e1b0662016-01-31 18:03:19 -050072 Eigen::Matrix<double, 1, 2> LH;
73 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
74 LH << 0, 1;
75 } else {
76 LH << 1, 0;
77 }
78 const double wh = LH.dot(P);
Austin Schuh64ebab22015-11-26 13:28:30 -080079
Brian Silverman4e1b0662016-01-31 18:03:19 -050080 Eigen::Matrix<double, 2, 2> standard;
81 standard << L45, LH;
82 Eigen::Matrix<double, 2, 1> W;
83 W << w45, wh;
84 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
Austin Schuh64ebab22015-11-26 13:28:30 -080085
Brian Silverman4e1b0662016-01-31 18:03:19 -050086 bool is_inside_h, is_inside_45;
87 const auto adjusted_pos_error_h =
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070088 DoCoerceGoal(pos_poly_hv, LH, wh, drive_error, &is_inside_h);
Brian Silverman4e1b0662016-01-31 18:03:19 -050089 const auto adjusted_pos_error_45 =
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070090 DoCoerceGoal(pos_poly_hv, L45, w45, intersection, &is_inside_45);
91 if (pos_poly_hv.IsInside(intersection)) {
Brian Silverman4e1b0662016-01-31 18:03:19 -050092 adjusted_pos_error = adjusted_pos_error_h;
93 } else {
94 if (is_inside_h) {
95 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
96 adjusted_pos_error_45.norm() > intersection.norm()) {
97 adjusted_pos_error = adjusted_pos_error_h;
Austin Schuh64ebab22015-11-26 13:28:30 -080098 } else {
99 adjusted_pos_error = adjusted_pos_error_45;
100 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500101 } else {
102 adjusted_pos_error = adjusted_pos_error_45;
Austin Schuh64ebab22015-11-26 13:28:30 -0800103 }
104 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500105 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800106
Austin Schuh093535c2016-03-05 23:21:00 -0800107 *U = -U_integral + velocity_K *velocity_error +
108 position_K *T_ *adjusted_pos_error + kf_->ff_U();
Brian Silverman4e1b0662016-01-31 18:03:19 -0500109
110 if (!output_was_capped_) {
Austin Schuh093535c2016-03-05 23:21:00 -0800111 if ((*U - kf_->U_uncapped()).norm() > 0.0001) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500112 LOG(FATAL, "U unnecessarily capped\n");
113 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800114 }
115}
116
Austin Schuh093535c2016-03-05 23:21:00 -0800117DrivetrainMotorsSS::DrivetrainMotorsSS(const DrivetrainConfig &dt_config,
118 StateFeedbackLoop<7, 2, 3> *kf,
119 double *integrated_kf_heading)
120 : dt_config_(dt_config),
121 kf_(kf),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700122 U_poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
123 /*[*/ -1, 0 /*]*/,
124 /*[*/ 0, 1 /*]*/,
125 /*[*/ 0, -1 /*]]*/).finished(),
126 (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
127 /*[*/ 12 /*]*/,
128 /*[*/ 12 /*]*/,
129 /*[*/ 12 /*]]*/).finished(),
130 (Eigen::Matrix<double, 2, 4>() << /*[[*/ 12, 12, -12, -12 /*]*/,
131 /*[*/ -12, 12, 12, -12 /*]*/).finished()),
Austin Schuh093535c2016-03-05 23:21:00 -0800132 linear_profile_(::aos::controls::kLoopFrequency),
133 angular_profile_(::aos::controls::kLoopFrequency),
134 integrated_kf_heading_(integrated_kf_heading) {
135 ::aos::controls::HPolytope<0>::Init();
136 T_ << 1, 1, 1, -1;
137 T_inverse_ = T_.inverse();
138 unprofiled_goal_.setZero();
Austin Schuh64ebab22015-11-26 13:28:30 -0800139}
140
Austin Schuh093535c2016-03-05 23:21:00 -0800141void DrivetrainMotorsSS::SetGoal(
142 const ::frc971::control_loops::DrivetrainQueue::Goal &goal) {
143 unprofiled_goal_ << goal.left_goal, goal.left_velocity_goal, goal.right_goal,
144 goal.right_velocity_goal, 0.0, 0.0, 0.0;
145
146 use_profile_ =
147 !kf_->Kff().isZero(0) &&
148 (goal.linear.max_velocity != 0.0 && goal.linear.max_acceleration != 0.0 &&
149 goal.angular.max_velocity != 0.0 &&
150 goal.angular.max_acceleration != 0.0);
151 linear_profile_.set_maximum_velocity(goal.linear.max_velocity);
152 linear_profile_.set_maximum_acceleration(goal.linear.max_acceleration);
153 angular_profile_.set_maximum_velocity(goal.angular.max_velocity);
154 angular_profile_.set_maximum_acceleration(goal.angular.max_acceleration);
Austin Schuh64ebab22015-11-26 13:28:30 -0800155}
156
Austin Schuh093535c2016-03-05 23:21:00 -0800157// (left + right) / 2 = linear
158// (right - left) / width = angular
159
160Eigen::Matrix<double, 2, 1> DrivetrainMotorsSS::LeftRightToLinear(
Austin Schuhba93d9e2016-03-18 22:38:57 -0700161 const Eigen::Matrix<double, 7, 1> &left_right) const {
Austin Schuh093535c2016-03-05 23:21:00 -0800162 Eigen::Matrix<double, 2, 1> linear;
163 linear << (left_right(0, 0) + left_right(2, 0)) / 2.0,
164 (left_right(1, 0) + left_right(3, 0)) / 2.0;
165 return linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800166}
167
Austin Schuh093535c2016-03-05 23:21:00 -0800168Eigen::Matrix<double, 2, 1> DrivetrainMotorsSS::LeftRightToAngular(
Austin Schuhba93d9e2016-03-18 22:38:57 -0700169 const Eigen::Matrix<double, 7, 1> &left_right) const {
Austin Schuh093535c2016-03-05 23:21:00 -0800170 Eigen::Matrix<double, 2, 1> angular;
171 angular << (left_right(2, 0) - left_right(0, 0)) /
172 (dt_config_.robot_radius * 2.0),
173 (left_right(3, 0) - left_right(1, 0)) / (dt_config_.robot_radius * 2.0);
174 return angular;
175}
176
177Eigen::Matrix<double, 4, 1> DrivetrainMotorsSS::AngularLinearToLeftRight(
178 const Eigen::Matrix<double, 2, 1> &linear,
Austin Schuhba93d9e2016-03-18 22:38:57 -0700179 const Eigen::Matrix<double, 2, 1> &angular) const {
Austin Schuh093535c2016-03-05 23:21:00 -0800180 Eigen::Matrix<double, 2, 1> scaled_angle = angular * dt_config_.robot_radius;
181 Eigen::Matrix<double, 4, 1> state;
182 state << linear(0, 0) - scaled_angle(0, 0), linear(1, 0) - scaled_angle(1, 0),
183 linear(0, 0) + scaled_angle(0, 0), linear(1, 0) + scaled_angle(1, 0);
184 return state;
185}
186
187void DrivetrainMotorsSS::Update(bool enable_control_loop) {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700188 Eigen::Matrix<double, 2, 1> wheel_heading = LeftRightToAngular(kf_->X_hat());
189
190 const double gyro_to_wheel_offset =
191 wheel_heading(0, 0) - *integrated_kf_heading_;
192
Austin Schuh64ebab22015-11-26 13:28:30 -0800193 if (enable_control_loop) {
Austin Schuh093535c2016-03-05 23:21:00 -0800194 // Update profiles.
195 Eigen::Matrix<double, 2, 1> unprofiled_linear =
Austin Schuhba93d9e2016-03-18 22:38:57 -0700196 LeftRightToLinear(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800197 Eigen::Matrix<double, 2, 1> unprofiled_angular =
Austin Schuhba93d9e2016-03-18 22:38:57 -0700198 LeftRightToAngular(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800199
200 Eigen::Matrix<double, 2, 1> next_linear;
201 Eigen::Matrix<double, 2, 1> next_angular;
202
203 if (use_profile_) {
204 next_linear = linear_profile_.Update(unprofiled_linear(0, 0),
205 unprofiled_linear(1, 0));
206 next_angular = angular_profile_.Update(unprofiled_angular(0, 0),
207 unprofiled_angular(1, 0));
Austin Schuh093535c2016-03-05 23:21:00 -0800208 } else {
209 next_angular = unprofiled_angular;
210 next_linear = unprofiled_linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800211 }
Austin Schuh093535c2016-03-05 23:21:00 -0800212
Austin Schuhba93d9e2016-03-18 22:38:57 -0700213 const double wheel_compensation_offset =
214 gyro_to_wheel_offset * dt_config_.robot_radius;
215 const double scaled_angle_delta =
216 (gyro_to_wheel_offset - last_gyro_to_wheel_offset_) *
217 dt_config_.robot_radius;
Austin Schuh093535c2016-03-05 23:21:00 -0800218
219 kf_->mutable_next_R().block<4, 1>(0, 0) =
220 AngularLinearToLeftRight(next_linear, next_angular);
221
222 kf_->mutable_next_R().block<3, 1>(4, 0) =
223 unprofiled_goal_.block<3, 1>(4, 0);
224
Austin Schuhba93d9e2016-03-18 22:38:57 -0700225 kf_->mutable_next_R(0, 0) -= wheel_compensation_offset;
226 kf_->mutable_next_R(2, 0) += wheel_compensation_offset;
227
Austin Schuh093535c2016-03-05 23:21:00 -0800228 if (!use_profile_) {
229 kf_->mutable_R() = kf_->next_R();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700230 } else {
231 kf_->mutable_R(0, 0) -= scaled_angle_delta;
232 kf_->mutable_R(2, 0) += scaled_angle_delta;
Austin Schuh093535c2016-03-05 23:21:00 -0800233 }
234
235 // Run the controller.
236 Eigen::Matrix<double, 2, 1> U = kf_->ControllerOutput();
237
238 kf_->mutable_U_uncapped() = kf_->mutable_U() = U;
239 ScaleCapU(&kf_->mutable_U());
240
241 // Now update the feed forwards.
242 kf_->UpdateFFReference();
243
244 // Now, move the profile if things didn't go perfectly.
245 if (use_profile_ &&
246 (kf_->U() - kf_->U_uncapped()).lpNorm<Eigen::Infinity>() > 1e-4) {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700247 // kf_->R() is in wheel coordinates, while the profile is in absolute
248 // coordinates. Convert back...
Austin Schuh093535c2016-03-05 23:21:00 -0800249 linear_profile_.MoveCurrentState(LeftRightToLinear(kf_->R()));
Austin Schuhba93d9e2016-03-18 22:38:57 -0700250
251 LOG(DEBUG, "Saturated while moving\n");
252
253 Eigen::Matrix<double, 2, 1> absolute_angular =
254 LeftRightToAngular(kf_->R());
255 absolute_angular(0, 0) -= gyro_to_wheel_offset;
256 angular_profile_.MoveCurrentState(absolute_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800257 }
258 } else {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700259 Eigen::Matrix<double, 2, 1> wheel_linear = LeftRightToLinear(kf_->X_hat());
260 Eigen::Matrix<double, 2, 1> next_angular = wheel_heading;
261 next_angular(0, 0) = *integrated_kf_heading_;
Austin Schuh093535c2016-03-05 23:21:00 -0800262
Austin Schuhba93d9e2016-03-18 22:38:57 -0700263 unprofiled_goal_.block<4, 1>(0, 0) =
264 AngularLinearToLeftRight(wheel_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800265
Austin Schuhba93d9e2016-03-18 22:38:57 -0700266 auto current_linear = LeftRightToLinear(unprofiled_goal_);
267 auto current_angular = LeftRightToAngular(unprofiled_goal_);
268 linear_profile_.MoveCurrentState(current_linear);
269 angular_profile_.MoveCurrentState(current_angular);
270
271 kf_->mutable_next_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
272 kf_->mutable_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800273 }
Austin Schuhba93d9e2016-03-18 22:38:57 -0700274 last_gyro_to_wheel_offset_ = gyro_to_wheel_offset;
Austin Schuh64ebab22015-11-26 13:28:30 -0800275}
276
Austin Schuh093535c2016-03-05 23:21:00 -0800277void DrivetrainMotorsSS::SetOutput(
Comran Morshed5323ecb2015-12-26 20:50:55 +0000278 ::frc971::control_loops::DrivetrainQueue::Output *output) const {
Austin Schuh64ebab22015-11-26 13:28:30 -0800279 if (output) {
Austin Schuh093535c2016-03-05 23:21:00 -0800280 output->left_voltage = kf_->U(0, 0);
281 output->right_voltage = kf_->U(1, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800282 output->left_high = true;
283 output->right_high = true;
284 }
285}
286
Austin Schuh093535c2016-03-05 23:21:00 -0800287void DrivetrainMotorsSS::PopulateStatus(
288 ::frc971::control_loops::DrivetrainQueue::Status *status) const {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700289 Eigen::Matrix<double, 2, 1> profiled_linear =
290 LeftRightToLinear(kf_->next_R());
291 Eigen::Matrix<double, 2, 1> profiled_angular =
292 LeftRightToAngular(kf_->next_R());
293
294 profiled_angular(0, 0) -= last_gyro_to_wheel_offset_;
295
296 Eigen::Matrix<double, 4, 1> profiled_gyro_left_right =
297 AngularLinearToLeftRight(profiled_linear, profiled_angular);
298
299 status->profiled_left_position_goal = profiled_gyro_left_right(0, 0);
300 status->profiled_left_velocity_goal = profiled_gyro_left_right(1, 0);
301 status->profiled_right_position_goal = profiled_gyro_left_right(2, 0);
302 status->profiled_right_velocity_goal = profiled_gyro_left_right(3, 0);
Austin Schuh093535c2016-03-05 23:21:00 -0800303}
304
Austin Schuh6197a182015-11-28 16:04:40 -0800305} // namespace drivetrain
Austin Schuh64ebab22015-11-26 13:28:30 -0800306} // namespace control_loops
Comran Morshed5323ecb2015-12-26 20:50:55 +0000307} // namespace frc971