blob: 74cc6ff6d1848eccd42c4b2b8377f3748aeadbc1 [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"
Austin Schuh64ebab22015-11-26 13:28:30 -08004#include "frc971/control_loops/coerce_goal.h"
Comran Morshed5323ecb2015-12-26 20:50:55 +00005#include "frc971/control_loops/drivetrain/drivetrain_config.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07006#include "frc971/control_loops/drivetrain/drivetrain_goal_generated.h"
7#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
8#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07009#include "frc971/control_loops/polytope.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
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace frc971::control_loops::drivetrain {
Austin Schuh6197a182015-11-28 16:04:40 -080013
Austin Schuh06b65b82018-12-23 09:21:44 +110014DrivetrainMotorsSS::DrivetrainMotorsSS(
15 const DrivetrainConfig<double> &dt_config, StateFeedbackLoop<7, 2, 4> *kf,
James Kuszmaul3431d622019-02-17 17:07:44 -080016 LocalizerInterface *localizer)
Austin Schuh06b65b82018-12-23 09:21:44 +110017 : dt_config_(dt_config),
18 kf_(kf),
19 U_poly_(
20 (Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
21 /*[*/ -1, 0 /*]*/,
22 /*[*/ 0, 1 /*]*/,
23 /*[*/ 0, -1 /*]]*/)
24 .finished(),
25 (Eigen::Matrix<double, 4, 1>() << /*[[*/ 1.0 /*]*/,
26 /*[*/ 1.0 /*]*/,
27 /*[*/ 1.0 /*]*/,
28 /*[*/ 1.0 /*]]*/)
29 .finished(),
30 (Eigen::Matrix<double, 2, 4>() << /*[[*/ 1.0, 1.0, -1.0, -1.0 /*]*/,
31 /*[*/ -1.0, 1.0, 1.0, -1.0 /*]*/)
32 .finished()),
James Kuszmaul61750662021-06-21 21:32:33 -070033 linear_profile_(::frc971::controls::kLoopFrequency),
34 angular_profile_(::frc971::controls::kLoopFrequency),
James Kuszmaul3431d622019-02-17 17:07:44 -080035 localizer_(localizer) {
James Kuszmaul61750662021-06-21 21:32:33 -070036 ::frc971::controls::HPolytope<0>::Init();
Austin Schuh06b65b82018-12-23 09:21:44 +110037 T_ << 1, 1, 1, -1;
38 T_inverse_ = T_.inverse();
39 unprofiled_goal_.setZero();
40}
41
Austin Schuh093535c2016-03-05 23:21:00 -080042void DrivetrainMotorsSS::ScaleCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070043 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
44 ::std::abs((*U)(1, 0)) > max_voltage_;
Austin Schuh093535c2016-03-05 23:21:00 -080045
46 if (output_was_capped_) {
Austin Schuh0aae9242018-03-14 19:49:44 -070047 *U *= max_voltage_ / kf_->U_uncapped().lpNorm<Eigen::Infinity>();
Austin Schuh093535c2016-03-05 23:21:00 -080048 }
Austin Schuh64ebab22015-11-26 13:28:30 -080049}
50
Brian Silverman4e1b0662016-01-31 18:03:19 -050051// This intentionally runs the U-capping code even when it's unnecessary to help
52// make it more deterministic. Only running it when one or both sides want
53// out-of-range voltages could lead to things like running out of CPU under
54// certain situations, which would be bad.
Austin Schuh093535c2016-03-05 23:21:00 -080055void DrivetrainMotorsSS::PolyCapU(Eigen::Matrix<double, 2, 1> *U) {
Austin Schuh0aae9242018-03-14 19:49:44 -070056 output_was_capped_ = ::std::abs((*U)(0, 0)) > max_voltage_ ||
57 ::std::abs((*U)(1, 0)) > max_voltage_;
Brian Silverman4e1b0662016-01-31 18:03:19 -050058
Austin Schuh093535c2016-03-05 23:21:00 -080059 const Eigen::Matrix<double, 7, 1> error = kf_->R() - kf_->X_hat();
Austin Schuh64ebab22015-11-26 13:28:30 -080060
Brian Silverman4e1b0662016-01-31 18:03:19 -050061 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh95771d92021-01-23 14:42:25 -080062 position_K << kf_->controller().K(kLeftVoltage, kLeftPosition),
63 kf_->controller().K(kLeftVoltage, kRightPosition),
64 kf_->controller().K(kRightVoltage, kLeftPosition),
65 kf_->controller().K(kRightVoltage, kRightPosition);
Brian Silverman4e1b0662016-01-31 18:03:19 -050066 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh95771d92021-01-23 14:42:25 -080067 velocity_K << kf_->controller().K(kLeftVoltage, kLeftVelocity),
68 kf_->controller().K(kLeftVoltage, kRightVelocity),
69 kf_->controller().K(kRightVoltage, kLeftVelocity),
70 kf_->controller().K(kRightVoltage, kRightVelocity);
Austin Schuh64ebab22015-11-26 13:28:30 -080071
Austin Schuh5be6c522021-01-23 14:43:02 -080072 Eigen::Matrix<double, 2, 2> error_K;
73 error_K << kf_->controller().K(kLeftVoltage, kLeftError), 0.0, 0.0,
74 kf_->controller().K(kRightVoltage, kRightError);
75
Brian Silverman4e1b0662016-01-31 18:03:19 -050076 Eigen::Matrix<double, 2, 1> position_error;
Austin Schuh95771d92021-01-23 14:42:25 -080077 position_error << error(kLeftPosition), error(kRightPosition);
Brian Silverman4e1b0662016-01-31 18:03:19 -050078 // drive_error = [total_distance_error, left_error - right_error]
79 const auto drive_error = T_inverse_ * position_error;
80 Eigen::Matrix<double, 2, 1> velocity_error;
Austin Schuh95771d92021-01-23 14:42:25 -080081 velocity_error << error(kLeftVelocity), error(kRightVelocity);
Austin Schuh093535c2016-03-05 23:21:00 -080082
Austin Schuh5be6c522021-01-23 14:43:02 -080083 Eigen::Matrix<double, 2, 1> U_integral =
84 error_K * Eigen::Matrix<double, 2, 1>(kf_->X_hat(kLeftError),
85 kf_->X_hat(kRightError));
Austin Schuh093535c2016-03-05 23:21:00 -080086
James Kuszmaul61750662021-06-21 21:32:33 -070087 const ::frc971::controls::HVPolytope<2, 4, 4> pos_poly_hv(
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070088 U_poly_.static_H() * position_K * T_,
89 U_poly_.static_H() *
90 (-velocity_K * velocity_error + U_integral - kf_->ff_U()) +
Austin Schuh0aae9242018-03-14 19:49:44 -070091 (U_poly_.static_k() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070092 (position_K * T_).inverse() *
James Kuszmaul61750662021-06-21 21:32:33 -070093 ::frc971::controls::ShiftPoints<2, 4, double>(
Austin Schuh0aae9242018-03-14 19:49:44 -070094 (U_poly_.StaticVertices() * max_voltage_),
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070095 -velocity_K * velocity_error + U_integral - kf_->ff_U()));
Austin Schuh64ebab22015-11-26 13:28:30 -080096
Brian Silverman4e1b0662016-01-31 18:03:19 -050097 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
98 {
99 const auto &P = drive_error;
Austin Schuh64ebab22015-11-26 13:28:30 -0800100
Brian Silverman4e1b0662016-01-31 18:03:19 -0500101 Eigen::Matrix<double, 1, 2> L45;
102 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
103 const double w45 = 0;
Austin Schuh64ebab22015-11-26 13:28:30 -0800104
Brian Silverman4e1b0662016-01-31 18:03:19 -0500105 Eigen::Matrix<double, 1, 2> LH;
106 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
107 LH << 0, 1;
108 } else {
109 LH << 1, 0;
110 }
111 const double wh = LH.dot(P);
Austin Schuh64ebab22015-11-26 13:28:30 -0800112
Brian Silverman4e1b0662016-01-31 18:03:19 -0500113 Eigen::Matrix<double, 2, 2> standard;
114 standard << L45, LH;
115 Eigen::Matrix<double, 2, 1> W;
116 W << w45, wh;
117 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
Austin Schuh64ebab22015-11-26 13:28:30 -0800118
Brian Silverman4e1b0662016-01-31 18:03:19 -0500119 bool is_inside_h, is_inside_45;
120 const auto adjusted_pos_error_h =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700121 DoCoerceGoal<double>(pos_poly_hv, LH, wh, drive_error, &is_inside_h);
Austin Schuhd749d932020-12-30 21:38:40 -0800122 const auto adjusted_pos_error_45 = DoCoerceGoal<double>(
123 pos_poly_hv, L45, w45, intersection, &is_inside_45);
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700124 if (pos_poly_hv.IsInside(intersection)) {
Brian Silverman4e1b0662016-01-31 18:03:19 -0500125 adjusted_pos_error = adjusted_pos_error_h;
126 } else {
127 if (is_inside_h) {
128 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
129 adjusted_pos_error_45.norm() > intersection.norm()) {
130 adjusted_pos_error = adjusted_pos_error_h;
Austin Schuh64ebab22015-11-26 13:28:30 -0800131 } else {
132 adjusted_pos_error = adjusted_pos_error_45;
133 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500134 } else {
135 adjusted_pos_error = adjusted_pos_error_45;
Austin Schuh64ebab22015-11-26 13:28:30 -0800136 }
137 }
Brian Silverman4e1b0662016-01-31 18:03:19 -0500138 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800139
Austin Schuhd749d932020-12-30 21:38:40 -0800140 *U = -U_integral + velocity_K * velocity_error +
141 position_K * T_ * adjusted_pos_error + kf_->ff_U();
Brian Silverman4e1b0662016-01-31 18:03:19 -0500142
143 if (!output_was_capped_) {
Austin Schuh093535c2016-03-05 23:21:00 -0800144 if ((*U - kf_->U_uncapped()).norm() > 0.0001) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700145 AOS_LOG(FATAL, "U unnecessarily capped\n");
Brian Silverman4e1b0662016-01-31 18:03:19 -0500146 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800147 }
148}
149
Austin Schuh093535c2016-03-05 23:21:00 -0800150void DrivetrainMotorsSS::SetGoal(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700151 const ::frc971::control_loops::drivetrain::Goal *goal) {
152 unprofiled_goal_ << goal->left_goal(), 0.0, goal->right_goal(), 0.0, 0.0, 0.0,
153 0.0;
154 if (!goal->has_max_ss_voltage()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700155 max_voltage_ = kMaxVoltage;
156 } else {
Milind Upadhyay10143452020-12-19 17:25:48 -0800157 max_voltage_ = goal->max_ss_voltage();
Austin Schuh0aae9242018-03-14 19:49:44 -0700158 }
Austin Schuh093535c2016-03-05 23:21:00 -0800159
Alex Perrycb7da4b2019-08-28 19:35:56 -0700160 use_profile_ = !kf_->controller().Kff().isZero(0) &&
161 (goal->has_linear() && goal->has_angular() &&
162 goal->linear()->has_max_velocity() &&
163 goal->linear()->has_max_acceleration() &&
164 goal->angular()->has_max_velocity() &&
165 goal->angular()->has_max_acceleration());
166 if (goal->has_linear()) {
167 linear_profile_.set_maximum_velocity(goal->linear()->max_velocity());
168 linear_profile_.set_maximum_acceleration(
169 goal->linear()->max_acceleration());
170 }
171 if (goal->has_angular()) {
172 angular_profile_.set_maximum_velocity(goal->angular()->max_velocity());
173 angular_profile_.set_maximum_acceleration(
174 goal->angular()->max_acceleration());
175 }
Austin Schuh64ebab22015-11-26 13:28:30 -0800176}
177
Austin Schuh093535c2016-03-05 23:21:00 -0800178void DrivetrainMotorsSS::Update(bool enable_control_loop) {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700179 Eigen::Matrix<double, 2, 1> wheel_heading =
180 dt_config_.LeftRightToAngular(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700181
James Kuszmaul3431d622019-02-17 17:07:44 -0800182 const double gyro_to_wheel_offset = wheel_heading(0, 0) - localizer_->theta();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700183
Austin Schuh64ebab22015-11-26 13:28:30 -0800184 if (enable_control_loop) {
Austin Schuh093535c2016-03-05 23:21:00 -0800185 // Update profiles.
186 Eigen::Matrix<double, 2, 1> unprofiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700187 dt_config_.LeftRightToLinear(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800188 Eigen::Matrix<double, 2, 1> unprofiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700189 dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuh093535c2016-03-05 23:21:00 -0800190
191 Eigen::Matrix<double, 2, 1> next_linear;
192 Eigen::Matrix<double, 2, 1> next_angular;
193
194 if (use_profile_) {
195 next_linear = linear_profile_.Update(unprofiled_linear(0, 0),
196 unprofiled_linear(1, 0));
197 next_angular = angular_profile_.Update(unprofiled_angular(0, 0),
198 unprofiled_angular(1, 0));
Austin Schuh093535c2016-03-05 23:21:00 -0800199 } else {
200 next_angular = unprofiled_angular;
201 next_linear = unprofiled_linear;
Austin Schuh64ebab22015-11-26 13:28:30 -0800202 }
Austin Schuh093535c2016-03-05 23:21:00 -0800203
Austin Schuhba93d9e2016-03-18 22:38:57 -0700204 const double wheel_compensation_offset =
205 gyro_to_wheel_offset * dt_config_.robot_radius;
206 const double scaled_angle_delta =
207 (gyro_to_wheel_offset - last_gyro_to_wheel_offset_) *
208 dt_config_.robot_radius;
Austin Schuh093535c2016-03-05 23:21:00 -0800209
Austin Schuh95771d92021-01-23 14:42:25 -0800210 kf_->mutable_next_R().block<4, 1>(kLeftPosition, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700211 dt_config_.AngularLinearToLeftRight(next_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800212
Austin Schuh95771d92021-01-23 14:42:25 -0800213 kf_->mutable_next_R().block<3, 1>(kLeftError, 0) =
214 unprofiled_goal_.block<3, 1>(kLeftError, 0);
Austin Schuh093535c2016-03-05 23:21:00 -0800215
Austin Schuhba93d9e2016-03-18 22:38:57 -0700216 kf_->mutable_next_R(0, 0) -= wheel_compensation_offset;
217 kf_->mutable_next_R(2, 0) += wheel_compensation_offset;
218
Austin Schuh093535c2016-03-05 23:21:00 -0800219 if (!use_profile_) {
220 kf_->mutable_R() = kf_->next_R();
Austin Schuhba93d9e2016-03-18 22:38:57 -0700221 } else {
222 kf_->mutable_R(0, 0) -= scaled_angle_delta;
223 kf_->mutable_R(2, 0) += scaled_angle_delta;
Austin Schuh093535c2016-03-05 23:21:00 -0800224 }
225
226 // Run the controller.
227 Eigen::Matrix<double, 2, 1> U = kf_->ControllerOutput();
228
229 kf_->mutable_U_uncapped() = kf_->mutable_U() = U;
230 ScaleCapU(&kf_->mutable_U());
231
232 // Now update the feed forwards.
233 kf_->UpdateFFReference();
234
235 // Now, move the profile if things didn't go perfectly.
236 if (use_profile_ &&
237 (kf_->U() - kf_->U_uncapped()).lpNorm<Eigen::Infinity>() > 1e-4) {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700238 // kf_->R() is in wheel coordinates, while the profile is in absolute
239 // coordinates. Convert back...
Austin Schuhd91c0d22016-10-15 21:24:28 -0700240 linear_profile_.MoveCurrentState(dt_config_.LeftRightToLinear(kf_->R()));
Austin Schuhba93d9e2016-03-18 22:38:57 -0700241
Austin Schuhf257f3c2019-10-27 21:00:43 -0700242 AOS_LOG(DEBUG, "Saturated while moving\n");
Austin Schuhba93d9e2016-03-18 22:38:57 -0700243
244 Eigen::Matrix<double, 2, 1> absolute_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700245 dt_config_.LeftRightToAngular(kf_->R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700246 absolute_angular(0, 0) -= gyro_to_wheel_offset;
247 angular_profile_.MoveCurrentState(absolute_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800248 }
249 } else {
Austin Schuhd91c0d22016-10-15 21:24:28 -0700250 Eigen::Matrix<double, 2, 1> wheel_linear =
251 dt_config_.LeftRightToLinear(kf_->X_hat());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700252 Eigen::Matrix<double, 2, 1> next_angular = wheel_heading;
James Kuszmaul3431d622019-02-17 17:07:44 -0800253 next_angular(0, 0) = localizer_->theta();
Austin Schuh093535c2016-03-05 23:21:00 -0800254
Austin Schuhba93d9e2016-03-18 22:38:57 -0700255 unprofiled_goal_.block<4, 1>(0, 0) =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700256 dt_config_.AngularLinearToLeftRight(wheel_linear, next_angular);
Austin Schuh093535c2016-03-05 23:21:00 -0800257
Austin Schuhd91c0d22016-10-15 21:24:28 -0700258 auto current_linear = dt_config_.LeftRightToLinear(unprofiled_goal_);
259 auto current_angular = dt_config_.LeftRightToAngular(unprofiled_goal_);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700260 linear_profile_.MoveCurrentState(current_linear);
261 angular_profile_.MoveCurrentState(current_angular);
262
263 kf_->mutable_next_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
264 kf_->mutable_R().block<4, 1>(0, 0) = kf_->X_hat().block<4, 1>(0, 0);
Austin Schuh64ebab22015-11-26 13:28:30 -0800265 }
Austin Schuhba93d9e2016-03-18 22:38:57 -0700266 last_gyro_to_wheel_offset_ = gyro_to_wheel_offset;
Austin Schuh64ebab22015-11-26 13:28:30 -0800267}
268
Austin Schuh093535c2016-03-05 23:21:00 -0800269void DrivetrainMotorsSS::SetOutput(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700270 ::frc971::control_loops::drivetrain::OutputT *output) const {
Austin Schuh64ebab22015-11-26 13:28:30 -0800271 if (output) {
Austin Schuh95771d92021-01-23 14:42:25 -0800272 output->left_voltage = kf_->U(kLeftVoltage);
273 output->right_voltage = kf_->U(kRightVoltage);
Austin Schuh64ebab22015-11-26 13:28:30 -0800274 output->left_high = true;
275 output->right_high = true;
276 }
277}
278
Austin Schuh093535c2016-03-05 23:21:00 -0800279void DrivetrainMotorsSS::PopulateStatus(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700280 ::frc971::control_loops::drivetrain::StatusBuilder *builder) const {
Austin Schuhba93d9e2016-03-18 22:38:57 -0700281 Eigen::Matrix<double, 2, 1> profiled_linear =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700282 dt_config_.LeftRightToLinear(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700283 Eigen::Matrix<double, 2, 1> profiled_angular =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700284 dt_config_.LeftRightToAngular(kf_->next_R());
Austin Schuhba93d9e2016-03-18 22:38:57 -0700285
286 profiled_angular(0, 0) -= last_gyro_to_wheel_offset_;
287
288 Eigen::Matrix<double, 4, 1> profiled_gyro_left_right =
Austin Schuhd91c0d22016-10-15 21:24:28 -0700289 dt_config_.AngularLinearToLeftRight(profiled_linear, profiled_angular);
Austin Schuhba93d9e2016-03-18 22:38:57 -0700290
Austin Schuh95771d92021-01-23 14:42:25 -0800291 builder->add_profiled_left_position_goal(
292 profiled_gyro_left_right(kLeftPosition));
293 builder->add_profiled_left_velocity_goal(
294 profiled_gyro_left_right(kLeftVelocity));
295 builder->add_profiled_right_position_goal(
296 profiled_gyro_left_right(kRightPosition));
297 builder->add_profiled_right_velocity_goal(
298 profiled_gyro_left_right(kRightVelocity));
Austin Schuh093535c2016-03-05 23:21:00 -0800299}
300
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800301} // namespace frc971::control_loops::drivetrain