James Kuszmaul | cdd033e | 2013-03-02 15:10:43 -0800 | [diff] [blame^] | 1 | #include "frc971/control_loops/shooter.h" |
| 2 | |
| 3 | #include "aos/aos_core.h" |
| 4 | |
| 5 | #include "aos/common/control_loop/control_loops.q.h" |
| 6 | #include "aos/common/logging/logging.h" |
| 7 | |
| 8 | #include "frc971/control_loops/shooter_motor_plant.h" |
| 9 | |
| 10 | namespace frc971 { |
| 11 | namespace control_loops { |
| 12 | |
| 13 | namespace { |
| 14 | // Motor controllers have a range of PWM values where they don't trigger, |
| 15 | // and we use these values to avoid that. |
| 16 | // TODO: find these values for the Talons. |
| 17 | static const double positive_deadband_power = 0.0; |
| 18 | static const double negative_deadband_power = 0.0; |
| 19 | } |
| 20 | |
| 21 | ShooterMotor::ShooterMotor(control_loops::ShooterLoop *my_shooter) |
| 22 | : aos::control_loops::ControlLoop<control_loops::ShooterLoop>(my_shooter), |
| 23 | loop_(new StateFeedbackLoop<2, 1, 1>(MakeShooterLoop())), |
| 24 | history_position_(0), |
| 25 | position_goal_(0.0), |
| 26 | time_(0.0) { |
| 27 | memset(history, 0, sizeof(history)); |
| 28 | // Creates the header for the data file. Overwrites anything already there. |
| 29 | loop_->StartDataFile("shooter.csv"); |
| 30 | } |
| 31 | |
| 32 | void ShooterMotor::RunIteration( |
| 33 | const control_loops::ShooterLoop::Goal *goal, |
| 34 | const control_loops::ShooterLoop::Position *position, |
| 35 | ::aos::control_loops::Output *output, |
| 36 | control_loops::ShooterLoop::Status *status) { |
| 37 | |
| 38 | bool bad_pos = false; |
| 39 | if (position == NULL) { |
| 40 | LOG(WARNING, "no position given\n"); |
| 41 | bad_pos = true; |
| 42 | } |
| 43 | |
| 44 | const double velocity_goal = std::min(goal->velocity, max_speed); |
| 45 | static double last_position = 0.0; |
| 46 | double output_voltage = 0.0; |
| 47 | |
| 48 | if (!bad_pos) { |
| 49 | static bool first_time = true; |
| 50 | if (first_time) { |
| 51 | position_goal_ = position->position; |
| 52 | first_time = false; |
| 53 | } |
| 54 | |
| 55 | // Add position to the history. |
| 56 | history[history_position_] = position->position; |
| 57 | history_position_ = (history_position_ + 1) % kHistoryLength; |
| 58 | |
| 59 | loop_->Y << position->position; |
| 60 | // Prevents the position from going out of the bounds |
| 61 | // where the control loop will run the motor at full power. |
| 62 | const double velocity_weight_scalar = 0.35; |
| 63 | const double max_reference = (loop_->plant.U_max[0] - velocity_weight_scalar * |
| 64 | (velocity_goal - loop_->X_hat[1]) * loop_->K[1]) |
| 65 | / loop_->K[0] + loop_->X_hat[0]; |
| 66 | const double min_reference = (loop_->plant.U_min[0] - velocity_weight_scalar * |
| 67 | (velocity_goal - loop_->X_hat[1]) * loop_->K[1]) |
| 68 | / loop_->K[0] + loop_->X_hat[0]; |
| 69 | position_goal_ = std::max(std::min(position_goal_, max_reference), min_reference); |
| 70 | loop_->R << position_goal_, velocity_goal; |
| 71 | position_goal_ += velocity_goal * dt; |
| 72 | } |
| 73 | |
| 74 | loop_->Update(!bad_pos, bad_pos || (output == NULL)); |
| 75 | // There is no need to make the motors actively assist the spin-down. |
| 76 | if (velocity_goal < 1.0) { |
| 77 | output_voltage = 0.0; |
| 78 | // Also, reset the position incrementer to avoid accumulating error. |
| 79 | position_goal_ = position->position; |
| 80 | } else { |
| 81 | output_voltage = loop_->U[0] / 12.0; |
| 82 | } |
| 83 | LOG(DEBUG, |
| 84 | "PWM: %f, raw_pos: %f rotations: %f " |
| 85 | "junk velocity: %f, xhat[0]: %f xhat[1]: %f, R[0]: %f R[1]: %f\n", |
| 86 | output_voltage, position->position, |
| 87 | position->position / (2 * M_PI), |
| 88 | (position->position - last_position) / dt, |
| 89 | loop_->X_hat[0], loop_->X_hat[1], loop_->R[0], loop_->R[1]); |
| 90 | |
| 91 | // aos::DriverStationDisplay::Send(2, "RPS%3.0f(%3.0f) RPM%4.0f", |
| 92 | // (position->position - last_position) * 100.0, goal->goal, |
| 93 | // (position->position - last_position) / (2.0 * M_PI) * 6000.0); |
| 94 | |
| 95 | // Calculates the velocity over the last kHistoryLength*.001 seconds |
| 96 | // by taking the difference between the current and next history positions. |
| 97 | int old_history_position = ((history_position_ == 0) ? |
| 98 | kHistoryLength : history_position_) - 1; |
| 99 | average_velocity_ = (history[old_history_position] |
| 100 | - history[history_position_]) * 100.0 / (double)(kHistoryLength - 1); |
| 101 | status->average_velocity = average_velocity_; |
| 102 | // Determines if the velocity is close enough to the goal to be ready. |
| 103 | if (std::abs(velocity_goal - average_velocity_) < 10.0 && |
| 104 | velocity_goal != 0.0) { |
| 105 | LOG(DEBUG, "Steady: "); |
| 106 | status->ready = true; |
| 107 | } else { |
| 108 | LOG(DEBUG, "Not ready: "); |
| 109 | status->ready = false; |
| 110 | } |
| 111 | LOG(DEBUG, "avg = %f goal = %f\n", average_velocity_, velocity_goal); |
| 112 | |
| 113 | // Deal with motor controller deadbands. |
| 114 | if (output_voltage > 0) { |
| 115 | output_voltage += positive_deadband_power; |
| 116 | } |
| 117 | if (output_voltage < 0) { |
| 118 | output_voltage -= negative_deadband_power; |
| 119 | } |
| 120 | |
| 121 | if (bad_pos) { |
| 122 | last_position = position->position; |
| 123 | } else { |
| 124 | // use the predicted position |
| 125 | last_position = loop_->X_hat[0]; |
| 126 | } |
| 127 | if (output) { |
| 128 | output->voltage = output_voltage; |
| 129 | } |
| 130 | |
| 131 | // If anything is happening, record it. Otherwise, reset the time. |
| 132 | // This should be removed once we are done with testing. |
| 133 | if (output_voltage != 0 && average_velocity_ != 0 && velocity_goal != 0) { |
| 134 | loop_->RecordDatum("shooter.csv", time_); |
| 135 | time_ += dt; |
| 136 | } |
| 137 | else { |
| 138 | time_ = 0; |
| 139 | } |
| 140 | } // RunIteration |
| 141 | |
| 142 | } // namespace control_loops |
| 143 | } // namespace frc971 |