Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 1 | #include "motors/fet12/motor_controls.h" |
| 2 | |
| 3 | #include "motors/peripheral/configuration.h" |
| 4 | |
| 5 | namespace frc971 { |
| 6 | namespace motors { |
| 7 | namespace { |
| 8 | |
| 9 | template <int kRows, int kCols> |
| 10 | using ComplexMatrix = MotorControlsImplementation::ComplexMatrix<kRows, kCols>; |
| 11 | |
| 12 | using Complex = ::std::complex<float>; |
| 13 | |
| 14 | constexpr int kCountsPerRevolution = |
| 15 | MotorControlsImplementation::constant_counts_per_revolution(); |
| 16 | |
| 17 | #if 1 |
| 18 | constexpr double kMaxDutyCycle = 0.98; |
| 19 | #elif 1 |
| 20 | constexpr double kMaxDutyCycle = 0.6; |
| 21 | #elif 0 |
| 22 | constexpr double kMaxDutyCycle = 0.2; |
| 23 | #endif |
| 24 | |
| 25 | constexpr int kPhaseBOffset = kCountsPerRevolution / 3; |
| 26 | constexpr int kPhaseCOffset = 2 * kCountsPerRevolution / 3; |
| 27 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 28 | constexpr double K1_unscaled = 1.0; |
| 29 | constexpr double K2_unscaled = 1.0 / -6.4786; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 30 | |
| 31 | // Make the amplitude of the fundamental 1 for ease of math. |
| 32 | constexpr double K2 = K2_unscaled / K1_unscaled; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 33 | constexpr double K1 = 1.0; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 34 | |
| 35 | // volts |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 36 | constexpr double kVcc = 31.5; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 37 | |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 38 | // 3.6 and 1.15 are adjustments from calibrations. |
| 39 | constexpr double Kv = 22000.0 * 2.0 * M_PI / 60.0 / 30.0 * 3.6 * 1.15; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 40 | |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 41 | constexpr double kL = 3e-06; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 42 | constexpr double kM = 0; |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 43 | constexpr double kR = 0.01008; |
| 44 | constexpr float kAdiscrete_diagonal = 0.845354f; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 45 | constexpr float kAdiscrete_offdiagonal = 0.0f; |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 46 | constexpr float kBdiscrete_inv_diagonal = 0.0651811f; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 47 | constexpr float kBdiscrete_inv_offdiagonal = 0.0f; |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 48 | constexpr double kOneAmpScalar = 1.46785; |
| 49 | constexpr double kMaxOneAmpDrivingVoltage = 0.0265038; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 50 | |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 51 | |
| 52 | ::Eigen::Matrix<float, 3, 3> A_discrete() { |
| 53 | ::Eigen::Matrix<float, 3, 3> r; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 54 | r << kAdiscrete_diagonal, kAdiscrete_offdiagonal, kAdiscrete_offdiagonal, |
| 55 | kAdiscrete_offdiagonal, kAdiscrete_diagonal, kAdiscrete_offdiagonal, |
| 56 | kAdiscrete_offdiagonal, kAdiscrete_offdiagonal, kAdiscrete_diagonal; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 57 | return r; |
| 58 | } |
| 59 | |
| 60 | ::Eigen::Matrix<float, 3, 3> B_discrete_inverse() { |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 61 | return ::Eigen::Matrix<float, 1, 3>::Constant(kBdiscrete_inv_diagonal) |
| 62 | .asDiagonal(); |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 63 | } |
| 64 | |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 65 | // Use FluxLinkageTable() to access this with a const so you don't accidentally |
| 66 | // modify it. |
| 67 | float flux_linkage_table[kCountsPerRevolution]; |
| 68 | |
| 69 | void MakeFluxLinkageTable() { |
| 70 | for (int i = 0; i < kCountsPerRevolution; ++i) { |
| 71 | const double theta = static_cast<double>(i) / |
| 72 | static_cast<double>(kCountsPerRevolution) * 2.0 * M_PI; |
| 73 | flux_linkage_table[i] = K1 * sin(theta) - K2 * sin(theta * 5); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // theta doesn't have to be less than kCountsPerRevolution. |
| 78 | ::Eigen::Matrix<float, 3, 1> FluxLinkageAt(uint32_t theta) { |
| 79 | ::Eigen::Matrix<float, 3, 1> r; |
| 80 | r(0) = flux_linkage_table[theta % kCountsPerRevolution]; |
| 81 | r(1) = flux_linkage_table[(theta + kPhaseBOffset) % kCountsPerRevolution]; |
| 82 | r(2) = flux_linkage_table[(theta + kPhaseCOffset) % kCountsPerRevolution]; |
| 83 | return r; |
| 84 | } |
| 85 | |
| 86 | ::Eigen::Matrix<float, 3, 3> MakeK() { |
| 87 | ::Eigen::Matrix<float, 3, 3> Vconv; |
| 88 | Vconv << 2.0f, -1.0f, -1.0f, -1.0f, 2.0f, -1.0f, -1.0f, -1.0f, 2.0f; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 89 | static constexpr float kControllerGain = 0.05f; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 90 | return kControllerGain * (Vconv / 3.0f); |
| 91 | } |
| 92 | |
| 93 | ComplexMatrix<3, 1> MakeE1Unrotated() { |
| 94 | ComplexMatrix<3, 1> rotation; |
| 95 | rotation << Complex(0, -1), Complex(::std::sqrt(3) / 2, 0.5), |
| 96 | Complex(-::std::sqrt(3) / 2, 0.5); |
| 97 | return K1 * rotation; |
| 98 | } |
| 99 | |
| 100 | ComplexMatrix<3, 1> MakeE2Unrotated() { |
| 101 | ComplexMatrix<3, 1> rotation; |
| 102 | rotation << Complex(0, -1), Complex(-::std::sqrt(3) / 2, 0.5), |
| 103 | Complex(::std::sqrt(3) / 2, 0.5); |
| 104 | return K2 * rotation; |
| 105 | } |
| 106 | |
| 107 | ComplexMatrix<3, 3> Hn(float omega, int scalar) { |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 108 | const Complex a(static_cast<float>(kR), |
| 109 | omega * static_cast<float>(scalar * kL)); |
| 110 | const Complex b(0, omega * static_cast<float>(scalar * kM)); |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 111 | const Complex temp1 = a + b; |
| 112 | const Complex temp2 = -b; |
| 113 | ComplexMatrix<3, 3> matrix; |
| 114 | matrix << temp1, temp2, temp2, temp2, temp1, temp2, temp2, temp2, temp1; |
| 115 | return matrix * |
| 116 | -(omega / static_cast<float>(Kv) / (a * a + a * b - 2.0f * b * b)); |
| 117 | } |
| 118 | |
| 119 | } // namespace |
| 120 | |
| 121 | MotorControlsImplementation::MotorControlsImplementation() |
| 122 | : E1Unrotated_(MakeE1Unrotated()), E2Unrotated_(MakeE2Unrotated()) { |
| 123 | MakeFluxLinkageTable(); |
| 124 | } |
| 125 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 126 | ::std::array<float, 3> MotorControlsImplementation::DoIteration( |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 127 | const float raw_currents[3], const uint32_t theta_in, |
| 128 | const float command_current) { |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 129 | static constexpr float kCurrentSlewRate = 0.10f; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 130 | if (command_current > filtered_current_ + kCurrentSlewRate) { |
| 131 | filtered_current_ += kCurrentSlewRate; |
| 132 | } else if (command_current < filtered_current_ - kCurrentSlewRate) { |
| 133 | filtered_current_ -= kCurrentSlewRate; |
| 134 | } else { |
| 135 | filtered_current_ = command_current; |
| 136 | } |
| 137 | const float goal_current_in = filtered_current_; |
| 138 | const float max_current = |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 139 | (static_cast<float>(kVcc * kMaxDutyCycle) - |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 140 | estimated_velocity_ / static_cast<float>(Kv / 2.0)) / |
| 141 | static_cast<float>(kMaxOneAmpDrivingVoltage); |
| 142 | const float min_current = |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 143 | (-static_cast<float>(kVcc * kMaxDutyCycle) - |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 144 | estimated_velocity_ / static_cast<float>(Kv / 2.0)) / |
| 145 | static_cast<float>(kMaxOneAmpDrivingVoltage); |
| 146 | const float goal_current = |
| 147 | ::std::max(min_current, ::std::min(max_current, goal_current_in)); |
| 148 | |
| 149 | #if 0 |
| 150 | const uint32_t theta = |
| 151 | (theta_in + static_cast<uint32_t>(estimated_velocity_ * 1.0f)) % 1024; |
| 152 | #elif 0 |
| 153 | const uint32_t theta = |
| 154 | (theta_in + kCountsPerRevolution - 160u) % kCountsPerRevolution; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 155 | #elif 0 |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 156 | const uint32_t theta = |
| 157 | (theta_in + kCountsPerRevolution + |
| 158 | ((estimated_velocity_ > 0) ? (kCountsPerRevolution - 10u) : 60u)) % |
| 159 | kCountsPerRevolution; |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 160 | #elif 1 |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 161 | const uint32_t theta = theta_in; |
| 162 | #endif |
| 163 | |
| 164 | const ::Eigen::Matrix<float, 3, 1> measured_current = |
| 165 | (::Eigen::Matrix<float, 3, 1>() << scale_current_reading(raw_currents[0]), |
| 166 | scale_current_reading(raw_currents[1]), |
| 167 | scale_current_reading(raw_currents[2])).finished(); |
| 168 | |
| 169 | const ComplexMatrix<3, 1> E1 = |
| 170 | E1Unrotated_ * |
| 171 | ImaginaryExpInt<::std::ratio<1, constant_counts_per_revolution()>>(theta); |
| 172 | const ComplexMatrix<3, 1> E2 = |
| 173 | E2Unrotated_ * |
| 174 | ImaginaryExpInt<::std::ratio<5, constant_counts_per_revolution()>>(theta); |
| 175 | |
| 176 | const float overall_measured_current = |
| 177 | ((E1 + E2).real().transpose() * measured_current / |
| 178 | static_cast<float>(kOneAmpScalar))(0); |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 179 | overall_measured_current_ = overall_measured_current; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 180 | const float current_error = goal_current - overall_measured_current; |
| 181 | estimated_velocity_ += current_error * 0.1f; |
| 182 | debug_[3] = theta; |
| 183 | const float omega = estimated_velocity_; |
| 184 | |
| 185 | debug_[4] = max_current * 10; |
| 186 | |
| 187 | const ::Eigen::Matrix<float, 3, 1> I_now = I_last_; |
| 188 | const ::Eigen::Matrix<float, 3, 1> I_next = |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 189 | FluxLinkageAt(theta + |
| 190 | static_cast<int32_t>( |
| 191 | 2.0f * omega * kCountsPerRevolution / |
| 192 | static_cast<float>(2.0 * M_PI * SWITCHING_FREQUENCY))) * |
| 193 | goal_current; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 194 | |
| 195 | const ComplexMatrix<3, 3> H1 = Hn(omega, 1); |
| 196 | const ComplexMatrix<3, 3> H2 = Hn(omega, 5); |
| 197 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 198 | const ::std::complex<float> first_speed_delay = |
| 199 | ImaginaryExpFloat(omega / SWITCHING_FREQUENCY); |
| 200 | const ::std::complex<float> fifth_speed_delay = |
| 201 | ImaginaryExpFloat(omega * 5.0f / SWITCHING_FREQUENCY); |
| 202 | const ComplexMatrix<3, 1> H1E1 = first_speed_delay * H1 * E1; |
| 203 | const ComplexMatrix<3, 1> H2E2 = fifth_speed_delay * H2 * E2; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 204 | const ComplexMatrix<3, 1> p_imaginary = H1E1 + H2E2; |
| 205 | const ComplexMatrix<3, 1> p_next_imaginary = |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 206 | first_speed_delay * H1E1 + fifth_speed_delay * H2E2; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 207 | const ::Eigen::Matrix<float, 3, 1> p = p_imaginary.real(); |
| 208 | const ::Eigen::Matrix<float, 3, 1> p_next = p_next_imaginary.real(); |
| 209 | |
| 210 | const ::Eigen::Matrix<float, 3, 1> Vn_ff = |
| 211 | B_discrete_inverse() * (I_next - A_discrete() * (I_now - p) - p_next); |
| 212 | const ::Eigen::Matrix<float, 3, 1> Vn = |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 213 | Vn_ff + MakeK() * (I_prev_ - measured_current); |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 214 | |
| 215 | debug_[0] = (I_next)(0) * 100; |
| 216 | debug_[1] = (I_next)(1) * 100; |
| 217 | debug_[2] = (I_next)(2) * 100; |
| 218 | |
| 219 | debug_[5] = Vn(0) * 100; |
| 220 | debug_[6] = Vn(1) * 100; |
| 221 | debug_[7] = Vn(2) * 100; |
| 222 | |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 223 | ::Eigen::Matrix<float, 3, 1> times = Vn / kVcc; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 224 | { |
| 225 | const float min_time = times.minCoeff(); |
| 226 | times -= ::Eigen::Matrix<float, 3, 1>::Constant(min_time); |
| 227 | } |
| 228 | { |
| 229 | const float max_time = times.maxCoeff(); |
| 230 | const float scalar = |
| 231 | static_cast<float>(kMaxDutyCycle) / |
| 232 | ::std::max(static_cast<float>(kMaxDutyCycle), max_time); |
| 233 | times *= scalar; |
| 234 | } |
| 235 | |
James Kuszmaul | 521eb65 | 2018-10-17 19:09:33 -0700 | [diff] [blame] | 236 | I_prev_ = I_now; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 237 | I_last_ = I_next; |
| 238 | |
| 239 | // TODO(Austin): Figure out why we need the min here. |
James Kuszmaul | 998d303 | 2018-09-08 15:41:41 -0700 | [diff] [blame] | 240 | return {::std::max(0.0f, times(0)), ::std::max(0.0f, times(1)), |
| 241 | ::std::max(0.0f, times(2))}; |
Brian Silverman | d956639 | 2018-06-10 15:02:03 -0700 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | int16_t MotorControlsImplementation::Debug(uint32_t theta) { |
| 245 | return debug_[theta]; |
| 246 | } |
| 247 | |
| 248 | } // namespace motors |
| 249 | } // namespace frc971 |