blob: 815f05ea6c4a2334fd3410fd97ede9aa80a704ac [file] [log] [blame]
James Kuszmaulf254c1a2013-03-10 16:31:26 -07001#include "frc971/control_loops/drivetrain/drivetrain.h"
brians343bc112013-02-10 01:53:46 +00002
3#include <stdio.h>
4#include <sched.h>
5#include <cmath>
Austin Schuh4352ac62013-03-19 06:23:16 +00006#include <memory>
Austin Schuh2054f5f2013-10-27 14:54:10 -07007#include "Eigen/Dense"
brians343bc112013-02-10 01:53:46 +00008
brians343bc112013-02-10 01:53:46 +00009#include "aos/common/logging/logging.h"
10#include "aos/common/queue.h"
Austin Schuh2054f5f2013-10-27 14:54:10 -070011#include "aos/controls/polytope.h"
12#include "aos/common/commonmath.h"
James Kuszmaulf254c1a2013-03-10 16:31:26 -070013#include "frc971/control_loops/state_feedback_loop.h"
14#include "frc971/control_loops/drivetrain/drivetrain_motor_plant.h"
Austin Schuh2054f5f2013-10-27 14:54:10 -070015#include "frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h"
Austin Schuh427b3702013-11-02 13:44:09 -070016#include "frc971/control_loops/drivetrain/polydrivetrain_cim_plant.h"
James Kuszmaulf254c1a2013-03-10 16:31:26 -070017#include "frc971/control_loops/drivetrain/drivetrain.q.h"
brians343bc112013-02-10 01:53:46 +000018#include "frc971/queues/GyroAngle.q.h"
19#include "frc971/queues/Piston.q.h"
Brian Silverman1a6590d2013-11-04 14:46:46 -080020#include "frc971/constants.h"
brians343bc112013-02-10 01:53:46 +000021
22using frc971::sensors::gyro;
23
24namespace frc971 {
25namespace control_loops {
26
27// Width of the robot.
28const double width = 22.0 / 100.0 * 2.54;
29
Austin Schuh2054f5f2013-10-27 14:54:10 -070030Eigen::Matrix<double, 2, 1> CoerceGoal(aos::controls::HPolytope<2> &region,
31 const Eigen::Matrix<double, 1, 2> &K,
32 double w,
33 const Eigen::Matrix<double, 2, 1> &R) {
34 if (region.IsInside(R)) {
35 return R;
36 }
37 Eigen::Matrix<double, 2, 1> parallel_vector;
38 Eigen::Matrix<double, 2, 1> perpendicular_vector;
39 perpendicular_vector = K.transpose().normalized();
40 parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0);
41
42 aos::controls::HPolytope<1> t_poly(
43 region.H() * parallel_vector,
44 region.k() - region.H() * perpendicular_vector * w);
45
46 Eigen::Matrix<double, 1, Eigen::Dynamic> vertices = t_poly.Vertices();
47 if (vertices.innerSize() > 0) {
48 double min_distance_sqr = 0;
49 Eigen::Matrix<double, 2, 1> closest_point;
50 for (int i = 0; i < vertices.innerSize(); i++) {
51 Eigen::Matrix<double, 2, 1> point;
52 point = parallel_vector * vertices(0, i) + perpendicular_vector * w;
53 const double length = (R - point).squaredNorm();
54 if (i == 0 || length < min_distance_sqr) {
55 closest_point = point;
56 min_distance_sqr = length;
57 }
58 }
59 return closest_point;
60 } else {
61 Eigen::Matrix<double, 2, Eigen::Dynamic> region_vertices =
62 region.Vertices();
63 double min_distance;
64 int closest_i = 0;
65 for (int i = 0; i < region_vertices.outerSize(); i++) {
66 const double length = ::std::abs(
67 (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0));
68 if (i == 0 || length < min_distance) {
69 closest_i = i;
70 min_distance = length;
71 }
72 }
73 return region_vertices.col(closest_i);
74 }
75}
76
Austin Schuh4352ac62013-03-19 06:23:16 +000077class DrivetrainMotorsSS {
brians343bc112013-02-10 01:53:46 +000078 public:
Austin Schuh4352ac62013-03-19 06:23:16 +000079 DrivetrainMotorsSS ()
80 : loop_(new StateFeedbackLoop<4, 2, 2>(MakeDrivetrainLoop())) {
brians343bc112013-02-10 01:53:46 +000081 _offset = 0;
82 _integral_offset = 0;
83 _left_goal = 0.0;
84 _right_goal = 0.0;
85 _raw_left = 0.0;
86 _raw_right = 0.0;
Austin Schuh4352ac62013-03-19 06:23:16 +000087 _control_loop_driving = false;
brians343bc112013-02-10 01:53:46 +000088 }
89 void SetGoal(double left, double left_velocity, double right, double right_velocity) {
90 _left_goal = left;
91 _right_goal = right;
Austin Schuh4352ac62013-03-19 06:23:16 +000092 loop_->R << left, left_velocity, right, right_velocity;
brians343bc112013-02-10 01:53:46 +000093 }
94 void SetRawPosition(double left, double right) {
95 _raw_right = right;
96 _raw_left = left;
Austin Schuh4352ac62013-03-19 06:23:16 +000097 loop_->Y << left, right;
brians343bc112013-02-10 01:53:46 +000098 }
Austin Schuh4352ac62013-03-19 06:23:16 +000099 void SetPosition(
100 double left, double right, double gyro, bool control_loop_driving) {
brians343bc112013-02-10 01:53:46 +0000101 // Decay the offset quickly because this gyro is great.
102 _offset = (0.25) * (right - left - gyro * width) / 2.0 + 0.75 * _offset;
Brian Silvermande8fd552013-11-03 15:53:42 -0800103 //const double angle_error = (_right_goal - _left_goal) / width - (_raw_right - _offset - _raw_left - _offset) / width;
Austin Schuh4352ac62013-03-19 06:23:16 +0000104 // TODO(aschuh): Add in the gyro.
105 _integral_offset = 0.0;
106 _offset = 0.0;
brians343bc112013-02-10 01:53:46 +0000107 _gyro = gyro;
Austin Schuh4352ac62013-03-19 06:23:16 +0000108 _control_loop_driving = control_loop_driving;
brians343bc112013-02-10 01:53:46 +0000109 SetRawPosition(left, right);
Brian Silvermande8fd552013-11-03 15:53:42 -0800110 //LOG(DEBUG, "Left %f->%f Right %f->%f Gyro %f aerror %f ioff %f\n", left + _offset, _left_goal, right - _offset, _right_goal, gyro, angle_error, _integral_offset);
brians343bc112013-02-10 01:53:46 +0000111 }
Austin Schuh4352ac62013-03-19 06:23:16 +0000112
113 void Update(bool update_observer, bool stop_motors) {
114 loop_->Update(update_observer, stop_motors);
brians343bc112013-02-10 01:53:46 +0000115 }
116
Austin Schuh4352ac62013-03-19 06:23:16 +0000117 void SendMotors(Drivetrain::Output *output) {
118 if (output) {
119 output->left_voltage = loop_->U(0, 0);
120 output->right_voltage = loop_->U(1, 0);
brians8ad74052013-03-16 21:04:51 +0000121 }
brians343bc112013-02-10 01:53:46 +0000122 }
123 void PrintMotors() const {
124 // LOG(DEBUG, "Left Power %f Right Power %f lg %f rg %f le %f re %f gyro %f\n", U[0], U[1], R[0], R[2], Y[0], Y[1], _gyro);
Austin Schuh4352ac62013-03-19 06:23:16 +0000125 ::Eigen::Matrix<double, 4, 1> E = loop_->R - loop_->X_hat;
126 LOG(DEBUG, "E[0, 0]: %f E[1, 0] %f E[2, 0] %f E[3, 0] %f\n", E(0, 0), E(1, 0), E(2, 0), E(3, 0));
brians343bc112013-02-10 01:53:46 +0000127 }
128
129 private:
Austin Schuh4352ac62013-03-19 06:23:16 +0000130 ::std::unique_ptr<StateFeedbackLoop<4, 2, 2>> loop_;
131
brians343bc112013-02-10 01:53:46 +0000132 double _integral_offset;
133 double _offset;
134 double _gyro;
135 double _left_goal;
136 double _right_goal;
137 double _raw_left;
138 double _raw_right;
Austin Schuh4352ac62013-03-19 06:23:16 +0000139 bool _control_loop_driving;
brians343bc112013-02-10 01:53:46 +0000140};
141
Austin Schuh2054f5f2013-10-27 14:54:10 -0700142class PolyDrivetrain {
143 public:
Austin Schuh427b3702013-11-02 13:44:09 -0700144
145 enum Gear {
146 HIGH,
147 LOW,
148 SHIFTING_UP,
149 SHIFTING_DOWN
150 };
151 // Stall Torque in N m
152 static constexpr double kStallTorque = 2.42;
153 // Stall Current in Amps
154 static constexpr double kStallCurrent = 133;
155 // Free Speed in RPM. Used number from last year.
156 static constexpr double kFreeSpeed = 4650.0;
157 // Free Current in Amps
158 static constexpr double kFreeCurrent = 2.7;
159 // Moment of inertia of the drivetrain in kg m^2
160 // Just borrowed from last year.
161 static constexpr double J = 6.4;
162 // Mass of the robot, in kg.
163 static constexpr double m = 68;
164 // Radius of the robot, in meters (from last year).
165 static constexpr double rb = 0.617998644 / 2.0;
Brian Silverman1a6590d2013-11-04 14:46:46 -0800166 static constexpr double kWheelRadius = 0.04445;
Austin Schuh427b3702013-11-02 13:44:09 -0700167 // Resistance of the motor, divided by the number of motors.
168 static constexpr double kR = (12.0 / kStallCurrent / 4 + 0.03) / (0.93 * 0.93);
169 // Motor velocity constant
170 static constexpr double Kv =
171 ((kFreeSpeed / 60.0 * 2.0 * M_PI) / (12.0 - kR * kFreeCurrent));
172 // Torque constant
173 static constexpr double Kt = kStallTorque / kStallCurrent;
Austin Schuh427b3702013-11-02 13:44:09 -0700174
Austin Schuh2054f5f2013-10-27 14:54:10 -0700175 PolyDrivetrain()
176 : U_Poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
177 /*[*/ -1, 0 /*]*/,
178 /*[*/ 0, 1 /*]*/,
179 /*[*/ 0, -1 /*]]*/).finished(),
180 (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
181 /*[*/ 12 /*]*/,
182 /*[*/ 12 /*]*/,
183 /*[*/ 12 /*]]*/).finished()),
Austin Schuh427b3702013-11-02 13:44:09 -0700184 loop_(new StateFeedbackLoop<2, 2, 2>(MakeVDrivetrainLoop())),
185 left_cim_(new StateFeedbackLoop<1, 1, 1>(MakeCIMLoop())),
186 right_cim_(new StateFeedbackLoop<1, 1, 1>(MakeCIMLoop())),
187 ttrust_(1.1),
188 wheel_(0.0),
189 throttle_(0.0),
190 quickturn_(false),
191 stale_count_(0),
192 position_time_delta_(0.01),
193 left_gear_(LOW),
Brian Silvermande8fd552013-11-03 15:53:42 -0800194 right_gear_(LOW),
195 counter_(0) {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700196
Austin Schuh427b3702013-11-02 13:44:09 -0700197 last_position_.Zero();
198 position_.Zero();
Austin Schuh2054f5f2013-10-27 14:54:10 -0700199 }
Austin Schuh427b3702013-11-02 13:44:09 -0700200 static bool IsInGear(Gear gear) { return gear == LOW || gear == HIGH; }
201
202 static double MotorSpeed(double shifter_position, double velocity) {
203 // TODO(austin): G_high, G_low and kWheelRadius
Brian Silvermande8fd552013-11-03 15:53:42 -0800204 if (shifter_position > 0.57) {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800205 return velocity / constants::GetValues().high_gear_ratio / kWheelRadius;
Austin Schuh427b3702013-11-02 13:44:09 -0700206 } else {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800207 return velocity / constants::GetValues().low_gear_ratio / kWheelRadius;
208 }
209 }
210
211 Gear ComputeGear(double velocity, Gear current) {
212 const double low_omega = MotorSpeed(0, ::std::abs(velocity));
213 const double high_omega = MotorSpeed(1.0, ::std::abs(velocity));
214
215 LOG(DEBUG, "velocity %f\n", velocity);
216 double high_torque = ((12.0 - high_omega / Kv) * Kt / kR);
217 double low_torque = ((12.0 - low_omega / Kv) * Kt / kR);
218 double high_power = high_torque * high_omega;
219 double low_power = low_torque * low_omega;
220 if ((current == HIGH ||
221 high_power > low_power + /*50*/50) &&
222 high_power > low_power - /*50*/200) {
223 return HIGH;
224 } else {
225 return LOW;
Austin Schuh427b3702013-11-02 13:44:09 -0700226 }
227 }
228
Austin Schuh2054f5f2013-10-27 14:54:10 -0700229 void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
Brian Silvermande8fd552013-11-03 15:53:42 -0800230 const double kWheelNonLinearity = 0.3;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700231 // Apply a sin function that's scaled to make it feel better.
232 const double angular_range = M_PI_2 * kWheelNonLinearity;
233 wheel_ = sin(angular_range * wheel) / sin(angular_range);
234 wheel_ = sin(angular_range * wheel_) / sin(angular_range);
Austin Schuh2054f5f2013-10-27 14:54:10 -0700235 quickturn_ = quickturn;
Austin Schuh427b3702013-11-02 13:44:09 -0700236
Brian Silverman1a6590d2013-11-04 14:46:46 -0800237 static const double kThrottleDeadband = 0.05;
238 if (::std::abs(throttle) < kThrottleDeadband) {
239 throttle_ = 0;
240 } else {
241 throttle_ = copysign((::std::abs(throttle) - kThrottleDeadband) /
242 (1.0 - kThrottleDeadband), throttle);
243 }
244
Austin Schuh427b3702013-11-02 13:44:09 -0700245 // TODO(austin): Fix the upshift logic to include states.
Brian Silverman1a6590d2013-11-04 14:46:46 -0800246 Gear requested_gear;
247 if (constants::GetValues().clutch_transmission) {
248 const double current_left_velocity =
249 (position_.left_encoder - last_position_.left_encoder) /
250 position_time_delta_;
251 const double current_right_velocity =
252 (position_.right_encoder - last_position_.right_encoder) /
253 position_time_delta_;
254
255 Gear left_requested = ComputeGear(current_left_velocity, left_gear_);
256 Gear right_requested = ComputeGear(current_right_velocity, right_gear_);
257 requested_gear =
258 (left_requested == HIGH || right_requested == HIGH) ? HIGH : LOW;
259 } else {
260 requested_gear = highgear ? HIGH : LOW;
261 }
262
263 const Gear shift_up =
264 constants::GetValues().clutch_transmission ? HIGH : SHIFTING_UP;
265 const Gear shift_down =
266 constants::GetValues().clutch_transmission ? LOW : SHIFTING_DOWN;
Austin Schuh427b3702013-11-02 13:44:09 -0700267
268 if (left_gear_ != requested_gear) {
269 if (IsInGear(left_gear_)) {
270 if (requested_gear == HIGH) {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800271 left_gear_ = shift_up;
Austin Schuh427b3702013-11-02 13:44:09 -0700272 } else {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800273 left_gear_ = shift_down;
Austin Schuh427b3702013-11-02 13:44:09 -0700274 }
275 }
276 }
277 if (right_gear_ != requested_gear) {
278 if (IsInGear(right_gear_)) {
279 if (requested_gear == HIGH) {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800280 right_gear_ = shift_up;
Austin Schuh427b3702013-11-02 13:44:09 -0700281 } else {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800282 right_gear_ = shift_down;
Austin Schuh427b3702013-11-02 13:44:09 -0700283 }
284 }
Austin Schuh2054f5f2013-10-27 14:54:10 -0700285 }
286 }
Austin Schuh427b3702013-11-02 13:44:09 -0700287 void SetPosition(const Drivetrain::Position *position) {
288 if (position == NULL) {
289 ++stale_count_;
290 } else {
291 last_position_ = position_;
292 position_ = *position;
293 position_time_delta_ = (stale_count_ + 1) * 0.01;
294 stale_count_ = 0;
295 }
296
297 if (position) {
298 // Switch to the correct controller.
Brian Silvermande8fd552013-11-03 15:53:42 -0800299 // TODO(austin): Un-hard code 0.57
300 if (position->left_shifter_position < 0.57) {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800301 if (position->right_shifter_position < 0.57 || right_gear_ == LOW) {
Brian Silvermande8fd552013-11-03 15:53:42 -0800302 LOG(DEBUG, "Loop Left low, Right low\n");
Austin Schuh427b3702013-11-02 13:44:09 -0700303 loop_->set_controller_index(0);
304 } else {
Brian Silvermande8fd552013-11-03 15:53:42 -0800305 LOG(DEBUG, "Loop Left low, Right high\n");
Austin Schuh427b3702013-11-02 13:44:09 -0700306 loop_->set_controller_index(1);
307 }
308 } else {
Brian Silverman1a6590d2013-11-04 14:46:46 -0800309 if (position->right_shifter_position < 0.57 || left_gear_ == LOW) {
Brian Silvermande8fd552013-11-03 15:53:42 -0800310 LOG(DEBUG, "Loop Left high, Right low\n");
Austin Schuh427b3702013-11-02 13:44:09 -0700311 loop_->set_controller_index(2);
312 } else {
Brian Silvermande8fd552013-11-03 15:53:42 -0800313 LOG(DEBUG, "Loop Left high, Right high\n");
Austin Schuh427b3702013-11-02 13:44:09 -0700314 loop_->set_controller_index(3);
315 }
316 }
Brian Silvermande8fd552013-11-03 15:53:42 -0800317 switch (left_gear_) {
318 case LOW:
319 LOG(DEBUG, "Left is in low\n");
320 break;
321 case HIGH:
322 LOG(DEBUG, "Left is in high\n");
323 break;
324 case SHIFTING_UP:
325 LOG(DEBUG, "Left is shifting up\n");
326 break;
327 case SHIFTING_DOWN:
328 LOG(DEBUG, "Left is shifting down\n");
329 break;
330 }
331 switch (right_gear_) {
332 case LOW:
333 LOG(DEBUG, "Right is in low\n");
334 break;
335 case HIGH:
336 LOG(DEBUG, "Right is in high\n");
337 break;
338 case SHIFTING_UP:
339 LOG(DEBUG, "Right is shifting up\n");
340 break;
341 case SHIFTING_DOWN:
342 LOG(DEBUG, "Right is shifting down\n");
343 break;
344 }
Austin Schuh427b3702013-11-02 13:44:09 -0700345 // TODO(austin): Constants.
346 if (position->left_shifter_position > 0.9 && left_gear_ == SHIFTING_UP) {
347 left_gear_ = HIGH;
348 }
349 if (position->left_shifter_position < 0.1 && left_gear_ == SHIFTING_DOWN) {
350 left_gear_ = LOW;
351 }
352 if (position->right_shifter_position > 0.9 && right_gear_ == SHIFTING_UP) {
353 right_gear_ = HIGH;
354 }
355 if (position->right_shifter_position < 0.1 && right_gear_ == SHIFTING_DOWN) {
356 right_gear_ = LOW;
357 }
358 }
359 }
360
Austin Schuh2054f5f2013-10-27 14:54:10 -0700361 double FilterVelocity(double throttle) {
362 const Eigen::Matrix<double, 2, 2> FF =
363 loop_->B().inverse() *
364 (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
365
366 constexpr int kHighGearController = 3;
367 const Eigen::Matrix<double, 2, 2> FF_high =
368 loop_->controller(kHighGearController).plant.B.inverse() *
369 (Eigen::Matrix<double, 2, 2>::Identity() -
370 loop_->controller(kHighGearController).plant.A);
371
372 ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
373 int min_FF_sum_index;
374 const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
375 const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
376 const double high_min_FF_sum = FF_high.col(0).sum();
377
378 const double adjusted_ff_voltage = ::aos::Clip(
379 throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
380 return ((adjusted_ff_voltage +
381 ttrust_ * min_K_sum * (loop_->X_hat(0, 0) + loop_->X_hat(1, 0)) /
382 2.0) /
383 (ttrust_ * min_K_sum + min_FF_sum));
384 }
385
Brian Silverman718b1d72013-10-28 16:22:45 -0700386 double MaxVelocity() {
387 const Eigen::Matrix<double, 2, 2> FF =
388 loop_->B().inverse() *
389 (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
390
391 constexpr int kHighGearController = 3;
392 const Eigen::Matrix<double, 2, 2> FF_high =
393 loop_->controller(kHighGearController).plant.B.inverse() *
394 (Eigen::Matrix<double, 2, 2>::Identity() -
395 loop_->controller(kHighGearController).plant.A);
396
397 ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
398 int min_FF_sum_index;
399 const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
400 //const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
401 const double high_min_FF_sum = FF_high.col(0).sum();
402
403 const double adjusted_ff_voltage = ::aos::Clip(
404 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
405 return adjusted_ff_voltage / min_FF_sum;
406 }
407
Austin Schuh2054f5f2013-10-27 14:54:10 -0700408 void Update() {
Austin Schuh427b3702013-11-02 13:44:09 -0700409 // TODO(austin): Observer for the current velocity instead of difference
410 // calculations.
Brian Silvermande8fd552013-11-03 15:53:42 -0800411 ++counter_;
Austin Schuh427b3702013-11-02 13:44:09 -0700412 const double current_left_velocity =
Brian Silvermande8fd552013-11-03 15:53:42 -0800413 (position_.left_encoder - last_position_.left_encoder) /
Austin Schuh427b3702013-11-02 13:44:09 -0700414 position_time_delta_;
415 const double current_right_velocity =
Brian Silvermande8fd552013-11-03 15:53:42 -0800416 (position_.right_encoder - last_position_.right_encoder) /
Austin Schuh427b3702013-11-02 13:44:09 -0700417 position_time_delta_;
418 const double left_motor_speed =
419 MotorSpeed(position_.left_shifter_position, current_left_velocity);
420 const double right_motor_speed =
421 MotorSpeed(position_.right_shifter_position, current_right_velocity);
Austin Schuh2054f5f2013-10-27 14:54:10 -0700422
Austin Schuh427b3702013-11-02 13:44:09 -0700423 // Reset the CIM model to the current conditions to be ready for when we shift.
424 if (IsInGear(left_gear_)) {
425 left_cim_->X_hat(0, 0) = left_motor_speed;
Brian Silvermande8fd552013-11-03 15:53:42 -0800426 LOG(DEBUG, "Setting left CIM to %f at robot speed %f\n", left_motor_speed,
427 current_left_velocity);
Austin Schuh427b3702013-11-02 13:44:09 -0700428 }
429 if (IsInGear(right_gear_)) {
Brian Silvermande8fd552013-11-03 15:53:42 -0800430 right_cim_->X_hat(0, 0) = right_motor_speed;
431 LOG(DEBUG, "Setting right CIM to %f at robot speed %f\n",
432 right_motor_speed, current_right_velocity);
Austin Schuh427b3702013-11-02 13:44:09 -0700433 }
Brian Silvermande8fd552013-11-03 15:53:42 -0800434 LOG(DEBUG, "robot speed l=%f r=%f\n", current_left_velocity,
435 current_right_velocity);
Austin Schuh2054f5f2013-10-27 14:54:10 -0700436
Austin Schuh427b3702013-11-02 13:44:09 -0700437 if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
438 // FF * X = U (steady state)
439 const Eigen::Matrix<double, 2, 2> FF =
440 loop_->B().inverse() *
441 (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
442
443 // Invert the plant to figure out how the velocity filter would have to
444 // work
445 // out in order to filter out the forwards negative inertia.
446 // This math assumes that the left and right power and velocity are
447 // equals,
448 // and that the plant is the same on the left and right.
449 const double fvel = FilterVelocity(throttle_);
450
451 const double sign_svel = wheel_ * ((fvel > 0.0) ? 1.0 : -1.0);
452 double steering_velocity;
453 if (quickturn_) {
454 steering_velocity = wheel_ * MaxVelocity();
455 } else {
456 steering_velocity = ::std::abs(fvel) * wheel_;
457 }
458 const double left_velocity = fvel - steering_velocity;
459 const double right_velocity = fvel + steering_velocity;
460
461 // Integrate velocity to get the position.
462 // This position is used to get integral control.
463 loop_->R << left_velocity, right_velocity;
464
465 if (!quickturn_) {
466 // K * R = w
467 Eigen::Matrix<double, 1, 2> equality_k;
468 equality_k << 1 + sign_svel, -(1 - sign_svel);
469 const double equality_w = 0.0;
470
471 // Construct a constraint on R by manipulating the constraint on U
472 ::aos::controls::HPolytope<2> R_poly = ::aos::controls::HPolytope<2>(
473 U_Poly_.H() * (loop_->K() + FF),
474 U_Poly_.k() + U_Poly_.H() * loop_->K() * loop_->X_hat);
475
476 // Limit R back inside the box.
477 loop_->R = CoerceGoal(R_poly, equality_k, equality_w, loop_->R);
478 }
479
480 const Eigen::Matrix<double, 2, 1> FF_volts = FF * loop_->R;
481 const Eigen::Matrix<double, 2, 1> U_ideal =
482 loop_->K() * (loop_->R - loop_->X_hat) + FF_volts;
483
484 for (int i = 0; i < 2; i++) {
485 loop_->U[i] = ::aos::Clip(U_ideal[i], -12, 12);
486 }
Brian Silvermande8fd552013-11-03 15:53:42 -0800487
488 // TODO(austin): Model this better.
489 // TODO(austin): Feed back?
490 loop_->X_hat = loop_->A() * loop_->X_hat + loop_->B() * loop_->U;
Brian Silverman718b1d72013-10-28 16:22:45 -0700491 } else {
Austin Schuh427b3702013-11-02 13:44:09 -0700492 // Any motor is not in gear. Speed match.
493 ::Eigen::Matrix<double, 1, 1> R_left;
494 R_left(0, 0) = left_motor_speed;
Brian Silvermande8fd552013-11-03 15:53:42 -0800495 const double wiggle = (static_cast<double>((counter_ % 4) / 2) - 0.5) * 3.5;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700496
Brian Silvermande8fd552013-11-03 15:53:42 -0800497 loop_->U(0, 0) =
498 ::aos::Clip((R_left / Kv)(0, 0) + wiggle, -position_.battery_voltage,
499 position_.battery_voltage);
500 right_cim_->X_hat = right_cim_->A() * right_cim_->X_hat +
501 right_cim_->B() * loop_->U(0, 0);
Austin Schuh2054f5f2013-10-27 14:54:10 -0700502
Austin Schuh427b3702013-11-02 13:44:09 -0700503 ::Eigen::Matrix<double, 1, 1> R_right;
504 R_right(0, 0) = right_motor_speed;
Brian Silvermande8fd552013-11-03 15:53:42 -0800505 loop_->U(1, 0) =
506 ::aos::Clip((R_right / Kv)(0, 0) + wiggle, -position_.battery_voltage,
507 position_.battery_voltage);
508 right_cim_->X_hat = right_cim_->A() * right_cim_->X_hat +
509 right_cim_->B() * loop_->U(1, 0);
510 loop_->U *= 12.0 / position_.battery_voltage;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700511 }
Austin Schuh2054f5f2013-10-27 14:54:10 -0700512 }
513
514 void SendMotors(Drivetrain::Output *output) {
515 LOG(DEBUG, "left pwm: %f right pwm: %f wheel: %f throttle: %f\n",
516 loop_->U(0, 0), loop_->U(1, 0), wheel_, throttle_);
Brian Silvermande8fd552013-11-03 15:53:42 -0800517 if (output != NULL) {
518 output->left_voltage = loop_->U(0, 0);
519 output->right_voltage = loop_->U(1, 0);
520 }
Austin Schuh427b3702013-11-02 13:44:09 -0700521 // Go in high gear if anything wants to be in high gear.
522 // TODO(austin): Seperate these.
523 if (left_gear_ == HIGH || left_gear_ == SHIFTING_UP ||
524 right_gear_ == HIGH || right_gear_ == SHIFTING_UP) {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700525 shifters.MakeWithBuilder().set(false).Send();
526 } else {
527 shifters.MakeWithBuilder().set(true).Send();
528 }
529 }
530
531 private:
532 const ::aos::controls::HPolytope<2> U_Poly_;
533
534 ::std::unique_ptr<StateFeedbackLoop<2, 2, 2>> loop_;
Austin Schuh427b3702013-11-02 13:44:09 -0700535 ::std::unique_ptr<StateFeedbackLoop<1, 1, 1>> left_cim_;
536 ::std::unique_ptr<StateFeedbackLoop<1, 1, 1>> right_cim_;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700537
Austin Schuh427b3702013-11-02 13:44:09 -0700538 const double ttrust_;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700539 double wheel_;
540 double throttle_;
541 bool quickturn_;
Austin Schuh427b3702013-11-02 13:44:09 -0700542 int stale_count_;
543 double position_time_delta_;
544 Gear left_gear_;
545 Gear right_gear_;
546 Drivetrain::Position last_position_;
547 Drivetrain::Position position_;
Brian Silvermande8fd552013-11-03 15:53:42 -0800548 int counter_;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700549};
550
551
brians343bc112013-02-10 01:53:46 +0000552class DrivetrainMotorsOL {
553 public:
554 DrivetrainMotorsOL() {
555 _old_wheel = 0.0;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700556 wheel_ = 0.0;
557 throttle_ = 0.0;
558 quickturn_ = false;
559 highgear_ = true;
brians343bc112013-02-10 01:53:46 +0000560 _neg_inertia_accumulator = 0.0;
561 _left_pwm = 0.0;
562 _right_pwm = 0.0;
563 }
564 void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700565 wheel_ = wheel;
566 throttle_ = throttle;
567 quickturn_ = quickturn;
568 highgear_ = highgear;
brians343bc112013-02-10 01:53:46 +0000569 _left_pwm = 0.0;
570 _right_pwm = 0.0;
571 }
Austin Schuh427b3702013-11-02 13:44:09 -0700572 void Update() {
brians343bc112013-02-10 01:53:46 +0000573 double overPower;
574 float sensitivity = 1.7;
575 float angular_power;
576 float linear_power;
577 double wheel;
578
Austin Schuh2054f5f2013-10-27 14:54:10 -0700579 double neg_inertia = wheel_ - _old_wheel;
580 _old_wheel = wheel_;
brians343bc112013-02-10 01:53:46 +0000581
582 double wheelNonLinearity;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700583 if (highgear_) {
Brian Silverman77a76002013-03-16 20:09:00 -0700584 wheelNonLinearity = 0.1; // used to be csvReader->TURN_NONLIN_HIGH
brians343bc112013-02-10 01:53:46 +0000585 // Apply a sin function that's scaled to make it feel better.
586 const double angular_range = M_PI / 2.0 * wheelNonLinearity;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700587 wheel = sin(angular_range * wheel_) / sin(angular_range);
Brian Silverman77a76002013-03-16 20:09:00 -0700588 wheel = sin(angular_range * wheel) / sin(angular_range);
brians343bc112013-02-10 01:53:46 +0000589 } else {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700590 wheelNonLinearity = 0.2; // used to be csvReader->TURN_NONLIN_LOW
brians343bc112013-02-10 01:53:46 +0000591 // Apply a sin function that's scaled to make it feel better.
592 const double angular_range = M_PI / 2.0 * wheelNonLinearity;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700593 wheel = sin(angular_range * wheel_) / sin(angular_range);
Brian Silverman77a76002013-03-16 20:09:00 -0700594 wheel = sin(angular_range * wheel) / sin(angular_range);
595 wheel = sin(angular_range * wheel) / sin(angular_range);
brians343bc112013-02-10 01:53:46 +0000596 }
597
Brian Silvermandf43ec22013-03-16 23:48:29 -0700598 static const double kThrottleDeadband = 0.05;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700599 if (::std::abs(throttle_) < kThrottleDeadband) {
600 throttle_ = 0;
Brian Silvermandf43ec22013-03-16 23:48:29 -0700601 } else {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700602 throttle_ = copysign((::std::abs(throttle_) - kThrottleDeadband) /
603 (1.0 - kThrottleDeadband), throttle_);
Brian Silvermandf43ec22013-03-16 23:48:29 -0700604 }
605
brians343bc112013-02-10 01:53:46 +0000606 double neg_inertia_scalar;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700607 if (highgear_) {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700608 neg_inertia_scalar = 8.0; // used to be csvReader->NEG_INTERTIA_HIGH
brians343bc112013-02-10 01:53:46 +0000609 sensitivity = 1.22; // used to be csvReader->SENSE_HIGH
610 } else {
611 if (wheel * neg_inertia > 0) {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700612 neg_inertia_scalar = 5; // used to be csvReader->NEG_INERTIA_LOW_MORE
brians343bc112013-02-10 01:53:46 +0000613 } else {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700614 if (::std::abs(wheel) > 0.65) {
615 neg_inertia_scalar = 5; // used to be csvReader->NEG_INTERTIA_LOW_LESS_EXT
brians343bc112013-02-10 01:53:46 +0000616 } else {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700617 neg_inertia_scalar = 5; // used to be csvReader->NEG_INTERTIA_LOW_LESS
brians343bc112013-02-10 01:53:46 +0000618 }
619 }
620 sensitivity = 1.24; // used to be csvReader->SENSE_LOW
brians343bc112013-02-10 01:53:46 +0000621 }
622 double neg_inertia_power = neg_inertia * neg_inertia_scalar;
623 _neg_inertia_accumulator += neg_inertia_power;
624
625 wheel = wheel + _neg_inertia_accumulator;
626 if (_neg_inertia_accumulator > 1) {
627 _neg_inertia_accumulator -= 1;
628 } else if (_neg_inertia_accumulator < -1) {
629 _neg_inertia_accumulator += 1;
630 } else {
631 _neg_inertia_accumulator = 0;
632 }
633
Austin Schuh2054f5f2013-10-27 14:54:10 -0700634 linear_power = throttle_;
brians343bc112013-02-10 01:53:46 +0000635
Austin Schuh2054f5f2013-10-27 14:54:10 -0700636 if (quickturn_) {
brians343bc112013-02-10 01:53:46 +0000637 double qt_angular_power = wheel;
Brian Silvermandf43ec22013-03-16 23:48:29 -0700638 if (::std::abs(linear_power) < 0.2) {
brians343bc112013-02-10 01:53:46 +0000639 if (qt_angular_power > 1) qt_angular_power = 1.0;
640 if (qt_angular_power < -1) qt_angular_power = -1.0;
641 } else {
642 qt_angular_power = 0.0;
643 }
brians343bc112013-02-10 01:53:46 +0000644 overPower = 1.0;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700645 if (highgear_) {
brians343bc112013-02-10 01:53:46 +0000646 sensitivity = 1.0;
647 } else {
648 sensitivity = 1.0;
649 }
650 angular_power = wheel;
651 } else {
652 overPower = 0.0;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700653 angular_power = ::std::abs(throttle_) * wheel * sensitivity;
brians343bc112013-02-10 01:53:46 +0000654 }
655
656 _right_pwm = _left_pwm = linear_power;
657 _left_pwm += angular_power;
658 _right_pwm -= angular_power;
659
660 if (_left_pwm > 1.0) {
661 _right_pwm -= overPower*(_left_pwm - 1.0);
662 _left_pwm = 1.0;
663 } else if (_right_pwm > 1.0) {
664 _left_pwm -= overPower*(_right_pwm - 1.0);
665 _right_pwm = 1.0;
666 } else if (_left_pwm < -1.0) {
667 _right_pwm += overPower*(-1.0 - _left_pwm);
668 _left_pwm = -1.0;
669 } else if (_right_pwm < -1.0) {
670 _left_pwm += overPower*(-1.0 - _right_pwm);
671 _right_pwm = -1.0;
672 }
673 }
674
675 void SendMotors(Drivetrain::Output *output) {
Brian Silvermandf43ec22013-03-16 23:48:29 -0700676 LOG(DEBUG, "left pwm: %f right pwm: %f wheel: %f throttle: %f\n",
Austin Schuh2054f5f2013-10-27 14:54:10 -0700677 _left_pwm, _right_pwm, wheel_, throttle_);
brians8ad74052013-03-16 21:04:51 +0000678 if (output) {
679 output->left_voltage = _left_pwm * 12.0;
680 output->right_voltage = _right_pwm * 12.0;
681 }
Austin Schuh2054f5f2013-10-27 14:54:10 -0700682 if (highgear_) {
brians343bc112013-02-10 01:53:46 +0000683 shifters.MakeWithBuilder().set(false).Send();
684 } else {
685 shifters.MakeWithBuilder().set(true).Send();
686 }
687 }
688
689 private:
690 double _old_wheel;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700691 double wheel_;
692 double throttle_;
693 bool quickturn_;
694 bool highgear_;
brians343bc112013-02-10 01:53:46 +0000695 double _neg_inertia_accumulator;
696 double _left_pwm;
697 double _right_pwm;
brians343bc112013-02-10 01:53:46 +0000698};
699
700void DrivetrainLoop::RunIteration(const Drivetrain::Goal *goal,
701 const Drivetrain::Position *position,
702 Drivetrain::Output *output,
703 Drivetrain::Status * /*status*/) {
704 // TODO(aschuh): These should be members of the class.
705 static DrivetrainMotorsSS dt_closedloop;
Austin Schuh2054f5f2013-10-27 14:54:10 -0700706 static PolyDrivetrain dt_openloop;
brians343bc112013-02-10 01:53:46 +0000707
708 bool bad_pos = false;
Austin Schuh427b3702013-11-02 13:44:09 -0700709 if (position == nullptr) {
James Kuszmaul3f354742013-03-10 17:27:56 -0700710 LOG(WARNING, "no position\n");
brians343bc112013-02-10 01:53:46 +0000711 bad_pos = true;
712 }
713
714 double wheel = goal->steering;
715 double throttle = goal->throttle;
716 bool quickturn = goal->quickturn;
717 bool highgear = goal->highgear;
718
719 bool control_loop_driving = goal->control_loop_driving;
720 double left_goal = goal->left_goal;
721 double right_goal = goal->right_goal;
722
Austin Schuh2054f5f2013-10-27 14:54:10 -0700723 dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
724 goal->right_velocity_goal);
brians343bc112013-02-10 01:53:46 +0000725 if (!bad_pos) {
726 const double left_encoder = position->left_encoder;
727 const double right_encoder = position->right_encoder;
728 if (gyro.FetchLatest()) {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700729 dt_closedloop.SetPosition(left_encoder, right_encoder, gyro->angle,
730 control_loop_driving);
brians343bc112013-02-10 01:53:46 +0000731 } else {
732 dt_closedloop.SetRawPosition(left_encoder, right_encoder);
733 }
734 }
Austin Schuh427b3702013-11-02 13:44:09 -0700735 dt_openloop.SetPosition(position);
Austin Schuh4352ac62013-03-19 06:23:16 +0000736 dt_closedloop.Update(position, output == NULL);
brians343bc112013-02-10 01:53:46 +0000737 dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
738 dt_openloop.Update();
Brian Silverman2845c4c2013-03-16 19:54:08 -0700739 if (control_loop_driving) {
740 dt_closedloop.SendMotors(output);
741 } else {
742 dt_openloop.SendMotors(output);
brians343bc112013-02-10 01:53:46 +0000743 }
744}
745
746} // namespace control_loops
747} // namespace frc971