blob: c1db075069947232d2a7d43055b3e2d893d37995 [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
16using ::frc971::control_loops::DoCoerceGoal;
Austin Schuh64ebab22015-11-26 13:28:30 -080017
Austin Schuh093535c2016-03-05 23:21:00 -080018void DrivetrainMotorsSS::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
19 output_was_capped_ =
20 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
21
22 if (output_was_capped_) {
23 *U *= 12.0 / kf_->U_uncapped().lpNorm<Eigen::Infinity>();
24 }
Austin Schuh64ebab22015-11-26 13:28:30 -080025}
26
Brian Silverman4e1b0662016-01-31 18:03:19 -050027// This intentionally runs the U-capping code even when it's unnecessary to help
28// make it more deterministic. Only running it when one or both sides want
29// out-of-range voltages could lead to things like running out of CPU under
30// certain situations, which would be bad.
Austin Schuh093535c2016-03-05 23:21:00 -080031void DrivetrainMotorsSS::PolyCapU(Eigen::Matrix<double, 2, 1> *U) {
32 output_was_capped_ =
33 ::std::abs((*U)(0, 0)) > 12.0 || ::std::abs((*U)(1, 0)) > 12.0;
Brian Silverman4e1b0662016-01-31 18:03:19 -050034
Austin Schuh093535c2016-03-05 23:21:00 -080035 const Eigen::Matrix<double, 7, 1> error = kf_->R() - kf_->X_hat();
Austin Schuh64ebab22015-11-26 13:28:30 -080036
Austin Schuh093535c2016-03-05 23:21:00 -080037 LOG_MATRIX(DEBUG, "U_uncapped", *U);
Austin Schuh64ebab22015-11-26 13:28:30 -080038
Brian Silverman4e1b0662016-01-31 18:03:19 -050039 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh093535c2016-03-05 23:21:00 -080040 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 -050041 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh093535c2016-03-05 23:21:00 -080042 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 -080043
Brian Silverman4e1b0662016-01-31 18:03:19 -050044 Eigen::Matrix<double, 2, 1> position_error;
45 position_error << error(0, 0), error(2, 0);
46 // drive_error = [total_distance_error, left_error - right_error]
47 const auto drive_error = T_inverse_ * position_error;
48 Eigen::Matrix<double, 2, 1> velocity_error;
49 velocity_error << error(1, 0), error(3, 0);
50 LOG_MATRIX(DEBUG, "error", error);
Austin Schuh64ebab22015-11-26 13:28:30 -080051
Austin Schuh093535c2016-03-05 23:21:00 -080052
53 Eigen::Matrix<double, 2, 1> U_integral;
54 U_integral << kf_->X_hat(4, 0), kf_->X_hat(5, 0);
55
56 const ::aos::controls::HPolytope<2> pos_poly(
57 U_poly_, position_K * T_,
58 -velocity_K * velocity_error + U_integral - kf_->ff_U());
Austin Schuh64ebab22015-11-26 13:28:30 -080059
Brian Silverman4e1b0662016-01-31 18:03:19 -050060 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
61 {
62 const auto &P = drive_error;
Austin Schuh64ebab22015-11-26 13:28:30 -080063
Brian Silverman4e1b0662016-01-31 18:03:19 -050064 Eigen::Matrix<double, 1, 2> L45;
65 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
66 const double w45 = 0;
Austin Schuh64ebab22015-11-26 13:28:30 -080067
Brian Silverman4e1b0662016-01-31 18:03:19 -050068 Eigen::Matrix<double, 1, 2> LH;
69 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
70 LH << 0, 1;
71 } else {
72 LH << 1, 0;
73 }
74 const double wh = LH.dot(P);
Austin Schuh64ebab22015-11-26 13:28:30 -080075
Brian Silverman4e1b0662016-01-31 18:03:19 -050076 Eigen::Matrix<double, 2, 2> standard;
77 standard << L45, LH;
78 Eigen::Matrix<double, 2, 1> W;
79 W << w45, wh;
80 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
Austin Schuh64ebab22015-11-26 13:28:30 -080081
Brian Silverman4e1b0662016-01-31 18:03:19 -050082 bool is_inside_h, is_inside_45;
83 const auto adjusted_pos_error_h =
84 DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
85 const auto adjusted_pos_error_45 =
86 DoCoerceGoal(pos_poly, L45, w45, intersection, &is_inside_45);
87 if (pos_poly.IsInside(intersection)) {
88 adjusted_pos_error = adjusted_pos_error_h;
89 } else {
90 if (is_inside_h) {
91 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
92 adjusted_pos_error_45.norm() > intersection.norm()) {
93 adjusted_pos_error = adjusted_pos_error_h;
Austin Schuh64ebab22015-11-26 13:28:30 -080094 } else {
95 adjusted_pos_error = adjusted_pos_error_45;
96 }
Brian Silverman4e1b0662016-01-31 18:03:19 -050097 } else {
98 adjusted_pos_error = adjusted_pos_error_45;
Austin Schuh64ebab22015-11-26 13:28:30 -080099 }
100 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500101 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800102
Austin Schuh093535c2016-03-05 23:21:00 -0800103 *U = -U_integral + velocity_K *velocity_error +
104 position_K *T_ *adjusted_pos_error + kf_->ff_U();
Brian Silverman4e1b0662016-01-31 18:03:19 -0500105
106 if (!output_was_capped_) {
Austin Schuh093535c2016-03-05 23:21:00 -0800107 if ((*U - kf_->U_uncapped()).norm() > 0.0001) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500108 LOG(FATAL, "U unnecessarily capped\n");
109 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800110 }
111}
112
Austin Schuh093535c2016-03-05 23:21:00 -0800113DrivetrainMotorsSS::DrivetrainMotorsSS(const DrivetrainConfig &dt_config,
114 StateFeedbackLoop<7, 2, 3> *kf,
115 double *integrated_kf_heading)
116 : dt_config_(dt_config),
117 kf_(kf),
118 U_poly_(
119 (Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
120 .finished(),
121 (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0, 12.0, 12.0).finished()),
122 linear_profile_(::aos::controls::kLoopFrequency),
123 angular_profile_(::aos::controls::kLoopFrequency),
124 integrated_kf_heading_(integrated_kf_heading) {
125 ::aos::controls::HPolytope<0>::Init();
126 T_ << 1, 1, 1, -1;
127 T_inverse_ = T_.inverse();
128 unprofiled_goal_.setZero();
Austin Schuh64ebab22015-11-26 13:28:30 -0800129}
130
Austin Schuh093535c2016-03-05 23:21:00 -0800131void DrivetrainMotorsSS::SetGoal(
132 const ::frc971::control_loops::DrivetrainQueue::Goal &goal) {
133 unprofiled_goal_ << goal.left_goal, goal.left_velocity_goal, goal.right_goal,
134 goal.right_velocity_goal, 0.0, 0.0, 0.0;
135
136 use_profile_ =
137 !kf_->Kff().isZero(0) &&
138 (goal.linear.max_velocity != 0.0 && goal.linear.max_acceleration != 0.0 &&
139 goal.angular.max_velocity != 0.0 &&
140 goal.angular.max_acceleration != 0.0);
141 linear_profile_.set_maximum_velocity(goal.linear.max_velocity);
142 linear_profile_.set_maximum_acceleration(goal.linear.max_acceleration);
143 angular_profile_.set_maximum_velocity(goal.angular.max_velocity);
144 angular_profile_.set_maximum_acceleration(goal.angular.max_acceleration);
Austin Schuh64ebab22015-11-26 13:28:30 -0800145}
146
Austin Schuh093535c2016-03-05 23:21:00 -0800147// (left + right) / 2 = linear
148// (right - left) / width = angular
149
150Eigen::Matrix<double, 2, 1> DrivetrainMotorsSS::LeftRightToLinear(
151 const Eigen::Matrix<double, 7, 1> &left_right) {
152 Eigen::Matrix<double, 2, 1> linear;
153 linear << (left_right(0, 0) + left_right(2, 0)) / 2.0,
154 (left_right(1, 0) + left_right(3, 0)) / 2.0;
155 return linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800156}
157
Austin Schuh093535c2016-03-05 23:21:00 -0800158Eigen::Matrix<double, 2, 1> DrivetrainMotorsSS::LeftRightToAngular(
159 const Eigen::Matrix<double, 7, 1> &left_right) {
160 Eigen::Matrix<double, 2, 1> angular;
161 angular << (left_right(2, 0) - left_right(0, 0)) /
162 (dt_config_.robot_radius * 2.0),
163 (left_right(3, 0) - left_right(1, 0)) / (dt_config_.robot_radius * 2.0);
164 return angular;
165}
166
167Eigen::Matrix<double, 4, 1> DrivetrainMotorsSS::AngularLinearToLeftRight(
168 const Eigen::Matrix<double, 2, 1> &linear,
169 const Eigen::Matrix<double, 2, 1> &angular) {
170 Eigen::Matrix<double, 2, 1> scaled_angle = angular * dt_config_.robot_radius;
171 Eigen::Matrix<double, 4, 1> state;
172 state << linear(0, 0) - scaled_angle(0, 0), linear(1, 0) - scaled_angle(1, 0),
173 linear(0, 0) + scaled_angle(0, 0), linear(1, 0) + scaled_angle(1, 0);
174 return state;
175}
176
177void DrivetrainMotorsSS::Update(bool enable_control_loop) {
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 =
181 LeftRightToLinear(unprofiled_goal_);
182 Eigen::Matrix<double, 2, 1> unprofiled_angular =
183 LeftRightToAngular(unprofiled_goal_);
184
185 Eigen::Matrix<double, 2, 1> wheel_heading =
186 LeftRightToAngular(kf_->X_hat());
187
188 Eigen::Matrix<double, 2, 1> next_linear;
189 Eigen::Matrix<double, 2, 1> next_angular;
190
191 if (use_profile_) {
192 next_linear = linear_profile_.Update(unprofiled_linear(0, 0),
193 unprofiled_linear(1, 0));
194 next_angular = angular_profile_.Update(unprofiled_angular(0, 0),
195 unprofiled_angular(1, 0));
196
197 } else {
198 next_angular = unprofiled_angular;
199 next_linear = unprofiled_linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800200 }
Austin Schuh093535c2016-03-05 23:21:00 -0800201
202 next_angular(0, 0) += wheel_heading(0, 0) - *integrated_kf_heading_;
203
204 kf_->mutable_next_R().block<4, 1>(0, 0) =
205 AngularLinearToLeftRight(next_linear, next_angular);
206
207 kf_->mutable_next_R().block<3, 1>(4, 0) =
208 unprofiled_goal_.block<3, 1>(4, 0);
209
210 if (!use_profile_) {
211 kf_->mutable_R() = kf_->next_R();
212 }
213
214 // Run the controller.
215 Eigen::Matrix<double, 2, 1> U = kf_->ControllerOutput();
216
217 kf_->mutable_U_uncapped() = kf_->mutable_U() = U;
218 ScaleCapU(&kf_->mutable_U());
219
220 // Now update the feed forwards.
221 kf_->UpdateFFReference();
222
223 // Now, move the profile if things didn't go perfectly.
224 if (use_profile_ &&
225 (kf_->U() - kf_->U_uncapped()).lpNorm<Eigen::Infinity>() > 1e-4) {
226 linear_profile_.MoveCurrentState(LeftRightToLinear(kf_->R()));
227 angular_profile_.MoveCurrentState(LeftRightToAngular(kf_->R()));
228 }
229 } else {
230 unprofiled_goal_(0, 0) = kf_->X_hat()(0, 0);
231 unprofiled_goal_(1, 0) = 0.0;
232 unprofiled_goal_(2, 0) = kf_->X_hat()(2, 0);
233 unprofiled_goal_(3, 0) = 0.0;
234
235 linear_profile_.MoveCurrentState(LeftRightToLinear(unprofiled_goal_));
236 angular_profile_.MoveCurrentState(LeftRightToAngular(unprofiled_goal_));
237
238 kf_->mutable_next_R() = unprofiled_goal_;
239 kf_->mutable_R() = unprofiled_goal_;
Austin Schuh64ebab22015-11-26 13:28:30 -0800240 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800241}
242
Austin Schuh093535c2016-03-05 23:21:00 -0800243void DrivetrainMotorsSS::SetOutput(
Comran Morshed5323ecb2015-12-26 20:50:55 +0000244 ::frc971::control_loops::DrivetrainQueue::Output *output) const {
Austin Schuh64ebab22015-11-26 13:28:30 -0800245 if (output) {
Austin Schuh093535c2016-03-05 23:21:00 -0800246 output->left_voltage = kf_->U(0, 0);
247 output->right_voltage = kf_->U(1, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800248 output->left_high = true;
249 output->right_high = true;
250 }
251}
252
Austin Schuh093535c2016-03-05 23:21:00 -0800253void DrivetrainMotorsSS::PopulateStatus(
254 ::frc971::control_loops::DrivetrainQueue::Status *status) const {
255 status->profiled_left_position_goal = kf_->next_R(0, 0);
256 status->profiled_left_velocity_goal = kf_->next_R(1, 0);
257 status->profiled_right_position_goal = kf_->next_R(2, 0);
258 status->profiled_right_velocity_goal = kf_->next_R(3, 0);
259}
260
Austin Schuh6197a182015-11-28 16:04:40 -0800261} // namespace drivetrain
Austin Schuh64ebab22015-11-26 13:28:30 -0800262} // namespace control_loops
Comran Morshed5323ecb2015-12-26 20:50:55 +0000263} // namespace frc971